2 // Copyright (C) 2014 Suguru Kawamoto
\r
4 // コードの再利用のため表記はwchar_t型だが実体はchar型でUTF-8
\r
7 #include <ws2tcpip.h>
\r
9 #include <mmsystem.h>
\r
10 typedef SOCKADDR_STORAGE_XP SOCKADDR_STORAGE;
\r
11 typedef SOCKADDR_STORAGE *PSOCKADDR_STORAGE, FAR *LPSOCKADDR_STORAGE;
\r
12 #include <winhttp.h>
\r
14 #include "updater.h"
\r
15 #include "socketwrapper.h"
\r
16 #include "protectprocess.h"
\r
17 #include "mbswrapper.h"
\r
18 #include "apiemulator.h"
\r
26 #define UPDATE_LIST_FILE_FLAG_DIRECTORY 0x00000001
\r
40 CHAR VersionString[32];
\r
41 CHAR Description[1024];
\r
43 UPDATE_LIST_FILE File[1];
\r
46 #define UPDATE_MAX_LIST_SIZE 1048576
\r
47 #define UPDATE_MAX_FILE_SIZE 16777216
\r
49 BOOL ReadFileViaHTTPW(void* pOut, DWORD Length, DWORD* pLength, LPCWSTR UserAgent, LPCWSTR ServerName, LPCWSTR ObjectName)
\r
57 WINHTTP_AUTOPROXY_OPTIONS AutoProxyOptions;
\r
58 WINHTTP_PROXY_INFO ProxyInfo;
\r
59 WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ProxyConfig;
\r
61 if(hSession = WinHttpOpen(UserAgent, WINHTTP_ACCESS_TYPE_NO_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0))
\r
63 Buffer = WINHTTP_OPTION_REDIRECT_POLICY_ALWAYS;
\r
64 if(WinHttpSetOption(hSession, WINHTTP_OPTION_REDIRECT_POLICY, &Buffer, sizeof(DWORD)))
\r
66 if(hConnect = WinHttpConnect(hSession, ServerName, INTERNET_DEFAULT_HTTP_PORT, 0))
\r
68 if(hRequest = WinHttpOpenRequest(hConnect, L"GET", ObjectName, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0))
\r
70 if(Url = (WCHAR*)malloc(sizeof(WCHAR) * (wcslen(L"http://") + wcslen(ServerName) + wcslen(ObjectName) + 1)))
\r
72 wcscpy(Url, L"http://");
\r
73 wcscat(Url, ServerName);
\r
74 wcscat(Url, ObjectName);
\r
76 memset(&AutoProxyOptions, 0, sizeof(WINHTTP_AUTOPROXY_OPTIONS));
\r
77 AutoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT;
\r
78 AutoProxyOptions.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A;
\r
79 AutoProxyOptions.fAutoLogonIfChallenged = TRUE;
\r
80 memset(&ProxyInfo, 0, sizeof(WINHTTP_PROXY_INFO));
\r
81 memset(&ProxyConfig, 0, sizeof(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG));
\r
82 if(WinHttpGetProxyForUrl(hSession, Url, &AutoProxyOptions, &ProxyInfo))
\r
84 WinHttpSetOption(hRequest, WINHTTP_OPTION_PROXY, &ProxyInfo, sizeof(WINHTTP_PROXY_INFO));
\r
85 if(ProxyInfo.lpszProxy)
\r
86 GlobalFree(ProxyInfo.lpszProxy);
\r
87 if(ProxyInfo.lpszProxyBypass)
\r
88 GlobalFree(ProxyInfo.lpszProxyBypass);
\r
90 else if(WinHttpGetIEProxyConfigForCurrentUser(&ProxyConfig))
\r
92 if(ProxyConfig.lpszAutoConfigUrl)
\r
94 memset(&AutoProxyOptions, 0, sizeof(WINHTTP_AUTOPROXY_OPTIONS));
\r
95 AutoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
\r
96 AutoProxyOptions.lpszAutoConfigUrl = ProxyConfig.lpszAutoConfigUrl;
\r
97 AutoProxyOptions.fAutoLogonIfChallenged = TRUE;
\r
98 memset(&ProxyInfo, 0, sizeof(WINHTTP_PROXY_INFO));
\r
99 if(WinHttpGetProxyForUrl(hSession, Url, &AutoProxyOptions, &ProxyInfo))
\r
101 WinHttpSetOption(hRequest, WINHTTP_OPTION_PROXY, &ProxyInfo, sizeof(WINHTTP_PROXY_INFO));
\r
102 if(ProxyInfo.lpszProxy)
\r
103 GlobalFree(ProxyInfo.lpszProxy);
\r
104 if(ProxyInfo.lpszProxyBypass)
\r
105 GlobalFree(ProxyInfo.lpszProxyBypass);
\r
108 else if(ProxyConfig.lpszProxy)
\r
110 memset(&ProxyInfo, 0, sizeof(WINHTTP_PROXY_INFO));
\r
111 ProxyInfo.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
\r
112 ProxyInfo.lpszProxy = ProxyConfig.lpszProxy;
\r
113 ProxyInfo.lpszProxyBypass = ProxyConfig.lpszProxyBypass;
\r
114 WinHttpSetOption(hRequest, WINHTTP_OPTION_PROXY, &ProxyInfo, sizeof(WINHTTP_PROXY_INFO));
\r
116 if(ProxyConfig.lpszAutoConfigUrl)
\r
117 GlobalFree(ProxyConfig.lpszAutoConfigUrl);
\r
118 if(ProxyConfig.lpszProxy)
\r
119 GlobalFree(ProxyConfig.lpszProxy);
\r
120 if(ProxyConfig.lpszProxyBypass)
\r
121 GlobalFree(ProxyConfig.lpszProxyBypass);
\r
125 if(WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0))
\r
127 if(WinHttpReceiveResponse(hRequest, NULL))
\r
129 if(WinHttpQueryDataAvailable(hRequest, pLength))
\r
131 if(*pLength <= Length)
\r
133 if(WinHttpReadData(hRequest, pOut, Length, pLength))
\r
139 WinHttpCloseHandle(hRequest);
\r
141 WinHttpCloseHandle(hConnect);
\r
144 WinHttpCloseHandle(hSession);
\r
149 BOOL ReadFileViaHTTP(void* pOut, DWORD Length, DWORD* pLength, LPCSTR UserAgent, LPCSTR ServerName, LPCSTR ObjectName)
\r
155 pw0 = DuplicateMtoW(UserAgent, -1);
\r
156 pw1 = DuplicateMtoW(ServerName, -1);
\r
157 pw2 = DuplicateMtoW(ObjectName, -1);
\r
158 r = ReadFileViaHTTPW(pOut, Length, pLength, pw0, pw1, pw2);
\r
159 FreeDuplicatedString(pw0);
\r
160 FreeDuplicatedString(pw1);
\r
161 FreeDuplicatedString(pw2);
\r
165 BOOL SaveMemoryToFileWithTimestamp(LPCTSTR FileName, void* pData, DWORD Size, FILETIME* pTimestamp)
\r
170 if((hFile = CreateFile(FileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE)
\r
172 if(WriteFile(hFile, pData, Size, &Size, NULL))
\r
176 if(SetFileTime(hFile, NULL, NULL, pTimestamp))
\r
182 CloseHandle(hFile);
\r
187 BOOL LoadMemoryFromFileWithTimestamp(LPCTSTR FileName, void* pData, DWORD Size, DWORD* pReadSize, FILETIME* pTimestamp)
\r
193 if((hFile = CreateFile(FileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE)
\r
195 if(GetFileSizeEx(hFile, &li))
\r
197 if(li.QuadPart <= (LONGLONG)Size)
\r
199 if(ReadFile(hFile, pData, Size, pReadSize, NULL))
\r
201 if(*pReadSize == li.LowPart)
\r
205 if(GetFileTime(hFile, NULL, NULL, pTimestamp))
\r
213 CloseHandle(hFile);
\r
219 BOOL CopyAllFilesInDirectory(LPCTSTR From, LPCTSTR To)
\r
224 SHFILEOPSTRUCT fop;
\r
226 if(pFrom = (TCHAR*)malloc(sizeof(TCHAR) * (_tcslen(From) + _tcslen(_T("\\*")) + 2)))
\r
228 _tcscpy(pFrom, From);
\r
229 _tcsncpy(pFrom + _tcslen(pFrom), _T("\\*"), _tcslen(_T("\\*")) + 2);
\r
230 if(pTo = (TCHAR*)malloc(sizeof(TCHAR) * (_tcslen(To) + 2)))
\r
232 _tcsncpy(pTo, To, _tcslen(To) + 2);
\r
233 memset(&fop, 0, sizeof(SHFILEOPSTRUCT));
\r
234 fop.wFunc = FO_COPY;
\r
237 fop.fFlags = FOF_NO_UI;
\r
238 if(SHFileOperation(&fop) == 0)
\r
247 BOOL DeleteDirectoryAndContents(LPCTSTR Path)
\r
251 SHFILEOPSTRUCT fop;
\r
253 if(pFrom = (TCHAR*)malloc(sizeof(TCHAR) * (_tcslen(Path) + 2)))
\r
255 _tcsncpy(pFrom, Path, _tcslen(Path) + 2);
\r
256 memset(&fop, 0, sizeof(SHFILEOPSTRUCT));
\r
257 fop.wFunc = FO_DELETE;
\r
259 fop.fFlags = FOF_NO_UI;
\r
260 if(SHFileOperation(&fop) == 0)
\r
267 DWORD ListUpdateFile(UPDATE_LIST* pList, DWORD MaxCount, LPCTSTR ServerPath, LPCTSTR ReferenceDir, LPCTSTR Path)
\r
270 TCHAR Temp1[MAX_PATH];
\r
271 TCHAR Temp2[MAX_PATH];
\r
272 TCHAR Temp3[MAX_PATH];
\r
274 WIN32_FIND_DATA Find;
\r
282 if(_tcslen(ReferenceDir) + _tcslen(Path) + _tcslen(_T("\\*")) < MAX_PATH)
\r
284 _tcscpy(Temp1, ReferenceDir);
\r
285 _tcscat(Temp1, Path);
\r
286 _tcscat(Temp1, _T("\\*"));
\r
287 if((hFind = FindFirstFile(Temp1, &Find)) != INVALID_HANDLE_VALUE)
\r
291 if(_tcscmp(Find.cFileName, _T(".")) != 0 && _tcscmp(Find.cFileName, _T("..")) != 0)
\r
293 // if(_tcslen(ServerPath) + _tcslen(_T("/")) + _tcslen(Find.cFileName) < 128 && _tcslen(Path) + _tcslen(_T("\\")) + _tcslen(Find.cFileName) < 128)
\r
294 if(_tcslen(ServerPath) + _tcslen(Find.cFileName) < 128 && _tcslen(Path) + _tcslen(_T("\\")) + _tcslen(Find.cFileName) < 128)
\r
296 _tcscpy(Temp1, ServerPath);
\r
297 // _tcscat(Temp1, _T("/"));
\r
298 _tcscat(Temp1, Find.cFileName);
\r
299 _tcscpy(Temp2, Path);
\r
300 _tcscat(Temp2, _T("\\"));
\r
301 _tcscat(Temp2, Find.cFileName);
\r
302 if(_tcslen(ReferenceDir) + _tcslen(Temp2) < MAX_PATH)
\r
304 _tcscpy(Temp3, ReferenceDir);
\r
305 _tcscat(Temp3, Temp2);
\r
306 if((Find.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
\r
308 if(!(Find.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
\r
312 memset(&pList->File[pList->FileCount], 0, sizeof(UPDATE_LIST_FILE));
\r
313 pList->File[pList->FileCount].Flags = UPDATE_LIST_FILE_FLAG_DIRECTORY;
\r
314 _tcscpy(pList->File[pList->FileCount].DstPath, Temp2);
\r
315 pList->FileCount++;
\r
318 if(Result >= MaxCount)
\r
320 Result += ListUpdateFile(pList, MaxCount, Temp1, ReferenceDir, Temp2);
\r
327 if(pBuf = malloc(UPDATE_MAX_FILE_SIZE))
\r
329 if(LoadMemoryFromFileWithTimestamp(Temp3, pBuf, UPDATE_MAX_FILE_SIZE, &Length, &Time))
\r
331 if(GetHashSHA512(pBuf, Length, &Hash))
\r
333 memset(&pList->File[pList->FileCount], 0, sizeof(UPDATE_LIST_FILE));
\r
334 _tcscpy(pList->File[pList->FileCount].SrcPath, Temp1);
\r
335 memcpy(&pList->File[pList->FileCount].SrcHash, &Hash, 64);
\r
336 _tcscpy(pList->File[pList->FileCount].DstPath, Temp2);
\r
337 pList->File[pList->FileCount].Timestamp = Time;
\r
338 pList->FileCount++;
\r
345 if(Result >= MaxCount)
\r
352 while(FindNextFile(hFind, &Find));
\r
360 BOOL BuildUpdates(LPCTSTR PrivateKeyFile, LPCTSTR Password, LPCTSTR ServerPath, LPCTSTR HashFile, LPCTSTR ListFile, DWORD Version, LPCTSTR VersionString, LPCTSTR Description)
\r
363 char PrivateKey[4096];
\r
365 TCHAR Name[MAX_PATH];
\r
367 UPDATE_LIST* pList;
\r
371 memset(PrivateKey, 0, sizeof(PrivateKey));
\r
372 if(LoadMemoryFromFileWithTimestamp(PrivateKeyFile, &PrivateKey, sizeof(PrivateKey) - sizeof(char), &Length, NULL))
\r
374 if(GetModuleFileName(NULL, Name, MAX_PATH) > 0)
\r
376 if(p = _tcsrchr(Name, _T('\\')))
\r
378 if(pList = (UPDATE_LIST*)malloc(UPDATE_MAX_LIST_SIZE))
\r
380 memset(pList, 0, UPDATE_MAX_LIST_SIZE);
\r
381 pList->Version = Version;
\r
382 _tcscpy(pList->VersionString, VersionString);
\r
383 _tcscpy(pList->Description, Description);
\r
384 ListUpdateFile(pList, (UPDATE_MAX_LIST_SIZE - sizeof(UPDATE_LIST)) / sizeof(UPDATE_LIST_FILE) + 1, ServerPath, Name, NULL);
\r
385 Length = (pList->FileCount - 1) * sizeof(UPDATE_LIST_FILE) + sizeof(UPDATE_LIST);
\r
386 if(SaveMemoryToFileWithTimestamp(ListFile, pList, Length, NULL))
\r
388 memcpy(&Hash.Signature, UPDATE_SIGNATURE, 64);
\r
389 if(GetHashSHA512(pList, Length, &Hash.ListHash))
\r
391 if(EncryptSignature(PrivateKey, Password, &Hash, sizeof(UPDATE_HASH), &Buf, sizeof(Buf), &Length))
\r
393 if(SaveMemoryToFileWithTimestamp(HashFile, &Buf, Length, NULL))
\r
406 BOOL CheckForUpdates(BOOL bDownload, LPCTSTR DownloadDir, DWORD* pVersion, LPTSTR pVersionString, LPTSTR pDescription)
\r
413 UPDATE_HASH UpdateHash;
\r
415 UPDATE_LIST* pUpdateList;
\r
417 if(ReadFileViaHTTP(&Buf1, sizeof(Buf1), &Length, HTTP_USER_AGENT, UPDATE_SERVER, UPDATE_HASH_PATH))
\r
419 if(DecryptSignature(UPDATE_RSA_PUBLIC_KEY, NULL, &Buf1, Length, &Buf2, sizeof(Buf2), &Length))
\r
421 if(Length == sizeof(UPDATE_HASH))
\r
423 memcpy(&UpdateHash, &Buf2, sizeof(UPDATE_HASH));
\r
424 if(memcmp(&UpdateHash.Signature, UPDATE_SIGNATURE, 64) == 0)
\r
426 if(pBuf = malloc(UPDATE_MAX_LIST_SIZE))
\r
428 if(ReadFileViaHTTP(pBuf, UPDATE_MAX_LIST_SIZE, &Length, HTTP_USER_AGENT, UPDATE_SERVER, UPDATE_LIST_PATH))
\r
430 if(GetHashSHA512(pBuf, Length, &Hash))
\r
432 if(memcmp(&Hash, &UpdateHash.ListHash, 64) == 0)
\r
434 if(Length >= sizeof(UPDATE_LIST))
\r
437 pUpdateList = (UPDATE_LIST*)pBuf;
\r
438 if(pUpdateList->Version > *pVersion)
\r
440 *pVersion = pUpdateList->Version;
\r
441 _tcscpy(pVersionString, pUpdateList->VersionString);
\r
442 _tcscpy(pDescription, pUpdateList->Description);
\r
445 bResult = PrepareUpdates(pBuf, Length, DownloadDir);
\r
460 BOOL PrepareUpdates(void* pList, DWORD ListLength, LPCTSTR DownloadDir)
\r
463 UPDATE_LIST* pUpdateList;
\r
465 TCHAR LocalDir[MAX_PATH];
\r
469 TCHAR Path[MAX_PATH];
\r
473 if(ListLength >= sizeof(UPDATE_LIST))
\r
475 pUpdateList = (UPDATE_LIST*)pList;
\r
476 if((pUpdateList->FileCount - 1) * sizeof(UPDATE_LIST_FILE) + sizeof(UPDATE_LIST) >= ListLength)
\r
479 DeleteDirectoryAndContents(DownloadDir);
\r
480 CreateDirectory(DownloadDir, NULL);
\r
481 if(pBuf = malloc(UPDATE_MAX_FILE_SIZE))
\r
483 if(GetModuleFileName(NULL, LocalDir, MAX_PATH) > 0)
\r
485 if(p = _tcsrchr(LocalDir, _T('\\')))
\r
489 _tcscpy(LocalDir, _T("."));
\r
490 for(i = 0; i < pUpdateList->FileCount; i++)
\r
493 if(pUpdateList->File[i].Flags & UPDATE_LIST_FILE_FLAG_DIRECTORY)
\r
495 _tcscpy(Path, DownloadDir);
\r
496 _tcscat(Path, pUpdateList->File[i].DstPath);
\r
497 if(CreateDirectory(Path, NULL))
\r
500 if(strlen(pUpdateList->File[i].SrcPath) > 0)
\r
502 _tcscpy(Path, LocalDir);
\r
503 _tcscat(Path, pUpdateList->File[i].DstPath);
\r
504 if(LoadMemoryFromFileWithTimestamp(Path, pBuf, UPDATE_MAX_FILE_SIZE, &Length, NULL))
\r
506 if(GetHashSHA512(pBuf, Length, &Hash))
\r
508 if(memcmp(&Hash, &pUpdateList->File[i].SrcHash, 64) == 0)
\r
514 if(ReadFileViaHTTP(pBuf, UPDATE_MAX_FILE_SIZE, &Length, HTTP_USER_AGENT, UPDATE_SERVER, pUpdateList->File[i].SrcPath))
\r
516 if(GetHashSHA512(pBuf, Length, &Hash))
\r
518 if(memcmp(&Hash, &pUpdateList->File[i].SrcHash, 64) == 0)
\r
520 _tcscpy(Path, DownloadDir);
\r
521 _tcscat(Path, pUpdateList->File[i].DstPath);
\r
522 if(SaveMemoryToFileWithTimestamp(Path, pBuf, Length, &pUpdateList->File[i].Timestamp))
\r
543 BOOL ApplyUpdates(LPCTSTR DestinationDir, LPCTSTR BackupDirName)
\r
546 TCHAR Source[MAX_PATH];
\r
547 TCHAR Backup[MAX_PATH];
\r
548 TCHAR DestinationBackup[MAX_PATH];
\r
551 if(GetModuleFileName(NULL, Source, MAX_PATH) > 0)
\r
553 if(p = _tcsrchr(Source, _T('\\')))
\r
555 _tcscpy(Backup, Source);
\r
556 _tcscat(Backup, _T("\\"));
\r
557 _tcscat(Backup, BackupDirName);
\r
558 DeleteDirectoryAndContents(Backup);
\r
559 if(CreateDirectory(Backup, NULL))
\r
561 if(CopyAllFilesInDirectory(DestinationDir, Backup))
\r
563 _tcscpy(DestinationBackup, DestinationDir);
\r
564 _tcscat(DestinationBackup, _T("\\"));
\r
565 _tcscat(DestinationBackup, BackupDirName);
\r
566 if(CopyAllFilesInDirectory(Source, DestinationDir))
\r
568 DeleteDirectoryAndContents(DestinationBackup);
\r
573 DeleteDirectoryAndContents(DestinationBackup);
\r
574 CopyAllFilesInDirectory(Backup, DestinationDir);
\r
583 BOOL CleanupUpdates(LPCTSTR DownloadDir)
\r
587 if(DeleteDirectoryAndContents(DownloadDir))
\r
593 BOOL StartUpdateProcess(LPCTSTR DownloadDir, LPCTSTR CommandLine)
\r
596 TCHAR Name[MAX_PATH];
\r
598 TCHAR Path[MAX_PATH];
\r
600 if(GetModuleFileName(NULL, Name, MAX_PATH) > 0)
\r
602 if(p = _tcsrchr(Name, _T('\\')))
\r
606 _tcscpy(Path, DownloadDir);
\r
607 _tcscat(Path, _T("\\"));
\r
609 if(ShellExecute(NULL, _T("open"), Path, CommandLine, NULL, SW_SHOW) > (HINSTANCE)32)
\r
615 // 更新用のプロセスを管理者権限で起動
\r
616 // Windows XP以前など起動できない場合は現在のプロセスで処理を続行
\r
617 BOOL RestartUpdateProcessAsAdministrator(LPCTSTR CommandLine, LPCTSTR Keyword)
\r
620 TCHAR* NewCommandLine;
\r
621 TCHAR Path[MAX_PATH];
\r
622 SHELLEXECUTEINFO Info;
\r
624 if(_tcslen(CommandLine) < _tcslen(Keyword) || _tcscmp(CommandLine + _tcslen(CommandLine) - _tcslen(Keyword), Keyword) != 0)
\r
626 if(NewCommandLine = (TCHAR*)malloc(sizeof(TCHAR) * (_tcslen(CommandLine) + _tcslen(Keyword) + 1)))
\r
628 _tcscpy(NewCommandLine, CommandLine);
\r
629 _tcscat(NewCommandLine, Keyword);
\r
630 if(GetModuleFileName(NULL, Path, MAX_PATH) > 0)
\r
632 memset(&Info, 0, sizeof(SHELLEXECUTEINFO));
\r
633 Info.cbSize = sizeof(SHELLEXECUTEINFO);
\r
634 Info.fMask = SEE_MASK_NOCLOSEPROCESS;
\r
635 if(IsUserAnAdmin())
\r
636 Info.lpVerb = _T("open");
\r
638 Info.lpVerb = _T("runas");
\r
639 Info.lpFile = Path;
\r
640 Info.lpParameters = NewCommandLine;
\r
641 Info.nShow = SW_SHOW;
\r
642 if(ShellExecuteEx(&Info))
\r
644 WaitForSingleObject(Info.hProcess, INFINITE);
\r
645 CloseHandle(Info.hProcess);
\r
649 free(NewCommandLine);
\r