aheinecke@404: /* Copyright (C) 2014 by Bundesamt für Sicherheit in der Informationstechnik aheinecke@404: * Software engineering by Intevation GmbH aheinecke@404: * aheinecke@404: * This file is Free Software under the GNU GPL (v>=2) aheinecke@404: * and comes with ABSOLUTELY NO WARRANTY! aheinecke@404: * See LICENSE.txt for details. aheinecke@404: */ 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@15: #include aheinecke@15: #include aheinecke@17: #include aheinecke@11: aheinecke@11: #define DEBUG if (1) qDebug() << __PRETTY_FUNCTION__ aheinecke@11: aheinecke@15: #define MAX_SW_SIZE 10485760 aheinecke@15: #define MAX_LIST_SIZE 1048576 andre@27: /** @brief Download a file from the Internet andre@27: * andre@27: * @param[in] HSession the session to work in. andre@27: * @param[in] HConnect the connection to use. andre@27: * @param[in] resource the resource to download. andre@27: * @param[in] filename where the file should be saved. andre@27: * @param[in] maxSize maximum amount of bytes to download andre@27: * andre@27: * @returns True if the download was successful. andre@27: */ andre@27: bool downloadFile(HINTERNET hSession, HINTERNET hConnect, andre@27: LPCWSTR resource, const QString &filename, DWORD maxSize); andre@27: andre@27: /** @brief get the last modified header of a resource. andre@27: * andre@27: * On error call getLastError to get extended error information. andre@27: * This function still does not do any networking but only initializes andre@27: * it. andre@27: * andre@27: * @param[in] HSession the session to work in. andre@27: * @param[in] HConnect the connection to use. andre@27: * @param[in] resource the resource to check the last-modified date on andre@27: * andre@27: * @returns the last modified date or a null datetime in case of errors andre@27: */ andre@27: QDateTime getLastModifiedHeader(HINTERNET hSession, andre@27: HINTERNET hConnect, LPCWSTR resource); andre@27: andre@27: /** @brief verify that the certificate of the request matches andre@27: * andre@27: * Validates the certificate against the member variable certificate andre@27: * andre@27: * @param[in] hRequest: The request from which to get the certificate andre@27: * andre@27: * @returns True if the certificate exactly matches the one in hRequest andre@27: */ andre@27: andre@27: bool verifyCertificate(HINTERNET hRequest); aheinecke@15: aheinecke@17: aheinecke@17: #define LIST_RESOURCE "/incoming/aheinecke/test" aheinecke@17: #define SW_RESOURCE "/incoming/aheinecke/test" aheinecke@17: aheinecke@23: /** @brief A wrapper around a HINTERNET structure that handles closing aheinecke@23: * aheinecke@23: * Holds a HINTERNET structure and closes it if necessary on destruction. aheinecke@23: * aheinecke@23: */ aheinecke@23: class SmartHINTERNET { aheinecke@23: public: aheinecke@23: SmartHINTERNET() : handle(NULL) {} aheinecke@23: aheinecke@23: ~SmartHINTERNET() { aheinecke@23: if (handle) { aheinecke@23: WinHttpCloseHandle(handle); aheinecke@23: } aheinecke@23: } aheinecke@23: aheinecke@23: HINTERNET handle; aheinecke@23: }; aheinecke@17: aheinecke@15: /** @brief Qt wrapper around FormatMessage aheinecke@15: * aheinecke@15: * @returns The error message of the error that occurred aheinecke@15: */ 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@15: if (!bufPtr) { aheinecke@15: HMODULE hWinhttp = GetModuleHandleW(L"winhttp"); aheinecke@15: if (hWinhttp) { aheinecke@15: FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | aheinecke@15: FORMAT_MESSAGE_FROM_HMODULE | aheinecke@15: FORMAT_MESSAGE_IGNORE_INSERTS, aheinecke@15: hWinhttp, HRESULT_CODE(err), 0, aheinecke@15: (LPWSTR)&bufPtr, 0, NULL); aheinecke@15: } aheinecke@15: } 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@15: aheinecke@15: /** @brief open a session with appropriate proxy settings aheinecke@11: * andre@1304: * @param[in,out] *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: 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@409: *pHSession = WinHttpOpen(L"TrustBridge "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@409: *pHSession = WinHttpOpen(L"TrustBridge "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. andre@1304: * @param[in,out] *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. andre@1304: * @param[in,out] *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@15: DWORD dwSSLFlag; 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@15: aheinecke@15: dwSSLFlag = SECURITY_FLAG_IGNORE_UNKNOWN_CA; aheinecke@15: dwSSLFlag |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID; aheinecke@15: dwSSLFlag |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID; aheinecke@15: dwSSLFlag |= SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE; aheinecke@15: aheinecke@15: WinHttpSetOption(*pHRequest, WINHTTP_OPTION_SECURITY_FLAGS, aheinecke@15: &dwSSLFlag, sizeof(dwSSLFlag)); aheinecke@15: aheinecke@11: return *pHRequest; aheinecke@11: } aheinecke@11: aheinecke@15: bool Downloader::verifyCertificate(HINTERNET hRequest) aheinecke@15: { aheinecke@15: CERT_CONTEXT *certContext = NULL; aheinecke@15: DWORD certContextLen = sizeof(CERT_CONTEXT); aheinecke@15: bool retval = false; aheinecke@11: aheinecke@15: if (!WinHttpQueryOption(hRequest, aheinecke@15: WINHTTP_OPTION_SERVER_CERT_CONTEXT, aheinecke@15: &certContext, aheinecke@15: &certContextLen)) { aheinecke@15: DEBUG << "Unable to get server certificate"; aheinecke@15: return false; aheinecke@11: } aheinecke@11: aheinecke@15: QByteArray serverCert ((const char *) certContext->pbCertEncoded, aheinecke@15: certContext->cbCertEncoded); aheinecke@15: aheinecke@15: retval = (serverCert == mCert); aheinecke@15: aheinecke@15: if (!retval) { aheinecke@15: DEBUG << "Certificate is not the same as the pinned one!" aheinecke@15: << "Base64 cert: " << serverCert.toBase64(); aheinecke@18: emit error("Invalid certificate", InvalidCertificate); aheinecke@15: } aheinecke@15: aheinecke@15: CertFreeCertificateContext(certContext); aheinecke@15: return retval; aheinecke@15: } aheinecke@15: aheinecke@15: QDateTime Downloader::getLastModifiedHeader(HINTERNET hSession, aheinecke@15: HINTERNET hConnect, LPCWSTR resource) aheinecke@15: { aheinecke@23: SmartHINTERNET sHRequest; aheinecke@15: SYSTEMTIME lMod; aheinecke@15: DWORD sizeOfSystemtime = sizeof (SYSTEMTIME); aheinecke@15: QDateTime retval; aheinecke@15: aheinecke@15: memset(&lMod, 0, sizeof (SYSTEMTIME)); aheinecke@15: aheinecke@15: if (!hSession || !hConnect || !resource) { aheinecke@15: SetLastError(ERROR_INVALID_PARAMETER); aheinecke@15: return retval; aheinecke@15: } aheinecke@15: aheinecke@23: if (!createRequest(hSession, hConnect, &sHRequest.handle, L"HEAD", aheinecke@15: resource)) { aheinecke@23: return retval; aheinecke@11: } aheinecke@11: aheinecke@23: if (!WinHttpSendRequest(sHRequest.handle, aheinecke@15: WINHTTP_NO_ADDITIONAL_HEADERS, aheinecke@15: 0, WINHTTP_NO_REQUEST_DATA, 0, aheinecke@15: 0, 0)) { aheinecke@23: return retval; aheinecke@11: } aheinecke@11: aheinecke@11: aheinecke@23: if (!WinHttpReceiveResponse(sHRequest.handle, NULL)) { aheinecke@23: return retval; aheinecke@11: } aheinecke@11: aheinecke@23: if (!verifyCertificate(sHRequest.handle)) { aheinecke@15: DEBUG << "Certificate verification failed"; aheinecke@23: return retval; aheinecke@11: } aheinecke@11: aheinecke@23: if (!(WinHttpQueryHeaders(sHRequest.handle, aheinecke@15: WINHTTP_QUERY_LAST_MODIFIED | aheinecke@15: WINHTTP_QUERY_FLAG_SYSTEMTIME, aheinecke@15: NULL, aheinecke@15: &lMod, aheinecke@15: &sizeOfSystemtime, aheinecke@15: WINHTTP_NO_HEADER_INDEX))) { aheinecke@23: return retval; aheinecke@15: } aheinecke@11: aheinecke@15: retval = QDateTime(QDate(lMod.wYear, lMod.wMonth, lMod.wDay), aheinecke@15: QTime(lMod.wHour, lMod.wMinute, lMod.wSecond, aheinecke@15: lMod.wMilliseconds), aheinecke@15: Qt::UTC); aheinecke@15: return retval; aheinecke@15: } aheinecke@15: aheinecke@15: bool Downloader::downloadFile(HINTERNET hSession, HINTERNET hConnect, aheinecke@17: LPCWSTR resource, const QString &fileName, DWORD maxSize) aheinecke@15: { aheinecke@23: SmartHINTERNET sHRequest; aheinecke@15: bool retval = false; aheinecke@15: DWORD bytesAvailable = 0, aheinecke@15: bytesRead = 0, aheinecke@17: totalDownloaded = 0, aheinecke@17: contentLength = 0, aheinecke@17: sizeOfDWORD = sizeof (DWORD); aheinecke@15: aheinecke@17: QSaveFile outputFile(fileName); aheinecke@15: aheinecke@15: if (!hSession || !hConnect || !resource) { aheinecke@15: SetLastError(ERROR_INVALID_PARAMETER); aheinecke@15: return retval; aheinecke@15: } aheinecke@15: aheinecke@23: if (!createRequest(hSession, hConnect, &sHRequest.handle, L"GET", aheinecke@15: resource)) { aheinecke@23: return retval; aheinecke@15: } aheinecke@15: aheinecke@23: if (!WinHttpSendRequest(sHRequest.handle, aheinecke@15: WINHTTP_NO_ADDITIONAL_HEADERS, aheinecke@15: 0, WINHTTP_NO_REQUEST_DATA, 0, aheinecke@15: 0, 0)) { aheinecke@23: return retval; aheinecke@15: } aheinecke@15: aheinecke@15: aheinecke@23: if (!WinHttpReceiveResponse(sHRequest.handle, NULL)) { aheinecke@23: return retval; aheinecke@15: } aheinecke@15: aheinecke@23: if (!verifyCertificate(sHRequest.handle)) { aheinecke@15: DEBUG << "Certificate verification failed"; aheinecke@23: return retval; aheinecke@15: } aheinecke@15: aheinecke@17: aheinecke@23: if (!(WinHttpQueryHeaders(sHRequest.handle, aheinecke@17: WINHTTP_QUERY_CONTENT_LENGTH | aheinecke@17: WINHTTP_QUERY_FLAG_NUMBER, aheinecke@17: NULL, aheinecke@17: &contentLength, aheinecke@17: &sizeOfDWORD, aheinecke@17: WINHTTP_NO_HEADER_INDEX))) { aheinecke@17: // Continue anyway as we later really check how aheinecke@17: // much we download. aheinecke@17: DEBUG << "No content-length"; aheinecke@17: } aheinecke@17: aheinecke@17: if (contentLength > maxSize) { aheinecke@23: return retval; aheinecke@17: } aheinecke@17: aheinecke@17: if (contentLength) { aheinecke@17: QFileInfo finf(fileName); aheinecke@17: if (finf.exists() && finf.isReadable() && aheinecke@17: finf.size() == contentLength) { aheinecke@17: // We already have data of the same size aheinecke@17: // No need to waste bandwidth. aheinecke@17: DEBUG << "Skipping download because file exists"; aheinecke@17: retval = true; aheinecke@23: return retval; aheinecke@17: } aheinecke@17: } aheinecke@17: aheinecke@15: // Open / Create the file to write to. aheinecke@15: if (!outputFile.open(QIODevice::WriteOnly)) { aheinecke@15: DEBUG << "Failed to open file"; aheinecke@23: return retval; aheinecke@15: } aheinecke@15: aheinecke@17: DEBUG << "output file size: " << outputFile.size(); aheinecke@15: do aheinecke@15: { aheinecke@15: char outBuf[8192]; // 8KB is the internal buffer size of winhttp aheinecke@15: memset(outBuf, 0, sizeof(outBuf)); aheinecke@15: bytesRead = 0; aheinecke@15: aheinecke@23: if (!WinHttpQueryDataAvailable(sHRequest.handle, &bytesAvailable)) { aheinecke@15: DEBUG << "Querying for available data failed"; aheinecke@15: retval = false; aheinecke@15: break; aheinecke@15: } aheinecke@15: aheinecke@15: if (!bytesAvailable) { aheinecke@15: // Might indicate that we are done. aheinecke@15: break; aheinecke@15: } aheinecke@15: aheinecke@15: if (bytesAvailable > maxSize) { aheinecke@15: DEBUG << "File to large"; aheinecke@15: retval = false; aheinecke@23: SetLastError(ERROR_INVALID_DATA); aheinecke@15: break; aheinecke@15: } aheinecke@15: aheinecke@23: if (!WinHttpReadData(sHRequest.handle, (LPVOID)outBuf, aheinecke@15: sizeof(outBuf), &bytesRead)) { aheinecke@15: DEBUG << "Error reading data"; aheinecke@15: break; aheinecke@15: } else { aheinecke@15: if (bytesRead) { aheinecke@15: // Write data to file. aheinecke@15: if (outputFile.write(outBuf, bytesRead) != aheinecke@15: bytesRead) { aheinecke@15: DEBUG << "Error writing to file."; aheinecke@15: retval = false; aheinecke@15: } aheinecke@15: // Completed a read / write cycle. If not error follows aheinecke@15: // the download was successful. aheinecke@15: retval = true; aheinecke@15: } else { aheinecke@15: // Should not happen as we queried for available aheinecke@15: // bytes before and the function did not return an aheinecke@15: // error. aheinecke@15: DEBUG << "Unable to read available data"; aheinecke@15: retval = false; aheinecke@15: break; aheinecke@15: } aheinecke@15: } aheinecke@15: totalDownloaded += bytesRead; aheinecke@15: aheinecke@15: if (totalDownloaded > maxSize) { aheinecke@15: DEBUG << "Downloaded too much data. Breaking."; aheinecke@15: retval = false; aheinecke@15: break; aheinecke@15: } aheinecke@15: } while (bytesAvailable > 0); aheinecke@15: aheinecke@17: if (retval && outputFile.isOpen()) { aheinecke@15: // Actually save the file to disk / move to homedir aheinecke@15: retval = outputFile.commit(); aheinecke@15: } aheinecke@15: aheinecke@15: return retval; aheinecke@15: } aheinecke@15: aheinecke@15: void Downloader::run() { aheinecke@15: bool results = false; aheinecke@23: SmartHINTERNET sHSession; aheinecke@23: SmartHINTERNET sHConnect; aheinecke@15: wchar_t wUrl[mUrl.size() + 1]; aheinecke@15: QDateTime lastModifiedSoftware; aheinecke@15: QDateTime lastModifiedList; aheinecke@15: aheinecke@15: int rc = 0; aheinecke@15: aheinecke@15: memset(wUrl, 0, sizeof (wchar_t) * (mUrl.size() + 1)); aheinecke@15: aheinecke@15: rc = mUrl.toWCharArray(wUrl); aheinecke@15: aheinecke@15: if (rc != mUrl.size()) { aheinecke@15: DEBUG << "Problem converting to wchar array"; aheinecke@15: return; aheinecke@15: } aheinecke@15: aheinecke@15: // Should not be necessary because we initialized the memory aheinecke@15: wUrl[rc] = '\0'; aheinecke@15: aheinecke@15: // Initialize connection aheinecke@23: if (!openSession(&sHConnect.handle)) { aheinecke@15: DEBUG << "Failed to open session: " << getLastErrorMsg(); aheinecke@15: return; aheinecke@15: } aheinecke@23: if (!initializeConnection(sHConnect.handle, &sHConnect.handle, wUrl)) { aheinecke@15: DEBUG << "Failed to initialize connection: " << getLastErrorMsg(); aheinecke@23: return; aheinecke@15: } aheinecke@15: aheinecke@15: aheinecke@23: lastModifiedSoftware = getLastModifiedHeader(sHConnect.handle, sHConnect.handle, aheinecke@17: L""SW_RESOURCE); aheinecke@15: aheinecke@23: lastModifiedList = getLastModifiedHeader(sHConnect.handle, sHConnect.handle, aheinecke@17: L""LIST_RESOURCE); aheinecke@15: aheinecke@15: if (!lastModifiedList.isValid() || !lastModifiedSoftware.isValid()) { aheinecke@15: DEBUG << "Could not read headers: " << getLastErrorMsg(); aheinecke@23: return; aheinecke@15: } aheinecke@15: aheinecke@15: if (!mLastModSW.isValid() || lastModifiedSoftware > mLastModSW) { aheinecke@15: QString dataDirectory = getDataDirectory(); aheinecke@15: aheinecke@15: if (dataDirectory.isEmpty()) { aheinecke@15: DEBUG << "Failed to get data directory"; aheinecke@23: return; aheinecke@15: } aheinecke@15: aheinecke@15: QString fileName = dataDirectory.append("/SW-") aheinecke@15: .append(lastModifiedSoftware.toString("yyyymmddHHmmss")) aheinecke@15: .append(".exe"); aheinecke@15: aheinecke@17: DEBUG << "fileName: " << fileName; aheinecke@15: aheinecke@23: if (!downloadFile(sHConnect.handle, sHConnect.handle, L""SW_RESOURCE, aheinecke@15: fileName, MAX_SW_SIZE)) { aheinecke@15: DEBUG << "Error downloading File: " << getLastErrorMsg(); aheinecke@23: return; aheinecke@15: } aheinecke@15: aheinecke@15: emit newSoftwareAvailable(fileName, lastModifiedSoftware); aheinecke@15: } else if (!mLastModList.isValid() || lastModifiedList > mLastModList) { aheinecke@15: QString dataDirectory = getDataDirectory(); aheinecke@15: aheinecke@15: if (dataDirectory.isEmpty()) { aheinecke@15: DEBUG << "Failed to get data directory"; aheinecke@23: return; aheinecke@15: } aheinecke@15: aheinecke@15: QString fileName = dataDirectory.append("/list-") aheinecke@15: .append(lastModifiedSoftware.toString("yyyymmddHHmmss")) aheinecke@15: .append(".txt"); aheinecke@15: aheinecke@17: DEBUG << "fileName: " << fileName; aheinecke@15: aheinecke@23: if (!downloadFile(sHConnect.handle, sHConnect.handle, L""LIST_RESOURCE, aheinecke@15: fileName, MAX_LIST_SIZE)) { aheinecke@15: DEBUG << "Error downloading File: " << getLastErrorMsg(); aheinecke@23: return; aheinecke@15: } aheinecke@15: aheinecke@15: emit newListAvailable(fileName, lastModifiedList); aheinecke@15: } aheinecke@15: aheinecke@15: DEBUG << "SW date: " << lastModifiedSoftware; aheinecke@15: DEBUG << "List date: " << lastModifiedList; aheinecke@15: aheinecke@15: if (!results) { aheinecke@15: // Report any errors. aheinecke@15: DEBUG << "Error" << GetLastError(); aheinecke@15: emit error(tr("Unknown Problem when connecting"), ErrUnknown); aheinecke@15: } aheinecke@20: aheinecke@11: return; aheinecke@11: } aheinecke@11: #endif