view ui/downloader_win.cpp @ 11:7e2f14c7aba2

Split up downloader component and further implement it
author Andre Heinecke <aheinecke@intevation.de>
date Fri, 14 Feb 2014 11:20:15 +0000
parents
children 95e1b6edf2fc
line wrap: on
line source
/**
 * @file downloader_win.cpp
 * @brief Downloader implementation for Windows
 *
 * We use Windows API here instead of Qt because we want to avoid
 * QtNetworks SSL stack which is based on OpenSSL and so
 * we might be incompatible with GPL code. Also using the
 * native API means that the security of the SSL implementation
 * is tied to the security of the system.
 *
 */
#include "downloader.h"
#ifdef Q_OS_WIN
#ifndef MYVERSION
#define MYVERSION "1"
#endif

#include <windows.h>
#include <winhttp.h>

#include <QDebug>

#define DEBUG if (1) qDebug() << __PRETTY_FUNCTION__

const QString getLastErrorMsg() {
    LPWSTR bufPtr = NULL;
    DWORD err = GetLastError();
    FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
                   FORMAT_MESSAGE_FROM_SYSTEM |
                   FORMAT_MESSAGE_IGNORE_INSERTS,
                   NULL, err, 0, (LPWSTR)&bufPtr, 0, NULL);
    const QString result =
        (bufPtr) ? QString::fromUtf16((const ushort*)bufPtr).trimmed() :
                   QString("Unknown Error %1").arg(err);
    LocalFree(bufPtr);
    return result;
}

/** @brief open a session with appropiate proxy settings
 *
 * @param[inout] *pHSession pointer to a HInternet structure
 *
 * On error call getLastError to get extended error information.
 *
 * @returns True on success, false on error.
 */


bool openSession(HINTERNET *pHSession)
{
    WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxyConfig;

    DEBUG;
    if (!pHSession) {
        SetLastError(ERROR_INVALID_PARAMETER);
        return false;
    }

    qDebug() << "2";
    memset(&proxyConfig, 0, sizeof (WINHTTP_CURRENT_USER_IE_PROXY_CONFIG));

    if (WinHttpGetIEProxyConfigForCurrentUser(&proxyConfig)) {
        if (proxyConfig.fAutoDetect) {
            // TODO Handle this
            qDebug() << "Autodetect is set";
        }

        if (proxyConfig.lpszProxy || proxyConfig.lpszProxyBypass) {
            DEBUG << "Using proxies.";
        }

        if (proxyConfig.lpszProxy) {
            *pHSession = WinHttpOpen(L"M13 "MYVERSION,
                                     WINHTTP_ACCESS_TYPE_NAMED_PROXY,
                                     proxyConfig.lpszProxy,
                                     proxyConfig.lpszProxyBypass, 0);
        }
    }

    if (!*pHSession) {
        DEBUG << "No IE Proxy falling back to default proxy";
        *pHSession = WinHttpOpen(L"M13 "MYVERSION,
                                 WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
                                 WINHTTP_NO_PROXY_NAME,
                                 WINHTTP_NO_PROXY_BYPASS, 0);
    }
    // Cleanup
    if (proxyConfig.lpszAutoConfigUrl) {
        GlobalFree(proxyConfig.lpszAutoConfigUrl);
    }

    if (proxyConfig.lpszProxy) {
        GlobalFree(proxyConfig.lpszProxy);
    }

    if (proxyConfig.lpszProxyBypass) {
        GlobalFree(proxyConfig.lpszProxyBypass);
    }
    return *pHSession;
}


/** @brief initialize a connection in the session
 *
 * @param[in] HSession the session to work in.
 * @param[inout] *pHConnect pointer to the connection.
 * @param[in] url pointer to the URL in wchar representation.
 *
 * On error call getLastError to get extended error information.
 *
 * @returns True on success, false on error.
 */
bool initializeConnection(HINTERNET hSession, HINTERNET *pHConnect,
        LPCWSTR url)
{
    DEBUG;
    if (!hSession || !pHConnect) {
        SetLastError(ERROR_INVALID_PARAMETER);
        return false;
    }
    // Initialize connection. No request is done here.
    *pHConnect = WinHttpConnect(hSession, url,
                                INTERNET_DEFAULT_HTTPS_PORT, 0);

    return *pHConnect;
}

