aheinecke@11: /** aheinecke@11: * @file downloader_win.cpp aheinecke@11: * @brief Downloader implementation for Windows aheinecke@11: * aheinecke@11: * We use Windows API here instead of Qt because we want to avoid aheinecke@11: * QtNetworks SSL stack which is based on OpenSSL and so aheinecke@11: * we might be incompatible with GPL code. Also using the aheinecke@11: * native API means that the security of the SSL implementation aheinecke@11: * is tied to the security of the system. aheinecke@11: * aheinecke@11: */ aheinecke@11: #include "downloader.h" aheinecke@11: #ifdef Q_OS_WIN aheinecke@11: #ifndef MYVERSION aheinecke@11: #define MYVERSION "1" aheinecke@11: #endif aheinecke@11: aheinecke@11: #include aheinecke@11: #include aheinecke@11: aheinecke@11: #include aheinecke@11: aheinecke@11: #define DEBUG if (1) qDebug() << __PRETTY_FUNCTION__ aheinecke@11: aheinecke@11: const QString getLastErrorMsg() { aheinecke@11: LPWSTR bufPtr = NULL; aheinecke@11: DWORD err = GetLastError(); aheinecke@11: FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | aheinecke@11: FORMAT_MESSAGE_FROM_SYSTEM | aheinecke@11: FORMAT_MESSAGE_IGNORE_INSERTS, aheinecke@11: NULL, err, 0, (LPWSTR)&bufPtr, 0, NULL); aheinecke@11: const QString result = aheinecke@11: (bufPtr) ? QString::fromUtf16((const ushort*)bufPtr).trimmed() : aheinecke@11: QString("Unknown Error %1").arg(err); aheinecke@11: LocalFree(bufPtr); aheinecke@11: return result; aheinecke@11: } aheinecke@11: aheinecke@11: /** @brief open a session with appropiate proxy settings aheinecke@11: * aheinecke@11: * @param[inout] *pHSession pointer to a HInternet structure aheinecke@11: * aheinecke@11: * On error call getLastError to get extended error information. aheinecke@11: * aheinecke@11: * @returns True on success, false on error. aheinecke@11: */ aheinecke@11: aheinecke@11: aheinecke@11: bool openSession(HINTERNET *pHSession) aheinecke@11: { aheinecke@11: WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxyConfig; aheinecke@11: aheinecke@11: DEBUG; aheinecke@11: if (!pHSession) { aheinecke@11: SetLastError(ERROR_INVALID_PARAMETER); aheinecke@11: return false; aheinecke@11: } aheinecke@11: aheinecke@11: qDebug() << "2"; aheinecke@11: memset(&proxyConfig, 0, sizeof (WINHTTP_CURRENT_USER_IE_PROXY_CONFIG)); aheinecke@11: aheinecke@11: if (WinHttpGetIEProxyConfigForCurrentUser(&proxyConfig)) { aheinecke@11: if (proxyConfig.fAutoDetect) { aheinecke@11: // TODO Handle this aheinecke@11: qDebug() << "Autodetect is set"; aheinecke@11: } aheinecke@11: aheinecke@11: if (proxyConfig.lpszProxy || proxyConfig.lpszProxyBypass) { aheinecke@11: DEBUG << "Using proxies."; aheinecke@11: } aheinecke@11: aheinecke@11: if (proxyConfig.lpszProxy) { aheinecke@11: *pHSession = WinHttpOpen(L"M13 "MYVERSION, aheinecke@11: WINHTTP_ACCESS_TYPE_NAMED_PROXY, aheinecke@11: proxyConfig.lpszProxy, aheinecke@11: proxyConfig.lpszProxyBypass, 0); aheinecke@11: } aheinecke@11: } aheinecke@11: aheinecke@11: if (!*pHSession) { aheinecke@11: DEBUG << "No IE Proxy falling back to default proxy"; aheinecke@11: *pHSession = WinHttpOpen(L"M13 "MYVERSION, aheinecke@11: WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, aheinecke@11: WINHTTP_NO_PROXY_NAME, aheinecke@11: WINHTTP_NO_PROXY_BYPASS, 0); aheinecke@11: } aheinecke@11: // Cleanup aheinecke@11: if (proxyConfig.lpszAutoConfigUrl) { aheinecke@11: GlobalFree(proxyConfig.lpszAutoConfigUrl); aheinecke@11: } aheinecke@11: aheinecke@11: if (proxyConfig.lpszProxy) { aheinecke@11: GlobalFree(proxyConfig.lpszProxy); aheinecke@11: } aheinecke@11: aheinecke@11: if (proxyConfig.lpszProxyBypass) { aheinecke@11: GlobalFree(proxyConfig.lpszProxyBypass); aheinecke@11: } aheinecke@11: return *pHSession; aheinecke@11: } aheinecke@11: aheinecke@11: aheinecke@11: /** @brief initialize a connection in the session aheinecke@11: * aheinecke@11: * @param[in] HSession the session to work in. aheinecke@11: * @param[inout] *pHConnect pointer to the connection. aheinecke@11: * @param[in] url pointer to the URL in wchar representation. aheinecke@11: * aheinecke@11: * On error call getLastError to get extended error information. aheinecke@11: * aheinecke@11: * @returns True on success, false on error. aheinecke@11: */ aheinecke@11: bool initializeConnection(HINTERNET hSession, HINTERNET *pHConnect, aheinecke@11: LPCWSTR url) aheinecke@11: { aheinecke@11: DEBUG; aheinecke@11: if (!hSession || !pHConnect) { aheinecke@11: SetLastError(ERROR_INVALID_PARAMETER); aheinecke@11: return false; aheinecke@11: } aheinecke@11: // Initialize connection. No request is done here. aheinecke@11: *pHConnect = WinHttpConnect(hSession, url, aheinecke@11: INTERNET_DEFAULT_HTTPS_PORT, 0); aheinecke@11: aheinecke@11: return *pHConnect; aheinecke@11: } aheinecke@11: aheinecke@11: /** @brief Create a request aheinecke@11: * aheinecke@11: * @param[in] HSession the session to work in. aheinecke@11: * @param[in] HConnect the connection to use. aheinecke@11: * @param[inout] *pHRequest pointer to the request structure to be filled. aheinecke@11: * @param[in] requestType the HTTP request to be made default is GET aheinecke@11: * @param[in] resource pointer to the resource to request in wchar aheinecke@11: * representation. aheinecke@11: * aheinecke@11: * On error call getLastError to get extended error information. aheinecke@11: * This function still does not do any networking but only initializes aheinecke@11: * it. aheinecke@11: * aheinecke@11: * @returns True on success, false on error. aheinecke@11: */ aheinecke@11: aheinecke@11: bool createRequest(HINTERNET hSession, HINTERNET hConnect, aheinecke@11: HINTERNET *pHRequest, LPCWSTR requestType, LPCWSTR resource) aheinecke@11: { aheinecke@11: DEBUG; aheinecke@11: if (!hSession || !hConnect || !pHRequest) { aheinecke@11: SetLastError(ERROR_INVALID_PARAMETER); aheinecke@11: return false; aheinecke@11: } aheinecke@11: aheinecke@11: *pHRequest = WinHttpOpenRequest(hConnect, requestType, resource, aheinecke@11: NULL, WINHTTP_NO_REFERER, aheinecke@11: WINHTTP_DEFAULT_ACCEPT_TYPES, aheinecke@11: WINHTTP_FLAG_SECURE); aheinecke@11: return *pHRequest; aheinecke@11: } aheinecke@11: aheinecke@11: void Downloader::run() { aheinecke@11: BOOL bResults = FALSE; aheinecke@11: HINTERNET hSession = NULL, aheinecke@11: hConnect = NULL, aheinecke@11: hRequest = NULL; aheinecke@11: aheinecke@11: SYSTEMTIME lastModified; aheinecke@11: DWORD sizeOfSystemtime = sizeof (SYSTEMTIME); aheinecke@11: aheinecke@11: memset(&lastModified, 0, sizeof (SYSTEMTIME)); aheinecke@11: aheinecke@11: if (!openSession(&hSession)) { aheinecke@11: DEBUG << "Failed to open session: " << getLastErrorMsg(); aheinecke@11: return; aheinecke@11: } aheinecke@11: aheinecke@11: if (!initializeConnection(hSession, &hConnect, L"www.intevation.de")) { aheinecke@11: DEBUG << "Failed to initialize connection: " << getLastErrorMsg(); aheinecke@11: goto cleanup; aheinecke@11: } aheinecke@11: aheinecke@11: if (!createRequest(hSession, hConnect, &hRequest, L"GET", L"/")) { aheinecke@11: DEBUG << "Failed to create the request: " << getLastErrorMsg(); aheinecke@11: goto cleanup; aheinecke@11: } aheinecke@11: aheinecke@11: if (hRequest) { aheinecke@11: DEBUG << "Doing Request"; aheinecke@11: bResults = WinHttpSendRequest(hRequest, aheinecke@11: WINHTTP_NO_ADDITIONAL_HEADERS, aheinecke@11: 0, WINHTTP_NO_REQUEST_DATA, 0, aheinecke@11: 0, 0); aheinecke@11: } else { aheinecke@11: DEBUG << "Error: " << GetLastError(); aheinecke@11: } aheinecke@11: aheinecke@11: aheinecke@11: if (bResults) { aheinecke@11: DEBUG << "Recieving Response"; aheinecke@11: bResults = WinHttpReceiveResponse(hRequest, NULL); aheinecke@11: } else { aheinecke@11: DEBUG << "Error: " << GetLastError(); aheinecke@11: } aheinecke@11: aheinecke@11: if (bResults) { aheinecke@11: DEBUG << "Querying Headers"; aheinecke@11: bResults = WinHttpQueryHeaders(hRequest, aheinecke@11: WINHTTP_QUERY_LAST_MODIFIED | aheinecke@11: WINHTTP_QUERY_FLAG_SYSTEMTIME, aheinecke@11: NULL, aheinecke@11: &lastModified, aheinecke@11: &sizeOfSystemtime, aheinecke@11: WINHTTP_NO_HEADER_INDEX); aheinecke@11: } else { aheinecke@11: DWORD errCode = GetLastError(); aheinecke@11: switch (errCode) { aheinecke@11: case ERROR_WINHTTP_HEADER_NOT_FOUND: aheinecke@11: DEBUG << "Header not found"; aheinecke@11: break; aheinecke@11: case ERROR_WINHTTP_INCORRECT_HANDLE_STATE: aheinecke@11: DEBUG << "Incorrect handle state"; aheinecke@11: break; aheinecke@11: case ERROR_WINHTTP_INCORRECT_HANDLE_TYPE: aheinecke@11: DEBUG << "Incorrect handle type"; aheinecke@11: break; aheinecke@11: case ERROR_WINHTTP_INTERNAL_ERROR: aheinecke@11: DEBUG << "Internal error"; aheinecke@11: break; aheinecke@11: case ERROR_NOT_ENOUGH_MEMORY: aheinecke@11: DEBUG << "OOM"; aheinecke@11: break; aheinecke@11: default: aheinecke@11: DEBUG << "Error: " << getLastErrorMsg(); aheinecke@11: } aheinecke@11: } aheinecke@11: aheinecke@11: DEBUG << "Last modified year: " << lastModified.wYear; aheinecke@11: aheinecke@11: aheinecke@11: if (!bResults) { aheinecke@11: // Report any errors. aheinecke@11: DEBUG << "Error" << GetLastError(); aheinecke@11: emit error(tr("Unknown Problem when connecting"), Unknown); aheinecke@11: } aheinecke@11: cleanup: aheinecke@11: if (hRequest) { aheinecke@11: WinHttpCloseHandle(hRequest); aheinecke@11: } aheinecke@11: aheinecke@11: if (hConnect) { aheinecke@11: WinHttpCloseHandle(hConnect); aheinecke@11: aheinecke@11: } aheinecke@11: aheinecke@11: if (hSession) { aheinecke@11: WinHttpCloseHandle(hSession); aheinecke@11: } aheinecke@11: return; aheinecke@11: } aheinecke@11: #endif