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