[RAPPS] Get rid of the flickering of the URL in the download dialog and decode it...
[reactos.git] / reactos / base / applications / rapps / loaddlg.c
1 /* PROJECT: ReactOS Applications Manager
2 * LICENSE: GPL - See COPYING in the top level directory
3 * FILE: base/applications/rapps/loaddlg.c
4 * PURPOSE: Displaying a download dialog
5 * COPYRIGHT: Copyright 2001 John R. Sheets (for CodeWeavers)
6 * Copyright 2004 Mike McCormack (for CodeWeavers)
7 * Copyright 2005 Ge van Geldorp (gvg@reactos.org)
8 * Copyright 2009 Dmitry Chapyshev (dmitry@reactos.org)
9 * Copyright 2015 Ismael Ferreras Morezuelas (swyterzone+ros@gmail.com)
10 */
11 /*
12 * Based on Wine dlls/shdocvw/shdocvw_main.c
13 *
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
18 *
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
23 *
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
27 */
28
29 #include "rapps.h"
30 #include <wininet.h>
31 #include <shellapi.h>
32
33 static PAPPLICATION_INFO AppInfo;
34
35 typedef struct _IBindStatusCallbackImpl
36 {
37 const IBindStatusCallbackVtbl *vtbl;
38 LONG ref;
39 HWND hDialog;
40 BOOL *pbCancelled;
41 BOOL UrlHasBeenCopied;
42 } IBindStatusCallbackImpl;
43
44 static
45 HRESULT WINAPI
46 dlQueryInterface(IBindStatusCallback* This, REFIID riid, void** ppvObject)
47 {
48 if (!ppvObject) return E_POINTER;
49
50 if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IBindStatusCallback))
51 {
52 IBindStatusCallback_AddRef(This);
53 *ppvObject = This;
54 return S_OK;
55 }
56
57 return E_NOINTERFACE;
58 }
59
60 static
61 ULONG WINAPI
62 dlAddRef(IBindStatusCallback* iface)
63 {
64 IBindStatusCallbackImpl *This = (IBindStatusCallbackImpl*) iface;
65 return InterlockedIncrement(&This->ref);
66 }
67
68 static
69 ULONG WINAPI
70 dlRelease(IBindStatusCallback* iface)
71 {
72 IBindStatusCallbackImpl *This = (IBindStatusCallbackImpl*) iface;
73 DWORD ref = InterlockedDecrement(&This->ref);
74
75 if (!ref)
76 {
77 DestroyWindow(This->hDialog);
78 HeapFree(GetProcessHeap(), 0, This);
79 }
80
81 return ref;
82 }
83
84 static
85 HRESULT WINAPI
86 dlOnStartBinding(IBindStatusCallback* iface, DWORD dwReserved, IBinding* pib)
87 {
88 return S_OK;
89 }
90
91 static
92 HRESULT WINAPI
93 dlGetPriority(IBindStatusCallback* iface, LONG* pnPriority)
94 {
95 return S_OK;
96 }
97
98 static
99 HRESULT WINAPI
100 dlOnLowResource( IBindStatusCallback* iface, DWORD reserved)
101 {
102 return S_OK;
103 }
104
105 static
106 HRESULT WINAPI
107 dlOnProgress(IBindStatusCallback* iface,
108 ULONG ulProgress,
109 ULONG ulProgressMax,
110 ULONG ulStatusCode,
111 LPCWSTR szStatusText)
112 {
113 IBindStatusCallbackImpl *This = (IBindStatusCallbackImpl *) iface;
114 HWND Item;
115 LONG r;
116
117 Item = GetDlgItem(This->hDialog, IDC_DOWNLOAD_PROGRESS);
118 if (Item && ulProgressMax)
119 {
120 SendMessageW(Item, PBM_SETPOS, ((ULONGLONG)ulProgress * 100) / ulProgressMax, 0);
121 }
122
123 Item = GetDlgItem(This->hDialog, IDC_DOWNLOAD_STATUS);
124 if (Item && szStatusText && wcslen(szStatusText) > 0 && This->UrlHasBeenCopied == FALSE)
125 {
126 DWORD len = wcslen(szStatusText) * sizeof(WCHAR);
127 PWSTR buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len);
128
129 if (buf)
130 {
131 /* beautify our url for display purposes */
132 InternetCanonicalizeUrl(szStatusText, buf, &len, ICU_DECODE | ICU_NO_ENCODE);
133
134 /* paste it into our dialog, free the temp buffer
135 and don't do it again in this instance */
136 SendMessageW(Item, WM_SETTEXT, 0, (LPARAM)buf);
137 HeapFree(GetProcessHeap(), 0, buf);
138 }
139 else
140 {
141 /* our computer is old and rusty and does not have enough ram for this,
142 use the ugly version and call it a day */
143 SendMessageW(Item, WM_SETTEXT, 0, (LPARAM)szStatusText);
144 }
145
146 This->UrlHasBeenCopied = TRUE;
147 }
148
149 SetLastError(0);
150 r = GetWindowLongPtrW(This->hDialog, GWLP_USERDATA);
151 if (0 != r || 0 != GetLastError())
152 {
153 *This->pbCancelled = TRUE;
154 return E_ABORT;
155 }
156
157 return S_OK;
158 }
159
160 static
161 HRESULT WINAPI
162 dlOnStopBinding(IBindStatusCallback* iface, HRESULT hresult, LPCWSTR szError)
163 {
164 return S_OK;
165 }
166
167 static
168 HRESULT WINAPI
169 dlGetBindInfo(IBindStatusCallback* iface, DWORD* grfBINDF, BINDINFO* pbindinfo)
170 {
171 return S_OK;
172 }
173
174 static
175 HRESULT WINAPI
176 dlOnDataAvailable(IBindStatusCallback* iface, DWORD grfBSCF,
177 DWORD dwSize, FORMATETC* pformatetc, STGMEDIUM* pstgmed)
178 {
179 return S_OK;
180 }
181
182 static
183 HRESULT WINAPI
184 dlOnObjectAvailable(IBindStatusCallback* iface, REFIID riid, IUnknown* punk)
185 {
186 return S_OK;
187 }
188
189 static const IBindStatusCallbackVtbl dlVtbl =
190 {
191 dlQueryInterface,
192 dlAddRef,
193 dlRelease,
194 dlOnStartBinding,
195 dlGetPriority,
196 dlOnLowResource,
197 dlOnProgress,
198 dlOnStopBinding,
199 dlGetBindInfo,
200 dlOnDataAvailable,
201 dlOnObjectAvailable
202 };
203
204 static IBindStatusCallback*
205 CreateDl(HWND Dlg, BOOL *pbCancelled)
206 {
207 IBindStatusCallbackImpl *This;
208
209 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IBindStatusCallbackImpl));
210 if (!This)
211 return NULL;
212
213 This->vtbl = &dlVtbl;
214 This->ref = 1;
215 This->hDialog = Dlg;
216 This->pbCancelled = pbCancelled;
217
218 return (IBindStatusCallback*) This;
219 }
220
221 #ifdef USE_CERT_PINNING
222 static BOOL CertIsValid(HINTERNET hInternet, LPWSTR lpszHostName)
223 {
224 HINTERNET hConnect;
225 HINTERNET hRequest;
226 DWORD certInfoLength;
227 BOOL Ret = FALSE;
228 INTERNET_CERTIFICATE_INFOW certInfo;
229
230 hConnect = InternetConnectW(hInternet, lpszHostName, INTERNET_DEFAULT_HTTPS_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, INTERNET_FLAG_SECURE, 0);
231 if (hConnect)
232 {
233 hRequest = HttpOpenRequestW(hConnect, L"HEAD", NULL, NULL, NULL, NULL, INTERNET_FLAG_SECURE, 0);
234 if (hRequest != NULL)
235 {
236 Ret = HttpSendRequestW(hRequest, L"", 0, NULL, 0);
237 if (Ret)
238 {
239 certInfoLength = sizeof(INTERNET_CERTIFICATE_INFOW);
240 Ret = InternetQueryOptionW(hRequest,
241 INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT,
242 &certInfo,
243 &certInfoLength);
244 if (Ret)
245 {
246 if (certInfo.lpszEncryptionAlgName)
247 LocalFree(certInfo.lpszEncryptionAlgName);
248 if (certInfo.lpszIssuerInfo)
249 {
250 if (strcmp((LPSTR)certInfo.lpszIssuerInfo, CERT_ISSUER_INFO) != 0)
251 Ret = FALSE;
252 LocalFree(certInfo.lpszIssuerInfo);
253 }
254 if (certInfo.lpszProtocolName)
255 LocalFree(certInfo.lpszProtocolName);
256 if (certInfo.lpszSignatureAlgName)
257 LocalFree(certInfo.lpszSignatureAlgName);
258 if (certInfo.lpszSubjectInfo)
259 {
260 if (strcmp((LPSTR)certInfo.lpszSubjectInfo, CERT_SUBJECT_INFO) != 0)
261 Ret = FALSE;
262 LocalFree(certInfo.lpszSubjectInfo);
263 }
264 }
265 }
266 InternetCloseHandle(hRequest);
267 }
268 }
269 return Ret;
270 }
271 #endif
272
273 static
274 DWORD WINAPI
275 ThreadFunc(LPVOID Context)
276 {
277 IBindStatusCallback *dl = NULL;
278 WCHAR path[MAX_PATH];
279 LPWSTR p;
280 HWND Dlg = (HWND) Context;
281 DWORD dwContentLen, dwBytesWritten, dwBytesRead, dwStatus;
282 DWORD dwCurrentBytesRead = 0;
283 DWORD dwStatusLen = sizeof(dwStatus);
284 BOOL bCancelled = FALSE;
285 BOOL bTempfile = FALSE;
286 BOOL bCab = FALSE;
287 HINTERNET hOpen = NULL;
288 HINTERNET hFile = NULL;
289 HANDLE hOut = INVALID_HANDLE_VALUE;
290 unsigned char lpBuffer[4096];
291 const LPWSTR lpszAgent = L"RApps/1.0";
292 URL_COMPONENTS urlComponents;
293 size_t urlLength;
294
295 /* built the path for the download */
296 p = wcsrchr(AppInfo->szUrlDownload, L'/');
297
298 if (!p)
299 goto end;
300
301 if (wcscmp(AppInfo->szUrlDownload, APPLICATION_DATABASE_URL) == 0)
302 {
303 bCab = TRUE;
304 if (!GetStorageDirectory(path, sizeof(path) / sizeof(path[0])))
305 goto end;
306 }
307 else
308 {
309 if (FAILED(StringCbCopyW(path, sizeof(path), SettingsInfo.szDownloadDir)))
310 goto end;
311 }
312
313
314 if (GetFileAttributesW(path) == INVALID_FILE_ATTRIBUTES)
315 {
316 if (!CreateDirectoryW(path, NULL))
317 goto end;
318 }
319
320 if (FAILED(StringCbCatW(path, sizeof(path), L"\\")))
321 goto end;
322 if (FAILED(StringCbCatW(path, sizeof(path), p + 1)))
323 goto end;
324
325 /* download it */
326 bTempfile = TRUE;
327 dl = CreateDl(Context, &bCancelled);
328
329 if (dl == NULL)
330 goto end;
331
332 switch(SettingsInfo.Proxy)
333 {
334 case 0: /* preconfig */
335 hOpen = InternetOpenW(lpszAgent, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
336 break;
337 case 1: /* direct (no proxy) */
338 hOpen = InternetOpenW(lpszAgent, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
339 break;
340 case 2: /* use proxy */
341 hOpen = InternetOpenW(lpszAgent, INTERNET_OPEN_TYPE_PROXY, SettingsInfo.szProxyServer, SettingsInfo.szNoProxyFor, 0);
342 break;
343 default: /* preconfig */
344 hOpen = InternetOpenW(lpszAgent, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
345 break;
346 }
347
348 if (!hOpen)
349 goto end;
350
351 hFile = InternetOpenUrlW(hOpen, AppInfo->szUrlDownload, NULL, 0, INTERNET_FLAG_PRAGMA_NOCACHE|INTERNET_FLAG_KEEP_CONNECTION, 0);
352 if (!hFile)
353 goto end;
354
355 if (!HttpQueryInfoW(hFile, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &dwStatus, &dwStatusLen, NULL))
356 goto end;
357
358 if(dwStatus != HTTP_STATUS_OK)
359 {
360 WCHAR szMsgText[MAX_STR_LEN];
361
362 if (!LoadStringW(hInst, IDS_UNABLE_TO_DOWNLOAD, szMsgText, sizeof(szMsgText) / sizeof(WCHAR)))
363 goto end;
364
365 MessageBoxW(hMainWnd, szMsgText, NULL, MB_OK | MB_ICONERROR);
366 goto end;
367 }
368
369 dwStatusLen = sizeof(dwStatus);
370
371 memset(&urlComponents, 0, sizeof(urlComponents));
372 urlComponents.dwStructSize = sizeof(urlComponents);
373
374 if(FAILED(StringCbLengthW(AppInfo->szUrlDownload, sizeof(AppInfo->szUrlDownload), &urlLength)))
375 goto end;
376
377 urlComponents.dwSchemeLength = urlLength*sizeof(WCHAR);
378 urlComponents.lpszScheme = malloc(urlComponents.dwSchemeLength);
379 urlComponents.dwHostNameLength = urlLength*sizeof(WCHAR);
380 urlComponents.lpszHostName = malloc(urlComponents.dwHostNameLength);
381
382 if(!InternetCrackUrlW(AppInfo->szUrlDownload, urlLength+1, ICU_DECODE | ICU_ESCAPE, &urlComponents))
383 goto end;
384
385 if(urlComponents.nScheme == INTERNET_SCHEME_HTTP || urlComponents.nScheme == INTERNET_SCHEME_HTTPS)
386 HttpQueryInfo(hFile, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &dwContentLen, &dwStatus, 0);
387
388 if(urlComponents.nScheme == INTERNET_SCHEME_FTP)
389 dwContentLen = FtpGetFileSize(hFile, &dwStatus);
390
391 #ifdef USE_CERT_PINNING
392 if ((urlComponents.nScheme == INTERNET_SCHEME_HTTPS) && (!CertIsValid(hOpen, urlComponents.lpszHostName)))
393 {
394 WCHAR szMsgText[MAX_STR_LEN];
395
396 if (!LoadStringW(hInst, IDS_CERT_DOES_NOT_MATCH, szMsgText, sizeof(szMsgText) / sizeof(WCHAR)))
397 goto end;
398
399 MessageBoxW(hMainWnd, szMsgText, NULL, MB_OK | MB_ICONERROR);
400 goto end;
401 }
402 #endif
403
404 free(urlComponents.lpszScheme);
405 free(urlComponents.lpszHostName);
406
407 hOut = CreateFileW(path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL);
408
409 if (hOut == INVALID_HANDLE_VALUE)
410 goto end;
411
412 do
413 {
414 if (!InternetReadFile(hFile, lpBuffer, _countof(lpBuffer), &dwBytesRead)) goto end;
415 if (!WriteFile(hOut, &lpBuffer[0], dwBytesRead, &dwBytesWritten, NULL)) goto end;
416 dwCurrentBytesRead += dwBytesRead;
417 IBindStatusCallback_OnProgress(dl, dwCurrentBytesRead, dwContentLen, 0, AppInfo->szUrlDownload);
418 }
419 while (dwBytesRead);
420
421 CloseHandle(hOut);
422 hOut = INVALID_HANDLE_VALUE;
423
424 if (bCancelled)
425 goto end;
426
427 ShowWindow(Dlg, SW_HIDE);
428
429 /* run it */
430 if (!bCab)
431 ShellExecuteW( NULL, L"open", path, NULL, NULL, SW_SHOWNORMAL );
432
433 end:
434 if (hOut != INVALID_HANDLE_VALUE)
435 CloseHandle(hOut);
436
437 InternetCloseHandle(hFile);
438 InternetCloseHandle(hOpen);
439
440 if (dl)
441 IBindStatusCallback_Release(dl);
442
443 if (bTempfile)
444 {
445 if (bCancelled || (SettingsInfo.bDelInstaller && !bCab))
446 DeleteFileW(path);
447 }
448
449 EndDialog(Dlg, 0);
450
451 return 0;
452 }
453
454 static
455 INT_PTR CALLBACK
456 DownloadDlgProc(HWND Dlg, UINT Msg, WPARAM wParam, LPARAM lParam)
457 {
458 HANDLE Thread;
459 DWORD ThreadId;
460 HWND Item;
461
462 switch (Msg)
463 {
464 case WM_INITDIALOG:
465 {
466 HICON hIconSm = NULL, hIconBg = NULL;
467
468 hIconBg = (HICON)GetClassLongPtr(hMainWnd, GCLP_HICON);
469 hIconSm = (HICON)GetClassLongPtr(hMainWnd, GCLP_HICONSM);
470
471 if (hIconBg && hIconSm)
472 {
473 SendMessageW(Dlg, WM_SETICON, ICON_BIG, (LPARAM) hIconBg);
474 SendMessageW(Dlg, WM_SETICON, ICON_SMALL, (LPARAM) hIconSm);
475 }
476
477 SetWindowLongPtrW(Dlg, GWLP_USERDATA, 0);
478 Item = GetDlgItem(Dlg, IDC_DOWNLOAD_PROGRESS);
479 if (Item)
480 {
481 SendMessageW(Item, PBM_SETRANGE, 0, MAKELPARAM(0, 100));
482 SendMessageW(Item, PBM_SETPOS, 0, 0);
483 }
484
485 Thread = CreateThread(NULL, 0, ThreadFunc, Dlg, 0, &ThreadId);
486 if (!Thread) return FALSE;
487 CloseHandle(Thread);
488 return TRUE;
489 }
490
491 case WM_COMMAND:
492 if (wParam == IDCANCEL)
493 {
494 SetWindowLongPtrW(Dlg, GWLP_USERDATA, 1);
495 PostMessageW(Dlg, WM_CLOSE, 0, 0);
496 }
497 return FALSE;
498
499 case WM_CLOSE:
500 EndDialog(Dlg, 0);
501 return TRUE;
502
503 default:
504 return FALSE;
505 }
506 }
507
508 BOOL
509 DownloadApplication(INT Index)
510 {
511 if (!IS_AVAILABLE_ENUM(SelectedEnumType))
512 return FALSE;
513
514 AppInfo = (PAPPLICATION_INFO) ListViewGetlParam(Index);
515 if (!AppInfo) return FALSE;
516
517 WriteLogMessage(EVENTLOG_SUCCESS, MSG_SUCCESS_INSTALL, AppInfo->szName);
518
519 DialogBoxW(hInst,
520 MAKEINTRESOURCEW(IDD_DOWNLOAD_DIALOG),
521 hMainWnd,
522 DownloadDlgProc);
523
524 return TRUE;
525 }
526
527 VOID
528 DownloadApplicationsDB(LPWSTR lpUrl)
529 {
530 APPLICATION_INFO IntInfo;
531
532 ZeroMemory(&IntInfo, sizeof(APPLICATION_INFO));
533 if (FAILED(StringCbCopyW(IntInfo.szUrlDownload,
534 sizeof(IntInfo.szUrlDownload),
535 lpUrl)))
536 {
537 return;
538 }
539
540 AppInfo = &IntInfo;
541
542 DialogBoxW(hInst,
543 MAKEINTRESOURCEW(IDD_DOWNLOAD_DIALOG),
544 hMainWnd,
545 DownloadDlgProc);
546 }
547