summaryrefslogtreecommitdiff
path: root/src/depot.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/depot.cc')
-rw-r--r--src/depot.cc717
1 files changed, 717 insertions, 0 deletions
diff --git a/src/depot.cc b/src/depot.cc
new file mode 100644
index 0000000..599bf97
--- /dev/null
+++ b/src/depot.cc
@@ -0,0 +1,717 @@
+/** --------------------------------------------------------------------
+ * @file depot.cc
+ * @brief Implementation of the Depot class.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ------------------------------------------------------------------ **/
+#include <map>
+#include <string>
+#include <unistd.h>
+#include <errno.h>
+
+#include "depot.h"
+#include "mailbox.h"
+#include "status.h"
+#include "convert.h"
+#include "iodevice.h"
+#include "iofactory.h"
+
+using namespace ::std;
+using namespace Binc;
+
+//--------------------------------------------------------------------
+DepotFactory::DepotFactory(void)
+{
+}
+
+//--------------------------------------------------------------------
+DepotFactory::~DepotFactory(void)
+{
+ for (vector<Depot *>::iterator i = depots.begin(); i != depots.end();
+ ++i)
+ delete *i;
+}
+
+//--------------------------------------------------------------------
+Depot *DepotFactory::get(const string &name) const
+{
+ for (vector<Depot *>::const_iterator i = depots.begin(); i != depots.end();
+ ++i)
+ if ((*i)->getName() == name)
+ return *i;
+
+ return 0;
+}
+
+//--------------------------------------------------------------------
+void DepotFactory::assign(Depot *depot)
+{
+ depots.push_back(depot);
+}
+
+//--------------------------------------------------------------------
+DepotFactory &DepotFactory::getInstance(void)
+{
+ static DepotFactory depotfactory;
+ return depotfactory;
+}
+
+//--------------------------------------------------------------------
+Depot::Depot(void) : enditerator(0, 0)
+{
+ defaultmailbox = 0;
+ selectedmailbox = 0;
+
+ delimiter = '/';
+}
+
+//--------------------------------------------------------------------
+Depot::Depot(const string &name) : enditerator(0, 0)
+{
+ defaultmailbox = 0;
+ selectedmailbox = 0;
+
+ delimiter = '/';
+
+ this->name = name;
+}
+
+//--------------------------------------------------------------------
+Depot::~Depot(void)
+{
+}
+
+//--------------------------------------------------------------------
+const string &Depot::getLastError(void) const
+{
+ return lastError;
+}
+
+//--------------------------------------------------------------------
+void Depot::setLastError(const string &error) const
+{
+ lastError = error;
+}
+
+//--------------------------------------------------------------------
+void Depot::assign(Mailbox *m)
+{
+ for (vector<Mailbox *>::const_iterator i = backends.begin();
+ i != backends.end(); ++i)
+ if (*i == m) break;
+
+ backends.push_back(m);
+}
+
+//--------------------------------------------------------------------
+Mailbox *Depot::get(const string &s_in) const
+{
+ for (vector<Mailbox *>::const_iterator i = backends.begin();
+ i != backends.end(); ++i)
+ if ((*i)->isMailbox(mailboxToFilename(s_in)))
+ return *i;
+
+ setLastError("No such mailbox " + toImapString(s_in));
+ return 0;
+}
+
+//--------------------------------------------------------------------
+bool Depot::setSelected(Mailbox *m)
+{
+ for (vector<Mailbox *>::const_iterator i = backends.begin();
+ i != backends.end(); ++i)
+ if (*i == m) {
+ selectedmailbox = m;
+ return true;
+ }
+
+ setLastError("Attempted to select unregistered Mailbox type in Depot");
+ return false;
+}
+
+//--------------------------------------------------------------------
+const string &Depot::getName(void) const
+{
+ return name;
+}
+
+//--------------------------------------------------------------------
+const string &Depot::getPersonalNamespace(void) const
+{
+ return personalNamespace;
+}
+
+//--------------------------------------------------------------------
+const string &Depot::getOthersNamespace(void) const
+{
+ return othersNamespace;
+}
+
+//--------------------------------------------------------------------
+const string &Depot::getSharedNamespace(void) const
+{
+ return sharedNamespace;
+}
+
+//--------------------------------------------------------------------
+void Depot::setDelimiter(char c)
+{
+ delimiter = c;
+}
+
+//--------------------------------------------------------------------
+const char Depot::getDelimiter(void) const
+{
+ return delimiter;
+}
+
+//--------------------------------------------------------------------
+bool Depot::setDefaultType(const string &name)
+{
+ for (vector<Mailbox *>::const_iterator i = backends.begin();
+ i != backends.end(); ++i)
+ if ((*i)->getTypeName() == name) {
+ defaultmailbox = *i;
+ return true;
+ }
+
+ setLastError("attempt to default to unregistered Mailbox type " + name);
+ return false;
+}
+
+//--------------------------------------------------------------------
+Mailbox *Depot::getSelected(void) const
+{
+ return selectedmailbox;
+}
+
+//--------------------------------------------------------------------
+void Depot::resetSelected(void)
+{
+ selectedmailbox = 0;
+}
+
+//--------------------------------------------------------------------
+Mailbox *Depot::getDefault(void) const
+{
+ return defaultmailbox;
+}
+
+//--------------------------------------------------------------------
+bool Depot::createMailbox(const string &s_in) const
+{
+ const string &mailboxname = mailboxToFilename(toCanonMailbox(s_in));
+ if (mailboxname == "") {
+ setLastError("invalid mailbox name");
+ return false;
+ }
+
+ Mailbox *mailbox = getDefault();
+ if (mailbox == 0) {
+ setLastError("no default mailbox defined");
+ return false;
+ }
+
+ bool result = mailbox->createMailbox(mailboxname, 0777);
+ if (result)
+ return true;
+ else {
+ setLastError(mailbox->getLastError());
+ return false;
+ }
+}
+
+//--------------------------------------------------------------------
+bool Depot::deleteMailbox(const string &s_in) const
+{
+ const string &mailboxname = mailboxToFilename(toCanonMailbox(s_in));
+
+ Mailbox *mailbox = get(s_in);
+ if (mailbox == 0) {
+ setLastError(s_in + ": no such mailbox");
+ return false;
+ }
+
+ bool result = mailbox->deleteMailbox(mailboxname);
+ if (result)
+ return true;
+ else {
+ setLastError(mailbox->getLastError());
+ return false;
+ }
+}
+
+//--------------------------------------------------------------------
+bool Depot::renameMailbox(const string &s_in, const string &t_in) const
+{
+ const string &source = mailboxToFilename(s_in).c_str();
+ const string &dest = mailboxToFilename(t_in).c_str();
+
+ int nrenamed = 0;
+ const iterator e = end();
+ for (iterator i = begin("."); i != e; ++i) {
+ string entry = *i;
+
+ if (entry.substr(0, source.length()) == source) {
+ string sourcename, destname;
+
+ if (entry.length() == source.length()) {
+ sourcename = source;
+ destname = dest;
+
+ } else if (entry.length() > source.length()
+ && entry[source.length()] == '.') {
+ sourcename = entry;
+ destname = dest + entry.substr(source.length());
+ } else continue;
+
+ if (rename(sourcename.c_str(), destname.c_str()) != 0) {
+ bincWarning << "error renaming " << sourcename << " to "
+ << destname << ": " << strerror(errno) << endl;
+ } else
+ nrenamed++;
+
+ Mailbox *mailbox;
+ if ((mailbox = get(filenameToMailbox(sourcename))) != 0)
+ mailbox->bumpUidValidity(filenameToMailbox(sourcename));
+ if ((mailbox = get(filenameToMailbox(destname))) != 0)
+ mailbox->bumpUidValidity(filenameToMailbox(destname));
+ }
+ }
+
+ if (nrenamed == 0) {
+ setLastError("An error occurred when renaming "
+ + toImapString(s_in)
+ + " to " + toImapString(t_in)
+ + ". Try creating a new mailbox,"
+ " then copy over all messages."
+ " Finally, delete the original mailbox");
+ return false;
+ } else
+ return true;
+}
+
+//--------------------------------------------------------------------
+bool Depot::getStatus(const std::string &s_in, Status &dest) const
+{
+ const string mailbox = toCanonMailbox(s_in);
+ Mailbox *m = get(mailbox);
+ if (m == 0) {
+ setLastError("Unrecognized mailbox: " + toImapString(s_in));
+ return false;
+}
+
+ int statusid = m->getStatusID(mailboxToFilename(mailbox));
+ if (mailboxstatuses.find(mailbox) != mailboxstatuses.end()) {
+ dest = mailboxstatuses[mailbox];
+ if (dest.getStatusID() == statusid)
+ return true;
+ }
+
+ if (!m->getStatus(mailboxToFilename(mailbox), dest)) {
+ setLastError(m->getLastError());
+ return false;
+ }
+
+ dest.setStatusID(statusid);
+ mailboxstatuses[mailbox] = dest;
+ return true;
+}
+
+//----------------------------------------------------------------------
+vector<string> Depot::getSubscriptions(void) const
+{
+ return subscribed;
+}
+
+//----------------------------------------------------------------------
+void Depot::subscribeTo(const std::string mailbox)
+{
+ for (vector<string>::iterator i = subscribed.begin();
+ i != subscribed.end(); ++i) {
+ if (*i == mailbox)
+ return;
+ }
+
+ subscribed.push_back(mailbox);
+}
+
+//----------------------------------------------------------------------
+bool Depot::unsubscribeTo(const std::string mailbox)
+{
+ for (vector<string>::iterator i = subscribed.begin();
+ i != subscribed.end(); ++i) {
+ if (*i == mailbox) {
+ subscribed.erase(i);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//----------------------------------------------------------------------
+void Depot::loadSubscribes(void)
+{
+ // drop all existing subscribed folders.
+ subscribed.clear();
+
+ // try loading the .subscribed file
+ bool ok = false;
+ FILE *fp = fopen(".subscribed", "r");
+ map<string, bool> addedEntries;
+ if (fp) {
+ int c;
+ string current;
+ while ((c = fgetc(fp)) != EOF) {
+ if (c == '\n') {
+ if (current != "") {
+ if (current == "INBOX")
+ current = ".";
+
+ if (current.substr(0, 5) == "INBOX")
+ current = current.substr(5);
+
+ if (addedEntries.find(current) == addedEntries.end()) {
+ subscribed.push_back(filenameToMailbox(current));
+ addedEntries[current] = true;
+ }
+ current = "";
+ }
+ } else
+ current += c;
+ }
+
+ fclose(fp);
+ ok = true;
+ }
+
+ if (!ok) {
+ subscribed.push_back("INBOX");
+ saveSubscribes();
+ }
+}
+
+//----------------------------------------------------------------------
+bool Depot::saveSubscribes(void) const
+{
+ // create a safe file name
+ string tpl = ".subscribed-tmp-XXXXXX";
+ char *ftemplate = new char[tpl.length() + 1];
+
+ strcpy(ftemplate, tpl.c_str());
+ int fd = mkstemp(ftemplate);
+ if (fd == -1) {
+ bincWarning << "unable to create temporary file \""
+ << tpl << "\"" << endl;
+ delete[] ftemplate;
+ return false;
+ }
+
+ map<string, bool> addedEntries;
+ for (vector<string>::const_iterator i = subscribed.begin();
+ i != subscribed.end(); ++i) {
+ if (addedEntries.find(*i) == addedEntries.end()) {
+ addedEntries[*i] = true;
+ string w = mailboxToFilename(*i) + "\n";
+ if (write(fd, w.c_str(), w.length()) != (ssize_t) w.length()) {
+ bincWarning << "failed to write to " << tpl << ": "
+ << strerror(errno) << endl;
+ break;
+ }
+ }
+ }
+
+ if ((fsync(fd) && (errno != EROFS || errno != EINVAL)) || close(fd)) {
+ bincWarning << "failed to close " << ftemplate
+ << ": " << strerror(errno) << endl;
+ delete[] ftemplate;
+ return false;
+ }
+
+ if (rename(ftemplate, ".subscribed") != 0) {
+ bincWarning << "failed to rename " << ftemplate
+ << " to .subscribed: "
+ << strerror(errno) << endl;
+ delete[] ftemplate;
+ return false;
+ }
+
+ delete[] ftemplate;
+ return true;
+}
+
+//--------------------------------------------------------------------
+Depot::iterator::iterator(void)
+{
+ dirp = 0;
+ ref = new int;
+ *ref = 1;
+}
+
+//--------------------------------------------------------------------
+Depot::iterator::iterator(DIR *dp, struct dirent *sp)
+{
+ dirp = dp;
+ direntp = sp;
+
+ ref = new int;
+ *ref = 1;
+}
+
+//--------------------------------------------------------------------
+Depot::iterator::iterator(const iterator &copy)
+{
+ if (*copy.ref != 0)
+ ++(*copy.ref);
+
+ ref = copy.ref;
+ dirp = copy.dirp;
+ direntp = copy.direntp;
+}
+
+//--------------------------------------------------------------------
+Depot::iterator::~iterator(void)
+{
+ deref();
+}
+
+//--------------------------------------------------------------------
+Depot::iterator &Depot::iterator::operator =(const iterator &copy)
+{
+ if (*copy.ref != 0)
+ ++(*copy.ref);
+
+ deref();
+
+ ref = copy.ref;
+ dirp = copy.dirp;
+ direntp = copy.direntp;
+
+ return *this;
+}
+
+//--------------------------------------------------------------------
+void Depot::iterator::deref(void)
+{
+ // decrease existing copy ref if there is one
+ if (*ref != 0 && --(*ref) == 0) {
+ if (dirp) {
+ closedir(dirp);
+ dirp = 0;
+ }
+
+ delete ref;
+ ref = 0;
+ }
+}
+
+//--------------------------------------------------------------------
+string Depot::iterator::operator * (void) const
+{
+ if (direntp == 0)
+ return "";
+
+ return direntp->d_name;
+}
+
+//--------------------------------------------------------------------
+void Depot::iterator::operator ++ (void)
+{
+ direntp = readdir(dirp);
+}
+
+//--------------------------------------------------------------------
+bool Depot::iterator::operator == (Depot::iterator i) const
+{
+ return direntp == i.direntp;
+}
+
+//--------------------------------------------------------------------
+bool Depot::iterator::operator != (Depot::iterator i) const
+{
+ return direntp != i.direntp;
+}
+
+//--------------------------------------------------------------------
+Depot::iterator Depot::begin(const string &path) const
+{
+ Depot::iterator i;
+
+ if ((i.dirp = opendir(path.c_str())) == 0) {
+ bincWarning << "opendir on " << path << " failed" << endl;
+ setLastError("opendir on " + path + " failed");
+ return end();
+ }
+
+ ++i;
+ return i;
+}
+
+//--------------------------------------------------------------------
+const Depot::iterator &Depot::end(void) const
+{
+ return enditerator;
+}
+
+//--------------------------------------------------------------------
+MaildirPPDepot::MaildirPPDepot(void) : Depot("Maildir++")
+{
+ privateNamespace = "INBOX";
+ privateNamespace += getDelimiter();
+}
+
+//--------------------------------------------------------------------
+MaildirPPDepot::~MaildirPPDepot(void)
+{
+}
+
+//--------------------------------------------------------------------
+const string &MaildirPPDepot::getPersonalNamespace(void) const
+{
+ return privateNamespace;
+}
+
+//--------------------------------------------------------------------
+string MaildirPPDepot::mailboxToFilename(const string &m) const
+{
+ string prefix = "INBOX"; prefix += delimiter;
+
+ string mm = m;
+ trim(mm, string(&delimiter, 1));
+ string tmp = mm;
+ uppercase(tmp);
+ if (tmp != "INBOX" && tmp.substr(0, 6) != prefix) {
+ setLastError("With a Maildir++ depot, you must create all"
+ " mailboxes under INBOX. Try creating"
+ " " + prefix + mm + " .");
+ return "";
+ }
+
+ string twodelim;
+ twodelim += delimiter;
+ twodelim += delimiter;
+
+ if (mm == "INBOX") return ".";
+ else if (mm.length() <= 6) {
+ setLastError("With a Maildir++ depot, you must create all"
+ " mailboxes under INBOX.");
+ return "";
+ } else if (mm.substr(0, 6) != prefix) {
+ setLastError("With a Maildir++ depot, you must create all"
+ " mailboxes under INBOX.");
+ return "";
+ } else if (mm.find(twodelim) != string::npos) {
+ setLastError("Invalid character combination "
+ + twodelim + " in mailbox name");
+ return "";
+ } else if (mm != "" && delimiter != '.' && mm.substr(1).find('.') != string::npos) {
+ setLastError("Invalid character '.' in mailbox name");
+ return "";
+ } else {
+ string tmp = mm.substr(6);
+ for (string::iterator i = tmp.begin(); i != tmp.end(); ++i)
+ if (*i == delimiter) *i = '.';
+
+ return "." + tmp;
+ }
+}
+
+//--------------------------------------------------------------------
+string MaildirPPDepot::filenameToMailbox(const string &m) const
+{
+ if (m == ".") return "INBOX";
+ else if (delimiter != '.' && m.find(delimiter) != string::npos) return "";
+ else if (m != "" && m[0] == '.') {
+ string tmp = m;
+ for (string::iterator i = tmp.begin(); i != tmp.end(); ++i)
+ if (*i == '.') *i = delimiter;
+
+ return "INBOX" + tmp;
+ } else return "";
+}
+
+//--------------------------------------------------------------------
+IMAPdirDepot::IMAPdirDepot(void) : Depot("IMAPdir")
+{
+}
+
+//--------------------------------------------------------------------
+IMAPdirDepot::~IMAPdirDepot(void)
+{
+}
+
+//--------------------------------------------------------------------
+string IMAPdirDepot::mailboxToFilename(const string &m) const
+{
+ string tmp;
+ string mm = m;
+ trim(mm, string(&delimiter, 1));
+
+ string twodelim;
+ twodelim += delimiter;
+ twodelim += delimiter;
+
+ if (mm.find(twodelim) != string::npos) {
+ setLastError("Invalid character combination "
+ + twodelim + " in mailbox name");
+ return "";
+ }
+
+ string::const_iterator i = mm.begin();
+ while (i != mm.end()) {
+ if (*i == delimiter) {
+ tmp += '.';
+ } else if (*i == '\\') {
+ tmp += "\\\\";
+ } else if (*i == '.') {
+ if (i == mm.begin())
+ tmp += ".";
+ else
+ tmp += "\\.";
+ } else {
+ tmp += *i;
+ }
+
+ ++i;
+ }
+
+ return tmp;
+}
+
+//--------------------------------------------------------------------
+string IMAPdirDepot::filenameToMailbox(const string &m) const
+{
+ string tmp;
+ bool escape = false;
+
+ // hide the magic "." mailbox.
+ if (m == "." || m == "..")
+ return "";
+
+ string::const_iterator i = m.begin();
+ while (i != m.end()) {
+ if (*i == '.') {
+ if (i != m.begin() && !escape) tmp += delimiter;
+ else if (i == m.begin() || escape) tmp += '.';
+ escape = false;
+ } else if (*i == '\\') {
+ if (!escape) escape = true; else {
+ tmp += '\\';
+ escape = false;
+ }
+ } else if (*i == delimiter) {
+ return "";
+ } else {
+ if (escape) return "";
+ else {
+ tmp += *i;
+ escape = false;
+ }
+ }
+
+ ++i;
+ }
+
+ return tmp;
+}