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 "US\r\nLet's Encrypt\r\nLet's Encrypt Authority X3" 51 #define CERT_SUBJECT_INFO "rapps.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 SIZE_T len = wcslen(szStatusText) + 1; 194 ATL::CStringW buf; 195 DWORD dummyLen; 196 197 /* beautify our url for display purposes */ 198 if (!InternetCanonicalizeUrlW(szStatusText, buf.GetBuffer(len), &dummyLen, ICU_DECODE | ICU_NO_ENCODE)) 199 { 200 /* just use the original */ 201 buf.ReleaseBuffer(); 202 buf = szStatusText; 203 } 204 else 205 { 206 buf.ReleaseBuffer(); 207 } 208 209 /* paste it into our dialog and don't do it again in this instance */ 210 SendMessageW(Item, WM_SETTEXT, 0, (LPARAM) buf.GetString()); 211 m_UrlHasBeenCopied = TRUE; 212 } 213 214 SetLastError(ERROR_SUCCESS); 215 r = GetWindowLongPtrW(m_hDialog, GWLP_USERDATA); 216 if (r || GetLastError() != ERROR_SUCCESS) 217 { 218 *m_pbCancelled = TRUE; 219 return E_ABORT; 220 } 221 222 return S_OK; 223 } 224 225 virtual HRESULT STDMETHODCALLTYPE OnStopBinding( 226 HRESULT hresult, 227 LPCWSTR szError) 228 { 229 return S_OK; 230 } 231 232 virtual HRESULT STDMETHODCALLTYPE GetBindInfo( 233 DWORD *grfBINDF, 234 BINDINFO *pbindinfo) 235 { 236 return S_OK; 237 } 238 239 virtual HRESULT STDMETHODCALLTYPE OnDataAvailable( 240 DWORD grfBSCF, 241 DWORD dwSize, 242 FORMATETC *pformatetc, 243 STGMEDIUM *pstgmed) 244 { 245 return S_OK; 246 } 247 248 virtual HRESULT STDMETHODCALLTYPE OnObjectAvailable( 249 REFIID riid, 250 IUnknown *punk) 251 { 252 return S_OK; 253 } 254 255 BEGIN_COM_MAP(CDownloadDialog) 256 COM_INTERFACE_ENTRY_IID(IID_IBindStatusCallback, IBindStatusCallback) 257 END_COM_MAP() 258 }; 259 260 class CDowloadingAppsListView 261 : public CUiWindow<CListView> 262 { 263 public: 264 HWND Create(HWND hwndParent) 265 { 266 RECT r = {10, 150, 320, 350}; 267 const DWORD style = WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_SINGLESEL 268 | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | LVS_NOCOLUMNHEADER; 269 270 HWND hwnd = CListView::Create(hwndParent, r, NULL, style, WS_EX_CLIENTEDGE); 271 272 AddColumn(0, 150, LVCFMT_LEFT); 273 AddColumn(1, 120, LVCFMT_LEFT); 274 275 return hwnd; 276 } 277 278 VOID LoadList(ATL::CSimpleArray<DownloadInfo> arrInfo) 279 { 280 for (INT i = 0; i < arrInfo.GetSize(); ++i) 281 { 282 AddRow(i, arrInfo[i].szName.GetString(), DLSTATUS_WAITING); 283 } 284 } 285 286 VOID SetDownloadStatus(INT ItemIndex, DownloadStatus Status) 287 { 288 HWND hListView = GetWindow(); 289 ATL::CStringW szBuffer = LoadStatusString(Status); 290 ListView_SetItemText(hListView, ItemIndex, 1, const_cast<LPWSTR>(szBuffer.GetString())); 291 } 292 293 BOOL AddItem(INT ItemIndex, LPWSTR lpText) 294 { 295 LVITEMW Item; 296 297 ZeroMemory(&Item, sizeof(Item)); 298 299 Item.mask = LVIF_TEXT | LVIF_STATE; 300 Item.pszText = lpText; 301 Item.iItem = ItemIndex; 302 303 return InsertItem(&Item); 304 } 305 306 VOID AddRow(INT RowIndex, LPCWSTR szAppName, const DownloadStatus Status) 307 { 308 ATL::CStringW szStatus = LoadStatusString(Status); 309 AddItem(RowIndex, 310 const_cast<LPWSTR>(szAppName)); 311 SetDownloadStatus(RowIndex, Status); 312 } 313 314 BOOL AddColumn(INT Index, INT Width, INT Format) 315 { 316 LVCOLUMNW Column; 317 ZeroMemory(&Column, sizeof(Column)); 318 319 Column.mask = LVCF_FMT | LVCF_WIDTH | LVCF_SUBITEM; 320 Column.iSubItem = Index; 321 Column.cx = Width; 322 Column.fmt = Format; 323 324 return (InsertColumn(Index, &Column) == -1) ? FALSE : TRUE; 325 } 326 }; 327 328 extern "C" 329 HRESULT WINAPI CDownloadDialog_Constructor(HWND Dlg, BOOL *pbCancelled, REFIID riid, LPVOID *ppv) 330 { 331 return ShellObjectCreatorInit<CDownloadDialog>(Dlg, pbCancelled, riid, ppv); 332 } 333 334 #ifdef USE_CERT_PINNING 335 typedef CHeapPtr<char, CLocalAllocator> CLocalPtr; 336 337 static BOOL CertGetSubjectAndIssuer(HINTERNET hFile, CLocalPtr& subjectInfo, CLocalPtr& issuerInfo) 338 { 339 DWORD certInfoLength; 340 INTERNET_CERTIFICATE_INFOA certInfo; 341 DWORD size, flags; 342 343 size = sizeof(flags); 344 if (!InternetQueryOptionA(hFile, INTERNET_OPTION_SECURITY_FLAGS, &flags, &size)) 345 { 346 return FALSE; 347 } 348 349 if (!flags & SECURITY_FLAG_SECURE) 350 { 351 return FALSE; 352 } 353 354 /* Despite what the header indicates, the implementation of INTERNET_CERTIFICATE_INFO is not Unicode-aware. */ 355 certInfoLength = sizeof(certInfo); 356 if (!InternetQueryOptionA(hFile, 357 INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT, 358 &certInfo, 359 &certInfoLength)) 360 { 361 return FALSE; 362 } 363 364 subjectInfo.Attach(certInfo.lpszSubjectInfo); 365 issuerInfo.Attach(certInfo.lpszIssuerInfo); 366 367 if (certInfo.lpszProtocolName) 368 LocalFree(certInfo.lpszProtocolName); 369 if (certInfo.lpszSignatureAlgName) 370 LocalFree(certInfo.lpszSignatureAlgName); 371 if (certInfo.lpszEncryptionAlgName) 372 LocalFree(certInfo.lpszEncryptionAlgName); 373 374 return certInfo.lpszSubjectInfo && certInfo.lpszIssuerInfo; 375 } 376 #endif 377 378 inline VOID MessageBox_LoadString(HWND hMainWnd, INT StringID) 379 { 380 ATL::CStringW szMsgText; 381 if (szMsgText.LoadStringW(StringID)) 382 { 383 MessageBoxW(hMainWnd, szMsgText.GetString(), NULL, MB_OK | MB_ICONERROR); 384 } 385 } 386 387 // CDownloadManager 388 ATL::CSimpleArray<DownloadInfo> CDownloadManager::AppsToInstallList; 389 CDowloadingAppsListView CDownloadManager::DownloadsListView; 390 391 VOID CDownloadManager::Download(const DownloadInfo &DLInfo, BOOL bIsModal) 392 { 393 AppsToInstallList.RemoveAll(); 394 AppsToInstallList.Add(DLInfo); 395 LaunchDownloadDialog(bIsModal); 396 } 397 398 INT_PTR CALLBACK CDownloadManager::DownloadDlgProc(HWND Dlg, UINT uMsg, WPARAM wParam, LPARAM lParam) 399 { 400 static WCHAR szCaption[MAX_PATH]; 401 402 switch (uMsg) 403 { 404 case WM_INITDIALOG: 405 { 406 HICON hIconSm, hIconBg; 407 ATL::CStringW szTempCaption; 408 409 hIconBg = (HICON) GetClassLongPtrW(hMainWnd, GCLP_HICON); 410 hIconSm = (HICON) GetClassLongPtrW(hMainWnd, GCLP_HICONSM); 411 412 if (hIconBg && hIconSm) 413 { 414 SendMessageW(Dlg, WM_SETICON, ICON_BIG, (LPARAM) hIconBg); 415 SendMessageW(Dlg, WM_SETICON, ICON_SMALL, (LPARAM) hIconSm); 416 } 417 418 SetWindowLongW(Dlg, GWLP_USERDATA, 0); 419 HWND Item = GetDlgItem(Dlg, IDC_DOWNLOAD_PROGRESS); 420 if (Item) 421 { 422 // initialize the default values for our nifty progress bar 423 // and subclass it so that it learns to print a status text 424 SendMessageW(Item, PBM_SETRANGE, 0, MAKELPARAM(0, 100)); 425 SendMessageW(Item, PBM_SETPOS, 0, 0); 426 427 SetWindowSubclass(Item, DownloadProgressProc, 0, 0); 428 } 429 430 // Add a ListView 431 HWND hListView = DownloadsListView.Create(Dlg); 432 if (!hListView) 433 { 434 return FALSE; 435 } 436 DownloadsListView.LoadList(AppsToInstallList); 437 438 // Get a dlg string for later use 439 GetWindowTextW(Dlg, szCaption, _countof(szCaption)); 440 441 // Hide a placeholder from displaying 442 szTempCaption = szCaption; 443 szTempCaption.Replace(L"%ls", L""); 444 SetWindowText(Dlg, szTempCaption.GetString()); 445 446 ShowWindow(Dlg, SW_SHOW); 447 448 // Start download process 449 DownloadParam *param = new DownloadParam(Dlg, AppsToInstallList, szCaption); 450 DWORD ThreadId; 451 HANDLE Thread = CreateThread(NULL, 0, ThreadFunc, (LPVOID) param, 0, &ThreadId); 452 453 if (!Thread) 454 { 455 return FALSE; 456 } 457 458 CloseHandle(Thread); 459 AppsToInstallList.RemoveAll(); 460 return TRUE; 461 } 462 463 case WM_COMMAND: 464 if (wParam == IDCANCEL) 465 { 466 SetWindowLongW(Dlg, GWLP_USERDATA, 1); 467 PostMessageW(Dlg, WM_CLOSE, 0, 0); 468 } 469 return FALSE; 470 471 case WM_CLOSE: 472 EndDialog(Dlg, 0); 473 //DestroyWindow(Dlg); 474 return TRUE; 475 476 default: 477 return FALSE; 478 } 479 } 480 481 LRESULT CALLBACK CDownloadManager::DownloadProgressProc(HWND hWnd, 482 UINT uMsg, 483 WPARAM wParam, 484 LPARAM lParam, 485 UINT_PTR uIdSubclass, 486 DWORD_PTR dwRefData) 487 { 488 static ATL::CStringW szProgressText; 489 490 switch (uMsg) 491 { 492 case WM_SETTEXT: 493 { 494 if (lParam) 495 { 496 szProgressText = (PCWSTR) lParam; 497 } 498 return TRUE; 499 } 500 501 case WM_ERASEBKGND: 502 case WM_PAINT: 503 { 504 PAINTSTRUCT ps; 505 HDC hDC = BeginPaint(hWnd, &ps), hdcMem; 506 HBITMAP hbmMem; 507 HANDLE hOld; 508 RECT myRect; 509 UINT win_width, win_height; 510 511 GetClientRect(hWnd, &myRect); 512 513 /* grab the progress bar rect size */ 514 win_width = myRect.right - myRect.left; 515 win_height = myRect.bottom - myRect.top; 516 517 /* create an off-screen DC for double-buffering */ 518 hdcMem = CreateCompatibleDC(hDC); 519 hbmMem = CreateCompatibleBitmap(hDC, win_width, win_height); 520 521 hOld = SelectObject(hdcMem, hbmMem); 522 523 /* call the original draw code and redirect it to our memory buffer */ 524 DefSubclassProc(hWnd, uMsg, (WPARAM) hdcMem, lParam); 525 526 /* draw our nifty progress text over it */ 527 SelectFont(hdcMem, GetStockFont(DEFAULT_GUI_FONT)); 528 DrawShadowText(hdcMem, szProgressText.GetString(), szProgressText.GetLength(), 529 &myRect, 530 DT_CENTER | DT_VCENTER | DT_NOPREFIX | DT_SINGLELINE, 531 GetSysColor(COLOR_CAPTIONTEXT), 532 GetSysColor(COLOR_3DSHADOW), 533 1, 1); 534 535 /* transfer the off-screen DC to the screen */ 536 BitBlt(hDC, 0, 0, win_width, win_height, hdcMem, 0, 0, SRCCOPY); 537 538 /* free the off-screen DC */ 539 SelectObject(hdcMem, hOld); 540 DeleteObject(hbmMem); 541 DeleteDC(hdcMem); 542 543 EndPaint(hWnd, &ps); 544 return 0; 545 } 546 547 /* Raymond Chen says that we should safely unsubclass all the things! 548 (http://blogs.msdn.com/b/oldnewthing/archive/2003/11/11/55653.aspx) */ 549 550 case WM_NCDESTROY: 551 { 552 szProgressText.Empty(); 553 RemoveWindowSubclass(hWnd, DownloadProgressProc, uIdSubclass); 554 } 555 /* Fall-through */ 556 default: 557 return DefSubclassProc(hWnd, uMsg, wParam, lParam); 558 } 559 } 560 561 VOID CDownloadManager::SetProgressMarquee(HWND Item, BOOL Enable) 562 { 563 if (!Item) 564 return; 565 566 DWORD style = GetWindowLongPtr(Item, GWL_STYLE); 567 if (!style) 568 return; 569 570 if (!SetWindowLongPtr(Item, GWL_STYLE, (Enable ? style | PBS_MARQUEE : style & ~PBS_MARQUEE))) 571 return; 572 573 SendMessageW(Item, PBM_SETMARQUEE, Enable, 0); 574 } 575 576 DWORD WINAPI CDownloadManager::ThreadFunc(LPVOID param) 577 { 578 CComPtr<IBindStatusCallback> dl; 579 ATL::CStringW Path; 580 PWSTR p, q; 581 582 HWND hDlg = static_cast<DownloadParam*>(param)->Dialog; 583 HWND Item; 584 INT iAppId; 585 586 ULONG dwContentLen, dwBytesWritten, dwBytesRead, dwStatus; 587 ULONG dwCurrentBytesRead = 0; 588 ULONG dwStatusLen = sizeof(dwStatus); 589 590 BOOL bCancelled = FALSE; 591 BOOL bTempfile = FALSE; 592 BOOL bCab = FALSE; 593 594 HINTERNET hOpen = NULL; 595 HINTERNET hFile = NULL; 596 HANDLE hOut = INVALID_HANDLE_VALUE; 597 598 unsigned char lpBuffer[4096]; 599 LPCWSTR lpszAgent = L"RApps/1.0"; 600 URL_COMPONENTS urlComponents; 601 size_t urlLength, filenameLength; 602 603 const ATL::CSimpleArray<DownloadInfo> &InfoArray = static_cast<DownloadParam*>(param)->AppInfo; 604 LPCWSTR szCaption = static_cast<DownloadParam*>(param)->szCaption; 605 ATL::CStringW szNewCaption; 606 607 const DWORD dwUrlConnectFlags = INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_PRAGMA_NOCACHE | INTERNET_FLAG_KEEP_CONNECTION; 608 609 if (InfoArray.GetSize() <= 0) 610 { 611 MessageBox_LoadString(hMainWnd, IDS_UNABLE_TO_DOWNLOAD); 612 goto end; 613 } 614 615 for (iAppId = 0; iAppId < InfoArray.GetSize(); ++iAppId) 616 { 617 // Reset progress bar 618 Item = GetDlgItem(hDlg, IDC_DOWNLOAD_PROGRESS); 619 if (Item) 620 { 621 SetProgressMarquee(Item, FALSE); 622 SendMessageW(Item, WM_SETTEXT, 0, (LPARAM) L""); 623 SendMessageW(Item, PBM_SETPOS, 0, 0); 624 } 625 626 // is this URL an update package for RAPPS? if so store it in a different place 627 if (InfoArray[iAppId].szUrl == APPLICATION_DATABASE_URL) 628 { 629 bCab = TRUE; 630 if (!GetStorageDirectory(Path)) 631 goto end; 632 } 633 else 634 { 635 bCab = FALSE; 636 Path = SettingsInfo.szDownloadDir; 637 } 638 639 // Change caption to show the currently downloaded app 640 if (!bCab) 641 { 642 szNewCaption.Format(szCaption, InfoArray[iAppId].szName.GetString()); 643 } 644 else 645 { 646 szNewCaption.LoadStringW(IDS_DL_DIALOG_DB_DOWNLOAD_DISP); 647 } 648 649 SetWindowTextW(hDlg, szNewCaption.GetString()); 650 651 // build the path for the download 652 p = wcsrchr(InfoArray[iAppId].szUrl.GetString(), L'/'); 653 q = wcsrchr(InfoArray[iAppId].szUrl.GetString(), L'?'); 654 655 // do we have a final slash separator? 656 if (!p) 657 goto end; 658 659 // prepare the tentative length of the filename, maybe we've to remove part of it later on 660 filenameLength = wcslen(p) * sizeof(WCHAR); 661 662 /* do we have query arguments in the target URL after the filename? account for them 663 (e.g. https://example.org/myfile.exe?no_adware_plz) */ 664 if (q && q > p && (q - p) > 0) 665 filenameLength -= wcslen(q - 1) * sizeof(WCHAR); 666 667 // is the path valid? can we access it? 668 if (GetFileAttributesW(Path.GetString()) == INVALID_FILE_ATTRIBUTES) 669 { 670 if (!CreateDirectoryW(Path.GetString(), NULL)) 671 goto end; 672 } 673 674 // append a \ to the provided file system path, and the filename portion from the URL after that 675 Path += L"\\"; 676 Path += (LPWSTR) (p + 1); 677 678 if (!bCab && InfoArray[iAppId].szSHA1[0] && GetFileAttributesW(Path.GetString()) != INVALID_FILE_ATTRIBUTES) 679 { 680 // only open it in case of total correctness 681 if (VerifyInteg(InfoArray[iAppId].szSHA1.GetString(), Path)) 682 goto run; 683 } 684 685 // Add the download URL 686 SetDlgItemTextW(hDlg, IDC_DOWNLOAD_STATUS, InfoArray[iAppId].szUrl.GetString()); 687 688 DownloadsListView.SetDownloadStatus(iAppId, DLSTATUS_DOWNLOADING); 689 690 // download it 691 bTempfile = TRUE; 692 CDownloadDialog_Constructor(hDlg, &bCancelled, IID_PPV_ARG(IBindStatusCallback, &dl)); 693 694 if (dl == NULL) 695 goto end; 696 697 /* FIXME: this should just be using the system-wide proxy settings */ 698 switch (SettingsInfo.Proxy) 699 { 700 case 0: // preconfig 701 default: 702 hOpen = InternetOpenW(lpszAgent, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); 703 break; 704 case 1: // direct (no proxy) 705 hOpen = InternetOpenW(lpszAgent, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); 706 break; 707 case 2: // use proxy 708 hOpen = InternetOpenW(lpszAgent, INTERNET_OPEN_TYPE_PROXY, SettingsInfo.szProxyServer, SettingsInfo.szNoProxyFor, 0); 709 break; 710 } 711 712 if (!hOpen) 713 goto end; 714 715 dwStatusLen = sizeof(dwStatus); 716 717 memset(&urlComponents, 0, sizeof(urlComponents)); 718 urlComponents.dwStructSize = sizeof(urlComponents); 719 720 urlLength = InfoArray[iAppId].szUrl.GetLength(); 721 urlComponents.dwSchemeLength = urlLength + 1; 722 urlComponents.lpszScheme = (LPWSTR) malloc(urlComponents.dwSchemeLength * sizeof(WCHAR)); 723 urlComponents.dwHostNameLength = urlLength + 1; 724 urlComponents.lpszHostName = (LPWSTR) malloc(urlComponents.dwHostNameLength * sizeof(WCHAR)); 725 726 if (!InternetCrackUrlW(InfoArray[iAppId].szUrl, urlLength + 1, ICU_DECODE | ICU_ESCAPE, &urlComponents)) 727 goto end; 728 729 dwContentLen = 0; 730 731 if (urlComponents.nScheme == INTERNET_SCHEME_HTTP || urlComponents.nScheme == INTERNET_SCHEME_HTTPS) 732 { 733 hFile = InternetOpenUrlW(hOpen, InfoArray[iAppId].szUrl.GetString(), NULL, 0, 734 dwUrlConnectFlags, 735 0); 736 if (!hFile) 737 { 738 MessageBox_LoadString(hMainWnd, IDS_UNABLE_TO_DOWNLOAD2); 739 goto end; 740 } 741 742 // query connection 743 if (!HttpQueryInfoW(hFile, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &dwStatus, &dwStatusLen, NULL)) 744 goto end; 745 746 if (dwStatus != HTTP_STATUS_OK) 747 { 748 MessageBox_LoadString(hMainWnd, IDS_UNABLE_TO_DOWNLOAD); 749 goto end; 750 } 751 752 // query content length 753 HttpQueryInfoW(hFile, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &dwContentLen, &dwStatusLen, NULL); 754 } 755 756 if (urlComponents.nScheme == INTERNET_SCHEME_FTP) 757 { 758 // force passive mode on FTP 759 hFile = InternetOpenUrlW(hOpen, InfoArray[iAppId].szUrl.GetString(), NULL, 0, 760 dwUrlConnectFlags | INTERNET_FLAG_PASSIVE, 761 0); 762 if (!hFile) 763 { 764 MessageBox_LoadString(hMainWnd, IDS_UNABLE_TO_DOWNLOAD2); 765 goto end; 766 } 767 768 dwContentLen = FtpGetFileSize(hFile, &dwStatus); 769 } 770 771 if (!dwContentLen) 772 { 773 // content-length is not known, enable marquee mode 774 SetProgressMarquee(Item, TRUE); 775 } 776 777 free(urlComponents.lpszScheme); 778 free(urlComponents.lpszHostName); 779 780 #ifdef USE_CERT_PINNING 781 // are we using HTTPS to download the RAPPS update package? check if the certificate is original 782 if ((urlComponents.nScheme == INTERNET_SCHEME_HTTPS) && 783 (wcscmp(InfoArray[iAppId].szUrl, APPLICATION_DATABASE_URL) == 0)) 784 { 785 CLocalPtr subjectName, issuerName; 786 CStringW szMsgText; 787 bool bAskQuestion = false; 788 if (!CertGetSubjectAndIssuer(hFile, subjectName, issuerName)) 789 { 790 szMsgText.LoadStringW(IDS_UNABLE_TO_QUERY_CERT); 791 bAskQuestion = true; 792 } 793 else 794 { 795 if (strcmp(subjectName, CERT_SUBJECT_INFO) || 796 strcmp(issuerName, CERT_ISSUER_INFO)) 797 { 798 szMsgText.Format(IDS_MISMATCH_CERT_INFO, (char*)subjectName, (const char*)issuerName); 799 bAskQuestion = true; 800 } 801 } 802 803 if (bAskQuestion) 804 { 805 if (MessageBoxW(hMainWnd, szMsgText.GetString(), NULL, MB_YESNO | MB_ICONERROR) != IDYES) 806 { 807 goto end; 808 } 809 } 810 } 811 #endif 812 813 hOut = CreateFileW(Path.GetString(), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL); 814 815 if (hOut == INVALID_HANDLE_VALUE) 816 goto end; 817 818 dwCurrentBytesRead = 0; 819 do 820 { 821 if (!InternetReadFile(hFile, lpBuffer, _countof(lpBuffer), &dwBytesRead)) 822 { 823 MessageBox_LoadString(hMainWnd, IDS_INTERRUPTED_DOWNLOAD); 824 goto end; 825 } 826 827 if (!WriteFile(hOut, &lpBuffer[0], dwBytesRead, &dwBytesWritten, NULL)) 828 { 829 MessageBox_LoadString(hMainWnd, IDS_UNABLE_TO_WRITE); 830 goto end; 831 } 832 833 dwCurrentBytesRead += dwBytesRead; 834 dl->OnProgress(dwCurrentBytesRead, dwContentLen, 0, InfoArray[iAppId].szUrl.GetString()); 835 } while (dwBytesRead && !bCancelled); 836 837 CloseHandle(hOut); 838 hOut = INVALID_HANDLE_VALUE; 839 840 if (bCancelled) 841 goto end; 842 843 if (!dwContentLen) 844 { 845 // set progress bar to 100% 846 SetProgressMarquee(Item, FALSE); 847 848 dwContentLen = dwCurrentBytesRead; 849 dl->OnProgress(dwCurrentBytesRead, dwContentLen, 0, InfoArray[iAppId].szUrl.GetString()); 850 } 851 852 /* if this thing isn't a RAPPS update and it has a SHA-1 checksum 853 verify its integrity by using the native advapi32.A_SHA1 functions */ 854 if (!bCab && InfoArray[iAppId].szSHA1[0] != 0) 855 { 856 ATL::CStringW szMsgText; 857 858 // change a few strings in the download dialog to reflect the verification process 859 if (!szMsgText.LoadStringW(IDS_INTEG_CHECK_TITLE)) 860 goto end; 861 862 SetWindowTextW(hDlg, szMsgText.GetString()); 863 SendMessageW(GetDlgItem(hDlg, IDC_DOWNLOAD_STATUS), WM_SETTEXT, 0, (LPARAM) Path.GetString()); 864 865 // this may take a while, depending on the file size 866 if (!VerifyInteg(InfoArray[iAppId].szSHA1.GetString(), Path.GetString())) 867 { 868 if (!szMsgText.LoadStringW(IDS_INTEG_CHECK_FAIL)) 869 goto end; 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 (!bCab) 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 MessageBox_LoadString(hMainWnd, IDS_UNABLE_TO_INSTALL); 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 && !bCab)) 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 BOOL CDownloadManager::DownloadListOfApplications(const ATL::CSimpleArray<CAvailableApplicationInfo>& AppsList, BOOL bIsModal) 931 { 932 if (AppsList.GetSize() == 0) 933 return FALSE; 934 935 // Initialize shared variables 936 for (INT i = 0; i < AppsList.GetSize(); ++i) 937 { 938 AppsToInstallList.Add(AppsList[i]); // implicit conversion to DownloadInfo 939 } 940 941 // Create a dialog and issue a download process 942 LaunchDownloadDialog(bIsModal); 943 944 return TRUE; 945 } 946 947 BOOL CDownloadManager::DownloadApplication(CAvailableApplicationInfo* pAppInfo, BOOL bIsModal) 948 { 949 if (!pAppInfo) 950 return FALSE; 951 952 Download(*pAppInfo, bIsModal); 953 return TRUE; 954 } 955 956 VOID CDownloadManager::DownloadApplicationsDB(LPCWSTR lpUrl) 957 { 958 static DownloadInfo DatabaseDLInfo; 959 DatabaseDLInfo.szUrl = lpUrl; 960 DatabaseDLInfo.szName.LoadStringW(IDS_DL_DIALOG_DB_DISP); 961 Download(DatabaseDLInfo, TRUE); 962 } 963 964 //TODO: Reuse the dialog 965 VOID CDownloadManager::LaunchDownloadDialog(BOOL bIsModal) 966 { 967 if (bIsModal) 968 { 969 DialogBoxW(hInst, 970 MAKEINTRESOURCEW(IDD_DOWNLOAD_DIALOG), 971 hMainWnd, 972 DownloadDlgProc); 973 } 974 else 975 { 976 CreateDialogW(hInst, 977 MAKEINTRESOURCEW(IDD_DOWNLOAD_DIALOG), 978 hMainWnd, 979 DownloadDlgProc); 980 } 981 } 982 // CDownloadManager 983