browsercore/appfw/Api/Managers/downloadcontroller.cpp
author hgs
Fri, 17 Sep 2010 12:11:40 -0400
changeset 12 afcd8e6d025b
parent 3 0954f5dd2cd0
permissions -rw-r--r--
201037

/*
* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 2.1 of the License.
* 
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program.  If not, 
* see "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html/".
*
* Description:
*
*/

#include "downloadcontroller.h"
#include "downloadcontroller_p.h"

#include "downloadproxy_p.h"

#include <QFileInfo>
#include <QNetworkProxy>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QString>
#include <QUrl>
#include <QWebPage>

#ifdef USE_DOWNLOAD_MANAGER
using namespace WRT;

static const char * downloadErrorToString(WRT::Error error)
{
    switch (error) {
    case WRT::NoError:
        return "WRT::NoError";
    case WRT::ConnectionRefusedError:
        return "WRT::ConnectionRefusedError";
    case WRT::RemoteHostClosedError:
        return "WRT::RemoteHostClosedError";
    case WRT::HostNotFoundError:
        return "WRT::HostNotFoundError";
    case WRT::TimeoutError:
        return "WRT::TimeoutError";
    case WRT::OperationCanceledError:
        return "WRT::OperationCanceledError";
    case WRT::SslHandshakeFailedError:
        return "WRT::SslHandshakeFailedError";
    case WRT::TemporaryNetworkFailureError:
        return "WRT::TemporaryNetworkFailureError";
    case WRT::UnknownNetworkError:
        return "WRT::UnknownNetworkError";
    case WRT::ProxyConnectionRefusedError:
        return "WRT::ProxyConnectionRefusedError";
    case WRT::ProxyConnectionClosedError:
        return "WRT::ProxyConnectionClosedError";
    case WRT::ProxyNotFoundError:
        return "WRT::ProxyNotFoundError";
    case WRT::ProxyTimeoutError:
        return "WRT::ProxyTimeoutError";
    case WRT::ProxyAuthenticationRequiredError:
        return "WRT::ProxyAuthenticationRequiredError";
    case WRT::UnknownProxyError:
        return "WRT::UnknownProxyError";
    case WRT::ContentAccessDenied:
        return "WRT::ContentAccessDenied";
    case WRT::ContentOperationNotPermittedError:
        return "WRT::ContentOperationNotPermittedError";
    case WRT::ContentNotFoundError:
        return "WRT::ContentNotFoundError";
    case WRT::AuthenticationRequiredError:
        return "WRT::AuthenticationRequiredError";
    case WRT::ContentReSendError:
        return "WRT::ContentReSendError";
    case WRT::UnknownContentError:
        return "WRT::UnknownContentError";
    case WRT::ProtocolUnknownError:
        return "WRT::ProtocolUnknownError";
    case WRT::ProtocolInvalidOperationError:
        return "WRT::ProtocolInvalidOperationError";
    case WRT::ProtocolFailure:
        return "WRT::ProtocolFailure";
    case WRT::ConnectionFailed:
        return "WRT::ConnectionFailed";
    case WRT::HttpRestartFailed:
        return "WRT::HttpRestartFailed";
    case WRT::ContentExpired:
        return "WRT::ContentExpired";
    case WRT::PartialContentModified:
        return "WRT::PartialContentModified";
    case WRT::ObjectNotFound:
        return "WRT::ObjectNotFound";
    case WRT::HttpUnhandled:
        return "WRT::HttpUnhandled";
    case WRT::DestinationFileInUse:
        return "WRT::DestinationFileInUse";
    case WRT::WrongDestinationFilename:
        return "WRT::WrongDestinationFilename";
    case WRT::BadUrl:
        return "WRT::BadUrl";
    case WRT::MediaRemoved:
        return "WRT::MediaRemoved";
    case WRT::FileWriteFailed:
        return "WRT::FileWriteFailed";
    case WRT::FileNotFound:
        return "WRT::FileNotFound";
    case WRT::InvalidDrive:
        return "WRT::InvalidDrive";
    case WRT::MoveFailed:
        return "WRT::MoveFailed";
    case WRT::DiskFull:
        return "WRT::DiskFull";
    case WRT::DiskError:
        return "WRT::DiskError";
    case WRT::TransactionFailed:
        return "WRT::TransactionFailed";
    case WRT::Internal:
        return "WRT::Internal";
    case WRT::General:
        return "WRT::General";
    case WRT::MaxRedirectionsReached:
        return "WRT::MaxRedirectionsReached";
    case WRT::InvalidDownloadDescriptor:
        return "WRT::InvalidDownloadDescriptor";
    default:
        return "???";
    }
}

