// updater.c\r
// Copyright (C) 2014 Suguru Kawamoto\r
// ソフトウェア自動更新\r
+// コードの再利用のため表記はwchar_t型だが実体はchar型でUTF-8\r
\r
#include <tchar.h>\r
#include <ws2tcpip.h>\r
BYTE ListHash[64];\r
} UPDATE_HASH;\r
\r
-BOOL DownloadFileViaHTTP(void* pOut, DWORD Length, DWORD* pLength, LPCWSTR UserAgent, LPCWSTR ServerName, LPCWSTR ObjectName)\r
+#define UPDATE_LIST_FILE_FLAG_DIRECTORY 0x00000001\r
+\r
+typedef struct\r
+{\r
+ DWORD Flags;\r
+ CHAR SrcPath[128];\r
+ BYTE SrcHash[64];\r
+ CHAR DstPath[128];\r
+ FILETIME Timestamp;\r
+} UPDATE_LIST_FILE;\r
+\r
+typedef struct\r
+{\r
+ DWORD Version;\r
+ CHAR VersionString[32];\r
+ DWORD FileCount;\r
+ UPDATE_LIST_FILE File[1];\r
+} UPDATE_LIST;\r
+\r
+BOOL ReadFileViaHTTPW(void* pOut, DWORD Length, DWORD* pLength, LPCWSTR UserAgent, LPCWSTR ServerName, LPCWSTR ObjectName)\r
{\r
BOOL bResult;\r
HINTERNET hSession;\r
return bResult;\r
}\r
\r
+BOOL ReadFileViaHTTP(void* pOut, DWORD Length, DWORD* pLength, LPCSTR UserAgent, LPCSTR ServerName, LPCSTR ObjectName)\r
+{\r
+ BOOL r;\r
+ wchar_t* pw0;\r
+ wchar_t* pw1;\r
+ wchar_t* pw2;\r
+ pw0 = DuplicateMtoW(UserAgent, -1);\r
+ pw1 = DuplicateMtoW(ServerName, -1);\r
+ pw2 = DuplicateMtoW(ObjectName, -1);\r
+ r = ReadFileViaHTTPW(pOut, Length, pLength, pw0, pw1, pw2);\r
+ FreeDuplicatedString(pw0);\r
+ FreeDuplicatedString(pw1);\r
+ FreeDuplicatedString(pw2);\r
+ return r;\r
+}\r
+\r
+BOOL SaveMemoryToFileWithTimestamp(LPCTSTR FileName, void* pData, DWORD Size, FILETIME* pTimestamp)\r
+{\r
+ BOOL bResult;\r
+ HANDLE hFile;\r
+ bResult = FALSE;\r
+ if((hFile = CreateFile(FileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE)\r
+ {\r
+ if(WriteFile(hFile, pData, Size, &Size, NULL))\r
+ {\r
+ if(SetFileTime(hFile, NULL, NULL, pTimestamp))\r
+ bResult = TRUE;\r
+ }\r
+ CloseHandle(hFile);\r
+ }\r
+ return bResult;\r
+}\r
+\r
+BOOL CopyAllFilesInDirectory(LPCTSTR From, LPCTSTR To)\r
+{\r
+ BOOL bResult;\r
+ TCHAR* pFrom;\r
+ TCHAR* pTo;\r
+ SHFILEOPSTRUCT fop;\r
+ bResult = FALSE;\r
+ if(pFrom = (TCHAR*)malloc(sizeof(TCHAR) * (_tcslen(From) + _tcslen(_T("\\*")) + 2)))\r
+ {\r
+ _tcscpy(pFrom, From);\r
+ _tcsncpy(pFrom + _tcslen(pFrom), _T("\\*"), _tcslen(_T("\\*")) + 2);\r
+ if(pTo = (TCHAR*)malloc(sizeof(TCHAR) * (_tcslen(To) + 2)))\r
+ {\r
+ _tcsncpy(pTo, To, _tcslen(To) + 2);\r
+ memset(&fop, 0, sizeof(SHFILEOPSTRUCT));\r
+ fop.wFunc = FO_COPY;\r
+ fop.pFrom = pFrom;\r
+ fop.pTo = pTo;\r
+ fop.fFlags = FOF_NO_UI;\r
+ if(SHFileOperation(&fop) == 0)\r
+ bResult = TRUE;\r
+ free(pTo);\r
+ }\r
+ free(pFrom);\r
+ }\r
+ return bResult;\r
+}\r
+\r
+BOOL DeleteDirectoryAndContents(LPCTSTR Path)\r
+{\r
+ BOOL bResult;\r
+ TCHAR* pFrom;\r
+ SHFILEOPSTRUCT fop;\r
+ bResult = FALSE;\r
+ if(pFrom = (TCHAR*)malloc(sizeof(TCHAR) * (_tcslen(Path) + 2)))\r
+ {\r
+ _tcsncpy(pFrom, Path, _tcslen(Path) + 2);\r
+ memset(&fop, 0, sizeof(SHFILEOPSTRUCT));\r
+ fop.wFunc = FO_DELETE;\r
+ fop.pFrom = pFrom;\r
+ fop.fFlags = FOF_NO_UI;\r
+ if(SHFileOperation(&fop) == 0)\r
+ bResult = TRUE;\r
+ free(pFrom);\r
+ }\r
+ return bResult;\r
+}\r
+\r
// FFFTPの更新情報を確認\r
-BOOL CheckForUpdates(BOOL bDownload, LPCTSTR DownloadDir)\r
+BOOL CheckForUpdates(BOOL bDownload, LPCTSTR DownloadDir, DWORD* pVersion, LPTSTR pVersionString)\r
{\r
BOOL bResult;\r
DWORD Length;\r
- BYTE Buf1[4096];\r
+ BYTE Buf1[65536];\r
BYTE Buf2[1024];\r
UPDATE_HASH UpdateHash;\r
BYTE Hash[64];\r
+ UPDATE_LIST* pUpdateList;\r
bResult = FALSE;\r
- if(DownloadFileViaHTTP(&Buf1, sizeof(Buf1), &Length, HTTP_USER_AGENT, UPDATE_SERVER, UPDATE_HASH_PATH))\r
+ if(ReadFileViaHTTP(&Buf1, sizeof(Buf1), &Length, HTTP_USER_AGENT, UPDATE_SERVER, UPDATE_HASH_PATH))\r
{\r
if(DecryptSignature(UPDATE_RSA_PUBLIC_KEY, &Buf1, Length, &Buf2, sizeof(Buf2), &Length))\r
{\r
memcpy(&UpdateHash, &Buf2, sizeof(UPDATE_HASH));\r
if(memcmp(&UpdateHash.Signature, UPDATE_SIGNATURE, 64) == 0)\r
{\r
- if(DownloadFileViaHTTP(&Buf1, sizeof(Buf1), &Length, HTTP_USER_AGENT, UPDATE_SERVER, UPDATE_LIST_PATH))\r
+ if(ReadFileViaHTTP(&Buf1, sizeof(Buf1), &Length, HTTP_USER_AGENT, UPDATE_SERVER, UPDATE_LIST_PATH))\r
{\r
GetHashSHA512(&Buf1, Length, &Hash);\r
if(memcmp(&Hash, &UpdateHash.ListHash, 64) == 0)\r
{\r
- // TODO: 更新情報を解析\r
- bResult = TRUE;\r
- if(bDownload)\r
- bResult = PrepareUpdates(&Buf1, Length, DownloadDir);\r
+ if(Length >= sizeof(UPDATE_LIST))\r
+ {\r
+ bResult = TRUE;\r
+ pUpdateList = (UPDATE_LIST*)&Buf1;\r
+ if(pUpdateList->Version > *pVersion)\r
+ {\r
+ *pVersion = pUpdateList->Version;\r
+ _tcscpy(pVersionString, pUpdateList->VersionString);\r
+ }\r
+ if(bDownload)\r
+ bResult = PrepareUpdates(&Buf1, Length, DownloadDir);\r
+ }\r
}\r
}\r
}\r
BOOL PrepareUpdates(void* pList, DWORD ListLength, LPCTSTR DownloadDir)\r
{\r
BOOL bResult;\r
+ UPDATE_LIST* pUpdateList;\r
+ void* pBuf;\r
+ DWORD i;\r
+ BOOL b;\r
+ DWORD Length;\r
+ BYTE Hash[64];\r
+ TCHAR Path[MAX_PATH];\r
bResult = FALSE;\r
- // TODO: 更新情報を解析\r
- // TODO: 更新するファイルをダウンロード\r
+ if(ListLength >= sizeof(UPDATE_LIST))\r
+ {\r
+ pUpdateList = (UPDATE_LIST*)pList;\r
+ if((pUpdateList->FileCount - 1) * sizeof(UPDATE_LIST_FILE) + sizeof(UPDATE_LIST) >= ListLength)\r
+ {\r
+ bResult = TRUE;\r
+ DeleteDirectoryAndContents(DownloadDir);\r
+ CreateDirectory(DownloadDir, NULL);\r
+ pBuf = malloc(16777216);\r
+ for(i = 0; i < pUpdateList->FileCount; i++)\r
+ {\r
+ b = FALSE;\r
+ if(pUpdateList->File[i].Flags & UPDATE_LIST_FILE_FLAG_DIRECTORY)\r
+ {\r
+ _tcscpy(Path, DownloadDir);\r
+ _tcscat(Path, _T("\\"));\r
+ _tcscat(Path, pUpdateList->File[i].DstPath);\r
+ if(CreateDirectory(Path, NULL))\r
+ b = TRUE;\r
+ }\r
+ if(strlen(pUpdateList->File[i].SrcPath) > 0)\r
+ {\r
+ if(ReadFileViaHTTP(pBuf, 16777216, &Length, HTTP_USER_AGENT, UPDATE_SERVER, pUpdateList->File[i].SrcPath))\r
+ {\r
+ GetHashSHA512(pBuf, Length, &Hash);\r
+ if(memcmp(&Hash, &pUpdateList->File[i].SrcHash, 64) == 0)\r
+ {\r
+ _tcscpy(Path, DownloadDir);\r
+ _tcscat(Path, _T("\\"));\r
+ _tcscat(Path, pUpdateList->File[i].DstPath);\r
+ if(SaveMemoryToFileWithTimestamp(Path, pBuf, Length, &pUpdateList->File[i].Timestamp))\r
+ b = TRUE;\r
+ }\r
+ }\r
+ }\r
+ if(!b)\r
+ {\r
+ bResult = FALSE;\r
+ break;\r
+ }\r
+ }\r
+ free(pBuf);\r
+ }\r
+ }\r
return bResult;\r
}\r
\r
BOOL ApplyUpdates(LPCTSTR DestinationDir)\r
{\r
BOOL bResult;\r
+ TCHAR Source[MAX_PATH];\r
+ TCHAR Backup[MAX_PATH];\r
+ TCHAR* p;\r
bResult = FALSE;\r
- // TODO:\r
+ if(GetModuleFileName(NULL, Source, MAX_PATH) > 0)\r
+ {\r
+ if(p = _tcsrchr(Source, _T('\\')))\r
+ *p = _T('\0');\r
+ _tcscpy(Backup, Source);\r
+ _tcscat(Backup, _T("\\updatebackup"));\r
+ DeleteDirectoryAndContents(Backup);\r
+ if(CopyAllFilesInDirectory(DestinationDir, Backup))\r
+ {\r
+ if(CopyAllFilesInDirectory(Source, DestinationDir))\r
+ {\r
+ bResult = TRUE;\r
+ _tcscpy(Backup, DestinationDir);\r
+ _tcscat(Backup, _T("\\updatebackup"));\r
+ DeleteDirectoryAndContents(Backup);\r
+ }\r
+ else\r
+ CopyAllFilesInDirectory(Backup, DestinationDir);\r
+ }\r
+ }\r
+ return bResult;\r
+}\r
+\r
+// 更新するファイルをダウンロード\r
+BOOL CleanupUpdates(LPCTSTR DownloadDir)\r
+{\r
+ BOOL bResult;\r
+ bResult = FALSE;\r
+ if(DeleteDirectoryAndContents(DownloadDir))\r
+ bResult = TRUE;\r
return bResult;\r
}\r
\r