summaryrefslogtreecommitdiffstats
path: root/libmail/imapfolders.C
diff options
context:
space:
mode:
Diffstat (limited to 'libmail/imapfolders.C')
-rw-r--r--libmail/imapfolders.C1637
1 files changed, 1637 insertions, 0 deletions
diff --git a/libmail/imapfolders.C b/libmail/imapfolders.C
new file mode 100644
index 0000000..2a3fab5
--- /dev/null
+++ b/libmail/imapfolders.C
@@ -0,0 +1,1637 @@
+/*
+** Copyright 2002-2004, Double Precision Inc.
+**
+** See COPYING for distribution information.
+*/
+#include "libmail_config.h"
+#include "addmessage.H"
+#include "imap.H"
+#include "misc.H"
+#include "imapacl.H"
+#include "imapfolder.H"
+#include "imapfolders.H"
+#include "imaplisthandler.H"
+
+#include "smapacl.H"
+#include "smapcreate.H"
+#include "smapdelete.H"
+#include "smapaddmessage.H"
+#include "smapsendfolder.H"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sstream>
+#include "unicode/unicode.h"
+#include "rfc822/rfc822.h"
+#include "maildir/maildiraclt.h"
+
+using namespace std;
+
+//
+// Top level folders -- this stuff is taken from the IMAP NAMESPACE messages
+// at login.
+
+void mail::imap::readTopLevelFolders(mail::callback::folderList &callback1,
+ mail::callback &callback2)
+{
+ vector<const mail::folder *> folders;
+ vector<mail::imapFolder>::iterator b, e;
+
+ b=namespaces.begin();
+ e=namespaces.end();
+
+ while (b != e)
+ {
+ folders.push_back(&*b);
+ b++;
+ }
+
+ callback1.success(folders);
+ callback2.success("OK");
+}
+
+mail::folder *mail::imap::folderFromString(string s)
+{
+ string words[4];
+
+ words[0]="";
+ words[1]="";
+ words[2]="";
+ words[3]="";
+ int i=0;
+
+ string::iterator b=s.begin(), e=s.end();
+
+ while (b != e)
+ {
+ if (*b == ':')
+ {
+ b++;
+ ++i;
+ continue;
+ }
+
+ if (*b == '\\')
+ {
+ b++;
+ if (b == e)
+ break;
+ }
+
+ if (i < 4)
+ words[i].append(&*b, 1);
+ b++;
+ }
+
+ mail::imapFolder *f=new mail::imapFolder(*this,
+ words[0],
+ words[1],
+ words[2],
+ -1);
+
+ if (f == NULL)
+ return NULL;
+
+ f->hasMessages(words[3].find('S') != std::string::npos);
+ f->hasSubFolders(words[3].find('C') != std::string::npos);
+
+ return f;
+}
+
+mail::imapFolder::imapFolder(mail::imap &myImap, string pathArg,
+ string hierArg, string nameArg,
+ int typeArg)
+ : mail::folder(&myImap),
+ imapAccount(myImap), path(pathArg), hiersep(hierArg), name(nameArg),
+ hasmessages(true),
+ hassubfolders(false), type(typeArg)
+{
+}
+
+mail::imapFolder::imapFolder(const mail::imapFolder &c)
+ : mail::folder(&c.imapAccount), imapAccount(c.imapAccount)
+{
+ (*this)=c;
+}
+
+mail::imapFolder &mail::imapFolder::operator=(const mail::imapFolder &c)
+{
+ mail::folder::operator=(c);
+ path=c.path;
+ hiersep=c.hiersep;
+ name=c.name;
+
+ hasmessages=c.hasmessages;
+ hassubfolders=c.hassubfolders;
+ type=c.type;
+ return *this;
+}
+
+mail::imapFolder::~imapFolder()
+{
+}
+
+void mail::imapFolder::hasMessages(bool flag)
+{
+ hasmessages=flag;
+}
+
+void mail::imapFolder::hasSubFolders(bool flag)
+{
+ hassubfolders=flag;
+}
+
+string mail::imapFolder::getName() const
+{
+ return name;
+}
+
+string mail::imapFolder::getPath() const
+{
+ return path;
+}
+
+bool mail::imapFolder::isParentOf(string cpath) const
+{
+ if (isDestroyed())
+ return false;
+
+ string pfix=path + hiersep;
+
+ if (imapAccount.smap)
+ pfix += "/"; // SMAP implies this
+
+ return (cpath.size() > pfix.size() &&
+ pfix == cpath.substr(0, pfix.size()));
+}
+
+bool mail::imapFolder::hasMessages() const
+{
+ return hasmessages;
+}
+
+bool mail::imapFolder::hasSubFolders() const
+{
+ return hassubfolders;
+}
+
+void mail::imapFolder::getParentFolder(callback::folderList &callback1,
+ callback &callback2) const
+{
+ if (isDestroyed(callback2))
+ return;
+
+ string sep=hiersep;
+
+ if (imapAccount.smap)
+ sep="/"; // SMAP implied
+
+ if (sep.size() > 0)
+ {
+ size_t n=path.rfind(sep[0]);
+
+ if (n != std::string::npos)
+ {
+ mail::account &a=imapAccount;
+
+ a.findFolder(path.substr(0, n),
+ callback1, callback2);
+ return;
+ }
+ }
+
+ mail::imapFolder topFolder( *this );
+
+ topFolder.path="";
+ topFolder.hiersep="";
+ topFolder.hasmessages=false;
+ topFolder.hassubfolders=true;
+ topFolder.name="";
+
+ vector<const mail::folder *> array;
+
+ array.push_back(&topFolder);
+
+ callback1.success(array);
+ callback2.success("OK");
+}
+
+void mail::imapFolder::readSubFolders(mail::callback::folderList &callback1,
+ mail::callback &callback2)
+ const
+{
+ if (isDestroyed(callback2))
+ return;
+
+ imapAccount.readSubFolders(path + hiersep, callback1, callback2);
+}
+
+mail::addMessage *mail::imapFolder::addMessage(mail::callback &callback) const
+{
+ if (isDestroyed(callback))
+ return NULL;
+
+ return imapAccount.addMessage(path, callback);
+}
+
+void mail::imapFolder::readFolderInfo(mail::callback::folderInfo &callback1,
+ mail::callback &callback2) const
+{
+ if (isDestroyed(callback2))
+ return;
+
+ if (imapAccount.currentFolder &&
+ imapAccount.currentFolder->path == path)
+ {
+ imapFOLDERinfo *f=imapAccount.currentFolder;
+
+ vector<imapFOLDERinfo::indexInfo>::iterator b, e;
+
+ b=f->index.begin();
+ e=f->index.end();
+
+ callback1.messageCount=f->index.size();
+ callback1.unreadCount=0;
+
+ while (b != e)
+ {
+ if (b->unread)
+ ++callback1.unreadCount;
+ b++;
+ }
+ callback1.success();
+ callback2.success("OK");
+ return;
+ }
+
+ if (!imapAccount.smap && callback1.fastInfo &&
+ !imapAccount.hasCapability(LIBMAIL_CHEAPSTATUS))
+ {
+ callback2.success("OK");
+ return;
+ }
+
+ imapAccount.folderStatus(path, callback1, callback2);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// CREATE a subfolder of an existing folder. First, issue a LIST to
+// determine the eventual hierarchy separator character, followed by a CREATE.
+//
+// imapCREATE is also commandeered to rename a folder, since the processing
+// is rather similar.
+
+LIBMAIL_START
+
+class imapCREATE : public imapCommandHandler {
+
+ string renamePath; // If not empty, this is a rename
+ string parentPath;
+ string name;
+ string encodedname;
+ bool hasMessages;
+ bool hasSubfolders;
+ mail::callback::folderList &callback1;
+ mail::callback &callback2;
+
+ bool listHierSent;
+ vector <imapFolder> hiersep; // Results of LIST for the hier sep
+
+public:
+ imapCREATE(string parentPathArg, // path + hiersep
+ string nameArg,
+ bool hasMessagesArg,
+ bool hasSubfoldersArg,
+ mail::callback::folderList &callback1Arg,
+ mail::callback &callback2Arg);
+
+ imapCREATE(string renamePathArg,
+ string parentPathArg, // path + hiersep
+ string nameArg,
+ bool hasMessagesArg,
+ bool hasSubfoldersArg,
+ mail::callback::folderList &callback1Arg,
+ mail::callback &callback2Arg);
+
+ void installed(imap &imapAccount);
+
+private:
+ const char *getName();
+ void timedOut(const char *errmsg);
+
+ bool untaggedMessage(imap &imapAccount, string msgname);
+
+ bool taggedMessage(imap &imapAccount, string msgname,
+ string message,
+ bool okfail, string errmsg);
+};
+
+LIBMAIL_END
+
+mail::imapCREATE::imapCREATE(string parentPathArg,
+ string nameArg,
+ bool hasMessagesArg,
+ bool hasSubfoldersArg,
+ mail::callback::folderList &callback1Arg,
+ mail::callback &callback2Arg)
+ : renamePath(""),
+ parentPath(parentPathArg),
+ name(nameArg),
+ hasMessages(hasMessagesArg),
+ hasSubfolders(hasSubfoldersArg),
+ callback1(callback1Arg),
+ callback2(callback2Arg),
+ listHierSent(false)
+{
+}
+
+mail::imapCREATE::imapCREATE(string renamePathArg,
+ string parentPathArg,
+ string nameArg,
+ bool hasMessagesArg,
+ bool hasSubfoldersArg,
+ mail::callback::folderList &callback1Arg,
+ mail::callback &callback2Arg)
+ : renamePath(renamePathArg),
+ parentPath(parentPathArg),
+ name(nameArg),
+ hasMessages(hasMessagesArg),
+ hasSubfolders(hasSubfoldersArg),
+ callback1(callback1Arg),
+ callback2(callback2Arg),
+ listHierSent(false)
+{
+}
+
+void mail::imapCREATE::installed(mail::imap &imapAccount)
+{
+ // First things first, get the hier separator.
+
+ imapAccount.imapcmd("CREATE", "LIST "
+ + imapAccount.quoteSimple(parentPath + "tree")
+ + " \"\"\r\n");
+}
+
+const char *mail::imapCREATE::getName()
+{
+ return "CREATE";
+}
+
+void mail::imapCREATE::timedOut(const char *errmsg)
+{
+ callbackTimedOut(callback2, errmsg);
+}
+
+bool mail::imapCREATE::untaggedMessage(mail::imap &imapAccount, string msgname)
+{
+ if (msgname == "LIST") // Untagged LIST replies
+ {
+ hiersep.clear();
+ imapAccount.installBackgroundTask( new mail::imapLIST(hiersep,
+ 0, true));
+ return true;
+ }
+ return false;
+}
+
+bool mail::imapCREATE::taggedMessage(mail::imap &imapAccount, string msgname,
+ string message,
+ bool okfail, string errmsg)
+{
+ if (msgname != "CREATE")
+ return false;
+
+ if (!okfail)
+ {
+ callback2.fail(errmsg);
+ imapAccount.uninstallHandler(this);
+ return true;
+ }
+
+ if (!listHierSent)
+ {
+ if (hiersep.size() == 0)
+ {
+ callback2.fail("IMAP server failed to process the LIST command.");
+ imapAccount.uninstallHandler(this);
+ return true;
+ }
+
+
+ listHierSent=true;
+ encodedname=name;
+
+ // The folder name should be utf7-encoded
+
+ {
+ char *p=libmail_u_convert_toutf8(encodedname.c_str(),
+ unicode_default_chset(),
+ NULL);
+
+ if (!p && strchr(p, *hiersep[0].getHierSep().c_str())
+ != NULL)
+ {
+ if (p)
+ free(p);
+ callback2.fail("Folder name contains an invalid character.");
+ imapAccount.uninstallHandler(this);
+ return true;
+ }
+ free(p);
+
+
+ p=libmail_u_convert_tobuf(encodedname.c_str(),
+ unicode_default_chset(),
+ unicode_x_imap_modutf7,
+ NULL);
+
+ if (p)
+ {
+ try {
+ encodedname=p;
+ free(p);
+ } catch (...) {
+ free(p);
+ LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
+ }
+ }
+ }
+
+
+ imapAccount.imapcmd("CREATE",
+ (renamePath.size() > 0
+ ? string("RENAME ") +
+ imapAccount.quoteSimple(renamePath) + " ":
+ string("CREATE ")) +
+ imapAccount
+ .quoteSimple(parentPath +
+ encodedname +
+ (!hasMessages ?
+ hiersep[0].getHierSep()
+ :""))
+ + "\r\n");
+ return true;
+
+ }
+
+ mail::imapFolder new_folder(imapAccount, parentPath + encodedname,
+ hiersep[0].getHierSep(),
+ name, -1);
+
+ new_folder.hasMessages(hasMessages);
+ new_folder.hasSubFolders(hasSubfolders);
+
+ vector<const mail::folder *> dummy_list;
+
+ dummy_list.push_back(&new_folder);
+
+ callback1.success(dummy_list);
+ callback2.success(errmsg);
+ imapAccount.uninstallHandler(this);
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Creating a previously known folder, from scratch. This is much easier.
+
+LIBMAIL_START
+
+class imapRECREATE : public imapCommandHandler {
+
+ string path;
+ mail::callback &callback;
+
+public:
+ imapRECREATE(string pathArg, mail::callback &callbackArg);
+ ~imapRECREATE();
+
+ void installed(imap &imapAccount);
+
+ const char *getName();
+ void timedOut(const char *errmsg);
+
+ bool untaggedMessage(imap &imapAccount, string msgname);
+
+ bool taggedMessage(imap &imapAccount, string msgname,
+ string message,
+ bool okfail, string errmsg);
+};
+
+LIBMAIL_END
+
+mail::imapRECREATE::imapRECREATE(string pathArg,
+ mail::callback &callbackArg)
+ : path(pathArg), callback(callbackArg)
+{
+}
+
+mail::imapRECREATE::~imapRECREATE()
+{
+}
+
+void mail::imapRECREATE::installed(mail::imap &imapAccount)
+{
+ imapAccount.imapcmd("CREATE", "CREATE "
+ + imapAccount.quoteSimple(path) + "\r\n");
+}
+
+const char *mail::imapRECREATE::getName()
+{
+ return "CREATE";
+}
+
+void mail::imapRECREATE::timedOut(const char *errmsg)
+{
+ callbackTimedOut(callback, errmsg);
+}
+
+bool mail::imapRECREATE::untaggedMessage(mail::imap &imapAccount, string msgname)
+{
+ return false;
+}
+
+bool mail::imapRECREATE::taggedMessage(mail::imap &imapAccount, string msgname,
+ string message,
+ bool okfail, string errmsg)
+{
+ if (msgname == "CREATE")
+ {
+ if (okfail)
+ callback.success(errmsg);
+ else
+ callback.fail(errmsg);
+ imapAccount.uninstallHandler(this);
+ return true;
+ }
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////
+//
+// DELETE a folder.
+
+LIBMAIL_START
+
+class imapDELETE : public imapCommandHandler {
+ mail::callback &callback;
+ string path;
+
+public:
+ imapDELETE(mail::callback &callbackArg,
+ string pathArg);
+
+ 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::imapDELETE::imapDELETE(mail::callback &callbackArg,
+ string pathArg)
+ : callback(callbackArg), path(pathArg)
+{
+}
+
+void mail::imapDELETE::installed(mail::imap &imapAccount)
+{
+ imapAccount.imapcmd("DELETE", "DELETE " + imapAccount.quoteSimple(path)
+ + "\r\n");
+}
+
+const char *mail::imapDELETE::getName()
+{
+ return "DELETE";
+}
+
+void mail::imapDELETE::timedOut(const char *errmsg)
+{
+ callbackTimedOut(callback, errmsg);
+}
+
+bool mail::imapDELETE::untaggedMessage(mail::imap &imapAccount, string name)
+{
+ return false;
+}
+
+bool mail::imapDELETE::taggedMessage(mail::imap &imapAccount, string name,
+ string message,
+ bool okfail, string errmsg)
+{
+ if (name == "DELETE")
+ {
+ if (okfail)
+ callback.success(errmsg);
+ else
+ callback.fail(errmsg);
+ imapAccount.uninstallHandler(this);
+ return true;
+ }
+ return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+void mail::imapFolder::createSubFolder(string name, bool isDirectory,
+ mail::callback::folderList &callback1,
+ mail::callback &callback2) const
+{
+ if (isDestroyed(callback2))
+ return;
+
+ if (!imapAccount.ready(callback2))
+ return;
+
+ if (imapAccount.smap)
+ {
+ mail::smapCREATE *h=
+ new mail::smapCREATE(path, name, isDirectory,
+ callback1, callback2);
+
+ if (!h)
+ {
+ callback2.fail(strerror(errno));
+ return;
+ }
+
+ try {
+ imapAccount.installForegroundTask(h);
+ } catch (...) {
+ delete h;
+ throw;
+ }
+
+ return;
+ }
+
+ mail::imapCREATE *h=new mail::imapCREATE(path + hiersep,
+ name,
+ !isDirectory,
+ isDirectory,
+ callback1,
+ callback2);
+ if (!h)
+ {
+ callback2.fail(strerror(errno));
+ return;
+ }
+
+ try {
+ imapAccount.installForegroundTask(h);
+ } catch (...) {
+ delete h;
+ throw;
+ }
+}
+
+void mail::imapFolder::renameFolder(const folder *newParent,
+ std::string newName,
+ mail::callback::folderList &callback1,
+ callback &callback2) const
+{
+ if (isDestroyed(callback2))
+ return;
+
+ if (!imapAccount.ready(callback2))
+ return;
+
+ imapAccount.sameServerFolderPtr=NULL;
+
+ string newPath="";
+
+ if (newParent)
+ {
+ newParent->sameServerAsHelperFunc();
+
+ if (imapAccount.sameServerFolderPtr == NULL)
+ {
+ callback2.fail("mail::imapFolder::renameFolder -"
+ " invalid destination folder object.");
+ return;
+ }
+
+ newPath=imapAccount.sameServerFolderPtr->path +
+ imapAccount.sameServerFolderPtr->hiersep;
+ }
+
+ imapHandler *h=imapAccount.smap ?
+ (imapHandler *)
+ new mail::smapCREATE(path, newPath, newName,
+ callback1, callback2) :
+ new mail::imapCREATE(path, newPath, newName,
+ hasmessages,
+ hassubfolders,
+ callback1,
+ callback2);
+
+ if (!h)
+ {
+ callback2.fail(strerror(errno));
+ return;
+ }
+
+ try {
+ imapAccount.installForegroundTask(h);
+ } catch (...) {
+ delete h;
+ throw;
+ }
+}
+
+void mail::imapFolder::create(bool isDirectory, mail::callback &callback2)
+ const
+{
+ if (isDestroyed(callback2))
+ return;
+ if (!imapAccount.ready(callback2))
+ return;
+
+ if (imapAccount.smap)
+ {
+ mail::smapCREATE *h=
+ new mail::smapCREATE(path, isDirectory, callback2);
+
+ if (!h)
+ {
+ callback2.fail(strerror(errno));
+ return;
+ }
+
+ try {
+ imapAccount.installForegroundTask(h);
+ } catch (...) {
+ delete h;
+ throw;
+ }
+ return;
+ }
+
+ mail::imapRECREATE *h=new mail::imapRECREATE(path + (isDirectory ?
+ hiersep:""),
+ callback2);
+ if (!h)
+ {
+ callback2.fail(strerror(errno));
+ return;
+ }
+ try {
+ imapAccount.installForegroundTask(h);
+ } catch (...) {
+ delete h;
+ throw;
+ }
+}
+
+void mail::imapFolder::destroy(mail::callback &callback, bool subdirs) const
+{
+ if (isDestroyed(callback))
+ return;
+
+ if (!imapAccount.ready(callback))
+ return;
+
+ imapHandler *h=imapAccount.smap ? (imapHandler *)
+ new mail::smapDELETE(path, subdirs, callback)
+ : new mail::imapDELETE(callback,
+ path + (subdirs ? hiersep:""));
+
+ if (!h)
+ {
+ callback.fail(strerror(errno));
+ return;
+ }
+ try {
+ imapAccount.installForegroundTask(h);
+ } catch (...) {
+ delete h;
+ throw;
+ }
+}
+
+void mail::imapFolder::open(mail::callback &openCallback,
+ mail::snapshot *restoreSnapshot,
+ mail::callback::folder &folderCallback) const
+{
+ if (isDestroyed(openCallback))
+ return;
+ imapAccount.openFolder(path, restoreSnapshot,
+ openCallback, folderCallback);
+}
+
+
+mail::folder *mail::imapFolder::clone() const
+{
+ mail::imapFolder *i=new mail::imapFolder(*this);
+
+ if (i) return i;
+ return NULL;
+}
+
+string mail::imapFolder::toString() const
+{
+ string s="";
+
+ int i;
+
+ for (i=0; i<3; i++)
+ {
+ if (i > 0)
+ s.append(":",1);
+
+ string ss;
+
+ string::iterator b, e;
+
+ switch (i) {
+ case 0:
+ ss=path;
+ break;
+ case 1:
+ ss=hiersep;
+ break;
+ default:
+ ss=name;
+ break;
+ }
+
+ b=ss.begin();
+ e=ss.end();
+
+ while (b != e)
+ {
+ if (*b == '\\' || *b == ':')
+ s.append("\\", 1);
+ s.append(&*b, 1);
+ b++;
+ }
+ }
+
+ s += ":";
+ if (hasMessages())
+ s += "S";
+ if (hasSubFolders())
+ s += "C";
+
+ return s;
+}
+
+void mail::imapFolder::getMyRights(callback &getCallback, string &rights)
+ const
+{
+ if (isDestroyed(getCallback))
+ return;
+
+ if (!imapAccount.ready(getCallback))
+ return;
+
+ if (!imapAccount.hasCapability("ACL"))
+ {
+ mail::folder::getMyRights(getCallback, rights);
+ return;
+ }
+
+ string s=getPath();
+
+ if (s.find('\r') != std::string::npos ||
+ s.find('\n') != std::string::npos) // Sanity check
+ {
+ getCallback.fail(strerror(EINVAL));
+ return;
+ }
+
+
+ if (imapAccount.smap)
+ {
+ if (imapAccount.getCapability("ACL") == "ACL")
+ {
+ // SMAP uses ACL2
+
+ mail::folder::getMyRights(getCallback, rights);
+ return;
+ }
+
+ smapACL *sa=new smapACL(s, rights, getCallback);
+
+ if (!sa)
+ {
+ getCallback.fail(strerror(errno));
+ return;
+ }
+
+ try
+ {
+ imapAccount.installForegroundTask(sa);
+ } catch (...)
+ {
+ delete sa;
+ throw;
+ }
+ return;
+ }
+
+ imapGetMyRights *gm=new imapGetMyRights(s, rights,
+ getCallback);
+
+ if (!gm)
+ {
+ getCallback.fail(strerror(errno));
+ return;
+ }
+
+ try
+ {
+ imapAccount.installForegroundTask(gm);
+ } catch (...) {
+ delete gm;
+ throw;
+ }
+}
+
+void mail::imapFolder::getRights(callback &getCallback,
+ list<pair<string, string> > &rights) const
+{
+ if (isDestroyed(getCallback))
+ return;
+
+ if (!imapAccount.ready(getCallback))
+ return;
+
+ if (!imapAccount.hasCapability("ACL"))
+ {
+ mail::folder::getRights(getCallback, rights);
+ return;
+ }
+
+ string s=getPath();
+
+ if (s.find('\r') != std::string::npos ||
+ s.find('\n') != std::string::npos) // Sanity check
+ {
+ getCallback.fail(strerror(EINVAL));
+ return;
+ }
+
+ if (imapAccount.smap)
+ {
+ if (imapAccount.getCapability("ACL") == "ACL")
+ {
+ // SMAP uses ACL2
+
+ mail::folder::getRights(getCallback, rights);
+ return;
+ }
+
+ smapGETACL *sa=new smapGETACL(s, rights, getCallback);
+
+ if (!sa)
+ {
+ getCallback.fail(strerror(errno));
+ return;
+ }
+
+ try
+ {
+ imapAccount.installForegroundTask(sa);
+ } catch (...)
+ {
+ delete sa;
+ throw;
+ }
+ return;
+ }
+
+ imapGetRights *gr=new imapGetRights(s, rights,
+ getCallback);
+
+ if (!gr)
+ {
+ getCallback.fail(strerror(errno));
+ return;
+ }
+
+ try
+ {
+ imapAccount.installForegroundTask(gr);
+ } catch (...) {
+ delete gr;
+ throw;
+ }
+}
+
+void mail::imapFolder::setRights(callback &setCallback,
+ string &errorIdentifier,
+ vector<string> &errorRights,
+ string identifier,
+ string rights) const
+{
+ errorIdentifier="";
+ errorRights.clear();
+ if (isDestroyed(setCallback))
+ return;
+
+ if (!imapAccount.ready(setCallback))
+ return;
+
+ if (!imapAccount.hasCapability("ACL"))
+ {
+ mail::folder::setRights(setCallback,
+ errorIdentifier,
+ errorRights,
+ identifier, rights);
+ return;
+ }
+
+ string s=getPath();
+
+ if (s.find('\r') != std::string::npos ||
+ s.find('\n') != std::string::npos ||
+ identifier.find('\r') != std::string::npos ||
+ identifier.find('\n') != std::string::npos ||
+ rights.find('\r') != std::string::npos ||
+ rights.find('\n') != std::string::npos) // Sanity check
+ {
+ setCallback.fail(strerror(EINVAL));
+ return;
+ }
+
+ if (imapAccount.smap)
+ {
+ if (imapAccount.getCapability("ACL") == "ACL")
+ {
+ // SMAP uses ACL2
+
+ mail::folder::setRights(setCallback,
+ errorIdentifier,
+ errorRights,
+ identifier, rights);
+ return;
+ }
+
+ smapSETACL *sa=new smapSETACL(s, identifier, rights,
+ false,
+ errorIdentifier,
+ errorRights,
+ setCallback);
+
+ if (!sa)
+ {
+ setCallback.fail(strerror(errno));
+ return;
+ }
+
+ try
+ {
+ imapAccount.installForegroundTask(sa);
+ } catch (...)
+ {
+ delete sa;
+ throw;
+ }
+ return;
+ }
+
+
+
+
+ /* Fix ACL/ACL2 brokenness */
+
+ if (rights.find(ACL_DELETEMSGS[0]) != std::string::npos &&
+ rights.find(ACL_DELETEFOLDER[0]) != std::string::npos &&
+ rights.find(ACL_EXPUNGE[0]) != std::string::npos)
+ {
+ size_t p=rights.find(ACL_DELETEMSGS[0]);
+
+ rights.erase(rights.begin()+p, rights.begin()+p+1);
+ p=rights.find(ACL_DELETEFOLDER[0]);
+ rights.erase(rights.begin()+p, rights.begin()+p+1);
+ p=rights.find(ACL_EXPUNGE[0]);
+ rights.erase(rights.begin()+p, rights.begin()+p+1);
+
+ rights += ACL_DELETE_SPECIAL;
+ }
+
+ imapSetRights *sr=new imapSetRights(s, identifier, rights,
+ false,
+ errorIdentifier,
+ errorRights,
+ setCallback);
+
+ if (!sr)
+ {
+ setCallback.fail(strerror(errno));
+ return;
+ }
+
+ try
+ {
+ imapAccount.installForegroundTask(sr);
+ } catch (...) {
+ delete sr;
+ throw;
+ }
+}
+
+void mail::imapFolder::delRights(callback &setCallback,
+ string &errorIdentifier,
+ vector<std::string> &errorRights,
+ string identifier) const
+{
+ errorIdentifier="";
+ errorRights.clear();
+ if (isDestroyed(setCallback))
+ return;
+
+ if (!imapAccount.ready(setCallback))
+ return;
+
+ if (!imapAccount.hasCapability("ACL"))
+ {
+ mail::folder::delRights(setCallback,
+ errorIdentifier,
+ errorRights,
+ identifier);
+ return;
+ }
+
+ string s=getPath();
+
+ if (s.find('\r') != std::string::npos ||
+ s.find('\n') != std::string::npos) // Sanity check
+ {
+ setCallback.fail(strerror(EINVAL));
+ return;
+ }
+
+ if (imapAccount.smap)
+ {
+ if (imapAccount.getCapability("ACL") == "ACL")
+ {
+ // SMAP uses ACL2
+
+ mail::folder::delRights(setCallback,
+ errorIdentifier,
+ errorRights,
+ identifier);
+ return;
+ }
+
+ smapSETACL *sa=new smapSETACL(s, identifier, "",
+ true,
+ errorIdentifier,
+ errorRights,
+ setCallback);
+
+ if (!sa)
+ {
+ setCallback.fail(strerror(errno));
+ return;
+ }
+
+ try
+ {
+ imapAccount.installForegroundTask(sa);
+ } catch (...)
+ {
+ delete sa;
+ throw;
+ }
+ return;
+ }
+ imapSetRights *sr=new imapSetRights(s, identifier, "",
+ true,
+ errorIdentifier,
+ errorRights,
+ setCallback);
+
+ if (!sr)
+ {
+ setCallback.fail(strerror(errno));
+ return;
+ }
+
+ try
+ {
+ imapAccount.installForegroundTask(sr);
+ } catch (...) {
+ delete sr;
+ throw;
+ }
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Adding a new message.
+
+LIBMAIL_START
+
+class imapAPPEND : public imapCommandHandler,
+ public addMessage {
+
+ imap &imapAccount;
+ mail::callback &callback;
+ string path;
+
+ bool continuationReceived;
+
+ // Helper class that pushes the temp file's contents to the server.
+
+ class Pusher : public fd::WriteBuffer {
+
+ public:
+
+ imapAPPEND *myappend;
+
+ Pusher();
+ ~Pusher();
+
+ bool fillWriteBuffer();
+ };
+
+ Pusher *myPusher;
+public:
+ friend class Pusher;
+
+ FILE *tmpfileptr;
+ string appendcmd;
+ long bytes;
+ long bytesDone;
+ long bytesTotal;
+
+ imapAPPEND(imap &imapArg,
+ mail::callback &callbackArg,
+ string pathArg);
+ ~imapAPPEND();
+
+ void installed(imap &imapAccount);
+
+
+ void saveMessageContents(string txt);
+ void go();
+ void fail(string errmsg);
+
+private:
+ const char *getName();
+ void timedOut(const char *timedout);
+ bool untaggedMessage(imap &imapAccount, string msgname);
+ bool taggedMessage(imap &imapAccount, string msgname,
+ string message,
+ bool okfail, string errmsg);
+ bool continuationRequest(imap &imapAccount, string msg);
+
+ bool fillWriteBuffer();
+
+
+};
+
+LIBMAIL_END
+
+mail::imapAPPEND::imapAPPEND(mail::imap &imapArg,
+ mail::callback &callbackArg,
+ string pathArg)
+ : mail::addMessage(&imapArg),
+ imapAccount(imapArg), callback(callbackArg), path(pathArg),
+ continuationReceived(false), myPusher(NULL), tmpfileptr(NULL)
+{
+}
+
+mail::imapAPPEND::~imapAPPEND()
+{
+ if (tmpfileptr)
+ fclose(tmpfileptr);
+ if (myPusher)
+ myPusher->myappend=NULL;
+}
+
+const char *mail::imapAPPEND::getName()
+{
+ return "APPEND";
+}
+
+void mail::imapAPPEND::timedOut(const char *timedout)
+{
+ callbackTimedOut(callback, timedout);
+}
+
+// The application pushes the message's contents here. Save the contents
+// into a temporary file, using CRNL line termination.
+
+void mail::imapAPPEND::saveMessageContents(string s)
+{
+ string::iterator b=s.begin(), e=s.end();
+
+ while (b != e)
+ {
+ char c= *b++;
+
+ if (c == '\n')
+ putc('\r', tmpfileptr);
+ putc(c, tmpfileptr);
+ }
+}
+
+
+void mail::imapAPPEND::installed(mail::imap &imapAccount)
+{
+ myPusher=new Pusher();
+
+ if (!myPusher)
+ LIBMAIL_THROW("Out of memory.");
+
+ try {
+ imapAccount.imapcmd("APPEND", appendcmd);
+ imapAccount.socketWrite(myPusher);
+ myPusher->myappend=this;
+ } catch (...) {
+ delete myPusher;
+ myPusher=NULL;
+ }
+}
+
+bool mail::imapAPPEND::untaggedMessage(mail::imap &imapAccount, string msgname)
+{
+ return false;
+}
+
+bool mail::imapAPPEND::continuationRequest(mail::imap &imapAccount, string msg)
+{
+ continuationReceived=true; // Tell pusher the show's on.
+ return true;
+}
+
+bool mail::imapAPPEND::taggedMessage(mail::imap &imapAccount, string name,
+ string message,
+ bool okfail, string errmsg)
+{
+ if (name == "APPEND")
+ {
+ if (okfail)
+ callback.success(errmsg);
+ else
+ callback.fail(errmsg);
+ imapAccount.uninstallHandler(this);
+ return true;
+ }
+ return false;
+}
+
+bool mail::imapAPPEND::fillWriteBuffer()
+{
+ char buffer[BUFSIZ];
+
+ if (!continuationReceived)
+ return true;
+
+ if (bytes <= 0)
+ {
+ callback.reportProgress(bytesTotal, bytesTotal, 0, 1);
+ return false;
+ }
+
+ int n=fread(buffer, 1, bytes > (long)sizeof(buffer)
+ ? sizeof(buffer):bytes, tmpfileptr);
+
+ if (n <= 0)
+ {
+ n=bytes > (long)sizeof(buffer) ? sizeof(buffer):bytes;
+ memset(buffer, '\n', n);
+ }
+
+ bytes -= n;
+ bytesDone += n;
+ setTimeout();
+ callback.reportProgress(bytesDone, bytesTotal, 0, 1);
+ myPusher->writebuffer.insert(myPusher->writebuffer.end(),
+ buffer, buffer+n);
+ return true;
+}
+
+mail::imapAPPEND::Pusher::Pusher() : myappend(NULL)
+{
+}
+
+mail::imapAPPEND::Pusher::~Pusher()
+{
+ if (myappend)
+ myappend->myPusher=NULL;
+}
+
+bool mail::imapAPPEND::Pusher::fillWriteBuffer()
+{
+ if (!myappend)
+ return false;
+
+ return myappend->fillWriteBuffer();
+}
+
+void mail::imap::addSmapFolderCmd(mail::smapAddMessage *add,
+ std::string path)
+{
+ vector<string> words;
+ smapHandler::path2words(path, words);
+
+ vector<string>::iterator b, e;
+
+ string pstr="ADD FOLDER ";
+
+ b=words.begin();
+ e=words.end();
+
+ while (b != e)
+ {
+ pstr += " ";
+ pstr += quoteSMAP( *b );
+ b++;
+ }
+
+ pstr += " \"\"\n";
+
+ add->cmds.push_back(pstr);
+}
+
+mail::folder *mail::imap::getSendFolder(const smtpInfo &info,
+ const mail::folder *folder,
+ std::string &errmsg)
+{
+ if (!smap)
+ return mail::account::getSendFolder(info, folder, errmsg);
+
+ if (folder)
+ {
+ sameServerFolderPtr=NULL;
+ folder->sameServerAsHelperFunc();
+ if (!sameServerFolderPtr)
+ return mail::account::getSendFolder(info, folder,
+ errmsg);
+ }
+
+ if (info.recipients.size() == 0)
+ {
+ errno=ENOENT;
+ errmsg="Empty recipient list.";
+ return NULL;
+ }
+
+ mail::folder *f=
+ new smapSendFolder(this, info, folder ? folder->getPath():"");
+
+ if (!f)
+ errmsg=strerror(errno);
+ return f;
+}
+
+mail::addMessage *mail::imap::addMessage(string path,
+ mail::callback &callback)
+{
+ if (smap)
+ {
+ mail::smapAddMessage *add=new
+ smapAddMessage(*this, callback);
+
+ if (!add)
+ {
+ callback.fail(strerror(errno));
+ return NULL;
+ }
+
+ try {
+ addSmapFolderCmd(add, path);
+ } catch (...) {
+ delete add;
+ add=NULL;
+ }
+ return add;
+ }
+
+ mail::imapAPPEND *append=new mail::imapAPPEND(*this, callback, path);
+
+ if (!append)
+ {
+ callback.fail(strerror(errno));
+ return NULL;
+ }
+
+ if ((append->tmpfileptr=tmpfile()) == NULL)
+ {
+ callback.fail(strerror(errno));
+ delete append;
+ return NULL;
+ }
+
+ time(&append->messageDate);
+
+ return append;
+}
+
+void mail::imapAPPEND::fail(string errmsg)
+{
+ callback.fail(errmsg);
+ delete this;
+}
+
+void mail::imapAPPEND::go()
+{
+ if (!checkServer())
+ return;
+
+ if (!imapAccount.ready(callback))
+ {
+ delete this;
+ return;
+ }
+
+ try {
+
+ string flags=imapAccount.messageFlagStr(messageInfo);
+
+ fprintf(tmpfileptr, "\r\n");
+
+ // Part of the APPEND cmd, actually
+
+ bytesDone=0;
+
+ if (fflush(tmpfileptr) < 0 || ferror(tmpfileptr)
+ || (bytes=ftell(tmpfileptr)) == -1
+ || fseek(tmpfileptr, 0L, SEEK_SET) < 0)
+ {
+ callback.fail(strerror(errno));
+ delete this;
+ return;
+ }
+
+ bytesTotal=bytes;
+
+ string cnt_s;
+
+ {
+ ostringstream o;
+
+ o << bytes-2;
+ cnt_s=o.str();
+ }
+
+ char buffer[100];
+
+ rfc822_mkdate_buf(messageDate, buffer);
+
+ char *p=strchr(buffer, ',');
+
+ if (p && *++p && *++p)
+ {
+ char *q=strchr(p, ' ');
+
+ if (q)
+ *q='-';
+
+ q=strchr(p, ' ');
+
+ if (q)
+ *q='-';
+ }
+ else
+ p=buffer;
+
+ appendcmd="APPEND " + imapAccount.quoteSimple(path)
+ + " (" + flags + ") "
+ + imapAccount.quoteSimple(p)
+ + " {" + cnt_s + "}\r\n";
+
+ imapAccount.installForegroundTask(this);
+ } catch (...) {
+ callback.fail(strerror(errno));
+ delete this;
+ }
+}
+
+#if 0
+bool mail::imapFolder:sameServerAs(const mail::folder *f) const
+{
+ if (isDestroyed())
+ return false;
+
+ imapAccount.sameServerHelperFlag=false;
+
+ f->sameServerAsHelperFunc();
+
+ return imapAccount.sameServerHelperFlag;
+}
+#endif
+
+void mail::imapFolder::sameServerAsHelperFunc() const
+{
+ if (isDestroyed())
+ return;
+
+ imapAccount.sameServerFolderPtr=this;
+}