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