// // Copyright (C) Anders Kjersem. Licensed under the zlib/libpng license //
// This file is intended to be compiled with MSVC's Omit Default Library Name (/Zl) // option enabled, in order to keep the file size low for bundling this DLL with // the stub installer. That means that any code requiring the C runtime will fail // to link. You'll see a couple of odd-looking things here for this reason; they // should all be called out with comments.
// Setup a buffer of size 256KiB to store the downloaded data.
constexpr UINT g_cbBufXF = 262144; // This buffer is only needed inside TaskThreadProc(), but declaring it on // the stack there triggers a runtime stack size check, which is implemented // by a C runtime library function, so we have to avoid the compiler wanting // to build that check by not having any large stack buffers.
BYTE g_bufXF[g_cbBufXF];
// Normally we would just call the C library wcstol, but since we can't use the // C runtime, we'll supply our own function as an understudy. static DWORD
MyTStrToL(TCHAR const* str)
{ if (!str) { return 0;
}
int len = lstrlen(str);
DWORD place = 1;
DWORD rv = 0; for (int i = len - 1; i >= 0; --i) { int digit = str[i] - 0x30;
rv += digit * place;
place *= 10;
} return rv;
}
void Reset()
{ // The g_hGETStartedEvent event is used to make sure that the Get() call will // acquire the lock before the Reset() call acquires the lock. if (g_hGETStartedEvent) {
TRACE(_T("InetBgDl: waiting on g_hGETStartedEvent\n"));
WaitForSingleObject(g_hGETStartedEvent, INFINITE);
CloseHandle(g_hGETStartedEvent);
g_hGETStartedEvent = NULL;
}
TaskLock_AcquireExclusive(); #ifndef ONELOCKTORULETHEMALL
StatsLock_AcquireExclusive(); #endif
g_FilesTotal = 0; // This causes the Task thread to exit the transfer loop if (g_hThread)
{
TRACE(_T("InetBgDl: waiting on g_hThread\n")); if (WAIT_OBJECT_0 != WaitForSingleObject(g_hThread, 5 * 1000))
{
TRACE(_T("InetBgDl: terminating g_hThread\n")); // Suspend the thread so that it's not still trying to use these handles // that we're about to close out from under it.
SuspendThread(g_hThread); if (g_hInetFile) {
InternetCloseHandle(g_hInetFile);
g_hInetFile = nullptr;
} if (g_hInetSes) {
InternetCloseHandle(g_hInetSes);
g_hInetSes = nullptr;
}
TerminateThread(g_hThread, ERROR_OPERATION_ABORTED);
}
CloseHandle(g_hThread);
g_hThread = NULL;
}
g_FilesTotal = 0;
g_FilesCompleted = 0;
g_Status = STATUS_INITIAL; #ifndef ONELOCKTORULETHEMALL
StatsLock_ReleaseExclusive(); #endif for (NSIS::stack_t*pTmpTast,*pTask = g_pLocations; pTask ;)
{
pTmpTast = pTask;
pTask = pTask->next;
StackFreeItem(pTmpTast);
}
g_pLocations = NULL;
TaskLock_ReleaseExclusive();
}
void __stdcall InetStatusCallback(HINTERNET hInternet, DWORD_PTR dwContext,
DWORD dwInternetStatus,
LPVOID lpvStatusInformation,
DWORD dwStatusInformationLength)
{ if (dwInternetStatus == INTERNET_STATUS_NAME_RESOLVED) { // If we're in the process of being reset, don't try to update g_ServerIP; // there's no need for it, and Reset() will be holding the StatsLock, so // we'll hang here and block the reset if we try to acquire it. if (g_FilesTotal != 0) { // The documentation states the IP address is a PCTSTR but it is usually a // PCSTR and only sometimes a PCTSTR.
StatsLock_AcquireExclusive();
wsprintf(g_ServerIP, _T("%S"), lpvStatusInformation); if (lstrlen(g_ServerIP) == 1)
{
wsprintf(g_ServerIP, _T("%s"), lpvStatusInformation);
}
StatsLock_ReleaseExclusive();
}
}
#ifdefined(PLUGIN_DEBUG) switch (dwInternetStatus)
{ case INTERNET_STATUS_RESOLVING_NAME:
TRACE(_T("InetBgDl: INTERNET_STATUS_RESOLVING_NAME (%d), name=%s\n"),
dwStatusInformationLength, lpvStatusInformation); break; case INTERNET_STATUS_NAME_RESOLVED:
TRACE(_T("InetBgDl: INTERNET_STATUS_NAME_RESOLVED (%d), resolved name=%s\n"),
dwStatusInformationLength, g_ServerIP); break; case INTERNET_STATUS_CONNECTING_TO_SERVER:
TRACE(_T("InetBgDl: INTERNET_STATUS_CONNECTING_TO_SERVER (%d)\n"),
dwStatusInformationLength); break; case INTERNET_STATUS_CONNECTED_TO_SERVER:
TRACE(_T("InetBgDl: INTERNET_STATUS_CONNECTED_TO_SERVER (%d)\n"),
dwStatusInformationLength); break; case INTERNET_STATUS_SENDING_REQUEST:
TRACE(_T("InetBgDl: INTERNET_STATUS_SENDING_REQUEST (%d)\n"),
dwStatusInformationLength); break; case INTERNET_STATUS_REQUEST_SENT:
TRACE(_T("InetBgDl: INTERNET_STATUS_REQUEST_SENT (%d), bytes sent=%d\n"),
dwStatusInformationLength, lpvStatusInformation); break; case INTERNET_STATUS_RECEIVING_RESPONSE:
TRACE(_T("InetBgDl: INTERNET_STATUS_RECEIVING_RESPONSE (%d)\n"),
dwStatusInformationLength); break; case INTERNET_STATUS_RESPONSE_RECEIVED:
TRACE(_T("InetBgDl: INTERNET_STATUS_RESPONSE_RECEIVED (%d)\n"),
dwStatusInformationLength); break; case INTERNET_STATUS_CTL_RESPONSE_RECEIVED:
TRACE(_T("InetBgDl: INTERNET_STATUS_CTL_RESPONSE_RECEIVED (%d)\n"),
dwStatusInformationLength); break; case INTERNET_STATUS_PREFETCH:
TRACE(_T("InetBgDl: INTERNET_STATUS_PREFETCH (%d)\n"),
dwStatusInformationLength); break; case INTERNET_STATUS_CLOSING_CONNECTION:
TRACE(_T("InetBgDl: INTERNET_STATUS_CLOSING_CONNECTION (%d)\n"),
dwStatusInformationLength); break; case INTERNET_STATUS_CONNECTION_CLOSED:
TRACE(_T("InetBgDl: INTERNET_STATUS_CONNECTION_CLOSED (%d)\n"),
dwStatusInformationLength); break; case INTERNET_STATUS_HANDLE_CREATED:
TRACE(_T("InetBgDl: INTERNET_STATUS_HANDLE_CREATED (%d)\n"),
dwStatusInformationLength); break; case INTERNET_STATUS_HANDLE_CLOSING:
TRACE(_T("InetBgDl: INTERNET_STATUS_HANDLE_CLOSING (%d)\n"),
dwStatusInformationLength); break; case INTERNET_STATUS_DETECTING_PROXY:
TRACE(_T("InetBgDl: INTERNET_STATUS_DETECTING_PROXY (%d)\n"),
dwStatusInformationLength); break; case INTERNET_STATUS_REQUEST_COMPLETE:
TRACE(_T("InetBgDl: INTERNET_STATUS_REQUEST_COMPLETE (%d)\n"),
dwStatusInformationLength); break; case INTERNET_STATUS_REDIRECT:
TRACE(_T("InetBgDl: INTERNET_STATUS_REDIRECT (%d), new url=%s\n"),
dwStatusInformationLength, lpvStatusInformation); break; case INTERNET_STATUS_INTERMEDIATE_RESPONSE:
TRACE(_T("InetBgDl: INTERNET_STATUS_INTERMEDIATE_RESPONSE (%d)\n"),
dwStatusInformationLength); break; case INTERNET_STATUS_USER_INPUT_REQUIRED:
TRACE(_T("InetBgDl: INTERNET_STATUS_USER_INPUT_REQUIRED (%d)\n"),
dwStatusInformationLength); break; case INTERNET_STATUS_STATE_CHANGE:
TRACE(_T("InetBgDl: INTERNET_STATUS_STATE_CHANGE (%d)\n"),
dwStatusInformationLength); break; case INTERNET_STATUS_COOKIE_SENT:
TRACE(_T("InetBgDl: INTERNET_STATUS_COOKIE_SENT (%d)\n"),
dwStatusInformationLength); break; case INTERNET_STATUS_COOKIE_RECEIVED:
TRACE(_T("InetBgDl: INTERNET_STATUS_COOKIE_RECEIVED (%d)\n"),
dwStatusInformationLength); break; case INTERNET_STATUS_PRIVACY_IMPACTED:
TRACE(_T("InetBgDl: INTERNET_STATUS_PRIVACY_IMPACTED (%d)\n"),
dwStatusInformationLength); break; case INTERNET_STATUS_P3P_HEADER:
TRACE(_T("InetBgDl: INTERNET_STATUS_P3P_HEADER (%d)\n"),
dwStatusInformationLength); break; case INTERNET_STATUS_P3P_POLICYREF:
TRACE(_T("InetBgDl: INTERNET_STATUS_P3P_POLICYREF (%d)\n"),
dwStatusInformationLength); break; case INTERNET_STATUS_COOKIE_HISTORY:
TRACE(_T("InetBgDl: INTERNET_STATUS_COOKIE_HISTORY (%d)\n"),
dwStatusInformationLength); break; default:
TRACE(_T("InetBgDl: Unknown Status %d\n"), dwInternetStatus); break;
} #endif
}
DWORD CALLBACK TaskThreadProc(LPVOID ThreadParam)
{
NSIS::stack_t *pURL,*pFile;
DWORD cbio = sizeof(DWORD);
DWORD previouslyWritten = 0, writtenThisSession = 0;
HANDLE hLocalFile; bool completedFile = false;
startnexttask:
hLocalFile = INVALID_HANDLE_VALUE;
pFile = NULL;
TaskLock_AcquireExclusive(); // Now that we've acquired the lock, we can set the event to indicate this. // SetEvent will likely never fail, but if it does we should set it to NULL // to avoid anyone waiting on it. if (!SetEvent(g_hGETStartedEvent)) {
CloseHandle(g_hGETStartedEvent);
g_hGETStartedEvent = NULL;
}
pURL = g_pLocations; if (pURL)
{
pFile = pURL->next;
g_pLocations = pFile->next;
} #ifndef ONELOCKTORULETHEMALL
StatsLock_AcquireExclusive(); #endif if (completedFile)
{
++g_FilesCompleted;
}
completedFile = false;
g_cbCurrXF = 0;
g_cbCurrTot = FILESIZE_UNKNOWN; if (!pURL)
{ if (g_FilesTotal)
{ if (g_FilesTotal == g_FilesCompleted)
{
g_Status = STATUS_COMPLETEDALL;
}
}
g_hThread = NULL;
} #ifndef ONELOCKTORULETHEMALL
StatsLock_ReleaseExclusive(); #endif
TaskLock_ReleaseExclusive();
if (!pURL)
{ if (0)
{
diegle:
DWORD gle = GetLastError(); //TODO? if (ERROR_INTERNET_EXTENDED_ERROR==gle) InternetGetLastResponseInfo(...)
g_Status = STATUS_ERR_GETLASTERROR;
}
die: if (g_hInetSes)
{
InternetCloseHandle(g_hInetSes);
g_hInetSes = nullptr;
} if (INVALID_HANDLE_VALUE != hLocalFile)
{
CloseHandle(hLocalFile);
}
StackFreeItem(pURL);
StackFreeItem(pFile); return 0;
}
if (!g_hInetSes)
{
g_hInetSes = InternetOpen(USERAGENT, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); if (!g_hInetSes)
{
TRACE(_T("InetBgDl: InternetOpen failed with gle=%u\n"),
GetLastError()); goto diegle;
}
InternetSetStatusCallback(g_hInetSes, (INTERNET_STATUS_CALLBACK)InetStatusCallback);
//msdn.microsoft.com/library/default.asp?url=/workshop/components/offline/offline.asp#Supporting Offline Browsing in Applications and Components
ULONG longOpt;
DWORD cbio = sizeof(ULONG); if (InternetQueryOption(g_hInetSes, INTERNET_OPTION_CONNECTED_STATE, &longOpt, &cbio))
{ if (INTERNET_STATE_DISCONNECTED_BY_USER&longOpt)
{
INTERNET_CONNECTED_INFO ci = {INTERNET_STATE_CONNECTED, 0};
InternetSetOption(g_hInetSes, INTERNET_OPTION_CONNECTED_STATE, &ci, sizeof(ci));
}
}
// Change the default connect timeout if specified. if(g_ConnectTimeout > 0)
{
InternetSetOption(g_hInetSes, INTERNET_OPTION_CONNECT_TIMEOUT,
&g_ConnectTimeout, sizeof(g_ConnectTimeout));
}
// Change the default receive timeout if specified. if (g_ReceiveTimeout)
{
InternetSetOption(g_hInetSes, INTERNET_OPTION_RECEIVE_TIMEOUT,
&g_ReceiveTimeout, sizeof(DWORD));
}
}
DWORD ec = ERROR_SUCCESS;
hLocalFile = CreateFile(pFile->text, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_DELETE,
NULL, OPEN_ALWAYS, 0, NULL); if (INVALID_HANDLE_VALUE == hLocalFile)
{
TRACE(_T("InetBgDl: CreateFile file handle invalid\n")); goto diegle;
} if (GetLastError() == ERROR_ALREADY_EXISTS) { // Resuming a download that was started earlier and then aborted.
previouslyWritten = GetFileSize(hLocalFile, NULL);
g_cbCurrXF = previouslyWritten;
SetFilePointer(hLocalFile, previouslyWritten, NULL, FILE_BEGIN);
}
if (!InternetCrackUrl(pURL->text, 0, ICU_ESCAPE, &uc))
{ // Bad url or param passed in
TRACE(_T("InetBgDl: InternetCrackUrl false with url=%s, gle=%u\n"),
pURL->text, GetLastError()); goto diegle;
}
// Only http and https are supported if (uc.nScheme != INTERNET_SCHEME_HTTP &&
uc.nScheme != INTERNET_SCHEME_HTTPS)
{
TRACE(_T("InetBgDl: only http and https is supported, aborting...\n")); goto diegle;
}
// Tell the server to pick up wherever we left off.
TCHAR headers[32]; // We're skipping building the C runtime to keep the file size low, so we // can't use a normal string initialization because that would call memset.
headers[0] = _T('\0');
wsprintf(headers, _T("Range: bytes=%d-\r\n"), previouslyWritten);
// Get the file length via the Content-Length header
FILESIZE_T cbThisFile;
cbio = sizeof(cbThisFile); if (!HttpQueryInfo(g_hInetFile,
HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER,
&cbThisFile, &cbio, NULL))
{
cbThisFile = FILESIZE_UNKNOWN;
}
TRACE(_T("InetBgDl: file size=%d bytes\n"), cbThisFile);
// Use a 4MiB read buffer for the connection. // Bigger buffers will be faster. // cbReadBufXF should be a multiple of g_cbBufXF. const UINT cbReadBufXF = 4194304;
// Up the default internal buffer size from 4096 to internalReadBufferSize.
DWORD internalReadBufferSize = cbReadBufXF; if (!InternetSetOption(g_hInetFile, INTERNET_OPTION_READ_BUFFER_SIZE,
&internalReadBufferSize, sizeof(DWORD)))
{
TRACE(_T("InetBgDl: InternetSetOption failed to set read buffer size to %u bytes, gle=%u\n"),
internalReadBufferSize, GetLastError());
// Maybe it's too big, try half of the optimal value. If that fails just // use the default.
internalReadBufferSize /= 2; if (!InternetSetOption(g_hInetFile, INTERNET_OPTION_READ_BUFFER_SIZE,
&internalReadBufferSize, sizeof(DWORD)))
{
TRACE(_T("InetBgDl: InternetSetOption failed to set read buffer size ") \
_T("to %u bytes (using default read buffer size), gle=%u\n"),
internalReadBufferSize, GetLastError());
}
}
if (0 == cbio)
{
ASSERT(ERROR_SUCCESS == ec); // EOF or broken connection? // TODO: Can InternetQueryDataAvailable detect this?
TRACE(_T("InetBgDl: InternetReadFile true with 0 cbio, cbThisFile=%d, gle=%u\n"),
cbThisFile, GetLastError()); // If we haven't transferred all of the file, and we know how big the file // is, and we have no more data to read from the HTTP request, then set a // broken pipe error. Reading without StatsLock is ok in this thread. if (FILESIZE_UNKNOWN != cbThisFile && writtenThisSession != cbThisFile)
{
TRACE(_T("InetBgDl: expected Content-Length of %d bytes, ")
_T("but transferred %d bytes\n"),
cbThisFile, writtenThisSession);
ec = ERROR_BROKEN_PIPE;
} break;
}
// Check if we canceled the download if (0 == g_FilesTotal)
{
TRACE(_T("InetBgDl: 0 == g_FilesTotal, aborting transfer loop...\n"));
ec = ERROR_CANCELLED; break;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.