1 /* 2 * PROJECT: ReactOS Applications Manager 3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) 4 * FILE: base/applications/rapps/loaddlg.cpp 5 * PURPOSE: Displaying a download dialog 6 * COPYRIGHT: Copyright 2001 John R. Sheets (for CodeWeavers) 7 * Copyright 2004 Mike McCormack (for CodeWeavers) 8 * Copyright 2005 Ge van Geldorp (gvg@reactos.org) 9 * Copyright 2009 Dmitry Chapyshev (dmitry@reactos.org) 10 * Copyright 2015 Ismael Ferreras Morezuelas (swyterzone+ros@gmail.com) 11 * Copyright 2017 Alexander Shaposhnikov (sanchaez@reactos.org) 12 */ 13 14 /* 15 * Based on Wine dlls/shdocvw/shdocvw_main.c 16 * 17 * This library is free software; you can redistribute it and/or 18 * modify it under the terms of the GNU Lesser General Public 19 * License as published by the Free Software Foundation; either 20 * version 2.1 of the License, or (at your option) any later version. 21 * 22 * This library is distributed in the hope that it will be useful, 23 * but WITHOUT ANY WARRANTY; without even the implied warranty of 24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 25 * Lesser General Public License for more details. 26 * 27 * You should have received a copy of the GNU Lesser General Public 28 * License along with this library; if not, write to the Free Software 29 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 30 */ 31 #include "rapps.h" 32 33 #include <shlobj_undoc.h> 34 #include <shlguid_undoc.h> 35 36 #include <atlbase.h> 37 #include <atlcom.h> 38 #include <atlwin.h> 39 #include <wininet.h> 40 #include <shellutils.h> 41 42 #include <rosctrls.h> 43 #include <windowsx.h> 44 #undef SubclassWindow 45 46 #include "rosui.h" 47 #include "dialogs.h" 48 #include "misc.h" 49 50 #ifdef USE_CERT_PINNING 51 #define CERT_ISSUER_INFO "US\r\nLet's Encrypt\r\nLet's Encrypt Authority X3" 52 #define CERT_SUBJECT_INFO "rapps.reactos.org" 53 #endif 54 55 enum DownloadStatus 56 { 57 DLSTATUS_WAITING = IDS_STATUS_WAITING, 58 DLSTATUS_DOWNLOADING = IDS_STATUS_DOWNLOADING, 59 DLSTATUS_WAITING_INSTALL = IDS_STATUS_DOWNLOADED, 60 DLSTATUS_INSTALLING = IDS_STATUS_INSTALLING, 61 DLSTATUS_INSTALLED = IDS_STATUS_INSTALLED, 62 DLSTATUS_FINISHED = IDS_STATUS_FINISHED 63 }; 64 65 ATL::CStringW LoadStatusString(DownloadStatus StatusParam) 66 { 67 ATL::CStringW szString; 68 szString.LoadStringW(StatusParam); 69 return szString; 70 } 71 72 struct DownloadInfo 73 { 74 DownloadInfo() {} 75 DownloadInfo(const CAvailableApplicationInfo& AppInfo) 76 :szUrl(AppInfo.m_szUrlDownload), szName(AppInfo.m_szName), szSHA1(AppInfo.m_szSHA1) 77 { 78 } 79 80 ATL::CStringW szUrl; 81 ATL::CStringW szName; 82 ATL::CStringW szSHA1; 83 }; 84 85 struct DownloadParam 86 { 87 DownloadParam() : Dialog(NULL), AppInfo(), szCaption(NULL) {} 88 DownloadParam(HWND dlg, const ATL::CSimpleArray<DownloadInfo> &info, LPCWSTR caption) 89 : Dialog(dlg), AppInfo(info), szCaption(caption) 90 { 91 } 92 93 HWND Dialog; 94 ATL::CSimpleArray<DownloadInfo> AppInfo; 95 LPCWSTR szCaption; 96 }; 97 98 99 class CDownloaderProgress 100 : public CWindowImpl<CDownloaderProgress, CWindow, CControlWinTraits> 101 { 102 ATL::CStringW m_szProgressText; 103 104 public: 105 CDownloaderProgress() 106 { 107 } 108 109 VOID SetMarquee(BOOL Enable) 110 { 111 if (Enable) 112 ModifyStyle(0, PBS_MARQUEE, 0); 113 else 114 ModifyStyle(PBS_MARQUEE, 0, 0); 115 116 SendMessage(PBM_SETMARQUEE, Enable, 0); 117 } 118 119 VOID SetProgress(ULONG ulProgress, ULONG ulProgressMax) 120 { 121 WCHAR szProgress[100]; 122 123 /* format the bits and bytes into pretty and accessible units... */ 124 StrFormatByteSizeW(ulProgress, szProgress, _countof(szProgress)); 125 126 /* use our subclassed progress bar text subroutine */ 127 ATL::CStringW ProgressText; 128 129 if (ulProgressMax) 130 { 131 /* total size is known */ 132 WCHAR szProgressMax[100]; 133 UINT uiPercentage = ((ULONGLONG) ulProgress * 100) / ulProgressMax; 134 135 /* send the current progress to the progress bar */ 136 SendMessage(PBM_SETPOS, uiPercentage, 0); 137 138 /* format total download size */ 139 StrFormatByteSizeW(ulProgressMax, szProgressMax, _countof(szProgressMax)); 140 141 /* generate the text on progress bar */ 142 ProgressText.Format(L"%u%% \x2014 %ls / %ls", 143 uiPercentage, 144 szProgress, 145 szProgressMax); 146 } 147 else 148 { 149 /* send the current progress to the progress bar */ 150 SendMessage(PBM_SETPOS, 0, 0); 151 152 /* total size is not known, display only current size */ 153 ProgressText.Format(L"%ls...", szProgress); 154 } 155 156 /* and finally display it */ 157 SendMessage(WM_SETTEXT, 0, (LPARAM) ProgressText.GetString()); 158 } 159 160 LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) 161 { 162 PAINTSTRUCT ps; 163 HDC hDC = BeginPaint(&ps), hdcMem; 164 HBITMAP hbmMem; 165 HANDLE hOld; 166 RECT myRect; 167 UINT win_width, win_height; 168 169 GetClientRect(&myRect); 170 171 /* grab the progress bar rect size */ 172 win_width = myRect.right - myRect.left; 173 win_height = myRect.bottom - myRect.top; 174 175 /* create an off-screen DC for double-buffering */ 176 hdcMem = CreateCompatibleDC(hDC); 177 hbmMem = CreateCompatibleBitmap(hDC, win_width, win_height); 178 179 hOld = SelectObject(hdcMem, hbmMem); 180 181 /* call the original draw code and redirect it to our memory buffer */ 182 DefWindowProc(uMsg, (WPARAM) hdcMem, lParam); 183 184 /* draw our nifty progress text over it */ 185 SelectFont(hdcMem, GetStockFont(DEFAULT_GUI_FONT)); 186 DrawShadowText(hdcMem, m_szProgressText.GetString(), m_szProgressText.GetLength(), 187 &myRect, 188 DT_CENTER | DT_VCENTER | DT_NOPREFIX | DT_SINGLELINE, 189 GetSysColor(COLOR_CAPTIONTEXT), 190 GetSysColor(COLOR_3DSHADOW), 191 1, 1); 192 193 /* transfer the off-screen DC to the screen */ 194 BitBlt(hDC, 0, 0, win_width, win_height, hdcMem, 0, 0, SRCCOPY); 195 196 /* free the off-screen DC */ 197 SelectObject(hdcMem, hOld); 198 DeleteObject(hbmMem); 199 DeleteDC(hdcMem); 200 201 EndPaint(&ps); 202 return 0; 203 } 204 205 LRESULT OnSetText(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) 206 { 207 if (lParam) 208 { 209 m_szProgressText = (PCWSTR) lParam; 210 } 211 return 0; 212 } 213 214 BEGIN_MSG_MAP(CDownloaderProgress) 215 MESSAGE_HANDLER(WM_ERASEBKGND, OnPaint) 216 MESSAGE_HANDLER(WM_PAINT, OnPaint) 217 MESSAGE_HANDLER(WM_SETTEXT, OnSetText) 218 END_MSG_MAP() 219 }; 220 221 class CDowloadingAppsListView 222 : public CListView 223 { 224 public: 225 HWND Create(HWND hwndParent) 226 { 227 RECT r = {10, 150, 320, 350}; 228 const DWORD style = WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_SINGLESEL 229 | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | LVS_NOCOLUMNHEADER; 230 231 HWND hwnd = CListView::Create(hwndParent, r, NULL, style, WS_EX_CLIENTEDGE); 232 233 AddColumn(0, 150, LVCFMT_LEFT); 234 AddColumn(1, 120, LVCFMT_LEFT); 235 236 return hwnd; 237 } 238 239 VOID LoadList(ATL::CSimpleArray<DownloadInfo> arrInfo) 240 { 241 for (INT i = 0; i < arrInfo.GetSize(); ++i) 242 { 243 AddRow(i, arrInfo[i].szName.GetString(), DLSTATUS_WAITING); 244 } 245 } 246 247 VOID SetDownloadStatus(INT ItemIndex, DownloadStatus Status) 248 { 249 ATL::CStringW szBuffer = LoadStatusString(Status); 250 SetItemText(ItemIndex, 1, szBuffer.GetString()); 251 } 252 253 BOOL AddItem(INT ItemIndex, LPWSTR lpText) 254 { 255 LVITEMW Item; 256 257 ZeroMemory(&Item, sizeof(Item)); 258 259 Item.mask = LVIF_TEXT | LVIF_STATE; 260 Item.pszText = lpText; 261 Item.iItem = ItemIndex; 262 263 return InsertItem(&Item); 264 } 265 266 VOID AddRow(INT RowIndex, LPCWSTR szAppName, const DownloadStatus Status) 267 { 268 ATL::CStringW szStatus = LoadStatusString(Status); 269 AddItem(RowIndex, 270 const_cast<LPWSTR>(szAppName)); 271 SetDownloadStatus(RowIndex, Status); 272 } 273 274 BOOL AddColumn(INT Index, INT Width, INT Format) 275 { 276 LVCOLUMNW Column; 277 ZeroMemory(&Column, sizeof(Column)); 278 279 Column.mask = LVCF_FMT | LVCF_WIDTH | LVCF_SUBITEM; 280 Column.iSubItem = Index; 281 Column.cx = Width; 282 Column.fmt = Format; 283 284 return (InsertColumn(Index, &Column) == -1) ? FALSE : TRUE; 285 } 286 }; 287 288 #ifdef USE_CERT_PINNING 289 typedef CHeapPtr<char, CLocalAllocator> CLocalPtr; 290 291 static BOOL CertGetSubjectAndIssuer(HINTERNET hFile, CLocalPtr& subjectInfo, CLocalPtr& issuerInfo) 292 { 293 DWORD certInfoLength; 294 INTERNET_CERTIFICATE_INFOA certInfo; 295 DWORD size, flags; 296 297 size = sizeof(flags); 298 if (!InternetQueryOptionA(hFile, INTERNET_OPTION_SECURITY_FLAGS, &flags, &size)) 299 { 300 return FALSE; 301 } 302 303 if (!flags & SECURITY_FLAG_SECURE) 304 { 305 return FALSE; 306 } 307 308 /* Despite what the header indicates, the implementation of INTERNET_CERTIFICATE_INFO is not Unicode-aware. */ 309 certInfoLength = sizeof(certInfo); 310 if (!InternetQueryOptionA(hFile, 311 INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT, 312 &certInfo, 313 &certInfoLength)) 314 { 315 return FALSE; 316 } 317 318 subjectInfo.Attach(certInfo.lpszSubjectInfo); 319 issuerInfo.Attach(certInfo.lpszIssuerInfo); 320 321 if (certInfo.lpszProtocolName) 322 LocalFree(certInfo.lpszProtocolName); 323 if (certInfo.lpszSignatureAlgName) 324 LocalFree(certInfo.lpszSignatureAlgName); 325 if (certInfo.lpszEncryptionAlgName) 326 LocalFree(certInfo.lpszEncryptionAlgName); 327 328 return certInfo.lpszSubjectInfo && certInfo.lpszIssuerInfo; 329 } 330 #endif 331 332 inline VOID MessageBox_LoadString(HWND hMainWnd, INT StringID) 333 { 334 ATL::CStringW szMsgText; 335 if (szMsgText.LoadStringW(StringID)) 336 { 337 MessageBoxW(hMainWnd, szMsgText.GetString(), NULL, MB_OK | MB_ICONERROR); 338 } 339 } 340 341 // Download dialog (loaddlg.cpp) 342 class CDownloadManager 343 { 344 static ATL::CSimpleArray<DownloadInfo> AppsToInstallList; 345 static CDowloadingAppsListView DownloadsListView; 346 static CDownloaderProgress ProgressBar; 347 static BOOL bCancelled; 348 static VOID UpdateProgress(HWND hDlg, ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText); 349 public: 350 static VOID Add(DownloadInfo info); 351 static VOID Download(const DownloadInfo& DLInfo, BOOL bIsModal = FALSE); 352 static INT_PTR CALLBACK DownloadDlgProc(HWND Dlg, UINT uMsg, WPARAM wParam, LPARAM lParam); 353 static DWORD WINAPI ThreadFunc(LPVOID Context); 354 static VOID LaunchDownloadDialog(BOOL); 355 }; 356 357 358 // CDownloadManager 359 ATL::CSimpleArray<DownloadInfo> CDownloadManager::AppsToInstallList; 360 CDowloadingAppsListView CDownloadManager::DownloadsListView; 361 CDownloaderProgress CDownloadManager::ProgressBar; 362 BOOL CDownloadManager::bCancelled; 363 364 VOID CDownloadManager::Add(DownloadInfo info) 365 { 366 AppsToInstallList.Add(info); 367 } 368 369 VOID CDownloadManager::Download(const DownloadInfo &DLInfo, BOOL bIsModal) 370 { 371 AppsToInstallList.RemoveAll(); 372 AppsToInstallList.Add(DLInfo); 373 LaunchDownloadDialog(bIsModal); 374 } 375 376 INT_PTR CALLBACK CDownloadManager::DownloadDlgProc(HWND Dlg, UINT uMsg, WPARAM wParam, LPARAM lParam) 377 { 378 static WCHAR szCaption[MAX_PATH]; 379 380 switch (uMsg) 381 { 382 case WM_INITDIALOG: 383 { 384 HICON hIconSm, hIconBg; 385 ATL::CStringW szTempCaption; 386 387 bCancelled = FALSE; 388 389 hIconBg = (HICON) GetClassLongPtrW(hMainWnd, GCLP_HICON); 390 hIconSm = (HICON) GetClassLongPtrW(hMainWnd, GCLP_HICONSM); 391 392 if (hIconBg && hIconSm) 393 { 394 SendMessageW(Dlg, WM_SETICON, ICON_BIG, (LPARAM) hIconBg); 395 SendMessageW(Dlg, WM_SETICON, ICON_SMALL, (LPARAM) hIconSm); 396 } 397 398 SetWindowLongW(Dlg, GWLP_USERDATA, 0); 399 HWND Item = GetDlgItem(Dlg, IDC_DOWNLOAD_PROGRESS); 400 if (Item) 401 { 402 // initialize the default values for our nifty progress bar 403 // and subclass it so that it learns to print a status text 404 ProgressBar.SubclassWindow(Item); 405 ProgressBar.SendMessage(PBM_SETRANGE, 0, MAKELPARAM(0, 100)); 406 ProgressBar.SendMessage(PBM_SETPOS, 0, 0); 407 } 408 409 // Add a ListView 410 HWND hListView = DownloadsListView.Create(Dlg); 411 if (!hListView) 412 { 413 return FALSE; 414 } 415 DownloadsListView.LoadList(AppsToInstallList); 416 417 // Get a dlg string for later use 418 GetWindowTextW(Dlg, szCaption, _countof(szCaption)); 419 420 // Hide a placeholder from displaying 421 szTempCaption = szCaption; 422 szTempCaption.Replace(L"%ls", L""); 423 SetWindowText(Dlg, szTempCaption.GetString()); 424 425 ShowWindow(Dlg, SW_SHOW); 426 427 // Start download process 428 DownloadParam *param = new DownloadParam(Dlg, AppsToInstallList, szCaption); 429 DWORD ThreadId; 430 HANDLE Thread = CreateThread(NULL, 0, ThreadFunc, (LPVOID) param, 0, &ThreadId); 431 432 if (!Thread) 433 { 434 return FALSE; 435 } 436 437 CloseHandle(Thread); 438 AppsToInstallList.RemoveAll(); 439 return TRUE; 440 } 441 442 case WM_COMMAND: 443 if (wParam == IDCANCEL) 444 { 445 bCancelled = TRUE; 446 PostMessageW(Dlg, WM_CLOSE, 0, 0); 447 } 448 return FALSE; 449 450 case WM_CLOSE: 451 EndDialog(Dlg, 0); 452 //DestroyWindow(Dlg); 453 return TRUE; 454 455 default: 456 return FALSE; 457 } 458 } 459 460 BOOL UrlHasBeenCopied; 461 462 VOID CDownloadManager::UpdateProgress( 463 HWND hDlg, 464 ULONG ulProgress, 465 ULONG ulProgressMax, 466 ULONG ulStatusCode, 467 LPCWSTR szStatusText) 468 { 469 HWND Item; 470 471 ProgressBar.SetProgress(ulProgress, ulProgressMax); 472 473 Item = GetDlgItem(hDlg, IDC_DOWNLOAD_STATUS); 474 if (Item && szStatusText && wcslen(szStatusText) > 0 && UrlHasBeenCopied == FALSE) 475 { 476 SIZE_T len = wcslen(szStatusText) + 1; 477 ATL::CStringW buf; 478 DWORD dummyLen; 479 480 /* beautify our url for display purposes */ 481 if (!InternetCanonicalizeUrlW(szStatusText, buf.GetBuffer(len), &dummyLen, ICU_DECODE | ICU_NO_ENCODE)) 482 { 483 /* just use the original */ 484 buf.ReleaseBuffer(); 485 buf = szStatusText; 486 } 487 else 488 { 489 buf.ReleaseBuffer(); 490 } 491 492 /* paste it into our dialog and don't do it again in this instance */ 493 SendMessageW(Item, WM_SETTEXT, 0, (LPARAM) buf.GetString()); 494 UrlHasBeenCopied = TRUE; 495 } 496 } 497 498 DWORD WINAPI CDownloadManager::ThreadFunc(LPVOID param) 499 { 500 ATL::CStringW Path; 501 PWSTR p, q; 502 503 HWND hDlg = static_cast<DownloadParam*>(param)->Dialog; 504 HWND Item; 505 INT iAppId; 506 507 ULONG dwContentLen, dwBytesWritten, dwBytesRead, dwStatus; 508 ULONG dwCurrentBytesRead = 0; 509 ULONG dwStatusLen = sizeof(dwStatus); 510 511 BOOL bTempfile = FALSE; 512 BOOL bCab = FALSE; 513 514 HINTERNET hOpen = NULL; 515 HINTERNET hFile = NULL; 516 HANDLE hOut = INVALID_HANDLE_VALUE; 517 518 unsigned char lpBuffer[4096]; 519 LPCWSTR lpszAgent = L"RApps/1.0"; 520 URL_COMPONENTS urlComponents; 521 size_t urlLength, filenameLength; 522 523 const ATL::CSimpleArray<DownloadInfo> &InfoArray = static_cast<DownloadParam*>(param)->AppInfo; 524 LPCWSTR szCaption = static_cast<DownloadParam*>(param)->szCaption; 525 ATL::CStringW szNewCaption; 526 527 const DWORD dwUrlConnectFlags = INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_PRAGMA_NOCACHE | INTERNET_FLAG_KEEP_CONNECTION; 528 529 if (InfoArray.GetSize() <= 0) 530 { 531 MessageBox_LoadString(hMainWnd, IDS_UNABLE_TO_DOWNLOAD); 532 goto end; 533 } 534 535 for (iAppId = 0; iAppId < InfoArray.GetSize(); ++iAppId) 536 { 537 // Reset progress bar 538 Item = GetDlgItem(hDlg, IDC_DOWNLOAD_PROGRESS); 539 if (Item) 540 { 541 ProgressBar.SetMarquee(FALSE); 542 ProgressBar.SendMessage(WM_SETTEXT, 0, (LPARAM) L""); 543 ProgressBar.SendMessage(PBM_SETPOS, 0, 0); 544 } 545 546 // is this URL an update package for RAPPS? if so store it in a different place 547 if (InfoArray[iAppId].szUrl == APPLICATION_DATABASE_URL) 548 { 549 bCab = TRUE; 550 if (!GetStorageDirectory(Path)) 551 goto end; 552 } 553 else 554 { 555 bCab = FALSE; 556 Path = SettingsInfo.szDownloadDir; 557 } 558 559 // Change caption to show the currently downloaded app 560 if (!bCab) 561 { 562 szNewCaption.Format(szCaption, InfoArray[iAppId].szName.GetString()); 563 } 564 else 565 { 566 szNewCaption.LoadStringW(IDS_DL_DIALOG_DB_DOWNLOAD_DISP); 567 } 568 569 SetWindowTextW(hDlg, szNewCaption.GetString()); 570 571 // build the path for the download 572 p = wcsrchr(InfoArray[iAppId].szUrl.GetString(), L'/'); 573 q = wcsrchr(InfoArray[iAppId].szUrl.GetString(), L'?'); 574 575 // do we have a final slash separator? 576 if (!p) 577 goto end; 578 579 // prepare the tentative length of the filename, maybe we've to remove part of it later on 580 filenameLength = wcslen(p) * sizeof(WCHAR); 581 582 /* do we have query arguments in the target URL after the filename? account for them 583 (e.g. https://example.org/myfile.exe?no_adware_plz) */ 584 if (q && q > p && (q - p) > 0) 585 filenameLength -= wcslen(q - 1) * sizeof(WCHAR); 586 587 // is the path valid? can we access it? 588 if (GetFileAttributesW(Path.GetString()) == INVALID_FILE_ATTRIBUTES) 589 { 590 if (!CreateDirectoryW(Path.GetString(), NULL)) 591 goto end; 592 } 593 594 // append a \ to the provided file system path, and the filename portion from the URL after that 595 Path += L"\\"; 596 Path += (LPWSTR) (p + 1); 597 598 if (!bCab && InfoArray[iAppId].szSHA1[0] && GetFileAttributesW(Path.GetString()) != INVALID_FILE_ATTRIBUTES) 599 { 600 // only open it in case of total correctness 601 if (VerifyInteg(InfoArray[iAppId].szSHA1.GetString(), Path)) 602 goto run; 603 } 604 605 // Add the download URL 606 SetDlgItemTextW(hDlg, IDC_DOWNLOAD_STATUS, InfoArray[iAppId].szUrl.GetString()); 607 608 DownloadsListView.SetDownloadStatus(iAppId, DLSTATUS_DOWNLOADING); 609 610 // download it 611 UrlHasBeenCopied = FALSE; 612 bTempfile = TRUE; 613 614 /* FIXME: this should just be using the system-wide proxy settings */ 615 switch (SettingsInfo.Proxy) 616 { 617 case 0: // preconfig 618 default: 619 hOpen = InternetOpenW(lpszAgent, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); 620 break; 621 case 1: // direct (no proxy) 622 hOpen = InternetOpenW(lpszAgent, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); 623 break; 624 case 2: // use proxy 625 hOpen = InternetOpenW(lpszAgent, INTERNET_OPEN_TYPE_PROXY, SettingsInfo.szProxyServer, SettingsInfo.szNoProxyFor, 0); 626 break; 627 } 628 629 if (!hOpen) 630 goto end; 631 632 dwStatusLen = sizeof(dwStatus); 633 634 memset(&urlComponents, 0, sizeof(urlComponents)); 635 urlComponents.dwStructSize = sizeof(urlComponents); 636 637 urlLength = InfoArray[iAppId].szUrl.GetLength(); 638 urlComponents.dwSchemeLength = urlLength + 1; 639 urlComponents.lpszScheme = (LPWSTR) malloc(urlComponents.dwSchemeLength * sizeof(WCHAR)); 640 urlComponents.dwHostNameLength = urlLength + 1; 641 urlComponents.lpszHostName = (LPWSTR) malloc(urlComponents.dwHostNameLength * sizeof(WCHAR)); 642 643 if (!InternetCrackUrlW(InfoArray[iAppId].szUrl, urlLength + 1, ICU_DECODE | ICU_ESCAPE, &urlComponents)) 644 goto end; 645 646 dwContentLen = 0; 647 648 if (urlComponents.nScheme == INTERNET_SCHEME_HTTP || urlComponents.nScheme == INTERNET_SCHEME_HTTPS) 649 { 650 hFile = InternetOpenUrlW(hOpen, InfoArray[iAppId].szUrl.GetString(), NULL, 0, 651 dwUrlConnectFlags, 652 0); 653 if (!hFile) 654 { 655 MessageBox_LoadString(hMainWnd, IDS_UNABLE_TO_DOWNLOAD2); 656 goto end; 657 } 658 659 // query connection 660 if (!HttpQueryInfoW(hFile, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &dwStatus, &dwStatusLen, NULL)) 661 goto end; 662 663 if (dwStatus != HTTP_STATUS_OK) 664 { 665 MessageBox_LoadString(hMainWnd, IDS_UNABLE_TO_DOWNLOAD); 666 goto end; 667 } 668 669 // query content length 670 HttpQueryInfoW(hFile, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &dwContentLen, &dwStatusLen, NULL); 671 } 672 673 if (urlComponents.nScheme == INTERNET_SCHEME_FTP) 674 { 675 // force passive mode on FTP 676 hFile = InternetOpenUrlW(hOpen, InfoArray[iAppId].szUrl.GetString(), NULL, 0, 677 dwUrlConnectFlags | INTERNET_FLAG_PASSIVE, 678 0); 679 if (!hFile) 680 { 681 MessageBox_LoadString(hMainWnd, IDS_UNABLE_TO_DOWNLOAD2); 682 goto end; 683 } 684 685 dwContentLen = FtpGetFileSize(hFile, &dwStatus); 686 } 687 688 if (!dwContentLen) 689 { 690 // content-length is not known, enable marquee mode 691 ProgressBar.SetMarquee(TRUE); 692 } 693 694 free(urlComponents.lpszScheme); 695 free(urlComponents.lpszHostName); 696 697 #ifdef USE_CERT_PINNING 698 // are we using HTTPS to download the RAPPS update package? check if the certificate is original 699 if ((urlComponents.nScheme == INTERNET_SCHEME_HTTPS) && 700 (wcscmp(InfoArray[iAppId].szUrl, APPLICATION_DATABASE_URL) == 0)) 701 { 702 CLocalPtr subjectName, issuerName; 703 CStringW szMsgText; 704 bool bAskQuestion = false; 705 if (!CertGetSubjectAndIssuer(hFile, subjectName, issuerName)) 706 { 707 szMsgText.LoadStringW(IDS_UNABLE_TO_QUERY_CERT); 708 bAskQuestion = true; 709 } 710 else 711 { 712 if (strcmp(subjectName, CERT_SUBJECT_INFO) || 713 strcmp(issuerName, CERT_ISSUER_INFO)) 714 { 715 szMsgText.Format(IDS_MISMATCH_CERT_INFO, (char*)subjectName, (const char*)issuerName); 716 bAskQuestion = true; 717 } 718 } 719 720 if (bAskQuestion) 721 { 722 if (MessageBoxW(hMainWnd, szMsgText.GetString(), NULL, MB_YESNO | MB_ICONERROR) != IDYES) 723 { 724 goto end; 725 } 726 } 727 } 728 #endif 729 730 hOut = CreateFileW(Path.GetString(), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL); 731 732 if (hOut == INVALID_HANDLE_VALUE) 733 goto end; 734 735 dwCurrentBytesRead = 0; 736 do 737 { 738 if (!InternetReadFile(hFile, lpBuffer, _countof(lpBuffer), &dwBytesRead)) 739 { 740 MessageBox_LoadString(hMainWnd, IDS_INTERRUPTED_DOWNLOAD); 741 goto end; 742 } 743 744 if (!WriteFile(hOut, &lpBuffer[0], dwBytesRead, &dwBytesWritten, NULL)) 745 { 746 MessageBox_LoadString(hMainWnd, IDS_UNABLE_TO_WRITE); 747 goto end; 748 } 749 750 dwCurrentBytesRead += dwBytesRead; 751 UpdateProgress(hDlg, dwCurrentBytesRead, dwContentLen, 0, InfoArray[iAppId].szUrl.GetString()); 752 } while (dwBytesRead && !bCancelled); 753 754 CloseHandle(hOut); 755 hOut = INVALID_HANDLE_VALUE; 756 757 if (bCancelled) 758 goto end; 759 760 if (!dwContentLen) 761 { 762 // set progress bar to 100% 763 ProgressBar.SetMarquee(FALSE); 764 765 dwContentLen = dwCurrentBytesRead; 766 UpdateProgress(hDlg, dwCurrentBytesRead, dwContentLen, 0, InfoArray[iAppId].szUrl.GetString()); 767 } 768 769 /* if this thing isn't a RAPPS update and it has a SHA-1 checksum 770 verify its integrity by using the native advapi32.A_SHA1 functions */ 771 if (!bCab && InfoArray[iAppId].szSHA1[0] != 0) 772 { 773 ATL::CStringW szMsgText; 774 775 // change a few strings in the download dialog to reflect the verification process 776 if (!szMsgText.LoadStringW(IDS_INTEG_CHECK_TITLE)) 777 goto end; 778 779 SetWindowTextW(hDlg, szMsgText.GetString()); 780 SendMessageW(GetDlgItem(hDlg, IDC_DOWNLOAD_STATUS), WM_SETTEXT, 0, (LPARAM) Path.GetString()); 781 782 // this may take a while, depending on the file size 783 if (!VerifyInteg(InfoArray[iAppId].szSHA1.GetString(), Path.GetString())) 784 { 785 if (!szMsgText.LoadStringW(IDS_INTEG_CHECK_FAIL)) 786 goto end; 787 788 MessageBoxW(hDlg, szMsgText.GetString(), NULL, MB_OK | MB_ICONERROR); 789 goto end; 790 } 791 } 792 793 run: 794 DownloadsListView.SetDownloadStatus(iAppId, DLSTATUS_WAITING_INSTALL); 795 796 // run it 797 if (!bCab) 798 { 799 SHELLEXECUTEINFOW shExInfo = {0}; 800 shExInfo.cbSize = sizeof(shExInfo); 801 shExInfo.fMask = SEE_MASK_NOCLOSEPROCESS; 802 shExInfo.lpVerb = L"open"; 803 shExInfo.lpFile = Path.GetString(); 804 shExInfo.lpParameters = L""; 805 shExInfo.nShow = SW_SHOW; 806 807 if (ShellExecuteExW(&shExInfo)) 808 { 809 //reflect installation progress in the titlebar 810 //TODO: make a separate string with a placeholder to include app name? 811 ATL::CStringW szMsgText = LoadStatusString(DLSTATUS_INSTALLING); 812 SetWindowTextW(hDlg, szMsgText.GetString()); 813 814 DownloadsListView.SetDownloadStatus(iAppId, DLSTATUS_INSTALLING); 815 816 //TODO: issue an install operation separately so that the apps could be downloaded in the background 817 WaitForSingleObject(shExInfo.hProcess, INFINITE); 818 CloseHandle(shExInfo.hProcess); 819 } 820 else 821 { 822 MessageBox_LoadString(hMainWnd, IDS_UNABLE_TO_INSTALL); 823 } 824 } 825 826 end: 827 if (hOut != INVALID_HANDLE_VALUE) 828 CloseHandle(hOut); 829 830 InternetCloseHandle(hFile); 831 InternetCloseHandle(hOpen); 832 833 if (bTempfile) 834 { 835 if (bCancelled || (SettingsInfo.bDelInstaller && !bCab)) 836 DeleteFileW(Path.GetString()); 837 } 838 839 DownloadsListView.SetDownloadStatus(iAppId, DLSTATUS_FINISHED); 840 } 841 842 delete static_cast<DownloadParam*>(param); 843 SendMessageW(hDlg, WM_CLOSE, 0, 0); 844 return 0; 845 } 846 847 //TODO: Reuse the dialog 848 VOID CDownloadManager::LaunchDownloadDialog(BOOL bIsModal) 849 { 850 if (bIsModal) 851 { 852 DialogBoxW(hInst, 853 MAKEINTRESOURCEW(IDD_DOWNLOAD_DIALOG), 854 hMainWnd, 855 DownloadDlgProc); 856 } 857 else 858 { 859 CreateDialogW(hInst, 860 MAKEINTRESOURCEW(IDD_DOWNLOAD_DIALOG), 861 hMainWnd, 862 DownloadDlgProc); 863 } 864 } 865 // CDownloadManager 866 867 868 BOOL DownloadListOfApplications(const ATL::CSimpleArray<CAvailableApplicationInfo>& AppsList, BOOL bIsModal) 869 { 870 if (AppsList.GetSize() == 0) 871 return FALSE; 872 873 // Initialize shared variables 874 for (INT i = 0; i < AppsList.GetSize(); ++i) 875 { 876 CDownloadManager::Add(AppsList[i]); // implicit conversion to DownloadInfo 877 } 878 879 // Create a dialog and issue a download process 880 CDownloadManager::LaunchDownloadDialog(bIsModal); 881 882 return TRUE; 883 } 884 885 BOOL DownloadApplication(CAvailableApplicationInfo* pAppInfo, BOOL bIsModal) 886 { 887 if (!pAppInfo) 888 return FALSE; 889 890 CDownloadManager::Download(*pAppInfo, bIsModal); 891 return TRUE; 892 } 893 894 VOID DownloadApplicationsDB(LPCWSTR lpUrl) 895 { 896 static DownloadInfo DatabaseDLInfo; 897 DatabaseDLInfo.szUrl = lpUrl; 898 DatabaseDLInfo.szName.LoadStringW(IDS_DL_DIALOG_DB_DISP); 899 CDownloadManager::Download(DatabaseDLInfo, TRUE); 900 } 901 902