static const char * downloadEventToString(WRT::DownloadEvent::Event type)
{
    switch (type) {
    case WRT::DownloadEvent::Created:
        return "DownloadEvent:Created";
    case WRT::DownloadEvent::Started:
        return "DownloadEvent::Started";
    case WRT::DownloadEvent::HeadersReceived:
        return "DownloadEvent::HeadersReceived";
    case WRT::DownloadEvent::InProgress:
        return "DownloadEvent::InProgress";
    case WRT::DownloadEvent::Paused:
        return "DownloadEvent::Paused";
    case WRT::DownloadEvent::Completed:
        return "DownloadEvent::Completed";
    case WRT::DownloadEvent::Failed:
        return "DownloadEvent::Failed";
    case WRT::DownloadEvent::Cancelled:
        return "DownloadEvent::Cancelled";
    case WRT::DownloadEvent::DescriptorUpdated:
        return "DownloadEvent::DescriptorUpdated";
    case WRT::DownloadEvent::NetworkLoss:
        return "DownloadEvent::NetworkLoss";
    case WRT::DownloadEvent::Error:
        return "DownloadEvent::Error";
    case WRT::DownloadEvent::DescriptorReady:
        return "DownloadEvent::DescriptorReady";
    case WRT::DownloadEvent::LicenseAcquiring:
        return "DownloadEvent::LicenseAcquiring";
    case WRT::DownloadEvent::MediaRemoved:
        return "DownloadEvent::MediaRemoved";
    case WRT::DownloadEvent::MediaInserted:
        return "DownloadEvent::MediaInserted";
    case WRT::DownloadEvent::RedirectedPermanently:
        return "DownloadEvent::RedirectedPermanently";
    case WRT::DownloadEvent::RedirectedTemporarily:
        return "DownloadEvent::RedirectedTemporarily";
    case WRT::DownloadEvent::NameChanged:
        return "DownloadEvent::NameChanged";
    case WRT::DownloadEvent::ContentTypeChanged:
        return "DownloadEvent::ContentTypeChanged";
    case WRT::DownloadEvent::CreatingConnection:
        return "DownloadEvent::CreatingConnection";
    case WRT::DownloadEvent::ConnectionNeeded:
        return "DownloadEvent::ConnectionNeeded";
    case WRT::DownloadEvent::ConnectionDisconnected:
        return "DownloadEvent::ConnectionDisconnected";
    case WRT::DownloadEvent::RightObjectsAcquired:
        return "DownloadEvent::RightObjectsAcquired";
    default:
        return 0;
    }
}

static void debugDownloadEvent(WRT::DownloadEvent::Event type)
{
    const char * name = downloadEventToString(type);
    if (name == 0) {
        return;
    }

    qDebug() << "Received event" << name;
}

// DownloadControllerPrivate implementation

DownloadControllerPrivate::DownloadControllerPrivate(
    DownloadController * downloadController,
    const QString & client,
    const QNetworkProxy & proxy)
{
    m_downloadController = downloadController;

    m_downloadManager = new WRT::DownloadManager(client);

    if (m_downloadManager->initialize() != 0) {
        return;
    }

    if (!connect(
                m_downloadManager,
                SIGNAL(downloadManagerEvent(DownloadManagerEvent *)),
                this,
                SLOT(handleDownloadManagerEvent(DownloadManagerEvent *)))) {
        //;;; how to handle error?
    }

    if (proxy.type() != QNetworkProxy::NoProxy)
        m_downloadManager->setProxy(proxy.hostName(), proxy.port());
}

DownloadControllerPrivate::~DownloadControllerPrivate()
{
    delete m_downloadManager;
}

static QString downloadFileName(QUrl url)
{
    QString scheme = url.scheme();

    // For http and https, let the download manager determine the filename.

    if (scheme == "http" || scheme == "https")
        return QString();

    // For data scheme (see http://en.wikipedia.org/wiki/Data_URI_scheme)
    // we don't have a file name per-se, so construct one from the content
    // type.

    if (scheme == "data") {
        // Typical example: data:image/png;base64,...

        QString path = url.path();
        QString type = path.section('/', 0, 0);
        QString subtype = path.section('/', 1, 1).section(';', 0, 0);

        // For now we just use type as base name and subtype
        // as extension.  For the common case of image/jpg
        // and stuff like that this works fine.

        return type + "." + subtype;
    }

    // For all other schemes, let the download manager determine the filename.

    return QString();
}

