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"