From: Amine Khaldi Date: Sat, 3 Oct 2015 08:39:05 +0000 (+0000) Subject: [RAPPS] Fix SSL certificate pinning, allow HTTPS links, add URL args support, some... X-Git-Tag: ReactOS-0.4.0~618 X-Git-Url: https://git.reactos.org/?p=reactos.git;a=commitdiff_plain;h=161dd3557d5af871b13bdcbd33a38d4cb2f88015 [RAPPS] Fix SSL certificate pinning, allow HTTPS links, add URL args support, some UX fixes, comment improvements and fix stopping the download process when pressing cancel. By Ismael Ferreras Morezuelas. CORE-9060 CORE-10289 svn path=/trunk/; revision=69429 --- diff --git a/reactos/base/applications/rapps/loaddlg.c b/reactos/base/applications/rapps/loaddlg.c index 0a498989374..0bd95aeac07 100644 --- a/reactos/base/applications/rapps/loaddlg.c +++ b/reactos/base/applications/rapps/loaddlg.c @@ -29,6 +29,7 @@ #include "rapps.h" #include #include +#include static PAPPLICATION_INFO AppInfo; @@ -39,6 +40,7 @@ typedef struct _IBindStatusCallbackImpl HWND hDialog; BOOL *pbCancelled; BOOL UrlHasBeenCopied; + WCHAR ProgressText[MAX_PATH]; } IBindStatusCallbackImpl; static @@ -117,7 +119,20 @@ dlOnProgress(IBindStatusCallback* iface, Item = GetDlgItem(This->hDialog, IDC_DOWNLOAD_PROGRESS); if (Item && ulProgressMax) { - SendMessageW(Item, PBM_SETPOS, ((ULONGLONG)ulProgress * 100) / ulProgressMax, 0); + WCHAR szProgress[100]; + WCHAR szProgressMax[100]; + UINT uiPercentage = ((ULONGLONG)ulProgress * 100) / ulProgressMax; + + /* send the current progress to the progress bar */ + SendMessageW(Item, PBM_SETPOS, uiPercentage, 0); + + /* format the bits and bytes into pretty and accesible units... */ + StrFormatByteSizeW(ulProgress, szProgress, sizeof(szProgress)); + StrFormatByteSizeW(ulProgressMax, szProgressMax, sizeof(szProgressMax)); + + /* ...and post all of it to our subclassed progress bar text subroutine */ + swprintf(This->ProgressText, L"%u%% — %s / %s", uiPercentage, szProgress, szProgressMax); + SendMessageW(Item, WM_SETTEXT, 0, (LPARAM)This->ProgressText); } Item = GetDlgItem(This->hDialog, IDC_DOWNLOAD_STATUS); @@ -227,15 +242,15 @@ static BOOL CertIsValid(HINTERNET hInternet, LPWSTR lpszHostName) BOOL Ret = FALSE; INTERNET_CERTIFICATE_INFOW certInfo; - hConnect = InternetConnectW(hInternet, lpszHostName, INTERNET_DEFAULT_HTTPS_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, INTERNET_FLAG_SECURE, 0); + hConnect = InternetConnectW(hInternet, lpszHostName, INTERNET_DEFAULT_HTTPS_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, INTERNET_FLAG_SECURE, 0); if (hConnect) { hRequest = HttpOpenRequestW(hConnect, L"HEAD", NULL, NULL, NULL, NULL, INTERNET_FLAG_SECURE, 0); if (hRequest != NULL) - { + { Ret = HttpSendRequestW(hRequest, L"", 0, NULL, 0); - if (Ret) - { + if (Ret) + { certInfoLength = sizeof(INTERNET_CERTIFICATE_INFOW); Ret = InternetQueryOptionW(hRequest, INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT, @@ -243,7 +258,7 @@ static BOOL CertIsValid(HINTERNET hInternet, LPWSTR lpszHostName) &certInfoLength); if (Ret) { - if (certInfo.lpszEncryptionAlgName) + if (certInfo.lpszEncryptionAlgName) LocalFree(certInfo.lpszEncryptionAlgName); if (certInfo.lpszIssuerInfo) { @@ -251,11 +266,11 @@ static BOOL CertIsValid(HINTERNET hInternet, LPWSTR lpszHostName) Ret = FALSE; LocalFree(certInfo.lpszIssuerInfo); } - if (certInfo.lpszProtocolName) + if (certInfo.lpszProtocolName) LocalFree(certInfo.lpszProtocolName); - if (certInfo.lpszSignatureAlgName) + if (certInfo.lpszSignatureAlgName) LocalFree(certInfo.lpszSignatureAlgName); - if (certInfo.lpszSubjectInfo) + if (certInfo.lpszSubjectInfo) { if (strcmp((LPSTR)certInfo.lpszSubjectInfo, CERT_SUBJECT_INFO) != 0) Ret = FALSE; @@ -276,7 +291,7 @@ ThreadFunc(LPVOID Context) { IBindStatusCallback *dl = NULL; WCHAR path[MAX_PATH]; - LPWSTR p; + PWSTR p, q; HWND Dlg = (HWND) Context; DWORD dwContentLen, dwBytesWritten, dwBytesRead, dwStatus; DWORD dwCurrentBytesRead = 0; @@ -290,45 +305,58 @@ ThreadFunc(LPVOID Context) unsigned char lpBuffer[4096]; const LPWSTR lpszAgent = L"RApps/1.0"; URL_COMPONENTS urlComponents; - size_t urlLength; + size_t urlLength, filenameLength; - /* built the path for the download */ + /* build the path for the download */ p = wcsrchr(AppInfo->szUrlDownload, L'/'); + q = wcsrchr(AppInfo->szUrlDownload, L'?'); + /* do we have a final slash separator? */ if (!p) goto end; - if (wcscmp(AppInfo->szUrlDownload, APPLICATION_DATABASE_URL) == 0) - { - bCab = TRUE; - if (!GetStorageDirectory(path, sizeof(path) / sizeof(path[0]))) - goto end; - } - else - { - if (FAILED(StringCbCopyW(path, sizeof(path), SettingsInfo.szDownloadDir))) - goto end; - } + /* prepare the tentative length of the filename, maybe we've to remove part of it later on */ + filenameLength = wcslen(p) * sizeof(WCHAR); + /* do we have query arguments in the target URL after the filename? account for them + (e.g. https://example.org/myfile.exe?no_adware_plz) */ + if (q && q > p && (q - p) > 0) + filenameLength -= wcslen(q - 1) * sizeof(WCHAR); + /* is this URL an update package for RAPPS? if so store it in a different place */ + if (wcscmp(AppInfo->szUrlDownload, APPLICATION_DATABASE_URL) == 0) + { + bCab = TRUE; + if (!GetStorageDirectory(path, _countof(path))) + goto end; + } + else + { + if (FAILED(StringCbCopyW(path, sizeof(path), SettingsInfo.szDownloadDir))) + goto end; + } + + /* is the path valid? can we access it? */ if (GetFileAttributesW(path) == INVALID_FILE_ATTRIBUTES) { if (!CreateDirectoryW(path, NULL)) goto end; } + /* append a \ to the provided file system path, and the filename portion from the URL after that */ if (FAILED(StringCbCatW(path, sizeof(path), L"\\"))) goto end; - if (FAILED(StringCbCatW(path, sizeof(path), p + 1))) + if (FAILED(StringCbCatNW(path, sizeof(path), p + 1, filenameLength))) goto end; - /* download it */ + /* create an async download context for it */ bTempfile = TRUE; dl = CreateDl(Context, &bCancelled); if (dl == NULL) goto end; + /* FIXME: this should just be using the system-wide proxy settings */ switch(SettingsInfo.Proxy) { case 0: /* preconfig */ @@ -348,7 +376,7 @@ ThreadFunc(LPVOID Context) if (!hOpen) goto end; - hFile = InternetOpenUrlW(hOpen, AppInfo->szUrlDownload, NULL, 0, INTERNET_FLAG_PRAGMA_NOCACHE|INTERNET_FLAG_KEEP_CONNECTION, 0); + hFile = InternetOpenUrlW(hOpen, AppInfo->szUrlDownload, NULL, 0, INTERNET_FLAG_PRAGMA_NOCACHE | INTERNET_FLAG_KEEP_CONNECTION, 0); if (!hFile) goto end; @@ -373,15 +401,15 @@ ThreadFunc(LPVOID Context) if(FAILED(StringCbLengthW(AppInfo->szUrlDownload, sizeof(AppInfo->szUrlDownload), &urlLength))) goto end; - + urlComponents.dwSchemeLength = urlLength*sizeof(WCHAR); urlComponents.lpszScheme = malloc(urlComponents.dwSchemeLength); urlComponents.dwHostNameLength = urlLength*sizeof(WCHAR); urlComponents.lpszHostName = malloc(urlComponents.dwHostNameLength); - + if(!InternetCrackUrlW(AppInfo->szUrlDownload, urlLength+1, ICU_DECODE | ICU_ESCAPE, &urlComponents)) goto end; - + if(urlComponents.nScheme == INTERNET_SCHEME_HTTP || urlComponents.nScheme == INTERNET_SCHEME_HTTPS) HttpQueryInfo(hFile, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &dwContentLen, &dwStatus, 0); @@ -389,14 +417,17 @@ ThreadFunc(LPVOID Context) dwContentLen = FtpGetFileSize(hFile, &dwStatus); #ifdef USE_CERT_PINNING - if ((urlComponents.nScheme == INTERNET_SCHEME_HTTPS) && (!CertIsValid(hOpen, urlComponents.lpszHostName))) + /* are we using HTTPS to download the RAPPS update package? check if the certificate is original */ + if ((urlComponents.nScheme == INTERNET_SCHEME_HTTPS) && + (wcscmp(AppInfo->szUrlDownload, APPLICATION_DATABASE_URL) == 0) && + (!CertIsValid(hOpen, urlComponents.lpszHostName))) { WCHAR szMsgText[MAX_STR_LEN]; if (!LoadStringW(hInst, IDS_CERT_DOES_NOT_MATCH, szMsgText, sizeof(szMsgText) / sizeof(WCHAR))) goto end; - MessageBoxW(hMainWnd, szMsgText, NULL, MB_OK | MB_ICONERROR); + MessageBoxW(Dlg, szMsgText, NULL, MB_OK | MB_ICONERROR); goto end; } #endif @@ -416,7 +447,7 @@ ThreadFunc(LPVOID Context) dwCurrentBytesRead += dwBytesRead; IBindStatusCallback_OnProgress(dl, dwCurrentBytesRead, dwContentLen, 0, AppInfo->szUrlDownload); } - while (dwBytesRead); + while (dwBytesRead && !bCancelled); CloseHandle(hOut); hOut = INVALID_HANDLE_VALUE; @@ -451,15 +482,88 @@ end: return 0; } + +LRESULT CALLBACK +DownloadProgressProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) +{ + static WCHAR szProgressText[MAX_STR_LEN] = {0}; + + switch (uMsg) + { + case WM_SETTEXT: + { + if (lParam) + wcscpy(szProgressText, (WCHAR *)lParam); + } + + case WM_ERASEBKGND: + case WM_PAINT: + { + PAINTSTRUCT ps; + HDC hDC = BeginPaint(hWnd, &ps), hdcMem; + HBITMAP hbmMem; + HANDLE hOld; + RECT myRect; + UINT win_width, win_height; + + GetClientRect(hWnd, &myRect); + + /* grab the progress bar rect size */ + win_width = myRect.right - myRect.left; + win_height = myRect.bottom - myRect.top; + + /* create an off-screen DC for double-buffering */ + hdcMem = CreateCompatibleDC(hDC); + hbmMem = CreateCompatibleBitmap(hDC, win_width, win_height); + + hOld = SelectObject(hdcMem, hbmMem); + + /* call the original draw code and redirect it to our memory buffer */ + DefSubclassProc(hWnd, uMsg, (WPARAM)hdcMem, lParam); + + /* draw our nifty progress text over it */ + SelectFont(hdcMem, GetStockFont(DEFAULT_GUI_FONT)); + DrawShadowText(hdcMem, szProgressText, wcslen(szProgressText), + &myRect, + DT_CENTER | DT_VCENTER | DT_NOPREFIX | DT_SINGLELINE, + GetSysColor(COLOR_CAPTIONTEXT), + GetSysColor(COLOR_3DSHADOW), + 1, 1); + + /* transfer the off-screen DC to the screen */ + BitBlt(hDC, 0, 0, win_width, win_height, hdcMem, 0, 0, SRCCOPY); + + /* free the off-screen DC */ + SelectObject(hdcMem, hOld); + DeleteObject(hbmMem); + DeleteDC(hdcMem); + + EndPaint(hWnd, &ps); + return 0; + } + + /* Raymond Chen says that we should safely unsubclass all the things! + (http://blogs.msdn.com/b/oldnewthing/archive/2003/11/11/55653.aspx) */ + case WM_NCDESTROY: + { + ZeroMemory(szProgressText, MAX_STR_LEN); + RemoveWindowSubclass(hWnd, DownloadProgressProc, uIdSubclass); + } + + default: + return DefSubclassProc(hWnd, uMsg, wParam, lParam); + } +} + static INT_PTR CALLBACK -DownloadDlgProc(HWND Dlg, UINT Msg, WPARAM wParam, LPARAM lParam) +DownloadDlgProc(HWND Dlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { HANDLE Thread; DWORD ThreadId; HWND Item; - switch (Msg) + switch (uMsg) { case WM_INITDIALOG: { @@ -478,16 +582,25 @@ DownloadDlgProc(HWND Dlg, UINT Msg, WPARAM wParam, LPARAM lParam) Item = GetDlgItem(Dlg, IDC_DOWNLOAD_PROGRESS); if (Item) { + /* initialize the default values for our nifty progress bar + and subclass it so that it learns to print a status text */ SendMessageW(Item, PBM_SETRANGE, 0, MAKELPARAM(0, 100)); SendMessageW(Item, PBM_SETPOS, 0, 0); + + SetWindowSubclass(Item, DownloadProgressProc, 0, 0); } + /* add a neat placeholder until the download URL is retrieved */ + Item = GetDlgItem(Dlg, IDC_DOWNLOAD_STATUS); + SendMessageW(Item, WM_SETTEXT, 0, (LPARAM) L"• • •"); + Thread = CreateThread(NULL, 0, ThreadFunc, Dlg, 0, &ThreadId); - if (!Thread) return FALSE; + if (!Thread) + return FALSE; + CloseHandle(Thread); return TRUE; } - case WM_COMMAND: if (wParam == IDCANCEL) {