void DownloadControllerPrivate::startDownload(const QUrl & url, const QFileInfo & info)
{
    WRT::Download * download = m_downloadManager->createDownload(url.toString());

    download->setAttribute(WRT::DestinationPath, info.absolutePath());
    download->setAttribute(WRT::FileName, info.fileName());

    startDownload(download, url);
}

void DownloadControllerPrivate::startDownload(QNetworkReply * reply)
{
    QUrl url = reply->url();

    WRT::Download * download = m_downloadManager->createDownload(reply);

    startDownload(download, url);
}

void DownloadControllerPrivate::startDownload(const QNetworkRequest & request)
{
    QUrl url = request.url();

    WRT::Download * download = m_downloadManager->createDownload(url.toString());

    startDownload(download, url);
}

void DownloadControllerPrivate::startDownload(WRT::Download * download, const QUrl & url)
{
    // If necessary suggest an alternate file name.
    // The download manager will adjust the file name for us to handle
    // duplicates in the destination directory.

    QString file = downloadFileName(url);

    if (file.length() > 0) {
        QVariant value(file);
        download->setAttribute(WRT::FileName, value);
    }

    // Start download.

    DownloadProxy downloadProxy(new DownloadProxyData(download));

    emit m_downloadController->downloadCreated(downloadProxy);

    if (!connect(
                download,
                SIGNAL(downloadEvent(DownloadEvent *)),
                this,
                SLOT(handleDownloadEvent(DownloadEvent *)))) {
        //;;; how to handle error?
    }

    if (!connect(
                download,
                SIGNAL(downloadError(Error)),
                this,
                SLOT(handleDownloadError(Error)))) {
        //;;; how to handle error?
    }

    download->start();

}

void DownloadControllerPrivate::handleDownloadManagerEvent(
        DownloadManagerEvent * event)
{
    int type = event->type();

    switch (type) {
    case WRT::DownloadManagerEvent::Created:
        // Instead of waiting for the DownloadManager Created event
        // we emit downloadCreated in startDownload above so that we
        // can add a pointer to the download created as a parameter.
        return;

    case WRT::DownloadManagerEvent::Removed:
        if (m_downloadManager->currentDownloads().empty()) {
            emit m_downloadController->downloadsCleared();
        }
        return;

    default:
        qDebug() << "Unexpected download manager event:" << type;
        return;
    }
}

void DownloadControllerPrivate::handleDownloadEvent(
        DownloadEvent * event)
{
    int id = event->id();

    WRT::Download * download = m_downloadManager->findDownload(id);

    if (!download) {
        qDebug() << "Cannot find download with id" << id << " for error " << event;
        return;
    }

    int errorNum = download->attribute(WRT::LastError).toInt();

    const char * errorStr = downloadErrorToString(
            static_cast<WRT::Error>(errorNum));

    QString error;
    if (errorStr != 0)
        error = errorStr;

    DownloadProxy downloadProxy(new DownloadProxyData(download));

    switch (event->type())
    {

    case WRT::DownloadEvent::Created:
        // Nothing to do, we already emitted a signal to report
        // creation of a new Download object from startDownload().
        return;

    case WRT::DownloadEvent::Started:
        emit m_downloadController->downloadStarted(downloadProxy);
        return;

    case WRT::DownloadEvent::HeadersReceived:
        emit m_downloadController->downloadHeaderReceived(downloadProxy);
        return;

    case WRT::DownloadEvent::InProgress:
        emit m_downloadController->downloadProgress(downloadProxy);
        return;

    case WRT::DownloadEvent::Completed:
        emit m_downloadController->downloadFinished(downloadProxy);
        return;

    case WRT::DownloadEvent::Paused:
        emit m_downloadController->downloadPaused(downloadProxy, error);
        return;

    case WRT::DownloadEvent::Cancelled:
        emit m_downloadController->downloadCancelled(downloadProxy, error);
        return;

    case WRT::DownloadEvent::Failed:
        emit m_downloadController->downloadFailed(downloadProxy, error);
        return;

    case WRT::DownloadEvent::DescriptorUpdated:
        // FIXME - Update to support OMA and DRM.
        return;

    case WRT::DownloadEvent::NetworkLoss:
        emit m_downloadController->downloadNetworkLoss(downloadProxy, error);
        return;

    case WRT::DownloadEvent::Error:
        emit m_downloadController->downloadError(downloadProxy, error);
        return;

    case WRT::DownloadEvent::DescriptorReady:
        // FIXME - Update to support OMA and DRM.
        return;

    case WRT::DownloadEvent::LicenseAcquiring:
        // FIXME - Update to support OMA and DRM.
        return;

    case WRT::DownloadEvent::MediaRemoved:
        // FIXME ;;; what to do?
        emit m_downloadController->downloadMessage(
                downloadProxy,
                "Media Removed");
        return;

    case WRT::DownloadEvent::MediaInserted:
        // FIXME ;;; what to do?
        emit m_downloadController->downloadMessage(
                downloadProxy,
                "Media Inserted");
        return;

    case WRT::DownloadEvent::RedirectedPermanently:
        // FIXME ;;; what to do?
        emit m_downloadController->downloadMessage(
                downloadProxy,
                "Redirected Permanently");
        return;

    case WRT::DownloadEvent::RedirectedTemporarily:
        // FIXME ;;; what to do?
        emit m_downloadController->downloadMessage(
                downloadProxy,
                "Redirected Temporarily");
        return;

    case WRT::DownloadEvent::NameChanged:
        // FIXME ;;; what to do?
        emit m_downloadController->downloadMessage(
                downloadProxy,
                "Name Changed");
        return;

    case WRT::DownloadEvent::ContentTypeChanged:
        // FIXME ;;; what to do?
        emit m_downloadController->downloadMessage(
                downloadProxy,
                "Type Changed");
        return;

    case WRT::DownloadEvent::CreatingConnection:
        // FIXME ;;; what to do?
        emit m_downloadController->downloadMessage(
                downloadProxy,
                "Creating Connection");
        return;

    case WRT::DownloadEvent::ConnectionNeeded:
        // FIXME ;;; what to do?
        emit m_downloadController->downloadMessage(
                downloadProxy,
                "Connection Needed");
        return;

    case WRT::DownloadEvent::ConnectionDisconnected:
        // FIXME ;;; what to do?
        emit m_downloadController->downloadMessage(
                downloadProxy,
                "Connection Disconnected");
        return;

    case WRT::DownloadEvent::RightObjectsAcquired:
        // FIXME - Update to support OMA and DRM.
        return;

    default:
        qDebug() << "Unexpected download event:" << event->type();
        break;
    }
}

