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