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 }