201538f7edb0fadf3f835faf5afd045e05b8c9b5
[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 CDowloadingAppsListView
222 : public CListView
223 {
224 public:
225 HWND Create(HWND hwndParent)
226 {
227 RECT r = {10, 150, 320, 350};
228 const DWORD style = WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_SINGLESEL
229 | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | LVS_NOCOLUMNHEADER;
230
231 HWND hwnd = CListView::Create(hwndParent, r, NULL, style, WS_EX_CLIENTEDGE);
232
233 AddColumn(0, 150, LVCFMT_LEFT);
234 AddColumn(1, 120, LVCFMT_LEFT);
235
236 return hwnd;
237 }
238
239 VOID LoadList(ATL::CSimpleArray<DownloadInfo> arrInfo)
240 {
241 for (INT i = 0; i < arrInfo.GetSize(); ++i)
242 {
243 AddRow(i, arrInfo[i].szName.GetString(), DLSTATUS_WAITING);
244 }
245 }
246
247 VOID SetDownloadStatus(INT ItemIndex, DownloadStatus Status)
248 {
249 ATL::CStringW szBuffer = LoadStatusString(Status);
250 SetItemText(ItemIndex, 1, szBuffer.GetString());
251 }
252
253 BOOL AddItem(INT ItemIndex, LPWSTR lpText)
254 {
255 LVITEMW Item;
256
257 ZeroMemory(&Item, sizeof(Item));
258
259 Item.mask = LVIF_TEXT | LVIF_STATE;
260 Item.pszText = lpText;
261 Item.iItem = ItemIndex;
262
263 return InsertItem(&Item);
264 }
265
266 VOID AddRow(INT RowIndex, LPCWSTR szAppName, const DownloadStatus Status)
267 {
268 ATL::CStringW szStatus = LoadStatusString(Status);
269 AddItem(RowIndex,
270 const_cast<LPWSTR>(szAppName));
271 SetDownloadStatus(RowIndex, Status);
272 }
273
274 BOOL AddColumn(INT Index, INT Width, INT Format)
275 {
276 LVCOLUMNW Column;
277 ZeroMemory(&Column, sizeof(Column));
278
279 Column.mask = LVCF_FMT | LVCF_WIDTH | LVCF_SUBITEM;
280 Column.iSubItem = Index;
281 Column.cx = Width;
282 Column.fmt = Format;
283
284 return (InsertColumn(Index, &Column) == -1) ? FALSE : TRUE;
285 }
286 };
287
288 #ifdef USE_CERT_PINNING
289 typedef CHeapPtr<char, CLocalAllocator> CLocalPtr;
290
291 static BOOL CertGetSubjectAndIssuer(HINTERNET hFile, CLocalPtr& subjectInfo, CLocalPtr& issuerInfo)
292 {
293 DWORD certInfoLength;
294 INTERNET_CERTIFICATE_INFOA certInfo;
295 DWORD size, flags;
296
297 size = sizeof(flags);
298 if (!InternetQueryOptionA(hFile, INTERNET_OPTION_SECURITY_FLAGS, &flags, &size))
299 {
300 return FALSE;
301 }
302
303 if (!flags & SECURITY_FLAG_SECURE)
304 {
305 return FALSE;
306 }
307
308 /* Despite what the header indicates, the implementation of INTERNET_CERTIFICATE_INFO is not Unicode-aware. */
309 certInfoLength = sizeof(certInfo);
310 if (!InternetQueryOptionA(hFile,
311 INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT,
312 &certInfo,
313 &certInfoLength))
314 {
315 return FALSE;
316 }
317
318 subjectInfo.Attach(certInfo.lpszSubjectInfo);
319 issuerInfo.Attach(certInfo.lpszIssuerInfo);
320
321 if (certInfo.lpszProtocolName)
322 LocalFree(certInfo.lpszProtocolName);
323 if (certInfo.lpszSignatureAlgName)
324 LocalFree(certInfo.lpszSignatureAlgName);
325 if (certInfo.lpszEncryptionAlgName)
326 LocalFree(certInfo.lpszEncryptionAlgName);
327
328 return certInfo.lpszSubjectInfo && certInfo.lpszIssuerInfo;
329 }
330 #endif
331
332 inline VOID MessageBox_LoadString(HWND hMainWnd, INT StringID)
333 {
334 ATL::CStringW szMsgText;
335 if (szMsgText.LoadStringW(StringID))
336 {
337 MessageBoxW(hMainWnd, szMsgText.GetString(), NULL, MB_OK | MB_ICONERROR);
338 }
339 }
340
341 // Download dialog (loaddlg.cpp)
342 class CDownloadManager
343 {
344 static ATL::CSimpleArray<DownloadInfo> AppsToInstallList;
345 static CDowloadingAppsListView DownloadsListView;
346 static CDownloaderProgress ProgressBar;
347 static BOOL bCancelled;
348 static VOID UpdateProgress(HWND hDlg, ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText);
349 public:
350 static VOID Add(DownloadInfo info);
351 static VOID Download(const DownloadInfo& DLInfo, BOOL bIsModal = FALSE);
352 static INT_PTR CALLBACK DownloadDlgProc(HWND Dlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
353 static DWORD WINAPI ThreadFunc(LPVOID Context);
354 static BOOL DownloadListOfApplications(const ATL::CSimpleArray<CAvailableApplicationInfo>& AppsList, BOOL bIsModal = FALSE);
355 static BOOL DownloadApplication(CAvailableApplicationInfo* pAppInfo, BOOL bIsModal = FALSE);
356 static VOID DownloadApplicationsDB(LPCWSTR lpUrl);
357 static VOID LaunchDownloadDialog(BOOL);
358 };
359
360
361 // CDownloadManager
362 ATL::CSimpleArray<DownloadInfo> CDownloadManager::AppsToInstallList;
363 CDowloadingAppsListView CDownloadManager::DownloadsListView;
364 CDownloaderProgress CDownloadManager::ProgressBar;
365 BOOL CDownloadManager::bCancelled;
366
367 VOID CDownloadManager::Add(DownloadInfo info)
368 {
369 AppsToInstallList.Add(info);
370 }
371
372 VOID CDownloadManager::Download(const DownloadInfo &DLInfo, BOOL bIsModal)
373 {
374 AppsToInstallList.RemoveAll();
375 AppsToInstallList.Add(DLInfo);
376 LaunchDownloadDialog(bIsModal);
377 }
378
379 INT_PTR CALLBACK CDownloadManager::DownloadDlgProc(HWND Dlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
380 {
381 static WCHAR szCaption[MAX_PATH];
382
383 switch (uMsg)
384 {
385 case WM_INITDIALOG:
386 {
387 HICON hIconSm, hIconBg;
388 ATL::CStringW szTempCaption;
389
390 bCancelled = FALSE;
391
392 hIconBg = (HICON) GetClassLongPtrW(hMainWnd, GCLP_HICON);
393 hIconSm = (HICON) GetClassLongPtrW(hMainWnd, GCLP_HICONSM);
394
395 if (hIconBg && hIconSm)
396 {
397 SendMessageW(Dlg, WM_SETICON, ICON_BIG, (LPARAM) hIconBg);
398 SendMessageW(Dlg, WM_SETICON, ICON_SMALL, (LPARAM) hIconSm);
399 }
400
401 SetWindowLongW(Dlg, GWLP_USERDATA, 0);
402 HWND Item = GetDlgItem(Dlg, IDC_DOWNLOAD_PROGRESS);
403 if (Item)
404 {
405 // initialize the default values for our nifty progress bar
406 // and subclass it so that it learns to print a status text
407 ProgressBar.SubclassWindow(Item);
408 ProgressBar.SendMessage(PBM_SETRANGE, 0, MAKELPARAM(0, 100));
409 ProgressBar.SendMessage(PBM_SETPOS, 0, 0);
410 }
411
412 // Add a ListView
413 HWND hListView = DownloadsListView.Create(Dlg);
414 if (!hListView)
415 {
416 return FALSE;
417 }
418 DownloadsListView.LoadList(AppsToInstallList);
419
420 // Get a dlg string for later use
421 GetWindowTextW(Dlg, szCaption, _countof(szCaption));
422
423 // Hide a placeholder from displaying
424 szTempCaption = szCaption;
425 szTempCaption.Replace(L"%ls", L"");
426 SetWindowText(Dlg, szTempCaption.GetString());
427
428 ShowWindow(Dlg, SW_SHOW);
429
430 // Start download process
431 DownloadParam *param = new DownloadParam(Dlg, AppsToInstallList, szCaption);
432 DWORD ThreadId;
433 HANDLE Thread = CreateThread(NULL, 0, ThreadFunc, (LPVOID) param, 0, &ThreadId);
434
435 if (!Thread)
436 {
437 return FALSE;
438 }
439
440 CloseHandle(Thread);
441 AppsToInstallList.RemoveAll();
442 return TRUE;
443 }
444
445 case WM_COMMAND:
446 if (wParam == IDCANCEL)
447 {
448 bCancelled = TRUE;
449 PostMessageW(Dlg, WM_CLOSE, 0, 0);
450 }
451 return FALSE;
452
453 case WM_CLOSE:
454 EndDialog(Dlg, 0);
455 //DestroyWindow(Dlg);
456 return TRUE;
457
458 default:
459 return FALSE;
460 }
461 }
462
463 BOOL UrlHasBeenCopied;
464
465 VOID CDownloadManager::UpdateProgress(
466 HWND hDlg,
467 ULONG ulProgress,
468 ULONG ulProgressMax,
469 ULONG ulStatusCode,
470 LPCWSTR szStatusText)
471 {
472 HWND Item;
473
474 ProgressBar.SetProgress(ulProgress, ulProgressMax);
475
476 Item = GetDlgItem(hDlg, IDC_DOWNLOAD_STATUS);
477 if (Item && szStatusText && wcslen(szStatusText) > 0 && UrlHasBeenCopied == FALSE)
478 {
479 SIZE_T len = wcslen(szStatusText) + 1;
480 ATL::CStringW buf;
481 DWORD dummyLen;
482
483 /* beautify our url for display purposes */
484 if (!InternetCanonicalizeUrlW(szStatusText, buf.GetBuffer(len), &dummyLen, ICU_DECODE | ICU_NO_ENCODE))
485 {
486 /* just use the original */
487 buf.ReleaseBuffer();
488 buf = szStatusText;
489 }
490 else
491 {
492 buf.ReleaseBuffer();
493 }
494
495 /* paste it into our dialog and don't do it again in this instance */
496 SendMessageW(Item, WM_SETTEXT, 0, (LPARAM) buf.GetString());
497 UrlHasBeenCopied = TRUE;
498 }
499 }
500
501 DWORD WINAPI CDownloadManager::ThreadFunc(LPVOID param)
502 {
503 ATL::CStringW Path;
504 PWSTR p, q;
505
506 HWND hDlg = static_cast<DownloadParam*>(param)->Dialog;
507 HWND Item;
508 INT iAppId;
509
510 ULONG dwContentLen, dwBytesWritten, dwBytesRead, dwStatus;
511 ULONG dwCurrentBytesRead = 0;
512 ULONG dwStatusLen = sizeof(dwStatus);
513
514 BOOL bTempfile = FALSE;
515 BOOL bCab = FALSE;
516
517 HINTERNET hOpen = NULL;
518 HINTERNET hFile = NULL;
519 HANDLE hOut = INVALID_HANDLE_VALUE;
520
521 unsigned char lpBuffer[4096];
522 LPCWSTR lpszAgent = L"RApps/1.0";
523 URL_COMPONENTS urlComponents;
524 size_t urlLength, filenameLength;
525
526 const ATL::CSimpleArray<DownloadInfo> &InfoArray = static_cast<DownloadParam*>(param)->AppInfo;
527 LPCWSTR szCaption = static_cast<DownloadParam*>(param)->szCaption;
528 ATL::CStringW szNewCaption;
529
530 const DWORD dwUrlConnectFlags = INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_PRAGMA_NOCACHE | INTERNET_FLAG_KEEP_CONNECTION;
531
532 if (InfoArray.GetSize() <= 0)
533 {
534 MessageBox_LoadString(hMainWnd, IDS_UNABLE_TO_DOWNLOAD);
535 goto end;
536 }
537
538 for (iAppId = 0; iAppId < InfoArray.GetSize(); ++iAppId)
539 {
540 // Reset progress bar
541 Item = GetDlgItem(hDlg, IDC_DOWNLOAD_PROGRESS);
542 if (Item)
543 {
544 ProgressBar.SetMarquee(FALSE);
545 ProgressBar.SendMessage(WM_SETTEXT, 0, (LPARAM) L"");
546 ProgressBar.SendMessage(PBM_SETPOS, 0, 0);
547 }
548
549 // is this URL an update package for RAPPS? if so store it in a different place
550 if (InfoArray[iAppId].szUrl == APPLICATION_DATABASE_URL)
551 {
552 bCab = TRUE;
553 if (!GetStorageDirectory(Path))
554 goto end;
555 }
556 else
557 {
558 bCab = FALSE;
559 Path = SettingsInfo.szDownloadDir;
560 }
561
562 // Change caption to show the currently downloaded app
563 if (!bCab)
564 {
565 szNewCaption.Format(szCaption, InfoArray[iAppId].szName.GetString());
566 }
567 else
568 {
569 szNewCaption.LoadStringW(IDS_DL_DIALOG_DB_DOWNLOAD_DISP);
570 }
571
572 SetWindowTextW(hDlg, szNewCaption.GetString());
573
574 // build the path for the download
575 p = wcsrchr(InfoArray[iAppId].szUrl.GetString(), L'/');
576 q = wcsrchr(InfoArray[iAppId].szUrl.GetString(), L'?');
577
578 // do we have a final slash separator?
579 if (!p)
580 goto end;
581
582 // prepare the tentative length of the filename, maybe we've to remove part of it later on
583 filenameLength = wcslen(p) * sizeof(WCHAR);
584
585 /* do we have query arguments in the target URL after the filename? account for them
586 (e.g. https://example.org/myfile.exe?no_adware_plz) */
587 if (q && q > p && (q - p) > 0)
588 filenameLength -= wcslen(q - 1) * sizeof(WCHAR);
589
590 // is the path valid? can we access it?
591 if (GetFileAttributesW(Path.GetString()) == INVALID_FILE_ATTRIBUTES)
592 {
593 if (!CreateDirectoryW(Path.GetString(), NULL))
594 goto end;
595 }
596
597 // append a \ to the provided file system path, and the filename portion from the URL after that
598 Path += L"\\";
599 Path += (LPWSTR) (p + 1);
600
601 if (!bCab && InfoArray[iAppId].szSHA1[0] && GetFileAttributesW(Path.GetString()) != INVALID_FILE_ATTRIBUTES)
602 {
603 // only open it in case of total correctness
604 if (VerifyInteg(InfoArray[iAppId].szSHA1.GetString(), Path))
605 goto run;
606 }
607
608 // Add the download URL
609 SetDlgItemTextW(hDlg, IDC_DOWNLOAD_STATUS, InfoArray[iAppId].szUrl.GetString());
610
611 DownloadsListView.SetDownloadStatus(iAppId, DLSTATUS_DOWNLOADING);
612
613 // download it
614 UrlHasBeenCopied = FALSE;
615 bTempfile = TRUE;
616
617 /* FIXME: this should just be using the system-wide proxy settings */
618 switch (SettingsInfo.Proxy)
619 {
620 case 0: // preconfig
621 default:
622 hOpen = InternetOpenW(lpszAgent, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
623 break;
624 case 1: // direct (no proxy)
625 hOpen = InternetOpenW(lpszAgent, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
626 break;
627 case 2: // use proxy
628 hOpen = InternetOpenW(lpszAgent, INTERNET_OPEN_TYPE_PROXY, SettingsInfo.szProxyServer, SettingsInfo.szNoProxyFor, 0);
629 break;
630 }
631
632 if (!hOpen)
633 goto end;
634
635 dwStatusLen = sizeof(dwStatus);
636
637 memset(&urlComponents, 0, sizeof(urlComponents));
638 urlComponents.dwStructSize = sizeof(urlComponents);
639
640 urlLength = InfoArray[iAppId].szUrl.GetLength();
641 urlComponents.dwSchemeLength = urlLength + 1;
642 urlComponents.lpszScheme = (LPWSTR) malloc(urlComponents.dwSchemeLength * sizeof(WCHAR));
643 urlComponents.dwHostNameLength = urlLength + 1;
644 urlComponents.lpszHostName = (LPWSTR) malloc(urlComponents.dwHostNameLength * sizeof(WCHAR));
645
646 if (!InternetCrackUrlW(InfoArray[iAppId].szUrl, urlLength + 1, ICU_DECODE | ICU_ESCAPE, &urlComponents))
647 goto end;
648
649 dwContentLen = 0;
650
651 if (urlComponents.nScheme == INTERNET_SCHEME_HTTP || urlComponents.nScheme == INTERNET_SCHEME_HTTPS)
652 {
653 hFile = InternetOpenUrlW(hOpen, InfoArray[iAppId].szUrl.GetString(), NULL, 0,
654 dwUrlConnectFlags,
655 0);
656 if (!hFile)
657 {
658 MessageBox_LoadString(hMainWnd, IDS_UNABLE_TO_DOWNLOAD2);
659 goto end;
660 }
661
662 // query connection
663 if (!HttpQueryInfoW(hFile, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &dwStatus, &dwStatusLen, NULL))
664 goto end;
665
666 if (dwStatus != HTTP_STATUS_OK)
667 {
668 MessageBox_LoadString(hMainWnd, IDS_UNABLE_TO_DOWNLOAD);
669 goto end;
670 }
671
672 // query content length
673 HttpQueryInfoW(hFile, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &dwContentLen, &dwStatusLen, NULL);
674 }
675
676 if (urlComponents.nScheme == INTERNET_SCHEME_FTP)
677 {
678 // force passive mode on FTP
679 hFile = InternetOpenUrlW(hOpen, InfoArray[iAppId].szUrl.GetString(), NULL, 0,
680 dwUrlConnectFlags | INTERNET_FLAG_PASSIVE,
681 0);
682 if (!hFile)
683 {
684 MessageBox_LoadString(hMainWnd, IDS_UNABLE_TO_DOWNLOAD2);
685 goto end;
686 }
687
688 dwContentLen = FtpGetFileSize(hFile, &dwStatus);
689 }
690
691 if (!dwContentLen)
692 {
693 // content-length is not known, enable marquee mode
694 ProgressBar.SetMarquee(TRUE);
695 }
696
697 free(urlComponents.lpszScheme);
698 free(urlComponents.lpszHostName);
699
700 #ifdef USE_CERT_PINNING
701 // are we using HTTPS to download the RAPPS update package? check if the certificate is original
702 if ((urlComponents.nScheme == INTERNET_SCHEME_HTTPS) &&
703 (wcscmp(InfoArray[iAppId].szUrl, APPLICATION_DATABASE_URL) == 0))
704 {
705 CLocalPtr subjectName, issuerName;
706 CStringW szMsgText;
707 bool bAskQuestion = false;
708 if (!CertGetSubjectAndIssuer(hFile, subjectName, issuerName))
709 {
710 szMsgText.LoadStringW(IDS_UNABLE_TO_QUERY_CERT);
711 bAskQuestion = true;
712 }
713 else
714 {
715 if (strcmp(subjectName, CERT_SUBJECT_INFO) ||
716 strcmp(issuerName, CERT_ISSUER_INFO))
717 {
718 szMsgText.Format(IDS_MISMATCH_CERT_INFO, (char*)subjectName, (const char*)issuerName);
719 bAskQuestion = true;
720 }
721 }
722
723 if (bAskQuestion)
724 {
725 if (MessageBoxW(hMainWnd, szMsgText.GetString(), NULL, MB_YESNO | MB_ICONERROR) != IDYES)
726 {
727 goto end;
728 }
729 }
730 }
731 #endif
732
733 hOut = CreateFileW(Path.GetString(), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL);
734
735 if (hOut == INVALID_HANDLE_VALUE)
736 goto end;
737
738 dwCurrentBytesRead = 0;
739 do
740 {
741 if (!InternetReadFile(hFile, lpBuffer, _countof(lpBuffer), &dwBytesRead))
742 {
743 MessageBox_LoadString(hMainWnd, IDS_INTERRUPTED_DOWNLOAD);
744 goto end;
745 }
746
747 if (!WriteFile(hOut, &lpBuffer[0], dwBytesRead, &dwBytesWritten, NULL))
748 {
749 MessageBox_LoadString(hMainWnd, IDS_UNABLE_TO_WRITE);
750 goto end;
751 }
752
753 dwCurrentBytesRead += dwBytesRead;
754 UpdateProgress(hDlg, dwCurrentBytesRead, dwContentLen, 0, InfoArray[iAppId].szUrl.GetString());
755 } while (dwBytesRead && !bCancelled);
756
757 CloseHandle(hOut);
758 hOut = INVALID_HANDLE_VALUE;
759
760 if (bCancelled)
761 goto end;
762
763 if (!dwContentLen)
764 {
765 // set progress bar to 100%
766 ProgressBar.SetMarquee(FALSE);
767
768 dwContentLen = dwCurrentBytesRead;
769 UpdateProgress(hDlg, dwCurrentBytesRead, dwContentLen, 0, InfoArray[iAppId].szUrl.GetString());
770 }
771
772 /* if this thing isn't a RAPPS update and it has a SHA-1 checksum
773 verify its integrity by using the native advapi32.A_SHA1 functions */
774 if (!bCab && InfoArray[iAppId].szSHA1[0] != 0)
775 {
776 ATL::CStringW szMsgText;
777
778 // change a few strings in the download dialog to reflect the verification process
779 if (!szMsgText.LoadStringW(IDS_INTEG_CHECK_TITLE))
780 goto end;
781
782 SetWindowTextW(hDlg, szMsgText.GetString());
783 SendMessageW(GetDlgItem(hDlg, IDC_DOWNLOAD_STATUS), WM_SETTEXT, 0, (LPARAM) Path.GetString());
784
785 // this may take a while, depending on the file size
786 if (!VerifyInteg(InfoArray[iAppId].szSHA1.GetString(), Path.GetString()))
787 {
788 if (!szMsgText.LoadStringW(IDS_INTEG_CHECK_FAIL))
789 goto end;
790
791 MessageBoxW(hDlg, szMsgText.GetString(), NULL, MB_OK | MB_ICONERROR);
792 goto end;
793 }
794 }
795
796 run:
797 DownloadsListView.SetDownloadStatus(iAppId, DLSTATUS_WAITING_INSTALL);
798
799 // run it
800 if (!bCab)
801 {
802 SHELLEXECUTEINFOW shExInfo = {0};
803 shExInfo.cbSize = sizeof(shExInfo);
804 shExInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
805 shExInfo.lpVerb = L"open";
806 shExInfo.lpFile = Path.GetString();
807 shExInfo.lpParameters = L"";
808 shExInfo.nShow = SW_SHOW;
809
810 if (ShellExecuteExW(&shExInfo))
811 {
812 //reflect installation progress in the titlebar
813 //TODO: make a separate string with a placeholder to include app name?
814 ATL::CStringW szMsgText = LoadStatusString(DLSTATUS_INSTALLING);
815 SetWindowTextW(hDlg, szMsgText.GetString());
816
817 DownloadsListView.SetDownloadStatus(iAppId, DLSTATUS_INSTALLING);
818
819 //TODO: issue an install operation separately so that the apps could be downloaded in the background
820 WaitForSingleObject(shExInfo.hProcess, INFINITE);
821 CloseHandle(shExInfo.hProcess);
822 }
823 else
824 {
825 MessageBox_LoadString(hMainWnd, IDS_UNABLE_TO_INSTALL);
826 }
827 }
828
829 end:
830 if (hOut != INVALID_HANDLE_VALUE)
831 CloseHandle(hOut);
832
833 InternetCloseHandle(hFile);
834 InternetCloseHandle(hOpen);
835
836 if (bTempfile)
837 {
838 if (bCancelled || (SettingsInfo.bDelInstaller && !bCab))
839 DeleteFileW(Path.GetString());
840 }
841
842 DownloadsListView.SetDownloadStatus(iAppId, DLSTATUS_FINISHED);
843 }
844
845 delete static_cast<DownloadParam*>(param);
846 SendMessageW(hDlg, WM_CLOSE, 0, 0);
847 return 0;
848 }
849
850 //TODO: Reuse the dialog
851 VOID CDownloadManager::LaunchDownloadDialog(BOOL bIsModal)
852 {
853 if (bIsModal)
854 {
855 DialogBoxW(hInst,
856 MAKEINTRESOURCEW(IDD_DOWNLOAD_DIALOG),
857 hMainWnd,
858 DownloadDlgProc);
859 }
860 else
861 {
862 CreateDialogW(hInst,
863 MAKEINTRESOURCEW(IDD_DOWNLOAD_DIALOG),
864 hMainWnd,
865 DownloadDlgProc);
866 }
867 }
868 // CDownloadManager
869
870
871 BOOL DownloadListOfApplications(const ATL::CSimpleArray<CAvailableApplicationInfo>& AppsList, BOOL bIsModal)
872 {
873 if (AppsList.GetSize() == 0)
874 return FALSE;
875
876 // Initialize shared variables
877 for (INT i = 0; i < AppsList.GetSize(); ++i)
878 {
879 CDownloadManager::Add(AppsList[i]); // implicit conversion to DownloadInfo
880 }
881
882 // Create a dialog and issue a download process
883 CDownloadManager::LaunchDownloadDialog(bIsModal);
884
885 return TRUE;
886 }
887
888 BOOL DownloadApplication(CAvailableApplicationInfo* pAppInfo, BOOL bIsModal)
889 {
890 if (!pAppInfo)
891 return FALSE;
892
893 CDownloadManager::Download(*pAppInfo, bIsModal);
894 return TRUE;
895 }
896
897 VOID DownloadApplicationsDB(LPCWSTR lpUrl)
898 {
899 static DownloadInfo DatabaseDLInfo;
900 DatabaseDLInfo.szUrl = lpUrl;
901 DatabaseDLInfo.szName.LoadStringW(IDS_DL_DIALOG_DB_DISP);
902 CDownloadManager::Download(DatabaseDLInfo, TRUE);
903 }
904