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