other-licenses/nsis/Contrib/InetBgDL/InetBgDL.cpp

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

michael@0 1 //
michael@0 2 // Copyright (C) Anders Kjersem. Licensed under the zlib/libpng license
michael@0 3 //
michael@0 4
michael@0 5 #include "InetBgDL.h"
michael@0 6
michael@0 7 #define USERAGENT _T("NSIS InetBgDL (Mozilla)")
michael@0 8
michael@0 9 #define STATUS_COMPLETEDALL 0
michael@0 10 #define STATUS_INITIAL 202
michael@0 11 #define STATUS_CONNECTING STATUS_INITIAL //102
michael@0 12 #define STATUS_DOWNLOADING STATUS_INITIAL
michael@0 13 #define STATUS_ERR_GETLASTERROR 418 //HTTP: I'm a teapot: Win32 error code in $3
michael@0 14 #define STATUS_ERR_LOCALFILEWRITEERROR 450 //HTTP: MS parental control extension
michael@0 15 #define STATUS_ERR_CANCELLED 499
michael@0 16
michael@0 17 typedef DWORD FILESIZE_T; // Limit to 4GB for now...
michael@0 18 #define FILESIZE_UNKNOWN (-1)
michael@0 19
michael@0 20 #define MAX_STRLEN 1024
michael@0 21
michael@0 22 HINSTANCE g_hInst;
michael@0 23 NSIS::stack_t*g_pLocations = NULL;
michael@0 24 HANDLE g_hThread = NULL;
michael@0 25 HANDLE g_hGETStartedEvent = NULL;
michael@0 26 volatile UINT g_FilesTotal = 0;
michael@0 27 volatile UINT g_FilesCompleted = 0;
michael@0 28 volatile UINT g_Status = STATUS_INITIAL;
michael@0 29 volatile FILESIZE_T g_cbCurrXF;
michael@0 30 volatile FILESIZE_T g_cbCurrTot = FILESIZE_UNKNOWN;
michael@0 31 CRITICAL_SECTION g_CritLock;
michael@0 32 UINT g_N_CCH;
michael@0 33 PTSTR g_N_Vars;
michael@0 34 TCHAR g_ServerIP[128] = { _T('\0') };
michael@0 35
michael@0 36 DWORD g_ConnectTimeout = 0;
michael@0 37 DWORD g_ReceiveTimeout = 0;
michael@0 38
michael@0 39 #define NSISPI_INITGLOBALS(N_CCH, N_Vars) do { \
michael@0 40 g_N_CCH = N_CCH; \
michael@0 41 g_N_Vars = N_Vars; \
michael@0 42 } while(0)
michael@0 43
michael@0 44 #define ONELOCKTORULETHEMALL
michael@0 45 #ifdef ONELOCKTORULETHEMALL
michael@0 46 #define TaskLock_AcquireExclusive() EnterCriticalSection(&g_CritLock)
michael@0 47 #define TaskLock_ReleaseExclusive() LeaveCriticalSection(&g_CritLock)
michael@0 48 #define StatsLock_AcquireExclusive() TaskLock_AcquireExclusive()
michael@0 49 #define StatsLock_ReleaseExclusive() TaskLock_ReleaseExclusive()
michael@0 50 #define StatsLock_AcquireShared() StatsLock_AcquireExclusive()
michael@0 51 #define StatsLock_ReleaseShared() StatsLock_ReleaseExclusive()
michael@0 52 #endif
michael@0 53
michael@0 54 PTSTR NSIS_SetRegStr(UINT Reg, LPCTSTR Value)
michael@0 55 {
michael@0 56 PTSTR s = g_N_Vars + (Reg * g_N_CCH);
michael@0 57 lstrcpy(s, Value);
michael@0 58 return s;
michael@0 59 }
michael@0 60 #define NSIS_SetRegStrEmpty(r) NSIS_SetRegStr(r, _T(""))
michael@0 61 void NSIS_SetRegUINT(UINT Reg, UINT Value)
michael@0 62 {
michael@0 63 TCHAR buf[32];
michael@0 64 wsprintf(buf, _T("%u"), Value);
michael@0 65 NSIS_SetRegStr(Reg, buf);
michael@0 66 }
michael@0 67 #define StackFreeItem(pI) GlobalFree(pI)
michael@0 68 NSIS::stack_t* StackPopItem(NSIS::stack_t**ppST)
michael@0 69 {
michael@0 70 if (*ppST)
michael@0 71 {
michael@0 72 NSIS::stack_t*pItem = *ppST;
michael@0 73 *ppST = pItem->next;
michael@0 74 return pItem;
michael@0 75 }
michael@0 76 return NULL;
michael@0 77 }
michael@0 78
michael@0 79 void Reset()
michael@0 80 {
michael@0 81 // The g_hGETStartedEvent event is used to make sure that the Get() call will
michael@0 82 // acquire the lock before the Reset() call acquires the lock.
michael@0 83 if (g_hGETStartedEvent) {
michael@0 84 TRACE(_T("InetBgDl: waiting on g_hGETStartedEvent\n"));
michael@0 85 WaitForSingleObject(g_hGETStartedEvent, INFINITE);
michael@0 86 CloseHandle(g_hGETStartedEvent);
michael@0 87 g_hGETStartedEvent = NULL;
michael@0 88 }
michael@0 89
michael@0 90 TaskLock_AcquireExclusive();
michael@0 91 #ifndef ONELOCKTORULETHEMALL
michael@0 92 StatsLock_AcquireExclusive();
michael@0 93 #endif
michael@0 94 g_FilesTotal = 0; // This causes the Task thread to exit the transfer loop
michael@0 95 if (g_hThread)
michael@0 96 {
michael@0 97 TRACE(_T("InetBgDl: waiting on g_hThread\n"));
michael@0 98 if (WAIT_OBJECT_0 != WaitForSingleObject(g_hThread, 10 * 1000))
michael@0 99 {
michael@0 100 TRACE(_T("InetBgDl: terminating g_hThread\n"));
michael@0 101 TerminateThread(g_hThread, ERROR_OPERATION_ABORTED);
michael@0 102 }
michael@0 103 CloseHandle(g_hThread);
michael@0 104 g_hThread = NULL;
michael@0 105 }
michael@0 106 g_FilesTotal = 0;
michael@0 107 g_FilesCompleted = 0;
michael@0 108 g_Status = STATUS_INITIAL;
michael@0 109 #ifndef ONELOCKTORULETHEMALL
michael@0 110 StatsLock_ReleaseExclusive();
michael@0 111 #endif
michael@0 112 for (NSIS::stack_t*pTmpTast,*pTask = g_pLocations; pTask ;)
michael@0 113 {
michael@0 114 pTmpTast = pTask;
michael@0 115 pTask = pTask->next;
michael@0 116 StackFreeItem(pTmpTast);
michael@0 117 }
michael@0 118 g_pLocations = NULL;
michael@0 119 TaskLock_ReleaseExclusive();
michael@0 120 }
michael@0 121
michael@0 122 UINT_PTR __cdecl NSISPluginCallback(UINT Event)
michael@0 123 {
michael@0 124 switch(Event)
michael@0 125 {
michael@0 126 case NSPIM_UNLOAD:
michael@0 127 Reset();
michael@0 128 break;
michael@0 129 }
michael@0 130 return NULL;
michael@0 131 }
michael@0 132
michael@0 133 void __stdcall InetStatusCallback(HINTERNET hInternet, DWORD_PTR dwContext,
michael@0 134 DWORD dwInternetStatus,
michael@0 135 LPVOID lpvStatusInformation,
michael@0 136 DWORD dwStatusInformationLength)
michael@0 137 {
michael@0 138 if (dwInternetStatus == INTERNET_STATUS_NAME_RESOLVED) {
michael@0 139 // The documentation states the IP address is a PCTSTR but it is usually a
michael@0 140 // PCSTR and only sometimes a PCTSTR.
michael@0 141 StatsLock_AcquireExclusive();
michael@0 142 wsprintf(g_ServerIP, _T("%S"), lpvStatusInformation);
michael@0 143 if (wcslen(g_ServerIP) == 1)
michael@0 144 {
michael@0 145 wsprintf(g_ServerIP, _T("%s"), lpvStatusInformation);
michael@0 146 }
michael@0 147 StatsLock_ReleaseExclusive();
michael@0 148 }
michael@0 149
michael@0 150 #if defined(PLUGIN_DEBUG)
michael@0 151 switch (dwInternetStatus)
michael@0 152 {
michael@0 153 case INTERNET_STATUS_RESOLVING_NAME:
michael@0 154 TRACE(_T("InetBgDl: INTERNET_STATUS_RESOLVING_NAME (%d), name=%s\n"),
michael@0 155 dwStatusInformationLength, lpvStatusInformation);
michael@0 156 break;
michael@0 157 case INTERNET_STATUS_NAME_RESOLVED:
michael@0 158 TRACE(_T("InetBgDl: INTERNET_STATUS_NAME_RESOLVED (%d), resolved name=%s\n"),
michael@0 159 dwStatusInformationLength, g_ServerIP);
michael@0 160 break;
michael@0 161 case INTERNET_STATUS_CONNECTING_TO_SERVER:
michael@0 162 TRACE(_T("InetBgDl: INTERNET_STATUS_CONNECTING_TO_SERVER (%d)\n"),
michael@0 163 dwStatusInformationLength);
michael@0 164 break;
michael@0 165 case INTERNET_STATUS_CONNECTED_TO_SERVER:
michael@0 166 TRACE(_T("InetBgDl: INTERNET_STATUS_CONNECTED_TO_SERVER (%d)\n"),
michael@0 167 dwStatusInformationLength);
michael@0 168 break;
michael@0 169 case INTERNET_STATUS_SENDING_REQUEST:
michael@0 170 TRACE(_T("InetBgDl: INTERNET_STATUS_SENDING_REQUEST (%d)\n"),
michael@0 171 dwStatusInformationLength);
michael@0 172 break;
michael@0 173 case INTERNET_STATUS_REQUEST_SENT:
michael@0 174 TRACE(_T("InetBgDl: INTERNET_STATUS_REQUEST_SENT (%d), bytes sent=%d\n"),
michael@0 175 dwStatusInformationLength, lpvStatusInformation);
michael@0 176 break;
michael@0 177 case INTERNET_STATUS_RECEIVING_RESPONSE:
michael@0 178 TRACE(_T("InetBgDl: INTERNET_STATUS_RECEIVING_RESPONSE (%d)\n"),
michael@0 179 dwStatusInformationLength);
michael@0 180 break;
michael@0 181 case INTERNET_STATUS_RESPONSE_RECEIVED:
michael@0 182 TRACE(_T("InetBgDl: INTERNET_STATUS_RESPONSE_RECEIVED (%d)\n"),
michael@0 183 dwStatusInformationLength);
michael@0 184 break;
michael@0 185 case INTERNET_STATUS_CTL_RESPONSE_RECEIVED:
michael@0 186 TRACE(_T("InetBgDl: INTERNET_STATUS_CTL_RESPONSE_RECEIVED (%d)\n"),
michael@0 187 dwStatusInformationLength);
michael@0 188 break;
michael@0 189 case INTERNET_STATUS_PREFETCH:
michael@0 190 TRACE(_T("InetBgDl: INTERNET_STATUS_PREFETCH (%d)\n"),
michael@0 191 dwStatusInformationLength);
michael@0 192 break;
michael@0 193 case INTERNET_STATUS_CLOSING_CONNECTION:
michael@0 194 TRACE(_T("InetBgDl: INTERNET_STATUS_CLOSING_CONNECTION (%d)\n"),
michael@0 195 dwStatusInformationLength);
michael@0 196 break;
michael@0 197 case INTERNET_STATUS_CONNECTION_CLOSED:
michael@0 198 TRACE(_T("InetBgDl: INTERNET_STATUS_CONNECTION_CLOSED (%d)\n"),
michael@0 199 dwStatusInformationLength);
michael@0 200 break;
michael@0 201 case INTERNET_STATUS_HANDLE_CREATED:
michael@0 202 TRACE(_T("InetBgDl: INTERNET_STATUS_HANDLE_CREATED (%d)\n"),
michael@0 203 dwStatusInformationLength);
michael@0 204 break;
michael@0 205 case INTERNET_STATUS_HANDLE_CLOSING:
michael@0 206 TRACE(_T("InetBgDl: INTERNET_STATUS_HANDLE_CLOSING (%d)\n"),
michael@0 207 dwStatusInformationLength);
michael@0 208 break;
michael@0 209 case INTERNET_STATUS_DETECTING_PROXY:
michael@0 210 TRACE(_T("InetBgDl: INTERNET_STATUS_DETECTING_PROXY (%d)\n"),
michael@0 211 dwStatusInformationLength);
michael@0 212 break;
michael@0 213 case INTERNET_STATUS_REQUEST_COMPLETE:
michael@0 214 TRACE(_T("InetBgDl: INTERNET_STATUS_REQUEST_COMPLETE (%d)\n"),
michael@0 215 dwStatusInformationLength);
michael@0 216 break;
michael@0 217 case INTERNET_STATUS_REDIRECT:
michael@0 218 TRACE(_T("InetBgDl: INTERNET_STATUS_REDIRECT (%d), new url=%s\n"),
michael@0 219 dwStatusInformationLength, lpvStatusInformation);
michael@0 220 break;
michael@0 221 case INTERNET_STATUS_INTERMEDIATE_RESPONSE:
michael@0 222 TRACE(_T("InetBgDl: INTERNET_STATUS_INTERMEDIATE_RESPONSE (%d)\n"),
michael@0 223 dwStatusInformationLength);
michael@0 224 break;
michael@0 225 case INTERNET_STATUS_USER_INPUT_REQUIRED:
michael@0 226 TRACE(_T("InetBgDl: INTERNET_STATUS_USER_INPUT_REQUIRED (%d)\n"),
michael@0 227 dwStatusInformationLength);
michael@0 228 break;
michael@0 229 case INTERNET_STATUS_STATE_CHANGE:
michael@0 230 TRACE(_T("InetBgDl: INTERNET_STATUS_STATE_CHANGE (%d)\n"),
michael@0 231 dwStatusInformationLength);
michael@0 232 break;
michael@0 233 case INTERNET_STATUS_COOKIE_SENT:
michael@0 234 TRACE(_T("InetBgDl: INTERNET_STATUS_COOKIE_SENT (%d)\n"),
michael@0 235 dwStatusInformationLength);
michael@0 236 break;
michael@0 237 case INTERNET_STATUS_COOKIE_RECEIVED:
michael@0 238 TRACE(_T("InetBgDl: INTERNET_STATUS_COOKIE_RECEIVED (%d)\n"),
michael@0 239 dwStatusInformationLength);
michael@0 240 break;
michael@0 241 case INTERNET_STATUS_PRIVACY_IMPACTED:
michael@0 242 TRACE(_T("InetBgDl: INTERNET_STATUS_PRIVACY_IMPACTED (%d)\n"),
michael@0 243 dwStatusInformationLength);
michael@0 244 break;
michael@0 245 case INTERNET_STATUS_P3P_HEADER:
michael@0 246 TRACE(_T("InetBgDl: INTERNET_STATUS_P3P_HEADER (%d)\n"),
michael@0 247 dwStatusInformationLength);
michael@0 248 break;
michael@0 249 case INTERNET_STATUS_P3P_POLICYREF:
michael@0 250 TRACE(_T("InetBgDl: INTERNET_STATUS_P3P_POLICYREF (%d)\n"),
michael@0 251 dwStatusInformationLength);
michael@0 252 break;
michael@0 253 case INTERNET_STATUS_COOKIE_HISTORY:
michael@0 254 TRACE(_T("InetBgDl: INTERNET_STATUS_COOKIE_HISTORY (%d)\n"),
michael@0 255 dwStatusInformationLength);
michael@0 256 break;
michael@0 257 default:
michael@0 258 TRACE(_T("InetBgDl: Unknown Status %d\n"), dwInternetStatus);
michael@0 259 break;
michael@0 260 }
michael@0 261 #endif
michael@0 262 }
michael@0 263
michael@0 264 DWORD CALLBACK TaskThreadProc(LPVOID ThreadParam)
michael@0 265 {
michael@0 266 NSIS::stack_t *pURL,*pFile;
michael@0 267 HINTERNET hInetSes = NULL, hInetFile = NULL;
michael@0 268 DWORD cbio = sizeof(DWORD);
michael@0 269 HANDLE hLocalFile;
michael@0 270 bool completedFile = false;
michael@0 271 startnexttask:
michael@0 272 hLocalFile = INVALID_HANDLE_VALUE;
michael@0 273 pFile = NULL;
michael@0 274 TaskLock_AcquireExclusive();
michael@0 275 // Now that we've acquired the lock, we can set the event to indicate this.
michael@0 276 // SetEvent will likely never fail, but if it does we should set it to NULL
michael@0 277 // to avoid anyone waiting on it.
michael@0 278 if (!SetEvent(g_hGETStartedEvent)) {
michael@0 279 CloseHandle(g_hGETStartedEvent);
michael@0 280 g_hGETStartedEvent = NULL;
michael@0 281 }
michael@0 282 pURL = g_pLocations;
michael@0 283 if (pURL)
michael@0 284 {
michael@0 285 pFile = pURL->next;
michael@0 286 g_pLocations = pFile->next;
michael@0 287 }
michael@0 288 #ifndef ONELOCKTORULETHEMALL
michael@0 289 StatsLock_AcquireExclusive();
michael@0 290 #endif
michael@0 291 if (completedFile)
michael@0 292 {
michael@0 293 ++g_FilesCompleted;
michael@0 294 }
michael@0 295 completedFile = false;
michael@0 296 g_cbCurrXF = 0;
michael@0 297 g_cbCurrTot = FILESIZE_UNKNOWN;
michael@0 298 if (!pURL)
michael@0 299 {
michael@0 300 if (g_FilesTotal)
michael@0 301 {
michael@0 302 if (g_FilesTotal == g_FilesCompleted)
michael@0 303 {
michael@0 304 g_Status = STATUS_COMPLETEDALL;
michael@0 305 }
michael@0 306 }
michael@0 307 g_hThread = NULL;
michael@0 308 }
michael@0 309 #ifndef ONELOCKTORULETHEMALL
michael@0 310 StatsLock_ReleaseExclusive();
michael@0 311 #endif
michael@0 312 TaskLock_ReleaseExclusive();
michael@0 313
michael@0 314 if (!pURL)
michael@0 315 {
michael@0 316 if (0)
michael@0 317 {
michael@0 318 diegle:
michael@0 319 DWORD gle = GetLastError();
michael@0 320 //TODO? if (ERROR_INTERNET_EXTENDED_ERROR==gle) InternetGetLastResponseInfo(...)
michael@0 321 g_Status = STATUS_ERR_GETLASTERROR;
michael@0 322 }
michael@0 323 if (hInetSes)
michael@0 324 {
michael@0 325 InternetCloseHandle(hInetSes);
michael@0 326 }
michael@0 327 if (INVALID_HANDLE_VALUE != hLocalFile)
michael@0 328 {
michael@0 329 CloseHandle(hLocalFile);
michael@0 330 }
michael@0 331 StackFreeItem(pURL);
michael@0 332 StackFreeItem(pFile);
michael@0 333 return 0;
michael@0 334 }
michael@0 335
michael@0 336 if (!hInetSes)
michael@0 337 {
michael@0 338 hInetSes = InternetOpen(USERAGENT, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
michael@0 339 if (!hInetSes)
michael@0 340 {
michael@0 341 TRACE(_T("InetBgDl: InternetOpen failed with gle=%u\n"),
michael@0 342 GetLastError());
michael@0 343 goto diegle;
michael@0 344 }
michael@0 345 InternetSetStatusCallback(hInetSes, (INTERNET_STATUS_CALLBACK)InetStatusCallback);
michael@0 346
michael@0 347 //msdn.microsoft.com/library/default.asp?url=/workshop/components/offline/offline.asp#Supporting Offline Browsing in Applications and Components
michael@0 348 ULONG longOpt;
michael@0 349 DWORD cbio = sizeof(ULONG);
michael@0 350 if (InternetQueryOption(hInetSes, INTERNET_OPTION_CONNECTED_STATE, &longOpt, &cbio))
michael@0 351 {
michael@0 352 if (INTERNET_STATE_DISCONNECTED_BY_USER&longOpt)
michael@0 353 {
michael@0 354 INTERNET_CONNECTED_INFO ci = {INTERNET_STATE_CONNECTED, 0};
michael@0 355 InternetSetOption(hInetSes, INTERNET_OPTION_CONNECTED_STATE, &ci, sizeof(ci));
michael@0 356 }
michael@0 357 }
michael@0 358
michael@0 359 // Change the default connect timeout if specified.
michael@0 360 if(g_ConnectTimeout > 0)
michael@0 361 {
michael@0 362 InternetSetOption(hInetSes, INTERNET_OPTION_CONNECT_TIMEOUT,
michael@0 363 &g_ConnectTimeout, sizeof(g_ConnectTimeout));
michael@0 364 }
michael@0 365
michael@0 366 // Change the default receive timeout if specified.
michael@0 367 if (g_ReceiveTimeout)
michael@0 368 {
michael@0 369 InternetSetOption(hInetSes, INTERNET_OPTION_RECEIVE_TIMEOUT,
michael@0 370 &g_ReceiveTimeout, sizeof(DWORD));
michael@0 371 }
michael@0 372 }
michael@0 373
michael@0 374 DWORD ec = ERROR_SUCCESS;
michael@0 375 hLocalFile = CreateFile(pFile->text, GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_DELETE,NULL,CREATE_ALWAYS, 0, NULL);
michael@0 376 if (INVALID_HANDLE_VALUE == hLocalFile)
michael@0 377 {
michael@0 378 TRACE(_T("InetBgDl: CreateFile file handle invalid\n"));
michael@0 379 goto diegle;
michael@0 380 }
michael@0 381
michael@0 382 const DWORD IOURedirFlags = INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP |
michael@0 383 INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS;
michael@0 384 const DWORD IOUCacheFlags = INTERNET_FLAG_RESYNCHRONIZE |
michael@0 385 INTERNET_FLAG_NO_CACHE_WRITE |
michael@0 386 INTERNET_FLAG_PRAGMA_NOCACHE |
michael@0 387 INTERNET_FLAG_RELOAD;
michael@0 388 const DWORD IOUCookieFlags = INTERNET_FLAG_NO_COOKIES;
michael@0 389 DWORD IOUFlags = IOURedirFlags | IOUCacheFlags | IOUCookieFlags |
michael@0 390 INTERNET_FLAG_NO_UI | INTERNET_FLAG_EXISTING_CONNECT;
michael@0 391
michael@0 392 TCHAR *hostname = (TCHAR*) GlobalAlloc(GPTR, MAX_STRLEN * sizeof(TCHAR)),
michael@0 393 *urlpath = (TCHAR*) GlobalAlloc(GPTR, MAX_STRLEN * sizeof(TCHAR)),
michael@0 394 *extrainfo = (TCHAR*) GlobalAlloc(GPTR, MAX_STRLEN * sizeof(TCHAR));
michael@0 395
michael@0 396 URL_COMPONENTS uc = { sizeof(URL_COMPONENTS), NULL, 0, (INTERNET_SCHEME)0,
michael@0 397 hostname, MAX_STRLEN, (INTERNET_PORT)0, NULL, 0,
michael@0 398 NULL, 0, urlpath, MAX_STRLEN, extrainfo, MAX_STRLEN};
michael@0 399 uc.dwHostNameLength = uc.dwUrlPathLength = uc.dwExtraInfoLength = MAX_STRLEN;
michael@0 400
michael@0 401 if (!InternetCrackUrl(pURL->text, 0, ICU_ESCAPE, &uc))
michael@0 402 {
michael@0 403 // Bad url or param passed in
michael@0 404 TRACE(_T("InetBgDl: InternetCrackUrl false with url=%s, gle=%u\n"),
michael@0 405 pURL->text, GetLastError());
michael@0 406 goto diegle;
michael@0 407 }
michael@0 408
michael@0 409 TRACE(_T("InetBgDl: scheme_id=%d, hostname=%s, port=%d, urlpath=%s, extrainfo=%s\n"),
michael@0 410 uc.nScheme, hostname, uc.nPort, urlpath, extrainfo);
michael@0 411
michael@0 412 // Only http and https are supported
michael@0 413 if (uc.nScheme != INTERNET_SCHEME_HTTP &&
michael@0 414 uc.nScheme != INTERNET_SCHEME_HTTPS)
michael@0 415 {
michael@0 416 TRACE(_T("InetBgDl: only http and https is supported, aborting...\n"));
michael@0 417 goto diegle;
michael@0 418 }
michael@0 419
michael@0 420 TRACE(_T("InetBgDl: calling InternetOpenUrl with url=%s\n"), pURL->text);
michael@0 421 hInetFile = InternetOpenUrl(hInetSes, pURL->text,
michael@0 422 NULL, 0, IOUFlags |
michael@0 423 (uc.nScheme == INTERNET_SCHEME_HTTPS ?
michael@0 424 INTERNET_FLAG_SECURE : 0), 1);
michael@0 425 if (!hInetFile)
michael@0 426 {
michael@0 427 TRACE(_T("InetBgDl: InternetOpenUrl failed with gle=%u\n"),
michael@0 428 GetLastError());
michael@0 429 goto diegle;
michael@0 430 }
michael@0 431
michael@0 432 // Get the file length via the Content-Length header
michael@0 433 FILESIZE_T cbThisFile;
michael@0 434 cbio = sizeof(cbThisFile);
michael@0 435 if (!HttpQueryInfo(hInetFile,
michael@0 436 HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER,
michael@0 437 &cbThisFile, &cbio, NULL))
michael@0 438 {
michael@0 439 cbThisFile = FILESIZE_UNKNOWN;
michael@0 440 }
michael@0 441 TRACE(_T("InetBgDl: file size=%d bytes\n"), cbThisFile);
michael@0 442
michael@0 443 // Setup a buffer of size 256KiB to store the downloaded data.
michael@0 444 const UINT cbBufXF = 262144;
michael@0 445 // Use a 4MiB read buffer for the connection.
michael@0 446 // Bigger buffers will be faster.
michael@0 447 // cbReadBufXF should be a multiple of cbBufXF.
michael@0 448 const UINT cbReadBufXF = 4194304;
michael@0 449 BYTE bufXF[cbBufXF];
michael@0 450
michael@0 451 // Up the default internal buffer size from 4096 to internalReadBufferSize.
michael@0 452 DWORD internalReadBufferSize = cbReadBufXF;
michael@0 453 if (!InternetSetOption(hInetFile, INTERNET_OPTION_READ_BUFFER_SIZE,
michael@0 454 &internalReadBufferSize, sizeof(DWORD)))
michael@0 455 {
michael@0 456 TRACE(_T("InetBgDl: InternetSetOption failed to set read buffer size to %u bytes, gle=%u\n"),
michael@0 457 internalReadBufferSize, GetLastError());
michael@0 458
michael@0 459 // Maybe it's too big, try half of the optimal value. If that fails just
michael@0 460 // use the default.
michael@0 461 internalReadBufferSize /= 2;
michael@0 462 if (!InternetSetOption(hInetFile, INTERNET_OPTION_READ_BUFFER_SIZE,
michael@0 463 &internalReadBufferSize, sizeof(DWORD)))
michael@0 464 {
michael@0 465 TRACE(_T("InetBgDl: InternetSetOption failed to set read buffer size ") \
michael@0 466 _T("to %u bytes (using default read buffer size), gle=%u\n"),
michael@0 467 internalReadBufferSize, GetLastError());
michael@0 468 }
michael@0 469 }
michael@0 470
michael@0 471 for(;;)
michael@0 472 {
michael@0 473 DWORD cbio = 0, cbXF = 0;
michael@0 474 BOOL retXF = InternetReadFile(hInetFile, bufXF, cbBufXF, &cbio);
michael@0 475 if (!retXF)
michael@0 476 {
michael@0 477 ec = GetLastError();
michael@0 478 TRACE(_T("InetBgDl: InternetReadFile failed, gle=%u\n"), ec);
michael@0 479 break;
michael@0 480 }
michael@0 481
michael@0 482 if (0 == cbio)
michael@0 483 {
michael@0 484 ASSERT(ERROR_SUCCESS == ec);
michael@0 485 // EOF or broken connection?
michael@0 486 // TODO: Can InternetQueryDataAvailable detect this?
michael@0 487
michael@0 488 TRACE(_T("InetBgDl: InternetReadFile true with 0 cbio, cbThisFile=%d, gle=%u\n"),
michael@0 489 cbThisFile, GetLastError());
michael@0 490 // If we haven't transferred all of the file, and we know how big the file
michael@0 491 // is, and we have no more data to read from the HTTP request, then set a
michael@0 492 // broken pipe error. Reading without StatsLock is ok in this thread.
michael@0 493 if (FILESIZE_UNKNOWN != cbThisFile && g_cbCurrXF != cbThisFile)
michael@0 494 {
michael@0 495 TRACE(_T("InetBgDl: downloaded file size of %d bytes doesn't equal ") \
michael@0 496 _T("expected file size of %d bytes\n"), g_cbCurrXF, cbThisFile);
michael@0 497 ec = ERROR_BROKEN_PIPE;
michael@0 498 }
michael@0 499 break;
michael@0 500 }
michael@0 501
michael@0 502 // Check if we canceled the download
michael@0 503 if (0 == g_FilesTotal)
michael@0 504 {
michael@0 505 TRACE(_T("InetBgDl: 0 == g_FilesTotal, aborting transfer loop...\n"));
michael@0 506 ec = ERROR_CANCELLED;
michael@0 507 break;
michael@0 508 }
michael@0 509
michael@0 510 cbXF = cbio;
michael@0 511 if (cbXF)
michael@0 512 {
michael@0 513 retXF = WriteFile(hLocalFile, bufXF, cbXF, &cbio, NULL);
michael@0 514 if (!retXF || cbXF != cbio)
michael@0 515 {
michael@0 516 ec = GetLastError();
michael@0 517 break;
michael@0 518 }
michael@0 519
michael@0 520 StatsLock_AcquireExclusive();
michael@0 521 if (FILESIZE_UNKNOWN != cbThisFile) {
michael@0 522 g_cbCurrTot = cbThisFile;
michael@0 523 }
michael@0 524 g_cbCurrXF += cbXF;
michael@0 525 StatsLock_ReleaseExclusive();
michael@0 526 }
michael@0 527 }
michael@0 528
michael@0 529 TRACE(_T("InetBgDl: TaskThreadProc completed %s, ec=%u\n"), pURL->text, ec);
michael@0 530 InternetCloseHandle(hInetFile);
michael@0 531 if (ERROR_SUCCESS == ec)
michael@0 532 {
michael@0 533 if (INVALID_HANDLE_VALUE != hLocalFile)
michael@0 534 {
michael@0 535 CloseHandle(hLocalFile);
michael@0 536 hLocalFile = INVALID_HANDLE_VALUE;
michael@0 537 }
michael@0 538 StackFreeItem(pURL);
michael@0 539 StackFreeItem(pFile);
michael@0 540 ++completedFile;
michael@0 541 }
michael@0 542 else
michael@0 543 {
michael@0 544 TRACE(_T("InetBgDl: failed with ec=%u\n"), ec);
michael@0 545 SetLastError(ec);
michael@0 546 goto diegle;
michael@0 547 }
michael@0 548 goto startnexttask;
michael@0 549 }
michael@0 550
michael@0 551 NSISPIEXPORTFUNC Get(HWND hwndNSIS, UINT N_CCH, TCHAR*N_Vars, NSIS::stack_t**ppST, NSIS::xparams_t*pX)
michael@0 552 {
michael@0 553 pX->RegisterPluginCallback(g_hInst, NSISPluginCallback);
michael@0 554 for (;;)
michael@0 555 {
michael@0 556 NSIS::stack_t*pURL = StackPopItem(ppST);
michael@0 557 if (!pURL)
michael@0 558 {
michael@0 559 break;
michael@0 560 }
michael@0 561
michael@0 562 if (lstrcmpi(pURL->text, _T("/connecttimeout")) == 0)
michael@0 563 {
michael@0 564 NSIS::stack_t*pConnectTimeout = StackPopItem(ppST);
michael@0 565 g_ConnectTimeout = _tcstol(pConnectTimeout->text, NULL, 10) * 1000;
michael@0 566 continue;
michael@0 567 }
michael@0 568 else if (lstrcmpi(pURL->text, _T("/receivetimeout")) == 0)
michael@0 569 {
michael@0 570 NSIS::stack_t*pReceiveTimeout = StackPopItem(ppST);
michael@0 571 g_ReceiveTimeout = _tcstol(pReceiveTimeout->text, NULL, 10) * 1000;
michael@0 572 continue;
michael@0 573 }
michael@0 574 else if (lstrcmpi(pURL->text, _T("/reset")) == 0)
michael@0 575 {
michael@0 576 StackFreeItem(pURL);
michael@0 577 Reset();
michael@0 578 continue;
michael@0 579 }
michael@0 580 else if (lstrcmpi(pURL->text, _T("/end")) == 0)
michael@0 581 {
michael@0 582 freeurlandexit:
michael@0 583 StackFreeItem(pURL);
michael@0 584 break;
michael@0 585 }
michael@0 586
michael@0 587 NSIS::stack_t*pFile = StackPopItem(ppST);
michael@0 588 if (!pFile)
michael@0 589 {
michael@0 590 goto freeurlandexit;
michael@0 591 }
michael@0 592
michael@0 593 TaskLock_AcquireExclusive();
michael@0 594
michael@0 595 pFile->next = NULL;
michael@0 596 pURL->next = pFile;
michael@0 597 NSIS::stack_t*pTasksTail = g_pLocations;
michael@0 598 while(pTasksTail && pTasksTail->next) pTasksTail = pTasksTail->next;
michael@0 599 if (pTasksTail)
michael@0 600 {
michael@0 601 pTasksTail->next = pURL;
michael@0 602 }
michael@0 603 else
michael@0 604 {
michael@0 605 g_pLocations = pURL;
michael@0 606 }
michael@0 607
michael@0 608 if (!g_hThread)
michael@0 609 {
michael@0 610 DWORD tid;
michael@0 611 if (g_hGETStartedEvent) {
michael@0 612 CloseHandle(g_hGETStartedEvent);
michael@0 613 }
michael@0 614 g_hGETStartedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
michael@0 615 g_hThread = CreateThread(NULL, 0, TaskThreadProc, NULL, 0, &tid);
michael@0 616 }
michael@0 617
michael@0 618 if (!g_hThread)
michael@0 619 {
michael@0 620 goto freeurlandexit;
michael@0 621 }
michael@0 622
michael@0 623 #ifndef ONELOCKTORULETHEMALL
michael@0 624 StatsLock_AcquireExclusive();
michael@0 625 #endif
michael@0 626 ++g_FilesTotal;
michael@0 627 #ifndef ONELOCKTORULETHEMALL
michael@0 628 StatsLock_ReleaseExclusive();
michael@0 629 #endif
michael@0 630 TaskLock_ReleaseExclusive();
michael@0 631 }
michael@0 632 }
michael@0 633
michael@0 634 NSISPIEXPORTFUNC GetStats(HWND hwndNSIS, UINT N_CCH, TCHAR*N_Vars, NSIS::stack_t**ppST, NSIS::xparams_t*pX)
michael@0 635 {
michael@0 636 NSISPI_INITGLOBALS(N_CCH, N_Vars);
michael@0 637 StatsLock_AcquireShared();
michael@0 638 NSIS_SetRegUINT(0, g_Status);
michael@0 639 NSIS_SetRegUINT(1, g_FilesCompleted);
michael@0 640 NSIS_SetRegUINT(2, g_FilesTotal - g_FilesCompleted);
michael@0 641 NSIS_SetRegUINT(3, g_cbCurrXF);
michael@0 642 NSIS_SetRegStrEmpty(4);
michael@0 643 if (FILESIZE_UNKNOWN != g_cbCurrTot)
michael@0 644 {
michael@0 645 NSIS_SetRegUINT(4, g_cbCurrTot);
michael@0 646 }
michael@0 647 NSIS_SetRegStr(5, g_ServerIP);
michael@0 648 StatsLock_ReleaseShared();
michael@0 649 }
michael@0 650
michael@0 651 EXTERN_C BOOL WINAPI _DllMainCRTStartup(HMODULE hInst, UINT Reason, LPVOID pCtx)
michael@0 652 {
michael@0 653 if (DLL_PROCESS_ATTACH==Reason)
michael@0 654 {
michael@0 655 g_hInst=hInst;
michael@0 656 InitializeCriticalSection(&g_CritLock);
michael@0 657 }
michael@0 658 return TRUE;
michael@0 659 }
michael@0 660
michael@0 661 BOOL WINAPI DllMain(HINSTANCE hInst, ULONG Reason, LPVOID pCtx)
michael@0 662 {
michael@0 663 return _DllMainCRTStartup(hInst, Reason, pCtx);
michael@0 664 }
michael@0 665
michael@0 666 // For some reason VC6++ doesn't like wcsicmp and swprintf.
michael@0 667 // If you use them, you get a linking error about _main
michael@0 668 // as an unresolved external.
michael@0 669 int main(int argc, char**argv)
michael@0 670 {
michael@0 671 return 0;
michael@0 672 }

mercurial