OSDN Git Service

Add routines to check for software updates.
authors_kawamoto <s_kawamoto@users.sourceforge.jp>
Sat, 7 Jun 2014 15:23:41 +0000 (00:23 +0900)
committers_kawamoto <s_kawamoto@users.sourceforge.jp>
Sat, 7 Jun 2014 15:23:41 +0000 (00:23 +0900)
FFFTP_Eng_Release/FFFTP.exe
Release/FFFTP.exe
common.h
main.c
mbswrapper.c
mbswrapper.h
mesg-eng.h
mesg-jpn.h
updater.c
updater.h

index 21e8d60..cf4e7e0 100644 (file)
Binary files a/FFFTP_Eng_Release/FFFTP.exe and b/FFFTP_Eng_Release/FFFTP.exe differ
index f136ada..9e33763 100644 (file)
Binary files a/Release/FFFTP.exe and b/Release/FFFTP.exe differ
index 1168fbd..e526a9d 100644 (file)
--- a/common.h
+++ b/common.h
 #endif\r
 #define VER_NUM                                        1990            /* 設定バージョン */\r
 #define PROGRAM_VERSION_NUM            1990            /* バージョン */\r
+// ソフトウェア自動更新\r
+// リリースバージョンはリリース予定年(10進数4桁)+月(2桁)+日(2桁)+通し番号(0スタート2桁)とする\r
+// 2014年7月31日中の30個目のリリースは2014073129\r
+#define RELEASE_VERSION_NUM            2014061500      /* リリースバージョン */\r
 \r
 \r
 // SourceForge.JPによるフォーク\r
diff --git a/main.c b/main.c
index e21a13e..5712cd6 100644 (file)
--- a/main.c
+++ b/main.c
@@ -372,13 +372,16 @@ int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLi
        {\r
                if(!StartUpdateProcessAsAdministrator(lpszCmdLine, " --restart"))\r
                {\r
-                       ApplyUpdates(UpdateDir);\r
+                       if(ApplyUpdates(UpdateDir))\r
+                               MessageBox(NULL, MSGJPN359, "FFFTP", MB_OK);\r
+                       else\r
+                               MessageBox(NULL, MSGJPN360, "FFFTP", MB_OK);\r
                }\r
                return 0;\r
        }\r
        else if(GetTokenAfterOption(lpszCmdLine, UpdateDir, "--software-cleanup", "--software-cleanup"))\r
        {\r
-               // TODO: ダウンロードした更新ファイルを削除\r
+               CleanupUpdates(UpdateDir);\r
        }\r
 \r
        // マルチコアCPUの特定環境下でファイル通信中にクラッシュするバグ対策\r
index 5759483..0651173 100644 (file)
@@ -2438,6 +2438,30 @@ END_ROUTINE
        return r;\r
 }\r
 \r
