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