File indexing completed on 2024-06-23 05:21:13

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 
0023 #include <algorithm>
0024 #include "FetchMsgPartTask.h"
0025 #include "Imap/Model/ItemRoles.h"
0026 #include "Imap/Model/Model.h"
0027 #include "Imap/Model/MailboxTree.h"
0028 #include "KeepMailboxOpenTask.h"
0029 
0030 namespace Imap
0031 {
0032 namespace Mailbox
0033 {
0034 
0035 FetchMsgPartTask::FetchMsgPartTask(Model *model, const QModelIndex &mailbox, const Uids &uids, const QList<QByteArray> &parts):
0036     ImapTask(model), uids(uids), parts(parts), mailboxIndex(mailbox)
0037 {
0038     Q_ASSERT(!uids.isEmpty());
0039     conn = model->findTaskResponsibleFor(mailboxIndex);
0040     conn->addDependentTask(this);
0041     connect(this, &ImapTask::completed, this, &FetchMsgPartTask::markPendingItemsUnavailable);
0042     connect(this, &ImapTask::failed, this, &FetchMsgPartTask::markPendingItemsUnavailable);
0043 }
0044 
0045 void FetchMsgPartTask::perform()
0046 {
0047     parser = conn->parser;
0048     markAsActiveTask();
0049 
0050     IMAP_TASK_CHECK_ABORT_DIE;
0051 
0052     Sequence seq = Sequence::fromVector(uids);
0053     tag = parser->uidFetch(seq, parts);
0054 }
0055 
0056 bool FetchMsgPartTask::handleFetch(const Imap::Responses::Fetch *const resp)
0057 {
0058     if (!mailboxIndex.isValid()) {
0059         _failed(tr("Mailbox disappeared"));
0060         return false;
0061     }
0062 
0063     TreeItemMailbox *mailbox = dynamic_cast<TreeItemMailbox *>(static_cast<TreeItem *>(mailboxIndex.internalPointer()));
0064     Q_ASSERT(mailbox);
0065     model->genericHandleFetch(mailbox, resp);
0066     return true;
0067 }
0068 
0069 bool FetchMsgPartTask::handleStateHelper(const Imap::Responses::State *const resp)
0070 {
0071     if (resp->tag.isEmpty())
0072         return false;
0073 
0074     if (!mailboxIndex.isValid()) {
0075         _failed(tr("Mailbox disappeared"));
0076         return false;
0077     }
0078 
0079     if (resp->tag == tag) {
0080         if (resp->respCode == Responses::UNKNOWN_CTE) {
0081             doForAllParts([this](TreeItemPart *part, const QByteArray &partId, const uint uid) {
0082                 handleUnknownCTE(part, partId, uid);
0083             });
0084             // clean up so that these won't get marked UNAVAILABLE
0085             uids.clear();
0086             parts.clear();
0087             _failed(QStringLiteral("BINARY fetch failed: server reported [UNKNOWN-CTE]"));
0088         } else if (resp->kind == Responses::OK) {
0089             log(QStringLiteral("Fetched parts"), Common::LOG_MESSAGES);
0090             model->changeConnectionState(parser, CONN_STATE_SELECTED);
0091             _completed();
0092         } else {
0093             _failed(QStringLiteral("Part fetch failed"));
0094         }
0095         return true;
0096     } else {
0097         return false;
0098     }
0099 }
0100 
0101 QString FetchMsgPartTask::debugIdentification() const
0102 {
0103     if (!mailboxIndex.isValid())
0104         return QStringLiteral("[invalid mailbox]");
0105 
0106     if (uids.isEmpty())
0107         return QStringLiteral("[no items to fetch]");
0108 
0109     Q_ASSERT(!uids.isEmpty());
0110     QStringList buf;
0111     std::transform(parts.begin(), parts.end(), std::back_inserter(buf),
0112                    [](const QByteArray &x) {return QString::fromUtf8(x);}
0113                    );
0114     return QStringLiteral("%1: parts %2 for UIDs %3")
0115            .arg(mailboxIndex.data(RoleMailboxName).toString(), buf.join(QStringLiteral(", ")),
0116                 QString::fromUtf8(Sequence::fromVector(uids).toByteArray()));
0117 }
0118 
0119 QVariant FetchMsgPartTask::taskData(const int role) const
0120 {
0121     return role == RoleTaskCompactName ? QVariant(tr("Downloading messages")) : QVariant();
0122 }
0123 
0124 void FetchMsgPartTask::doForAllParts(const std::function<void(TreeItemPart *, const QByteArray &, const uint)> &f)
0125 {
0126     if (!mailboxIndex.isValid())
0127         return;
0128 
0129     Q_ASSERT(model);
0130     TreeItemMailbox *mailbox = dynamic_cast<TreeItemMailbox *>(static_cast<TreeItem *>(mailboxIndex.internalPointer()));
0131     Q_ASSERT(mailbox);
0132     const auto messages = model->findMessagesByUids(mailbox, uids);
0133     for(auto message: messages) {
0134         for (const auto &partId: parts) {
0135             auto part = mailbox->partIdToPtr(model, static_cast<TreeItemMessage *>(message), partId);
0136             f(part, partId, message->uid());
0137         }
0138     }
0139 }
0140 
0141 /** @short We're dead, the data which hasn't arrived so far won't arrive in future unless a reset happens */
0142 void FetchMsgPartTask::markPendingItemsUnavailable()
0143 {
0144     doForAllParts([this](TreeItemPart *part, const QByteArray &partId, const uint uid) {
0145         if (!part) {
0146             log(QStringLiteral("FETCH: Cannot find part %1 for UID %2 in the tree")
0147                 .arg(QString::fromUtf8(partId), QString::number(uid)), Common::LOG_MESSAGES);
0148             return;
0149         }
0150         if (part->loading()) {
0151             log(QStringLiteral("Received no data for part %1 UID %2").arg(QString::fromUtf8(partId), QString::number(uid)),
0152                 Common::LOG_MESSAGES);
0153             markPartUnavailable(part);
0154         } else {
0155             log(QStringLiteral("Fetched part %1 for UID %2").arg(QString::fromUtf8(partId), QString::number(uid)),
0156                 Common::LOG_MESSAGES);
0157         }
0158     });
0159 }
0160 
0161 /** @short Give up fetching attempts for this part */
0162 void FetchMsgPartTask::markPartUnavailable(TreeItemPart *part)
0163 {
0164     part->setFetchStatus(TreeItem::UNAVAILABLE);
0165     QModelIndex idx = part->toIndex(model);
0166     emit model->dataChanged(idx, idx);
0167 }
0168 
0169 void FetchMsgPartTask::handleUnknownCTE(TreeItemPart *part, const QByteArray &partId, const uint uid)
0170 {
0171     if (!part) {
0172         log(QStringLiteral("FETCH: Cannot find part %1 of UID %2 in the tree")
0173             .arg(QString::fromUtf8(partId), QString::number(uid)), Common::LOG_MESSAGES);
0174         return;
0175     }
0176     if (part->fetched()) {
0177         log(QStringLiteral("Fetched part %1 of for UID %2").arg(QString::fromUtf8(partId), QString::number(uid)),
0178             Common::LOG_MESSAGES);
0179         return;
0180     }
0181     if (part->m_binaryCTEFailed) {
0182         // This is nasty -- how come that we issued something which ended up in an [UNKNOWN-CTE] for a second time?
0183         // Better prevent infinite loops here.
0184         log(QStringLiteral("FETCH BINARY: retry apparently also failed for part %1 UID %2")
0185             .arg(QString::fromUtf8(partId), QString::number(uid)));
0186         markPartUnavailable(part);
0187         return;
0188     } else {
0189         log(QStringLiteral("FETCH BINARY: got UNKNOWN-CTE for part %1 of UID %2, will fetch using the old-school way")
0190             .arg(QString::fromUtf8(partId), QString::number(uid)));
0191         part->m_binaryCTEFailed = true;
0192         model->askForMsgPart(part);
0193     }
0194 }
0195 
0196 }
0197 }