1 /*
2 ** Copyright 2002-2004, Double Precision Inc.
3 **
4 ** See COPYING for distribution information.
5 */
6 #include "libmail_config.h"
7
8 #include "mail.H"
9 #include "imap.H"
10 #include "misc.H"
11 #include "generic.H"
12 #include "genericdecode.H"
13 #include "runlater.H"
14 #include "envelope.H"
15 #include "structure.H"
16 #include "rfcaddr.H"
17 #include "runlater.H"
18 #include "objectmonitor.H"
19
20 #include "rfc822/rfc822.h"
21 #include "rfc2045/rfc2045.h"
22 #include "maildir/maildirkeywords.h"
23 #include <errno.h>
24 #include <ctype.h>
25 #include <sstream>
26 #include <queue>
27 #include <set>
28 #include <string>
29
30 using namespace std;
31
generic()32 mail::generic::generic()
33 {
34 }
35
~generic()36 mail::generic::~generic()
37 {
38 }
39
40 //////////////////////////////////////////////////////////////////////////////
41 //
42 // mail::generic::genericAttributes() creates an Attributes object.
43 //
44 // The Attributes objects takes the original list of messages. For each
45 // message, genericGetMessage functions are invoked to grab the message,
46 // then synthesize the attributes.
47
48 class mail::generic::Attributes : public mail::callback::message,
49 public mail::runLater {
50
51 int fd;
52 struct rfc2045 *rfc2045p;
53
54 void reportProgress(size_t bytesCompleted,
55 size_t bytesEstimatedTotal,
56
57 size_t messagesCompleted,
58 size_t messagesEstimatedTotal);
59
60 public:
61
62 mail::ptr<mail::account> account; // My account
63 mail::generic *genericInterface; // Ditto
64
65 mail::envelope envelope;
66 mail::mimestruct structure;
67
68 time_t arrivalDate;
69
70 vector< pair<string, size_t> > messages;
71
72 vector< pair<string, size_t> >::iterator nextMsg;
73
74 mail::account::MessageAttributes attributes, // Attributes requested
75
76 attributesToDo; // Attributes left to synthesize for cur msg
77
78 mail::callback::message *callback; // Original app callback
79
80 Attributes(mail::account *accountArg,
81 mail::generic *genericInterfaceArg,
82 const vector<size_t> &msgsArg,
83 mail::account::MessageAttributes attributesArg,
84 mail::callback::message *callbackArg);
85 ~Attributes();
86
87 void success(string msg);
88 void fail(string msg);
89 void RunningLater();
90 void go(string);
91
92 string headerBuffer;
93 void messageTextCallback(size_t n, string text);
94
95 void messageSizeCallback(size_t messageNumber,
96 unsigned long size);
97 };
98
Attributes(mail::account * accountArg,mail::generic * genericInterfaceArg,const vector<size_t> & msgsArg,mail::account::MessageAttributes attributesArg,mail::callback::message * callbackArg)99 mail::generic::Attributes::Attributes(mail::account *accountArg,
100 mail::generic *genericInterfaceArg,
101 const vector<size_t> &msgsArg,
102 mail::account::MessageAttributes attributesArg,
103 mail::callback::message *callbackArg)
104 : account(accountArg),
105 genericInterface(genericInterfaceArg),
106 attributes(attributesArg),
107 callback(callbackArg)
108 {
109 // Memorize all message numbers and uids
110
111 vector<size_t>::const_iterator b=msgsArg.begin(), e=msgsArg.end();
112
113 while (b != e)
114 {
115 size_t n= *b++;
116
117 messages.push_back( make_pair(accountArg->getFolderIndexInfo(n)
118 .uid, n));
119 }
120 nextMsg=messages.begin();
121 attributesToDo=attributes;
122 }
123
~Attributes()124 mail::generic::Attributes::~Attributes()
125 {
126 mail::callback::message *c=callback;
127
128 callback=NULL;
129
130 if (c)
131 c->fail("Server connection unexpectedly shut down.");
132 }
133
134
135 // See mail::Runlater
136
RunningLater()137 void mail::generic::Attributes::RunningLater()
138 {
139 go("OK");
140 }
141
go(string msg)142 void mail::generic::Attributes::go(string msg)
143 {
144 while (nextMsg != messages.end() && !account.isDestroyed())
145 {
146 if (!fixMessageNumber(account, nextMsg->first,
147 nextMsg->second)) // Msg disappeared?
148 {
149 attributesToDo=attributes;
150 nextMsg++;
151 continue;
152 }
153
154 // Pick next attributes to synthesize
155
156 if (attributesToDo & mail::account::MESSAGESIZE)
157 {
158 genericInterface->genericMessageSize(nextMsg->first,
159 nextMsg->second,
160 *this);
161 return;
162 }
163
164
165 if (attributesToDo & mail::account::MIMESTRUCTURE)
166 {
167 genericInterface->
168 genericGetMessageFdStruct(nextMsg->first,
169 nextMsg->second,
170 true,
171 fd,
172 rfc2045p,
173 *this);
174 return;
175 }
176
177 // ARRIVALDATE is synthesized from header information
178 // So is the envelope
179
180 if (attributesToDo & (mail::account::ENVELOPE | mail::account::ARRIVALDATE))
181 {
182 envelope=mail::envelope();
183 arrivalDate=0;
184
185 headerBuffer="";
186 genericInterface->genericMessageRead(nextMsg->first,
187 nextMsg->second,
188 true,
189 mail::readHeadersFolded,
190 *this);
191 return;
192 }
193
194 attributesToDo=attributes;
195 nextMsg++;
196 RunLater();
197 return;
198 }
199
200 // All done.
201
202 mail::callback::message *c=callback;
203
204 callback=NULL;
205
206 if (c)
207 c->success(msg);
208
209 delete this;
210 return;
211 }
212
success(string msg)213 void mail::generic::Attributes::success(string msg)
214 {
215 if (account.isDestroyed())
216 {
217 delete this;
218 return;
219 }
220
221 // Must use the same order as go(), in order to figure out which
222 // attribute to synthesize. The synthesized attribute is turned off.
223 // Lather, rinse, repeat.
224
225 if (attributesToDo & mail::account::MESSAGESIZE)
226 {
227 attributesToDo &= ~mail::account::MESSAGESIZE;
228 go(msg);
229 return;
230 }
231
232 bool goodMsg=fixMessageNumber(account, nextMsg->first,
233 nextMsg->second);
234
235 if (attributesToDo & mail::account::MIMESTRUCTURE)
236 {
237 attributesToDo &= ~mail::account::MIMESTRUCTURE;
238
239 mail::mimestruct s;
240
241 genericMakeMimeStructure(s, fd, rfc2045p, "", NULL);
242
243 if (callback && goodMsg)
244 callback->messageStructureCallback(nextMsg->second,
245 s);
246 go(msg);
247 return;
248 }
249
250 if (attributesToDo & mail::account::ENVELOPE)
251 if (callback && goodMsg)
252 callback->messageEnvelopeCallback(nextMsg->second,
253 envelope);
254
255 if ((attributesToDo & mail::account::ARRIVALDATE) && callback && goodMsg)
256 {
257 if (arrivalDate != 0) // First date in Received: hdr seen.
258 callback->messageArrivalDateCallback(nextMsg->second,
259 arrivalDate);
260 else if (envelope.date != 0)
261 callback->messageArrivalDateCallback(nextMsg->second,
262 envelope.date);
263 }
264
265 attributesToDo &= ~(mail::account::ENVELOPE | mail::account::ARRIVALDATE);
266 go(msg);
267 }
268
fail(string msg)269 void mail::generic::Attributes::fail(string msg)
270 {
271 mail::callback::message *c=callback;
272
273 callback=NULL;
274
275 delete this;
276
277 if (c)
278 c->fail(msg);
279 }
280
reportProgress(size_t bytesCompleted,size_t bytesEstimatedTotal,size_t messagesCompleted,size_t messagesEstimatedTotal)281 void mail::generic::Attributes::reportProgress(size_t bytesCompleted,
282 size_t bytesEstimatedTotal,
283
284 size_t messagesCompleted,
285 size_t messagesEstimatedTotal)
286 {
287 callback->reportProgress(bytesCompleted, bytesEstimatedTotal,
288 nextMsg - messages.begin(),
289 messages.size());
290 }
291
messageSizeCallback(size_t messageNumber,unsigned long size)292 void mail::generic::Attributes::messageSizeCallback(size_t messageNumber,
293 unsigned long size)
294 {
295 if (fixMessageNumber(account, nextMsg->first,
296 nextMsg->second))
297 callback->messageSizeCallback(nextMsg->second, size);
298 }
299
300 // This messageTextCallback is used for reading headers.
301
messageTextCallback(size_t dummy,string text)302 void mail::generic::Attributes::messageTextCallback(size_t dummy,
303 string text)
304 {
305 headerBuffer += text;
306
307 size_t n;
308
309 while ((n=headerBuffer.find('\n')) != std::string::npos)
310 {
311 string header=headerBuffer.substr(0, n);
312
313 headerBuffer=headerBuffer.substr(n+1);
314
315 n=header.find(':');
316
317 string value="";
318
319 if (n != std::string::npos)
320 {
321 size_t nsave=n++;
322
323 while (n < header.size() &&
324 unicode_isspace((unsigned char)header[n]))
325 n++;
326
327 value=header.substr(n);
328 header=header.substr(0, nsave);
329 }
330
331 genericBuildEnvelope(header, value, envelope);
332 // Accumulate envelope information
333
334 if (strcasecmp(header.c_str(), "Received:") == 0 &&
335 arrivalDate == 0)
336 {
337 // Attempt to synthesize arrival date, based on the
338 // first parsed Received: header.
339
340 size_t n=value.size();
341
342 while (n > 0)
343 if (value[--n] == ';')
344 {
345 rfc822_parsedate_chk(value.c_str()
346 + n + 1,
347 &arrivalDate);
348 break;
349 }
350 }
351
352 }
353 }
354
355 //
356 // Accumulate header lines into the envelope structure
357 //
358
genericBuildEnvelope(string header,string value,mail::envelope & envelope)359 void mail::generic::genericBuildEnvelope(string header, string value,
360 mail::envelope &envelope)
361 {
362 if (strcasecmp(header.c_str(), "From") == 0)
363 {
364 size_t dummy;
365
366 mail::address::fromString(value, envelope.from, dummy);
367 }
368 else if (strcasecmp(header.c_str(), "Sender") == 0)
369 {
370 size_t dummy;
371
372 mail::address::fromString(value, envelope.sender,
373 dummy);
374 }
375 else if (strcasecmp(header.c_str(), "Reply-To") == 0)
376 {
377 size_t dummy;
378
379 mail::address::fromString(value, envelope.replyto,
380 dummy);
381 }
382 else if (strcasecmp(header.c_str(), "To") == 0)
383 {
384 size_t dummy;
385
386 mail::address::fromString(value, envelope.to,
387 dummy);
388 }
389 else if (strcasecmp(header.c_str(), "Cc") == 0)
390 {
391 size_t dummy;
392
393 mail::address::fromString(value, envelope.cc,
394 dummy);
395 }
396 else if (strcasecmp(header.c_str(), "Bcc") == 0)
397 {
398 size_t dummy;
399
400 mail::address::fromString(value, envelope.bcc,
401 dummy);
402 }
403 else if (strcasecmp(header.c_str(), "In-Reply-To") == 0)
404 {
405 envelope.inreplyto=value;
406 }
407 else if (strcasecmp(header.c_str(), "Message-ID") == 0)
408 {
409 envelope.messageid=value;
410 }
411 else if (strcasecmp(header.c_str(), "Subject") == 0)
412 {
413 envelope.subject=value;
414 }
415 else if (strcasecmp(header.c_str(), "Date") == 0)
416 {
417 rfc822_parsedate_chk(value.c_str(), &envelope.date);
418 }
419 else if (strcasecmp(header.c_str(), "References") == 0)
420 {
421 vector<address> address_list;
422 size_t err_index;
423
424 address::fromString(value, address_list, err_index);
425
426 envelope.references.clear();
427
428 vector<address>::iterator
429 b=address_list.begin(),
430 e=address_list.end();
431
432 while (b != e)
433 {
434 string s=b->getAddr();
435
436 if (s.size() > 0)
437 envelope.references
438 .push_back("<" + s + ">");
439 ++b;
440 }
441 }
442 }
443
444 //////////////////////////////////////////////////////////////////////////////
445 //
446 // A reusable class that reads a raw message from a file descriptor, and:
447 //
448 // A) gets rids of the carriage returns.
449 //
450 // B) If only the header portion is requested, stop at the first blank line.
451 //
452
453 class mail::generic::CopyMimePart {
454
455 char input_buffer[BUFSIZ];
456 char output_buffer[BUFSIZ];
457
458 public:
459 CopyMimePart();
460 virtual ~CopyMimePart();
461
462 bool copy(int fd, off_t *cnt,
463 mail::ptr<mail::account> &account,
464 mail::readMode readType,
465 mail::callback::message *callback);
466
467 virtual void copyto(string)=0; // Cooked text goes here
468 };
469
CopyMimePart()470 mail::generic::CopyMimePart::CopyMimePart()
471 {
472 }
473
~CopyMimePart()474 mail::generic::CopyMimePart::~CopyMimePart()
475 {
476 }
477
copy(int fd,off_t * cnt,mail::ptr<mail::account> & account,mail::readMode readType,mail::callback::message * callback)478 bool mail::generic::CopyMimePart::copy(int fd, // File descriptor to copy from
479 off_t *cnt, // Not null - max byte count
480 mail::ptr<mail::account> &account,
481 // The canary (drops dead, we're done)
482
483 mail::readMode readType,
484 mail::callback::message *callback
485 // Original callback
486 )
487 {
488 int n=0;
489 size_t output_ptr=0;
490 char lastCh='\n';
491 bool inHeaders=true;
492 bool foldingHeader=false;
493
494 while (cnt == NULL || *cnt > 0)
495 {
496 if (cnt == NULL || (off_t)sizeof(input_buffer) < *cnt)
497 n=sizeof(input_buffer);
498 else
499 n= (int) *cnt;
500
501 n=read(fd, input_buffer, n);
502
503 if (n <= 0)
504 break;
505
506 if (cnt)
507 *cnt -= n;
508
509 int i;
510
511 for (i=0; i<n; i++)
512 {
513 if (input_buffer[i] == '\r')
514 continue;
515
516 bool endingHeaders=
517 inHeaders &&
518 input_buffer[i] == '\n' && lastCh == '\n';
519
520 if (inHeaders ?
521 readType != mail::readContents:
522 readType == mail::readContents ||
523 readType == mail::readBoth)
524 {
525 if (inHeaders && readType ==
526 mail::readHeadersFolded)
527 // Fold headers
528 {
529 if (output_ptr > 0 &&
530 output_buffer[output_ptr-1]
531 == '\n' && input_buffer[i] != '\n'
532 && unicode_isspace((unsigned char)
533 input_buffer[i]))
534 {
535 output_buffer[output_ptr-1]
536 =' ';
537 foldingHeader=true;
538 }
539 }
540
541 if (foldingHeader &&
542 (input_buffer[i] == '\n' ||
543 !unicode_isspace((unsigned char)
544 input_buffer[i])))
545 {
546 foldingHeader=false;
547 }
548
549 if (!foldingHeader)
550 {
551 if (output_ptr >= sizeof(output_buffer)
552 || endingHeaders)
553 {
554 if (output_ptr > 0)
555 copyto(string(output_buffer,
556 output_buffer +
557 output_ptr));
558
559 if (account.isDestroyed())
560 return false;
561
562 output_ptr=0;
563 }
564 output_buffer[output_ptr++]=input_buffer[i];
565 }
566 }
567
568 if (endingHeaders)
569 inHeaders=false;
570
571 lastCh=input_buffer[i];
572 }
573 }
574
575 if (n < 0)
576 return false;
577
578 if (output_ptr > 0)
579 {
580 copyto(string(output_buffer, output_buffer + output_ptr));
581
582 if (account.isDestroyed())
583 return false;
584
585 if (foldingHeader)
586 copyto(string("\n"));
587 if (account.isDestroyed())
588 return false;
589 }
590 return true;
591 }
592
593 ///////////////////////////////////////////////////////////////////////////
594 //
595 // The ReadMultiple object implements the multiple message version of
596 // genericReadMessageContent().
597
598
599 class mail::generic::ReadMultiple : public mail::callback::message,
600 public mail::generic::CopyMimePart,
601 public mail::runLater {
602
603 mail::ptr<mail::account> account;
604 mail::generic *generic;
605
606 bool peek;
607 enum mail::readMode readType;
608
609 size_t completedCnt;
610
611 mail::callback::message *callback;
612
613 int temp_fd;
614
615 // Callback for when genericGetMessageFd() is used
616
617 class TempFileCallback : public mail::callback {
618 ReadMultiple *me;
619
620 public:
621 TempFileCallback(ReadMultiple *meArg);
622 ~TempFileCallback();
623
624 void success(string);
625 void fail(string);
626
627 void reportProgress(size_t bytesCompleted,
628 size_t bytesEstimatedTotal,
629
630 size_t messagesCompleted,
631 size_t messagesEstimatedTotal);
632
633 };
634
635 TempFileCallback temp_callback;
636
637 void copyto(string);
638
639 string uid;
640 size_t messageNumber;
641
642 void reportProgress(size_t bytesCompleted,
643 size_t bytesEstimatedTotal,
644
645 size_t messagesCompleted,
646 size_t messagesEstimatedTotal);
647
648 public:
649 size_t totalCnt;
650 bool useTempFile;
651 // True if the contents of exactly one message were requested.
652 // Use genericGetMessageFd() instead of genericMessageRead(), perhaps
653 // the dumb driver will be able to take advantage of this.
654
655 queue< pair<size_t, string> > messageq; // msg num/msg uid
656
657 ReadMultiple(mail::account *accountArg, mail::generic *genericArg,
658 bool peekArg,
659 enum mail::readMode readTypeArg,
660 mail::callback::message &callbackArg,
661 size_t totalCntArg);
662
663 ~ReadMultiple();
664
665 void success(string);
666 void fail(string);
667 void messageTextCallback(size_t n, string text);
668 void RunningLater();
669
670 void processTempFile(string);
671 };
672
TempFileCallback(ReadMultiple * meArg)673 mail::generic::ReadMultiple::TempFileCallback::TempFileCallback(ReadMultiple
674 *meArg)
675 : me(meArg)
676 {
677 }
678
~TempFileCallback()679 mail::generic::ReadMultiple::TempFileCallback::~TempFileCallback()
680 {
681 }
682
success(string msg)683 void mail::generic::ReadMultiple::TempFileCallback::success(string msg)
684 {
685 me->processTempFile(msg);
686 }
687
fail(string msg)688 void mail::generic::ReadMultiple::TempFileCallback::fail(string msg)
689 {
690 me->fail(msg);
691 }
692
693 void mail::generic::ReadMultiple
reportProgress(size_t bytesCompleted,size_t bytesEstimatedTotal,size_t messagesCompleted,size_t messagesEstimatedTotal)694 ::TempFileCallback::reportProgress(size_t bytesCompleted,
695 size_t bytesEstimatedTotal,
696
697 size_t messagesCompleted,
698 size_t messagesEstimatedTotal)
699 {
700 me->reportProgress(bytesCompleted, bytesEstimatedTotal,
701 messagesCompleted,
702 messagesEstimatedTotal);
703 }
704
ReadMultiple(mail::account * accountArg,mail::generic * genericArg,bool peekArg,enum mail::readMode readTypeArg,mail::callback::message & callbackArg,size_t totalCntArg)705 mail::generic::ReadMultiple::ReadMultiple(mail::account *accountArg,
706 mail::generic *genericArg,
707 bool peekArg,
708 enum mail::readMode readTypeArg,
709 mail::callback::message
710 &callbackArg,
711 size_t totalCntArg)
712 : account(accountArg),
713 generic(genericArg),
714 peek(peekArg),
715 readType(readTypeArg),
716 completedCnt(0),
717 callback(&callbackArg),
718 temp_callback(this),
719 totalCnt(totalCntArg),
720 useTempFile(false)
721 {
722 }
723
~ReadMultiple()724 mail::generic::ReadMultiple::~ReadMultiple()
725 {
726 }
727
success(string s)728 void mail::generic::ReadMultiple::success(string s)
729 {
730 completedCnt++;
731
732 try {
733 if (!messageq.empty())
734 {
735 RunLater();
736 return;
737 }
738 } catch (...) {
739 delete this;
740 LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
741 }
742
743 mail::callback *c=callback;
744 callback=NULL;
745
746 delete this;
747 c->success(s);
748 }
749
reportProgress(size_t bytesCompleted,size_t bytesEstimatedTotal,size_t messagesCompleted,size_t messagesEstimatedTotal)750 void mail::generic::ReadMultiple::reportProgress(size_t bytesCompleted,
751 size_t bytesEstimatedTotal,
752
753 size_t messagesCompleted,
754 size_t messagesEstimatedTotal)
755 {
756 callback->reportProgress(bytesCompleted, bytesEstimatedTotal,
757 completedCnt, totalCnt);
758 }
759
RunningLater()760 void mail::generic::ReadMultiple::RunningLater()
761 {
762 try {
763 if (account.isDestroyed())
764 {
765 fail("Server connection unexpectedly shut down.");
766 return;
767 }
768
769 if (!fixMessageNumber(account, messageq.front().second,
770 messageq.front().first))
771 {
772 messageq.pop();
773 success("OK");
774 return;
775 }
776
777 uid=messageq.front().second;
778 messageNumber=messageq.front().first;
779
780 messageq.pop();
781
782 if (!peek)
783 {
784 ptr<mail::account> a(account);
785
786 generic->genericMarkRead(messageNumber);
787
788 if (a.isDestroyed())
789 {
790 fail("Aborted.");
791 return;
792 }
793 }
794
795 if (!useTempFile)
796 generic->genericMessageRead(uid,
797 messageNumber,
798 peek,
799 readType,
800 *this);
801 else
802 generic->genericGetMessageFd(uid,
803 messageNumber,
804 peek,
805 temp_fd,
806 temp_callback);
807 } catch (...) {
808 delete this;
809 LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
810 }
811 }
812
messageTextCallback(size_t n,string text)813 void mail::generic::ReadMultiple::messageTextCallback(size_t n,
814 string text)
815 {
816 callback->messageTextCallback(messageNumber, text);
817 }
818
819 // Extract the read data.
820
processTempFile(string s)821 void mail::generic::ReadMultiple::processTempFile(string s)
822 {
823 try {
824 if (lseek(temp_fd, 0L, SEEK_SET) < 0)
825 {
826 fail(strerror(errno));
827 return;
828 }
829
830 if (!copy(temp_fd, NULL, account, readType, callback))
831 {
832 fail("Server connection unexpectedly shut down.");
833 return;
834 }
835 } catch (...) {
836 delete this;
837
838 LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
839 }
840 success(s);
841 }
842
copyto(string s)843 void mail::generic::ReadMultiple::copyto(string s)
844 {
845 callback->messageTextCallback(messageNumber, s);
846 }
847
fail(string s)848 void mail::generic::ReadMultiple::fail(string s)
849 {
850 mail::callback *c=callback;
851 callback=NULL;
852
853 delete this;
854 c->fail(s);
855 }
856
857 //////////////////////////////////////////////////////////////////////////////
858 //
859 // This object implements the MIME section version of
860 // genericReadMessageContent.
861
862 class mail::generic::ReadMimePart : public mail::callback,
863 public mail::generic::CopyMimePart {
864
865 mail::ptr<mail::account> account;
866 mail::generic *generic;
867
868 size_t messageNum;
869
870 string mime_id;
871
872 enum mail::readMode readType;
873
874 mail::callback::message *callback;
875
876 void copyto(string);
877
878 bool copyHeaders();
879 bool copyContents();
880
881 void reportProgress(size_t bytesCompleted,
882 size_t bytesEstimatedTotal,
883
884 size_t messagesCompleted,
885 size_t messagesEstimatedTotal);
886
887 public:
888 int fd;
889 struct rfc2045 *rfcp;
890
891 ReadMimePart(mail::account *account, mail::generic *generic,
892 size_t messageNum,
893 string mime_id,
894 enum mail::readMode readTypeArg,
895 mail::callback::message *callback);
896
897 ~ReadMimePart();
898
899 void success(string message);
900 void fail(string message);
901 };
902
ReadMimePart(mail::account * accountArg,mail::generic * genericArg,size_t messageNumArg,string mime_idArg,enum mail::readMode readTypeArg,mail::callback::message * callbackArg)903 mail::generic::ReadMimePart::ReadMimePart(mail::account *accountArg,
904 mail::generic *genericArg,
905 size_t messageNumArg,
906 string mime_idArg,
907 // from mail::mimestruct
908
909 enum mail::readMode readTypeArg,
910 mail::callback::message
911 *callbackArg)
912 : account(accountArg),
913 generic(genericArg),
914 messageNum(messageNumArg),
915 mime_id(mime_idArg),
916 readType(readTypeArg),
917 callback(callbackArg)
918 {
919 }
920
~ReadMimePart()921 mail::generic::ReadMimePart::~ReadMimePart()
922 {
923 }
924
fail(string message)925 void mail::generic::ReadMimePart::fail(string message)
926 {
927 mail::callback *c=callback;
928
929 callback=NULL;
930
931 delete this;
932 c->fail(message);
933 }
934
success(string message)935 void mail::generic::ReadMimePart::success(string message)
936 {
937 const char *p=mime_id.c_str();
938
939 // Parse the synthesized MIME id, and locate the relevant rfc2045
940 // tructure.
941 while (rfcp && *p)
942 {
943 unsigned partNumber=0;
944
945 while (*p && isdigit((int)(unsigned char)*p))
946 partNumber=partNumber * 10 + (*p++ - '0');
947
948 if (*p)
949 p++;
950
951 rfcp=rfcp->firstpart;
952
953 while (rfcp)
954 {
955 if (!rfcp->isdummy && --partNumber == 0)
956 break;
957
958 rfcp=rfcp->next;
959 }
960 }
961
962 bool rc=true;
963
964 if (rfcp)
965 try {
966 rc=readType == mail::readBoth ||
967 readType == mail::readContents
968 ? copyContents():copyHeaders();
969 } catch (...) {
970 delete this;
971 LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
972 }
973
974 mail::callback *c=callback;
975
976 callback=NULL;
977
978 delete this;
979 if (rc)
980 c->success(message);
981 else
982 c->fail(message);
983 }
984
985 // Return just the header portion.
986
copyHeaders()987 bool mail::generic::ReadMimePart::copyHeaders()
988 {
989 struct rfc2045src *src=rfc2045src_init_fd(fd);
990 struct rfc2045headerinfo *h=src ? rfc2045header_start(src, rfcp):NULL;
991 int flags=RFC2045H_NOLC;
992
993 if (readType == mail::readHeaders)
994 flags |= RFC2045H_KEEPNL;
995
996 try {
997 char *header, *value;
998
999 while (h && rfc2045header_get(h, &header, &value,
1000 flags) == 0 && header)
1001 copyto(string(header) + ": " + value + "\n");
1002
1003 if (h)
1004 rfc2045header_end(h);
1005 if (src)
1006 rfc2045src_deinit(src);
1007 } catch (...) {
1008 if (h)
1009 rfc2045header_end(h);
1010 if (src)
1011 rfc2045src_deinit(src);
1012 LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
1013 }
1014 return true;
1015 }
1016
1017 // Return body end/or header.
1018
copyContents()1019 bool mail::generic::ReadMimePart::copyContents()
1020 {
1021 off_t start_pos, end_pos, start_body;
1022
1023 off_t nlines, nbodylines;
1024
1025 rfc2045_mimepos(rfcp, &start_pos, &end_pos, &start_body,
1026 &nlines, &nbodylines);
1027
1028 if (lseek(fd, start_pos, SEEK_SET) < 0)
1029 return false;
1030
1031 end_pos -= start_pos;
1032
1033 return copy(fd, &end_pos, account, readType, callback);
1034 }
1035
reportProgress(size_t bytesCompleted,size_t bytesEstimatedTotal,size_t messagesCompleted,size_t messagesEstimatedTotal)1036 void mail::generic::ReadMimePart::reportProgress(size_t bytesCompleted,
1037 size_t bytesEstimatedTotal,
1038
1039 size_t messagesCompleted,
1040 size_t messagesEstimatedTotal)
1041 {
1042 callback->reportProgress(bytesCompleted, bytesEstimatedTotal,
1043 messagesCompleted,
1044 messagesEstimatedTotal);
1045 }
1046
copyto(string s)1047 void mail::generic::ReadMimePart::copyto(string s)
1048 {
1049 callback->messageTextCallback(messageNum, s);
1050 }
1051
1052 //////////////////////////////////////////////////////////////////////////////
1053 //
1054 // Default genericGetMessageFdStruct() implementation. First, invoke
1055 // genericGetMessageFd(), with the following object used as the callback.
1056 // The GetMessageFdStruct then immediately invoked genericGetMessageStruct.
1057 //
1058
1059 class mail::generic::GetMessageFdStruct : public mail::callback {
1060
1061 string uid;
1062 size_t messageNumber;
1063
1064 void reportProgress(size_t bytesCompleted,
1065 size_t bytesEstimatedTotal,
1066
1067 size_t messagesCompleted,
1068 size_t messagesEstimatedTotal);
1069
1070 public:
1071 mail::generic *generic;
1072 mail::callback *callback;
1073
1074 int &fd;
1075 struct rfc2045 * &rfc2045p;
1076
1077 GetMessageFdStruct(string uidArg,
1078 size_t messageNumberArg,
1079 mail::generic *genericArg,
1080 mail::callback *callbackArg,
1081 int &fdArg,
1082 struct rfc2045 *&rfc2045pArg);
1083 ~GetMessageFdStruct();
1084
1085 void success(string);
1086 void fail(string);
1087 };
1088
GetMessageFdStruct(string uidArg,size_t messageNumberArg,mail::generic * genericArg,mail::callback * callbackArg,int & fdArg,struct rfc2045 * & rfc2045pArg)1089 mail::generic::GetMessageFdStruct::GetMessageFdStruct(string uidArg,
1090 size_t messageNumberArg,
1091 mail::generic
1092 *genericArg,
1093 mail::callback
1094 *callbackArg,
1095 int &fdArg,
1096 struct rfc2045
1097 *&rfc2045pArg)
1098 : uid(uidArg),
1099 messageNumber(messageNumberArg),
1100 generic(genericArg),
1101 callback(callbackArg),
1102 fd(fdArg),
1103 rfc2045p(rfc2045pArg)
1104 {
1105 fd= -1;
1106 rfc2045p=NULL;
1107 }
1108
~GetMessageFdStruct()1109 mail::generic::GetMessageFdStruct::~GetMessageFdStruct()
1110 {
1111 }
1112
success(string message)1113 void mail::generic::GetMessageFdStruct::success(string message)
1114 {
1115 if (rfc2045p == NULL)
1116 {
1117 generic->genericGetMessageStruct(uid, messageNumber,
1118 rfc2045p, *this);
1119 return;
1120 }
1121
1122 mail::callback *c=callback;
1123
1124 callback=NULL;
1125 delete this;
1126 c->success(message);
1127 }
1128
fail(string message)1129 void mail::generic::GetMessageFdStruct::fail(string message)
1130 {
1131 mail::callback *c=callback;
1132
1133 callback=NULL;
1134 delete this;
1135 c->fail(message);
1136 }
1137
1138 void mail::generic
reportProgress(size_t bytesCompleted,size_t bytesEstimatedTotal,size_t messagesCompleted,size_t messagesEstimatedTotal)1139 ::GetMessageFdStruct::reportProgress(size_t bytesCompleted,
1140 size_t bytesEstimatedTotal,
1141
1142 size_t messagesCompleted,
1143 size_t messagesEstimatedTotal)
1144 {
1145 callback->reportProgress(bytesCompleted, bytesEstimatedTotal,
1146 messagesCompleted,
1147 messagesEstimatedTotal);
1148 }
1149
1150
1151 //////////////////////////////////////////////////////////////////////////////
1152 //
1153 // IMPLEMENTATIONS
1154
genericGetMessageFdStruct(string uid,size_t messageNumber,bool peek,int & fdRet,struct rfc2045 * & structRet,mail::callback & callback)1155 void mail::generic::genericGetMessageFdStruct(string uid,
1156 size_t messageNumber,
1157 bool peek,
1158 int &fdRet,
1159 struct rfc2045 *&structRet,
1160 mail::callback &callback)
1161 {
1162 GetMessageFdStruct *s=new GetMessageFdStruct(uid, messageNumber,
1163 this,
1164 &callback,
1165 fdRet,
1166 structRet);
1167
1168 if (!s)
1169 {
1170 callback.fail("Out of memory.");
1171 return;
1172 }
1173
1174 try {
1175 genericGetMessageFd(uid, messageNumber, peek, fdRet, *s);
1176 } catch (...) {
1177 delete s;
1178 LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
1179 }
1180 }
1181
genericAttributes(mail::account * account,mail::generic * genericInterface,const vector<size_t> & msgs,mail::account::MessageAttributes attributes,mail::callback::message & callback)1182 void mail::generic::genericAttributes(mail::account *account,
1183 mail::generic *genericInterface,
1184 const vector<size_t> &msgs,
1185 mail::account::MessageAttributes attributes,
1186 mail::callback::message &callback)
1187 {
1188 Attributes *a=new Attributes(account, genericInterface, msgs,
1189 attributes, &callback);
1190
1191 if (!a)
1192 LIBMAIL_THROW("Out of memory.");
1193
1194 a->go("OK");
1195 }
1196
1197
fixMessageNumber(mail::account * account,string uid,size_t & msgNum)1198 bool mail::generic::fixMessageNumber(mail::account *account,
1199 string uid,
1200 size_t &msgNum)
1201 {
1202 if (!account)
1203 return false;
1204
1205 size_t n=account->getFolderIndexSize();
1206
1207 if (n > msgNum &&
1208 account->getFolderIndexInfo(msgNum).uid == uid)
1209 return true;
1210
1211 while (n)
1212 {
1213 --n;
1214 if (account->getFolderIndexInfo(n).uid == uid)
1215 {
1216 msgNum=n;
1217 return true;
1218 }
1219 }
1220
1221 return false;
1222 }
1223
genericReadMessageContent(mail::account * account,mail::generic * generic,const vector<size_t> & messages,bool peek,enum mail::readMode readType,mail::callback::message & callback)1224 void mail::generic::genericReadMessageContent(mail::account *account,
1225 mail::generic *generic,
1226 const vector<size_t> &messages,
1227 bool peek,
1228 enum mail::readMode readType,
1229 mail::callback::message
1230 &callback)
1231 {
1232 ReadMultiple *r=new ReadMultiple(account, generic,
1233 peek, readType,
1234 callback, messages.size());
1235
1236 if (!r)
1237 {
1238 callback.fail(strerror(errno));
1239 return;
1240 }
1241
1242 try {
1243 vector<size_t>::const_iterator b=messages.begin(), e=messages.end();
1244 while (b != e)
1245 {
1246 size_t n= *b++;
1247
1248 r->messageq.push( make_pair(n, account->
1249 getFolderIndexInfo(n).uid)
1250 );
1251 }
1252
1253 if (messages.size() == 1 &&
1254 (readType == mail::readBoth ||
1255 readType == mail::readContents ||
1256 generic->genericCachedUid(r->messageq.front().second)
1257 ))
1258 r->useTempFile=true; // Maybe we can cache this
1259
1260 r->success("OK");
1261 } catch (...) {
1262 delete r;
1263 LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
1264 }
1265 }
1266
1267 // Use mail::generic::Decode to synthesize decoded content from
1268 // genericReadMessageContent
1269
genericReadMessageContentDecoded(mail::account * account,mail::generic * generic,size_t messageNum,bool peek,const mail::mimestruct & info,mail::callback::message & callback)1270 void mail::generic::genericReadMessageContentDecoded(mail::account *account,
1271 mail::generic *generic,
1272 size_t messageNum,
1273 bool peek,
1274 const mail::mimestruct
1275 &info,
1276 mail::callback::message
1277 &callback)
1278 {
1279 mail::generic::Decode *d=
1280 new mail::generic::Decode(callback,
1281 info.content_transfer_encoding);
1282
1283 if (!d)
1284 LIBMAIL_THROW("Out of memory.");
1285
1286 try {
1287 genericReadMessageContent(account, generic,
1288 messageNum, peek, info,
1289 mail::readContents, *d);
1290 } catch (...)
1291 {
1292 delete d;
1293 LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
1294 }
1295 }
1296
genericReadMessageContent(mail::account * account,mail::generic * generic,size_t messageNum,bool peek,const mail::mimestruct & msginfo,enum mail::readMode readType,mail::callback::message & callback)1297 void mail::generic::genericReadMessageContent(mail::account *account,
1298 mail::generic *generic,
1299 size_t messageNum,
1300 bool peek,
1301 const mail::mimestruct &msginfo,
1302 enum mail::readMode readType,
1303 mail::callback::message
1304 &callback)
1305 {
1306 ptr<mail::account> a(account);
1307
1308 if (!peek)
1309 generic->genericMarkRead(messageNum);
1310
1311 if (a.isDestroyed())
1312 {
1313 callback.fail("Aborted.");
1314 return;
1315 }
1316
1317 ReadMimePart *m=new ReadMimePart(account, generic, messageNum,
1318 msginfo.mime_id,
1319 readType,
1320 &callback);
1321 if (!m)
1322 {
1323 callback.fail(strerror(errno));
1324 return;
1325 }
1326
1327 try {
1328 string uid=account->getFolderIndexInfo(messageNum).uid;
1329
1330 generic->genericGetMessageFdStruct(uid, messageNum, peek,
1331 m->fd,
1332 m->rfcp,
1333 *m);
1334
1335 } catch (...) {
1336 delete m;
1337 LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
1338 }
1339 }
1340
1341
1342 ///////////////////////////////////////////////////////////////////////////////
1343 //
1344 // Convert rfc2045 parse structure to a mail::mimestruct. A simple
1345 // mechanism is used to synthesize mime IDs.
1346
genericMakeMimeStructure(mail::mimestruct & s,int fd,struct rfc2045 * rfcp,string mime_id,mail::envelope * envelopePtr)1347 void mail::generic::genericMakeMimeStructure(mail::mimestruct &s,
1348 int fd,
1349 struct rfc2045 *rfcp,
1350 string mime_id,
1351 mail::envelope *
1352 envelopePtr)
1353 {
1354 s=mail::mimestruct();
1355
1356 s.mime_id=mime_id;
1357
1358 s.type="text/plain"; // Fixed below
1359
1360 s.content_transfer_encoding="8BIT";
1361
1362 // Now read the headers, and figure out the rest
1363
1364 struct rfc2045src *src=rfc2045src_init_fd(fd);
1365 struct rfc2045headerinfo *h=src ? rfc2045header_start(src, rfcp):NULL;
1366
1367 char *header;
1368 char *value;
1369
1370 off_t start_pos, end_pos, start_body, nlines, nbodylines;
1371
1372 rfc2045_mimepos(rfcp, &start_pos, &end_pos, &start_body,
1373 &nlines, &nbodylines);
1374
1375 s.content_size=end_pos - start_body;
1376 s.content_lines=nbodylines;
1377 s.smtputf8=rfcp->rfcviolation & RFC2045_ERR8BITHEADER ? true:false;
1378
1379 try {
1380 while (h && rfc2045header_get(h, &header, &value, 0) == 0)
1381 {
1382 if (header == NULL)
1383 break;
1384
1385 if (strcmp(header, "content-id") == 0)
1386 {
1387 s.content_id=value;
1388 continue;
1389 }
1390
1391 if (strcmp(header, "content-description") == 0)
1392 {
1393 s.content_description=value;
1394 continue;
1395 }
1396
1397 if (strcmp(header, "content-transfer-encoding") == 0)
1398 {
1399 s.content_transfer_encoding=value;
1400 continue;
1401 }
1402
1403 if (strcmp(header, "content-md5") == 0)
1404 {
1405 s.content_md5=value;
1406 continue;
1407 }
1408
1409 if (strcmp(header, "content-language") == 0)
1410 {
1411 s.content_id=value;
1412 continue;
1413 }
1414
1415 string *name;
1416
1417 mail::mimestruct::parameterList *attributes;
1418
1419 if (strcmp(header, "content-type") == 0)
1420 {
1421 name= &s.type;
1422 attributes= &s.type_parameters;
1423 }
1424 else if (strcmp(header, "content-disposition") == 0)
1425 {
1426 name= &s.content_disposition;
1427 attributes= &s.content_disposition_parameters;
1428 }
1429 else
1430 {
1431 if (envelopePtr)
1432 genericBuildEnvelope(header, value,
1433 *envelopePtr);
1434 continue;
1435 }
1436
1437 const char *p=value;
1438
1439 while (p && *p && *p != ';' && !unicode_isspace((unsigned char)*p))
1440 p++;
1441
1442 *name= string((const char *)value, p);
1443
1444 mail::upper(*name);
1445
1446 while (p && *p)
1447 {
1448 if (unicode_isspace((unsigned char)*p) || *p == ';')
1449 {
1450 p++;
1451 continue;
1452 }
1453
1454 const char *q=p;
1455
1456 while (*q
1457 && !unicode_isspace((unsigned char)*q) && *q != ';'
1458 && *q != '=')
1459 q++;
1460
1461 string paramName=string(p, q);
1462
1463 mail::upper(paramName);
1464
1465 while (*q
1466 && unicode_isspace((unsigned char)*q))
1467 q++;
1468
1469 string paramValue="";
1470
1471 if (*q == '=')
1472 {
1473 q++;
1474
1475 while (*q
1476 && unicode_isspace((unsigned char)*q))
1477 q++;
1478
1479 bool inQuote=false;
1480
1481 while (*q)
1482 {
1483 if (!inQuote)
1484 {
1485 if (*q == ';')
1486 break;
1487 if (unicode_isspace(
1488 (unsigned char)*q))
1489 break;
1490 }
1491
1492 if (*q == '"')
1493 {
1494 inQuote= !inQuote;
1495 q++;
1496 continue;
1497 }
1498
1499 if (*q == '\\' && q[1])
1500 ++q;
1501
1502 paramValue += *q;
1503 q++;
1504 }
1505 }
1506
1507 attributes->set_simple(paramName, paramValue);
1508 p=q;
1509 }
1510 }
1511 } catch (...) {
1512 if (h)
1513 rfc2045header_end(h);
1514 if (src)
1515 rfc2045src_deinit(src);
1516 LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
1517 }
1518
1519 if (h)
1520 rfc2045header_end(h);
1521
1522 if (src)
1523 rfc2045src_deinit(src);
1524
1525 // Fix content type/subtype
1526
1527 size_t n=s.type.find('/');
1528
1529 if (n != std::string::npos)
1530 {
1531 s.subtype=s.type.substr(n+1);
1532 s.type=s.type.substr(0, n);
1533 }
1534
1535 mail::upper(s.type);
1536 mail::upper(s.subtype);
1537 mail::upper(s.content_transfer_encoding);
1538
1539 // Now, parse the subsections
1540 //
1541
1542 mail::envelope *env=NULL;
1543
1544 if (s.messagerfc822())
1545 env= &s.getEnvelope(); // Subsection needs an envelope
1546
1547 if (mime_id.size() > 0)
1548 mime_id += ".";
1549
1550 size_t cnt=1;
1551
1552 for (rfcp=rfcp->firstpart; rfcp; rfcp=rfcp->next)
1553 {
1554 if (rfcp->isdummy)
1555 continue;
1556
1557 string buffer;
1558
1559 {
1560 ostringstream o;
1561
1562 o << cnt++;
1563 buffer=o.str();
1564 }
1565
1566 genericMakeMimeStructure( *s.addChild(),
1567 fd,
1568 rfcp,
1569 mime_id + buffer,
1570 env );
1571 }
1572 }
1573
1574 ///////////////////////////////////////////////////////////////////////////////
1575 //
1576 // Generic remove messages for accounts that use mark deleted/expunge
1577 // paradigm.
1578
1579 class mail::generic::Remove : public mail::callback {
1580
1581 mail::ptr<mail::account> acct;
1582
1583 set<string> msgs;
1584
1585 mail::callback *callback;
1586
1587 void expunge(string);
1588 void restore(string);
1589 void done(string);
1590
1591 void (mail::generic::Remove::*success_func)(string);
1592
1593 void reportProgress(size_t bytesCompleted,
1594 size_t bytesEstimatedTotal,
1595 size_t messagesCompleted,
1596 size_t messagesEstimatedTotal);
1597
1598 public:
1599 void success(string message);
1600 void fail(string message);
1601
1602 Remove(mail::account *acctArg,
1603 const vector<size_t> &messages,
1604 mail::callback *callbackArg);
1605 ~Remove();
1606
1607 void mkMessageList(vector<size_t> &msgList);
1608 };
1609
1610
Remove(mail::account * acctArg,const std::vector<size_t> & messages,mail::callback * callbackArg)1611 mail::generic::Remove::Remove(mail::account *acctArg,
1612 const std::vector<size_t> &messages,
1613 mail::callback *callbackArg)
1614 : acct(acctArg), callback(callbackArg),
1615 success_func(&mail::generic::Remove::expunge)
1616 {
1617 vector<size_t>::const_iterator b=messages.begin(), e=messages.end();
1618
1619 size_t n= acct->getFolderIndexSize();
1620
1621 while (b != e)
1622 {
1623 if (*b < n)
1624 msgs.insert(acct->getFolderIndexInfo(*b).uid);
1625 b++;
1626 }
1627 }
1628
~Remove()1629 mail::generic::Remove::~Remove()
1630 {
1631 if (callback)
1632 {
1633 mail::callback * volatile p=callback;
1634
1635 callback=NULL;
1636 p->success("OK");
1637 }
1638 }
1639
expunge(std::string message)1640 void mail::generic::Remove::expunge(std::string message)
1641 {
1642 success_func= &mail::generic::Remove::restore;
1643 acct->updateFolderIndexInfo( *this );
1644 }
1645
restore(std::string message)1646 void mail::generic::Remove::restore(std::string message)
1647 {
1648 success_func= &mail::generic::Remove::done;
1649
1650 vector<size_t> flipDeleted;
1651
1652 mkMessageList(flipDeleted);
1653
1654 if (flipDeleted.size() > 0)
1655 {
1656 messageInfo flags;
1657
1658 flags.deleted=true;
1659
1660 acct->updateFolderIndexFlags(flipDeleted, false,
1661 true, flags, *this);
1662 return;
1663 }
1664 done(message);
1665 }
1666
success(std::string message)1667 void mail::generic::Remove::success(std::string message)
1668 {
1669 if (acct.isDestroyed())
1670 {
1671 delete this;
1672 return;
1673 }
1674 (this->*success_func)(message);
1675 }
1676
fail(std::string message)1677 void mail::generic::Remove::fail(std::string message)
1678 {
1679 if (callback)
1680 {
1681 mail::callback * volatile p=callback;
1682
1683 callback=NULL;
1684 p->fail(message);
1685 }
1686 }
1687
done(std::string message)1688 void mail::generic::Remove::done(std::string message)
1689 {
1690 if (callback)
1691 {
1692 mail::callback * volatile p=callback;
1693
1694 callback=NULL;
1695 p->success(message);
1696 }
1697 delete this;
1698 }
1699
reportProgress(size_t bytesCompleted,size_t bytesEstimatedTotal,size_t messagesCompleted,size_t messagesEstimatedTotal)1700 void mail::generic::Remove::reportProgress(size_t bytesCompleted,
1701 size_t bytesEstimatedTotal,
1702 size_t messagesCompleted,
1703 size_t messagesEstimatedTotal)
1704 {
1705 if (callback)
1706 callback->reportProgress(bytesCompleted,
1707 bytesEstimatedTotal,
1708 messagesCompleted,
1709 messagesEstimatedTotal);
1710 }
1711
1712
1713
mkMessageList(std::vector<size_t> & msgList)1714 void mail::generic::Remove::mkMessageList(std::vector<size_t> &msgList)
1715 {
1716 mail::account *p=acct;
1717
1718 if (!p)
1719 return;
1720
1721 size_t n=p->getFolderIndexSize();
1722 size_t i;
1723
1724 for (i=0; i<n; i++)
1725 if (msgs.count(p->getFolderIndexInfo(i).uid) > 0)
1726 msgList.push_back(i);
1727 }
1728
1729
genericRemoveMessages(mail::account * account,const std::vector<size_t> & messages,callback & cb)1730 void mail::generic::genericRemoveMessages(mail::account *account,
1731 const std::vector<size_t> &messages,
1732 callback &cb)
1733 {
1734 vector<size_t> msgToDo;
1735
1736 {
1737 set<size_t> msgSet;
1738
1739 msgSet.insert(messages.begin(), messages.end());
1740
1741 size_t i, n=account->getFolderIndexSize();
1742
1743 for (i=0; i<n; i++)
1744 {
1745 bool toDelete=msgSet.count(i) > 0;
1746
1747 if (toDelete != account->getFolderIndexInfo(i).deleted)
1748 msgToDo.push_back(i);
1749 }
1750 }
1751
1752 Remove *r=new Remove(account, msgToDo, &cb);
1753
1754 if (!r)
1755 {
1756 cb.fail(strerror(errno));
1757 return;
1758 }
1759
1760 try
1761 {
1762 vector<size_t> flipDeleted;
1763
1764 r->mkMessageList(flipDeleted);
1765
1766 if (flipDeleted.size() > 0)
1767 {
1768 messageInfo flags;
1769
1770 flags.deleted=true;
1771
1772 account->updateFolderIndexFlags(flipDeleted, true,
1773 false, flags,
1774 *r);
1775 return;
1776 }
1777
1778 r->success("OK");
1779 } catch (...) {
1780 delete r;
1781 LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
1782 }
1783 }
1784
1785 ///////////////////////////////////////////////////////////////////////////////
1786 //
1787 // Generic 'move messages' for accounts that do not support a message 'move',
1788 // or for moves between accounts. This is implemented as a COPY, followed by
1789 // a Remove.
1790
1791 class mail::generic::Move : public mail::callback {
1792
1793 mail::ptr<mail::account> acct;
1794
1795 set<string> msgs;
1796
1797 mail::callback *callback;
1798
1799 public:
1800 Move(mail::account *acctArg,
1801 const vector<size_t> &messages,
1802 mail::callback *callbackArg);
1803 ~Move();
1804
1805 void success(string message);
1806 void fail(string message);
1807 void reportProgress(size_t bytesCompleted,
1808 size_t bytesEstimatedTotal,
1809 size_t messagesCompleted,
1810 size_t messagesEstimatedTotal);
1811 };
1812
Move(mail::account * acctArg,const vector<size_t> & messages,mail::callback * callbackArg)1813 mail::generic::Move::Move(mail::account *acctArg,
1814 const vector<size_t> &messages,
1815 mail::callback *callbackArg)
1816 : acct(acctArg), callback(callbackArg)
1817 {
1818 vector<size_t>::const_iterator b=messages.begin(), e=messages.end();
1819 size_t n= acct->getFolderIndexSize();
1820
1821 while (b != e)
1822 {
1823 if (*b < n)
1824 msgs.insert(acct->getFolderIndexInfo(*b).uid);
1825 b++;
1826 }
1827 }
1828
~Move()1829 mail::generic::Move::~Move()
1830 {
1831 if (callback)
1832 {
1833 mail::callback * volatile p=callback;
1834
1835 callback=NULL;
1836 p->fail("Exception caught in generic::Move::success");
1837 }
1838 }
1839
reportProgress(size_t bytesCompleted,size_t bytesEstimatedTotal,size_t messagesCompleted,size_t messagesEstimatedTotal)1840 void mail::generic::Move::reportProgress(size_t bytesCompleted,
1841 size_t bytesEstimatedTotal,
1842 size_t messagesCompleted,
1843 size_t messagesEstimatedTotal)
1844 {
1845 if (callback)
1846 callback->reportProgress(bytesCompleted,
1847 bytesEstimatedTotal,
1848 messagesCompleted,
1849 messagesEstimatedTotal);
1850 }
1851
success(string message)1852 void mail::generic::Move::success(string message)
1853 {
1854 if (!acct.isDestroyed() && callback)
1855 {
1856 vector<size_t> msgList;
1857
1858 size_t n=acct->getFolderIndexSize();
1859 size_t i;
1860
1861 for (i=0; i<n; i++)
1862 if (msgs.count(acct->getFolderIndexInfo(i).uid) > 0)
1863 msgList.push_back(i);
1864
1865 if (msgs.size() > 0)
1866 {
1867 mail::callback * volatile p=callback;
1868 mail::account * volatile a=acct;
1869
1870 callback=NULL;
1871 delete this;
1872
1873 try {
1874 a->removeMessages(msgList, *p);
1875 } catch (...) {
1876 p->fail("Exception caught in generic::Move::success");
1877 }
1878 return;
1879 }
1880 }
1881
1882 if (callback)
1883 {
1884 mail::callback * volatile p=callback;
1885
1886 callback=NULL;
1887 delete this;
1888 p->success(message);
1889 }
1890 }
1891
fail(string message)1892 void mail::generic::Move::fail(string message)
1893 {
1894 if (callback)
1895 {
1896 mail::callback * volatile p=callback;
1897
1898 callback=NULL;
1899 delete this;
1900 p->fail(message);
1901 }
1902 }
1903
genericMoveMessages(mail::account * account,const vector<size_t> & messages,mail::folder * copyTo,mail::callback & callback)1904 void mail::generic::genericMoveMessages(mail::account *account,
1905 const vector<size_t> &messages,
1906 mail::folder *copyTo,
1907 mail::callback &callback)
1908 {
1909 Move *m=new Move(account, messages, &callback);
1910
1911 if (!m)
1912 {
1913 callback.fail(strerror(errno));
1914 return;
1915 }
1916
1917 try {
1918 account->copyMessagesTo(messages, copyTo, *m);
1919 } catch (...) {
1920 delete m;
1921 }
1922 }
1923
1924 //////////////////////////////////////////////////////////////////////////
1925
updateKeywordHelper(const set<string> & keywordsArg,bool setOrChangeArg,bool changeToArg)1926 mail::generic::updateKeywordHelper::updateKeywordHelper(const set<string> &keywordsArg,
1927 bool setOrChangeArg,
1928 bool changeToArg)
1929 : keywords(keywordsArg),
1930 setOrChange(setOrChangeArg),
1931 changeTo(changeToArg)
1932 {
1933 }
1934
~updateKeywordHelper()1935 mail::generic::updateKeywordHelper::~updateKeywordHelper()
1936 {
1937 }
1938
1939 bool mail::generic::updateKeywordHelper
doUpdateKeyword(mail::keywords::Message & keyWords,mail::keywords::Hashtable & h)1940 ::doUpdateKeyword(mail::keywords::Message &keyWords,
1941 mail::keywords::Hashtable &h)
1942 {
1943 if (!setOrChange)
1944 return keyWords.setFlags(h, keywords);
1945
1946 set<string>::iterator b=keywords.begin(), e=keywords.end();
1947
1948 while (b != e)
1949 {
1950 if (!(changeTo ? keyWords.addFlag(h, *b):
1951 keyWords.remFlag(*b)))
1952 return false;
1953 ++b;
1954 }
1955
1956 return true;
1957 }
1958
genericProcessKeyword(size_t messageNumber,updateKeywordHelper & helper)1959 bool mail::generic::genericProcessKeyword(size_t messageNumber,
1960 updateKeywordHelper &helper)
1961 {
1962 return true;
1963 }
1964
genericUpdateKeywords(const vector<size_t> & messages,const set<string> & keywords,bool setOrChange,bool changeTo,mail::callback::folder * folderCallback,mail::generic * generic,mail::callback & cb)1965 void mail::generic::genericUpdateKeywords(const vector<size_t> &messages,
1966 const set<string> &keywords,
1967 bool setOrChange,
1968 // false: set, true: see changeTo
1969 bool changeTo,
1970 mail::callback::folder
1971 *folderCallback,
1972 mail::generic *generic,
1973 mail::callback &cb)
1974 {
1975 vector<size_t>::const_iterator b=messages.begin(),
1976 e=messages.end();
1977
1978 updateKeywordHelper helper(keywords, setOrChange, changeTo);
1979
1980 while (b != e)
1981 {
1982 --e;
1983 if (!generic->genericProcessKeyword(*e, helper))
1984 {
1985 cb.fail(strerror(errno));
1986 return;
1987 }
1988 }
1989
1990 e=messages.end();
1991
1992 while (b != e)
1993 folderCallback->messageChanged(*--e);
1994
1995 return cb.success("Ok.");
1996 }
1997