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