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 "FileDownloadManager.h"
0023 #include "Imap/Model/FullMessageCombiner.h"
0024 #include "Imap/Model/ItemRoles.h"
0025 #include "Imap/Model/MailboxTree.h"
0026 
0027 #include <QDir>
0028 
0029 namespace Imap
0030 {
0031 
0032 namespace Network
0033 {
0034 
0035 FileDownloadManager::FileDownloadManager(QObject *parent, Imap::Network::MsgPartNetAccessManager *manager, const QModelIndex &partIndex):
0036     QObject(parent), manager(manager), partIndex(partIndex), reply(0), saved(false)
0037 {
0038 }
0039 
0040 FileDownloadManager::FileDownloadManager(QObject *parent, Imap::Network::MsgPartNetAccessManager *manager, const QUrl &url, const QModelIndex &relativeRoot):
0041     QObject(parent), manager(manager), partIndex(QModelIndex()), reply(0), saved(false)
0042 {
0043     if (url.scheme().toLower() == QLatin1String("cid")) {
0044         if (!relativeRoot.isValid()) {
0045             m_errorMessage = tr("Cannot translate CID URL: message is gone");
0046             return;
0047         }
0048         auto cid = url.path().toUtf8();
0049         if (!cid.startsWith('<')) {
0050             cid = '<' + cid;
0051         }
0052         if (!cid.endsWith('>')) {
0053             cid += '>';
0054         }
0055         partIndex = manager->cidToPart(relativeRoot, cid);
0056         if (!partIndex.isValid()) {
0057             m_errorMessage = tr("CID not found (%1)").arg(url.toString());
0058         }
0059     } else {
0060         m_errorMessage = tr("Unsupported URL (%1)").arg(url.toString());
0061     }
0062 }
0063 
0064 QString FileDownloadManager::toRealFileName(const QModelIndex &index)
0065 {
0066     QString fileName = index.data(Imap::Mailbox::RolePartFileName).toString();
0067     if (!fileName.isEmpty())
0068         return fileName;
0069 
0070     QString uid = index.data(Imap::Mailbox::RoleMessageUid).toString();
0071     QString partId = index.data(Imap::Mailbox::RolePartId).toString();
0072     if (partId.isEmpty()) {
0073         // we're saving the complete message
0074         return tr("msg_%1.eml").arg(uid);
0075     } else {
0076         // it's a message part
0077         return tr("msg_%1_%2").arg(uid, partId);
0078     }
0079 }
0080 
0081 void FileDownloadManager::downloadPart()
0082 {
0083     if (!m_errorMessage.isNull()) {
0084         emit transferError(m_errorMessage);
0085         return;
0086     }
0087 
0088     if (!partIndex.isValid()) {
0089         emit transferError(tr("FileDownloadManager::downloadPart(): part has disappeared"));
0090         return;
0091     }
0092     QString saveFileName = toRealFileName(partIndex);
0093     emit fileNameRequested(&saveFileName);
0094     if (saveFileName.isEmpty()) {
0095         emit cancelled();
0096         return;
0097     }
0098 
0099     saving.setFileName(saveFileName);
0100     saved = false;
0101 
0102     QNetworkRequest request;
0103     QUrl url;
0104     url.setScheme(QStringLiteral("trojita-imap"));
0105     url.setHost(QStringLiteral("msg"));
0106     url.setPath(partIndex.data(Imap::Mailbox::RolePartPathToPart).toString());
0107     request.setUrl(url);
0108     reply = manager->get(request);
0109     connect(reply, &QNetworkReply::finished, this, &FileDownloadManager::onPartDataTransfered);
0110     connect(reply, static_cast<void (QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::errorOccurred),
0111             this, &FileDownloadManager::onReplyTransferError);
0112     connect(manager, &QNetworkAccessManager::finished, this, &FileDownloadManager::deleteReply);
0113 }
0114 
0115 void FileDownloadManager::downloadMessage()
0116 {
0117     if (!partIndex.isValid()) {
0118         emit transferError(tr("FileDownloadManager::downloadMessage(): message has disappeared"));
0119         return;
0120     }
0121     QString saveFileName = toRealFileName(partIndex);
0122     Q_ASSERT(!m_combiner);
0123     m_combiner = new Imap::Mailbox::FullMessageCombiner(partIndex, this);
0124 
0125     emit fileNameRequested(&saveFileName);
0126     if (saveFileName.isEmpty()) {
0127         emit cancelled();
0128         return;
0129     }
0130 
0131     saving.setFileName(saveFileName);
0132     saved = false;
0133     connect(m_combiner.data(), &Mailbox::FullMessageCombiner::completed, this, &FileDownloadManager::onMessageDataTransferred);
0134     connect(m_combiner.data(), &Mailbox::FullMessageCombiner::failed, this, &FileDownloadManager::onCombinerTransferError);
0135     connect(m_combiner.data(), &Mailbox::FullMessageCombiner::completed, m_combiner.data(), &QObject::deleteLater, Qt::QueuedConnection);
0136     connect(m_combiner.data(), &Mailbox::FullMessageCombiner::failed, m_combiner.data(), &QObject::deleteLater, Qt::QueuedConnection);
0137     m_combiner->load();
0138 }
0139 
0140 void FileDownloadManager::onPartDataTransfered()
0141 {
0142     if (!reply) {
0143         return;
0144     }
0145     if (reply->error() == QNetworkReply::NoError) {
0146         if (!saving.open(QIODevice::WriteOnly)) {
0147             emit transferError(saving.errorString());
0148             return;
0149         }
0150         if (saving.write(reply->readAll()) == -1) {
0151             emit transferError(saving.errorString());
0152             return;
0153         }
0154         if (!saving.flush()) {
0155             emit transferError(saving.errorString());
0156             return;
0157         }
0158         saving.close();
0159         saved = true;
0160         emit succeeded();
0161     }
0162 }
0163 
0164 void FileDownloadManager::onMessageDataTransferred()
0165 {
0166     Q_ASSERT(m_combiner);
0167     if (!saving.open(QIODevice::WriteOnly)) {
0168         emit transferError(saving.errorString());
0169         return;
0170     }
0171     if (saving.write(m_combiner->data().data()) == -1) {
0172         emit transferError(saving.errorString());
0173         return;
0174     }
0175     if (!saving.flush()) {
0176         emit transferError(saving.errorString());
0177         return;
0178     }
0179     saving.close();
0180     saved = true;
0181     emit succeeded();
0182 }
0183 
0184 void FileDownloadManager::onReplyTransferError()
0185 {
0186     Q_ASSERT(reply);
0187     emit transferError(reply->errorString());
0188 }
0189 
0190 void FileDownloadManager::onCombinerTransferError(const QString &message)
0191 {
0192     emit transferError(message);
0193 }
0194 
0195 void FileDownloadManager::deleteReply(QNetworkReply *reply)
0196 {
0197     if (reply && reply == this->reply) {
0198         if (!saved)
0199             onPartDataTransfered();
0200         reply->deleteLater();
0201         this->reply = nullptr;
0202     }
0203 }
0204 
0205 }
0206 }