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