summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDon Sanders <don.sanders@nokia.com>2011-11-21 20:02:38 +1000
committerDon Sanders <don.sanders@nokia.com>2011-11-21 20:03:56 +1000
commit951cc9c9aeb05d419b892fc68d9afadff9d3699d (patch)
treed69e04e4f8829d61fc859828b4b31721f801a674
parentc7723e6587d7d1cbe7768411b3cca32d29f1eca8 (diff)
Implement support for XLIST IMAP extension2011W47
To automatically detect standard folders. Also set flags for messages in standard folders, and unset flags for messages previously in standard folders. Used in QMailRetrievalAction::retrieveFolderList.
-rw-r--r--src/plugins/messageservices/imap/imapclient.cpp93
-rw-r--r--src/plugins/messageservices/imap/imapprotocol.cpp49
-rw-r--r--src/plugins/messageservices/imap/imapstrategy.cpp28
-rw-r--r--src/plugins/messageservices/imap/imapstrategy.h2
4 files changed, 132 insertions, 40 deletions
diff --git a/src/plugins/messageservices/imap/imapclient.cpp b/src/plugins/messageservices/imap/imapclient.cpp
index 9aba6d40..a59c794f 100644
--- a/src/plugins/messageservices/imap/imapclient.cpp
+++ b/src/plugins/messageservices/imap/imapclient.cpp
@@ -216,7 +216,76 @@ namespace {
{
return decodeModUTF7(name);
}
+
+ struct FlagInfo
+ {
+ FlagInfo(QString flagName, quint64 flag, QMailFolder::StandardFolder standardFolder, quint64 messageFlag)
+ :_flagName(flagName), _flag(flag), _standardFolder(standardFolder), _messageFlag(messageFlag) {};
+
+ QString _flagName;
+ quint64 _flag;
+ QMailFolder::StandardFolder _standardFolder;
+ quint64 _messageFlag;
+ };
+
+ static void setFolderFlags(QMailAccount *account, QMailFolder *folder, const QString &flags)
+ {
+ // Set permitted flags
+ bool childCreationPermitted(!flags.contains("\\NoInferiors", Qt::CaseInsensitive));
+ bool messagesPermitted(!flags.contains("\\NoSelect", Qt::CaseInsensitive));
+ folder->setStatus(QMailFolder::ChildCreationPermitted, childCreationPermitted);
+ folder->setStatus(QMailFolder::MessagesPermitted, messagesPermitted);
+ if (!folder->id().isValid()) {
+ qWarning() << "setFolderFlags must be called on folder in store " << folder->id();
+ return;
+ }
+ // Set standard folder flags
+ QList<FlagInfo> flagInfoList;
+ flagInfoList << FlagInfo("\\Drafts", QMailFolder::Drafts, QMailFolder::DraftsFolder, QMailMessage::Draft)
+ << FlagInfo("\\Trash", QMailFolder::Trash, QMailFolder::TrashFolder, QMailMessage::Trash)
+ << FlagInfo("\\Sent", QMailFolder::Sent, QMailFolder::SentFolder, QMailMessage::Sent)
+ << FlagInfo("\\Spam", QMailFolder::Junk, QMailFolder::JunkFolder, QMailMessage::Junk);
+
+ for (int i = 0; i < flagInfoList.count(); ++i) {
+ QString flagName(flagInfoList[i]._flagName);
+ quint64 flag(flagInfoList[i]._flag);
+ QMailFolder::StandardFolder standardFolder(flagInfoList[i]._standardFolder);
+ quint64 messageFlag(flagInfoList[i]._messageFlag);
+ bool isFlagged(flags.contains(flagName, Qt::CaseInsensitive));
+
+ folder->setStatus(flag, isFlagged);
+ if (isFlagged) {
+ QMailFolderId oldFolderId = account->standardFolder(standardFolder);
+ if (oldFolderId.isValid() && (oldFolderId != folder->id())) {
+ QMailFolder oldFolder(oldFolderId);
+ oldFolder.setStatus(flag, false);
+ // Do the updates in the right order, so if there is a crash
+ // there will be a graceful recovery next time folders are list.
+ // It is expected that no disconnected move operations will be outstanding
+ // otherwise flags for those messages may be updated incorrectly.
+ // So call exportUpdates before retrieveFolderList
+ QMailMessageKey oldFolderKey(QMailMessageKey::parentFolderId(oldFolderId));
+ if (!QMailStore::instance()->updateMessagesMetaData(oldFolderKey, messageFlag, false)) {
+ qWarning() << "Unable to update messages in folder" << oldFolderId << "to remove flag" << flagName;
+ }
+ if (!QMailStore::instance()->updateFolder(&oldFolder)) {
+ qWarning() << "Unable to update folder" << oldFolderId << "to remove flag" << flagName;
+ }
+ }
+ if (!oldFolderId.isValid() || (oldFolderId != folder->id())) {
+ account->setStandardFolder(standardFolder, folder->id());
+ if (!QMailStore::instance()->updateAccount(account)) {
+ qWarning() << "Unable to update account" << account->id() << "to set flag" << flagName;
+ }
+ QMailMessageKey folderKey(QMailMessageKey::parentFolderId(folder->id()));
+ if (!QMailStore::instance()->updateMessagesMetaData(folderKey, messageFlag, true)) {
+ qWarning() << "Unable to update messages in folder" << folder->id() << "to set flag" << flagName;
+ }
+ }
+ }
+ }
+ }
}
class IdleProtocol : public ImapProtocol {
@@ -804,8 +873,6 @@ void ImapClient::mailboxListed(const QString &flags, const QString &path)
QStringList list = _protocol.flatHierarchy() ? QStringList(path) : path.split(_protocol.delimiter());
for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
- bool childCreationPermitted(!flags.contains("\\NoInferiors", Qt::CaseInsensitive));
- bool messagesPermitted(!flags.contains("\\NoSelect", Qt::CaseInsensitive));
if (!mailboxPath.isEmpty())
mailboxPath.append(_protocol.delimiter());
@@ -817,8 +884,7 @@ void ImapClient::mailboxListed(const QString &flags, const QString &path)
if (mailboxPath == path) {
QMailFolder folder(boxId);
QMailFolder folderOriginal(folder);
- folder.setStatus(QMailFolder::ChildCreationPermitted, childCreationPermitted);
- folder.setStatus(QMailFolder::MessagesPermitted, messagesPermitted);
+ setFolderFlags(&account, &folder, flags);
if (folder.status() != folderOriginal.status()) {
if (!QMailStore::instance()->updateFolder(&folder)) {
@@ -845,8 +911,6 @@ void ImapClient::mailboxListed(const QString &flags, const QString &path)
folder.setStatus(QMailFolder::DeletionPermitted, true);
folder.setStatus(QMailFolder::RenamePermitted, true);
}
- folder.setStatus(QMailFolder::ChildCreationPermitted, childCreationPermitted);
- folder.setStatus(QMailFolder::MessagesPermitted, messagesPermitted);
// The reported flags pertain to the listed folder only
QString folderFlags;
@@ -854,7 +918,24 @@ void ImapClient::mailboxListed(const QString &flags, const QString &path)
folderFlags = flags;
}
+ // Only folders beneath the base folder are relevant
+ QString path(folder.path());
+ QString baseFolder(_strategyContext->baseFolder());
+
+ if (baseFolder.isEmpty() ||
+ (path.startsWith(baseFolder, Qt::CaseInsensitive) && (path.length() == baseFolder.length())) ||
+ (path.startsWith(baseFolder + _protocol.delimiter(), Qt::CaseInsensitive))) {
+ if (!QMailStore::instance()->addFolder(&folder)) {
+ qWarning() << "Unable to add folder for account:" << folder.parentAccountId() << "path:" << folder.path();
+ }
+ }
+
+ setFolderFlags(&account, &folder, flags); // requires valid folder.id()
_strategyContext->mailboxListed(folder, folderFlags);
+
+ if (!QMailStore::instance()->updateFolder(&folder)) {
+ qWarning() << "Unable to update folder for account:" << folder.parentAccountId() << "path:" << folder.path();
+ }
boxId = mailboxId(mailboxPath);
parentId = boxId;
diff --git a/src/plugins/messageservices/imap/imapprotocol.cpp b/src/plugins/messageservices/imap/imapprotocol.cpp
index 3228bb0f..0c3a2b76 100644
--- a/src/plugins/messageservices/imap/imapprotocol.cpp
+++ b/src/plugins/messageservices/imap/imapprotocol.cpp
@@ -834,7 +834,7 @@ class ListState : public ImapState
public:
ListState() : ImapState(IMAP_List, "List") { ListState::init(); }
- void setParameters(const QString &reference, const QString &mailbox);
+ void setParameters(const QString &reference, const QString &mailbox, bool xlist = false);
void setDiscoverDelimiter();
virtual bool permitsPipelining() const { return true; }
@@ -848,13 +848,27 @@ signals:
void mailboxListed(const QString &flags, const QString &path);
private:
+ struct ListParameters
+ {
+ ListParameters() : _xlist(false) {}
+
+ QString _reference;
+ QString _mailbox;
+ bool _xlist;
+ };
+
// The list of reference/mailbox pairs we're listing (via multiple commands), in order
- QList<QPair<QString, QString> > _parameters;
+ QList<ListParameters> _parameters;
};
-void ListState::setParameters(const QString &reference, const QString &mailbox)
+void ListState::setParameters(const QString &reference, const QString &mailbox, bool xlist)
{
- _parameters.append(qMakePair(reference, mailbox));
+ ListParameters params;
+ params._reference = reference;
+ params._mailbox = mailbox;
+ params._xlist = xlist;
+
+ _parameters.append(params);
}
void ListState::setDiscoverDelimiter()
@@ -870,23 +884,26 @@ void ListState::init()
QString ListState::transmit(ImapContext *c)
{
- const QPair<QString, QString> &params(_parameters.last());
+ ListParameters &params(_parameters.last());
- if (!params.first.isEmpty()) {
+ if (!params._reference.isEmpty()) {
if (c->protocol()->delimiterUnknown()) {
//Don't do anything, we're waiting on our delimiter.
return QString();
}
}
- QString reference = params.first;
- QString mailbox = params.second;
+ QString reference = params._reference;
+ QString mailbox = params._mailbox;
if (!reference.isEmpty())
reference.append(c->protocol()->delimiter());
reference = ImapProtocol::quoteString(reference);
mailbox = ImapProtocol::quoteString(mailbox);
- return c->sendCommand(QString("LIST %1 %2").arg(reference).arg(mailbox));
+ QString command("LIST");
+ if (params._xlist)
+ command = "XLIST";
+ return c->sendCommand(QString("%1 %2 %3").arg(command).arg(reference).arg(mailbox));
}
void ListState::leave(ImapContext *)
@@ -897,12 +914,16 @@ void ListState::leave(ImapContext *)
void ListState::untaggedResponse(ImapContext *c, const QString &line)
{
- if (!line.startsWith(QLatin1String("* LIST"))) {
+ QString str;
+ if (line.startsWith(QLatin1String("* LIST"))) {
+ str = line.mid(7);
+ } else if (line.startsWith(QLatin1String("* XLIST"))) {
+ str = line.mid(8);
+ } else {
ImapState::untaggedResponse(c, line);
return;
}
- QString str = line.mid(7);
QString flags, path, delimiter;
int pos, index = 0;
@@ -940,9 +961,9 @@ void ListState::untaggedResponse(ImapContext *c, const QString &line)
void ListState::taggedResponse(ImapContext *c, const QString &line)
{
- const QPair<QString, QString> &params(_parameters.first());
+ ListParameters &params(_parameters.first());
- if (params.first.isNull() && params.second.isNull()) {
+ if (params._reference.isNull() && params._mailbox.isNull()) {
// This was a delimiter discovery request - don't report it to the client
} else {
ImapState::taggedResponse(c, line);
@@ -3041,7 +3062,7 @@ void ImapProtocol::sendList( const QMailFolder &reference, const QString &mailbo
// Now request the actual list
- _fsm->listState.setParameters(path, mailbox);
+ _fsm->listState.setParameters(path, mailbox, capabilities().contains("XLIST"));
_fsm->setState(&_fsm->listState);
}
diff --git a/src/plugins/messageservices/imap/imapstrategy.cpp b/src/plugins/messageservices/imap/imapstrategy.cpp
index 4e654187..fc2196dc 100644
--- a/src/plugins/messageservices/imap/imapstrategy.cpp
+++ b/src/plugins/messageservices/imap/imapstrategy.cpp
@@ -515,20 +515,8 @@ void ImapStrategy::initialAction(ImapStrategyContextBase *context)
void ImapStrategy::mailboxListed(ImapStrategyContextBase *c, QMailFolder& folder, const QString &flags)
{
- if (!folder.id().isValid()) {
- // Only folders beneath the base folder are relevant
- QString path(folder.path());
-
- if (_baseFolder.isEmpty() ||
- (path.startsWith(_baseFolder, Qt::CaseInsensitive) && (path.length() == _baseFolder.length())) ||
- (path.startsWith(_baseFolder + c->protocol().delimiter(), Qt::CaseInsensitive))) {
- if (!QMailStore::instance()->addFolder(&folder)) {
- _error = true;
- qWarning() << "Unable to add folder for account:" << folder.parentAccountId() << "path:" << folder.path();
- }
- }
- }
-
+ Q_UNUSED(c)
+ Q_UNUSED(folder)
Q_UNUSED(flags)
}
@@ -2056,17 +2044,17 @@ void ImapFolderListStrategy::mailboxListed(ImapStrategyContextBase *context, QMa
if (folder.id().isValid()) {
// Record the status of the listed mailbox
int status = 0;
- if (flags.indexOf("NoInferiors", 0, Qt::CaseInsensitive) != -1)
+ if (flags.indexOf("\\NoInferiors", 0, Qt::CaseInsensitive) != -1)
status |= NoInferiors;
- if (flags.indexOf("NoSelect", 0, Qt::CaseInsensitive) != -1)
+ if (flags.indexOf("\\NoSelect", 0, Qt::CaseInsensitive) != -1)
status |= NoSelect;
- if (flags.indexOf("Marked", 0, Qt::CaseInsensitive) != -1)
+ if (flags.indexOf("\\Marked", 0, Qt::CaseInsensitive) != -1)
status |= Marked;
- if (flags.indexOf("Unmarked", 0, Qt::CaseInsensitive) != -1)
+ if (flags.indexOf("\\Unmarked", 0, Qt::CaseInsensitive) != -1)
status |= Unmarked;
- if (flags.indexOf("HasChildren", 0, Qt::CaseInsensitive) != -1)
+ if (flags.indexOf("\\HasChildren", 0, Qt::CaseInsensitive) != -1)
status |= HasChildren;
- if (flags.indexOf("HasNoChildren", 0, Qt::CaseInsensitive) != -1)
+ if (flags.indexOf("\\HasNoChildren", 0, Qt::CaseInsensitive) != -1)
status |= HasNoChildren;
_folderStatus[folder.id()] = static_cast<FolderStatus>(status);
diff --git a/src/plugins/messageservices/imap/imapstrategy.h b/src/plugins/messageservices/imap/imapstrategy.h
index 219b0680..41e0004f 100644
--- a/src/plugins/messageservices/imap/imapstrategy.h
+++ b/src/plugins/messageservices/imap/imapstrategy.h
@@ -156,6 +156,7 @@ public:
void clearError() { _error = false; }
bool error() { return _error; }
+ QString baseFolder() { return _baseFolder; }
protected:
virtual void initialAction(ImapStrategyContextBase *context);
@@ -859,6 +860,7 @@ public:
void folderCreated(const QString &folder) { _strategy->folderCreated(this, folder); }
void folderDeleted(const QMailFolder &folder) { _strategy->folderDeleted(this, folder); }
void folderRenamed(const QMailFolder &folder, const QString &name) { _strategy->folderRenamed(this, folder, name); }
+ QString baseFolder() { return _strategy->baseFolder(); }
ImapStrategy *strategy() const { return _strategy; }
void setStrategy(ImapStrategy *strategy) { _strategy = strategy; }