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   //----------------------------------------------------------------------
printOneHeader(IO & io,const MimePart * message,const string & s_in,bool removecomments=true)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   //----------------------------------------------------------------------
printOneAddressList(IO & io,const MimePart * message,const string & s_in,bool removecomments=true)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   //----------------------------------------------------------------------
envelope(IO & io,const MimePart * message)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   //----------------------------------------------------------------------
bodyStructure(IO & io,const MimePart * message,bool extended)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 //------------------------------------------------------------------------
MaildirMessage(Maildir & hom)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 //------------------------------------------------------------------------
MaildirMessage(const MaildirMessage & copy)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 //------------------------------------------------------------------------
~MaildirMessage(void)437 MaildirMessage::~MaildirMessage(void)
438 {
439 }
440 
441 //------------------------------------------------------------------------
operator =(const MaildirMessage & copy)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 //------------------------------------------------------------------------
operator <(const MaildirMessage & a) const459 bool MaildirMessage::operator <(const MaildirMessage &a) const
460 {
461   return uid < a.uid;
462 }
463 //------------------------------------------------------------------------
close(void)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 //------------------------------------------------------------------------
setExpunged(void)503 void MaildirMessage::setExpunged(void)
504 {
505   internalFlags |= Expunged;
506 }
507 
508 //------------------------------------------------------------------------
setUnExpunged(void)509 void MaildirMessage::setUnExpunged(void)
510 {
511   internalFlags &= ~Expunged;
512 }
513 
514 //------------------------------------------------------------------------
setFlagsUnchanged(void)515 void MaildirMessage::setFlagsUnchanged(void)
516 {
517   internalFlags &= ~FlagsChanged;
518 }
519 
520 //------------------------------------------------------------------------
hasFlagsChanged(void) const521 bool MaildirMessage::hasFlagsChanged(void) const
522 {
523   return (internalFlags & FlagsChanged) != 0;
524 }
525 
526 //------------------------------------------------------------------------
getStdFlags(void) const527 unsigned char MaildirMessage::getStdFlags(void) const
528 {
529   return stdflags;
530 }
531 
532 //------------------------------------------------------------------------
isExpunged(void) const533 bool MaildirMessage::isExpunged(void) const
534 {
535   return (internalFlags & Expunged) != 0;
536 }
537 
538 //------------------------------------------------------------------------
getUID(void) const539 unsigned int MaildirMessage::getUID(void) const
540 {
541   return uid;
542 }
543 
544 //------------------------------------------------------------------------
getSize(bool render) const545 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 //------------------------------------------------------------------------
getUnique(void) const556 const string &MaildirMessage::getUnique(void) const
557 {
558   return unique;
559 }
560 
561 //------------------------------------------------------------------------
getInternalDate(void) const562 time_t MaildirMessage::getInternalDate(void) const
563 {
564   return internaldate;
565 }
566 
567 //------------------------------------------------------------------------
setInternalDate(time_t t)568 void MaildirMessage::setInternalDate(time_t t)
569 {
570   internaldate = t;
571 }
572 
573 //------------------------------------------------------------------------
setStdFlag(unsigned char f_in)574 void MaildirMessage::setStdFlag(unsigned char f_in)
575 {
576   internalFlags |= FlagsChanged;
577   stdflags |= f_in;
578 }
579 
580 //------------------------------------------------------------------------
resetStdFlags(void)581 void MaildirMessage::resetStdFlags(void)
582 {
583   internalFlags |= FlagsChanged;
584   stdflags = F_NONE;
585 }
586 
587 //------------------------------------------------------------------------
setUID(unsigned int i_in)588 void MaildirMessage::setUID(unsigned int i_in)
589 {
590   uid = i_in;
591 }
592 
593 //------------------------------------------------------------------------
setSize(unsigned int i_in)594 void MaildirMessage::setSize(unsigned int i_in)
595 {
596   size = i_in;
597 }
598 
599 //------------------------------------------------------------------------
setUnique(const string & s_in)600 void MaildirMessage::setUnique(const string &s_in)
601 {
602   unique = s_in;
603 }
604 
605 //------------------------------------------------------------------------
getFile(void) const606 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 //------------------------------------------------------------------------
setFile(int fd)652 void MaildirMessage::setFile(int fd)
653 {
654   this->fd = fd;
655 }
656 
657 //------------------------------------------------------------------------
setSafeName(const string & name)658 void MaildirMessage::setSafeName(const string &name)
659 {
660   safeName = name;
661 }
662 
663 //------------------------------------------------------------------------
getSafeName(void) const664 const string &MaildirMessage::getSafeName(void) const
665 {
666   return safeName;
667 }
668 
669 //------------------------------------------------------------------------
getFileName(void) const670 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 //------------------------------------------------------------------------
rewind(void)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 //------------------------------------------------------------------------
readChunk(string & chunk)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 //------------------------------------------------------------------------
appendChunk(const string & chunk)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 //------------------------------------------------------------------------
parseFull(void) const753 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 //------------------------------------------------------------------------
parseHeaders(void) const775 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 //------------------------------------------------------------------------
printBodyStructure(bool extended) const798 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 //------------------------------------------------------------------------
printEnvelope(void) const809 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 //------------------------------------------------------------------------
printHeader(const std::string & section,std::vector<std::string> headers,bool includeHeaders,unsigned int startOffset,unsigned int length,bool mime) const820 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 //------------------------------------------------------------------------
getHeaderSize(const std::string & section,std::vector<std::string> headers,bool includeHeaders,unsigned int startOffset,unsigned int length,bool mime) const834 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 //------------------------------------------------------------------------
printBody(const std::string & section,unsigned int startOffset,unsigned int length) const868 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 //------------------------------------------------------------------------
getBodySize(const std::string & section,unsigned int startOffset,unsigned int length) const892 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 //------------------------------------------------------------------------
printDoc(unsigned int startOffset,unsigned int length,bool onlyText) const913 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 //------------------------------------------------------------------------
getDocSize(unsigned int startOffset,unsigned int length,bool onlyText) const933 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 //------------------------------------------------------------------------
headerContains(const std::string & header,const std::string & text)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 //------------------------------------------------------------------------
bodyContains(const std::string & text)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 //------------------------------------------------------------------------
textContains(const std::string & text)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 //------------------------------------------------------------------------
getHeader(const std::string & header)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 //------------------------------------------------------------------------
MaildirMessageCache(void)1050 MaildirMessageCache::MaildirMessageCache(void)
1051 {
1052 }
1053 
1054 //------------------------------------------------------------------------
~MaildirMessageCache(void)1055 MaildirMessageCache::~MaildirMessageCache(void)
1056 {
1057   clear();
1058 }
1059 
1060 //------------------------------------------------------------------------
getInstance(void)1061 MaildirMessageCache &MaildirMessageCache::getInstance(void)
1062 {
1063   static MaildirMessageCache cache;
1064   return cache;
1065 }
1066 
1067 //------------------------------------------------------------------------
addStatus(const MaildirMessage * m,ParseStatus s)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
getStatus(const MaildirMessage * m) const1087 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 //------------------------------------------------------------------------
clear(void)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 //------------------------------------------------------------------------
removeStatus(const MaildirMessage * m)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 //------------------------------------------------------------------------
setInternalFlag(unsigned char f)1125 void MaildirMessage::setInternalFlag(unsigned char f)
1126 {
1127   internalFlags |= f;
1128 }
1129 
1130 //------------------------------------------------------------------------
getInternalFlags(void) const1131 unsigned char MaildirMessage::getInternalFlags(void) const
1132 {
1133   return internalFlags;
1134 }
1135 
1136 //------------------------------------------------------------------------
clearInternalFlag(unsigned char f)1137 void MaildirMessage::clearInternalFlag(unsigned char f)
1138 {
1139   internalFlags &= ~f;
1140 }
1141