|
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 |
|
17 typedef DWORD FILESIZE_T; // Limit to 4GB for now... |
|
18 #define FILESIZE_UNKNOWN (-1) |
|
19 |
|
20 #define MAX_STRLEN 1024 |
|
21 |
|
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') }; |
|
35 |
|
36 DWORD g_ConnectTimeout = 0; |
|
37 DWORD g_ReceiveTimeout = 0; |
|
38 |
|
39 #define NSISPI_INITGLOBALS(N_CCH, N_Vars) do { \ |
|
40 g_N_CCH = N_CCH; \ |
|
41 g_N_Vars = N_Vars; \ |
|
42 } while(0) |
|
43 |
|
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 |
|
53 |
|
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 } |
|
78 |
|
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 } |
|
89 |
|
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 } |
|
121 |
|
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 } |
|
132 |
|
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 } |
|
149 |
|
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 } |
|
263 |
|
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(); |
|
313 |
|
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 } |
|
335 |
|
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); |
|
346 |
|
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 } |
|
358 |
|
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 } |
|
365 |
|
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 } |
|
373 |
|
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 } |
|
381 |
|
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; |
|
391 |
|
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)); |
|
395 |
|
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; |
|
400 |
|
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 } |
|
408 |
|
409 TRACE(_T("InetBgDl: scheme_id=%d, hostname=%s, port=%d, urlpath=%s, extrainfo=%s\n"), |
|
410 uc.nScheme, hostname, uc.nPort, urlpath, extrainfo); |
|
411 |
|
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 } |
|
419 |
|
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 } |
|
431 |
|
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); |
|
442 |
|
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]; |
|
450 |
|
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()); |
|
458 |
|
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 } |
|
470 |
|
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 } |
|
481 |
|
482 if (0 == cbio) |
|
483 { |
|
484 ASSERT(ERROR_SUCCESS == ec); |
|
485 // EOF or broken connection? |
|
486 // TODO: Can InternetQueryDataAvailable detect this? |
|
487 |
|
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 } |
|
501 |
|
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 } |
|
509 |
|
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 } |
|
519 |
|
520 StatsLock_AcquireExclusive(); |
|
521 if (FILESIZE_UNKNOWN != cbThisFile) { |
|
522 g_cbCurrTot = cbThisFile; |
|
523 } |
|
524 g_cbCurrXF += cbXF; |
|
525 StatsLock_ReleaseExclusive(); |
|
526 } |
|
527 } |
|
528 |
|
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 } |
|
550 |
|
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 } |
|
561 |
|
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 } |
|
586 |
|
587 NSIS::stack_t*pFile = StackPopItem(ppST); |
|
588 if (!pFile) |
|
589 { |
|
590 goto freeurlandexit; |
|
591 } |
|
592 |
|
593 TaskLock_AcquireExclusive(); |
|
594 |
|
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 } |
|
607 |
|
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 } |
|
617 |
|
618 if (!g_hThread) |
|
619 { |
|
620 goto freeurlandexit; |
|
621 } |
|
622 |
|
623 #ifndef ONELOCKTORULETHEMALL |
|
624 StatsLock_AcquireExclusive(); |
|
625 #endif |
|
626 ++g_FilesTotal; |
|
627 #ifndef ONELOCKTORULETHEMALL |
|
628 StatsLock_ReleaseExclusive(); |
|
629 #endif |
|
630 TaskLock_ReleaseExclusive(); |
|
631 } |
|
632 } |
|
633 |
|
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 } |
|
650 |
|
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 } |
|
660 |
|
661 BOOL WINAPI DllMain(HINSTANCE hInst, ULONG Reason, LPVOID pCtx) |
|
662 { |
|
663 return _DllMainCRTStartup(hInst, Reason, pCtx); |
|
664 } |
|
665 |
|
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 } |