+BOOL CreateDirectoryM(LPCSTR lpPathName, LPSECURITY_ATTRIBUTES lpSecurityAttributes)\r
+{\r
+       BOOL r = FALSE;\r
+       wchar_t* pw0 = NULL;\r
+START_ROUTINE\r
+       pw0 = DuplicateMtoW(lpPathName, -1);\r
+       r = CreateDirectoryW(pw0, lpSecurityAttributes);\r
+END_ROUTINE\r
+       FreeDuplicatedString(pw0);\r
+       return r;\r
+}\r
+\r
+BOOL RemoveDirectoryM(LPCSTR lpPathName)\r
+{\r
+       BOOL r = FALSE;\r
+       wchar_t* pw0 = NULL;\r
+START_ROUTINE\r
+       pw0 = DuplicateMtoW(lpPathName, -1);\r
+       r = RemoveDirectoryW(pw0);\r
+END_ROUTINE\r
+       FreeDuplicatedString(pw0);\r
+       return r;\r
+}\r
+\r
 int mkdirM(const char * _Path)\r
 {\r
        int r = -1;\r
index e6d3e15..65b0832 100644 (file)
@@ -182,6 +182,12 @@ BOOL MoveFileM(LPCSTR lpExistingFileName, LPCSTR lpNewFileName);
 #undef DeleteFile\r
 #define DeleteFile DeleteFileM\r
 BOOL DeleteFileM(LPCSTR lpFileName);\r
+#undef CreateDirectory\r
+#define CreateDirectory CreateDirectoryM\r
+BOOL CreateDirectoryM(LPCSTR lpPathName, LPSECURITY_ATTRIBUTES lpSecurityAttributes);\r
+#undef RemoveDirectory\r
+#define RemoveDirectory RemoveDirectoryM\r
+BOOL RemoveDirectoryM(LPCSTR lpPathName);\r
 #undef mkdir\r
 #define mkdir _mkdirM\r
 int mkdirM(const char * _Path);\r
index a3cc46f..8d24c3e 100644 (file)
 #define MSGJPN356              _Tu8("Move to &parent folder...", "Move to &parent folder...")\r
 #define MSGJPN357              _Tu8("XML file\0*.xml\0All file\0*\0", "XML file\0*.xml\0All file\0*\0")\r
 #define MSGJPN358              _Tu8("Failed to export the settings.\nPlease change saving path or format.", "Failed to export the settings.\nPlease change saving path or format.")\r
+#define MSGJPN359              _Tu8("Software update has been completed.", "Software update has been completed.")\r
+#define MSGJPN360              _Tu8("Failed to update the software.\nPlease get the latest version from our web site and update it manually.", "Failed to update the software.\nPlease get the latest version from our web site and update it manually.")\r
 #if defined(HAVE_TANDEM)\r
 #define MSGJPN2000             _Tu8("NonStop Server", "NonStop Server")\r
 #define MSGJPN2001             _Tu8("OSS<->GUARDIAN Switch(&O)", "OSS<->GUARDIAN Switch(&O)")\r
index 7ca27a2..7e9e3b2 100644 (file)
 #define MSGJPN356              _Tu8("一つ上のフォルダへ移動(&P)...", "\xE4\xB8\x80\xE3\x81\xA4\xE4\xB8\x8A\xE3\x81\xAE\xE3\x83\x95\xE3\x82\xA9\xE3\x83\xAB\xE3\x83\x80\xE3\x81\xB8\xE7\xA7\xBB\xE5\x8B\x95(&P)...")\r
 #define MSGJPN357              _Tu8("XMLファイル\0*.xml\0全てのファイル\0*\0", "XML\xE3\x83\x95\xE3\x82\xA1\xE3\x82\xA4\xE3\x83\xAB\0*.xml\0\xE5\x85\xA8\xE3\x81\xA6\xE3\x81\xAE\xE3\x83\x95\xE3\x82\xA1\xE3\x82\xA4\xE3\x83\xAB\0*\0")\r
 #define MSGJPN358              _Tu8("設定のエクスポートに失敗しました.\n保存する場所や形式を変更してください.", "\xE8\xA8\xAD\xE5\xAE\x9A\xE3\x81\xAE\xE3\x82\xA8\xE3\x82\xAF\xE3\x82\xB9\xE3\x83\x9D\xE3\x83\xBC\xE3\x83\x88\xE3\x81\xAB\xE5\xA4\xB1\xE6\x95\x97\xE3\x81\x97\xE3\x81\xBE\xE3\x81\x97\xE3\x81\x9F.\n\xE4\xBF\x9D\xE5\xAD\x98\xE3\x81\x99\xE3\x82\x8B\xE5\xA0\xB4\xE6\x89\x80\xE3\x82\x84\xE5\xBD\xA2\xE5\xBC\x8F\xE3\x82\x92\xE5\xA4\x89\xE6\x9B\xB4\xE3\x81\x97\xE3\x81\xA6\xE3\x81\x8F\xE3\x81\xA0\xE3\x81\x95\xE3\x81\x84.")\r
+#define MSGJPN359              _Tu8("ソフトウェアの更新が完了しました.", "\xE3\x82\xBD\xE3\x83\x95\xE3\x83\x88\xE3\x82\xA6\xE3\x82\xA7\xE3\x82\xA2\xE3\x81\xAE\xE6\x9B\xB4\xE6\x96\xB0\xE3\x81\x8C\xE5\xAE\x8C\xE4\xBA\x86\xE3\x81\x97\xE3\x81\xBE\xE3\x81\x97\xE3\x81\x9F.")\r
+#define MSGJPN360              _Tu8("ソフトウェアの更新に失敗しました.\nWebサイトから最新版を入手して手動で更新してください.", "\xE3\x82\xBD\xE3\x83\x95\xE3\x83\x88\xE3\x82\xA6\xE3\x82\xA7\xE3\x82\xA2\xE3\x81\xAE\xE6\x9B\xB4\xE6\x96\xB0\xE3\x81\xAB\xE5\xA4\xB1\xE6\x95\x97\xE3\x81\x97\xE3\x81\xBE\xE3\x81\x97\xE3\x81\x9F.\nWeb\xE3\x82\xB5\xE3\x82\xA4\xE3\x83\x88\xE3\x81\x8B\xE3\x82\x89\xE6\x9C\x80\xE6\x96\xB0\xE7\x89\x88\xE3\x82\x92\xE5\x85\xA5\xE6\x89\x8B\xE3\x81\x97\xE3\x81\xA6\xE6\x89\x8B\xE5\x8B\x95\xE3\x81\xA7\xE6\x9B\xB4\xE6\x96\xB0\xE3\x81\x97\xE3\x81\xA6\xE3\x81\x8F\xE3\x81\xA0\xE3\x81\x95\xE3\x81\x84.")\r
 #if defined(HAVE_TANDEM)\r
 #define MSGJPN2000             _Tu8("NonStop Server", "NonStop Server")\r
 #define MSGJPN2001             _Tu8("OSS<->GUARDIAN 切り替え(&O)", "OSS<->GUARDIAN \xE5\x88\x87\xE3\x82\x8A\xE6\x9B\xBF\xE3\x81\x88(&O)")\r
index 815996b..39cba78 100644 (file)
--- a/updater.c
+++ b/updater.c
@@ -1,6 +1,7 @@
 // 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
@@ -21,7 +22,26 @@ typedef struct
        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
@@ -57,17 +77,99 @@ BOOL DownloadFileViaHTTP(void* pOut, DWORD Length, DWORD* pLength, LPCWSTR UserA
        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
@@ -76,15 +178,23 @@ BOOL CheckForUpdates(BOOL bDownload, LPCTSTR DownloadDir)
                                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
@@ -98,9 +208,58 @@ BOOL CheckForUpdates(BOOL bDownload, LPCTSTR DownloadDir)
 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
@@ -108,8 +267,40 @@ BOOL PrepareUpdates(void* pList, DWORD ListLength, LPCTSTR DownloadDir)
 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
index deecaac..24ffd11 100644 (file)
--- a/updater.h
+++ b/updater.h
@@ -7,10 +7,15 @@
 \r
 #include <windows.h>\r
 \r
-#define HTTP_USER_AGENT L"Mozilla/4.0"\r
-#define UPDATE_SERVER L"ffftp.sourceforge.jp"\r
-#define UPDATE_HASH_PATH L"/update/hash"\r
-#define UPDATE_LIST_PATH L"/update/list"\r
+#define HTTP_USER_AGENT "Mozilla/4.0"\r
+#define UPDATE_SERVER "ffftp.sourceforge.jp"\r
+#if defined(_M_IX86)\r
+#define UPDATE_HASH_PATH "/update/hash"\r
+#define UPDATE_LIST_PATH "/update/list"\r
+#elif defined(_M_AMD64)\r
+#define UPDATE_HASH_PATH "/update/amd64/hash"\r
+#define UPDATE_LIST_PATH "/update/amd64/list"\r
+#endif\r
 #define UPDATE_RSA_PUBLIC_KEY \\r
        "-----BEGIN PUBLIC KEY-----\n" \\r
        "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsVo13yricPHxkQypqiMy\n" \\r
        "-----END PUBLIC KEY-----\n"\r
 #define UPDATE_SIGNATURE "\x4C\x2A\x8E\x57\xAB\x75\x0C\xB5\xDA\x5F\xFE\xB9\x57\x9A\x1B\xA2\x7A\x61\x32\xF8\xFA\x4B\x61\xE2\xBA\x20\x9C\x37\xD5\x0A\xDC\x94\x10\x4D\x02\x30\x9B\xCD\x01\x9B\xB8\x73\x1E\xDB\xFD\xD7\x45\xCA\xE0\x8E\xF9\xB0\x1F\xB4\x0D\xD8\xFB\xE8\x41\x48\xE7\xF5\xE8\x64"\r
 \r
-BOOL CheckForUpdates(BOOL bDownload, LPCTSTR DownloadDir);\r
+BOOL CheckForUpdates(BOOL bDownload, LPCTSTR DownloadDir, DWORD* pVersion, LPTSTR pVersionString);\r
 BOOL PrepareUpdates(void* pList, DWORD ListLength, LPCTSTR DownloadDir);\r
 BOOL ApplyUpdates(LPCTSTR DestinationDir);\r
+BOOL CleanupUpdates(LPCTSTR DownloadDir);\r
 BOOL StartUpdateProcess(LPCTSTR CommandLine, LPCTSTR Keyword);\r
 BOOL StartUpdateProcessAsAdministrator(LPCTSTR CommandLine, LPCTSTR Keyword);\r
 \r