bincimap

Log | Files | Refs | LICENSE

maildir.cc (22130B)


      1 /* -*- Mode: c++; -*- */
      2 /*  --------------------------------------------------------------------
      3  *  Filename:
      4  *    maildir.cc
      5  *  
      6  *  Description:
      7  *    Implementation of the Maildir class.
      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 #ifdef HAVE_CONFIG_H
     35 #include <config.h>
     36 #endif
     37 
     38 #include <iostream>
     39 #include <iomanip>
     40 #include <algorithm>
     41 
     42 #include <ctype.h>
     43 #include <dirent.h>
     44 #include <errno.h>
     45 #include <fcntl.h>
     46 #include <stdio.h>
     47 #include <sys/stat.h>
     48 #include <sys/types.h>
     49 #include <unistd.h>
     50 
     51 #include "io.h"
     52 #include "session.h"
     53 #include "status.h"
     54 #include "storage.h"
     55 #include "convert.h"
     56 #include "maildir.h"
     57 #include "maildirmessage.h"
     58 #include "pendingupdates.h"
     59 
     60 using namespace ::std;
     61 using namespace Binc;
     62 
     63 // Used to generate the unique names for Maildir delivery
     64 static int numDeliveries = 0;
     65 
     66 //------------------------------------------------------------------------
     67 Maildir::iterator::iterator(void)
     68 {
     69 }
     70 
     71 //------------------------------------------------------------------------
     72 Maildir::iterator::iterator(Maildir *home,
     73 			    MessageMap::iterator it,
     74 			    const SequenceSet &_bset,
     75 			    unsigned int _mod) 
     76   : BaseIterator(1), mailbox(home), bset(_bset), mod(_mod), i(it)
     77 {
     78   uidmax = home->getMaxUid();
     79   sqnrmax = home->getMaxSqnr();
     80 }
     81 
     82 //------------------------------------------------------------------------
     83 Maildir::iterator::iterator(const iterator &copy)
     84   : BaseIterator(copy.sqnr), mailbox(copy.mailbox),
     85     bset(copy.bset), mod(copy.mod), i(copy.i), uidmax(copy.uidmax),
     86     sqnrmax(copy.sqnrmax)
     87 {
     88 }
     89 
     90 //------------------------------------------------------------------------
     91 Maildir::iterator &Maildir::iterator::operator =(const iterator &copy)
     92 {
     93   sqnr = copy.sqnr;
     94   mailbox = copy.mailbox;
     95   bset = copy.bset;
     96   mod = copy.mod;
     97   i = copy.i;
     98   uidmax = copy.uidmax;
     99   sqnrmax = copy.sqnrmax;
    100   return *this;
    101 }
    102 
    103 //------------------------------------------------------------------------
    104 Maildir::iterator::~iterator(void)
    105 {
    106 }
    107 
    108 //------------------------------------------------------------------------
    109 MaildirMessage &Maildir::iterator::curMessage(void)
    110 {
    111   return i->second;
    112 }
    113 
    114 //------------------------------------------------------------------------
    115 Message &Maildir::iterator::operator *(void)
    116 {
    117   return curMessage();
    118 }
    119 
    120 //------------------------------------------------------------------------
    121 void Maildir::iterator::operator ++(void)
    122 {
    123   ++i;
    124   ++sqnr;
    125   reposition();
    126 }
    127 
    128 //------------------------------------------------------------------------
    129 bool Maildir::iterator::operator ==(const BaseIterator &a) const
    130 {
    131   const iterator *b = dynamic_cast<const iterator *>(&a);
    132   return b ? (i == b->i) : false;
    133 }
    134 
    135 //------------------------------------------------------------------------
    136 bool Maildir::iterator::operator !=(const BaseIterator &a) const
    137 {
    138   return !((*this) == a);
    139 }
    140 
    141 //------------------------------------------------------------------------
    142 void Maildir::iterator::reposition(void)
    143 {
    144   for (;;) {
    145     if (i == mailbox->messages.end())
    146       break;
    147 
    148     Message &message = curMessage();
    149     if ((mod & SKIP_EXPUNGED) && message.isExpunged()) {
    150       ++i;
    151       continue;
    152     }
    153 
    154     bool inset = false;
    155     if (mod & SQNR_MODE) {
    156       if (bset.isInSet(sqnr) || (!bset.isLimited() && sqnr == sqnrmax))
    157 	inset = true;
    158     } else {
    159       if (bset.isInSet(message.getUID()) || (!bset.isLimited() && message.getUID() == uidmax))
    160 	inset = true;
    161     }
    162 
    163     if (!inset) {
    164       ++i;
    165       if (!message.isExpunged())
    166 	++sqnr;
    167       continue;
    168     }
    169 
    170     break;
    171   }
    172 }
    173 
    174 //------------------------------------------------------------------------
    175 Mailbox::iterator Maildir::begin(const SequenceSet &bset,
    176 				 unsigned int mod) const
    177 {
    178   beginIterator = iterator((Maildir *)this, messages.begin(), bset, mod);
    179   beginIterator.reposition();
    180 
    181   return Mailbox::iterator(beginIterator);
    182 }
    183 
    184 //------------------------------------------------------------------------
    185 Mailbox::iterator Maildir::end(void) const
    186 {
    187   endIterator = iterator((Maildir *)this, messages.end(),
    188 			 endIterator.bset, endIterator.mod);
    189   return Mailbox::iterator(endIterator);
    190 }
    191 
    192 //------------------------------------------------------------------------
    193 void Maildir::iterator::erase(void)
    194 {
    195   MessageMap::iterator iter = i;
    196   ++iter;
    197    
    198   MaildirMessageCache::getInstance().removeStatus(&curMessage());
    199   mailbox->index.remove(i->second.getUnique());
    200   mailbox->messages.erase(i);
    201 
    202   i = iter;
    203   reposition();
    204 }
    205 
    206 //------------------------------------------------------------------------
    207 Maildir::Maildir(void) : Mailbox()
    208 {
    209   firstscan = true;
    210   cacheRead = false;
    211   uidvalidity = 0;
    212   uidnext = 1;
    213   selected = false;
    214   oldrecent = 0;
    215   oldexists = 0;
    216 }
    217 
    218 //------------------------------------------------------------------------
    219 Maildir::~Maildir(void)
    220 {
    221 }
    222 
    223 //------------------------------------------------------------------------
    224 void Maildir::setPath(const string &path_in)
    225 {
    226   path = path_in;
    227 }
    228 
    229 //------------------------------------------------------------------------
    230 bool Maildir::getUpdates(bool doscan, unsigned int type,
    231 			 PendingUpdates &updates, bool forceScan)
    232 {
    233   if (doscan && scan(forceScan) != Success)
    234     return false;
    235 
    236   unsigned int exists = 0;
    237   unsigned int recent = 0;
    238   bool displayExists = false;
    239 
    240   // count messages, find recent
    241   if (!readOnly && (type & PendingUpdates::EXPUNGE)) {  
    242     Mailbox::iterator i = begin(SequenceSet::all(),
    243 				INCLUDE_EXPUNGED | SQNR_MODE);
    244 
    245     while (i != end()) {
    246       Message &message = *i;
    247 
    248       if (message.isExpunged()) {
    249 	updates.addExpunged(i.getSqnr());
    250 	i.erase();
    251 	mailboxchanged = true;
    252 	displayExists = true;
    253       } else
    254 	++i;
    255     }
    256   }
    257 
    258   Mailbox::iterator i = begin(SequenceSet::all(),
    259 			      INCLUDE_EXPUNGED | SQNR_MODE);
    260   for (; i != end(); ++i) {
    261     Message & message = *i;
    262     // at this point, there is a message that is not expunged
    263     ++exists;
    264     if (message.getStdFlags() & Message::F_RECENT) ++recent;
    265   }
    266 
    267   if (displayExists || exists != oldexists)
    268     updates.setExists(oldexists = exists);
    269 
    270   if (recent != oldrecent)
    271     updates.setRecent(oldrecent = recent);
    272   
    273   if (type & PendingUpdates::FLAGS) {   
    274     Mailbox::iterator i = begin(SequenceSet::all(), SQNR_MODE);
    275     for (; i != end(); ++i) {
    276       Message &message = *i;
    277       if (message.hasFlagsChanged()) {
    278 	int flags = message.getStdFlags();
    279 	
    280 	updates.addFlagUpdates(i.getSqnr(), message.getUID(), flags);
    281 
    282 	message.setFlagsUnchanged();
    283       }
    284     }
    285   }  
    286 
    287   return true;
    288 }
    289 
    290 //------------------------------------------------------------------------
    291 bool Maildir::isMailbox(const std::string &s_in) const
    292 {
    293   if (s_in == "")
    294     return false;
    295 
    296   struct stat mystat;
    297 
    298   return ((stat((s_in + "/cur").c_str(), &mystat) == 0
    299 	   && S_ISDIR(mystat.st_mode))
    300       && (stat((s_in + "/new").c_str(), &mystat) == 0 
    301 	  && S_ISDIR(mystat.st_mode))
    302       && (stat((s_in + "/tmp").c_str(), &mystat) == 0 
    303 	  && S_ISDIR(mystat.st_mode)));
    304 }
    305 
    306 //------------------------------------------------------------------------
    307 const std::string Maildir::getTypeName(void) const
    308 {
    309   return "Maildir";
    310 }
    311 
    312 //------------------------------------------------------------------------
    313 void Maildir::bumpUidValidity(const string &s_in) const
    314 {
    315   unlink((s_in + "/bincimap-uidvalidity").c_str());
    316   unlink((s_in + "/bincimap-cache").c_str());
    317 }
    318 
    319 //------------------------------------------------------------------------
    320 bool Maildir::isMarked(const std::string &s_in) const
    321 {
    322   DIR *dirp = opendir((s_in + "/new").c_str());
    323   if (dirp == 0) return false;
    324 
    325   struct dirent *direntp;  
    326   while ((direntp = readdir(dirp)) != 0) {
    327     string s = direntp->d_name;
    328     if (s[0] != '.' 
    329 	&& s.find('/') == string::npos
    330 	&& s.find(':') == string::npos) {
    331       closedir(dirp);
    332       return true;
    333     }
    334   }
    335 
    336   closedir(dirp);
    337   return false;
    338 }
    339 
    340 //------------------------------------------------------------------------
    341 unsigned int Maildir::getStatusID(const string &path) const 
    342 {
    343   if (path == "")
    344     return 0;
    345 
    346   unsigned int statusid = 0;
    347   struct stat mystat;
    348   if (stat((path + "/new").c_str(), &mystat) == 0)
    349     statusid = mystat.st_ctime;
    350 
    351   if (stat((path + "/cur").c_str(), &mystat) == 0)
    352     statusid += mystat.st_ctime;
    353 
    354   if (stat((path + "/bincimap-cache").c_str(), &mystat) == 0)
    355     statusid += mystat.st_ctime;
    356 
    357   return statusid;
    358 }
    359 
    360 //------------------------------------------------------------------------
    361 bool Maildir::getStatus(const string &path, Status &s) const 
    362 {
    363   unsigned int messages = 0;
    364   unsigned int unseen = 0;
    365   unsigned int recent = 0;
    366 
    367   const string cachefilename = path + "/bincimap-cache";
    368   const string uidvalfilename = path + "/bincimap-uidvalidity";
    369 
    370   Storage cache(cachefilename, Storage::ReadOnly);
    371   Storage uidvalfile(uidvalfilename, Storage::ReadOnly);
    372 
    373   string section, key, value;
    374   map<string, bool> mincache;
    375   while (cache.get(&section, &key, &value))
    376     if (isdigit(section[0]) && key == "_ID")
    377       mincache[value] = true;
    378 
    379   unsigned int uidvalidity = 0;
    380   unsigned int uidnext = 0;
    381   while (uidvalfile.get(&section, &key, &value))
    382     if (section == "depot" && key == "_uidvalidity")
    383       uidvalidity = (unsigned int) atoi(value);
    384     else if (section == "depot" && key == "_uidnext")
    385       uidnext  = (unsigned int) atoi(value);
    386 
    387   s.setUidValidity(uidvalidity < 1 ? time(0) : uidvalidity);
    388 
    389   // Scan new
    390   DIR *dirp = opendir((path + "/new").c_str());
    391   if (dirp == 0) return false;
    392 
    393   struct dirent *direntp;  
    394   while ((direntp = readdir(dirp)) != 0) {
    395     const string filename = direntp->d_name;
    396     if (filename[0] == '.'
    397 	|| filename.find(':') != string::npos
    398 	|| filename.find('/') != string::npos)
    399       continue;
    400 
    401     ++recent;
    402     ++uidnext;
    403     ++unseen;
    404     ++messages;
    405   }
    406 
    407   closedir(dirp);
    408 
    409   // Scan cur
    410   if ((dirp = opendir((path + "/cur").c_str())) == 0)
    411     return false;
    412 
    413   while ((direntp = readdir(dirp)) != 0) {
    414     const string dname = direntp->d_name;
    415     if (dname[0] == '.')
    416       continue;
    417 
    418     ++messages;
    419 
    420     // Add to unseen if it doesn't have the seen flag or if it has no
    421     // flags.
    422     const string::size_type pos = dname.find(':');
    423     if (pos != string::npos) {
    424       if (mincache.find(dname.substr(0, pos)) == mincache.end()) {
    425 	++recent;
    426 	++uidnext;
    427       }
    428 
    429       if (dname.substr(pos).find('S') == string::npos)
    430 	++unseen;
    431     } else {
    432       if (mincache.find(dname) == mincache.end()) {
    433 	++recent;
    434 	++uidnext;
    435       }
    436 
    437       ++unseen;
    438     }
    439   }
    440 
    441   closedir(dirp);
    442   
    443   s.setRecent(recent);
    444   s.setMessages(messages);
    445   s.setUnseen(unseen);
    446   s.setUidNext(uidnext);
    447 
    448   return true;
    449 }
    450 
    451 //------------------------------------------------------------------------
    452 unsigned int Maildir::getMaxUid(void) const
    453 {
    454   MessageMap::const_iterator i = messages.end();
    455   if (i == messages.begin())
    456     return 0;
    457 
    458   --i;
    459   for (;;) {
    460     const MaildirMessage &message = i->second;
    461     if (message.isExpunged()) {
    462       if (i == messages.begin())
    463 	return 0;
    464       --i;
    465     } else {
    466       return message.getUID();
    467     }
    468   }
    469 
    470   return 0;
    471 }
    472 
    473 //------------------------------------------------------------------------
    474 unsigned int Maildir::getMaxSqnr(void) const
    475 {
    476   int sqnr = messages.size();
    477   MessageMap::const_iterator i = messages.end();
    478   if (i == messages.begin())
    479     return 0;
    480 
    481   --i;
    482   for (;;) {
    483     const MaildirMessage &message = i->second;
    484     if (message.isExpunged()) {
    485       if (i == messages.begin())
    486 	return 0;
    487       --sqnr;
    488       --i;
    489     } else {
    490       return sqnr;
    491     }
    492   }
    493 
    494   return 0;
    495 }
    496 
    497 //------------------------------------------------------------------------
    498 unsigned int Maildir::getUidValidity(void) const
    499 {
    500   return uidvalidity;
    501 }
    502 
    503 //------------------------------------------------------------------------
    504 unsigned int Maildir::getUidNext(void) const
    505 {
    506   return uidnext;
    507 }
    508 
    509 //------------------------------------------------------------------------
    510 Message *Maildir::createMessage(const string &mbox, time_t idate)
    511 {
    512   string sname = mbox + "/tmp/bincimap-XXXXXX";
    513   char *safename = strdup(sname.c_str());
    514 
    515   int fd = mkstemp(safename);
    516   if (fd == -1) {
    517     setLastError("Unable to create safe name.");
    518     return 0;
    519   }
    520 
    521   string safenameStr = safename;
    522   delete safename;
    523 
    524   MaildirMessage message(*this);
    525 
    526   message.setFile(fd);
    527   message.setSafeName(safenameStr);
    528   message.setInternalDate(idate);
    529 
    530   newMessages.push_back(message);
    531   vector<MaildirMessage>::iterator i = newMessages.end();
    532   --i;
    533   return &(*i);
    534 }
    535 
    536 //------------------------------------------------------------------------
    537 bool Maildir::commitNewMessages(const string &mbox)
    538 {
    539   Session &session = Session::getInstance();
    540   IO &logger = IOFactory::getInstance().get(2);
    541 
    542   vector<MaildirMessage>::iterator i = newMessages.begin();
    543   map<MaildirMessage *, string> committedMessages;
    544 
    545   struct timeval youngestFile = {0, 0};
    546   
    547   bool abort = false;
    548   while (!abort && i != newMessages.end()) {
    549     MaildirMessage &m = *i;
    550 
    551     if (m.getInternalFlags() & MaildirMessage::Committed) {
    552       ++i;
    553       continue;
    554     }
    555 
    556     m.setInternalFlag(MaildirMessage::Committed);
    557 
    558     string safeName = m.getSafeName();
    559 
    560     for (int attempt = 0; !abort && attempt < 1000; ++attempt) {
    561       struct timeval tv;
    562       gettimeofday(&tv, 0);
    563       youngestFile = tv;
    564 
    565       // Generate Maildir conformant file name
    566       BincStream ssid;
    567       ssid  << (int) tv.tv_sec << "."
    568 	    << "R" << (int) rand()
    569 	    << "M" << (int) tv.tv_usec
    570 	    << "P" << (int) session.getPid()
    571 	    << "Q" << numDeliveries++
    572 	    << "." << session.getHostname();
    573       
    574       BincStream ss;
    575       ss << mbox << "/new/" << ssid.str();
    576       
    577       string fileName = ss.str();
    578 
    579       if (link(safeName.c_str(), fileName.c_str()) == 0) {
    580 	unlink(safeName.c_str());
    581 	m.setInternalDate(tv.tv_sec);
    582 	m.setUnique(ssid.str());
    583 	m.setUID(0);
    584 	committedMessages[&m] = fileName;
    585 	break;
    586       }
    587 
    588       if (errno == EEXIST)
    589 	continue;
    590 
    591       logger << "Warning: link(" << toImapString(safeName) << ", " 
    592 	     << toImapString(fileName) << ") failed: "
    593 	     << strerror(errno) << endl;
    594 
    595       session.setResponseCode("TRYCREATE");
    596       session.setLastError("failed, internal error.");
    597       abort = true;
    598       break;
    599     }
    600 
    601     ++i;
    602   }
    603 
    604   // abort means to make an attempt to restore the mailbox to
    605   // its original state.
    606   if (abort) {
    607     // Fixme: Messages that are in committedMessages should be skipped
    608     // here.
    609     for (i = newMessages.begin(); i != newMessages.end(); ++i)
    610       unlink((*i).getSafeName().c_str());
    611 
    612     map<MaildirMessage *, string>::const_iterator j
    613       = committedMessages.begin();
    614     while (j != committedMessages.end()) {
    615       if (unlink(j->second.c_str()) != 0) {
    616 	if (errno == ENOENT) {
    617 	  // FIXME: The message was probably moves away from new/ by
    618 	  // another IMAP session.
    619 	  logger << "error rollbacking after failed commit to "
    620 		 << toImapString(mbox) << ", failed to unlink "
    621 		 << toImapString(j->second) 
    622 		 << ": " << strerror(errno) << endl;
    623 	} else {
    624 	  logger << "error rollbacking after failed commit to "
    625 		 << toImapString(mbox) << ", failed to unlink "
    626 		 << toImapString(j->second) 
    627 		 << ": " << strerror(errno) << endl;
    628 	  newMessages.clear();
    629 	  return false;
    630 	}
    631       }
    632 
    633       ++j;
    634     }
    635 
    636     newMessages.clear();
    637     return false;
    638   }
    639 
    640   // cover the extremely unlikely event that another concurrent
    641   // Maildir accessor has just made a file with the same timestamp and
    642   // random number by spinning until the timestamp has changed before
    643   // moving the message into cur.
    644   struct timeval tv;
    645   gettimeofday(&tv, 0);
    646   while (tv.tv_sec == youngestFile.tv_sec 
    647 	 && tv.tv_usec == youngestFile.tv_usec) {
    648     gettimeofday(&tv, 0);
    649   }
    650 
    651   map<MaildirMessage *, string>::const_iterator j
    652     = committedMessages.begin();
    653   for (;j != committedMessages.end(); ++j) {
    654     string basename = j->second.substr(j->second.rfind('/') + 1);
    655 
    656     int flags = j->first->getStdFlags();
    657     string flagStr;
    658     if (flags & Message::F_DRAFT) flagStr += "D";
    659     if (flags & Message::F_FLAGGED) flagStr += "F";
    660     if (flags & Message::F_ANSWERED) flagStr += "R";
    661     if (flags & Message::F_SEEN) flagStr += "S";
    662     if (flags & Message::F_DELETED) flagStr += "T";
    663     
    664     string dest = mbox + "/cur/" + basename + ":2," + flagStr;
    665     if (rename(j->second.c_str(), dest.c_str()) == 0)
    666       continue;
    667 
    668     if (errno != ENOENT)
    669       logger << "when setting flags on: " << j->second 
    670 	     << ": " << strerror(errno) << endl;
    671   }
    672 
    673   committedMessages.clear();
    674   return true;
    675 }
    676 
    677 //------------------------------------------------------------------------
    678 bool Maildir::rollBackNewMessages(void)
    679 {
    680   vector<MaildirMessage>::const_iterator i = newMessages.begin();
    681   // Fixme: Messages that are in committedMessages should be skipped
    682   // here.
    683   for (; i != newMessages.end(); ++i)
    684     unlink((*i).getSafeName().c_str());
    685 
    686   newMessages.clear();
    687   return true;
    688 }
    689 
    690 //------------------------------------------------------------------------
    691 bool Maildir::fastCopy(Message &m, Mailbox &desttype,
    692 		       const std::string &destname)
    693 {
    694   // At this point, fastCopy is broken because the creation time is
    695   // equal for the two clones. The new clone must have a new creation
    696   // time. Symlinks are a possibility, but they break if other Maildir
    697   // accessors rename mailboxes.
    698   //  return false;
    699 
    700   Session &session = Session::getInstance();
    701   IO &logger = IOFactory::getInstance().get(2);
    702 
    703   MaildirMessage *message = dynamic_cast<MaildirMessage *>(&m);
    704   if (!message)
    705     return false;
    706 
    707   string mfilename = message->getFileName();
    708   if (mfilename == "")
    709     return false;
    710 
    711   Maildir *mailbox = dynamic_cast<Maildir *>(&desttype);
    712   if (!mailbox)
    713     return false;
    714 
    715   for (int attempt = 0; attempt < 1000; ++attempt) {
    716 
    717     struct timeval tv;
    718     gettimeofday(&tv, 0);
    719 
    720     // Generate Maildir conformant file name
    721     BincStream ssid;
    722     ssid  << (int) tv.tv_sec << "."
    723 	  << "R" << (int) rand()
    724 	  << "M" << (int) tv.tv_usec
    725 	  << "P" << (int) session.getPid()
    726 	  << "Q" << numDeliveries++
    727 	  << "." << session.getHostname();
    728 
    729     BincStream ss;
    730     ss << destname << "/tmp/" << ssid.str();
    731     
    732     string fileName = ss.str();
    733     
    734     if (link((path + "/cur/" + mfilename).c_str(), fileName.c_str()) == 0) {
    735       MaildirMessage newmess = *message;
    736       newmess.setSafeName(fileName);
    737       newmess.setUnique(ssid.str());
    738       newmess.setInternalDate((time_t) tv.tv_sec);
    739       newmess.setUID(0);
    740       newMessages.push_back(newmess);
    741       break;
    742     }
    743     
    744     if (errno == EEXIST)
    745       continue;
    746     
    747     logger << "Warning: link(" << toImapString(path + "/cur/" + mfilename) 
    748 	   << ", " << toImapString(fileName) << ") failed: "
    749 	   << strerror(errno) << endl;
    750     
    751     session.setResponseCode("TRYCREATE");
    752     session.setLastError("failed, internal error.");
    753     return false;
    754   }
    755 
    756   return true;
    757 }
    758 
    759 //------------------------------------------------------------------------
    760 MaildirMessage *Maildir::get(const std::string &id)
    761 {
    762   MaildirIndexItem *item = index.find(id);
    763   if (!item)
    764     return 0;
    765 
    766   unsigned int uid = item->uid;
    767   if (messages.find(uid) == messages.end())
    768     return 0;
    769 
    770   return &messages.find(uid)->second;
    771 }
    772 
    773 //------------------------------------------------------------------------
    774 void Maildir::add(MaildirMessage &m)
    775 {
    776   MessageMap::iterator it = messages.find(m.getUID());
    777   if (it != messages.end())
    778     messages.erase(it);
    779   messages.insert(make_pair(m.getUID(), m));
    780   index.insert(m.getUnique(), m.getUID());
    781 }
    782 
    783 //------------------------------------------------------------------------
    784 unsigned int MaildirIndex::getSize(void) const
    785 {
    786   return idx.size();
    787 }
    788 
    789 //------------------------------------------------------------------------
    790 void MaildirIndex::insert(const string &unique, unsigned int uid,
    791 			  const string &fileName)
    792 {
    793   if (idx.find(unique) == idx.end()) {
    794     MaildirIndexItem item;
    795     item.uid = uid;
    796     item.fileName = fileName;
    797     idx[unique] = item;
    798   } else {
    799     MaildirIndexItem &item = idx[unique];
    800     if (uid != 0) item.uid = uid;
    801     if (fileName != "") item.fileName = fileName;
    802   }
    803 }
    804 
    805 //------------------------------------------------------------------------
    806 void MaildirIndex::remove(const string &unique)
    807 {
    808   map<string, MaildirIndexItem>::iterator it = idx.find(unique);
    809   if (it != idx.end())
    810     idx.erase(it);
    811 }
    812 
    813 //------------------------------------------------------------------------
    814 MaildirIndexItem *MaildirIndex::find(const string &unique)
    815 {
    816   map<string, MaildirIndexItem>::iterator it = idx.find(unique);
    817   if (it != idx.end())
    818     return &it->second;
    819 
    820   return 0;
    821 }
    822 
    823 //------------------------------------------------------------------------
    824 void MaildirIndex::clear(void)
    825 {
    826   idx.clear();
    827 }
    828 
    829 //------------------------------------------------------------------------
    830 void MaildirIndex::clearUids(void)
    831 {
    832   map<string, MaildirIndexItem>::iterator it = idx.begin();
    833   for (; it != idx.end(); ++it)
    834     it->second.uid = 0;
    835 }
    836 
    837 //------------------------------------------------------------------------
    838 void MaildirIndex::clearFileNames(void)
    839 {
    840   map<string, MaildirIndexItem>::iterator it = idx.begin();
    841   for (; it != idx.end(); ++it)
    842     it->second.fileName = "";
    843 }