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 <windows.h>
aheinecke@11: #include <winhttp.h>
aheinecke@11: 
aheinecke@11: #include <QDebug>
aheinecke@15: #include <QDateTime>
aheinecke@15: #include <QSaveFile>
aheinecke@17: #include <QFileInfo>
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:  *
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:     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.
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@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