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 ©)
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 ©)
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 §ion,
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 §ion,
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 §ion,
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 §ion,
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