bincimap

Log | Files | Refs | LICENSE

depot.cc (15142B)


      1 /* -*- Mode: c++; -*- */
      2 /*  --------------------------------------------------------------------
      3  *  Filename:
      4  *    depot.cc
      5  *  
      6  *  Description:
      7  *    <--->
      8  *
      9  *  Authors:
     10  *    Andreas Aardal Hanssen <andreas-binc curly bincimap spot org>
     11  *
     12  *  Bugs:
     13  *
     14  *  ChangeLog:
     15  *
     16  *  --------------------------------------------------------------------
     17  *  Copyright 2002-2005 Andreas Aardal Hanssen
     18  *
     19  *  This program is free software; you can redistribute it and/or modify
     20  *  it under the terms of the GNU General Public License as published by
     21  *  the Free Software Foundation; either version 2 of the License, or
     22  *  (at your option) any later version.
     23  *
     24  *  This program is distributed in the hope that it will be useful,
     25  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     26  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     27  *  GNU General Public License for more details.
     28  *
     29  *  You should have received a copy of the GNU General Public License
     30  *  along with this program; if not, write to the Free Software
     31  *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
     32  *  --------------------------------------------------------------------
     33  */
     34 #include <map>
     35 #include <string>
     36 
     37 #include "depot.h"
     38 #include "mailbox.h"
     39 #include "status.h"
     40 #include "convert.h"
     41 #include "io.h"
     42 
     43 using namespace ::std;
     44 using namespace Binc;
     45 
     46 //--------------------------------------------------------------------
     47 DepotFactory::DepotFactory(void)
     48 {
     49 }
     50 
     51 //--------------------------------------------------------------------
     52 DepotFactory::~DepotFactory(void)
     53 {
     54   for (vector<Depot *>::iterator i = depots.begin(); i != depots.end();
     55        ++i)
     56     delete *i;
     57 }
     58 
     59 //--------------------------------------------------------------------
     60 Depot *DepotFactory::get(const string &name) const
     61 {
     62   for (vector<Depot *>::const_iterator i = depots.begin(); i != depots.end();
     63        ++i)
     64     if ((*i)->getName() == name)
     65       return *i;
     66 
     67   return 0;
     68 }
     69 
     70 //--------------------------------------------------------------------
     71 void DepotFactory::assign(Depot *depot)
     72 {
     73   depots.push_back(depot);
     74 }
     75 
     76 //--------------------------------------------------------------------
     77 DepotFactory &DepotFactory::getInstance(void)
     78 {
     79   static DepotFactory depotfactory;
     80   return depotfactory;
     81 }
     82 
     83 //--------------------------------------------------------------------
     84 Depot::Depot(void) : enditerator(0, 0)
     85 {
     86   defaultmailbox = 0;
     87   selectedmailbox = 0;
     88 
     89   delimiter = '/';
     90 }
     91 
     92 //--------------------------------------------------------------------
     93 Depot::Depot(const string &name) : enditerator(0, 0)
     94 {
     95   defaultmailbox = 0;
     96   selectedmailbox = 0;
     97 
     98   delimiter = '/';
     99 
    100   this->name = name;
    101 }
    102 
    103 //--------------------------------------------------------------------
    104 Depot::~Depot(void)
    105 {
    106 }
    107 
    108 //--------------------------------------------------------------------
    109 const string &Depot::getLastError(void) const
    110 {
    111   return lastError;
    112 }
    113 
    114 //--------------------------------------------------------------------
    115 void Depot::setLastError(const string &error) const
    116 {
    117   lastError = error;
    118 }
    119 
    120 //--------------------------------------------------------------------
    121 void Depot::assign(Mailbox *m)
    122 {
    123   for (vector<Mailbox *>::const_iterator i = backends.begin();
    124        i != backends.end(); ++i)
    125     if (*i == m) break;
    126 
    127   backends.push_back(m);
    128 }
    129 
    130 //--------------------------------------------------------------------
    131 Mailbox *Depot::get(const string &s_in) const
    132 {
    133   for (vector<Mailbox *>::const_iterator i = backends.begin();
    134        i != backends.end(); ++i)
    135     if ((*i)->isMailbox(mailboxToFilename(s_in)))
    136       return *i;
    137 
    138   setLastError("No such mailbox " + toImapString(s_in));
    139   return 0;
    140 }
    141 
    142 //--------------------------------------------------------------------
    143 bool Depot::setSelected(Mailbox *m)
    144 {
    145   for (vector<Mailbox *>::const_iterator i = backends.begin();
    146        i != backends.end(); ++i)
    147     if (*i == m) {
    148       selectedmailbox = m;
    149       return true;
    150     }
    151 
    152   setLastError("Attempted to select unregistered Mailbox type in Depot");
    153   return false;
    154 }
    155 
    156 //--------------------------------------------------------------------
    157 const string &Depot::getName(void) const
    158 {
    159   return name;
    160 }
    161 
    162 //--------------------------------------------------------------------
    163 void Depot::setDelimiter(char c)
    164 {
    165   delimiter = c;
    166 }
    167 
    168 //--------------------------------------------------------------------
    169 const char Depot::getDelimiter(void) const
    170 {
    171   return delimiter;
    172 }
    173 
    174 //--------------------------------------------------------------------
    175 bool Depot::setDefaultType(const string &name)
    176 {
    177   for (vector<Mailbox *>::const_iterator i = backends.begin();
    178        i != backends.end(); ++i)
    179     if ((*i)->getTypeName() == name) {
    180       defaultmailbox = *i;
    181       return true;
    182     }
    183 
    184   setLastError("attempt to default to unregistered Mailbox type " + name);
    185   return false;
    186 }
    187 
    188 //--------------------------------------------------------------------
    189 Mailbox *Depot::getSelected(void) const
    190 {
    191   return selectedmailbox;
    192 }
    193 
    194 //--------------------------------------------------------------------
    195 void Depot::resetSelected(void)
    196 {
    197   selectedmailbox = 0;
    198 }
    199 
    200 //--------------------------------------------------------------------
    201 Mailbox *Depot::getDefault(void) const
    202 {
    203   return defaultmailbox;
    204 }
    205 
    206 //--------------------------------------------------------------------
    207 bool Depot::createMailbox(const string &s_in) const
    208 {
    209   const string &mailboxname = mailboxToFilename(toCanonMailbox(s_in));
    210   if (mailboxname == "")
    211     return false;
    212 
    213   Mailbox *mailbox = getDefault();
    214   if (mailbox == 0) {
    215     setLastError("no default mailbox defined");
    216     return false;
    217   }
    218 
    219   bool result = mailbox->createMailbox(mailboxname, 0777);
    220   if (result)
    221     return true;
    222   else {
    223     setLastError(mailbox->getLastError());
    224     return false;
    225   }
    226 }
    227 
    228 //--------------------------------------------------------------------
    229 bool Depot::deleteMailbox(const string &s_in) const
    230 {
    231   const string &mailboxname = mailboxToFilename(toCanonMailbox(s_in));
    232   if (mailboxname == "")
    233     return false;
    234 
    235   Mailbox *mailbox = get(s_in);
    236   if (mailbox == 0) {
    237     setLastError(s_in + ": no such mailbox");
    238     return false;
    239   }
    240 
    241   bool result = mailbox->deleteMailbox(mailboxname);
    242   if (result)
    243     return true;
    244   else {
    245     setLastError(mailbox->getLastError());
    246     return false;
    247   }
    248 }
    249 
    250 //--------------------------------------------------------------------
    251 bool Depot::renameMailbox(const string &s_in, const string &t_in) const
    252 {
    253   IO &logger = IOFactory::getInstance().get(2);
    254     
    255   const string &source = mailboxToFilename(s_in).c_str();
    256   const string &dest = mailboxToFilename(t_in).c_str();
    257   if (source == "" || dest == "")
    258     return false;
    259 
    260   int nrenamed = 0;
    261   const iterator e = end();
    262   for (iterator i = begin("."); i != e; ++i) {
    263     string entry = *i;
    264 
    265     if (entry.substr(0, source.length()) == source) {
    266       string sourcename, destname;
    267 
    268       if (entry.length() == source.length()) {
    269 	sourcename = source;
    270 	destname = dest;
    271 
    272       } else if (entry.length() > source.length() 
    273 		 && entry[source.length()] == '.') {
    274 	sourcename = entry;
    275 	destname = dest + entry.substr(source.length());
    276       } else continue;
    277 
    278       if (rename(sourcename.c_str(), destname.c_str()) != 0) {
    279 	logger << "error renaming " << sourcename << " to " 
    280 	       << destname << ": " << strerror(errno) << endl;
    281       } else
    282 	nrenamed++;
    283       
    284       Mailbox *mailbox;
    285       if ((mailbox = get(filenameToMailbox(sourcename))) != 0)
    286 	mailbox->bumpUidValidity(filenameToMailbox(sourcename));
    287       if ((mailbox = get(filenameToMailbox(destname))) != 0)
    288 	mailbox->bumpUidValidity(filenameToMailbox(destname));
    289     }
    290   }
    291 
    292   if (nrenamed == 0) {
    293     setLastError("An error occurred when renaming " 
    294 		 + toImapString(s_in)
    295 		 + " to " + toImapString(t_in)
    296 		 + ". Try creating a new mailbox,"
    297 		 " then copy over all messages."
    298 		 " Finally, delete the original mailbox");
    299     return false;
    300   } else
    301     return true;
    302 }
    303 
    304 //--------------------------------------------------------------------
    305 bool Depot::getStatus(const std::string &s_in, Status &dest) const
    306 {
    307   const string mailbox = toCanonMailbox(s_in);
    308   if (mailbox == "") {
    309     setLastError("Unrecognized mailbox: " + toImapString(s_in));
    310     return false;
    311   }
    312 
    313   string mailboxFilename = mailboxToFilename(mailbox);
    314   if (mailboxFilename == "")
    315     return false;
    316 
    317   Mailbox *m = get(mailbox);
    318   if (m == 0) {
    319     setLastError("Unrecognized mailbox: " + toImapString(s_in));
    320     return false;
    321   }
    322 
    323   int statusid = m->getStatusID(mailboxFilename);
    324   if (mailboxstatuses.find(mailbox) != mailboxstatuses.end()) {
    325     dest = mailboxstatuses[mailbox];
    326     if (dest.getStatusID() == statusid)
    327       return true;
    328   }
    329 
    330   if (!m->getStatus(mailboxFilename, dest)) {
    331     setLastError(m->getLastError());
    332     return false;
    333   }
    334 
    335   dest.setStatusID(statusid);
    336   mailboxstatuses[mailbox] = dest;
    337   return true;
    338 }
    339 
    340 //--------------------------------------------------------------------
    341 Depot::iterator::iterator(void)
    342 {
    343   dirp = 0;
    344   ref = new int;
    345   *ref = 1;
    346 }
    347 
    348 //--------------------------------------------------------------------
    349 Depot::iterator::iterator(DIR *dp, struct dirent *sp)
    350 {
    351   dirp = dp;
    352   direntp = sp;
    353 
    354   ref = new int;
    355   *ref = 1;
    356 }
    357 
    358 //--------------------------------------------------------------------
    359 Depot::iterator::iterator(const iterator &copy)
    360 {
    361   if (*copy.ref != 0)
    362     ++(*copy.ref);
    363 
    364   ref = copy.ref;
    365   dirp = copy.dirp;
    366   direntp = copy.direntp;
    367 }
    368 
    369 //--------------------------------------------------------------------
    370 Depot::iterator::~iterator(void)
    371 {
    372   deref();
    373 }
    374 
    375 //--------------------------------------------------------------------
    376 Depot::iterator &Depot::iterator::operator =(const iterator &copy)
    377 {
    378   if (*copy.ref != 0)
    379     ++(*copy.ref);
    380 
    381   deref();
    382 
    383   ref = copy.ref;
    384   dirp = copy.dirp;
    385   direntp = copy.direntp;
    386 
    387   return *this;
    388 }
    389 
    390 //--------------------------------------------------------------------
    391 void Depot::iterator::deref(void)
    392 {
    393   // decrease existing copy ref if there is one
    394   if (*ref != 0 && --(*ref) == 0) {
    395     if (dirp) {
    396       closedir(dirp);
    397       dirp = 0;
    398     }
    399 
    400     delete ref;
    401     ref = 0;
    402   }
    403 }
    404 
    405 //--------------------------------------------------------------------
    406 string Depot::iterator::operator * (void) const
    407 {
    408   if (direntp == 0)
    409     return "";
    410 
    411   return direntp->d_name;
    412 }
    413 
    414 //--------------------------------------------------------------------
    415 void Depot::iterator::operator ++ (void)
    416 {
    417   direntp = readdir(dirp);
    418 }
    419 
    420 //--------------------------------------------------------------------
    421 bool Depot::iterator::operator == (Depot::iterator i) const
    422 {
    423   return direntp == i.direntp;
    424 }
    425 
    426 //--------------------------------------------------------------------
    427 bool Depot::iterator::operator != (Depot::iterator i) const
    428 {
    429   return direntp != i.direntp;
    430 }
    431 
    432 //--------------------------------------------------------------------
    433 Depot::iterator Depot::begin(const string &path) const
    434 {
    435   Depot::iterator i;
    436 
    437   if ((i.dirp = opendir(path.c_str())) == 0) {
    438     IO &logger = IOFactory::getInstance().get(2);
    439 
    440     logger << "opendir on " + path + " failed" << endl;
    441     setLastError("opendir on " + path + " failed");
    442     return end();
    443   }
    444 
    445   ++i;
    446   return i;
    447 }
    448 
    449 //--------------------------------------------------------------------
    450 const Depot::iterator &Depot::end(void) const
    451 {
    452   return enditerator;
    453 }
    454 
    455 //--------------------------------------------------------------------
    456 MaildirPPDepot::MaildirPPDepot(void) : Depot("Maildir++")
    457 {
    458 }
    459 
    460 //--------------------------------------------------------------------
    461 MaildirPPDepot::~MaildirPPDepot(void)
    462 {
    463 }
    464 
    465 //--------------------------------------------------------------------
    466 string MaildirPPDepot::mailboxToFilename(const string &m) const
    467 {
    468   string prefix = "INBOX"; prefix += delimiter;
    469 
    470   string mm = m;
    471   trim(mm, string(&delimiter, 1));
    472   string tmp = mm;
    473   uppercase(tmp);
    474   if (tmp != "INBOX" && tmp.substr(0, 6) != "INBOX/") {
    475     setLastError("With a Maildir++ depot, you must create all"
    476 		 " mailboxes under INBOX. Try creating"
    477 		 " INBOX/" + mm + ".");
    478     return "";
    479   }
    480 
    481   string twodelim;
    482   twodelim += delimiter;
    483   twodelim += delimiter;
    484 
    485   if (mm == "INBOX") return ".";
    486   else if (mm.length() <= 6) {
    487     setLastError("With a Maildir++ depot, you must create all"
    488 		 " mailboxes under INBOX.");
    489     return "";
    490   } else if (mm.substr(0, 6) != prefix) {
    491     setLastError("With a Maildir++ depot, you must create all"
    492 		 " mailboxes under INBOX.");
    493     return "";
    494   } else if (mm.find(twodelim) != string::npos) {
    495     setLastError("Invalid character combination " 
    496 		 + twodelim + " in mailbox name");
    497     return "";
    498   } else if (mm != "" && mm.substr(1).find('.') != string::npos) {
    499     setLastError("Invalid character '.' in mailbox name");
    500     return "";
    501   } else {
    502     string tmp = mm.substr(6);
    503     for (string::iterator i = tmp.begin(); i != tmp.end(); ++i)
    504       if (*i == '/') *i = '.';
    505 
    506     return "." + tmp;
    507   }
    508 }
    509 
    510 //--------------------------------------------------------------------
    511 string MaildirPPDepot::filenameToMailbox(const string &m) const
    512 {
    513   if (m == "." || m == "..") 
    514     return "INBOX";
    515   else if (m.find(delimiter) != string::npos) 
    516     return "";
    517   else if (m != "" && m[0] == '.') {
    518     string tmp = m;
    519     for (string::iterator i = tmp.begin(); i != tmp.end(); ++i)
    520       if (*i == '.') *i = delimiter;
    521 
    522     return "INBOX" + tmp;
    523   } else 
    524     return "";
    525 }
    526 
    527 //--------------------------------------------------------------------
    528 IMAPdirDepot::IMAPdirDepot(void) : Depot("IMAPdir")
    529 {
    530 }
    531 
    532 //--------------------------------------------------------------------
    533 IMAPdirDepot::~IMAPdirDepot(void)
    534 {
    535 }
    536 
    537 //--------------------------------------------------------------------
    538 string IMAPdirDepot::mailboxToFilename(const string &m) const
    539 {
    540   string tmp;
    541   string mm = m;
    542   trim(mm, string(&delimiter, 1));
    543 
    544   string twodelim;
    545   twodelim += delimiter;
    546   twodelim += delimiter;
    547 
    548   if (mm.find(twodelim) != string::npos) {
    549     setLastError("Invalid character combination " 
    550 		 + twodelim + " in mailbox name");
    551     return "";
    552   }
    553 
    554   string::const_iterator i = mm.begin();
    555   while (i != mm.end()) {
    556     if (*i == delimiter) {
    557       tmp += '.';
    558     } else if (*i == '\\') {
    559       tmp += "\\\\";
    560     } else if (*i == '.') {
    561       if (i == mm.begin())
    562 	tmp += ".";
    563       else
    564 	tmp += "\\.";
    565     } else {
    566       tmp += *i;
    567     }
    568 
    569     ++i;
    570   }
    571 
    572   return tmp;
    573 }
    574 
    575 //--------------------------------------------------------------------
    576 string IMAPdirDepot::filenameToMailbox(const string &m) const
    577 {
    578   string tmp;
    579   bool escape = false;
    580 
    581   // hide the magic "." mailbox.
    582   if (m == "." || m == "..")
    583     return "";
    584 
    585   string::const_iterator i = m.begin();
    586   while (i != m.end()) {
    587     if (*i == '.') {
    588       if (i != m.begin() && !escape) tmp += delimiter;
    589       else if (i == m.begin() || escape) tmp += '.';
    590       escape = false;
    591     } else if (*i == '\\') {
    592       if (!escape) escape = true; else {
    593 	tmp += '\\';
    594 	escape = false;
    595       }
    596     } else if (*i == delimiter) {
    597       return "";
    598     } else {
    599       if (escape) return "";
    600       else {
    601 	tmp += *i;
    602 	escape = false;
    603       }
    604     }
    605 
    606     ++i;
    607   }
    608 
    609   return tmp;
    610 }