/** @brief Create a request
 *
 * @param[in] HSession the session to work in.
 * @param[in] HConnect the connection to use.
 * @param[inout] *pHRequest pointer to the request structure to be filled.
 * @param[in] requestType the HTTP request to be made default is GET
 * @param[in] resource pointer to the resource to request in wchar
 *            representation.
 *
 * On error call getLastError to get extended error information.
 * This function still does not do any networking but only initializes
 * it.
 *
 * @returns True on success, false on error.
 */

bool createRequest(HINTERNET hSession, HINTERNET hConnect,
        HINTERNET *pHRequest, LPCWSTR requestType, LPCWSTR resource)
{
    DEBUG;
    if (!hSession || !hConnect || !pHRequest) {
        SetLastError(ERROR_INVALID_PARAMETER);
        return false;
    }

    *pHRequest = WinHttpOpenRequest(hConnect, requestType, resource,
                                    NULL, WINHTTP_NO_REFERER,
                                    WINHTTP_DEFAULT_ACCEPT_TYPES,
                                    WINHTTP_FLAG_SECURE);
    return *pHRequest;
}

void Downloader::run() {
    BOOL bResults = FALSE;
    HINTERNET hSession = NULL,
              hConnect = NULL,
              hRequest = NULL;

    SYSTEMTIME lastModified;
    DWORD sizeOfSystemtime = sizeof (SYSTEMTIME);

    memset(&lastModified, 0, sizeof (SYSTEMTIME));

    if (!openSession(&hSession)) {
        DEBUG << "Failed to open session: " << getLastErrorMsg();
        return;
    }

    if (!initializeConnection(hSession, &hConnect, L"www.intevation.de")) {
        DEBUG << "Failed to initialize connection: " << getLastErrorMsg();
        goto cleanup;
    }

    if (!createRequest(hSession, hConnect, &hRequest, L"GET", L"/")) {
        DEBUG << "Failed to create the request: " << getLastErrorMsg();
        goto cleanup;
    }

    if (hRequest) {
        DEBUG << "Doing Request";
        bResults = WinHttpSendRequest(hRequest,
                                      WINHTTP_NO_ADDITIONAL_HEADERS,
                                      0, WINHTTP_NO_REQUEST_DATA, 0,
                                      0, 0);
    } else {
        DEBUG << "Error: " << GetLastError();
    }


    if (bResults) {
        DEBUG << "Recieving Response";
        bResults = WinHttpReceiveResponse(hRequest, NULL);
    } else {
        DEBUG << "Error: " << GetLastError();
    }

    if (bResults) {
        DEBUG << "Querying Headers";
        bResults = WinHttpQueryHeaders(hRequest,
                                       WINHTTP_QUERY_LAST_MODIFIED |
                                       WINHTTP_QUERY_FLAG_SYSTEMTIME,
                                       NULL,
                                       &lastModified,
                                       &sizeOfSystemtime,
                                       WINHTTP_NO_HEADER_INDEX);
    } else {
        DWORD errCode = GetLastError();
        switch (errCode) {
            case ERROR_WINHTTP_HEADER_NOT_FOUND:
                DEBUG << "Header not found";
                break;
            case ERROR_WINHTTP_INCORRECT_HANDLE_STATE:
                DEBUG << "Incorrect handle state";
                break;
            case ERROR_WINHTTP_INCORRECT_HANDLE_TYPE:
                DEBUG << "Incorrect handle type";
                break;
            case ERROR_WINHTTP_INTERNAL_ERROR:
                DEBUG << "Internal error";
                break;
            case ERROR_NOT_ENOUGH_MEMORY:
                DEBUG << "OOM";
                break;
            default:
                DEBUG << "Error: " << getLastErrorMsg();
        }
    }

    DEBUG << "Last modified year: " << lastModified.wYear;


    if (!bResults) {
        // Report any errors.
        DEBUG << "Error" << GetLastError();
        emit error(tr("Unknown Problem when connecting"), Unknown);
    }
cleanup:
    if (hRequest) {
        WinHttpCloseHandle(hRequest);
    }

    if (hConnect) {
        WinHttpCloseHandle(hConnect);

    }

    if (hSession) {
        WinHttpCloseHandle(hSession);
    }
    return;
}
#endif

http://wald.intevation.org/projects/trustbridge/