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