diff options
Diffstat (limited to 'libmail/imapfolders.C')
| -rw-r--r-- | libmail/imapfolders.C | 1637 | 
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; +} | 
