src/network/access/qnetworkaccessfilebackend.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtNetwork module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "qnetworkaccessfilebackend_p.h"
       
    43 #include "qfileinfo.h"
       
    44 #include "qurlinfo.h"
       
    45 #include "qdir.h"
       
    46 #include "private/qnoncontiguousbytedevice_p.h"
       
    47 
       
    48 #include <QtCore/QCoreApplication>
       
    49 
       
    50 QT_BEGIN_NAMESPACE
       
    51 
       
    52 QNetworkAccessBackend *
       
    53 QNetworkAccessFileBackendFactory::create(QNetworkAccessManager::Operation op,
       
    54                                          const QNetworkRequest &request) const
       
    55 {
       
    56     // is it an operation we know of?
       
    57     switch (op) {
       
    58     case QNetworkAccessManager::GetOperation:
       
    59     case QNetworkAccessManager::PutOperation:
       
    60         break;
       
    61 
       
    62     default:
       
    63         // no, we can't handle this operation
       
    64         return 0;
       
    65     }
       
    66 
       
    67     QUrl url = request.url();
       
    68     if (url.scheme() == QLatin1String("qrc") || !url.toLocalFile().isEmpty())
       
    69         return new QNetworkAccessFileBackend;
       
    70     else if (!url.isEmpty() && url.authority().isEmpty()) {
       
    71         // check if QFile could, in theory, open this URL
       
    72         QFileInfo fi(url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery));
       
    73         if (fi.exists() || (op == QNetworkAccessManager::PutOperation && fi.dir().exists()))
       
    74             return new QNetworkAccessFileBackend;
       
    75     }
       
    76 
       
    77     return 0;
       
    78 }
       
    79 
       
    80 QNetworkAccessFileBackend::QNetworkAccessFileBackend()
       
    81     : uploadByteDevice(0), totalBytes(0), hasUploadFinished(false)
       
    82 {
       
    83 }
       
    84 
       
    85 QNetworkAccessFileBackend::~QNetworkAccessFileBackend()
       
    86 {
       
    87 }
       
    88 
       
    89 void QNetworkAccessFileBackend::open()
       
    90 {
       
    91     QUrl url = this->url();
       
    92 
       
    93     if (url.host() == QLatin1String("localhost"))
       
    94         url.setHost(QString());
       
    95 #if !defined(Q_OS_WIN)
       
    96     // do not allow UNC paths on Unix
       
    97     if (!url.host().isEmpty()) {
       
    98         // we handle only local files
       
    99         error(QNetworkReply::ProtocolInvalidOperationError,
       
   100               QCoreApplication::translate("QNetworkAccessFileBackend", "Request for opening non-local file %1").arg(url.toString()));
       
   101         finished();
       
   102         return;
       
   103     }
       
   104 #endif // !defined(Q_OS_WIN)
       
   105     if (url.path().isEmpty())
       
   106         url.setPath(QLatin1String("/"));
       
   107     setUrl(url);
       
   108 
       
   109     QString fileName = url.toLocalFile();
       
   110     if (fileName.isEmpty()) {
       
   111         if (url.scheme() == QLatin1String("qrc"))
       
   112             fileName = QLatin1Char(':') + url.path();
       
   113         else
       
   114             fileName = url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery);
       
   115     }
       
   116     file.setFileName(fileName);
       
   117 
       
   118     if (operation() == QNetworkAccessManager::GetOperation) {
       
   119         if (!loadFileInfo())
       
   120             return;
       
   121     }
       
   122 
       
   123     QIODevice::OpenMode mode;
       
   124     switch (operation()) {
       
   125     case QNetworkAccessManager::GetOperation:
       
   126         mode = QIODevice::ReadOnly;
       
   127         break;
       
   128     case QNetworkAccessManager::PutOperation:
       
   129         mode = QIODevice::WriteOnly | QIODevice::Truncate;
       
   130         uploadByteDevice = createUploadByteDevice();
       
   131         QObject::connect(uploadByteDevice, SIGNAL(readyRead()), this, SLOT(uploadReadyReadSlot()));
       
   132         QMetaObject::invokeMethod(this, "uploadReadyReadSlot", Qt::QueuedConnection);
       
   133         break;
       
   134     default:
       
   135         Q_ASSERT_X(false, "QNetworkAccessFileBackend::open",
       
   136                    "Got a request operation I cannot handle!!");
       
   137         return;
       
   138     }
       
   139 
       
   140     mode |= QIODevice::Unbuffered;
       
   141     bool opened = file.open(mode);
       
   142 
       
   143     // could we open the file?
       
   144     if (!opened) {
       
   145         QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Error opening %1: %2")
       
   146                                                 .arg(this->url().toString(), file.errorString());
       
   147 
       
   148         // why couldn't we open the file?
       
   149         // if we're opening for reading, either it doesn't exist, or it's access denied
       
   150         // if we're opening for writing, not existing means it's access denied too
       
   151         if (file.exists() || operation() == QNetworkAccessManager::PutOperation)
       
   152             error(QNetworkReply::ContentAccessDenied, msg);
       
   153         else
       
   154             error(QNetworkReply::ContentNotFoundError, msg);
       
   155         finished();
       
   156     }
       
   157 }
       
   158 
       
   159 void QNetworkAccessFileBackend::uploadReadyReadSlot()
       
   160 {
       
   161     if (hasUploadFinished)
       
   162         return;
       
   163 
       
   164     forever {
       
   165         qint64 haveRead;
       
   166         const char *readPointer = uploadByteDevice->readPointer(-1, haveRead);
       
   167         if (haveRead == -1) {
       
   168             // EOF
       
   169             hasUploadFinished = true;
       
   170             file.flush();
       
   171             file.close();
       
   172             finished();
       
   173             break;
       
   174         } else if (haveRead == 0 || readPointer == 0) {
       
   175             // nothing to read right now, we will be called again later
       
   176             break;
       
   177         } else {
       
   178             qint64 haveWritten;
       
   179             haveWritten = file.write(readPointer, haveRead);
       
   180 
       
   181             if (haveWritten < 0) {
       
   182                 // write error!
       
   183                 QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Write error writing to %1: %2")
       
   184                               .arg(url().toString(), file.errorString());
       
   185                 error(QNetworkReply::ProtocolFailure, msg);
       
   186 
       
   187                 finished();
       
   188                 return;
       
   189             } else {
       
   190                 uploadByteDevice->advanceReadPointer(haveWritten);
       
   191             }
       
   192 
       
   193 
       
   194             file.flush();
       
   195         }
       
   196     }
       
   197 }
       
   198 
       
   199 void QNetworkAccessFileBackend::closeDownstreamChannel()
       
   200 {
       
   201     if (operation() == QNetworkAccessManager::GetOperation) {
       
   202         file.close();
       
   203     }
       
   204 }
       
   205 
       
   206 bool QNetworkAccessFileBackend::waitForDownstreamReadyRead(int)
       
   207 {
       
   208     Q_ASSERT(operation() == QNetworkAccessManager::GetOperation);
       
   209     return readMoreFromFile();
       
   210 }
       
   211 
       
   212 void QNetworkAccessFileBackend::downstreamReadyWrite()
       
   213 {
       
   214     Q_ASSERT_X(operation() == QNetworkAccessManager::GetOperation, "QNetworkAccessFileBackend",
       
   215                "We're being told to download data but operation isn't GET!");
       
   216 
       
   217     readMoreFromFile();
       
   218 }
       
   219 
       
   220 bool QNetworkAccessFileBackend::loadFileInfo()
       
   221 {
       
   222     QFileInfo fi(file);
       
   223     setHeader(QNetworkRequest::LastModifiedHeader, fi.lastModified());
       
   224     setHeader(QNetworkRequest::ContentLengthHeader, fi.size());
       
   225 
       
   226     // signal we're open
       
   227     metaDataChanged();
       
   228 
       
   229     if (fi.isDir()) {
       
   230         error(QNetworkReply::ContentOperationNotPermittedError,
       
   231               QCoreApplication::translate("QNetworkAccessFileBackend", "Cannot open %1: Path is a directory").arg(url().toString()));
       
   232         finished();
       
   233         return false;
       
   234     }
       
   235 
       
   236     return true;
       
   237 }
       
   238 
       
   239 bool QNetworkAccessFileBackend::readMoreFromFile()
       
   240 {
       
   241     qint64 wantToRead;
       
   242     while ((wantToRead = nextDownstreamBlockSize()) > 0) {
       
   243         // ### FIXME!!
       
   244         // Obtain a pointer from the ringbuffer!
       
   245         // Avoid extra copy
       
   246         QByteArray data;
       
   247         data.reserve(wantToRead);
       
   248         qint64 actuallyRead = file.read(data.data(), wantToRead);
       
   249         if (actuallyRead <= 0) {
       
   250             // EOF or error
       
   251             if (file.error() != QFile::NoError) {
       
   252                 QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Read error reading from %1: %2")
       
   253                               .arg(url().toString(), file.errorString());
       
   254                 error(QNetworkReply::ProtocolFailure, msg);
       
   255 
       
   256                 finished();
       
   257                 return false;
       
   258             }
       
   259 
       
   260             finished();
       
   261             return true;
       
   262         }
       
   263 
       
   264         data.resize(actuallyRead);
       
   265         totalBytes += actuallyRead;
       
   266 
       
   267         QByteDataBuffer list;
       
   268         list.append(data);
       
   269         data.clear(); // important because of implicit sharing!
       
   270         writeDownstreamData(list);
       
   271     }
       
   272     return true;
       
   273 }
       
   274 
       
   275 QT_END_NAMESPACE