michael@0: // michael@0: // Copyright (C) Anders Kjersem. Licensed under the zlib/libpng license michael@0: // michael@0: michael@0: #include "InetBgDL.h" michael@0: michael@0: #define USERAGENT _T("NSIS InetBgDL (Mozilla)") michael@0: michael@0: #define STATUS_COMPLETEDALL 0 michael@0: #define STATUS_INITIAL 202 michael@0: #define STATUS_CONNECTING STATUS_INITIAL //102 michael@0: #define STATUS_DOWNLOADING STATUS_INITIAL michael@0: #define STATUS_ERR_GETLASTERROR 418 //HTTP: I'm a teapot: Win32 error code in $3 michael@0: #define STATUS_ERR_LOCALFILEWRITEERROR 450 //HTTP: MS parental control extension michael@0: #define STATUS_ERR_CANCELLED 499 michael@0: michael@0: typedef DWORD FILESIZE_T; // Limit to 4GB for now... michael@0: #define FILESIZE_UNKNOWN (-1) michael@0: michael@0: #define MAX_STRLEN 1024 michael@0: michael@0: HINSTANCE g_hInst; michael@0: NSIS::stack_t*g_pLocations = NULL; michael@0: HANDLE g_hThread = NULL; michael@0: HANDLE g_hGETStartedEvent = NULL; michael@0: volatile UINT g_FilesTotal = 0; michael@0: volatile UINT g_FilesCompleted = 0; michael@0: volatile UINT g_Status = STATUS_INITIAL; michael@0: volatile FILESIZE_T g_cbCurrXF; michael@0: volatile FILESIZE_T g_cbCurrTot = FILESIZE_UNKNOWN; michael@0: CRITICAL_SECTION g_CritLock; michael@0: UINT g_N_CCH; michael@0: PTSTR g_N_Vars; michael@0: TCHAR g_ServerIP[128] = { _T('\0') }; michael@0: michael@0: DWORD g_ConnectTimeout = 0; michael@0: DWORD g_ReceiveTimeout = 0; michael@0: michael@0: #define NSISPI_INITGLOBALS(N_CCH, N_Vars) do { \ michael@0: g_N_CCH = N_CCH; \ michael@0: g_N_Vars = N_Vars; \ michael@0: } while(0) michael@0: michael@0: #define ONELOCKTORULETHEMALL michael@0: #ifdef ONELOCKTORULETHEMALL michael@0: #define TaskLock_AcquireExclusive() EnterCriticalSection(&g_CritLock) michael@0: #define TaskLock_ReleaseExclusive() LeaveCriticalSection(&g_CritLock) michael@0: #define StatsLock_AcquireExclusive() TaskLock_AcquireExclusive() michael@0: #define StatsLock_ReleaseExclusive() TaskLock_ReleaseExclusive() michael@0: #define StatsLock_AcquireShared() StatsLock_AcquireExclusive() michael@0: #define StatsLock_ReleaseShared() StatsLock_ReleaseExclusive() michael@0: #endif michael@0: michael@0: PTSTR NSIS_SetRegStr(UINT Reg, LPCTSTR Value) michael@0: { michael@0: PTSTR s = g_N_Vars + (Reg * g_N_CCH); michael@0: lstrcpy(s, Value); michael@0: return s; michael@0: } michael@0: #define NSIS_SetRegStrEmpty(r) NSIS_SetRegStr(r, _T("")) michael@0: void NSIS_SetRegUINT(UINT Reg, UINT Value) michael@0: { michael@0: TCHAR buf[32]; michael@0: wsprintf(buf, _T("%u"), Value); michael@0: NSIS_SetRegStr(Reg, buf); michael@0: } michael@0: #define StackFreeItem(pI) GlobalFree(pI) michael@0: NSIS::stack_t* StackPopItem(NSIS::stack_t**ppST) michael@0: { michael@0: if (*ppST) michael@0: { michael@0: NSIS::stack_t*pItem = *ppST; michael@0: *ppST = pItem->next; michael@0: return pItem; michael@0: } michael@0: return NULL; michael@0: } michael@0: michael@0: void Reset() michael@0: { michael@0: // The g_hGETStartedEvent event is used to make sure that the Get() call will michael@0: // acquire the lock before the Reset() call acquires the lock. michael@0: if (g_hGETStartedEvent) { michael@0: TRACE(_T("InetBgDl: waiting on g_hGETStartedEvent\n")); michael@0: WaitForSingleObject(g_hGETStartedEvent, INFINITE); michael@0: CloseHandle(g_hGETStartedEvent); michael@0: g_hGETStartedEvent = NULL; michael@0: } michael@0: michael@0: TaskLock_AcquireExclusive(); michael@0: #ifndef ONELOCKTORULETHEMALL michael@0: StatsLock_AcquireExclusive(); michael@0: #endif michael@0: g_FilesTotal = 0; // This causes the Task thread to exit the transfer loop michael@0: if (g_hThread) michael@0: { michael@0: TRACE(_T("InetBgDl: waiting on g_hThread\n")); michael@0: if (WAIT_OBJECT_0 != WaitForSingleObject(g_hThread, 10 * 1000)) michael@0: { michael@0: TRACE(_T("InetBgDl: terminating g_hThread\n")); michael@0: TerminateThread(g_hThread, ERROR_OPERATION_ABORTED); michael@0: } michael@0: CloseHandle(g_hThread); michael@0: g_hThread = NULL; michael@0: } michael@0: g_FilesTotal = 0; michael@0: g_FilesCompleted = 0; michael@0: g_Status = STATUS_INITIAL; michael@0: #ifndef ONELOCKTORULETHEMALL michael@0: StatsLock_ReleaseExclusive(); michael@0: #endif michael@0: for (NSIS::stack_t*pTmpTast,*pTask = g_pLocations; pTask ;) michael@0: { michael@0: pTmpTast = pTask; michael@0: pTask = pTask->next; michael@0: StackFreeItem(pTmpTast); michael@0: } michael@0: g_pLocations = NULL; michael@0: TaskLock_ReleaseExclusive(); michael@0: } michael@0: michael@0: UINT_PTR __cdecl NSISPluginCallback(UINT Event) michael@0: { michael@0: switch(Event) michael@0: { michael@0: case NSPIM_UNLOAD: michael@0: Reset(); michael@0: break; michael@0: } michael@0: return NULL; michael@0: } michael@0: michael@0: void __stdcall InetStatusCallback(HINTERNET hInternet, DWORD_PTR dwContext, michael@0: DWORD dwInternetStatus, michael@0: LPVOID lpvStatusInformation, michael@0: DWORD dwStatusInformationLength) michael@0: { michael@0: if (dwInternetStatus == INTERNET_STATUS_NAME_RESOLVED) { michael@0: // The documentation states the IP address is a PCTSTR but it is usually a michael@0: // PCSTR and only sometimes a PCTSTR. michael@0: StatsLock_AcquireExclusive(); michael@0: wsprintf(g_ServerIP, _T("%S"), lpvStatusInformation); michael@0: if (wcslen(g_ServerIP) == 1) michael@0: { michael@0: wsprintf(g_ServerIP, _T("%s"), lpvStatusInformation); michael@0: } michael@0: StatsLock_ReleaseExclusive(); michael@0: } michael@0: michael@0: #if defined(PLUGIN_DEBUG) michael@0: switch (dwInternetStatus) michael@0: { michael@0: case INTERNET_STATUS_RESOLVING_NAME: michael@0: TRACE(_T("InetBgDl: INTERNET_STATUS_RESOLVING_NAME (%d), name=%s\n"), michael@0: dwStatusInformationLength, lpvStatusInformation); michael@0: break; michael@0: case INTERNET_STATUS_NAME_RESOLVED: michael@0: TRACE(_T("InetBgDl: INTERNET_STATUS_NAME_RESOLVED (%d), resolved name=%s\n"), michael@0: dwStatusInformationLength, g_ServerIP); michael@0: break; michael@0: case INTERNET_STATUS_CONNECTING_TO_SERVER: michael@0: TRACE(_T("InetBgDl: INTERNET_STATUS_CONNECTING_TO_SERVER (%d)\n"), michael@0: dwStatusInformationLength); michael@0: break; michael@0: case INTERNET_STATUS_CONNECTED_TO_SERVER: michael@0: TRACE(_T("InetBgDl: INTERNET_STATUS_CONNECTED_TO_SERVER (%d)\n"), michael@0: dwStatusInformationLength); michael@0: break; michael@0: case INTERNET_STATUS_SENDING_REQUEST: michael@0: TRACE(_T("InetBgDl: INTERNET_STATUS_SENDING_REQUEST (%d)\n"), michael@0: dwStatusInformationLength); michael@0: break; michael@0: case INTERNET_STATUS_REQUEST_SENT: michael@0: TRACE(_T("InetBgDl: INTERNET_STATUS_REQUEST_SENT (%d), bytes sent=%d\n"), michael@0: dwStatusInformationLength, lpvStatusInformation); michael@0: break; michael@0: case INTERNET_STATUS_RECEIVING_RESPONSE: michael@0: TRACE(_T("InetBgDl: INTERNET_STATUS_RECEIVING_RESPONSE (%d)\n"), michael@0: dwStatusInformationLength); michael@0: break; michael@0: case INTERNET_STATUS_RESPONSE_RECEIVED: michael@0: TRACE(_T("InetBgDl: INTERNET_STATUS_RESPONSE_RECEIVED (%d)\n"), michael@0: dwStatusInformationLength); michael@0: break; michael@0: case INTERNET_STATUS_CTL_RESPONSE_RECEIVED: michael@0: TRACE(_T("InetBgDl: INTERNET_STATUS_CTL_RESPONSE_RECEIVED (%d)\n"), michael@0: dwStatusInformationLength); michael@0: break; michael@0: case INTERNET_STATUS_PREFETCH: michael@0: TRACE(_T("InetBgDl: INTERNET_STATUS_PREFETCH (%d)\n"), michael@0: dwStatusInformationLength); michael@0: break; michael@0: case INTERNET_STATUS_CLOSING_CONNECTION: michael@0: TRACE(_T("InetBgDl: INTERNET_STATUS_CLOSING_CONNECTION (%d)\n"), michael@0: dwStatusInformationLength); michael@0: break; michael@0: case INTERNET_STATUS_CONNECTION_CLOSED: michael@0: TRACE(_T("InetBgDl: INTERNET_STATUS_CONNECTION_CLOSED (%d)\n"), michael@0: dwStatusInformationLength); michael@0: break; michael@0: case INTERNET_STATUS_HANDLE_CREATED: michael@0: TRACE(_T("InetBgDl: INTERNET_STATUS_HANDLE_CREATED (%d)\n"), michael@0: dwStatusInformationLength); michael@0: break; michael@0: case INTERNET_STATUS_HANDLE_CLOSING: michael@0: TRACE(_T("InetBgDl: INTERNET_STATUS_HANDLE_CLOSING (%d)\n"), michael@0: dwStatusInformationLength); michael@0: break; michael@0: case INTERNET_STATUS_DETECTING_PROXY: michael@0: TRACE(_T("InetBgDl: INTERNET_STATUS_DETECTING_PROXY (%d)\n"), michael@0: dwStatusInformationLength); michael@0: break; michael@0: case INTERNET_STATUS_REQUEST_COMPLETE: michael@0: TRACE(_T("InetBgDl: INTERNET_STATUS_REQUEST_COMPLETE (%d)\n"), michael@0: dwStatusInformationLength); michael@0: break; michael@0: case INTERNET_STATUS_REDIRECT: michael@0: TRACE(_T("InetBgDl: INTERNET_STATUS_REDIRECT (%d), new url=%s\n"), michael@0: dwStatusInformationLength, lpvStatusInformation); michael@0: break; michael@0: case INTERNET_STATUS_INTERMEDIATE_RESPONSE: michael@0: TRACE(_T("InetBgDl: INTERNET_STATUS_INTERMEDIATE_RESPONSE (%d)\n"), michael@0: dwStatusInformationLength); michael@0: break; michael@0: case INTERNET_STATUS_USER_INPUT_REQUIRED: michael@0: TRACE(_T("InetBgDl: INTERNET_STATUS_USER_INPUT_REQUIRED (%d)\n"), michael@0: dwStatusInformationLength); michael@0: break; michael@0: case INTERNET_STATUS_STATE_CHANGE: michael@0: TRACE(_T("InetBgDl: INTERNET_STATUS_STATE_CHANGE (%d)\n"), michael@0: dwStatusInformationLength); michael@0: break; michael@0: case INTERNET_STATUS_COOKIE_SENT: michael@0: TRACE(_T("InetBgDl: INTERNET_STATUS_COOKIE_SENT (%d)\n"), michael@0: dwStatusInformationLength); michael@0: break; michael@0: case INTERNET_STATUS_COOKIE_RECEIVED: michael@0: TRACE(_T("InetBgDl: INTERNET_STATUS_COOKIE_RECEIVED (%d)\n"), michael@0: dwStatusInformationLength); michael@0: break; michael@0: case INTERNET_STATUS_PRIVACY_IMPACTED: michael@0: TRACE(_T("InetBgDl: INTERNET_STATUS_PRIVACY_IMPACTED (%d)\n"), michael@0: dwStatusInformationLength); michael@0: break; michael@0: case INTERNET_STATUS_P3P_HEADER: michael@0: TRACE(_T("InetBgDl: INTERNET_STATUS_P3P_HEADER (%d)\n"), michael@0: dwStatusInformationLength); michael@0: break; michael@0: case INTERNET_STATUS_P3P_POLICYREF: michael@0: TRACE(_T("InetBgDl: INTERNET_STATUS_P3P_POLICYREF (%d)\n"), michael@0: dwStatusInformationLength); michael@0: break; michael@0: case INTERNET_STATUS_COOKIE_HISTORY: michael@0: TRACE(_T("InetBgDl: INTERNET_STATUS_COOKIE_HISTORY (%d)\n"), michael@0: dwStatusInformationLength); michael@0: break; michael@0: default: michael@0: TRACE(_T("InetBgDl: Unknown Status %d\n"), dwInternetStatus); michael@0: break; michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: DWORD CALLBACK TaskThreadProc(LPVOID ThreadParam) michael@0: { michael@0: NSIS::stack_t *pURL,*pFile; michael@0: HINTERNET hInetSes = NULL, hInetFile = NULL; michael@0: DWORD cbio = sizeof(DWORD); michael@0: HANDLE hLocalFile; michael@0: bool completedFile = false; michael@0: startnexttask: michael@0: hLocalFile = INVALID_HANDLE_VALUE; michael@0: pFile = NULL; michael@0: TaskLock_AcquireExclusive(); michael@0: // Now that we've acquired the lock, we can set the event to indicate this. michael@0: // SetEvent will likely never fail, but if it does we should set it to NULL michael@0: // to avoid anyone waiting on it. michael@0: if (!SetEvent(g_hGETStartedEvent)) { michael@0: CloseHandle(g_hGETStartedEvent); michael@0: g_hGETStartedEvent = NULL; michael@0: } michael@0: pURL = g_pLocations; michael@0: if (pURL) michael@0: { michael@0: pFile = pURL->next; michael@0: g_pLocations = pFile->next; michael@0: } michael@0: #ifndef ONELOCKTORULETHEMALL michael@0: StatsLock_AcquireExclusive(); michael@0: #endif michael@0: if (completedFile) michael@0: { michael@0: ++g_FilesCompleted; michael@0: } michael@0: completedFile = false; michael@0: g_cbCurrXF = 0; michael@0: g_cbCurrTot = FILESIZE_UNKNOWN; michael@0: if (!pURL) michael@0: { michael@0: if (g_FilesTotal) michael@0: { michael@0: if (g_FilesTotal == g_FilesCompleted) michael@0: { michael@0: g_Status = STATUS_COMPLETEDALL; michael@0: } michael@0: } michael@0: g_hThread = NULL; michael@0: } michael@0: #ifndef ONELOCKTORULETHEMALL michael@0: StatsLock_ReleaseExclusive(); michael@0: #endif michael@0: TaskLock_ReleaseExclusive(); michael@0: michael@0: if (!pURL) michael@0: { michael@0: if (0) michael@0: { michael@0: diegle: michael@0: DWORD gle = GetLastError(); michael@0: //TODO? if (ERROR_INTERNET_EXTENDED_ERROR==gle) InternetGetLastResponseInfo(...) michael@0: g_Status = STATUS_ERR_GETLASTERROR; michael@0: } michael@0: if (hInetSes) michael@0: { michael@0: InternetCloseHandle(hInetSes); michael@0: } michael@0: if (INVALID_HANDLE_VALUE != hLocalFile) michael@0: { michael@0: CloseHandle(hLocalFile); michael@0: } michael@0: StackFreeItem(pURL); michael@0: StackFreeItem(pFile); michael@0: return 0; michael@0: } michael@0: michael@0: if (!hInetSes) michael@0: { michael@0: hInetSes = InternetOpen(USERAGENT, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); michael@0: if (!hInetSes) michael@0: { michael@0: TRACE(_T("InetBgDl: InternetOpen failed with gle=%u\n"), michael@0: GetLastError()); michael@0: goto diegle; michael@0: } michael@0: InternetSetStatusCallback(hInetSes, (INTERNET_STATUS_CALLBACK)InetStatusCallback); michael@0: michael@0: //msdn.microsoft.com/library/default.asp?url=/workshop/components/offline/offline.asp#Supporting Offline Browsing in Applications and Components michael@0: ULONG longOpt; michael@0: DWORD cbio = sizeof(ULONG); michael@0: if (InternetQueryOption(hInetSes, INTERNET_OPTION_CONNECTED_STATE, &longOpt, &cbio)) michael@0: { michael@0: if (INTERNET_STATE_DISCONNECTED_BY_USER&longOpt) michael@0: { michael@0: INTERNET_CONNECTED_INFO ci = {INTERNET_STATE_CONNECTED, 0}; michael@0: InternetSetOption(hInetSes, INTERNET_OPTION_CONNECTED_STATE, &ci, sizeof(ci)); michael@0: } michael@0: } michael@0: michael@0: // Change the default connect timeout if specified. michael@0: if(g_ConnectTimeout > 0) michael@0: { michael@0: InternetSetOption(hInetSes, INTERNET_OPTION_CONNECT_TIMEOUT, michael@0: &g_ConnectTimeout, sizeof(g_ConnectTimeout)); michael@0: } michael@0: michael@0: // Change the default receive timeout if specified. michael@0: if (g_ReceiveTimeout) michael@0: { michael@0: InternetSetOption(hInetSes, INTERNET_OPTION_RECEIVE_TIMEOUT, michael@0: &g_ReceiveTimeout, sizeof(DWORD)); michael@0: } michael@0: } michael@0: michael@0: DWORD ec = ERROR_SUCCESS; michael@0: hLocalFile = CreateFile(pFile->text, GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_DELETE,NULL,CREATE_ALWAYS, 0, NULL); michael@0: if (INVALID_HANDLE_VALUE == hLocalFile) michael@0: { michael@0: TRACE(_T("InetBgDl: CreateFile file handle invalid\n")); michael@0: goto diegle; michael@0: } michael@0: michael@0: const DWORD IOURedirFlags = INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP | michael@0: INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS; michael@0: const DWORD IOUCacheFlags = INTERNET_FLAG_RESYNCHRONIZE | michael@0: INTERNET_FLAG_NO_CACHE_WRITE | michael@0: INTERNET_FLAG_PRAGMA_NOCACHE | michael@0: INTERNET_FLAG_RELOAD; michael@0: const DWORD IOUCookieFlags = INTERNET_FLAG_NO_COOKIES; michael@0: DWORD IOUFlags = IOURedirFlags | IOUCacheFlags | IOUCookieFlags | michael@0: INTERNET_FLAG_NO_UI | INTERNET_FLAG_EXISTING_CONNECT; michael@0: michael@0: TCHAR *hostname = (TCHAR*) GlobalAlloc(GPTR, MAX_STRLEN * sizeof(TCHAR)), michael@0: *urlpath = (TCHAR*) GlobalAlloc(GPTR, MAX_STRLEN * sizeof(TCHAR)), michael@0: *extrainfo = (TCHAR*) GlobalAlloc(GPTR, MAX_STRLEN * sizeof(TCHAR)); michael@0: michael@0: URL_COMPONENTS uc = { sizeof(URL_COMPONENTS), NULL, 0, (INTERNET_SCHEME)0, michael@0: hostname, MAX_STRLEN, (INTERNET_PORT)0, NULL, 0, michael@0: NULL, 0, urlpath, MAX_STRLEN, extrainfo, MAX_STRLEN}; michael@0: uc.dwHostNameLength = uc.dwUrlPathLength = uc.dwExtraInfoLength = MAX_STRLEN; michael@0: michael@0: if (!InternetCrackUrl(pURL->text, 0, ICU_ESCAPE, &uc)) michael@0: { michael@0: // Bad url or param passed in michael@0: TRACE(_T("InetBgDl: InternetCrackUrl false with url=%s, gle=%u\n"), michael@0: pURL->text, GetLastError()); michael@0: goto diegle; michael@0: } michael@0: michael@0: TRACE(_T("InetBgDl: scheme_id=%d, hostname=%s, port=%d, urlpath=%s, extrainfo=%s\n"), michael@0: uc.nScheme, hostname, uc.nPort, urlpath, extrainfo); michael@0: michael@0: // Only http and https are supported michael@0: if (uc.nScheme != INTERNET_SCHEME_HTTP && michael@0: uc.nScheme != INTERNET_SCHEME_HTTPS) michael@0: { michael@0: TRACE(_T("InetBgDl: only http and https is supported, aborting...\n")); michael@0: goto diegle; michael@0: } michael@0: michael@0: TRACE(_T("InetBgDl: calling InternetOpenUrl with url=%s\n"), pURL->text); michael@0: hInetFile = InternetOpenUrl(hInetSes, pURL->text, michael@0: NULL, 0, IOUFlags | michael@0: (uc.nScheme == INTERNET_SCHEME_HTTPS ? michael@0: INTERNET_FLAG_SECURE : 0), 1); michael@0: if (!hInetFile) michael@0: { michael@0: TRACE(_T("InetBgDl: InternetOpenUrl failed with gle=%u\n"), michael@0: GetLastError()); michael@0: goto diegle; michael@0: } michael@0: michael@0: // Get the file length via the Content-Length header michael@0: FILESIZE_T cbThisFile; michael@0: cbio = sizeof(cbThisFile); michael@0: if (!HttpQueryInfo(hInetFile, michael@0: HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, michael@0: &cbThisFile, &cbio, NULL)) michael@0: { michael@0: cbThisFile = FILESIZE_UNKNOWN; michael@0: } michael@0: TRACE(_T("InetBgDl: file size=%d bytes\n"), cbThisFile); michael@0: michael@0: // Setup a buffer of size 256KiB to store the downloaded data. michael@0: const UINT cbBufXF = 262144; michael@0: // Use a 4MiB read buffer for the connection. michael@0: // Bigger buffers will be faster. michael@0: // cbReadBufXF should be a multiple of cbBufXF. michael@0: const UINT cbReadBufXF = 4194304; michael@0: BYTE bufXF[cbBufXF]; michael@0: michael@0: // Up the default internal buffer size from 4096 to internalReadBufferSize. michael@0: DWORD internalReadBufferSize = cbReadBufXF; michael@0: if (!InternetSetOption(hInetFile, INTERNET_OPTION_READ_BUFFER_SIZE, michael@0: &internalReadBufferSize, sizeof(DWORD))) michael@0: { michael@0: TRACE(_T("InetBgDl: InternetSetOption failed to set read buffer size to %u bytes, gle=%u\n"), michael@0: internalReadBufferSize, GetLastError()); michael@0: michael@0: // Maybe it's too big, try half of the optimal value. If that fails just michael@0: // use the default. michael@0: internalReadBufferSize /= 2; michael@0: if (!InternetSetOption(hInetFile, INTERNET_OPTION_READ_BUFFER_SIZE, michael@0: &internalReadBufferSize, sizeof(DWORD))) michael@0: { michael@0: TRACE(_T("InetBgDl: InternetSetOption failed to set read buffer size ") \ michael@0: _T("to %u bytes (using default read buffer size), gle=%u\n"), michael@0: internalReadBufferSize, GetLastError()); michael@0: } michael@0: } michael@0: michael@0: for(;;) michael@0: { michael@0: DWORD cbio = 0, cbXF = 0; michael@0: BOOL retXF = InternetReadFile(hInetFile, bufXF, cbBufXF, &cbio); michael@0: if (!retXF) michael@0: { michael@0: ec = GetLastError(); michael@0: TRACE(_T("InetBgDl: InternetReadFile failed, gle=%u\n"), ec); michael@0: break; michael@0: } michael@0: michael@0: if (0 == cbio) michael@0: { michael@0: ASSERT(ERROR_SUCCESS == ec); michael@0: // EOF or broken connection? michael@0: // TODO: Can InternetQueryDataAvailable detect this? michael@0: michael@0: TRACE(_T("InetBgDl: InternetReadFile true with 0 cbio, cbThisFile=%d, gle=%u\n"), michael@0: cbThisFile, GetLastError()); michael@0: // If we haven't transferred all of the file, and we know how big the file michael@0: // is, and we have no more data to read from the HTTP request, then set a michael@0: // broken pipe error. Reading without StatsLock is ok in this thread. michael@0: if (FILESIZE_UNKNOWN != cbThisFile && g_cbCurrXF != cbThisFile) michael@0: { michael@0: TRACE(_T("InetBgDl: downloaded file size of %d bytes doesn't equal ") \ michael@0: _T("expected file size of %d bytes\n"), g_cbCurrXF, cbThisFile); michael@0: ec = ERROR_BROKEN_PIPE; michael@0: } michael@0: break; michael@0: } michael@0: michael@0: // Check if we canceled the download michael@0: if (0 == g_FilesTotal) michael@0: { michael@0: TRACE(_T("InetBgDl: 0 == g_FilesTotal, aborting transfer loop...\n")); michael@0: ec = ERROR_CANCELLED; michael@0: break; michael@0: } michael@0: michael@0: cbXF = cbio; michael@0: if (cbXF) michael@0: { michael@0: retXF = WriteFile(hLocalFile, bufXF, cbXF, &cbio, NULL); michael@0: if (!retXF || cbXF != cbio) michael@0: { michael@0: ec = GetLastError(); michael@0: break; michael@0: } michael@0: michael@0: StatsLock_AcquireExclusive(); michael@0: if (FILESIZE_UNKNOWN != cbThisFile) { michael@0: g_cbCurrTot = cbThisFile; michael@0: } michael@0: g_cbCurrXF += cbXF; michael@0: StatsLock_ReleaseExclusive(); michael@0: } michael@0: } michael@0: michael@0: TRACE(_T("InetBgDl: TaskThreadProc completed %s, ec=%u\n"), pURL->text, ec); michael@0: InternetCloseHandle(hInetFile); michael@0: if (ERROR_SUCCESS == ec) michael@0: { michael@0: if (INVALID_HANDLE_VALUE != hLocalFile) michael@0: { michael@0: CloseHandle(hLocalFile); michael@0: hLocalFile = INVALID_HANDLE_VALUE; michael@0: } michael@0: StackFreeItem(pURL); michael@0: StackFreeItem(pFile); michael@0: ++completedFile; michael@0: } michael@0: else michael@0: { michael@0: TRACE(_T("InetBgDl: failed with ec=%u\n"), ec); michael@0: SetLastError(ec); michael@0: goto diegle; michael@0: } michael@0: goto startnexttask; michael@0: } michael@0: michael@0: NSISPIEXPORTFUNC Get(HWND hwndNSIS, UINT N_CCH, TCHAR*N_Vars, NSIS::stack_t**ppST, NSIS::xparams_t*pX) michael@0: { michael@0: pX->RegisterPluginCallback(g_hInst, NSISPluginCallback); michael@0: for (;;) michael@0: { michael@0: NSIS::stack_t*pURL = StackPopItem(ppST); michael@0: if (!pURL) michael@0: { michael@0: break; michael@0: } michael@0: michael@0: if (lstrcmpi(pURL->text, _T("/connecttimeout")) == 0) michael@0: { michael@0: NSIS::stack_t*pConnectTimeout = StackPopItem(ppST); michael@0: g_ConnectTimeout = _tcstol(pConnectTimeout->text, NULL, 10) * 1000; michael@0: continue; michael@0: } michael@0: else if (lstrcmpi(pURL->text, _T("/receivetimeout")) == 0) michael@0: { michael@0: NSIS::stack_t*pReceiveTimeout = StackPopItem(ppST); michael@0: g_ReceiveTimeout = _tcstol(pReceiveTimeout->text, NULL, 10) * 1000; michael@0: continue; michael@0: } michael@0: else if (lstrcmpi(pURL->text, _T("/reset")) == 0) michael@0: { michael@0: StackFreeItem(pURL); michael@0: Reset(); michael@0: continue; michael@0: } michael@0: else if (lstrcmpi(pURL->text, _T("/end")) == 0) michael@0: { michael@0: freeurlandexit: michael@0: StackFreeItem(pURL); michael@0: break; michael@0: } michael@0: michael@0: NSIS::stack_t*pFile = StackPopItem(ppST); michael@0: if (!pFile) michael@0: { michael@0: goto freeurlandexit; michael@0: } michael@0: michael@0: TaskLock_AcquireExclusive(); michael@0: michael@0: pFile->next = NULL; michael@0: pURL->next = pFile; michael@0: NSIS::stack_t*pTasksTail = g_pLocations; michael@0: while(pTasksTail && pTasksTail->next) pTasksTail = pTasksTail->next; michael@0: if (pTasksTail) michael@0: { michael@0: pTasksTail->next = pURL; michael@0: } michael@0: else michael@0: { michael@0: g_pLocations = pURL; michael@0: } michael@0: michael@0: if (!g_hThread) michael@0: { michael@0: DWORD tid; michael@0: if (g_hGETStartedEvent) { michael@0: CloseHandle(g_hGETStartedEvent); michael@0: } michael@0: g_hGETStartedEvent = CreateEvent(NULL, TRUE, FALSE, NULL); michael@0: g_hThread = CreateThread(NULL, 0, TaskThreadProc, NULL, 0, &tid); michael@0: } michael@0: michael@0: if (!g_hThread) michael@0: { michael@0: goto freeurlandexit; michael@0: } michael@0: michael@0: #ifndef ONELOCKTORULETHEMALL michael@0: StatsLock_AcquireExclusive(); michael@0: #endif michael@0: ++g_FilesTotal; michael@0: #ifndef ONELOCKTORULETHEMALL michael@0: StatsLock_ReleaseExclusive(); michael@0: #endif michael@0: TaskLock_ReleaseExclusive(); michael@0: } michael@0: } michael@0: michael@0: NSISPIEXPORTFUNC GetStats(HWND hwndNSIS, UINT N_CCH, TCHAR*N_Vars, NSIS::stack_t**ppST, NSIS::xparams_t*pX) michael@0: { michael@0: NSISPI_INITGLOBALS(N_CCH, N_Vars); michael@0: StatsLock_AcquireShared(); michael@0: NSIS_SetRegUINT(0, g_Status); michael@0: NSIS_SetRegUINT(1, g_FilesCompleted); michael@0: NSIS_SetRegUINT(2, g_FilesTotal - g_FilesCompleted); michael@0: NSIS_SetRegUINT(3, g_cbCurrXF); michael@0: NSIS_SetRegStrEmpty(4); michael@0: if (FILESIZE_UNKNOWN != g_cbCurrTot) michael@0: { michael@0: NSIS_SetRegUINT(4, g_cbCurrTot); michael@0: } michael@0: NSIS_SetRegStr(5, g_ServerIP); michael@0: StatsLock_ReleaseShared(); michael@0: } michael@0: michael@0: EXTERN_C BOOL WINAPI _DllMainCRTStartup(HMODULE hInst, UINT Reason, LPVOID pCtx) michael@0: { michael@0: if (DLL_PROCESS_ATTACH==Reason) michael@0: { michael@0: g_hInst=hInst; michael@0: InitializeCriticalSection(&g_CritLock); michael@0: } michael@0: return TRUE; michael@0: } michael@0: michael@0: BOOL WINAPI DllMain(HINSTANCE hInst, ULONG Reason, LPVOID pCtx) michael@0: { michael@0: return _DllMainCRTStartup(hInst, Reason, pCtx); michael@0: } michael@0: michael@0: // For some reason VC6++ doesn't like wcsicmp and swprintf. michael@0: // If you use them, you get a linking error about _main michael@0: // as an unresolved external. michael@0: int main(int argc, char**argv) michael@0: { michael@0: return 0; michael@0: }