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