void DownloadControllerPrivate::handleDownloadError(Error error)
{
    // Expect the WRT::DownloadEvent::Error case in handleDownloadEvent()
    // above should handle anything likely to go through here.
}

// DownloadController implementation

DownloadController::DownloadController(
    const QString & client,
    const QNetworkProxy & proxy)
{
    d = new DownloadControllerPrivate(this, client, proxy);
}

DownloadController::~DownloadController()
{
    delete d;
}

void DownloadController::startDownload(const QUrl & url, const QFileInfo & info)
{
    qDebug() << "Download URL" << url;

    d->startDownload(url, info);
}

void DownloadController::startDownload(QNetworkReply * reply)
{
    QUrl url = reply->url();

    qDebug() << "Download unsupported content" << url;

    d->startDownload(reply);
}

void DownloadController::startDownload(const QNetworkRequest & request)
{
    QUrl url = request.url();

    qDebug() << "Save link or image" << url;

    d->startDownload(request);
}

#else // USE_DOWNLOAD_MANAGER

// Empty implementation for when DownloadManager is unsupported.

DownloadController::DownloadController(
    const QString & /*client*/,
    const QNetworkProxy & /*proxy*/)
{}

DownloadController::~DownloadController()
{}

void DownloadController::startDownload(const QUrl & url, const QFileInfo & info)
{
    Q_UNUSED(info)

    emit unsupportedDownload(url);
}

void DownloadController::startDownload(QNetworkReply * reply)
{
    QUrl url = reply->url();

    emit unsupportedDownload(url);
}

void DownloadController::startDownload(const QNetworkRequest & request)
{
    QUrl url = request.url();

    emit unsupportedDownload(url);
}

void DownloadControllerPrivate::handleDownloadManagerEvent(
        DownloadManagerEvent * event)
{
}

void DownloadControllerPrivate::handleDownloadEvent(
        DownloadEvent * event)
{
}

DownloadControllerPrivate::~DownloadControllerPrivate()
{}

#endif // USE_DOWNLOAD_MANAGER

bool DownloadController::handlePage(QWebPage * page)
{
    bool succeeded = true;

    // Handle click on link when the link type is not supported.
    page->setForwardUnsupportedContent(true);
    if (!connect(page, SIGNAL(unsupportedContent(QNetworkReply *)),
            this, SLOT(startDownload(QNetworkReply *)))) {
        succeeded = false;
    };

    // Handle Save Link and Save Image requests from the context menu.
    if (!connect(page, SIGNAL(downloadRequested(const QNetworkRequest &)),
            this, SLOT(startDownload(const QNetworkRequest &)))) {
        succeeded = false;
    }

    return succeeded;
}