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