File indexing completed on 2024-05-12 05:17:14
0001 /* 0002 SPDX-FileCopyrightText: 2009 Kevin Ottens <ervin@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "listjob.h" 0008 0009 #include <KLocalizedString> 0010 #include <QTimer> 0011 0012 #include "job_p.h" 0013 #include "response_p.h" 0014 #include "rfccodecs.h" 0015 #include "session_p.h" 0016 0017 namespace KIMAP 0018 { 0019 class ListJobPrivate : public JobPrivate 0020 { 0021 public: 0022 ListJobPrivate(ListJob *job, Session *session, const QString &name) 0023 : JobPrivate(session, name) 0024 , q(job) 0025 , option(ListJob::NoOption) 0026 { 0027 } 0028 ~ListJobPrivate() 0029 { 0030 } 0031 0032 void emitPendings() 0033 { 0034 if (pendingDescriptors.isEmpty()) { 0035 return; 0036 } 0037 0038 Q_EMIT q->mailBoxesReceived(pendingDescriptors, pendingFlags); 0039 0040 pendingDescriptors.clear(); 0041 pendingFlags.clear(); 0042 } 0043 0044 ListJob *const q; 0045 0046 ListJob::Option option; 0047 QList<MailBoxDescriptor> namespaces; 0048 QByteArray command; 0049 0050 QTimer emitPendingsTimer; 0051 QList<MailBoxDescriptor> pendingDescriptors; 0052 QList<QList<QByteArray>> pendingFlags; 0053 }; 0054 } 0055 0056 using namespace KIMAP; 0057 0058 ListJob::ListJob(Session *session) 0059 : Job(*new ListJobPrivate(this, session, i18n("List"))) 0060 { 0061 Q_D(ListJob); 0062 connect(&d->emitPendingsTimer, &QTimer::timeout, this, [d]() { 0063 d->emitPendings(); 0064 }); 0065 } 0066 0067 ListJob::~ListJob() 0068 { 0069 } 0070 0071 void ListJob::setIncludeUnsubscribed(bool include) 0072 { 0073 Q_D(ListJob); 0074 if (include) { 0075 d->option = ListJob::IncludeUnsubscribed; 0076 } else { 0077 d->option = ListJob::NoOption; 0078 } 0079 } 0080 0081 bool ListJob::isIncludeUnsubscribed() const 0082 { 0083 Q_D(const ListJob); 0084 return (d->option == ListJob::IncludeUnsubscribed); 0085 } 0086 0087 void ListJob::setOption(Option option) 0088 { 0089 Q_D(ListJob); 0090 d->option = option; 0091 } 0092 0093 ListJob::Option ListJob::option() const 0094 { 0095 Q_D(const ListJob); 0096 return d->option; 0097 } 0098 0099 void ListJob::setQueriedNamespaces(const QList<MailBoxDescriptor> &namespaces) 0100 { 0101 Q_D(ListJob); 0102 d->namespaces = namespaces; 0103 } 0104 0105 QList<MailBoxDescriptor> ListJob::queriedNamespaces() const 0106 { 0107 Q_D(const ListJob); 0108 return d->namespaces; 0109 } 0110 0111 QList<MailBoxDescriptor> ListJob::mailBoxes() const 0112 { 0113 return QList<MailBoxDescriptor>(); 0114 } 0115 0116 QMap<MailBoxDescriptor, QList<QByteArray>> ListJob::flags() const 0117 { 0118 return QMap<MailBoxDescriptor, QList<QByteArray>>(); 0119 } 0120 0121 void ListJob::doStart() 0122 { 0123 Q_D(ListJob); 0124 0125 switch (d->option) { 0126 case IncludeUnsubscribed: 0127 d->command = "LIST"; 0128 break; 0129 case IncludeFolderRoleFlags: 0130 d->command = "XLIST"; 0131 break; 0132 case NoOption: 0133 d->command = "LSUB"; 0134 } 0135 0136 d->emitPendingsTimer.start(100); 0137 0138 if (d->namespaces.isEmpty()) { 0139 d->tags << d->sessionInternal()->sendCommand(d->command, "\"\" *"); 0140 } else { 0141 for (const MailBoxDescriptor &descriptor : std::as_const(d->namespaces)) { 0142 QString parameters = QStringLiteral("\"\" \"%1\""); 0143 0144 if (descriptor.name.endsWith(descriptor.separator)) { 0145 QString name = encodeImapFolderName(descriptor.name); 0146 name.chop(1); 0147 d->tags << d->sessionInternal()->sendCommand(d->command, parameters.arg(name).toUtf8()); 0148 } 0149 0150 d->tags << d->sessionInternal()->sendCommand(d->command, parameters.arg(descriptor.name + QLatin1Char('*')).toUtf8()); 0151 } 0152 } 0153 } 0154 0155 void ListJob::handleResponse(const Response &response) 0156 { 0157 Q_D(ListJob); 0158 0159 // We can predict it'll be handled by handleErrorReplies() so stop 0160 // the timer now so that result() will really be the last emitted signal. 0161 if (!response.content.isEmpty() && d->tags.size() == 1 && d->tags.contains(response.content.first().toString())) { 0162 d->emitPendingsTimer.stop(); 0163 d->emitPendings(); 0164 } 0165 0166 if (handleErrorReplies(response) == NotHandled) { 0167 if (response.content.size() >= 5 && response.content[1].toString() == d->command) { 0168 QList<QByteArray> flags = response.content[2].toList(); 0169 for (QList<QByteArray>::iterator it = flags.begin(), itEnd = flags.end(); it != itEnd; ++it) { 0170 *it = it->toLower(); 0171 } 0172 QByteArray separator = response.content[3].toString(); 0173 if (separator.isEmpty()) { 0174 // Defaults to / for servers reporting an empty list 0175 // it's supposedly not a problem as servers doing that 0176 // only do it for mailboxes with no child. 0177 separator = "/"; // krazy:exclude=doublequote_chars since a QByteArray 0178 } 0179 Q_ASSERT(separator.size() == 1); 0180 QByteArray fullName; 0181 for (int i = 4; i < response.content.size(); i++) { 0182 fullName += response.content[i].toString() + ' '; 0183 } 0184 fullName.chop(1); 0185 0186 fullName = decodeImapFolderName(fullName); 0187 0188 MailBoxDescriptor mailBoxDescriptor; 0189 mailBoxDescriptor.separator = QLatin1Char(separator[0]); 0190 mailBoxDescriptor.name = QString::fromUtf8(fullName); 0191 convertInboxName(mailBoxDescriptor); 0192 0193 d->pendingDescriptors << mailBoxDescriptor; 0194 d->pendingFlags << flags; 0195 } 0196 } 0197 } 0198 0199 void ListJob::convertInboxName(KIMAP::MailBoxDescriptor &descriptor) 0200 { 0201 // Inbox must be case sensitive, according to the RFC, so make it always uppercase 0202 QStringList pathParts = descriptor.name.split(descriptor.separator); 0203 if (!pathParts.isEmpty() && pathParts[0].compare(QLatin1StringView("INBOX"), Qt::CaseInsensitive) == 0) { 0204 pathParts.removeAt(0); 0205 descriptor.name = QStringLiteral("INBOX"); 0206 if (!pathParts.isEmpty()) { 0207 descriptor.name += descriptor.separator + pathParts.join(descriptor.separator); 0208 } 0209 } 0210 } 0211 #include "moc_listjob.cpp"