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