File indexing completed on 2024-11-24 04:53:15
0001 /* Copyright (C) 2006 - 2014 Jan Kundrát <jkt@flaska.net> 0002 0003 This file is part of the Trojita Qt IMAP e-mail client, 0004 http://trojita.flaska.net/ 0005 0006 This program is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU General Public License as 0008 published by the Free Software Foundation; either version 2 of 0009 the License or (at your option) version 3 or any later version 0010 accepted by the membership of KDE e.V. (or its successor approved 0011 by the membership of KDE e.V.), which shall act as a proxy 0012 defined in Section 14 of version 3 of the license. 0013 0014 This program is distributed in the hope that it will be useful, 0015 but WITHOUT ANY WARRANTY; without even the implied warranty of 0016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0017 GNU General Public License for more details. 0018 0019 You should have received a copy of the GNU General Public License 0020 along with this program. If not, see <http://www.gnu.org/licenses/>. 0021 */ 0022 #include <QNetworkRequest> 0023 #include <QStringList> 0024 #include <QDebug> 0025 0026 #include "MsgPartNetAccessManager.h" 0027 #include "ForbiddenReply.h" 0028 #include "MsgPartNetworkReply.h" 0029 #include "Imap/Model/ItemRoles.h" 0030 #include "Imap/Model/MailboxTree.h" 0031 #include "QQuickNetworkReplyWrapper.h" 0032 0033 namespace Imap 0034 { 0035 0036 /** @short Classes associated with the implementation of the QNetworkAccessManager */ 0037 namespace Network 0038 { 0039 0040 MsgPartNetAccessManager::MsgPartNetAccessManager(QObject *parent): 0041 QNetworkAccessManager(parent), externalsEnabled(false) 0042 { 0043 // The "image/pjpeg" nonsense is non-standard kludge produced by Micorosft Internet Explorer 0044 // (http://msdn.microsoft.com/en-us/library/ms775147(VS.85).aspx#_replace). As of May 2011, it is not listed in 0045 // the official list of assigned MIME types (http://www.iana.org/assignments/media-types/image/index.html), but generated 0046 // by MSIE nonetheless. Users of e-mail can see it for example in messages produced by webmails which do not check the 0047 // client-provided MIME types. QWebView would (arguably correctly) refuse to display such a blob, but the damned users 0048 // typically want to see their images (I certainly do), even though they are not standards-compliant. Hence we fix the 0049 // header here. 0050 registerMimeTypeTranslation(QStringLiteral("image/pjpeg"), QStringLiteral("image/jpeg")); 0051 } 0052 0053 void MsgPartNetAccessManager::setModelMessage(const QModelIndex &message_) 0054 { 0055 message = message_; 0056 } 0057 0058 /** @short Prepare a network request 0059 0060 This function handles delegating access to the other body parts using various schemes (ie. the special trojita-imap:// one used 0061 by Trojita for internal purposes and the cid: one for referencing to other body parts). Policy checks for filtering access to 0062 the public Internet are also performed at this level. 0063 */ 0064 QNetworkReply *MsgPartNetAccessManager::createRequest(Operation op, const QNetworkRequest &req, QIODevice *outgoingData) 0065 { 0066 Q_UNUSED(op); 0067 Q_UNUSED(outgoingData); 0068 0069 if (!message.isValid()) { 0070 // Our message got removed in the meanwhile 0071 // FIXME: add a better class here 0072 return new Imap::Network::ForbiddenReply(this); 0073 } 0074 0075 Q_ASSERT(message.isValid()); 0076 0077 if (req.url().scheme() == QLatin1String("trojita-imap") && req.url().host() == QLatin1String("msg")) { 0078 // Internal Trojita reference 0079 QModelIndex partIndex = pathToPart(message, req.url().path().toUtf8()); 0080 if (partIndex.isValid()) { 0081 return new Imap::Network::MsgPartNetworkReply(this, partIndex); 0082 } else { 0083 qDebug() << "No such part:" << req.url(); 0084 return new Imap::Network::ForbiddenReply(this); 0085 } 0086 } else if (req.url().scheme() == QLatin1String("cid")) { 0087 // The cid: scheme for cross-part references 0088 QByteArray cid = req.url().path().toUtf8(); 0089 if (!cid.startsWith("<")) 0090 cid = QByteArray("<") + cid; 0091 if (!cid.endsWith(">")) 0092 cid += ">"; 0093 QModelIndex target = cidToPart(message, cid); 0094 if (target.isValid()) { 0095 return new Imap::Network::MsgPartNetworkReply(this, target); 0096 } else { 0097 qDebug() << "Content-ID not found" << cid; 0098 return new Imap::Network::ForbiddenReply(this); 0099 } 0100 } else if (req.url() == QUrl(QStringLiteral("about:blank"))) { 0101 // about:blank is a relatively harmless URL which is used for opening an empty page 0102 return QNetworkAccessManager::createRequest(op, req, outgoingData); 0103 } else if (req.url().scheme() == QLatin1String("data")) { 0104 // data: scheme shall be safe, it's just a method of local access after all 0105 return QNetworkAccessManager::createRequest(op, req, outgoingData); 0106 } else { 0107 // Regular access -- we've got to check policy here 0108 if (req.url().scheme() == QLatin1String("http") || req.url().scheme() == QLatin1String("https")) { 0109 if (externalsEnabled) { 0110 return QNetworkAccessManager::createRequest(op, req, outgoingData); 0111 } else { 0112 emit requestingExternal(req.url()); 0113 return new Imap::Network::ForbiddenReply(this); 0114 } 0115 } else { 0116 qDebug() << "Forbidden per policy:" << req.url(); 0117 return new Imap::Network::ForbiddenReply(this); 0118 } 0119 } 0120 } 0121 0122 /** @short Find a message body part through its slash-separated string path */ 0123 QModelIndex MsgPartNetAccessManager::pathToPart(const QModelIndex &message, const QByteArray &path) 0124 { 0125 if (!path.startsWith("/")) 0126 return QModelIndex(); 0127 0128 QModelIndex target = message; 0129 QList<QByteArray> items = path.mid(1).split('/'); // mid(1) to get rid of the leading slash 0130 0131 for (QList<QByteArray>::const_iterator it = items.constBegin(); target.isValid() && it != items.constEnd(); ++it) { 0132 const QAbstractItemModel *model = target.model(); 0133 bool ok; 0134 int offset = it->toInt(&ok); 0135 if (ok) 0136 target = model->index(offset, 0, target); 0137 else if (*it == "HEADER") 0138 target = model->index(0, Imap::Mailbox::TreeItem::OFFSET_HEADER, target); 0139 else if (*it == "TEXT") 0140 target = model->index(0, Imap::Mailbox::TreeItem::OFFSET_TEXT, target); 0141 else if (*it == "MIME") 0142 target = model->index(0, Imap::Mailbox::TreeItem::OFFSET_MIME, target); 0143 else 0144 return QModelIndex(); 0145 } 0146 return target; 0147 } 0148 0149 /** @short Convert a CID-formatted specification of a MIME part to a QModelIndex 0150 0151 The MIME messages contain a scheme which can be used to provide a reference from one message part to another using the content id 0152 headers. This function walks the MIME tree and tries to find a MIME part whose ID matches the requested item. 0153 */ 0154 QModelIndex MsgPartNetAccessManager::cidToPart(const QModelIndex& rootIndex, const QByteArray &cid) 0155 { 0156 // A DFS search through the MIME parts tree of the current message which tries to check for a matching body part 0157 for (int i = 0; i < rootIndex.model()->rowCount(rootIndex); ++i) { 0158 QModelIndex partIndex = rootIndex.model()->index(i, 0, rootIndex); 0159 Q_ASSERT(partIndex.isValid()); 0160 if (partIndex.data(Imap::Mailbox::RolePartBodyFldId).toByteArray() == cid) 0161 return partIndex; 0162 partIndex = cidToPart(partIndex, cid); 0163 if (partIndex.isValid()) 0164 return partIndex; 0165 } 0166 return QModelIndex(); 0167 } 0168 0169 /** @short Enable/disable fetching of external items 0170 0171 The external items are anything on the Internet which is outside of the scope of the current message. At this time, we support 0172 fetching contents via HTTP and FTP protocols. 0173 */ 0174 void MsgPartNetAccessManager::setExternalsEnabled(bool enabled) 0175 { 0176 externalsEnabled = enabled; 0177 } 0178 0179 /** @short Look for registered translations of MIME types 0180 0181 Certain renderers (the QWebView, most notably) are rather picky about the content they can render. 0182 For example, a C++ header file's MIME type inherits from text/plain, but QWebView would still treat 0183 it as a file to download. The image/pjpeg "type" is another example. 0184 0185 This MIME type translation apparently has to happen at the QNetworkReply layer, so it makes sense to 0186 track the registered translations within the QNAM subclass. 0187 */ 0188 QString MsgPartNetAccessManager::translateToSupportedMimeType(const QString &originalMimeType) const 0189 { 0190 QMap<QString, QString>::const_iterator it = m_mimeTypeFixups.constFind(originalMimeType); 0191 return it == m_mimeTypeFixups.constEnd() ? originalMimeType : *it; 0192 } 0193 0194 /** @short Register a MIME type for an automatic translation to one that is recognized by the renderers */ 0195 void MsgPartNetAccessManager::registerMimeTypeTranslation(const QString &originalMimeType, const QString &translatedMimeType) 0196 { 0197 m_mimeTypeFixups[originalMimeType] = translatedMimeType; 0198 } 0199 0200 void MsgPartNetAccessManager::wrapQmlWebViewRequest(QObject *request, QObject *reply) 0201 { 0202 QNetworkRequest qnr(request->property("url").toUrl()); 0203 QNetworkReply *qnreply = get(qnr); 0204 new QQuickNetworkReplyWrapper(reply, qnreply); 0205 } 0206 } 0207 } 0208 0209