bincimap

Log | Files | Refs | LICENSE

maildirmessage.cc (27315B)


      1 /* -*- Mode: c++; -*- */
      2 /*  --------------------------------------------------------------------
      3  *  Filename:
      4  *    maildirmessage.cc
      5  *  
      6  *  Description:
      7  *    Implementation of the MaildirMessage 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 <string>
     39 
     40 #include <stack>
     41 #include <fcntl.h>
     42 #include <unistd.h>
     43 #include <ctype.h>
     44 #include <time.h>
     45 #include <utime.h>
     46 
     47 #include "maildir.h"
     48 #include "maildirmessage.h"
     49 #include "convert.h"
     50 #include "mime.h"
     51 #include "io.h"
     52 #include "mime-utils.h"
     53 
     54 using namespace ::std;
     55 using namespace Binc;
     56 
     57 string Message::lastError;
     58 string MaildirMessage::storage;
     59 
     60 namespace {
     61   //----------------------------------------------------------------------
     62   void printOneHeader(IO &io, const MimePart *message, const string &s_in,
     63 		      bool removecomments = true)
     64   {
     65     string tmp = "";
     66     HeaderItem hitem;
     67 
     68     if (message->h.getFirstHeader(s_in, hitem)) {
     69       tmp = hitem.getValue();
     70       io << toImapString(unfold(tmp, removecomments));
     71     } else
     72       io << "NIL";
     73   }
     74 
     75   //----------------------------------------------------------------------
     76   void printOneAddressList(IO &io, const MimePart *message,
     77 			   const string &s_in, bool removecomments = true)
     78   {
     79     string tmp = "";
     80     HeaderItem hitem;
     81 
     82     if (message->h.getFirstHeader(s_in, hitem)) {
     83       tmp = hitem.getValue();
     84       vector<string> addr;
     85       splitAddr(unfold(tmp, removecomments), addr);
     86       if (addr.size() != 0) {
     87 	io << "(";
     88 	for (vector<string>::const_iterator i = addr.begin();
     89 	     i != addr.end(); ++i)
     90 	  io << Address(*i).toParenList();
     91 	io << ")";
     92       } else io << "NIL";
     93     } else
     94       io << "NIL";
     95   }
     96 
     97   //----------------------------------------------------------------------
     98   void envelope(IO &io, const MimePart *message)
     99   {
    100     HeaderItem hitem;
    101     io << "(";
    102     printOneHeader(io, message, "date");
    103     io << " ";
    104     printOneHeader(io, message, "subject", false);
    105     io << " ";
    106     printOneAddressList(io, message, "from", false);
    107     io << " ";
    108     printOneAddressList(io, message, 
    109 			message->h.getFirstHeader("sender", hitem) 
    110 			? "sender" : "from", false);
    111     io << " ";
    112     printOneAddressList(io, message, 
    113 			message->h.getFirstHeader("reply-to", hitem) 
    114 			? "reply-to" : "from", false);
    115     io << " ";
    116     printOneAddressList(io, message, "to", false);
    117     io << " ";
    118     printOneAddressList(io, message, "cc", false);
    119     io << " ";
    120     printOneAddressList(io, message, "bcc", false);
    121     io << " ";
    122     printOneHeader(io, message, "in-reply-to");
    123     io << " ";
    124     printOneHeader(io, message, "message-id");
    125     io << ")";
    126   }
    127 
    128   //----------------------------------------------------------------------
    129   void bodyStructure(IO &io, const MimePart *message, bool extended)
    130   {
    131     HeaderItem hitem;
    132     if (message->isMultipart() && message->members.size() > 0) {
    133       io << "(";
    134       
    135       for (vector<MimePart>::const_iterator i = message->members.begin();
    136 	   i != message->members.end(); ++i)
    137 	bodyStructure(io, &(*i), extended);
    138 
    139       io << " ";
    140       io << toImapString(message->getSubType());
    141 
    142       if (extended) {
    143 	io << " ";
    144 
    145 	vector<string> parameters;
    146 	vector<string> headers;
    147 	string tmp;
    148 
    149 	string type, subtype;
    150 
    151 	tmp = "";
    152 	if (message->h.getFirstHeader("content-type", hitem)) {
    153 	  tmp = unfold(hitem.getValue());
    154 	  trim(tmp);
    155 
    156 	  vector<string> v;
    157 	  split(tmp, ";", v);
    158 	
    159 	  for (vector<string>::const_iterator i = v.begin(); i != v.end(); ++i) {
    160 	    string element = *i;
    161 	    trim(element);
    162 	    if (element.find('=') != string::npos) {
    163 	      string::size_type pos = element.find('=');
    164 	      string s = element.substr(0, pos);
    165 	      string t = element.substr(pos + 1);
    166 	      trim(s, " \"");
    167 	      trim(t, " \"");
    168 	      parameters.push_back(s);
    169 	      parameters.push_back(t);
    170 	    }
    171 	  }
    172 
    173 	  if (parameters.size() != 0) {
    174 	    io << "(";
    175 	    for (vector<string>::const_iterator i = parameters.begin();
    176 		 i != parameters.end(); ++i) {
    177 	      if (i != parameters.begin())
    178 		io << " ";
    179 	      io << toImapString(*i);
    180 	    }
    181 	    io << ")";
    182 	  } else
    183 	    io << "NIL";
    184 	} else
    185 	  io << "NIL";
    186 
    187 	// CONTENT-DISPOSITION
    188 	io << " ";
    189 	tmp = "";
    190 	if (message->h.getFirstHeader("content-disposition", hitem)) {
    191 	  tmp = hitem.getValue();
    192 	  trim(tmp);
    193 	
    194 	  vector<string> v;
    195 	  split(tmp, ";", v);
    196 	  if (v.size() > 0) {
    197 	    string disp = v[0];
    198 	    trim(disp);
    199 	    io << "(" << toImapString(disp);
    200 	    io << " ";
    201 	    if (v.size() > 1) {
    202 	      io << "(";
    203 	      vector<string>::const_iterator i = v.begin();
    204 	      ++i;
    205 	      bool wrote = false;
    206 	      while (i != v.end()) {
    207 		string s = *i;
    208 		trim(s);
    209 	      
    210 		string::size_type pos = s.find('=');
    211 		string key = s.substr(0, pos);
    212 		string value = s.substr(pos + 1);
    213 		trim(key);
    214 		trim(value);
    215 	      
    216 		trim(key, " \"");
    217 		trim(value, " \"");
    218 	      
    219 		if (!wrote) wrote = true;
    220 		else io << " ";
    221 		io << toImapString(key);
    222 	      
    223 		io << " ";
    224 		io << toImapString(value);
    225 	      
    226 		++i;
    227 	      }
    228 	      io << ")";
    229 	    } else 
    230 	      io << "NIL";
    231 	    io << ")";
    232 	  } else
    233 	    io << "NIL";
    234 	} else
    235 	  io << "NIL";
    236       
    237 	// CONTENT-LANGUAGE
    238 	io << " ";
    239 	printOneHeader(io, message, "content-language");
    240       }
    241 
    242       io << ")";
    243     } else {
    244       io << "(";
    245 
    246       vector<string> parameters;
    247       vector<string> headers;
    248       string tmp;
    249       tmp = "";
    250       string type, subtype;
    251 
    252       tmp = "";
    253       if (message->h.getFirstHeader("content-type", hitem)) {
    254 	tmp = unfold(hitem.getValue());
    255 	
    256 	vector<string> v;
    257 	split(tmp, ";", v);
    258 
    259 	if (v.size() > 0) {
    260 	  vector<string> b;
    261 	  split(v[0], "/", b);
    262 	    
    263 	  if (b.size() > 0)
    264 	    type = b[0];
    265 	  else
    266 	    type = "text";
    267 
    268 	  if (b.size() > 1)
    269 	    subtype = b[1];
    270 	  else
    271 	    subtype = "plain";
    272 	}
    273 	
    274 	for (vector<string>::const_iterator i = v.begin(); i != v.end(); ++i) {
    275 	  if (i == v.begin()) continue;
    276 
    277 	  string element = *i;
    278 	  trim(element);
    279 
    280 	  if (element.find('=') != string::npos) {
    281 	    string::size_type pos = element.find('=');
    282 	    string s = element.substr(0, pos);
    283 	    string t = element.substr(pos + 1);
    284 	    trim(s, " \"");
    285 	    trim(t, " \"");
    286 	    parameters.push_back(s);
    287 	    parameters.push_back(t);
    288 	  }
    289 	}
    290       } else {
    291 	type = "text";
    292 	subtype = "plain";
    293       }
    294 
    295       io << toImapString(type);
    296       io << " ";
    297       io << toImapString(subtype);
    298 
    299       io << " ";
    300       if (parameters.size() != 0) {
    301 	io << "(";
    302 	for (vector<string>::const_iterator i = parameters.begin();
    303 	     i != parameters.end(); ++i) {
    304 	  if (i != parameters.begin())
    305 	    io << " ";
    306 	  io << toImapString(*i);
    307 	}
    308 	io << ")";
    309       } else
    310 	io << "NIL";
    311       
    312       // CONTENT-ID
    313       io << " ";
    314       printOneHeader(io, message, "content-id");
    315 
    316       // CONTENT-DESCRIPTION
    317       io << " ";
    318       printOneHeader(io, message, "content-description");
    319 
    320       // CONTENT-TRANSFER-ENCODING
    321       io << " ";
    322       tmp = "";
    323       if (message->h.getFirstHeader("content-transfer-encoding", hitem)) {
    324 	tmp = hitem.getValue();
    325 	trim(tmp);
    326 	io << toImapString(tmp);
    327       } else
    328 	io << "\"7bit\"";
    329       io << " ";
    330 
    331       // Size of body in octets
    332       io << message->getBodyLength();
    333 
    334       lowercase(type);
    335       if (type == "text") {
    336 	io << " ";
    337 	io << message->getNofBodyLines();
    338       } else if (message->isMessageRFC822()) {
    339 	io << " ";
    340 	envelope(io, &message->members[0]);
    341 	io << " ";
    342 	bodyStructure(io, &message->members[0], extended);
    343 	io << " ";
    344 	io << message->getNofBodyLines();
    345       }
    346       
    347       // Extension data follows
    348 
    349       if (extended) {
    350 
    351 	// CONTENT-MD5
    352 	io << " ";
    353 	printOneHeader(io, message, "content-md5");
    354 
    355 	// CONTENT-DISPOSITION
    356 	io << " ";
    357 	tmp = "";
    358 	if (message->h.getFirstHeader("content-disposition", hitem)) {
    359 	  tmp = hitem.getValue();
    360 	  trim(tmp);
    361 
    362 	  vector<string> v;
    363 	  split(tmp, ";", v);
    364 	  if (v.size() > 0) {
    365 	    string disp = v[0];
    366 	    trim(disp);
    367 	    io << "(" << toImapString(disp);
    368 	    io << " ";
    369 	    if (v.size() > 1) {
    370 	      io << "(";
    371 	      vector<string>::const_iterator i = v.begin();
    372 	      ++i;
    373 	      bool wrote = false;
    374 	      while (i != v.end()) {
    375 		string s = *i;
    376 		trim(s);
    377 
    378 		string::size_type pos = s.find('=');
    379 		string key = s.substr(0, pos);
    380 		string value = s.substr(pos + 1);
    381 		trim(key);
    382 		trim(value);
    383 
    384 		trim(key, " \"");
    385 		trim(value, " \"");
    386 
    387 		if (!wrote) wrote = true;
    388 		else io << " ";
    389 		io << toImapString(key);
    390 
    391 		io << " ";
    392 		io << toImapString(value);
    393 
    394 		++i;
    395 	      }
    396 	      io << ")";
    397 	    } else 
    398 	      io << "NIL";
    399 	    io << ")";
    400 	  } else
    401 	    io << "NIL";
    402 	} else
    403 	  io << "NIL";
    404       
    405 	// CONTENT-LANGUAGE
    406 	io << " ";
    407 	printOneHeader(io, message, "content-language");
    408 
    409 	// CONTENT-LOCATION
    410 	io << " ";
    411 	printOneHeader(io, message, "content-location");
    412       }
    413 
    414       io << ")";
    415     }
    416   }
    417 }
    418 
    419 //------------------------------------------------------------------------
    420 MaildirMessage::MaildirMessage(Maildir &hom) 
    421   : fd(-1), doc(0), internalFlags(None), stdflags(F_NONE),
    422     uid(0), size(0), unique(""), safeName(""), internaldate(0),
    423     home(hom)
    424 {
    425 }
    426 
    427 //------------------------------------------------------------------------
    428 MaildirMessage::MaildirMessage(const MaildirMessage &copy) 
    429   : fd(copy.fd), doc(copy.doc), internalFlags(copy.internalFlags),
    430     stdflags(copy.stdflags), uid(copy.uid), size(copy.size),
    431     unique(copy.unique), safeName(copy.safeName),
    432     internaldate(copy.internaldate), home(copy.home)
    433 {
    434 }
    435 
    436 //------------------------------------------------------------------------
    437 MaildirMessage::~MaildirMessage(void)
    438 {
    439 }
    440 
    441 //------------------------------------------------------------------------
    442 MaildirMessage &MaildirMessage::operator =(const MaildirMessage &copy) 
    443 {
    444   fd = copy.fd;
    445   doc = copy.doc; 
    446   internalFlags = copy.internalFlags;
    447   stdflags = copy.stdflags;
    448   uid = copy.uid;
    449   size = copy.size;
    450   unique = copy.unique; 
    451   safeName = copy.safeName;
    452   internaldate = copy.internaldate;
    453   home = copy.home;
    454 
    455   return *this;
    456 }
    457 
    458 //------------------------------------------------------------------------
    459 bool MaildirMessage::operator <(const MaildirMessage &a) const
    460 {
    461   return uid < a.uid;
    462 }
    463 //------------------------------------------------------------------------
    464 void MaildirMessage::close(void)
    465 {
    466   if (fd != -1) {
    467     if ((internalFlags & WasWrittenTo) && fsync(fd) != 0
    468 	&& errno != EINVAL && errno != EROFS) {
    469       // FIXME: report this error
    470     }
    471 
    472     if (::close(fd) != 0) {
    473       // FIXME: report this error
    474     }
    475 
    476     fd = -1;
    477   }
    478 
    479   // The file will not be moved from tmp/ before this function has
    480   // finished. So it's safe to assume that safeName is still valid.
    481   if (internalFlags & WasWrittenTo) {
    482     if (internaldate != 0) {
    483       struct utimbuf tim = {internaldate, internaldate};
    484       utime(safeName.c_str(), &tim);
    485     } else {
    486       time_t t = time(0);
    487       struct utimbuf tim = {t, t};
    488       utime(safeName.c_str(), &tim);
    489     }
    490 
    491     internalFlags &= ~WasWrittenTo;
    492   }
    493 
    494 
    495   if (doc) {
    496     doc->clear();
    497     delete doc;
    498     doc = 0;
    499   }
    500 }
    501 
    502 //------------------------------------------------------------------------
    503 void MaildirMessage::setExpunged(void)
    504 {
    505   internalFlags |= Expunged;
    506 }
    507 
    508 //------------------------------------------------------------------------
    509 void MaildirMessage::setUnExpunged(void)
    510 {
    511   internalFlags &= ~Expunged;
    512 }
    513 
    514 //------------------------------------------------------------------------
    515 void MaildirMessage::setFlagsUnchanged(void)
    516 {
    517   internalFlags &= ~FlagsChanged;
    518 }
    519 
    520 //------------------------------------------------------------------------
    521 bool MaildirMessage::hasFlagsChanged(void) const
    522 {
    523   return (internalFlags & FlagsChanged) != 0;
    524 }
    525 
    526 //------------------------------------------------------------------------
    527 unsigned char MaildirMessage::getStdFlags(void) const
    528 {
    529   return stdflags;
    530 }
    531 
    532 //------------------------------------------------------------------------
    533 bool MaildirMessage::isExpunged(void) const
    534 {
    535   return (internalFlags & Expunged) != 0;
    536 }
    537 
    538 //------------------------------------------------------------------------
    539 unsigned int MaildirMessage::getUID(void) const
    540 {
    541   return uid;
    542 }
    543 
    544 //------------------------------------------------------------------------
    545 unsigned int MaildirMessage::getSize(bool render) const
    546 {
    547   if (size == 0 && render) {
    548     size = getDocSize();
    549     home.mailboxchanged = true;
    550   }
    551 
    552   return size;
    553 }
    554 
    555 //------------------------------------------------------------------------
    556 const string &MaildirMessage::getUnique(void) const
    557 {
    558   return unique;
    559 }
    560 
    561 //------------------------------------------------------------------------
    562 time_t MaildirMessage::getInternalDate(void) const
    563 {
    564   return internaldate;
    565 }
    566 
    567 //------------------------------------------------------------------------
    568 void MaildirMessage::setInternalDate(time_t t)
    569 {
    570   internaldate = t;
    571 }
    572 
    573 //------------------------------------------------------------------------
    574 void MaildirMessage::setStdFlag(unsigned char f_in)
    575 {
    576   internalFlags |= FlagsChanged;
    577   stdflags |= f_in;
    578 }
    579 
    580 //------------------------------------------------------------------------
    581 void MaildirMessage::resetStdFlags(void)
    582 {
    583   internalFlags |= FlagsChanged;
    584   stdflags = F_NONE;
    585 }
    586 
    587 //------------------------------------------------------------------------
    588 void MaildirMessage::setUID(unsigned int i_in)
    589 {
    590   uid = i_in;
    591 }
    592 
    593 //------------------------------------------------------------------------
    594 void MaildirMessage::setSize(unsigned int i_in)
    595 {
    596   size = i_in;
    597 }
    598 
    599 //------------------------------------------------------------------------
    600 void MaildirMessage::setUnique(const string &s_in)
    601 {
    602   unique = s_in;
    603 }
    604 
    605 //------------------------------------------------------------------------
    606 int MaildirMessage::getFile(void) const
    607 {
    608   if (fd != -1)
    609     return fd;
    610 
    611   const string &id = getUnique();
    612   MaildirIndexItem *item = home.index.find(id);
    613   if (item) {
    614     string fpath = home.path + "/cur/" + item->fileName;
    615     
    616     unsigned int oflags = O_RDONLY;
    617 #ifdef HAVE_OLARGEFILE
    618     oflags |= O_LARGEFILE;
    619 #endif
    620     while ((fd = open(fpath.c_str(), oflags)) == -1) {
    621       if (errno == ENOENT) {
    622         struct stat st;
    623         if (lstat(fpath.c_str(), &st) != -1) {
    624            IO &logger = IOFactory::getInstance().get(2);
    625            logger << "dangling symlink: " << fpath << endl;
    626            return -1;
    627         }
    628       } else {
    629 	IO &logger = IOFactory::getInstance().get(2);
    630 	logger << "unable to open " << fpath << ": "
    631 	       << strerror(errno) << endl;
    632 	return -1;
    633       }
    634       
    635       home.scanFileNames();
    636       if ((item = home.index.find(id)) == 0)
    637 	break;
    638       else
    639 	fpath = home.path + "/cur/" + item->fileName;
    640     }
    641 
    642     MaildirMessageCache &cache = MaildirMessageCache::getInstance();
    643     cache.addStatus(this, MaildirMessageCache::NotParsed);
    644 
    645     return fd;
    646   }
    647 
    648   return -1;
    649 }
    650 
    651 //------------------------------------------------------------------------
    652 void MaildirMessage::setFile(int fd)
    653 {
    654   this->fd = fd;
    655 }
    656 
    657 //------------------------------------------------------------------------
    658 void MaildirMessage::setSafeName(const string &name)
    659 {
    660   safeName = name;
    661 }
    662 
    663 //------------------------------------------------------------------------
    664 const string &MaildirMessage::getSafeName(void) const
    665 {
    666   return safeName;
    667 }
    668 
    669 //------------------------------------------------------------------------
    670 string MaildirMessage::getFileName(void) const
    671 {
    672   MaildirIndexItem *item = home.index.find(getUnique());
    673   if (!item) {
    674     home.scanFileNames();
    675 
    676     if ((item = home.index.find(getUnique())) == 0)
    677       return "";
    678   }
    679 
    680   return item->fileName;
    681 }
    682 
    683 //------------------------------------------------------------------------
    684 void MaildirMessage::rewind(void)
    685 {
    686   if (fd == -1) {
    687     if ((fd == getFile()) == -1)
    688       return;
    689   }
    690 
    691   lseek(fd, 0, SEEK_SET);
    692 }
    693 
    694 //------------------------------------------------------------------------
    695 int MaildirMessage::readChunk(string &chunk)
    696 {
    697   if (fd == -1) {
    698     if ((fd == getFile()) == -1)
    699       return -1;
    700   }
    701 
    702   char buffer[1024];
    703   ssize_t readBytes = read(fd, buffer, (size_t) sizeof(buffer));
    704   if (readBytes == -1) {
    705     setLastError("Error reading from " + getFileName()
    706 		 + ": " + string(strerror(errno)));
    707     return -1;
    708   }
    709 
    710   chunk = string(buffer, readBytes);
    711   return readBytes;
    712 }
    713 
    714 //------------------------------------------------------------------------
    715 bool MaildirMessage::appendChunk(const string &chunk)
    716 {
    717   if (fd == -1) {
    718     setLastError("Error writing to " + getSafeName() 
    719 		 + ": File is not open");
    720     return false;
    721   }
    722 
    723   internalFlags |= WasWrittenTo;
    724 
    725   string out;
    726   for (string::const_iterator i = chunk.begin(); i != chunk.end(); ++i) {
    727     const char c = *i;
    728     if (c != '\r')
    729       out += c;
    730   }
    731 
    732   ssize_t wroteBytes = 0;
    733   for (;;) {
    734     wroteBytes = write(fd, out.c_str(), (size_t) out.length());
    735     if (wroteBytes == -1) {
    736       if (errno == EINTR)
    737 	continue;
    738       wroteBytes = 0;
    739     }
    740 
    741     break;
    742   }
    743 
    744   if (wroteBytes == (ssize_t) out.length())
    745     return true;
    746 
    747   setLastError("Error writing to " + getSafeName() 
    748 	       + ": " + string(strerror(errno)));
    749   return false;
    750 }
    751 
    752 //------------------------------------------------------------------------
    753 bool MaildirMessage::parseFull(void) const
    754 {
    755   MaildirMessageCache &cache = MaildirMessageCache::getInstance();
    756   MaildirMessageCache::ParseStatus ps = cache.getStatus(this);
    757   if (ps == MaildirMessageCache::AllParsed && doc)
    758     return true;
    759 
    760   int fd = getFile();
    761   if (fd == -1)
    762     return false;
    763 
    764   // FIXME: parse errors
    765   if (!doc)
    766     doc = new MimeDocument;
    767   doc->parseFull(fd);
    768 
    769   cache.addStatus(this, MaildirMessageCache::AllParsed);
    770 
    771   return true;
    772 }
    773 
    774 //------------------------------------------------------------------------
    775 bool MaildirMessage::parseHeaders(void) const
    776 {
    777   MaildirMessageCache &cache = MaildirMessageCache::getInstance();
    778   MaildirMessageCache::ParseStatus ps = cache.getStatus(this);
    779   if ((ps == MaildirMessageCache::AllParsed
    780       || ps == MaildirMessageCache::HeaderParsed) && doc)
    781     return true;
    782 
    783   int fd = getFile();
    784   if (fd == -1)
    785     return false;
    786 
    787   // FIXME: parse errors
    788   if (!doc)
    789     doc = new MimeDocument;
    790   doc->parseOnlyHeader(fd);
    791 
    792   cache.addStatus(this, MaildirMessageCache::HeaderParsed);
    793 
    794   return true;
    795 }
    796 
    797 //------------------------------------------------------------------------
    798 bool MaildirMessage::printBodyStructure(bool extended) const
    799 {
    800   if (!parseFull())
    801     return false;
    802 
    803   IO &com = IOFactory::getInstance().get(1);
    804   bodyStructure(com, doc, extended);
    805   return true;
    806 }
    807 
    808 //------------------------------------------------------------------------
    809 bool MaildirMessage::printEnvelope(void) const
    810 {
    811   if (!parseFull())
    812     return false;
    813 
    814   IO &com = IOFactory::getInstance().get(1);
    815   envelope(com, doc);
    816   return true;
    817 }
    818 
    819 //------------------------------------------------------------------------
    820 bool MaildirMessage::printHeader(const std::string &section,
    821 				 std::vector<std::string> headers,
    822 				 bool includeHeaders,
    823 				 unsigned int startOffset,
    824 				 unsigned int length,
    825 				 bool mime) const
    826 {
    827   IO &com = IOFactory::getInstance().get(1);
    828   com << storage;
    829   storage = "";
    830   return true;
    831 }
    832 
    833 //------------------------------------------------------------------------
    834 unsigned int MaildirMessage::getHeaderSize(const std::string &section,
    835 					   std::vector<std::string> headers,
    836 					   bool includeHeaders,
    837 					   unsigned int startOffset,
    838 					   unsigned int length,
    839 					   bool mime) const
    840 {
    841   IO &com = IOFactory::getInstance().get(1);
    842 
    843   if (section == "") {
    844     if (!parseHeaders())
    845       return 0;
    846   } else if (!parseFull())
    847     return 0;
    848 
    849   const MimePart *part = doc->getPart(section, "", mime ? MimePart::FetchMime : MimePart::FetchHeader);
    850   if (!part) {
    851     storage = "";
    852     return 0;
    853   }
    854   
    855   int fd = getFile();
    856   if (fd == -1)
    857     return 0;
    858 
    859   storage = "";
    860   part->printHeader(fd, com, headers, 
    861 		    includeHeaders, startOffset,
    862 		    length, storage);
    863 
    864   return storage.size();
    865 }
    866 
    867 //------------------------------------------------------------------------
    868 bool MaildirMessage::printBody(const std::string &section,
    869 			       unsigned int startOffset,
    870 			       unsigned int length) const
    871 {
    872   IO &com = IOFactory::getInstance().get(1);
    873   if (!parseFull())
    874     return false;
    875 
    876   const MimePart *part = doc->getPart(section, "");
    877   if (!part) {
    878     storage = "";
    879     return 0;
    880   }
    881   
    882   int fd = getFile();
    883   if (fd == -1)
    884     return false;
    885 
    886   storage = "";
    887   part->printBody(fd, com, startOffset, length);
    888   return true;
    889 }
    890 
    891 //------------------------------------------------------------------------
    892 unsigned int MaildirMessage::getBodySize(const std::string &section,
    893 					 unsigned int startOffset,
    894 					 unsigned int length) const
    895 {
    896   if (!parseFull())
    897     return false;
    898 
    899   const MimePart *part = doc->getPart(section, "");
    900   if (!part) {
    901     storage = "";
    902     return 0;
    903   }
    904 
    905   if (startOffset > part->bodylength)
    906     return 0;
    907   
    908   unsigned int s = part->bodylength - startOffset;
    909   return s < length ? s : length;
    910 }
    911 
    912 //------------------------------------------------------------------------
    913 bool MaildirMessage::printDoc(unsigned int startOffset, 
    914 			      unsigned int length, bool onlyText) const
    915 {
    916   IO &com = IOFactory::getInstance().get(1);
    917   if (!parseFull())
    918     return false;
    919 
    920   int fd = getFile();
    921   if (fd == -1)
    922     return false;
    923 
    924   if (onlyText)
    925     startOffset += doc->bodystartoffsetcrlf;
    926 
    927   storage = "";
    928   doc->printDoc(fd, com, startOffset, length);
    929   return true;
    930 }
    931 
    932 //------------------------------------------------------------------------
    933 unsigned int MaildirMessage::getDocSize(unsigned int startOffset,
    934 					unsigned int length, 
    935 					bool onlyText) const
    936 {
    937   if (!parseFull())
    938     return false;
    939 
    940   unsigned int s = doc->size;
    941   if (onlyText) s -= doc->bodystartoffsetcrlf;
    942 
    943   if (startOffset > s)
    944     return 0;
    945 
    946   s -= startOffset;
    947   return s < length ? s : length;
    948 }
    949 
    950 //------------------------------------------------------------------------
    951 bool MaildirMessage::headerContains(const std::string &header,
    952 				    const std::string &text)
    953 {
    954   if (!parseHeaders())
    955     return false;
    956 
    957   HeaderItem hitem;
    958   if (!doc->h.getFirstHeader(header, hitem))
    959     return false;
    960 
    961   string tmp = hitem.getValue();
    962   uppercase(tmp);
    963   string tmp2 = text;
    964   uppercase(tmp2);
    965   return (tmp.find(tmp2) != string::npos);
    966 }
    967 
    968 //------------------------------------------------------------------------
    969 bool MaildirMessage::bodyContains(const std::string &text)
    970 {
    971   if (!parseFull())
    972     return false;
    973 
    974   // search the body part of the message..
    975   int fd = getFile();
    976   if (fd == -1)
    977     return false;
    978 
    979   crlffile = fd;
    980   crlfReset();
    981 
    982   char c;
    983   for (unsigned int i = 0; i < doc->getBodyStartOffset(); ++i)
    984     if (!crlfGetChar(c))
    985       break;
    986   
    987   char *ring = new char[text.length()];
    988   int pos = 0;
    989   int length = doc->getBodyLength();
    990   while (crlfGetChar(c) && length--) {
    991     ring[pos % text.length()] = toupper(c);
    992     
    993     if (compareStringToQueue(text, ring, pos + 1, text.length())) {
    994       delete ring;
    995       return true;
    996     }
    997     
    998     ++pos;
    999   }
   1000   
   1001   delete ring;
   1002   return false;
   1003 }
   1004 
   1005 //------------------------------------------------------------------------
   1006 bool MaildirMessage::textContains(const std::string &text)
   1007 {
   1008   // search the body part of the message..
   1009   int fd = getFile();
   1010   if (fd == -1)
   1011     return false;
   1012 
   1013   crlffile = fd;
   1014   crlfReset();
   1015 
   1016   char c;
   1017   char *ring = new char[text.length()];
   1018   int pos = 0;
   1019   while (crlfGetChar(c)) {
   1020     ring[pos % text.length()] = toupper(c);
   1021     
   1022     if (compareStringToQueue(text, ring, pos + 1, text.length())) {
   1023       delete ring;
   1024       return true;
   1025     }
   1026     
   1027     ++pos;
   1028   }
   1029   
   1030   delete ring;
   1031   return false;
   1032 }
   1033 
   1034 //------------------------------------------------------------------------
   1035 const std::string &MaildirMessage::getHeader(const std::string &header)
   1036 {
   1037   static string NIL = "";
   1038 
   1039   if (!parseHeaders())
   1040     return NIL;
   1041 
   1042   HeaderItem hitem;
   1043   if (!doc->h.getFirstHeader(header, hitem))
   1044     return NIL;
   1045   
   1046   return hitem.getValue(); 
   1047 }
   1048 
   1049 //------------------------------------------------------------------------
   1050 MaildirMessageCache::MaildirMessageCache(void)
   1051 {
   1052 }
   1053 
   1054 //------------------------------------------------------------------------
   1055 MaildirMessageCache::~MaildirMessageCache(void)
   1056 {
   1057   clear();
   1058 }
   1059 
   1060 //------------------------------------------------------------------------
   1061 MaildirMessageCache &MaildirMessageCache::getInstance(void)
   1062 {
   1063   static MaildirMessageCache cache;
   1064   return cache;
   1065 }
   1066 
   1067 //------------------------------------------------------------------------
   1068 void MaildirMessageCache::addStatus(const MaildirMessage *m,
   1069 				    ParseStatus s)
   1070 {
   1071   if (statuses.find(m) == statuses.end()) {
   1072     // Insert status. Perhaps remove oldest status.
   1073     if (statuses.size() > 2) {
   1074       MaildirMessage *message = const_cast<MaildirMessage *>(parsed.front());
   1075 
   1076       removeStatus(message);
   1077     }
   1078 
   1079     parsed.push_back(m);
   1080   }
   1081 
   1082   statuses[m] = s;
   1083 }
   1084 
   1085 //------------------------------------------------------------------------
   1086 MaildirMessageCache::ParseStatus
   1087 MaildirMessageCache::getStatus(const MaildirMessage *m) const
   1088 {
   1089   if (statuses.find(m) == statuses.end())
   1090     return NotParsed;
   1091 
   1092   return statuses[m];
   1093 }
   1094 
   1095 //------------------------------------------------------------------------
   1096 void MaildirMessageCache::clear(void)
   1097 {
   1098   for (deque<const MaildirMessage *>::iterator i = parsed.begin();
   1099        i != parsed.end(); ++i)
   1100     const_cast<MaildirMessage *>(*i)->close();
   1101 
   1102   parsed.clear();
   1103   statuses.clear();
   1104 }
   1105 
   1106 //------------------------------------------------------------------------
   1107 void MaildirMessageCache::removeStatus(const MaildirMessage *m)
   1108 {
   1109   if (statuses.find(m) == statuses.end())
   1110     return;
   1111 
   1112   statuses.erase(statuses.find(m));
   1113 
   1114   for (deque<const MaildirMessage *>::iterator i = parsed.begin();
   1115        i != parsed.end(); ++i) {
   1116     if (*i == m) {
   1117       const_cast<MaildirMessage *>(*i)->close();
   1118       parsed.erase(i);
   1119       break;
   1120     }
   1121   }
   1122 }
   1123 
   1124 //------------------------------------------------------------------------
   1125 void MaildirMessage::setInternalFlag(unsigned char f)
   1126 {
   1127   internalFlags |= f;
   1128 }
   1129 
   1130 //------------------------------------------------------------------------
   1131 unsigned char MaildirMessage::getInternalFlags(void) const
   1132 {
   1133   return internalFlags;
   1134 }
   1135 
   1136 //------------------------------------------------------------------------
   1137 void MaildirMessage::clearInternalFlag(unsigned char f)
   1138 {
   1139   internalFlags &= ~f;
   1140 }