summaryrefslogtreecommitdiffstats
path: root/libmail/imapfolder.C
diff options
context:
space:
mode:
authorSam Varshavchik2013-08-19 16:39:41 -0400
committerSam Varshavchik2013-08-25 14:43:51 -0400
commit9c45d9ad13fdf439d44d7443ae75da15ea0223ed (patch)
tree7a81a04cb51efb078ee350859a64be2ebc6b8813 /libmail/imapfolder.C
parenta9520698b770168d1f33d6301463bb70a19655ec (diff)
downloadcourier-libs-9c45d9ad13fdf439d44d7443ae75da15ea0223ed.tar.bz2
Initial checkin
Imported from subversion report, converted to git. Updated all paths in scripts and makefiles, reflecting the new directory hierarchy.
Diffstat (limited to 'libmail/imapfolder.C')
-rw-r--r--libmail/imapfolder.C3498
1 files changed, 3498 insertions, 0 deletions
diff --git a/libmail/imapfolder.C b/libmail/imapfolder.C
new file mode 100644
index 0000000..7f00f35
--- /dev/null
+++ b/libmail/imapfolder.C
@@ -0,0 +1,3498 @@
+/*
+** Copyright 2003-2004, Double Precision Inc.
+**
+** See COPYING for distribution information.
+*/
+#include "libmail_config.h"
+#include "imap.H"
+#include "misc.H"
+#include "imaphandler.H"
+#include "imapidle.H"
+#include "imapfetchhandler.H"
+#include "imapfolder.H"
+#include "imapparsefmt.H"
+
+#include "smapopen.H"
+#include "smapidle.H"
+#include "smapnoopexpunge.H"
+#include "smapstore.H"
+#include "smapcopy.H"
+#include "smapsearch.H"
+
+#include "generic.H"
+#include "copymessage.H"
+#include "search.H"
+#include "rfcaddr.H"
+#include "envelope.H"
+#include "structure.H"
+#include "rfc822/rfc822.h"
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <sstream>
+#include <limits.h>
+#include <errno.h>
+
+using namespace std;
+
+mail::imapFOLDERinfo::imapFOLDERinfo(string pathArg,
+ mail::callback::folder &folderCallbackArg)
+ : folderCallback(folderCallbackArg),
+ exists(0), closeInProgress(false),
+ path(pathArg)
+{
+}
+
+mail::imapFOLDERinfo::~imapFOLDERinfo()
+{
+}
+
+
+mail::imapFOLDERinfo::indexInfo::indexInfo()
+{
+}
+
+mail::imapFOLDERinfo::indexInfo::~indexInfo()
+{
+}
+
+void mail::imapFOLDERinfo::setUid(size_t count, string uid)
+{
+ if (count < index.size())
+ index[count].uid=uid;
+}
+
+void mail::imapFOLDERinfo::opened()
+{
+}
+
+
+/////////////////////////////////////////////////////////////////////////
+//
+// Helper class for processing the SELECT command. It's installed when
+// a SELECT command is issued. It handles the * FLAGS, * OK, and * NO
+// untagged messages, during folder open, as well as the * n EXISTS message
+// ( TODO - ignore any other garbage )
+//
+
+LIBMAIL_START
+
+class imapSELECT : public imapCommandHandler {
+
+ mail::callback &openCallback;
+ mail::callback::folder &folderCallback;
+
+ string path;
+
+public:
+ string uidv;
+ size_t exists;
+
+ imapSELECT(string pathArg,
+ mail::callback &openCallbackArg,
+ mail::callback::folder &folderCallbackArg);
+
+ ~imapSELECT();
+
+ void installed(imap &imapAccount);
+ static const char name[];
+
+private:
+ const char *getName();
+ void timedOut(const char *errmsg);
+
+ bool untaggedMessage(imap &imapAccount, string name);
+ bool taggedMessage(imap &imapAccount, string name,
+ string message,
+ bool okfail, string errmsg);
+};
+
+LIBMAIL_END
+
+const char mail::imapSELECT::name[]="SELECT";
+
+mail::imapSELECT::imapSELECT(string pathArg,
+ mail::callback &openCallbackArg,
+ mail::callback::folder &folderCallbackArg)
+ : openCallback(openCallbackArg),
+ folderCallback(folderCallbackArg), path(pathArg),
+ uidv(""), exists(0)
+{
+}
+
+mail::imapSELECT::~imapSELECT()
+{
+}
+
+void mail::imapSELECT::installed(mail::imap &imapAccount)
+{
+ imapAccount.imapcmd("SELECT", "SELECT " +
+ imapAccount.quoteSimple(path) + "\r\n");
+
+ imapAccount.folderFlags.clear();
+ imapAccount.permanentFlags.clear();
+}
+
+const char *mail::imapSELECT::getName()
+{
+ return name;
+}
+
+void mail::imapSELECT::timedOut(const char *errmsg)
+{
+ callbackTimedOut(openCallback, errmsg);
+}
+
+//
+// Helper class for processing the untagged FLAGS response
+//
+
+LIBMAIL_START
+
+class imapSELECT_FLAGS : public imapHandlerStructured {
+
+ void (imapSELECT_FLAGS::*next_func)(imap &, Token t);
+
+public:
+ imapSELECT_FLAGS();
+ ~imapSELECT_FLAGS();
+ void installed(imap &imapAccount);
+
+private:
+ const char *getName();
+ void timedOut(const char *errmsg);
+
+ void process(imap &imapAccount, Token t);
+ void start_flags(imap &imapAccount, Token t);
+ void process_flags(imap &imapAccount, Token t);
+};
+
+LIBMAIL_END
+
+mail::imapSELECT_FLAGS::imapSELECT_FLAGS()
+ : next_func(&mail::imapSELECT_FLAGS::start_flags)
+{
+}
+
+mail::imapSELECT_FLAGS::~imapSELECT_FLAGS()
+{
+}
+
+void mail::imapSELECT_FLAGS::installed(mail::imap &imapAccount)
+{
+}
+
+const char *mail::imapSELECT_FLAGS::getName()
+{
+ return ("* FLAGS");
+}
+
+void mail::imapSELECT_FLAGS::timedOut(const char *errmsg)
+{
+}
+
+void mail::imapSELECT_FLAGS::process(mail::imap &imapAccount, Token t)
+{
+ (this->*next_func)(imapAccount, t);
+}
+
+//
+// Helper class for processing an untagged * #nnnn response during a SELECT
+//
+
+LIBMAIL_START
+
+class imapSELECT_COUNT : public imapHandlerStructured {
+
+ imapSELECT &select;
+
+ void (imapSELECT_COUNT::*next_func)(imap &, Token t);
+
+ size_t count;
+
+public:
+ imapSELECT_COUNT(imapSELECT &mySelect,
+ size_t myCount);
+ ~imapSELECT_COUNT();
+ void installed(imap &imapAccount);
+
+private:
+ const char *getName();
+ void timedOut(const char *);
+ void process(imap &imapAccount, Token t);
+ void get_count(imap &imapAccount, Token t);
+};
+
+LIBMAIL_END
+
+mail::imapSELECT_COUNT::imapSELECT_COUNT(mail::imapSELECT &mySelect,
+ size_t myCount)
+ : select(mySelect),
+ next_func(&mail::imapSELECT_COUNT::get_count),
+ count(myCount)
+{
+}
+
+mail::imapSELECT_COUNT::~imapSELECT_COUNT()
+{
+}
+
+void mail::imapSELECT_COUNT::installed(mail::imap &imapAccount)
+{
+}
+
+const char *mail::imapSELECT_COUNT::getName()
+{
+ return ("* #");
+}
+
+void mail::imapSELECT_COUNT::timedOut(const char *errmsg)
+{
+}
+
+void mail::imapSELECT_COUNT::process(mail::imap &imapAccount, Token t)
+{
+ (this->*next_func)(imapAccount, t);
+}
+
+//
+// Helper class for processing an untagged * OK response during a SELECT
+//
+
+LIBMAIL_START
+
+class imapSELECT_OK : public imapHandler {
+
+ imapSELECT &select;
+ string type;
+public:
+ imapSELECT_OK(imapSELECT &mySelect,
+ string typeArg);
+ ~imapSELECT_OK();
+ void installed(imap &imapAccount);
+
+private:
+ const char *getName();
+ void timedOut(const char *errmsg);
+
+ int process(imap &imapAccount, string &buffer);
+ void process(string &buffer);
+};
+
+LIBMAIL_END
+
+mail::imapSELECT_OK::imapSELECT_OK(mail::imapSELECT &mySelect,
+ string typeArg)
+ : select(mySelect), type(typeArg)
+{
+ upper(type);
+}
+
+mail::imapSELECT_OK::~imapSELECT_OK()
+{
+}
+
+void mail::imapSELECT_OK::installed(mail::imap &imapAccount)
+{
+}
+
+const char *mail::imapSELECT_OK::getName()
+{
+ return("* OK");
+}
+
+void mail::imapSELECT_OK::timedOut(const char *errmsg)
+{
+}
+
+//////////////////////////////////////////////////////////////////////////
+//
+// After a SELECT, and any time * n EXISTS received, a SELECT FLAGS UID
+// is automatically issued (either for the entire folder, or just the new
+// messages). When this command completes, the application callback methods
+// will be invoked accordingly.
+//
+
+LIBMAIL_START
+
+class imapSYNCHRONIZE : public imapCommandHandler {
+
+ mail::callback &callback;
+
+ size_t fetchedCounter;
+
+ size_t newExists;
+
+public:
+ static size_t counter;
+ // Keeps track of the # of objects in existence.
+
+ imapSYNCHRONIZE(mail::callback &callbackArg);
+ ~imapSYNCHRONIZE();
+
+ void installed(imap &imapAccount);
+
+ void fetchedUID();
+
+private:
+ const char *getName();
+ void timedOut(const char *errmsg);
+
+ bool untaggedMessage(imap &imapAccount, string name);
+ bool taggedMessage(imap &imapAccount, string name,
+ string message,
+ bool okfail, string errmsg);
+};
+
+LIBMAIL_END
+
+const char *mail::imapSYNCHRONIZE::getName()
+{
+ return("SYNC");
+}
+
+void mail::imapSYNCHRONIZE::timedOut(const char *errmsg)
+{
+ callbackTimedOut(callback, errmsg);
+}
+
+///////////////////////////////////////////////////////////////////////
+//
+// Stub call from mail::imapFolder::open()
+//
+
+void mail::imap::openFolder(string path,
+ mail::snapshot *restoreSnapshot,
+ mail::callback &openCallback,
+ mail::callback::folder &folderCallback)
+{
+ if (!ready(openCallback))
+ return;
+
+ // If an existing folder is opened, tell the folder not to poll for
+ // new mail any more.
+ if (currentFolder)
+ currentFolder->closeInProgress=true;
+
+ installForegroundTask(smap ?
+ (imapHandler *)
+ new mail::smapOPEN(path,
+ restoreSnapshot,
+ openCallback,
+ folderCallback)
+ : new mail::imapSELECT(path, openCallback,
+ folderCallback));
+}
+
+//
+// A manually-requested NOOP.
+//
+
+LIBMAIL_START
+
+class imapCHECKMAIL : public imapCommandHandler {
+
+ mail::callback &callback;
+
+public:
+ imapCHECKMAIL(mail::callback &myCallback);
+ ~imapCHECKMAIL();
+ void installed(imap &imapAccount);
+
+ class dummyCallback : public mail::callback {
+ public:
+ dummyCallback();
+ ~dummyCallback();
+
+ void success(string);
+ void fail(string);
+
+ void reportProgress(size_t bytesCompleted,
+ size_t bytesEstimatedTotal,
+
+ size_t messagesCompleted,
+ size_t messagesEstimatedTotal);
+ };
+
+private:
+
+ const char *getName();
+
+ void timedOut(const char *errmsg);
+
+ bool untaggedMessage(imap &imapAccount, string name);
+
+ bool taggedMessage(imap &imapAccount, string name,
+ string message,
+ bool okfail, string errmsg);
+};
+
+LIBMAIL_END
+
+mail::imapCHECKMAIL::imapCHECKMAIL(mail::callback &myCallback)
+ : callback(myCallback)
+{
+}
+
+mail::imapCHECKMAIL::~imapCHECKMAIL()
+{
+}
+
+void mail::imapCHECKMAIL::installed(mail::imap &imapAccount)
+{
+ imapAccount.imapcmd("NOOP2", "NOOP\r\n");
+}
+
+const char *mail::imapCHECKMAIL::getName()
+{
+ return "NOOP2";
+}
+
+void mail::imapCHECKMAIL::timedOut(const char *errmsg)
+{
+ callbackTimedOut(callback, errmsg);
+}
+
+bool mail::imapCHECKMAIL::untaggedMessage(mail::imap &imapAccount, string name)
+{
+ return false;
+}
+
+bool mail::imapCHECKMAIL::taggedMessage(mail::imap &imapAccount, string name,
+ string message,
+ bool okfail, string errmsg)
+{
+
+ // The check for completion of a manual new mail check must be
+ // done slightly differently. That's because new mail
+ // processing is handled by the regular new mail processing
+ // objects, which don't really know anything about us.
+ // If new mail processing got kicked off, there should be a
+ // mail::imapSYNCHRONIZE object floating around somewhere.
+ // If there is, what we do is we throw another checkNewMail
+ // object in the queue, which should float to the beginning of
+ // the queue after the current mail::imapSYNCHRONIZE object
+ // goes away.
+
+ if (name == "NOOP2")
+ {
+ if (mail::imapSYNCHRONIZE::counter == 0)
+ {
+ callback.success("DONE");
+ imapAccount.uninstallHandler(this);
+ return true;
+ }
+
+ imapAccount.installForegroundTask(new
+ mail::imapCHECKMAIL(callback));
+
+ imapAccount.uninstallHandler(this);
+ return true;
+ }
+
+ return false;
+}
+
+mail::imapCHECKMAIL::dummyCallback::dummyCallback()
+{
+}
+
+mail::imapCHECKMAIL::dummyCallback::~dummyCallback()
+{
+}
+
+void mail::imapCHECKMAIL::dummyCallback::success(string dummy)
+{
+ delete this;
+}
+
+void mail::imapCHECKMAIL::dummyCallback::fail(string dummy)
+{
+ delete this;
+}
+
+void mail::imapCHECKMAIL
+::dummyCallback::reportProgress(size_t bytesCompleted,
+ size_t bytesEstimatedTotal,
+
+ size_t messagesCompleted,
+ size_t messagesEstimatedTot)
+{
+}
+
+void mail::imap::checkNewMail(mail::callback &callback)
+{
+ if (!ready(callback))
+ return;
+
+ if (currentFolder)
+ currentFolder->resetMailCheckTimer();
+
+ installForegroundTask(smap ?
+ (imapHandler *)new smapNoopExpunge("NOOP",
+ callback,
+ *this)
+ : new mail::imapCHECKMAIL(callback));
+}
+
+void mail::imapFOLDER::resetMailCheckTimer()
+{
+ // Reset the regular new mail checking interval
+ setTimeout(mailCheckInterval);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// SELECT processing.
+
+bool mail::imapSELECT::untaggedMessage(mail::imap &imapAccount, string name)
+{
+ if (name == "FLAGS")
+ {
+ imapAccount.installBackgroundTask(new mail::imapSELECT_FLAGS()
+ );
+ return true;
+ }
+ if (name == "OK" || name == "NO")
+ {
+ imapAccount.installBackgroundTask(new mail::imapSELECT_OK(*this,
+ name));
+ return true;
+ }
+
+ if (isdigit((int)(unsigned char)*name.begin()))
+ {
+ size_t c=0;
+
+ istringstream(name.c_str()) >> c;
+
+ imapAccount.installBackgroundTask(new mail::imapSELECT_COUNT(*this,
+ c));
+ return true;
+ }
+
+ return false;
+}
+
+void mail::imapSELECT_FLAGS::start_flags(mail::imap &imapAccount, Token t)
+{
+ imapAccount.folderFlags.clear();
+ if (t == NIL)
+ {
+ done();
+ return;
+ }
+
+ if (t != '(')
+ error(imapAccount);
+ next_func= &mail::imapSELECT_FLAGS::process_flags;
+}
+
+void mail::imapSELECT_FLAGS::process_flags(mail::imap &imapAccount, Token t)
+{
+ if (t == ATOM)
+ {
+ string name=t.text;
+
+ upper(name);
+ imapAccount.folderFlags.insert(name);
+ return;
+ }
+
+ if (t != ')')
+ error(imapAccount);
+ done();
+}
+
+void mail::imapSELECT_COUNT::get_count(mail::imap &imapAccount, Token t)
+{
+ if (t != ATOM)
+ error(imapAccount);
+ else
+ {
+ if (strcasecmp(t.text.c_str(), "EXISTS") == 0)
+ {
+ select.exists=count;
+
+ // Check against hostile servers
+
+ if (select.exists > UINT_MAX
+ / sizeof(mail::messageInfo))
+ select.exists=UINT_MAX
+ / sizeof(mail::messageInfo);
+ }
+ }
+ done();
+}
+
+int mail::imapSELECT_OK::process(mail::imap &imapAccount, string &buffer)
+{
+ size_t p=buffer.find('\n');
+
+ if (p == std::string::npos)
+ {
+ if (p + 16000 < buffer.size())
+ return buffer.size() - 16000;
+ // SANITY CHECK - DON'T LET HOSTILE SERVER DOS us
+
+ return 0;
+ }
+
+ string buffer_cpy=buffer;
+
+ buffer_cpy.erase(p);
+ process(buffer_cpy);
+ imapAccount.uninstallHandler(this);
+ return p+1;
+}
+
+void mail::imapSELECT_OK::process(string &buffer)
+{
+ if (type != "OK")
+ return;
+
+ string::iterator b=buffer.begin(), e=buffer.end();
+
+ while (b != e && unicode_isspace((unsigned char)*b))
+ b++;
+
+ if (b == e || *b != '[')
+ return;
+ b++;
+
+ string w=mail::imap::get_word(&b, &e);
+
+ upper(w);
+
+ if (w == "UIDVALIDITY") // Memorize * OK [UIDVALIDITY]
+ {
+ w=mail::imap::get_word(&b, &e);
+ select.uidv=w;
+ }
+ else if (w == "PERMANENTFLAGS")
+ {
+ myimap->setPermanentFlags(b, e);
+ }
+}
+
+//
+// After an initial SELECT succeeds, even if the subsequent resynchronization
+// commands fail, we still want to indicate success.
+//
+
+LIBMAIL_START
+
+class imapSELECTCallback : public mail::callback {
+
+ mail::callback &origCallback;
+
+ void reportProgress(size_t bytesCompleted,
+ size_t bytesEstimatedTotal,
+
+ size_t messagesCompleted,
+ size_t messagesEstimatedTotal);
+
+public:
+ imapSELECTCallback(mail::callback &cb);
+ ~imapSELECTCallback();
+ void success(string msg);
+ void fail(string msg);
+};
+LIBMAIL_END
+
+bool mail::imapSELECT::taggedMessage(mail::imap &imapAccount, string name,
+ string message,
+ bool okfail, string errmsg)
+{
+ if (name != "SELECT")
+ return false;
+
+ if (okfail && uidv.length() == 0)
+ {
+ okfail=false;
+ errmsg="Server did not return the folder UID-validity token.";
+ }
+
+ if (okfail)
+ {
+ mail::imapFOLDER *f=new mail::imapFOLDER(path, uidv, exists,
+ folderCallback,
+ imapAccount);
+ imapAccount.installBackgroundTask(f);
+ imapAccount.currentFolder=f;
+
+ f->exists=0;
+
+ if (exists > 0)
+ {
+ mail::imapSELECTCallback *cb=
+ new mail::imapSELECTCallback(openCallback);
+
+ try {
+ imapAccount.installForegroundTask
+ (new mail::imapSYNCHRONIZE(*cb));
+ } catch (...) {
+ delete cb;
+ }
+ }
+ else
+ openCallback.success("Empty folder.");
+ }
+ else
+ {
+ imapAccount.currentFolder=NULL;
+ openCallback.fail(errmsg);
+ }
+ imapAccount.uninstallHandler(this);
+ return true;
+}
+
+mail::imapSELECTCallback::imapSELECTCallback(mail::callback &cb)
+ : origCallback(cb)
+{
+}
+
+mail::imapSELECTCallback::~imapSELECTCallback()
+{
+}
+
+void mail::imapSELECTCallback::success(string msg)
+{
+ origCallback.success(msg);
+ delete this;
+}
+
+void mail::imapSELECTCallback::fail(string msg)
+{
+ origCallback.success(msg);
+ delete this;
+}
+
+void mail::imapSELECTCallback::reportProgress(size_t bytesCompleted,
+ size_t bytesEstimatedTotal,
+
+ size_t messagesCompleted,
+ size_t messagesEstimatedTotal)
+{
+ origCallback.reportProgress(bytesCompleted, bytesEstimatedTotal,
+ messagesCompleted,
+ messagesEstimatedTotal);
+}
+
+////////////////////////////////////////////////////////////////////////
+//
+// Helper class to resynchronize a folder
+
+
+size_t mail::imapSYNCHRONIZE::counter=0;
+
+mail::imapSYNCHRONIZE::imapSYNCHRONIZE(mail::callback &callbackArg)
+ : callback(callbackArg), fetchedCounter(0)
+{
+ ++counter;
+}
+
+void mail::imapSYNCHRONIZE::installed(mail::imap &imapAccount)
+{
+ ostringstream o;
+
+ newExists=imapAccount.currentFolder ?
+ imapAccount.currentFolder->index.size():0;
+
+ if (imapAccount.currentFolder == NULL ||
+ imapAccount.currentFolder->exists >= newExists)
+ {
+ imapAccount.currentFolder->exists=newExists; // Insurance
+
+ o << "NOOP\r\n";
+ }
+ else
+ {
+
+ o << "FETCH " << (imapAccount.currentFolder->exists+1)
+ << ":" << newExists << " (UID FLAGS)\r\n";
+
+ }
+
+ imapAccount.imapcmd("SYNC", o.str());
+ imapAccount.currentSYNCHRONIZE=this;
+}
+
+mail::imapSYNCHRONIZE::~imapSYNCHRONIZE()
+{
+ if (myimap)
+ myimap->currentSYNCHRONIZE=NULL;
+ --counter;
+}
+
+void mail::imapSYNCHRONIZE::fetchedUID()
+{
+ size_t c=myimap && myimap->currentFolder ?
+ myimap->currentFolder->exists:0;
+
+
+ if (c >= newExists)
+ c=newExists;
+
+ if (fetchedCounter < newExists - c)
+ {
+ ++fetchedCounter;
+ callback.reportProgress(0, 0,
+ fetchedCounter, newExists - c);
+ }
+}
+
+bool mail::imapSYNCHRONIZE::untaggedMessage(mail::imap &imapAccount, string name)
+{
+ return false;
+}
+
+bool mail::imapSYNCHRONIZE::taggedMessage(mail::imap &imapAccount, string name,
+ string message,
+ bool okfail, string errmsg)
+{
+ if (name != "SYNC")
+ return false;
+
+ if (!okfail)
+ {
+ callback.fail(errmsg);
+ imapAccount.uninstallHandler(this);
+ return true;
+ }
+
+ mail::imapFOLDERinfo *f=imapAccount.currentFolder;
+
+ size_t n;
+
+ for (n=f->exists; n<f->index.size(); n++)
+ if (f->index[n].uid.length() == 0)
+ {
+ callback.fail("Server failed to respond with message identifier.");
+ imapAccount.uninstallHandler(this);
+ return true;
+ }
+
+ if (f->exists < newExists)
+ f->exists=newExists;
+
+
+ callback.success("Folder index updated.");
+ imapAccount.uninstallHandler(this);
+ return true;
+}
+
+
+size_t mail::imap::getFolderIndexSize()
+{
+ if (currentFolder == NULL)
+ return 0;
+
+ return currentFolder->exists;
+}
+
+mail::messageInfo mail::imap::getFolderIndexInfo(size_t n)
+{
+ if (currentFolder != NULL && n < currentFolder->exists)
+ return currentFolder->index[n];
+
+ return mail::messageInfo();
+}
+
+void mail::imap::getFolderKeywordInfo(size_t n, std::set<std::string> &kwset)
+{
+ if (currentFolder != NULL && n < currentFolder->exists)
+ {
+ currentFolder->index[n].keywords.getFlags(kwset);
+ }
+ else
+ kwset.clear();
+}
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Server replies when a folder is currently open.
+//
+
+const char mail::imapFOLDER::name[]="FolderHandler";
+
+//
+// Helper class for processing an untagged * #nnnn response during a SELECT
+//
+
+LIBMAIL_START
+
+class imapFOLDER_COUNT : public imapHandlerStructured {
+
+ void (imapFOLDER_COUNT::*next_func)(imap &, Token t);
+
+ size_t count;
+ bool body_part_reference; // Fetch References: header
+ string references_buf;
+ vector<string> flags;
+
+ bool accept_largestring;
+
+ bool wantLargeString();
+public:
+ imapFOLDER_COUNT(size_t myCount);
+ ~imapFOLDER_COUNT();
+ void installed(imap &imapAccount) {}
+private:
+ const char *getName();
+ void timedOut(const char *errmsg);
+ void process(imap &imapAccount, Token t);
+
+ void get_count(imap &imapAccount, Token t);
+
+ void get_fetch(imap &imapAccount, Token t);
+ void get_fetch_item(imap &imapAccount, Token t);
+ void get_fetch_uid(imap &imapAccount, Token t);
+
+ void get_fetch_flags(imap &imapAccount, Token t);
+ void get_fetch_flag(imap &imapAccount, Token t);
+ void saveflags(imap &imapAccount);
+
+ void get_internaldate(imap &imapAccount, Token t);
+ void get_rfc822size(imap &imapAccount, Token t);
+
+ void get_envelope(imap &imapAccount, Token t);
+ void get_bodystructure(imap &imapAccount, Token t);
+ void get_body(imap &imapAccount, Token t);
+
+ bool fillbodystructure(imap &imapAccount, imapparsefmt &parser,
+ mimestruct &bodystructure,
+ string mime_id);
+
+ imapparsefmt parser;
+};
+
+LIBMAIL_END
+
+mail::imapFOLDER::imapFOLDER(string pathArg, string uidvArg, size_t existsArg,
+ mail::callback::folder &folderCallbackArg,
+ mail::imap &myserver)
+ : mail::imapCommandHandler(myserver.noopSetting),
+ imapFOLDERinfo(pathArg, folderCallbackArg),
+ existsNotify(NULL),
+ uidv(uidvArg)
+{
+ mailCheckInterval=myserver.noopSetting;
+ setBackgroundTask(true);
+ index.resize(existsArg);
+}
+
+mail::imapFOLDER::~imapFOLDER()
+{
+ if (existsNotify)
+ existsNotify->me=NULL;
+}
+
+mail::imapFOLDER::existsCallback::existsCallback()
+{
+}
+
+mail::imapFOLDER::existsCallback::~existsCallback()
+{
+}
+
+void mail::imapFOLDER::existsCallback::success(string message)
+{
+ if (me)
+ {
+ mail::imapFOLDER *cb=me;
+ me=NULL;
+ cb->existsNotify=NULL;
+ cb->folderCallback.newMessages();
+ }
+ delete this;
+}
+
+void mail::imapFOLDER::existsCallback
+::reportProgress(size_t bytesCompleted,
+ size_t bytesEstimatedTotal,
+
+ size_t messagesCompleted,
+ size_t messagesEstimatedTotal)
+{
+}
+
+void mail::imapFOLDER::existsCallback::fail(string message)
+{
+ if (me)
+ {
+ mail::imapFOLDER *cb=me;
+ me=NULL;
+ cb->existsNotify=NULL;
+ cb->folderCallback.newMessages();
+ }
+ delete this;
+}
+
+const char *mail::imapFOLDER::getName()
+{
+ return name;
+}
+
+void mail::imapFOLDER::installed(mail::imap &imapAccount)
+{
+}
+
+void mail::imapFOLDER::timedOut(const char *errmsg)
+{
+}
+
+int mail::imapFOLDER::getTimeout(mail::imap &imapAccount)
+{
+ int t=mail::imapCommandHandler::getTimeout(imapAccount);
+
+ if (t == 0)
+ {
+ setTimeout(t=imapAccount.noopSetting);
+
+ imapHandler *h;
+
+ if (!closeInProgress // Not closing the folder
+ && ((h=imapAccount.hasForegroundTask()) == NULL ||
+ strcmp(h->getName(), "IDLE") == 0))
+ // Something else is already in progress
+ {
+ imapCHECKMAIL::dummyCallback *dummy=
+ new imapCHECKMAIL::dummyCallback();
+
+ if (!dummy)
+ LIBMAIL_THROW((strerror(errno)));
+
+ try {
+ imapAccount
+ .installForegroundTask(new
+ imapCHECKMAIL
+ (*dummy));
+ } catch (...) {
+ delete dummy;
+ throw;
+ }
+ }
+ }
+ return t; // This handler never causes a timeout
+}
+
+bool mail::imapFOLDER::untaggedMessage(mail::imap &imapAccount, string name)
+{
+ if (isdigit((int)(unsigned char)*name.begin()))
+ {
+ if (imapAccount.handlers.count(mail::imapSELECT::name) > 0)
+ return false; // SELECT in progress
+ size_t c=0;
+
+ istringstream(name.c_str()) >> c;
+
+ imapAccount.installBackgroundTask(new mail::imapFOLDER_COUNT(c));
+ return true;
+ }
+
+ if (name == "FLAGS")
+ {
+ imapAccount
+ .installBackgroundTask(new mail::imapSELECT_FLAGS());
+ return true;
+ }
+
+ return false;
+}
+
+bool mail::imapFOLDER::taggedMessage(mail::imap &imapAccount, string name,
+ string message,
+ bool okfail, string errmsg)
+{
+ return false;
+}
+
+
+mail::imapFOLDER_COUNT::imapFOLDER_COUNT(size_t myCount)
+ : next_func(&mail::imapFOLDER_COUNT::get_count),
+ count(myCount), body_part_reference(false),
+ accept_largestring(false)
+{
+}
+
+mail::imapFOLDER_COUNT::~imapFOLDER_COUNT()
+{
+}
+
+bool mail::imapFOLDER_COUNT::wantLargeString()
+{
+ return accept_largestring;
+}
+
+const char *mail::imapFOLDER_COUNT::getName()
+{
+ return ("* #");
+}
+
+void mail::imapFOLDER_COUNT::timedOut(const char *errmsg)
+{
+}
+
+void mail::imapFOLDER_COUNT::process(mail::imap &imapAccount, Token t)
+{
+ (this->*next_func)(imapAccount, t);
+}
+
+// Seen * nnnnn
+
+void mail::imapFOLDER_COUNT::get_count(mail::imap &imapAccount, Token t)
+{
+ if (t != ATOM)
+ {
+ error(imapAccount);
+ return;
+ }
+
+ string name=t.text;
+
+ upper(name);
+ if (name == "FETCH")
+ {
+ next_func= &mail::imapFOLDER_COUNT::get_fetch;
+ return;
+ }
+
+ if (name == "EXPUNGE")
+ {
+ done();
+
+ if (imapAccount.currentFolder != NULL && count > 0 &&
+ count <= imapAccount.currentFolder->index.size())
+ {
+ size_t c=count-1;
+
+ vector<imapFOLDERinfo::indexInfo> &indexRef=
+ imapAccount.currentFolder->index;
+
+ indexRef.erase(indexRef.begin() + c,
+ indexRef.begin() + c + 1);
+
+ if (c < imapAccount.currentFolder->exists)
+ {
+ --imapAccount.currentFolder->exists;
+
+ vector< pair<size_t, size_t> > a;
+
+ a.push_back(make_pair(c, c));
+ imapAccount.currentFolder->folderCallback
+ .messagesRemoved(a);
+ }
+ }
+ return;
+ }
+ if (name == "EXISTS")
+ {
+ done();
+
+ if (imapAccount.currentFolder != NULL &&
+ !imapAccount.currentFolder->closeInProgress &&
+ count > imapAccount.currentFolder->index.size())
+ {
+ imapAccount.currentFolder
+ ->existsMore(imapAccount, count);
+ }
+ }
+
+ done();
+}
+
+void mail::imapFOLDER::existsMore(mail::imap &imapAccount, size_t count)
+{
+ if (closeInProgress)
+ return;
+
+ if (exists > count)
+ exists=count;
+
+ index.resize(count, imapFOLDERinfo::indexInfo());
+
+ if (existsNotify)
+ existsNotify->me=NULL;
+
+ existsNotify=new mail::imapFOLDER::existsCallback;
+
+ if (!existsNotify)
+ LIBMAIL_THROW("Out of memory.");
+
+ existsNotify->me=this;
+
+ imapAccount
+ .installForegroundTask(new mail::imapSYNCHRONIZE(*existsNotify)
+ );
+}
+
+void mail::imapFOLDER_COUNT::get_fetch(mail::imap &imapAccount, Token t)
+{
+ if (t != '(')
+ error(imapAccount);
+ next_func= &mail::imapFOLDER_COUNT::get_fetch_item;
+}
+
+void mail::imapFOLDER_COUNT::get_fetch_item(mail::imap &imapAccount, Token t)
+{
+ accept_largestring=false;
+ if (t == ')')
+ {
+ done();
+ return;
+ }
+
+ if (t != ATOM)
+ {
+ error(imapAccount);
+ return;
+ }
+
+ string name=t.text;
+
+ upper(name);
+
+ if (name == "UID")
+ {
+ next_func= &mail::imapFOLDER_COUNT::get_fetch_uid;
+ return;
+ }
+
+ if (name == "FLAGS")
+ {
+ next_func= &mail::imapFOLDER_COUNT::get_fetch_flags;
+ return;
+ }
+
+ if (name == "INTERNALDATE")
+ {
+ next_func= &mail::imapFOLDER_COUNT::get_internaldate;
+ return;
+ }
+
+ if (name == "RFC822.SIZE")
+ {
+ next_func= &mail::imapFOLDER_COUNT::get_rfc822size;
+ return;
+ }
+
+ if (name == "ENVELOPE")
+ {
+ parser.begin();
+ next_func= &mail::imapFOLDER_COUNT::get_envelope;
+ return;
+ }
+
+ if (name == "BODYSTRUCTURE")
+ {
+ parser.begin();
+ next_func= &mail::imapFOLDER_COUNT::get_bodystructure;
+ return;
+ }
+
+ if (strncasecmp(name.c_str(), "BODY[", 5) == 0 ||
+ strncasecmp(name.c_str(), "BODY.PEEK[", 10) == 0)
+ {
+ accept_largestring=true;
+ next_func= &mail::imapFOLDER_COUNT::get_body;
+
+ size_t i=name.find(']');
+
+ if (i != name.length()-1)
+ {
+ error(imapAccount);
+ return;
+ }
+ name.erase(i);
+ name.erase(0, name.find('[')+1);
+
+ body_part_reference=strncasecmp(name.c_str(),
+ "HEADER.FIELDS", 13) == 0;
+ references_buf="";
+ // Getting the References: header.
+ return;
+ }
+
+ error(imapAccount);
+}
+
+void mail::imapFOLDER_COUNT::get_fetch_uid(mail::imap &imapAccount, Token t)
+{
+ if (t != ATOM)
+ {
+ error(imapAccount);
+ return;
+ }
+
+ if (imapAccount.currentFolder && count > 0)
+ imapAccount.currentFolder->setUid(count-1, t.text);
+
+ // Report progress
+ if (imapAccount.currentSYNCHRONIZE)
+ imapAccount.currentSYNCHRONIZE->fetchedUID();
+
+ next_func= &mail::imapFOLDER_COUNT::get_fetch_item;
+}
+
+void mail::imapFOLDER::setUid(size_t count, string uid)
+{
+ imapFOLDERinfo::setUid(count, uidv + "/" + uid);
+}
+
+void mail::imapFOLDER_COUNT::get_fetch_flags(mail::imap &imapAccount, Token t)
+{
+ flags.clear();
+
+ if (t == NIL)
+ {
+ saveflags(imapAccount);
+ return;
+ }
+
+ if (t != '(')
+ {
+ error(imapAccount);
+ return;
+ }
+
+ next_func= &mail::imapFOLDER_COUNT::get_fetch_flag;
+}
+
+void mail::imapFOLDER_COUNT::get_fetch_flag(mail::imap &imapAccount, Token t)
+{
+ if (t == ')')
+ {
+ saveflags(imapAccount);
+ return;
+ }
+
+ if (t != ATOM)
+ {
+ error(imapAccount);
+ return;
+ }
+
+ flags.push_back(t.text);
+}
+
+void mail::imapFOLDER_COUNT::saveflags(mail::imap &imapAccount)
+{
+ next_func= &mail::imapFOLDER_COUNT::get_fetch_item;
+
+ if (imapAccount.currentFolder && count > 0 &&
+ count <= imapAccount.currentFolder->index.size())
+ {
+ imapFOLDERinfo::indexInfo &i=
+ imapAccount.currentFolder->index[count-1];
+
+ if (imapAccount.folderFlags.count("\\DRAFT") > 0)
+ i.draft=0;
+
+ if (imapAccount.folderFlags.count("\\ANSWERED") > 0)
+ i.replied=0;
+
+ if (imapAccount.folderFlags.count("\\FLAGGED") > 0)
+ i.marked=0;
+
+ if (imapAccount.folderFlags.count("\\DELETED") > 0)
+ i.deleted=0;
+
+ if (imapAccount.folderFlags.count("\\SEEN") > 0)
+ i.unread=1;
+
+ if (imapAccount.folderFlags.count("\\RECENT") > 0)
+ i.recent=0;
+
+ size_t n;
+
+ mail::keywords::Message newMessage;
+
+ for (n=0; n<flags.size(); n++)
+ {
+ string f=flags[n];
+
+ upper(f);
+ if (f == "\\DRAFT")
+ i.draft=1;
+ else if (f == "\\ANSWERED")
+ i.replied=1;
+ else if (f == "\\FLAGGED")
+ i.marked=1;
+ else if (f == "\\DELETED")
+ i.deleted=1;
+ else if (f == "\\SEEN")
+ i.unread=0;
+ else if (f == "\\RECENT")
+ i.recent=1;
+ else if (*flags[n].c_str() != '\\')
+ {
+ if (!newMessage.addFlag(imapAccount
+ .keywordHashtable,
+ flags[n]))
+ LIBMAIL_THROW(strerror(errno));
+ }
+ }
+
+ i.keywords=newMessage;
+
+ if (count <= imapAccount.currentFolder->exists)
+ imapAccount.currentFolder->
+ folderCallback.messageChanged(count-1);
+ }
+}
+
+static void parse_addr_list(mail::imapparsefmt *list,
+ vector<mail::address> &addresses)
+{
+ vector<mail::imapparsefmt *>::iterator b=list->children.begin(),
+ e=list->children.end();
+
+ while (b != e)
+ {
+ mail::imapparsefmt *address= *b;
+
+ if (address->children.size() >= 4)
+ {
+ mail::imapparsefmt *name=address->children[0];
+ mail::imapparsefmt *mailbox=address->children[2];
+ mail::imapparsefmt *host=address->children[3];
+
+ if (host->nil)
+ {
+ if (mailbox->nil)
+ addresses.
+ push_back(mail::address(";",
+ ""));
+ else
+ addresses.
+ push_back(mail::address(mailbox
+ ->value
+ + ":",
+ ""));
+ }
+ else
+ {
+ addresses.
+ push_back(mail::address(name->value,
+ mailbox->value
+ + "@" +
+ host->value));
+ }
+ }
+ b++;
+ }
+}
+
+static bool fillenvelope(mail::imapparsefmt &imapenvelope,
+ mail::envelope &envelope)
+{
+ if (imapenvelope.children.size() < 10)
+ return false;
+
+ vector<mail::imapparsefmt *>::iterator b=imapenvelope.children.begin();
+
+
+ envelope.date=rfc822_parsedt((*b)->value.c_str()); b++;
+
+ envelope.subject= (*b)->value; b++;
+
+ parse_addr_list(*b, envelope.from); b++;
+ parse_addr_list(*b, envelope.sender); b++;
+ parse_addr_list(*b, envelope.replyto); b++;
+ parse_addr_list(*b, envelope.to); b++;
+ parse_addr_list(*b, envelope.cc); b++;
+ parse_addr_list(*b, envelope.bcc); b++;
+
+ envelope.inreplyto= (*b)->value; b++;
+ envelope.messageid= (*b)->value;
+ return true;
+}
+
+void mail::imapFOLDER_COUNT::get_internaldate(mail::imap &imapAccount, Token t)
+{
+ if (t != ATOM && t != STRING)
+ {
+ error(imapAccount);
+ return;
+ }
+
+ if (imapAccount.currentFetch)
+ {
+ // Make IMAP date presentable for rfc822_parsedt() by
+ // replacing dd-mmm-yyyy with dd mmm yyyy
+
+ string d=t.text;
+
+ if (d[0] == ' ')
+ d[0]='0';
+
+ size_t spacepos=d.find(' ');
+ size_t n;
+
+ while ((n=d.find('-')) < spacepos)
+ d[n]=' ';
+
+ time_t tm=rfc822_parsedt(d.c_str());
+
+ if (tm)
+ imapAccount.currentFetch
+ ->messageArrivalDateCallback(count-1, tm);
+ }
+
+ next_func= &mail::imapFOLDER_COUNT::get_fetch_item;
+}
+
+void mail::imapFOLDER_COUNT::get_rfc822size(mail::imap &imapAccount, Token t)
+{
+ if (t != ATOM)
+ {
+ error(imapAccount);
+ return;
+ }
+
+ if (imapAccount.currentFetch)
+ {
+ unsigned long msgsize=0;
+
+ istringstream(t.text.c_str()) >> msgsize;
+
+ imapAccount.currentFetch
+ ->messageSizeCallback(count-1, msgsize);
+ }
+
+ next_func= &mail::imapFOLDER_COUNT::get_fetch_item;
+}
+
+void mail::imapFOLDER_COUNT::get_envelope(mail::imap &imapAccount, Token t)
+{
+ bool err_flag;
+
+ if (!parser.process(imapAccount, t, err_flag))
+ return; // Not yet
+
+ next_func= &mail::imapFOLDER_COUNT::get_fetch_item;
+
+ mail::envelope envelope;
+
+ if (err_flag || !fillenvelope(parser, envelope))
+ {
+ error(imapAccount);
+ return;
+ }
+
+ if (imapAccount.currentFetch)
+ imapAccount.currentFetch
+ ->messageEnvelopeCallback(count-1, envelope);
+}
+
+
+static void fill_type_parameters(mail::imapparsefmt *ptr,
+ mail::mimestruct &bodystructure)
+{
+ vector<mail::imapparsefmt *>::iterator
+ bb=ptr->children.begin(),
+ ee=ptr->children.end();
+
+ while (bb != ee)
+ {
+ string name= (*bb)->value; bb++;
+
+ if (bb != ee)
+ {
+ bodystructure.type_parameters.
+ set_simple(name, (*bb)->value);
+ bb++;
+ }
+ }
+}
+
+
+static void fill_disposition(mail::imapparsefmt *ptr,
+ mail::mimestruct &bodystructure)
+{
+ if (ptr->children.size() > 0)
+ bodystructure.content_disposition=
+ ptr->children[0]->value;
+
+ if (ptr->children.size() < 2)
+ return;
+
+ vector<mail::imapparsefmt *>::iterator
+ bb=ptr->children[1]->children.begin(),
+ ee=ptr->children[1]->children.end();
+
+ while (bb != ee)
+ {
+ string name= (*bb)->value; bb++;
+
+ if (bb != ee)
+ {
+ bodystructure.content_disposition_parameters
+ .set_simple(name, (*bb)->value);
+ bb++;
+ }
+ }
+}
+
+bool mail::imapFOLDER_COUNT::fillbodystructure(mail::imap &imapAccount,
+ mail::imapparsefmt &parser,
+ mail::mimestruct &bodystructure,
+ string mime_id)
+{
+ vector<mail::imapparsefmt *>::iterator b=parser.children.begin(),
+ e=parser.children.end();
+
+ if (b == e)
+ {
+ error(imapAccount);
+ return false;
+ }
+
+ if ( (*b)->children.size() == 0) // Non-multipart
+ {
+
+ bodystructure.mime_id=mime_id;
+
+ if (mime_id.length() == 0)
+ mime_id="1";
+
+ bodystructure.type= (*b)->value; b++;
+
+ if (b != e)
+ {
+ bodystructure.subtype= (*b)->value;
+ b++;
+ }
+
+ mail::upper(bodystructure.type);
+ mail::upper(bodystructure.subtype);
+
+ if (b != e)
+ {
+ fill_type_parameters( *b, bodystructure ); b++;
+ }
+
+ if (b != e)
+ {
+ bodystructure.content_id= (*b)->value;
+ b++;
+ }
+
+ if (b != e)
+ {
+ bodystructure.content_description= (*b)->value;
+ b++;
+ }
+
+ if (b != e)
+ {
+ bodystructure.content_transfer_encoding= (*b)->value;
+ b++;
+ }
+
+ if (b != e)
+ {
+ istringstream((*b)->value.c_str()) >>
+ bodystructure.content_size;
+ b++;
+
+ if (bodystructure.messagerfc822())
+ {
+ if (b != e)
+ {
+ if (!fillenvelope(**b,
+ bodystructure
+ .getEnvelope())
+ )
+ {
+ error(imapAccount);
+ return false;
+ }
+
+ b++;
+ }
+ if (b != e)
+ {
+ if ( (*b)->children.size() == 0)
+ mime_id=mime_id + ".1";
+
+ mail::mimestruct &child_struct=
+ *bodystructure.addChild();
+
+ if (!fillbodystructure(imapAccount,
+ **b,
+ child_struct,
+ mime_id))
+ return false;
+
+ if (child_struct.getNumChildren() == 0)
+ child_struct.mime_id=
+ mime_id + ".1";
+ // Fixup
+ }
+
+ if (b != e)
+ {
+ istringstream((*b)->value.c_str()) >>
+ bodystructure.content_lines;
+ }
+ }
+ else if (bodystructure.type == "TEXT")
+ {
+ if (b != e)
+ {
+ istringstream((*b)->value.c_str()) >>
+ bodystructure.content_lines;
+ b++;
+ }
+ }
+ }
+
+ if (b != e)
+ {
+ bodystructure.content_md5= (*b)->value;
+ b++;
+ }
+ }
+ else // MULTIPART
+ {
+ size_t body_num=1;
+
+ bodystructure.mime_id=mime_id;
+
+ if (mime_id.length() > 0)
+ mime_id=mime_id+".";
+#if 0
+ else
+ bodystructure.mime_id="1";
+#endif
+
+ while (b != e)
+ {
+ if ( (*b)->children.size() == 0)
+ break;
+
+ string buffer;
+ {
+ ostringstream o;
+
+ o << body_num;
+ buffer=o.str();
+ }
+ body_num++;
+
+ if (!fillbodystructure(imapAccount, **b,
+ *bodystructure.addChild(),
+ mime_id + buffer))
+ return false;
+ b++;
+ }
+
+ bodystructure.type="MULTIPART";
+ bodystructure.subtype="MIXED";
+
+ if (b != e)
+ {
+ bodystructure.subtype= (*b)->value;
+ b++;
+ }
+
+ mail::upper(bodystructure.subtype);
+
+ if (b != e)
+ {
+ fill_type_parameters( *b, bodystructure );
+ b++;
+ }
+ }
+
+
+ if (b != e)
+ {
+ fill_disposition( (*b), bodystructure );
+ b++;
+ }
+
+ if (b != e)
+ {
+ bodystructure.content_language= (*b)->value;
+ b++;
+ }
+
+ return true;
+}
+
+void mail::imapFOLDER_COUNT::get_bodystructure(mail::imap &imapAccount, Token t)
+{
+ bool err_flag;
+
+ if (!parser.process(imapAccount, t, err_flag))
+ return; // Not yet
+
+ next_func= &mail::imapFOLDER_COUNT::get_fetch_item;
+
+ mail::mimestruct bodystructure;
+ if (err_flag)
+ {
+ error(imapAccount);
+ return;
+ }
+
+ if (!fillbodystructure(imapAccount, parser, bodystructure, ""))
+ return;
+
+ if (imapAccount.currentFetch)
+ imapAccount.currentFetch
+ ->messageStructureCallback(count-1, bodystructure);
+
+}
+
+void mail::imapFOLDER_COUNT::get_body(mail::imap &imapAccount, Token t)
+{
+ if (t == STRING)
+ next_func= &mail::imapFOLDER_COUNT::get_fetch_item;
+
+ if (t == STRING || t == LARGESTRING)
+ {
+ string buf=t.text;
+
+ size_t cr;
+
+ while ((cr=buf.find('\r')) != std::string::npos)
+ buf.erase(cr, 1);
+
+ if (imapAccount.currentFetch)
+ {
+ imapAccount.currentFetch->messageTextEstimatedSize =
+ t == LARGESTRING
+ ? largestringsize:t.text.size();
+
+ imapAccount.currentFetch
+ ->messageTextCompleted += t.text.size();
+
+ if (t == STRING)
+ {
+ imapAccount.currentFetch->messageTextCompleted
+ = imapAccount.currentFetch->
+ messageTextEstimatedSize;
+ ++imapAccount.currentFetch->messageCntDone;
+ }
+
+ if (body_part_reference)
+ // Processing References: header
+ {
+ references_buf += buf;
+
+ while (references_buf.size() > 10000)
+ // Prevent the server from DOssing us
+ {
+ size_t n=references_buf.find('>');
+
+ if (n != std::string::npos)
+ ++n;
+ else n=references_buf.size()-10000;
+
+ references_buf
+ .erase(references_buf.begin(),
+ references_buf.begin()
+ +n);
+ }
+
+ if (t == STRING)
+ {
+ vector<address> address_list;
+ size_t err_index;
+
+ address::fromString(references_buf,
+ address_list,
+ err_index);
+
+ vector<string> msgid_list;
+
+ vector<address>::iterator
+ b=address_list.begin(),
+ e=address_list.end();
+
+ while (b != e)
+ {
+ string s=b->getAddr();
+
+ if (s.size() > 0)
+ msgid_list.push_back
+ ("<" +
+ s + ">");
+ ++b;
+ }
+
+ if (msgid_list.size() > 0)
+ imapAccount.currentFetch->
+ messageReferencesCallback(count-1, msgid_list);
+ }
+ }
+ else
+ imapAccount.currentFetch
+ ->messageTextCallback(count-1, buf);
+ }
+
+ }
+ else
+ error(imapAccount);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// The STORE command.
+//
+
+LIBMAIL_START
+
+class imapSTORE : public imapCommandHandler {
+
+ mail::callback &callback;
+
+ string storecmd;
+
+public:
+ imapSTORE(mail::callback &callbackArg, string storecmdArg);
+ ~imapSTORE();
+
+ void installed(imap &imapAccount);
+
+private:
+ const char *getName();
+ void timedOut(const char *errmsg);
+
+ bool untaggedMessage(imap &imapAccount, string name);
+ bool taggedMessage(imap &imapAccount, string name,
+ string message,
+ bool okfail, string errmsg);
+};
+
+LIBMAIL_END
+
+void mail::imapSTORE::timedOut(const char *errmsg)
+{
+ callbackTimedOut(callback, errmsg);
+}
+
+mail::imapSTORE::imapSTORE(mail::callback &callbackArg,
+ string storecmdArg)
+ : callback(callbackArg), storecmd(storecmdArg)
+{
+}
+
+mail::imapSTORE::~imapSTORE()
+{
+}
+
+void mail::imapSTORE::installed(mail::imap &imapAccount)
+{
+ imapAccount.imapcmd("STORE", storecmd + "\r\n");
+}
+
+const char *mail::imapSTORE::getName()
+{
+ return "STORE";
+}
+
+bool mail::imapSTORE::untaggedMessage(mail::imap &imapAccount, string name)
+{
+ return false;
+}
+
+bool mail::imapSTORE::taggedMessage(mail::imap &imapAccount, string name,
+ string message,
+ bool okfail, string errmsg)
+{
+ if (name == "STORE")
+ {
+ if (okfail)
+ callback.success(errmsg);
+ else
+ callback.fail(errmsg);
+ imapAccount.uninstallHandler(this);
+ return true;
+ }
+ return false;
+}
+
+bool mail::imap::keywordAllowed(string kwName)
+{
+ upper(kwName);
+
+ return permanentFlags.count(kwName) > 0 ||
+ permanentFlags.count("\\*") > 0;
+}
+
+void mail::imap::saveFolderIndexInfo(size_t messageNum,
+ const mail::messageInfo &indexInfo,
+ mail::callback &callback)
+{
+ if (!ready(callback))
+ return;
+
+ if (messageNum >= getFolderIndexSize())
+ {
+ callback.success("Invalid message number - ignored.");
+ return;
+ }
+
+ if (smap)
+ {
+ string flags;
+
+#define FLAG
+#define NOTFLAG !
+#define DOFLAG(NOT, field, name) \
+ if (NOT indexInfo.field) flags += "," name
+
+ LIBMAIL_SMAPFLAGS;
+#undef DOFLAG
+#undef NOTFLAG
+#undef FLAG
+
+ if (flags.size() > 0)
+ flags=flags.substr(1);
+
+
+ installForegroundTask(new smapSTORE(messageNum,
+ "FLAGS=" + flags,
+ *this,
+ callback));
+ return;
+ }
+
+ bool fakeUpdateFlag;
+
+ string flags=messageFlagStr(indexInfo,
+ &currentFolder->index[messageNum],
+ &fakeUpdateFlag);
+
+ {
+ set<string> oflags;
+ currentFolder->index[messageNum].keywords.getFlags(oflags);
+
+ set<string>::iterator b=oflags.begin(), e=oflags.end();
+
+ while (b != e)
+ {
+ if (keywordAllowed(*b))
+ {
+ if (flags.size() > 0)
+ flags += " ";
+ flags += *b;
+ }
+
+ ++b;
+ }
+ }
+
+ string messageNumBuffer;
+
+ {
+ ostringstream o;
+
+ o << "STORE " << (messageNum+1) << " FLAGS (";
+ messageNumBuffer=o.str();
+ }
+
+ installForegroundTask(new mail::imapSTORE(callback,
+ messageNumBuffer
+ + flags + ")"));
+
+ if (fakeUpdateFlag && messageNum < currentFolder->exists)
+ currentFolder->folderCallback.messageChanged(messageNum);
+}
+
+/////////////////////////////////////////////////////////////////////////
+//
+// A STORE for a list may be broken up into multiple commands.
+// Count the number of tagged STORE acknowledgments received, and run the
+// app callback only after the last STORE ack comes in.
+
+LIBMAIL_START
+
+class imapUpdateFlagsCallback : public mail::callback {
+
+ mail::callback &origCallback;
+
+ void reportProgress(size_t bytesCompleted,
+ size_t bytesEstimatedTotal,
+
+ size_t messagesCompleted,
+ size_t messagesEstimatedTotal);
+
+public:
+ imapUpdateFlagsCallback(mail::callback &callback);
+ ~imapUpdateFlagsCallback();
+
+ void success(string message);
+ void fail(string message);
+
+ bool failFlag;
+ string message;
+
+ size_t cnt;
+};
+
+LIBMAIL_END
+
+mail::imapUpdateFlagsCallback::imapUpdateFlagsCallback(mail::callback
+ &callback)
+ : origCallback(callback),
+ failFlag(false), message(""), cnt(0)
+{
+}
+
+mail::imapUpdateFlagsCallback::~imapUpdateFlagsCallback()
+{
+}
+
+void mail::imapUpdateFlagsCallback::success(string message)
+{
+ // If any failures were received, the app fail() callback will be
+ // invoked.
+
+ if (!failFlag)
+ this->message=message;
+
+ if (--cnt == 0)
+ {
+ if (failFlag)
+ origCallback.fail(message);
+ else
+ origCallback.success(message);
+ delete this;
+ }
+}
+
+void mail::imapUpdateFlagsCallback::fail(string message)
+{
+ this->message=message;
+ failFlag=true;
+ success(message);
+}
+
+void mail::imapUpdateFlagsCallback
+::reportProgress(size_t bytesCompleted,
+ size_t bytesEstimatedTotal,
+
+ size_t messagesCompleted,
+ size_t messagesEstimatedTotal)
+{
+ origCallback.reportProgress(bytesCompleted,
+ bytesEstimatedTotal,
+ messagesCompleted,
+ messagesEstimatedTotal);
+}
+
+void mail::imap::updateFolderIndexFlags(const vector<size_t> &messages,
+ bool doFlip,
+ bool enableDisable,
+ const mail::messageInfo &flags,
+ mail::callback &callback)
+{
+ if (doFlip)
+ {
+ mail::imapUpdateFlagsCallback *myCallback=
+ new mail::imapUpdateFlagsCallback(callback);
+
+ if (!myCallback)
+ {
+ callback.fail("Out of memory.");
+ return;
+ }
+
+ //
+ // Very ugly, but this is the cleanest ugly solution.
+ // For each flipped flag, segregate messages whose
+ // flag should be turned on, and ones that should be
+ // turned off, and issue a separate STORE for each set.
+ // Lather, rinse, repeat, for all flags.
+ //
+
+ try {
+
+#define DOFLAG(NOT, field, name) { \
+ vector<size_t> enabled, disabled;\
+\
+ vector<size_t>::const_iterator b=messages.begin(),\
+ e=messages.end();\
+\
+ while (b != e)\
+ {\
+ size_t n= *b++;\
+\
+ if (n >= getFolderIndexSize())\
+ continue;\
+\
+ mail::messageInfo &info=currentFolder->index[n];\
+\
+ if ( flags.field ) \
+ { \
+ if (info.field)\
+ disabled.push_back(n);\
+ else\
+ enabled.push_back(n);\
+ }\
+ }\
+\
+ mail::messageInfo oneFlag;\
+\
+ oneFlag.field=true;\
+ ++myCallback->cnt;\
+ updateFolderIndexFlags(enabled, true, oneFlag, *myCallback);\
+ ++myCallback->cnt;\
+ updateFolderIndexFlags(disabled, false, oneFlag, *myCallback);\
+ }
+
+ ++myCallback->cnt;
+#define NOTFLAG
+#define FLAG
+ LIBMAIL_MSGFLAGS;
+#undef NOTFLAG
+#undef FLAG
+#undef DOFLAG
+ myCallback->success("Success");
+
+ } catch (...) {
+ delete myCallback;
+
+ callback.fail("An exception occured in updateFolderIndexFlags()");
+ }
+ return;
+ }
+
+ updateFolderIndexFlags(messages, enableDisable, flags, callback);
+}
+
+LIBMAIL_START
+
+class imapMessageCallbackStub : public mail::callback::message {
+
+ mail::callback &origCallback;
+
+ void reportProgress(size_t bytesCompleted,
+ size_t bytesEstimatedTotal,
+
+ size_t messagesCompleted,
+ size_t messagesEstimatedTotal);
+
+public:
+ imapMessageCallbackStub(mail::callback &callbackArg);
+ ~imapMessageCallbackStub();
+
+ void success(string message);
+ void fail(string message);
+};
+
+LIBMAIL_END
+
+mail::imapMessageCallbackStub
+::imapMessageCallbackStub(mail::callback &callbackArg)
+ : origCallback(callbackArg)
+{
+}
+
+mail::imapMessageCallbackStub::~imapMessageCallbackStub()
+{
+}
+
+void mail::imapMessageCallbackStub::success(string message)
+{
+ origCallback.success(message);
+ delete this;
+}
+
+void mail::imapMessageCallbackStub::fail(string message)
+{
+ origCallback.fail(message);
+ delete this;
+}
+
+void mail::imapMessageCallbackStub
+::reportProgress(size_t bytesCompleted,
+ size_t bytesEstimatedTotal,
+ size_t messagesCompleted,
+ size_t messagesEstimatedTotal)
+{
+ origCallback.reportProgress(bytesCompleted, bytesEstimatedTotal,
+ messagesCompleted, messagesEstimatedTotal);
+}
+
+void mail::imap::updateFolderIndexFlags(const vector<size_t> &messages,
+ bool enableDisable,
+ const mail::messageInfo &flags,
+ mail::callback &callback)
+{
+ if (smap)
+ {
+ const char *plus="+FLAGS=";
+ const char *minus="-FLAGS=";
+
+
+ string lista;
+ string listb;
+
+#define FLAG lista
+#define NOTFLAG listb
+#define DOFLAG(FLAG, field, name) \
+ if (flags.field) FLAG += "," name
+
+ LIBMAIL_SMAPFLAGS;
+#undef DOFLAG
+#undef NOTFLAG
+#undef FLAG
+
+ if (lista.size() > 0)
+ lista=(enableDisable ? plus:minus)
+ + lista.substr(1);
+
+ if (listb.size() > 0)
+ listb=(enableDisable ? minus:plus)
+ + listb.substr(1);
+
+
+ installForegroundTask(new smapSTORE(messages,
+ lista + " " + listb,
+ *this,
+ callback));
+ return;
+ }
+
+ const char *plusminus=NULL;
+
+ string name="";
+
+#define DOFLAG(MARK, field, n) if ( flags.field ) { plusminus=MARK; name += " " n; }
+#define FLAG "+-"
+#define NOTFLAG "-+"
+
+ LIBMAIL_MSGFLAGS;
+
+#undef FLAG
+#undef NOTFLAG
+#undef DOFLAG
+
+ if (!plusminus || messages.size() == 0)
+ {
+ callback.success("Ok");
+ return;
+ }
+
+ name=name.substr(1);
+
+ char buf[2];
+
+ buf[0]=plusminus[enableDisable ? 0:1];
+ buf[1]=0;
+
+ mail::imapMessageCallbackStub *c=
+ new mail::imapMessageCallbackStub(callback);
+
+ if (!c)
+ {
+ callback.fail("Out of memory.");
+ return;
+ }
+
+ try {
+ messagecmd(messages, string(" ")
+ + buf + "FLAGS (" + name + ")",
+ "UID STORE", "STORE", *c);
+ } catch (...) {
+ delete c;
+ LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
+ }
+}
+
+class mail::imap::updateImapKeywordHelper : public mail::callback {
+
+ mail::callback *origCallback;
+ mail::ptr<mail::imap> myimap;
+
+public:
+ set<string> newflags;
+ set<string> uids;
+
+ updateImapKeywordHelper(mail::callback *origCallbackArg,
+ mail::imap *myimapArg);
+ ~updateImapKeywordHelper();
+
+ void success(std::string message);
+ void fail(std::string message);
+
+ void reportProgress(size_t bytesCompleted,
+ size_t bytesEstimatedTotal,
+
+ size_t messagesCompleted,
+ size_t messagesEstimatedTotal);
+};
+
+
+
+void mail::imap::updateKeywords(const vector<size_t> &messages,
+ const set<string> &keywords,
+ bool setOrChange,
+ // false: set, true: see changeTo
+ bool changeTo,
+ callback &cb)
+{
+ set<string>::const_iterator b=keywords.begin(), e=keywords.end();
+
+ while (b != e)
+ {
+ string::const_iterator sb=b->begin(), se=b->end();
+
+ while (sb != se)
+ {
+ if (strchr(smap ? ",\t\r\n,":"() \t\r\n[]{}\\\"", *sb))
+ {
+ cb.fail("Invalid flag");
+ return;
+ }
+ ++sb;
+ }
+ ++b;
+ }
+
+ b=keywords.begin();
+ e=keywords.end();
+
+ if (smap)
+ {
+ string setCmd= setOrChange ? changeTo
+ ? "+KEYWORDS=":"-KEYWORDS=":"KEYWORDS=";
+
+ const char *comma="";
+
+ while (b != e)
+ {
+ setCmd += comma;
+ setCmd += *b;
+ ++b;
+ comma=",";
+ }
+
+ installForegroundTask(new smapSTORE(messages,
+ quoteSMAP(setCmd),
+ *this, cb));
+ return;
+ }
+
+ if (!setOrChange)
+ {
+ updateImapKeywordHelper *h=
+ new updateImapKeywordHelper(&cb, this);
+
+ if (!h)
+ {
+ cb.fail(strerror(errno));
+ return;
+ }
+
+ try {
+ h->newflags=keywords;
+
+ map<string, string> allFlags;
+ // Keyed by upper(name), value=name
+ // (be nice, and preserve casing when turning off
+ // flags).
+
+ vector<size_t>::const_iterator mb=messages.begin(),
+ me=messages.end();
+
+ while (mb != me)
+ {
+ size_t n= *mb;
+
+ ++mb;
+
+ if (n >= currentFolder->index.size())
+ continue;
+
+ h->uids.insert(currentFolder->index[n].uid);
+ set<string> tempSet;
+
+ currentFolder->index[n].keywords
+ .getFlags(tempSet);
+
+ set<string>::iterator
+ tsb=tempSet.begin(),
+ tse=tempSet.end();
+
+ while (tsb != tse)
+ {
+ string flagName= *tsb;
+
+ // IMAP kws are case insensitive
+
+ mail::upper(flagName);
+ allFlags.insert(make_pair(flagName,
+ *tsb));
+ ++tsb;
+ }
+ }
+
+ b=keywords.begin();
+ e=keywords.end();
+
+ while (b != e)
+ {
+ string flagName= *b;
+ ++b;
+
+ mail::upper(flagName);
+ allFlags.erase(flagName);
+ // No need to remove flags that are going to be
+ // set anyway.
+ }
+
+ if (allFlags.empty())
+ h->success("Ok - no keywords changed.");
+ // Nothing to turn off, so punt it.
+ else
+ {
+ set<string> allFlagsV;
+
+ map<string, string>::iterator b=
+ allFlags.begin(), e=allFlags.end();
+
+ while (b != e)
+ {
+ allFlagsV.insert(b->second);
+ ++b;
+ }
+
+ allFlags.clear();
+
+ updateImapKeywords(messages, allFlagsV,
+ false, *h);
+ }
+ } catch (...) {
+ delete h;
+ }
+ return;
+ }
+
+ updateImapKeywords(messages, keywords, changeTo, cb);
+}
+
+mail::imap::updateImapKeywordHelper
+::updateImapKeywordHelper(mail::callback *origCallbackArg,
+ mail::imap *myimapArg)
+ : origCallback(origCallbackArg), myimap(myimapArg)
+{
+}
+
+mail::imap::updateImapKeywordHelper::~updateImapKeywordHelper()
+{
+ if (origCallback)
+ origCallback->fail("Aborted.");
+}
+
+void mail::imap::updateImapKeywordHelper::success(std::string message)
+{
+ vector<size_t> msgSet;
+
+ mail::imapFOLDERinfo *f=myimap.isDestroyed() ? NULL:
+ myimap->currentFolder;
+
+ size_t n=f ? f->index.size():0;
+ size_t i;
+
+ for (i=0; i<n; i++)
+ if (uids.count(f->index[i].uid) > 0)
+ msgSet.push_back(i);
+
+ try {
+ myimap->updateImapKeywords(msgSet, newflags, true,
+ *origCallback);
+ origCallback=NULL;
+ delete this;
+ } catch (...) {
+ delete this;
+ }
+}
+
+void mail::imap::updateImapKeywordHelper::fail(std::string message)
+{
+ mail::callback *c=origCallback;
+ origCallback=NULL;
+ delete this;
+
+ c->fail(message);
+}
+
+void mail::imap::updateImapKeywordHelper
+::reportProgress(size_t bytesCompleted,
+ size_t bytesEstimatedTotal,
+ size_t messagesCompleted,
+ size_t messagesEstimatedTotal)
+{
+}
+
+void mail::imap::updateImapKeywords(const vector<size_t> &messages,
+ const set<string> &keywords,
+ bool changeTo,
+ callback &cb)
+{
+ set<string>::const_iterator b=keywords.begin(), e=keywords.end();
+ string setCmd= changeTo ? " +FLAGS (":" -FLAGS (";
+ const char *space="";
+
+ while (b != e)
+ {
+ string flagname= *b;
+
+ if (!keywordAllowed(flagname))
+ {
+ vector<size_t>::const_iterator mb, me;
+
+ mb=messages.begin();
+ me=messages.end();
+
+ while (mb != me)
+ {
+ size_t n= *mb;
+ ++mb;
+
+ if (n >= (currentFolder ?
+ currentFolder->index.size():0))
+ continue;
+
+ if (changeTo)
+ {
+ if (!currentFolder->index[n]
+ .keywords.addFlag(keywordHashtable,
+ flagname))
+ {
+ LIBMAIL_THROW(strerror(errno));
+ }
+ }
+ else
+ {
+ if (!currentFolder->index[n].keywords
+ .remFlag(flagname))
+ LIBMAIL_THROW(strerror(errno));
+ }
+ }
+ ++b;
+ continue;
+ }
+
+ setCmd += space;
+ setCmd += *b;
+ ++b;
+ space=" ";
+ }
+
+ if (*space == 0)
+ {
+ MONITOR(mail::imap);
+
+ vector<size_t>::const_iterator mb, me;
+
+ mb=messages.begin();
+ me=messages.end();
+
+ while (!DESTROYED() && mb != me)
+ {
+ size_t n= *--me;
+
+ if (n >= (currentFolder ?
+ currentFolder->index.size():0))
+ continue;
+
+ currentFolder->folderCallback.messageChanged(n);
+ }
+
+ cb.success("Ok.");
+ return;
+ }
+
+
+ mail::imapMessageCallbackStub *c=
+ new mail::imapMessageCallbackStub(cb);
+
+ if (!c)
+ {
+ cb.fail("Out of memory.");
+ return;
+ }
+
+ try {
+ messagecmd(messages, setCmd + ")",
+ "UID STORE", "STORE", *c);
+ } catch (...) {
+ delete c;
+ LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+
+void mail::imap::copyMessagesTo(const vector<size_t> &messages,
+ mail::folder *copyTo,
+ mail::callback &callback)
+{
+ sameServerFolderPtr=NULL;
+
+ copyTo->sameServerAsHelperFunc();
+
+ if (!sameServerFolderPtr)
+ {
+ // Copy to some other server, use a generic function.
+
+ mail::copyMessages::copy(this, messages, copyTo, callback);
+ return;
+ }
+
+ if (smap)
+ {
+ installForegroundTask(new smapCOPY(messages, copyTo,
+ *this,
+ callback, "COPY"));
+ return;
+ }
+
+ mail::imapMessageCallbackStub *c=
+ new mail::imapMessageCallbackStub(callback);
+
+ if (!c)
+ {
+ callback.fail("Out of memory.");
+ return;
+ }
+
+ try {
+ messagecmd(messages, " " + quoteSimple(copyTo->getPath()),
+ "UID COPY", "COPY", *c);
+ } catch (...) {
+ delete c;
+ LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
+ }
+
+}
+
+void mail::imap::moveMessagesTo(const vector<size_t> &messages,
+ mail::folder *copyTo,
+ mail::callback &callback)
+{
+ sameServerFolderPtr=NULL;
+
+ copyTo->sameServerAsHelperFunc();
+
+ if (smap && sameServerFolderPtr)
+ {
+ installForegroundTask(new smapCOPY(messages, copyTo,
+ *this,
+ callback, "MOVE"));
+ return;
+ }
+
+ generic::genericMoveMessages(this, messages, copyTo, callback);
+}
+
+///////////////////////////////////////////////////////////////////////////
+//
+// The search command.
+
+LIBMAIL_START
+
+class imapSEARCH : public imapCommandHandler {
+
+ string searchCmd;
+ bool hasStringArg;
+ string stringArg;
+
+ mail::searchCallback &callback;
+
+ vector<size_t> found;
+
+ class SearchResults : public imapHandlerStructured {
+ vector<size_t> &found;
+ public:
+ SearchResults(vector<size_t> &);
+ ~SearchResults();
+
+ void installed(imap &imapAccount);
+ private:
+ const char *getName();
+ void timedOut(const char *errmsg);
+
+ void process(imap &imapAccount, Token t);
+ };
+
+public:
+ imapSEARCH(string searchCmdArg,
+ bool hasParam2,
+ string param2Arg,
+ mail::searchCallback &callbackArg);
+ ~imapSEARCH();
+
+ void installed(imap &);
+ const char *getName();
+ void timedOut(const char *errmsg);
+
+ bool untaggedMessage(imap &imapAccount, string name);
+ bool taggedMessage(imap &imapAccount, string name,
+ string message,
+ bool okfail, string errmsg);
+ bool continuationRequest(imap &imapAccount, string request);
+};
+
+LIBMAIL_END
+
+mail::imapSEARCH::imapSEARCH(string searchCmdArg,
+ bool hasParam2,
+ string param2Arg,
+ mail::searchCallback &callbackArg)
+ : searchCmd(searchCmdArg),
+ hasStringArg(hasParam2),
+ stringArg(param2Arg),
+ callback(callbackArg)
+{
+}
+
+mail::imapSEARCH::~imapSEARCH()
+{
+}
+
+void mail::imapSEARCH::installed(mail::imap &imapAccount)
+{
+ if (hasStringArg)
+ {
+ string::iterator b=stringArg.begin(), e=stringArg.end();
+
+ while (b != e)
+ {
+ if (*b < ' ' || *b >= 127)
+ break;
+ b++;
+ }
+
+ if (b == e)
+ searchCmd += mail::imap::quoteSimple(stringArg);
+ else
+ {
+ ostringstream o;
+
+ o << "{" << stringArg.size() << "}";
+
+ searchCmd += o.str();
+ }
+ }
+ imapAccount.imapcmd("SEARCH", "SEARCH " + searchCmd + "\r\n");
+}
+
+//
+// High-8 search string must be sent via a literal.
+//
+
+bool mail::imapSEARCH::continuationRequest(mail::imap &imapAccount, string request)
+{
+ imapAccount.socketWrite(stringArg + "\r\n");
+ return true;
+}
+
+const char *mail::imapSEARCH::getName()
+{
+ return "SEARCH";
+}
+
+void mail::imapSEARCH::timedOut(const char *errmsg)
+{
+ callback.fail(errmsg);
+}
+
+bool mail::imapSEARCH::untaggedMessage(mail::imap &imapAccount, string name)
+{
+ if (name == "SEARCH")
+ {
+ imapAccount.installBackgroundTask(new SearchResults(found));
+ return true;
+ }
+ return false;
+}
+
+bool mail::imapSEARCH::taggedMessage(mail::imap &imapAccount, string name,
+ string message,
+ bool okfail, string errmsg)
+{
+ if (name != "SEARCH")
+ return false;
+
+ if (okfail)
+ callback.success(found);
+ else
+ callback.fail(errmsg);
+
+ imapAccount.uninstallHandler(this);
+ return true;
+}
+
+mail::imapSEARCH::SearchResults::SearchResults(vector <size_t> &foundArg)
+ : found(foundArg)
+{
+}
+
+mail::imapSEARCH::SearchResults::~SearchResults()
+{
+}
+
+void mail::imapSEARCH::SearchResults::installed(mail::imap &imapAccount)
+{
+}
+
+const char *mail::imapSEARCH::SearchResults::getName()
+{
+ return "* SEARCH";
+}
+
+void mail::imapSEARCH::SearchResults::timedOut(const char *errmsg)
+{
+}
+
+void mail::imapSEARCH::SearchResults::process(mail::imap &imapAccount, Token t)
+{
+ if (t == EOL)
+ return;
+
+ if (t != ATOM)
+ {
+ error(imapAccount);
+ return;
+ }
+
+ size_t n=0;
+
+ istringstream i(t.text.c_str());
+
+ i >> n;
+
+ if (n == 0 || imapAccount.currentFolder == NULL
+ || n > imapAccount.currentFolder->index.size())
+ {
+ error(imapAccount);
+ return;
+ }
+
+ found.push_back(n-1);
+}
+
+void mail::imap::searchMessages(const class mail::searchParams &searchInfo,
+ class mail::searchCallback &callback)
+{
+ if (smap)
+ {
+ installForegroundTask(new mail::smapSEARCH(searchInfo,
+ callback,
+ *this));
+ return;
+ }
+
+ if (folderFlags.count("\\FLAGGED") == 0)
+ {
+ // Server doesn't implement \Flagged, do everything by hand
+
+ mail::searchMessages::search(callback, searchInfo, this);
+ return;
+ }
+
+ string cmd="";
+
+ if (searchInfo.charset.size() > 0)
+ cmd="CHARSET " + quoteSimple(searchInfo.charset) + " ";
+
+ switch (searchInfo.scope) {
+
+ case searchParams::search_all:
+ cmd += "ALL";
+ break;
+
+ case searchParams::search_unmarked:
+ cmd += "ALL NOT FLAGGED";
+ break;
+
+ case searchParams::search_marked:
+ cmd += "ALL FLAGGED";
+ break;
+ case searchParams::search_range:
+
+ {
+ ostringstream o;
+
+ o << searchInfo.rangeLo+1 << ':'
+ << searchInfo.rangeHi;
+
+ cmd += o.str();
+ }
+ break;
+ }
+
+ bool notFlag=searchInfo.searchNot;
+
+ if (searchInfo.criteria == searchInfo.unread)
+ notFlag= !notFlag;
+
+ if (notFlag)
+ cmd += " NOT";
+
+ string sizeNumStr="";
+
+ switch (searchInfo.criteria) {
+ case searchParams::larger:
+ case searchParams::smaller:
+
+ {
+ unsigned long sizeNum=0;
+
+ istringstream i(searchInfo.param2.c_str());
+
+ i >> sizeNum;
+
+ if (i.fail())
+ {
+ callback.fail("Invalid message size string.");
+ return;
+ }
+
+ string buffer;
+
+ {
+ ostringstream o;
+
+ o << sizeNum;
+ buffer=o.str();
+ }
+
+ sizeNumStr=buffer;
+ }
+ break;
+ default:
+ break;
+ }
+
+ string param2="";
+ bool hasParam2=false;
+
+ string::const_iterator b, e;
+
+ switch (searchInfo.criteria) {
+ case searchParams::replied:
+ cmd += " ANSWERED";
+ break;
+ case searchParams::deleted:
+ cmd += " DELETED";
+ break;
+ case searchParams::draft:
+ cmd += " DRAFT";
+ break;
+ case searchParams::unread:
+ cmd += " SEEN";
+ break;
+
+ // Header match
+
+ case searchParams::from:
+ cmd += " FROM "; hasParam2=true; param2=searchInfo.param2;
+ break;
+ case searchParams::to:
+ cmd += " TO "; hasParam2=true; param2=searchInfo.param2;
+ break;
+ case searchParams::cc:
+ cmd += " CC "; hasParam2=true; param2=searchInfo.param2;
+ break;
+ case searchParams::bcc:
+ cmd += " BCC "; hasParam2=true; param2=searchInfo.param2;
+ break;
+ case searchParams::subject:
+ cmd += " SUBJECT "; hasParam2=true; param2=searchInfo.param2;
+ break;
+
+ case searchParams::header:
+
+ b=searchInfo.param1.begin();
+ e=searchInfo.param1.end();
+
+ while (b != e)
+ {
+ if (*b < ' '|| *b >= 127)
+ {
+ callback.fail("Invalid header name.");
+ return;
+ }
+ }
+
+ cmd += " HEADER " + quoteSimple(searchInfo.param1)
+ + " "; hasParam2=true; param2=searchInfo.param2;
+ break;
+ case searchParams::body:
+ cmd += " BODY "; hasParam2=true; param2=searchInfo.param2;
+ break;
+ case searchParams::text:
+ cmd += " TEXT "; hasParam2=true; param2=searchInfo.param2;
+ break;
+
+ // Internal date:
+
+ case searchParams::before:
+ cmd += " BEFORE "; hasParam2=true; param2=searchInfo.param2;
+ break;
+ case searchParams::on:
+ cmd += " ON "; hasParam2=true; param2=searchInfo.param2;
+ break;
+ case searchParams::since:
+ cmd += " SINCE "; hasParam2=true; param2=searchInfo.param2;
+ break;
+
+ // Sent date:
+
+ case searchParams::sentbefore:
+ cmd += " SENTBEFORE "; hasParam2=true; param2=searchInfo.param2;
+ break;
+ case searchParams::senton:
+ cmd += " SENTON "; hasParam2=true; param2=searchInfo.param2;
+ break;
+ case searchParams::sentsince:
+ cmd += " SENTSINCE "; hasParam2=true; param2=searchInfo.param2;
+ break;
+
+ case searchParams::larger:
+ cmd += " LARGER " + sizeNumStr;
+ break;
+ case searchParams::smaller:
+ cmd += " SMALLER " + sizeNumStr;
+ break;
+ default:
+ callback.fail("Unknown search criteria.");
+ return;
+ }
+
+ installForegroundTask(new mail::imapSEARCH(cmd, hasParam2, param2,
+ callback));
+}
+
+
+///////////////////////////////////////////////////////////////////////////
+//
+// Convert flags in mail::messageInfo to IMAP flag names.
+// For those flags that are not supported on the server, update the
+// flags in the folder index manually, and set the flagsUpdated flag.
+
+string mail::imap::messageFlagStr(const mail::messageInfo &indexInfo,
+ mail::messageInfo *oldIndexInfo,
+ bool *flagsUpdated)
+{
+ string flags="";
+
+ if (flagsUpdated)
+ *flagsUpdated=false;
+
+#define DOFLAG(NOT, field, name) \
+ if (oldIndexInfo == NULL /* APPEND assumes all flags are present */ ||\
+ folderFlags.count( name ) > 0)\
+ {\
+ if (NOT indexInfo.field)\
+ flags += " " name;\
+ }\
+ else if ( oldIndexInfo->field != indexInfo.field)\
+ {\
+ oldIndexInfo->field=indexInfo.field;\
+ *flagsUpdated=true;\
+ }
+
+#define FLAG
+#define NOTFLAG !
+
+ LIBMAIL_MSGFLAGS;
+
+#undef FLAG
+#undef DOFLAG
+#undef NOTFLAG
+
+
+ if (flags.size() > 0)
+ flags=flags.substr(1);
+
+ return flags;
+}
+
+////////////////////////////////////////////////////////////////////////////
+//
+// The IMAP EXPUNGE command.
+
+LIBMAIL_START
+
+class imapEXPUNGE : public imapCommandHandler {
+
+ mail::callback &callback;
+
+public:
+ imapEXPUNGE(mail::callback &callbackArg);
+ ~imapEXPUNGE();
+
+ void installed(imap &imapAccount);
+
+private:
+ const char *getName();
+ void timedOut(const char *errmsg);
+
+ bool untaggedMessage(imap &imapAccount, string name);
+ bool taggedMessage(imap &imapAccount, string name,
+ string message,
+ bool okfail, string errmsg);
+};
+
+LIBMAIL_END
+
+mail::imapEXPUNGE::imapEXPUNGE(mail::callback &callbackArg)
+ : callback(callbackArg)
+{
+}
+
+void mail::imapEXPUNGE::timedOut(const char *errmsg)
+{
+ callbackTimedOut(callback, errmsg);
+}
+
+mail::imapEXPUNGE::~imapEXPUNGE()
+{
+}
+
+void mail::imapEXPUNGE::installed(mail::imap &imapAccount)
+{
+ imapAccount.imapcmd("EXPUNGE", "EXPUNGE\r\n");
+}
+
+const char *mail::imapEXPUNGE::getName()
+{
+ return "EXPUNGE";
+}
+
+bool mail::imapEXPUNGE::untaggedMessage(mail::imap &imapAccount, string name)
+{
+ return false;
+}
+
+bool mail::imapEXPUNGE::taggedMessage(mail::imap &imapAccount, string name,
+ string message,
+ bool okfail, string errmsg)
+{
+ if (name == "EXPUNGE")
+ {
+ if (okfail)
+ callback.success(errmsg);
+ else
+ callback.fail(errmsg);
+ imapAccount.uninstallHandler(this);
+ return true;
+ }
+ return false;
+}
+
+void mail::imap::updateNotify(bool enableDisable, callback &callbackArg)
+{
+ if (!ready(callbackArg))
+ return;
+
+ wantIdleMode=enableDisable;
+ updateNotify(&callbackArg);
+}
+
+void mail::imap::updateNotify(callback *callbackArg)
+{
+ if (!smap && (!hasCapability("IDLE") || noIdle))
+ {
+ if (callbackArg)
+ callbackArg->success("Ok");
+ return;
+ }
+
+ installForegroundTask(smap ?
+ (imapHandler *)new smapIdleHandler(wantIdleMode,
+ callbackArg):
+ new imapIdleHandler(wantIdleMode, callbackArg));
+}
+
+void mail::imap::updateFolderIndexInfo(mail::callback &callback)
+{
+ if (!ready(callback))
+ return;
+
+ installForegroundTask(smap ?
+ (imapHandler *)new smapNoopExpunge("EXPUNGE",
+ callback,
+ *this)
+ : new mail::imapEXPUNGE(callback));
+}
+
+//////////////////////////////////////////////////////////////////////////
+//
+// UID EXPUNGE
+
+////////////////////////////////////////////////////////////////////////////
+//
+// The IMAP EXPUNGE command.
+
+LIBMAIL_START
+
+class imapUIDEXPUNGE : public mail::callback::message {
+
+ set<string> uids;
+
+ ptr<mail::imap> acct;
+ mail::callback &callback;
+
+ bool uidSent;
+
+public:
+
+ imapUIDEXPUNGE(mail::imap &imapAccount,
+ mail::callback &callbackArg,
+ const vector<size_t> &msgs);
+ ~imapUIDEXPUNGE();
+
+ void mkVector(mail::imap &, vector<size_t> &);
+
+private:
+ void success(string msg);
+ void fail(string msg);
+
+ void reportProgress(size_t bytesCompleted,
+ size_t bytesEstimatedTotal,
+
+ size_t messagesCompleted,
+ size_t messagesEstimatedTotal);
+
+};
+
+LIBMAIL_END
+
+mail::imapUIDEXPUNGE::imapUIDEXPUNGE(mail::imap &imapAccount,
+ mail::callback &callbackArg,
+ const vector<size_t> &msgs)
+ : acct(&imapAccount), callback(callbackArg), uidSent(false)
+{
+ vector<size_t>::const_iterator b, e;
+
+ for (b=msgs.begin(), e=msgs.end(); b != e; b++)
+ {
+ if (imapAccount.currentFolder &&
+ *b < imapAccount.currentFolder->index.size())
+ uids.insert(imapAccount.currentFolder
+ ->index[*b].uid);
+ }
+}
+
+mail::imapUIDEXPUNGE::~imapUIDEXPUNGE()
+{
+}
+
+void mail::imapUIDEXPUNGE::mkVector(mail::imap &imapAccount,
+ vector<size_t> &vec)
+{
+ if (imapAccount.currentFolder)
+ {
+ size_t i;
+
+ for (i=0; i<imapAccount.currentFolder->exists; i++)
+ {
+ if (uids.count(imapAccount.currentFolder
+ ->index[i].uid) > 0)
+ vec.push_back(i);
+ }
+ }
+}
+
+void mail::imapUIDEXPUNGE::success(string dummy)
+{
+ if (acct.isDestroyed() || uidSent)
+ {
+ callback.success(dummy);
+ delete this;
+ return;
+ }
+
+ vector<size_t> vec;
+
+ mkVector(*acct, vec);
+
+ if (vec.size() == 0)
+ {
+ mail::callback * volatile c= &callback;
+
+ delete this;
+
+ c->fail(dummy);
+ return;
+ }
+
+ uidSent=true;
+ acct->messagecmd(vec, "", "UID EXPUNGE", "EXPUNGE", *this);
+}
+
+void mail::imapUIDEXPUNGE::reportProgress(size_t bytesCompleted,
+ size_t bytesEstimatedTotal,
+
+ size_t messagesCompleted,
+ size_t messagesEstimatedTotal)
+{
+}
+
+void mail::imapUIDEXPUNGE::fail(string dummy)
+{
+ mail::callback * volatile c= &callback;
+
+ delete this;
+
+ c->fail(dummy);
+}
+
+void mail::imap::removeMessages(const std::vector<size_t> &messages,
+ callback &cb)
+{
+ if (!ready(cb))
+ return;
+
+ if (smap)
+ {
+ installForegroundTask(new smapNoopExpunge(messages, cb,
+ *this));
+ return;
+ }
+
+ if (!hasCapability("UIDPLUS"))
+ {
+ mail::generic::genericRemoveMessages(this, messages, cb);
+ return;
+ }
+
+ imapUIDEXPUNGE *exp=new imapUIDEXPUNGE(*this, cb, messages);
+
+ try {
+ vector<size_t> msgs;
+
+ exp->mkVector(*this, msgs);
+
+ if (msgs.size() == 0)
+ {
+ delete exp;
+ exp=NULL;
+
+ cb.success("OK");
+ return;
+ }
+
+ messagecmd(msgs, " +FLAGS.SILENT (\\Deleted)",
+ "UID STORE", "STORE", *exp);
+ } catch (...) {
+ if (exp)
+ delete exp;
+
+ cb.fail("An exception occured in removeMessages");
+ return;
+ }
+}