1 /*
2 ** Copyright 2002-2008, Double Precision Inc.
3 **
4 ** See COPYING for distribution information.
5 */
6 #include "libmail_config.h"
7 #include "driver.H"
8 #include "mail.H"
9 #include "misc.H"
10 #include "addmessage.H"
11 #include "pop3.H"
12 #include "copymessage.H"
13 #include "search.H"
14 #include "imaphmac.H"
15 #include "base64.H"
16 #include "objectmonitor.H"
17 #include "libcouriertls.h"
18 #include "expungelist.H"
19 #include <sstream>
20 #include <iomanip>
21 #include <time.h>
22 #include <errno.h>
23 #include <unistd.h>
24 #include <algorithm>
25 #include <vector>
26 #include <signal.h>
27 #include <fcntl.h>
28 #include <set>
29 #include <cstring>
30 
31 #include "rfc2045/rfc2045.h"
32 
33 using namespace std;
34 
35 /////////////////////////////////////////////////////////////////////////////
36 
37 LIBMAIL_START
38 
open_pop3(mail::account * & accountRet,mail::account::openInfo & oi,mail::callback & callback,mail::callback::disconnect & disconnectCallback)39 static bool open_pop3(mail::account *&accountRet,
40 		      mail::account::openInfo &oi,
41 		      mail::callback &callback,
42 		      mail::callback::disconnect &disconnectCallback)
43 {
44 	mail::loginInfo pop3LoginInfo;
45 
46 	if (!mail::loginUrlDecode(oi.url, pop3LoginInfo))
47 		return false;
48 
49 	if (pop3LoginInfo.method != "pop3" &&
50 	    pop3LoginInfo.method != "pop3s")
51 		return false;
52 
53 	accountRet=new mail::pop3(oi.url, oi.pwd,
54 				  oi.certificates,
55 				  oi.loginCallbackObj,
56 				  callback,
57 				  disconnectCallback);
58 	return true;
59 }
60 
pop3_remote(string url,bool & flag)61 static bool pop3_remote(string url, bool &flag)
62 {
63 	mail::loginInfo pop3LoginInfo;
64 
65 	if (!mail::loginUrlDecode(url, pop3LoginInfo))
66 		return false;
67 
68 	if (pop3LoginInfo.method != "pop3" &&
69 	    pop3LoginInfo.method != "pop3s")
70 		return false;
71 
72 	flag=true;
73 	return true;
74 }
75 
76 driver pop3_driver= { &open_pop3, &pop3_remote };
77 
78 LIBMAIL_END
79 
80 /////////////////////////////////////////////////////////////////////////////
81 //
82 // Most tasks will subclass from LoggedInTask which makes sure we're
83 // logged in to the POP3 server if we're not currently logged in.
84 //
85 // Exported methods:
86 //
87 // LoggedInTask(mail::pop3 &server) // Constructor
88 //
89 // mail::pop3 *myserver; // Inherited from Task
90 //
91 // void loggedIn(); // Logged in to the server, begin doing what you need to do
92 // void loginFailed(string errmsg); // Log in failed, about to be destroyed
93 //
94 // int getTimeout(); // Inherited from Task
95 // void disconnected(const char *reason); // Inherited from Task
96 // void serverResponse(const char *message); // Inherited from task
97 //
98 
99 class mail::pop3::LoggedInTask : public mail::pop3::Task,
100 		  public mail::callback {
101 
102 	// Inherited from mail::callback
103 
104 	void success(string);
105 	void fail(string);
106 
107 	void installedTask();
108 
109 	void reportProgress(size_t bytesCompleted,
110 			    size_t bytesEstimatedTotal,
111 
112 			    size_t messagesCompleted,
113 			    size_t messagesEstimatedTotal);
114 
115 public:
116 	LoggedInTask(mail::callback *callback, mail::pop3 &myserverArg);
117 	~LoggedInTask();
118 
119 	virtual void loggedIn()=0;
120 	virtual void loginFailed(string error)=0;
121 };
122 
Task(mail::callback * callbackArg,mail::pop3 & myserverArg)123 mail::pop3::Task::Task(mail::callback *callbackArg, mail::pop3 &myserverArg)
124 	: callbackPtr(callbackArg), myserver(&myserverArg)
125 {
126 	time(&defaultTimeout);
127 	defaultTimeout += myserverArg.timeoutSetting;
128 }
129 
~Task()130 mail::pop3::Task::~Task()
131 {
132 	mail::callback *c=callbackPtr;
133 
134 	callbackPtr=NULL;
135 
136 	if (c)
137 		c->fail("POP3 task terminated unexpectedly.");
138 }
139 
installedTask()140 void mail::pop3::Task::installedTask()
141 {
142 	resetTimeout();
143 }
144 
resetTimeout()145 void mail::pop3::Task::resetTimeout()
146 {
147 	time(&defaultTimeout);
148 	defaultTimeout += myserver->timeoutSetting;
149 }
150 
getTimeout()151 int mail::pop3::Task::getTimeout()
152 {
153 	time_t now;
154 
155 	time(&now);
156 
157 	if (now < defaultTimeout)
158 		return defaultTimeout - now;
159 
160 	string errmsg=myserver->socketDisconnect();
161 
162 	if (errmsg.size() == 0)
163 		errmsg="Server timed out.";
164 
165 	disconnected(errmsg.c_str());
166 	return 0;
167 }
168 
disconnected(const char * reason)169 void mail::pop3::Task::disconnected(const char *reason)
170 {
171 	if (myserver->tasks.empty() ||
172 	    (*myserver->tasks.begin()) != this)
173 		kill(getpid(), SIGABRT);
174 
175 	mail::callback *c=callbackPtr;
176 
177 	callbackPtr=NULL;
178 
179 	try {
180 		myserver->tasks.erase(myserver->tasks.begin());
181 
182 		if (!myserver->tasks.empty())
183 			(*myserver->tasks.begin())->disconnected(reason);
184 		delete this;
185 
186 		if (c)
187 			c->fail(reason);
188 	} catch (...) {
189 		if (c)
190 			c->fail(reason);
191 		LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
192 	}
193 }
194 
done()195 void mail::pop3::Task::done()
196 {
197 	mail::callback *c=callbackPtr;
198 
199 	callbackPtr=NULL;
200 
201 	if (myserver->tasks.empty() || (*myserver->tasks.begin()) != this)
202 		kill(getpid(), SIGABRT);
203 
204 	myserver->tasks.erase(myserver->tasks.begin());
205 
206 	mail::pop3 *s=myserver;
207 
208 	bool flag=isLogoutTask();
209 
210 	delete this;
211 
212 	if (!s->tasks.empty())
213 		(*s->tasks.begin())->installedTask();
214 	else if (!flag)
215 		time(&s->lastTaskCompleted);
216 
217 	if (c)
218 		c->fail("POP3 task aborted.");
219 }
220 
isLogoutTask()221 bool mail::pop3::Task::isLogoutTask()
222 {
223 	return false;
224 }
225 
willReconnect()226 bool mail::pop3::Task::willReconnect()
227 {
228 	return false;
229 }
230 
231 /////////////////////////////////////////////////////////////////////////////
232 //
233 // Log into the server, and create mail::pop3::uidlmap.
234 
235 class mail::pop3::LoginTask : public mail::pop3::Task,
236 	    public mail::loginInfo::callbackTarget {
237 
238 	void fail(const char *reason);
239 
240 	void (LoginTask::*currentHandler)(const char *status);
241 
242 	void serverResponse(const char *message);
243 
244 	void installedTask();
245 
246 	void greetingHandler(const char *message);
247 	void capaHandler(const char *message);
248 	void stlsHandler(const char *message);
249 	void stlsCapaHandler(const char *message);
250 
251 	int hmac_index;	// Next HMAC method to try.
252 	void hmacHandler(const char *message);
253 	void hmacChallengeHandler(const char *message);
254 	void userHandler(const char *message);
255 	void passHandler(const char *message);
256 	void listHandler(const char *message);
257 	void uidlHandler(const char *message);
258 
259 	void addCapability(const char *message);
260 	void addStlsCapability(const char *message);
261 	void processExternalLogin(const char *message);
262 
263 	void processedCapabilities();
264 	void loggedIn(const char *message);
265 
266 	void (mail::pop3::LoginTask::*login_callback_handler)(std::string);
267 
268 	void loginInfoCallback(std::string);
269 	void loginInfoCallbackCancel();
270 
271 	void loginCallbackUid(std::string);
272 	void loginCallbackPwd(std::string);
273 
274 	void utf8Handler(const char *message);
275 	void utf8CapaDone();
276 	void stlsCapaDone();
277 	void nonExternalLogin();
278 public:
279 	LoginTask(mail::pop3 &server, mail::callback *callbackArg);
280 	~LoginTask();
281 
282 private:
283 	bool utf8_capability;
284 };
285 
LoginTask(mail::pop3 & server,mail::callback * callbackArg)286 mail::pop3::LoginTask::LoginTask(mail::pop3 &server,
287 				 mail::callback *callbackArg)
288 	: Task(callbackArg, server), utf8_capability(false)
289 {
290 }
291 
~LoginTask()292 mail::pop3::LoginTask::~LoginTask()
293 {
294 }
295 
fail(const char * reason)296 void mail::pop3::LoginTask::fail(const char *reason)
297 {
298 	mail::callback *c=callbackPtr;
299 
300 	callbackPtr=NULL;
301 
302 	try {
303 		string errmsg="ERROR: ";
304 
305 		if (strncmp(reason, "-ERR", 4) == 0)
306 		{
307 			reason += 4;
308 
309 			while (*reason == ' ')
310 				reason++;
311 		}
312 
313 		errmsg += reason;
314 
315 		done();
316 
317 		if (c)
318 			c->fail(errmsg.c_str());
319 	} catch (...) {
320 		if (c)
321 			c->fail("ERROR: An exception occured in mail::pop3");
322 		LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
323 	}
324 }
325 
serverResponse(const char * message)326 void mail::pop3::LoginTask::serverResponse(const char *message)
327 {
328 	if (currentHandler)
329 		(this->*currentHandler)(message);
330 }
331 
installedTask()332 void mail::pop3::LoginTask::installedTask()
333 {
334 	Task::installedTask();
335 
336 	if (myserver->getfd() >= 0)
337 	{
338 		loggedIn("+OK Already logged in."); // Already
339 		return;
340 	}
341 
342 	currentHandler= &LoginTask::greetingHandler;
343 
344 	myserver->pop3LoginInfo = myserver->savedLoginInfo;
345 
346         string errmsg=myserver->
347 		socketConnect(myserver->pop3LoginInfo, "pop3", "pop3s");
348 
349         if (errmsg.size() > 0)
350         {
351 		fail(errmsg.c_str());
352                 return;
353         }
354 }
355 
356 // First message from the server.
357 
greetingHandler(const char * message)358 void mail::pop3::LoginTask::greetingHandler(const char *message)
359 {
360 	if (*message != '+')
361 	{
362 		myserver->socketDisconnect();
363 		fail(message);
364 		return;
365 	}
366 
367 	myserver->socketWrite("CAPA\r\n");
368 	currentHandler= &LoginTask::capaHandler;
369 }
370 
371 // CAPA reply.
372 
capaHandler(const char * message)373 void mail::pop3::LoginTask::capaHandler(const char *message)
374 {
375 	myserver->capabilities.clear();
376 
377 	if (*message == '-')
378 	{
379 		processedCapabilities(); // No capabilities
380 		return;
381 	}
382 
383 	if (*message != '+')
384 	{
385 		fail(message);
386 		return;
387 	}
388 	currentHandler=&LoginTask::addCapability;
389 }
390 
391 // Capability list.
392 
addCapability(const char * message)393 void mail::pop3::LoginTask::addCapability(const char *message)
394 {
395 	if (strcmp(message, ".") == 0)
396 	{
397 		processedCapabilities();
398 		return;
399 	}
400 
401 	string c=message, v="";
402 	size_t p=c.find(' ');
403 
404 	if (p != std::string::npos)
405 	{
406 		v=c.substr(p+1);
407 		c=c.substr(0, p);
408 	}
409 
410 	// Add AUTH=method capability for each SASL listed method.
411 
412 	if (c == "SASL")
413 	{
414 		while ((p=v.find(' ')) != std::string::npos)
415 		{
416 			c="AUTH=" + v.substr(0, p);
417 			v=v.substr(p+1);
418 			myserver->capabilities.insert(make_pair(c,
419 							       string("1")));
420 		}
421 		c="AUTH=" + v;
422 		v="1";
423 	}
424 
425 	if (c == "UTF8")
426 		utf8_capability=true;
427 
428 	if (v.size() == 0)
429 		v="1";
430 
431 	myserver->capabilities.insert(make_pair(c, v));
432 }
433 
processedCapabilities()434 void mail::pop3::LoginTask::processedCapabilities()
435 {
436 	if (!utf8_capability)
437 		utf8CapaDone();
438 
439 	myserver->socketWrite("UTF8\r\n");
440 	currentHandler= &LoginTask::utf8Handler;
441 }
442 
utf8Handler(const char * message)443 void mail::pop3::LoginTask::utf8Handler(const char *message)
444 {
445 	utf8CapaDone();
446 }
447 
utf8CapaDone()448 void mail::pop3::LoginTask::utf8CapaDone()
449 {
450 #if HAVE_LIBCOURIERTLS
451 
452 	if (myserver->pop3LoginInfo.use_ssl
453 	    || myserver->pop3LoginInfo.options.count("notls") > 0
454 	    || !myserver->hasCapability("STLS"))
455 	{
456 		stlsCapaDone();
457 		return;
458 	}
459 
460 	currentHandler=&LoginTask::stlsHandler;
461 	myserver->socketWrite("STLS\r\n");
462 #else
463 	stlsCapaDone();
464 #endif
465 }
466 
stlsHandler(const char * message)467 void mail::pop3::LoginTask::stlsHandler(const char *message)
468 {
469 	if (*message != '+')
470 	{
471 		fail(message);
472 		return;
473 	}
474 
475 #if HAVE_LIBCOURIERTLS
476 
477 	myserver->pop3LoginInfo.callbackPtr=callbackPtr;
478 
479 	callbackPtr=NULL;
480 	if (!myserver->socketBeginEncryption(myserver->pop3LoginInfo))
481 		return; // Can't set callbackPtr to NULL now, because
482 	// this object could be destroyed.
483 
484 	// If beginEcnryption() succeeded, restore the callback ptr.
485 	callbackPtr=myserver->pop3LoginInfo.callbackPtr;
486 #endif
487 	currentHandler=&LoginTask::stlsCapaHandler;
488 	myserver->socketWrite("CAPA\r\n");
489 }
490 
stlsCapaHandler(const char * message)491 void mail::pop3::LoginTask::stlsCapaHandler(const char *message)
492 {
493 	myserver->capabilities.clear();
494 
495 	if (*message == '-')
496 	{
497 		processedCapabilities(); // No capabilities
498 		return;
499 	}
500 
501 	if (*message != '+')
502 	{
503 		fail(message);
504 		return;
505 	}
506 	currentHandler=&LoginTask::addStlsCapability;
507 }
508 
addStlsCapability(const char * message)509 void mail::pop3::LoginTask::addStlsCapability(const char *message)
510 {
511 	if (strcmp(message, ".") == 0)
512 	{
513 		stlsCapaDone();
514 		return;
515 	}
516 	addCapability(message);
517 }
518 
stlsCapaDone()519 void mail::pop3::LoginTask::stlsCapaDone()
520 {
521 	if (!myserver->hasCapability("AUTH=EXTERNAL"))
522 	{
523 		nonExternalLogin();
524 		return;
525 	}
526 
527 	currentHandler=&LoginTask::processExternalLogin;
528 	myserver->socketWrite("AUTH EXTERNAL =\r\n");
529 }
530 
processExternalLogin(const char * message)531 void mail::pop3::LoginTask::processExternalLogin(const char *message)
532 {
533 	if (*message != '+')
534 	{
535 		nonExternalLogin();
536 		return;
537 	}
538 
539 	loggedIn(message);
540 }
541 
nonExternalLogin()542 void mail::pop3::LoginTask::nonExternalLogin()
543 {
544 	if (!myserver->pop3LoginInfo.loginCallbackFunc)
545 	{
546 		loginCallbackPwd(myserver->pop3LoginInfo.pwd);
547 		return;
548 	}
549 
550 	currentHandler=NULL;
551 	if (myserver->pop3LoginInfo.uid.size() > 0)
552 	{
553 		loginCallbackUid(myserver->pop3LoginInfo.uid);
554 		return;
555 	}
556 
557 	login_callback_handler= &mail::pop3::LoginTask::loginCallbackUid;
558 	currentCallback=myserver->pop3LoginInfo.loginCallbackFunc;
559 	currentCallback->target=this;
560 	currentCallback->getUserid();
561 }
562 
loginInfoCallback(std::string cbvalue)563 void mail::pop3::LoginTask::loginInfoCallback(std::string cbvalue)
564 {
565 	currentCallback=NULL;
566 	(this->*login_callback_handler)(cbvalue);
567 }
568 
569 
loginInfoCallbackCancel()570 void mail::pop3::LoginTask::loginInfoCallbackCancel()
571 {
572 	currentCallback=NULL;
573 	fail("Login cancelled.");
574 }
575 
loginCallbackUid(std::string uid)576 void mail::pop3::LoginTask::loginCallbackUid(std::string uid)
577 {
578 	myserver->savedLoginInfo.uid=
579 		myserver->pop3LoginInfo.uid=uid;
580 
581 	if (myserver->pop3LoginInfo.pwd.size() > 0)
582 	{
583 		loginCallbackPwd(myserver->pop3LoginInfo.pwd);
584 		return;
585 	}
586 
587 	login_callback_handler= &mail::pop3::LoginTask::loginCallbackPwd;
588 	currentCallback=myserver->pop3LoginInfo.loginCallbackFunc;
589 	currentCallback->target=this;
590 	currentCallback->getPassword();
591 }
592 
loginCallbackPwd(std::string pwd)593 void mail::pop3::LoginTask::loginCallbackPwd(std::string pwd)
594 {
595 	myserver->savedLoginInfo.pwd=myserver->pop3LoginInfo.pwd=pwd;
596 
597 	hmac_index=0;
598 	hmacHandler("-ERR Login failed"); // Not really
599 }
600 
hmacHandler(const char * message)601 void mail::pop3::LoginTask::hmacHandler(const char *message)
602 {
603 	if (*message == '+')
604 	{
605 		loggedIn(message);
606 		return;
607 	}
608 
609 	for (;;)
610 	{
611 		if (mail::imaphmac::hmac_methods[hmac_index] == NULL ||
612 		    myserver->pop3LoginInfo.uid.size() == 0)
613 		{
614 			if (myserver->pop3LoginInfo.options.count("cram"))
615 				fail(message);
616 
617 			if (myserver->pop3LoginInfo.uid.size() == 0)
618 			{
619 				loggedIn("+OK No userid, assuming preauthenticated login.");
620 				return;
621 			}
622 
623 			currentHandler=&LoginTask::userHandler;
624 			myserver->socketWrite("USER " +
625 					     myserver->pop3LoginInfo.uid
626 					     + "\r\n");
627 			return;
628 		}
629 
630 		if (!myserver->hasCapability(string("AUTH=CRAM-") +
631 					    mail::imaphmac
632 					    ::hmac_methods[hmac_index]
633 					    ->getName()))
634 		{
635 			++hmac_index;
636 			continue;
637 		}
638 		break;
639 	}
640 
641 	currentHandler=&LoginTask::hmacChallengeHandler;
642 	myserver->socketWrite(string("AUTH CRAM-") +
643 			     mail::imaphmac::hmac_methods[hmac_index]
644 			     ->getName() + "\r\n");
645 }
646 
hmacChallengeHandler(const char * message)647 void mail::pop3::LoginTask::hmacChallengeHandler(const char *message)
648 {
649 	if (*message != '+')
650 	{
651 		++hmac_index;
652 		hmacHandler(message);
653 		return;
654 	}
655 
656 	do
657 	{
658 		++message;
659 	} while (*message == ' ');
660 
661 	string s=mail::decodebase64str(message);
662 
663 	s= (*mail::imaphmac::hmac_methods[hmac_index++])
664 		(myserver->pop3LoginInfo.pwd, s);
665 	string sHex;
666 
667 	{
668 		ostringstream hexEncode;
669 
670 		hexEncode << hex;
671 
672 		string::iterator b=s.begin();
673 		string::iterator e=s.end();
674 
675 		while (b != e)
676 			hexEncode << setw(2) << setfill('0')
677 				  << (int)(unsigned char)*b++;
678 		sHex=hexEncode.str();
679 	}
680 
681 	s=mail::encodebase64str(myserver->pop3LoginInfo.uid + " " + sHex);
682 	currentHandler=&LoginTask::hmacHandler;
683 	myserver->socketWrite(s + "\r\n");
684 }
685 
userHandler(const char * message)686 void mail::pop3::LoginTask::userHandler(const char *message)
687 {
688 	if (*message != '+')
689 	{
690 		fail(message);
691 		return;
692 	}
693 
694 	currentHandler=&LoginTask::passHandler;
695 	myserver->socketWrite("PASS " +
696 			     myserver->pop3LoginInfo.pwd + "\r\n");
697 }
698 
passHandler(const char * message)699 void mail::pop3::LoginTask::passHandler(const char *message)
700 {
701 	if (*message != '+')
702 	{
703 		fail(message);
704 		return;
705 	}
706 	loggedIn(message);
707 }
708 
loggedIn(const char * message)709 void mail::pop3::LoginTask::loggedIn(const char *message)
710 {
711 	myserver->savedLoginInfo.loginCallbackFunc=
712 		myserver->pop3LoginInfo.loginCallbackFunc=NULL; // Got it
713 	currentHandler=&LoginTask::listHandler;
714 	myserver->listMap.clear();
715 	myserver->socketWrite("LIST\r\n");
716 }
717 
listHandler(const char * message)718 void mail::pop3::LoginTask::listHandler(const char *message)
719 {
720 	if (*message == '-')
721 	{
722 		fail(message);
723 		return;
724 	}
725 
726 	if (*message == '+')
727 		return; // LIST results follow
728 
729 	if (strcmp(message, ".") == 0)
730 	{
731 		myserver->uidlMap.clear();
732 
733 		if (!myserver->ispop3maildrop())
734 		{
735 			currentHandler=&LoginTask::uidlHandler;
736 			myserver->socketWrite("UIDL\r\n");
737 			return;
738 		}
739 
740 		uidlHandler("."); /* Don't do UIDL for pop3 maildrops */
741 		return;
742 	}
743 
744 	int messageNum=0;
745 	unsigned long messageSize=0;
746 
747 	istringstream i(message);
748 
749 	i >> messageNum;
750 
751 	i >> messageSize;
752 
753 	myserver->listMap.insert(make_pair(messageNum, messageSize));
754 }
755 
uidlHandler(const char * message)756 void mail::pop3::LoginTask::uidlHandler(const char *message)
757 {
758 	if (*message == '-')
759 	{
760 		fail("-ERR This POP3 server does not appear to implement the POP3 UIDL command.  Upgrade the POP3 server software, or use the POP3 maildrop mode.");
761 		return;
762 	}
763 
764 	if (*message == '+')
765 		return; // UIDL results follow
766 
767 	if (strcmp(message, ".") == 0)
768 	{
769 		mail::callback *c=callbackPtr;
770 
771 		callbackPtr=NULL;
772 
773 		done();
774 
775 		if (c)
776 		{
777 			c->success("Logged in.");
778 		}
779 		return;
780 	}
781 
782 	int msgNum=0;
783 
784 	while (*message && isdigit((int)(unsigned char)*message))
785 		msgNum=msgNum * 10 + (*message++ - '0');
786 
787 	while (*message && unicode_isspace((unsigned char)*message))
788 		message++;
789 
790 	myserver->uidlMap.insert(make_pair(string(message), msgNum));
791 }
792 
793 
794 //////////////////////////////////////////////////////////////////////////////
795 
LoggedInTask(mail::callback * callback,mail::pop3 & myserverArg)796 mail::pop3::LoggedInTask::LoggedInTask(mail::callback *callback,
797 				       mail::pop3 &myserverArg)
798 	: Task(callback, myserverArg)
799 {
800 }
801 
~LoggedInTask()802 mail::pop3::LoggedInTask::~LoggedInTask()
803 {
804 	mail::callback *c=callbackPtr;
805 
806 	callbackPtr=NULL;
807 
808 	if (c)
809 		c->fail("An exception occurred while trying to initialize this task.");
810 }
811 
reportProgress(size_t bytesCompleted,size_t bytesEstimatedTotal,size_t messagesCompleted,size_t messagesEstimatedTotal)812 void mail::pop3::LoggedInTask::reportProgress(size_t bytesCompleted,
813 					      size_t bytesEstimatedTotal,
814 
815 					      size_t messagesCompleted,
816 					      size_t messagesEstimatedTotal)
817 {
818 	if (callbackPtr)
819 		callbackPtr->
820 			reportProgress(bytesCompleted, bytesEstimatedTotal,
821 				       messagesCompleted,
822 				       messagesEstimatedTotal);
823 }
824 
installedTask()825 void mail::pop3::LoggedInTask::installedTask()
826 {
827 	Task::installedTask();
828 
829 	if (myserver->getfd() >= 0) // Logged in, yippee!!
830 	{
831 		loggedIn();
832 		return;
833 	}
834 
835 	myserver->installTask(new LoginTask( *myserver, this));
836 
837 	if ((*myserver->tasks.begin()) != this)
838 		kill(getpid(), SIGABRT);
839 
840 	myserver->tasks.erase(myserver->tasks.begin()); // myself
841 
842 	(*myserver->tasks.begin())->installedTask();
843 	// Guaranteed something's there.
844 }
845 
success(string message)846 void mail::pop3::LoggedInTask::success(string message)
847 {
848 	myserver->installTask(this); // Try again
849 }
850 
fail(string message)851 void mail::pop3::LoggedInTask::fail(string message)
852 {
853 	mail::callback *c=callbackPtr;
854 
855 	callbackPtr=NULL;
856 
857 	delete this;
858 
859 	if (c)
860 		c->fail(message);
861 }
862 
863 /////////////////////////////////////////////////////////////////////////////
864 
865 class mail::pop3::CheckNewMailTask : public mail::pop3::LoggedInTask {
866 
867 public:
868 	bool forceSaveSnapshot;
869 
870 	class sortNewMail {
871 
872 		map<string, int> &umap;
873 	public:
874 		sortNewMail(map<string, int> &umapArg);
875 		~sortNewMail();
876 
877 		bool operator()(string a, string b);
878 	};
879 
880 
881 	CheckNewMailTask(mail::pop3 &serverArg, mail::callback &callbackArg);
882 	~CheckNewMailTask();
883 
884 	void loggedIn();
885 	void loginFailed(string errmsg);
886 
887 	void serverResponse(const char *message);
888 
889 	void done();
890 private:
891 	void (CheckNewMailTask::*currentHandler)(const char *status);
892 	int next2Download; // When subclass by pop3maildrop, next msg 2 get.
893 	bool willReconnectFlag;
894 
895 	bool willReconnect();
896 
897 
898 	void doNextDownload();
899 	mail::addMessage *downloadMsg;
900 
901 	void retrHandler(const char *);
902 	void retrBodyHandler(const char *);
903 	void doDeleteDownloaded();
904 	void deleHandler(const char *);
905 	void quitHandler(const char *);
906 
907 	size_t bytesCompleted;
908 	size_t bytesEstimatedTotal;
909 
910 	size_t messagesEstimatedTotal;
911 
912 };
913 
CheckNewMailTask(mail::pop3 & serverArg,mail::callback & callbackArg)914 mail::pop3::CheckNewMailTask::CheckNewMailTask(mail::pop3 &serverArg,
915 					       mail::callback &callbackArg)
916 	: LoggedInTask(&callbackArg, serverArg),
917 	  forceSaveSnapshot(false), willReconnectFlag(false)
918 {
919 }
920 
~CheckNewMailTask()921 mail::pop3::CheckNewMailTask::~CheckNewMailTask()
922 {
923 }
924 
serverResponse(const char * message)925 void mail::pop3::CheckNewMailTask::serverResponse(const char *message)
926 {
927 	if (currentHandler)
928 		(this->*currentHandler)(message);
929 }
930 
loggedIn()931 void mail::pop3::CheckNewMailTask::loggedIn()
932 {
933 	mail::callback *myCallback=callbackPtr;
934 
935 	callbackPtr=NULL;
936 
937 	mail::ptr<mail::pop3> serverPtr= myserver;
938 
939 	bool changedFolder=myserver->reconcileFolderIndex();
940 
941 	if (forceSaveSnapshot)
942 		changedFolder=true; // Explicit check new mail saves snapshot
943 
944 	//
945 	// Now, save a snapshot.
946 	//
947 	if (!serverPtr.isDestroyed() && serverPtr->folderCallback &&
948 	    changedFolder)
949 		serverPtr->folderCallback->saveSnapshot("POP3");
950 
951 	if (!serverPtr.isDestroyed())
952 	{
953 		if (serverPtr->ispop3maildrop())
954 		{
955 			serverPtr->pop3maildropreset();
956 
957 			callbackPtr=myCallback;
958 
959 			next2Download=1;
960 			messagesEstimatedTotal=myserver->listMap.size();
961 			doNextDownload();
962 			return;
963 		}
964 		done();
965 	}
966 
967 	if (myCallback)
968 		myCallback->success("OK");
969 }
970 
loginFailed(string errmsg)971 void mail::pop3::CheckNewMailTask::loginFailed(string errmsg)
972 {
973 	mail::callback *c=callbackPtr;
974 
975 	callbackPtr=NULL;
976 
977 	done();
978 
979 	if (c)
980 		c->fail(errmsg);
981 }
982 
983 mail::pop3::CheckNewMailTask::sortNewMail
sortNewMail(map<string,int> & umapArg)984 ::sortNewMail(map<string, int> &umapArg)
985 	: umap(umapArg)
986 {
987 }
988 
~sortNewMail()989 mail::pop3::CheckNewMailTask::sortNewMail::~sortNewMail()
990 {
991 }
992 
operator()993 bool mail::pop3::CheckNewMailTask::sortNewMail::operator()(string a,
994 							   string b)
995 {
996 	map<string, int>::iterator ap, bp;
997 
998 	ap=umap.find(a);
999 	bp=umap.find(b);
1000 
1001 	return ((ap == umap.end() ? 0:ap->second) <
1002 		(bp == umap.end() ? 0:bp->second));
1003 }
1004 
1005 
doNextDownload()1006 void mail::pop3::CheckNewMailTask::doNextDownload()
1007 {
1008 	std::map<int, unsigned long>::iterator p=
1009 		myserver->listMap.find(next2Download);
1010 
1011 	bytesCompleted=0;
1012 	bytesEstimatedTotal=0;
1013 
1014 	if (next2Download > 200 ||
1015 	    // Grab max 200 msgs at a time to prevent out of file descriptors
1016 	    p == myserver->listMap.end())
1017 	{
1018 		string errmsg=myserver->commitDownloadedMsgs();
1019 
1020 		if (errmsg.size() > 0)
1021 		{
1022 			mail::callback *c=callbackPtr;
1023 			callbackPtr=NULL;
1024 			done();
1025 			if (c)
1026 				c->fail(errmsg.c_str());
1027 			return;
1028 		}
1029 		if (callbackPtr)
1030 			callbackPtr->reportProgress(0, 0,
1031 						    messagesEstimatedTotal,
1032 						    messagesEstimatedTotal);
1033 		doDeleteDownloaded();
1034 		return;
1035 	}
1036 
1037 
1038 	downloadMsg=myserver->newDownloadMsg();
1039 
1040 	if (!downloadMsg)
1041 	{
1042 		mail::callback *c=callbackPtr;
1043 		callbackPtr=NULL;
1044 		done();
1045 		if (c)
1046 			c->fail(strerror(errno));
1047 		return;
1048 	}
1049 	downloadMsg->messageInfo.unread=true; /* New message */
1050 
1051 	currentHandler=&CheckNewMailTask::retrHandler;
1052 
1053 	ostringstream o;
1054 
1055 	bytesCompleted=0;
1056 
1057 	if ((size_t)next2Download > messagesEstimatedTotal)
1058 		messagesEstimatedTotal=next2Download;
1059 
1060 	map<int, unsigned long>::iterator s=
1061 		myserver->listMap.find(next2Download);
1062 
1063 	bytesEstimatedTotal=s == myserver->listMap.end() ? 0:
1064 		s->second;
1065 
1066 	if (callbackPtr)
1067 		callbackPtr->reportProgress(0,
1068 					    bytesEstimatedTotal,
1069 					    next2Download-1,
1070 					    messagesEstimatedTotal);
1071 	o << "RETR " << next2Download << "\r\n";
1072 
1073 	myserver->socketWrite(o.str());
1074 	++next2Download;
1075 }
1076 
1077 
retrHandler(const char * message)1078 void mail::pop3::CheckNewMailTask::retrHandler(const char *message)
1079 {
1080 	if (*message != '+')
1081 	{
1082 		mail::callback *c=callbackPtr;
1083 		callbackPtr=NULL;
1084 		done();
1085 		if (c)
1086 			c->fail(message);
1087 		return;
1088 	}
1089 
1090 	currentHandler=&CheckNewMailTask::retrBodyHandler;
1091 }
1092 
retrBodyHandler(const char * message)1093 void mail::pop3::CheckNewMailTask::retrBodyHandler(const char *message)
1094 {
1095 	if (strcmp(message, "."))
1096 	{
1097 		if (*message == '.')
1098 			++message;
1099 
1100 		bytesCompleted += strlen(message)+2;
1101 		if (bytesCompleted > bytesEstimatedTotal)
1102 			bytesCompleted=bytesEstimatedTotal;
1103 		if (callbackPtr)
1104 			callbackPtr->reportProgress(bytesCompleted,
1105 						    bytesEstimatedTotal,
1106 						    next2Download-1,
1107 						    messagesEstimatedTotal);
1108 		downloadMsg->saveMessageContents(string(message)+"\n");
1109 		return;
1110 	}
1111 
1112 	doNextDownload();
1113 }
1114 
doDeleteDownloaded()1115 void mail::pop3::CheckNewMailTask::doDeleteDownloaded()
1116 {
1117 	if (--next2Download <= 0)
1118 	{
1119 		willReconnectFlag=true;
1120 		currentHandler=&CheckNewMailTask::quitHandler;
1121 		myserver->socketWrite("QUIT\r\n");
1122 		return;
1123 	}
1124 
1125 	currentHandler=&CheckNewMailTask::deleHandler;
1126 
1127 	ostringstream o;
1128 
1129 	o << "DELE " << next2Download << "\r\n";
1130 	myserver->socketWrite(o.str());
1131 }
1132 
deleHandler(const char * message)1133 void mail::pop3::CheckNewMailTask::deleHandler(const char *message)
1134 {
1135 	doDeleteDownloaded();
1136 }
1137 
quitHandler(const char * message)1138 void mail::pop3::CheckNewMailTask::quitHandler(const char *message)
1139 {
1140 	if (myserver->socketEndEncryption())
1141 		return; // Catch me on the rebound.
1142 
1143 	mail::callback *c=callbackPtr;
1144 
1145 	callbackPtr=NULL;
1146 
1147 	myserver->socketDisconnect();
1148 	myserver->orderlyShutdown=true;
1149 	myserver->disconnect("");
1150 
1151 	if (c)
1152 		c->success("Ok");
1153 }
1154 
1155 
willReconnect()1156 bool mail::pop3::CheckNewMailTask::willReconnect()
1157 {
1158 	return willReconnectFlag;
1159 }
1160 
done()1161 void mail::pop3::CheckNewMailTask::done()
1162 {
1163 	if (!willReconnectFlag)
1164 	{
1165 		LoggedInTask::done();
1166 		return;
1167 	}
1168 
1169 	mail::callback *c=callbackPtr;
1170 	callbackPtr=NULL;
1171 	LoggedInTask::done();
1172 	if (c)
1173 		c->success("Ok.");
1174 }
1175 
1176 /////////////////////////////////////////////////////////////////////////////
1177 
1178 class mail::pop3::LogoutTask : public mail::pop3::Task {
1179 
1180 	void fail(const char *reason);
1181 
1182 	void serverResponse(const char *message);
1183 
1184 	void installedTask();
1185 	bool isLogoutTask();
1186 
1187 	mail::callback *disconnectCallback;
1188 
1189 	bool willReconnectFlag;
1190 
1191 public:
1192 	LogoutTask(mail::pop3 &server, mail::callback *callbackArg,
1193 		   bool willReconnectFlagArg=false);
1194 	~LogoutTask();
1195 
1196 	bool willReconnect();
1197 };
1198 
LogoutTask(mail::pop3 & server,mail::callback * callbackArg,bool willReconnectFlagArg)1199 mail::pop3::LogoutTask::LogoutTask(mail::pop3 &server,
1200 				   mail::callback *callbackArg,
1201 				   bool willReconnectFlagArg)
1202 	: Task(callbackArg, server),
1203 	  disconnectCallback(NULL),
1204 	  willReconnectFlag(willReconnectFlagArg)
1205 {
1206 }
1207 
willReconnect()1208 bool mail::pop3::LogoutTask::willReconnect()
1209 {
1210 	return willReconnectFlag;
1211 }
1212 
~LogoutTask()1213 mail::pop3::LogoutTask::~LogoutTask()
1214 {
1215 	myserver->socketDisconnect();
1216 	if (disconnectCallback)
1217 		disconnectCallback->success("OK");
1218 }
1219 
isLogoutTask()1220 bool mail::pop3::LogoutTask::isLogoutTask()
1221 {
1222 	return true;
1223 }
1224 
installedTask()1225 void mail::pop3::LogoutTask::installedTask()
1226 {
1227 	Task::installedTask();
1228 
1229 	if (myserver->getfd() < 0)
1230 	{
1231 		serverResponse("OK"); // Already logged out.
1232 		return;
1233 	}
1234 
1235 	myserver->socketWrite("QUIT\r\n");
1236 }
1237 
serverResponse(const char * message)1238 void mail::pop3::LogoutTask::serverResponse(const char *message)
1239 {
1240 	const char *p=strchr(message, ' ');
1241 
1242 	if (p)
1243 		message=p+1;
1244 
1245 	mail::callback *c=callbackPtr;
1246 
1247 	callbackPtr=NULL;
1248 
1249 	disconnectCallback=c;
1250 
1251 	if (myserver->socketEndEncryption())
1252 		return; // Catch me on the rebound.
1253 
1254 	myserver->socketDisconnect();
1255 	myserver->orderlyShutdown=true;
1256 	myserver->disconnect("");
1257 }
1258 
1259 /////////////////////////////////////////////////////////////////////////////
1260 
1261 class mail::pop3::ForceCheckNewMailTask : public mail::callback {
1262 
1263 	mail::callback *realCallback;
1264 	mail::pop3 *server;
1265 
1266 	void reportProgress(size_t bytesCompleted,
1267 			    size_t bytesEstimatedTotal,
1268 
1269 			    size_t messagesCompleted,
1270 			    size_t messagesEstimatedTotal);
1271 
1272 public:
1273 	ForceCheckNewMailTask(mail::callback *realCallbackArg,
1274 			      mail::pop3 *serverArg);
1275 	~ForceCheckNewMailTask();
1276 
1277 	void success(string);
1278 	void fail(string);
1279 };
1280 
1281 mail::pop3::ForceCheckNewMailTask
ForceCheckNewMailTask(mail::callback * realCallbackArg,mail::pop3 * serverArg)1282 ::ForceCheckNewMailTask(mail::callback *realCallbackArg,
1283 			mail::pop3 *serverArg)
1284 	: realCallback(realCallbackArg), server(serverArg)
1285 {
1286 }
1287 
~ForceCheckNewMailTask()1288 mail::pop3::ForceCheckNewMailTask::~ForceCheckNewMailTask()
1289 {
1290 }
1291 
1292 void mail::pop3::ForceCheckNewMailTask
reportProgress(size_t bytesCompleted,size_t bytesEstimatedTotal,size_t messagesCompleted,size_t messagesEstimatedTotal)1293 ::reportProgress(size_t bytesCompleted,
1294 		 size_t bytesEstimatedTotal,
1295 
1296 		 size_t messagesCompleted,
1297 		 size_t messagesEstimatedTotal)
1298 {
1299 	if (realCallback)
1300 		realCallback->reportProgress(bytesCompleted,
1301 					     bytesEstimatedTotal,
1302 					     messagesCompleted,
1303 					     messagesEstimatedTotal);
1304 }
1305 
1306 //
1307 // checkNewMail installed a LogoutTask.  LogoutTask's destructor ends up
1308 // calling the logout callback function, which is me.
1309 // LogoutTask is destroyed by LogoutTask::Task::done()'s delete this.
1310 //
1311 // Install the CheckNewMailTask at the beginning of the task queue, so
1312 // Task::done() ends up calling CheckNewMailTask's installedTask.
1313 
success(string message)1314 void mail::pop3::ForceCheckNewMailTask::success(string message)
1315 {
1316 	CheckNewMailTask *cn=new CheckNewMailTask(*server, *realCallback);
1317 
1318 	if (!cn)
1319 		realCallback->fail(strerror(errno));
1320 
1321 	try {
1322 		cn->forceSaveSnapshot=true;
1323 		server->lastTaskCompleted=0;
1324 		server->tasks.insert(server->tasks.begin(), cn);
1325 		delete this;
1326 	} catch (...) {
1327 		delete cn;
1328 		realCallback->fail(message);
1329 		delete this;
1330 	}
1331 }
1332 
fail(string errmsg)1333 void mail::pop3::ForceCheckNewMailTask::fail(string errmsg)
1334 {
1335 	success(errmsg);
1336 }
1337 
1338 /////////////////////////////////////////////////////////////////////////////
1339 //
1340 // EXPUNGE - sent all the DELEs, then QUIT out of the folder, then reopen it.
1341 
1342 class mail::pop3::UpdateTask : public mail::pop3::LoggedInTask {
1343 
1344 	size_t updateIndex;
1345 
1346 #if 0
1347 	class LogoutCallback : public mail::callback {
1348 		mail::ptr<mail::pop3> myserver;
1349 		mail::callback *callback;
1350 	public:
1351 		LogoutCallback(mail::pop3 *serverArg,
1352 			       mail::callback *callbackArg);
1353 		~LogoutCallback();
1354 
1355 		void success(string);
1356 		void fail(string);
1357 	};
1358 #endif
1359 
1360 public:
1361 	UpdateTask(mail::pop3 &serverArg, mail::callback &callbackArg);
1362 	~UpdateTask();
1363 
1364 	void loggedIn();
1365 	void loginFailed(string errmsg);
1366 
1367 	void serverResponse(const char *message);
1368 };
1369 
UpdateTask(mail::pop3 & serverArg,mail::callback & callbackArg)1370 mail::pop3::UpdateTask::UpdateTask(mail::pop3 &serverArg,
1371 				   mail::callback &callbackArg)
1372 	: LoggedInTask(&callbackArg, serverArg),
1373 	  updateIndex(0)
1374 {
1375 }
1376 
~UpdateTask()1377 mail::pop3::UpdateTask::~UpdateTask()
1378 {
1379 }
1380 
loggedIn()1381 void mail::pop3::UpdateTask::loggedIn()
1382 {
1383 	serverResponse("+OK");
1384 }
1385 
loginFailed(string errmsg)1386 void mail::pop3::UpdateTask::loginFailed(string errmsg)
1387 {
1388 	mail::callback *c=callbackPtr;
1389 
1390 	callbackPtr=NULL;
1391 
1392 	done();
1393 
1394 	if (c)
1395 		c->fail(errmsg);
1396 }
1397 
serverResponse(const char * message)1398 void mail::pop3::UpdateTask::serverResponse(const char *message)
1399 {
1400 	if (*message != '+')
1401 	{
1402 		loginFailed(message);
1403 		return;
1404 	}
1405 
1406 	// Check for deleted messages.
1407 
1408 	while (updateIndex < myserver->currentFolderIndex.size())
1409 	{
1410 		string uid;
1411 
1412 		if (myserver->currentFolderIndex[updateIndex].deleted &&
1413 		    myserver->uidlMap.count(uid=myserver->currentFolderIndex
1414 					   [updateIndex].uid) > 0)
1415 		{
1416 			int msgNum=myserver->uidlMap.find(uid)->second;
1417 
1418 			string buffer;
1419 			{
1420 				ostringstream o;
1421 
1422 				o << msgNum;
1423 				buffer=o.str();
1424 			}
1425 
1426 			++updateIndex;
1427 			myserver->socketWrite(string("DELE ") + buffer
1428 					     + "\r\n");
1429 			return;
1430 		}
1431 		++updateIndex;
1432 	}
1433 
1434 	// Do the usual logout logic, which will take care of this.
1435 
1436 	ForceCheckNewMailTask *l=
1437 		new ForceCheckNewMailTask(callbackPtr, myserver);
1438 
1439 	if (!l)
1440 	{
1441 		loginFailed("Out of memory.");
1442 		return;
1443 	}
1444 
1445 	callbackPtr=NULL;
1446 
1447 	if ( (*myserver->tasks.begin()) != this)
1448 		kill(getpid(), SIGABRT);
1449 
1450 	try {
1451 		LogoutTask *lo=new LogoutTask( *myserver, l, true);
1452 
1453 		if (!lo)
1454 			strerror(errno);
1455 
1456 		try {
1457 			myserver->tasks.insert(++myserver->tasks.begin(),
1458 					      lo);
1459 		} catch (...) {
1460 			delete lo;
1461 			LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
1462 		}
1463 	} catch (...) {
1464 		delete l;
1465 		LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
1466 	}
1467 	done();
1468 }
1469 
1470 #if 0
1471 mail::pop3::UpdateTask::LogoutCallback::LogoutCallback(mail::pop3 *serverArg,
1472 						       mail::callback
1473 						       *callbackArg)
1474 	: myserver(serverArg), callback(callbackArg)
1475 {
1476 }
1477 
1478 mail::pop3::UpdateTask::LogoutCallback::~LogoutCallback()
1479 {
1480 }
1481 
1482 void mail::pop3::UpdateTask::LogoutCallback::success(string message)
1483 {
1484 	mail::callback *c=callback;
1485 	mail::pop3 *s=myserver;
1486 
1487 	callback=NULL;
1488 	delete this;
1489 
1490 	if (!s)
1491 		c->success(message);
1492 	else try {
1493 		s->checkNewMail(*c); // Immediately check for new mail.
1494 	} catch (...) {
1495 		c->success(message);
1496 	}
1497 }
1498 
1499 void mail::pop3::UpdateTask::LogoutCallback::fail(string message)
1500 {
1501 	success(message); // I'm an optimist.
1502 }
1503 #endif
1504 
1505 /////////////////////////////////////////////////////////////////////////////
1506 //
1507 // Generic message read.
1508 
1509 class mail::pop3::ReadMessageTask : public mail::pop3::LoggedInTask {
1510 
1511 	string uid;
1512 	size_t messageNum;
1513 	bool peek;
1514 	mail::readMode readType;
1515 
1516 	bool seenHeaders;
1517 	string currentHeader;
1518 	bool expectingStatus;
1519 
1520 	size_t expected_cnt;
1521 	size_t received_cnt;
1522 
1523 	mail::callback::message *msgCallback;
1524 
1525 public:
1526 	ReadMessageTask(mail::pop3 &serverArg,
1527 			mail::callback::message &callbackArg,
1528 			string uidArg,
1529 			size_t messageNumArg,
1530 			bool peekArg,
1531 			mail::readMode readTypeArg);
1532 	~ReadMessageTask();
1533 
1534 	void loggedIn();
1535 	void loginFailed(string errmsg);
1536 
1537 	void serverResponse(const char *message);
1538 };
1539 
ReadMessageTask(mail::pop3 & serverArg,mail::callback::message & callbackArg,string uidArg,size_t messageNumArg,bool peekArg,mail::readMode readTypeArg)1540 mail::pop3::ReadMessageTask::ReadMessageTask(mail::pop3 &serverArg,
1541 					     mail::callback::message
1542 					     &callbackArg,
1543 					     string uidArg,
1544 					     size_t messageNumArg,
1545 					     bool peekArg,
1546 					     mail::readMode readTypeArg)
1547 	: LoggedInTask(&callbackArg, serverArg),
1548 	  uid(uidArg),
1549 	  messageNum(messageNumArg),
1550 	  peek(peekArg),
1551 	  readType(readTypeArg),
1552 	  msgCallback(&callbackArg)
1553 {
1554 }
1555 
~ReadMessageTask()1556 mail::pop3::ReadMessageTask::~ReadMessageTask()
1557 {
1558 }
1559 
loggedIn()1560 void mail::pop3::ReadMessageTask::loggedIn()
1561 {
1562 	map<string, int>::iterator p=myserver->uidlMap.find(uid);
1563 
1564 	if (p == myserver->uidlMap.end())
1565 	{
1566 		loginFailed("Message was deleted by the POP3 server.");
1567 		return;
1568 	}
1569 
1570 	int msgNum=p->second;
1571 
1572 	seenHeaders=false;
1573 	currentHeader="";
1574 	expectingStatus=true;
1575 
1576 	if (!fixMessageNumber(myserver, uid, messageNum))
1577 	{
1578 		expectingStatus=false;
1579 		serverResponse(".");
1580 		return;
1581 	}
1582 
1583 	string buffer;
1584 
1585 	{
1586 		ostringstream o;
1587 
1588 		o << msgNum;
1589 		buffer=o.str();
1590 	}
1591 
1592 	string cmd;
1593 
1594 	expected_cnt=0;
1595 	received_cnt=0;
1596 
1597 	if (readType == mail::readContents ||
1598 	    readType == mail::readBoth)
1599 	{
1600 		cmd=string("RETR ") + buffer + "\r\n";
1601 
1602 		map<int, unsigned long>::iterator p
1603 			=myserver->listMap.find(msgNum);
1604 
1605 		if (p != myserver->listMap.end())
1606 			expected_cnt=p->second;
1607 	}
1608 	else
1609 		cmd=string("TOP ") + buffer + " 0\r\n";
1610 
1611 	myserver->socketWrite(cmd);
1612 }
1613 
loginFailed(string errmsg)1614 void mail::pop3::ReadMessageTask::loginFailed(string errmsg)
1615 {
1616 	mail::callback *c=callbackPtr;
1617 
1618 	callbackPtr=NULL;
1619 
1620 	done();
1621 
1622 	if (c)
1623 		c->fail(errmsg);
1624 }
1625 
serverResponse(const char * message)1626 void mail::pop3::ReadMessageTask::serverResponse(const char *message)
1627 {
1628 	if (expectingStatus)
1629 	{
1630 		expectingStatus=false;
1631 		if (*message != '+')
1632 			loginFailed(message);
1633 		return;
1634 	}
1635 
1636 	time(&defaultTimeout);
1637 	defaultTimeout += myserver->timeoutSetting;
1638 	// Some activity -- don't time out
1639 
1640 	if (strcmp(message, ".") == 0)
1641 	{
1642 		mail::callback::message *c=msgCallback;
1643 
1644 		mail::ptr<mail::pop3> ptr= myserver;
1645 
1646 		bool peekFlag=peek;
1647 
1648 		if (!fixMessageNumber(myserver, uid, messageNum))
1649 			peekFlag=true;
1650 
1651 		if (callbackPtr == NULL)
1652 			c=NULL;
1653 
1654 		callbackPtr=NULL;
1655 		msgCallback=NULL;
1656 
1657 		if (currentHeader.size() > 0)
1658 		{
1659 			if (c)
1660 				c->messageTextCallback(0,
1661 						       currentHeader + "\n");
1662 		}
1663 
1664 		size_t n=messageNum;
1665 
1666 		received_cnt=expected_cnt;
1667 		if (c)
1668 			c->reportProgress(expected_cnt, expected_cnt, 1, 1);
1669 
1670 		done();
1671 
1672 		if (c)
1673 			c->success("OK");
1674 
1675 		if (!peekFlag && !ptr.isDestroyed())
1676 		{
1677 			ptr->currentFolderIndex[n].unread=false;
1678 			if (ptr->folderCallback)
1679 				ptr->folderCallback
1680 					->messageChanged(n);
1681 
1682 			if (!ptr.isDestroyed() && ptr->folderCallback)
1683 				ptr->folderCallback->saveSnapshot("POP3");
1684 		}
1685 
1686 		return;
1687 	}
1688 
1689 	if (*message == '.')
1690 		++message;
1691 
1692 	received_cnt += strlen(message)+2;
1693 
1694 	if (callbackPtr && msgCallback)
1695 		msgCallback->reportProgress(received_cnt, expected_cnt, 0, 1);
1696 
1697 
1698 	if (readType == mail::readHeadersFolded)
1699 		// Reformat headers, one per line
1700 	{
1701 		if (!*message) // End of headers
1702 		{
1703 			string s=currentHeader;
1704 
1705 			currentHeader="";
1706 
1707 			if (s.size() > 0 && callbackPtr && msgCallback)
1708 				msgCallback->messageTextCallback(0, s + "\n");
1709 			return;
1710 		}
1711 
1712 		if (unicode_isspace((unsigned char)*message))
1713 		{
1714 			while (*message &&
1715 			       unicode_isspace((unsigned char)*message))
1716 				++message;
1717 
1718 			if (currentHeader.size() > 0)
1719 				currentHeader += " ";
1720 			currentHeader += message;
1721 			return;
1722 		}
1723 
1724 		string s=currentHeader;
1725 		currentHeader=message;
1726 
1727 		if (callbackPtr && msgCallback && s.size() > 0)
1728 			msgCallback->messageTextCallback(0, s + "\n");
1729 		return;
1730 	}
1731 
1732 	if (readType == mail::readContents && !seenHeaders)
1733 	{
1734 		if (!*message)
1735 			seenHeaders=true;
1736 		return; // Eat the headers
1737 	}
1738 
1739 	string line=message;
1740 
1741 	line += "\n";
1742 
1743 	if (callbackPtr && msgCallback)
1744 		msgCallback->messageTextCallback(0, line);
1745 }
1746 
1747 
1748 /////////////////////////////////////////////////////////////////////////////
1749 
1750 class mail::pop3::CacheMessageTask : public mail::callback::message {
1751 
1752 	mail::ptr<mail::pop3> myserver;
1753 
1754 	mail::callback *callback;
1755 
1756 	string uid;
1757 
1758 	int *fdptr;
1759 	struct rfc2045 **rfcptr;
1760 
1761 	FILE *tmpfilefp;
1762 	struct rfc2045 *rfc;
1763 
1764 	void reportProgress(size_t bytesCompleted,
1765 			    size_t bytesEstimatedTotal,
1766 
1767 			    size_t messagesCompleted,
1768 			    size_t messagesEstimatedTotal);
1769 
1770 public:
1771 	CacheMessageTask(mail::pop3 *myserverArg,
1772 			 mail::callback *callbackArg,
1773 			 string uidArg,
1774 			 int *fdArg,
1775 			 struct rfc2045 **rfcArg);
1776 	~CacheMessageTask();
1777 
1778 	bool init();
1779 
1780 	void success(string);
1781 	void fail(string);
1782 
1783 	void messageTextCallback(size_t n, string text);
1784 };
1785 
CacheMessageTask(mail::pop3 * myserverArg,mail::callback * callbackArg,string uidArg,int * fdArg,struct rfc2045 ** rfcArg)1786 mail::pop3::CacheMessageTask::CacheMessageTask(mail::pop3 *myserverArg,
1787 					       mail::callback *callbackArg,
1788 					       string uidArg,
1789 					       int *fdArg,
1790 					       struct rfc2045 **rfcArg)
1791 	: myserver(myserverArg),
1792 	  callback(callbackArg),
1793 	  uid(uidArg),
1794 	  fdptr(fdArg),
1795 	  rfcptr(rfcArg),
1796 	  tmpfilefp(NULL),
1797 	  rfc(NULL)
1798 {
1799 }
1800 
~CacheMessageTask()1801 mail::pop3::CacheMessageTask::~CacheMessageTask()
1802 {
1803 	if (tmpfilefp)
1804 		fclose(tmpfilefp);
1805 
1806 	if (rfc)
1807 		rfc2045_free(rfc);
1808 }
1809 
1810 void mail::pop3
reportProgress(size_t bytesCompleted,size_t bytesEstimatedTotal,size_t messagesCompleted,size_t messagesEstimatedTotal)1811 ::CacheMessageTask::reportProgress(size_t bytesCompleted,
1812 				   size_t bytesEstimatedTotal,
1813 
1814 				   size_t messagesCompleted,
1815 				   size_t messagesEstimatedTotal)
1816 {
1817 	if (callback)
1818 		callback->reportProgress(bytesCompleted,
1819 					 bytesEstimatedTotal,
1820 					 messagesCompleted,
1821 					 messagesEstimatedTotal);
1822 }
1823 
success(string message)1824 void mail::pop3::CacheMessageTask::success(string message)
1825 {
1826 	if (myserver.isDestroyed())
1827 	{
1828 		fail("POP3 server connection aborted.");
1829 		     return;
1830 	}
1831 
1832 	if (fflush(tmpfilefp) || ferror(tmpfilefp))
1833 	{
1834 		fail(strerror(errno));
1835 		return;
1836 	}
1837 
1838 	if (myserver->genericTmpFd >= 0)
1839 		close(myserver->genericTmpFd);
1840 
1841 	rfc2045_parse_partial(rfc);
1842 
1843 	if (myserver->genericTmpRfcp)
1844 	{
1845 		rfc2045_free(myserver->genericTmpRfcp);
1846 		myserver->genericTmpRfcp=NULL;
1847 	}
1848 
1849 	myserver->cachedUid=uid;
1850 
1851 	if ((myserver->genericTmpFd=dup(fileno(tmpfilefp))) < 0)
1852 	{
1853 		fail(strerror(errno));
1854 		return;
1855 	}
1856 
1857 	fcntl(myserver->genericTmpFd, F_SETFD, FD_CLOEXEC);
1858 
1859 	if (fdptr)
1860 		*fdptr=myserver->genericTmpFd;
1861 
1862 	myserver->genericTmpRfcp=rfc;
1863 
1864 	if (rfcptr)
1865 		*rfcptr=rfc;
1866 
1867 	rfc=NULL;
1868 
1869 	mail::callback *c=callback;
1870 
1871 	callback=NULL;
1872 
1873 	delete this;
1874 	c->success(message);
1875 }
1876 
fail(string message)1877 void mail::pop3::CacheMessageTask::fail(string message)
1878 {
1879 	mail::callback *c=callback;
1880 
1881 	callback=NULL;
1882 
1883 	delete this;
1884 	c->fail(message);
1885 }
1886 
init()1887 bool mail::pop3::CacheMessageTask::init()
1888 {
1889 	if ((tmpfilefp=tmpfile()) == NULL ||
1890 	    (rfc=rfc2045_alloc()) == NULL)
1891 	{
1892 		fail(strerror(errno));
1893 		return false;
1894 	}
1895 
1896 	return true;
1897 }
1898 
messageTextCallback(size_t n,string text)1899 void mail::pop3::CacheMessageTask::messageTextCallback(size_t n, string text)
1900 {
1901 	if (fwrite(text.c_str(), text.size(), 1, tmpfilefp) != 1)
1902 		; // Ignore gcc warning
1903 	rfc2045_parse(rfc, text.c_str(), text.size());
1904 }
1905 
1906 
1907 /////////////////////////////////////////////////////////////////////////////
1908 
1909 #if 0
1910 mail::pop3::AutologoutCallback::AutologoutCallback()
1911 {
1912 }
1913 
1914 mail::pop3::AutologoutCallback::~AutologoutCallback()
1915 {
1916 }
1917 
1918 void mail::pop3::AutologoutCallback::success(string message)
1919 {
1920 	delete this;
1921 }
1922 
1923 void mail::pop3::AutologoutCallback::fail(string message)
1924 {
1925 	delete this;
1926 }
1927 #endif
1928 
1929 
1930 /////////////////////////////////////////////////////////////////////////////
1931 //
1932 // A NOOP msg - keep the server from timing out.
1933 
1934 class mail::pop3::NoopTask : public mail::pop3::LoggedInTask {
1935 public:
1936 	NoopTask(mail::pop3 &myseverArg);
1937 	~NoopTask();
1938 	void serverResponse(const char *message);
1939 	void loggedIn();
1940 	void loginFailed(string errmsg);
1941 };
1942 
NoopTask(mail::pop3 & serverArg)1943 mail::pop3::NoopTask::NoopTask(mail::pop3 &serverArg)
1944 	: LoggedInTask(NULL, serverArg)
1945 {
1946 }
1947 
~NoopTask()1948 mail::pop3::NoopTask::~NoopTask()
1949 {
1950 }
1951 
loginFailed(string errmsg)1952 void mail::pop3::NoopTask::loginFailed(string errmsg)
1953 {
1954 }
1955 
loggedIn()1956 void mail::pop3::NoopTask::loggedIn()
1957 {
1958 	myserver->socketWrite("NOOP\r\n");
1959 }
1960 
serverResponse(const char * message)1961 void mail::pop3::NoopTask::serverResponse(const char *message)
1962 {
1963 	done();
1964 }
1965 
1966 
1967 /////////////////////////////////////////////////////////////////////////////
1968 
1969 
pop3MessageInfo()1970 mail::pop3::pop3MessageInfo::pop3MessageInfo()
1971 {
1972 }
1973 
~pop3MessageInfo()1974 mail::pop3::pop3MessageInfo::~pop3MessageInfo()
1975 {
1976 }
1977 
1978 /////////////////////////////////////////////////////////////////////////////
1979 
1980 
pop3(string url,string passwd,std::vector<std::string> & certificates,mail::loginCallback * callback_func,mail::callback & callback,mail::callback::disconnect & disconnectCallback)1981 mail::pop3::pop3(string url, string passwd,
1982 		 std::vector<std::string> &certificates,
1983 		 mail::loginCallback *callback_func,
1984 		 mail::callback &callback,
1985 		 mail::callback::disconnect &disconnectCallback)
1986 	: mail::fd(disconnectCallback, certificates),
1987 	  calledDisconnected(false),
1988 	  orderlyShutdown(false),
1989 	  lastTaskCompleted(0),
1990 	  inbox(this), folderCallback(NULL),
1991 	  genericTmpFd(-1), genericTmpRfcp(NULL)
1992 {
1993 	savedLoginInfo.callbackPtr=NULL;
1994 	savedLoginInfo.loginCallbackFunc=callback_func;
1995 
1996 	if (!loginUrlDecode(url, savedLoginInfo))
1997 	{
1998 		callback.fail("Invalid POP3 URL.");
1999 		return;
2000 	}
2001 
2002 	timeoutSetting=getTimeoutSetting(savedLoginInfo, "timeout", 60,
2003 					 30, 600);
2004 	noopSetting=getTimeoutSetting(savedLoginInfo, "noop", 60,
2005 				      5, 1800);
2006 
2007 	savedLoginInfo.use_ssl=savedLoginInfo.method == "pop3s";
2008 	if (passwd.size() > 0)
2009 		savedLoginInfo.pwd=passwd;
2010 
2011 	installTask(new CheckNewMailTask(*this, callback));
2012 }
2013 
resumed()2014 void mail::pop3::resumed()
2015 {
2016 	if (!tasks.empty())
2017 		(*tasks.begin())->resetTimeout();
2018 }
2019 
handler(vector<pollfd> & fds,int & ioTimeout)2020 void mail::pop3::handler(vector<pollfd> &fds, int &ioTimeout)
2021 {
2022 	int fd_save=getfd();
2023 
2024 	if (!tasks.empty())
2025 	{
2026 		int t=(*tasks.begin())->getTimeout() * 1000;
2027 
2028 		if (t < ioTimeout)
2029 			ioTimeout=t;
2030 	}
2031 
2032 	if (fd_save >= 0 && lastTaskCompleted)
2033 	{
2034 		time_t now;
2035 
2036 		time(&now);
2037 
2038 		if (now < lastTaskCompleted)
2039 			lastTaskCompleted=now;
2040 
2041 		if (now >= lastTaskCompleted + noopSetting)
2042 		{
2043 			lastTaskCompleted=0;
2044 
2045 			installTask(new NoopTask(*this));
2046 		}
2047 		else
2048 		{
2049 			if (ioTimeout >=
2050 			    (lastTaskCompleted + noopSetting - now) * 1000)
2051 			{
2052 				ioTimeout=(lastTaskCompleted + noopSetting
2053 					   - now) * 1000;
2054 			}
2055 		}
2056 	}
2057 
2058 	MONITOR(mail::account);
2059 
2060 	if (process(fds, ioTimeout) < 0)
2061 		return;
2062 
2063 	if (DESTROYED())
2064 		ioTimeout=0;
2065 
2066 	if (DESTROYED() || getfd() < 0)
2067 	{
2068 		size_t i;
2069 
2070 		for (i=fds.size(); i; )
2071 		{
2072 			--i;
2073 
2074 			if (fds[i].fd == fd_save)
2075 			{
2076 				fds.erase(fds.begin()+i, fds.begin()+i+1);
2077 				break;
2078 			}
2079 		}
2080 	}
2081 }
2082 
socketRead(const string & readbuffer)2083 int mail::pop3::socketRead(const string &readbuffer)
2084 {
2085 	mail::ptr<mail::pop3> myself=this;
2086 
2087 	size_t n=0;
2088 
2089 	string::const_iterator b=readbuffer.begin(), e=readbuffer.end(), c;
2090 
2091 	c=b;
2092 
2093 	while (b != e)
2094 	{
2095 		if (*b != '\n')
2096 		{
2097 			b++;
2098 			continue;
2099 		}
2100 
2101 		string l=string(c, b++);
2102 
2103 		n += b-c;
2104 		c=b;
2105 
2106 		size_t i;
2107 
2108 		while ((i=l.find('\r')) != std::string::npos)
2109 			l=l.substr(0, i) + l.substr(i+1);
2110 
2111 		if (!tasks.empty())
2112 			(*tasks.begin())->serverResponse(l.c_str());
2113 
2114 		break;
2115 	}
2116 
2117 	if (myself.isDestroyed())
2118 		return n;
2119 
2120 	if (n + 16000 < readbuffer.size())
2121 		n=readbuffer.size() - 16000;
2122 		// SANITY CHECK - DON'T LET HOSTILE SERVER DOS us
2123 
2124 	return n;
2125 }
2126 
disconnect(const char * reason)2127 void mail::pop3::disconnect(const char *reason)
2128 {
2129 	if (!tasks.empty())
2130 	{
2131 		if ( (*tasks.begin())->willReconnect())
2132 		{
2133 			socketDisconnect();
2134 			(*tasks.begin())->done();
2135 			return;
2136 		}
2137 	}
2138 
2139 	if (genericTmpFd >= 0)
2140 	{
2141 		close(genericTmpFd);
2142 		genericTmpFd= -1;
2143 	}
2144 
2145 	if (genericTmpRfcp != NULL)
2146 	{
2147 		rfc2045_free(genericTmpRfcp);
2148 		genericTmpRfcp=NULL;
2149 	}
2150 
2151 	string errmsg=reason ? reason:"";
2152 
2153 	if (getfd() >= 0)
2154 	{
2155 		string errmsg2=socketDisconnect();
2156 
2157 		if (errmsg2.size() > 0)
2158 			errmsg=errmsg2;
2159 
2160 		if (orderlyShutdown)
2161 			errmsg="";
2162 		else if (errmsg.size() == 0)
2163 			errmsg="Connection closed by remote host.";
2164 	}
2165 
2166 	while (!tasks.empty())
2167 		(*tasks.begin())
2168 			->disconnected(errmsg.size() > 0 ?
2169 				       errmsg.c_str()
2170 				       : "POP3 server connection closed.");
2171 
2172 	if (!calledDisconnected)
2173 	{
2174 		calledDisconnected=true;
2175 		disconnect_callback.disconnected("");
2176 	}
2177 
2178 }
2179 
installTask(Task * t)2180 void mail::pop3::installTask(Task *t)
2181 {
2182 	if (!t)
2183 		LIBMAIL_THROW("Out of memory.");
2184 
2185 	bool wasEmpty;
2186 
2187 	lastTaskCompleted=0;
2188 
2189 	try {
2190 		wasEmpty=tasks.empty();
2191 
2192 		tasks.insert(tasks.end(), t);
2193 	} catch (...) {
2194 		delete t;
2195 		LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
2196 	}
2197 
2198 	if (wasEmpty)
2199 		(*tasks.begin())->installedTask();
2200 }
2201 
~pop3()2202 mail::pop3::~pop3()
2203 {
2204 	disconnect("POP3 server connection aborted.");
2205 	currentFolderIndex.clear();
2206 }
2207 
logout(mail::callback & callback)2208 void mail::pop3::logout(mail::callback &callback)
2209 {
2210 	installTask(new LogoutTask(*this, &callback));
2211 }
2212 
checkNewMail(mail::callback & callback)2213 void mail::pop3::checkNewMail(mail::callback &callback)
2214 {
2215 	ForceCheckNewMailTask *reloginTask=
2216 		new ForceCheckNewMailTask(&callback, this);
2217 
2218 	if (!reloginTask)
2219 		callback.fail("Out of memory");
2220 
2221 	try {
2222 		installTask(new LogoutTask(*this, reloginTask, true));
2223 	} catch (...) {
2224 		delete reloginTask;
2225 
2226 		callback.fail("Out of memory.");
2227 	}
2228 }
2229 
hasCapability(string capability)2230 bool mail::pop3::hasCapability(string capability)
2231 {
2232 	if (capability == LIBMAIL_SINGLEFOLDER)
2233 		return true;
2234 
2235 	return capabilities.count(capability) > 0;
2236 }
2237 
getCapability(string capability)2238 string mail::pop3::getCapability(string capability)
2239 {
2240 	upper(capability);
2241 
2242 	if (capability == LIBMAIL_SERVERTYPE)
2243 	{
2244 		return "pop3";
2245 	}
2246 
2247 	map<string, string>::iterator p;
2248 
2249 	if (capability == LIBMAIL_SERVERDESCR)
2250 	{
2251 		p=capabilities.find("IMPLEMENTATION");
2252 
2253 		return (p == capabilities.end() ?
2254 			(ispop3maildrop() ? "POP3 maildrop":
2255 			 "POP3 account")
2256 			:
2257 			(ispop3maildrop() ? "POP3 maildrop - ":"POP3 - ")
2258 			+ p->second);
2259 	}
2260 
2261 	p=capabilities.find(capability);
2262 
2263 	if (p == capabilities.end())
2264 		return "";
2265 
2266 	return p->second;
2267 }
2268 
folderFromString(string string)2269 mail::folder *mail::pop3::folderFromString(string string)
2270 {
2271 	return inbox.clone();
2272 }
2273 
readTopLevelFolders(mail::callback::folderList & callback1,mail::callback & callback2)2274 void mail::pop3::readTopLevelFolders(mail::callback::folderList &callback1,
2275 				     mail::callback &callback2)
2276 {
2277 	vector<const mail::folder *> folders;
2278 
2279 	folders.push_back(&inbox);
2280 
2281 	callback1.success(folders);
2282 	callback2.success("OK");
2283 }
2284 
findFolder(string folder,mail::callback::folderList & callback1,mail::callback & callback2)2285 void mail::pop3::findFolder(string folder,
2286 			    mail::callback::folderList &callback1,
2287 			    mail::callback &callback2)
2288 {
2289 	return readTopLevelFolders(callback1, callback2);
2290 }
2291 
genericMessageRead(string uid,size_t messageNumber,bool peek,mail::readMode readType,mail::callback::message & callback)2292 void mail::pop3::genericMessageRead(string uid,
2293 				    size_t messageNumber,
2294 				    bool peek,
2295 				    mail::readMode readType,
2296 				    mail::callback::message &callback)
2297 {
2298 	if (fixMessageNumber(this, uid, messageNumber) &&
2299 	    genericCachedUid(uid))
2300 	{
2301 		// Optimum path
2302 
2303 		vector<size_t> vec;
2304 		vec.push_back(messageNumber);
2305 
2306 		readMessageContent(vec, peek, readType, callback);
2307 		return;
2308 	}
2309 
2310 	installTask(new ReadMessageTask(*this, callback,
2311 					uid,
2312 					messageNumber,
2313 					peek,
2314 					readType));
2315 }
2316 
genericMessageSize(string uid,size_t messageNumber,mail::callback::message & callback)2317 void mail::pop3::genericMessageSize(string uid,
2318 				    size_t messageNumber,
2319 				    mail::callback::message &callback)
2320 {
2321 	if (uidlMap.count(uid) > 0)
2322 	{
2323 		int messageNum=uidlMap.find(uid)->second;
2324 
2325 		if (listMap.count(messageNum) > 0 &&
2326 		    fixMessageNumber(this, uid, messageNumber))
2327 		{
2328 			callback.messageSizeCallback(messageNumber,
2329 						     listMap.find(messageNum)
2330 						     ->second);
2331 		}
2332 	}
2333 	callback.success("OK");
2334 }
2335 
readMessageAttributes(const vector<size_t> & messages,MessageAttributes attributes,mail::callback::message & callback)2336 void mail::pop3::readMessageAttributes(const vector<size_t> &messages,
2337 				       MessageAttributes attributes,
2338 				       mail::callback::message &callback)
2339 {
2340 	mail::generic::genericAttributes(this, this, messages,
2341 					 attributes,
2342 					 callback);
2343 }
2344 
genericGetMessageFd(string uid,size_t messageNumber,bool peek,int & fdRet,mail::callback & callback)2345 void mail::pop3::genericGetMessageFd(string uid,
2346 				     size_t messageNumber,
2347 				     bool peek,
2348 				     int &fdRet,
2349 				     mail::callback &callback)
2350 {
2351 	if (genericTmpFd >= 0 && cachedUid == uid)
2352 	{
2353 		fdRet=genericTmpFd;
2354 		callback.success("OK");
2355 		return;
2356 	}
2357 
2358 	if (uidlMap.count(uid) == 0)
2359 	{
2360 		callback.fail("Message removed from the POP3 server.");
2361 		return;
2362 	}
2363 
2364 	CacheMessageTask *t=
2365 		new CacheMessageTask(this, &callback, uid, &fdRet, NULL);
2366 
2367 	if (!t || !t->init())
2368 	{
2369 		if (t)
2370 			delete t;
2371 
2372 		callback.fail("Out of memory.");
2373 	}
2374 
2375 	try {
2376 		genericMessageRead(uid, messageNumber, peek,
2377 				   mail::readBoth, *t);
2378 	} catch (...) {
2379 		delete t;
2380 		LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
2381 	}
2382 }
2383 
2384 
genericGetMessageStruct(string uid,size_t messageNumber,struct rfc2045 * & structRet,mail::callback & callback)2385 void mail::pop3::genericGetMessageStruct(string uid,
2386 					 size_t messageNumber,
2387 					 struct rfc2045 *&structRet,
2388 					 mail::callback &callback)
2389 {
2390 	if (genericTmpRfcp && cachedUid == uid)
2391 	{
2392 		structRet=genericTmpRfcp;
2393 		callback.success("OK");
2394 		return;
2395 	}
2396 
2397 	if (uidlMap.count(uid) == 0)
2398 	{
2399 		callback.fail("Message removed from the POP3 server.");
2400 		return;
2401 	}
2402 
2403 	CacheMessageTask *t=
2404 		new CacheMessageTask(this, &callback, uid, NULL, &structRet);
2405 
2406 	if (!t || !t->init())
2407 	{
2408 		if (t)
2409 			delete t;
2410 
2411 		callback.fail("Out of memory.");
2412 	}
2413 
2414 	try {
2415 		genericMessageRead(uid, messageNumber, true,
2416 				   mail::readBoth, *t);
2417 	} catch (...) {
2418 		delete t;
2419 		LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
2420 	}
2421 
2422 }
2423 
genericCachedUid(string uid)2424 bool mail::pop3::genericCachedUid(string uid)
2425 {
2426 	return genericTmpRfcp && cachedUid == uid;
2427 }
2428 
readMessageContent(const vector<size_t> & messages,bool peek,enum mail::readMode readType,mail::callback::message & callback)2429 void mail::pop3::readMessageContent(const vector<size_t> &messages,
2430 				bool peek,
2431 				enum mail::readMode readType,
2432 				mail::callback::message &callback)
2433 {
2434 	genericReadMessageContent(this, this, messages, peek, readType,
2435 				  callback);
2436 }
2437 
readMessageContent(size_t messageNum,bool peek,const mail::mimestruct & msginfo,enum mail::readMode readType,mail::callback::message & callback)2438 void mail::pop3::readMessageContent(size_t messageNum,
2439 				    bool peek,
2440 				    const mail::mimestruct &msginfo,
2441 				    enum mail::readMode readType,
2442 				    mail::callback::message &callback)
2443 {
2444 	genericReadMessageContent(this, this, messageNum,
2445 				  peek,
2446 				  msginfo,
2447 				  readType,
2448 				  callback);
2449 }
2450 
readMessageContentDecoded(size_t messageNum,bool peek,const mail::mimestruct & msginfo,mail::callback::message & callback)2451 void mail::pop3::readMessageContentDecoded(size_t messageNum,
2452 					   bool peek,
2453 					   const mail::mimestruct &msginfo,
2454 					   mail::callback::message &callback)
2455 {
2456 	genericReadMessageContentDecoded(this, this, messageNum,
2457 					 peek, msginfo, callback);
2458 }
2459 
getFolderIndexSize()2460 size_t mail::pop3::getFolderIndexSize()
2461 {
2462 	return currentFolderIndex.size();
2463 }
2464 
getFolderIndexInfo(size_t n)2465 mail::messageInfo mail::pop3::getFolderIndexInfo(size_t n)
2466 {
2467 	if (n < currentFolderIndex.size())
2468 		return currentFolderIndex[n];
2469 
2470 	return mail::messageInfo();
2471 }
2472 
2473 
saveFolderIndexInfo(size_t messageNum,const mail::messageInfo & info,mail::callback & callback)2474 void mail::pop3::saveFolderIndexInfo(size_t messageNum,
2475 				     const mail::messageInfo &info,
2476 				     mail::callback &callback)
2477 {
2478 	MONITOR(mail::pop3);
2479 
2480 	if (messageNum < currentFolderIndex.size())
2481 	{
2482 
2483 #define DOFLAG(dummy1, field, dummy2) \
2484 		(currentFolderIndex[messageNum].field= info.field)
2485 
2486 		LIBMAIL_MSGFLAGS;
2487 
2488 #undef DOFLAG
2489 
2490 	}
2491 
2492 	callback.success("OK");
2493 
2494 	if (! DESTROYED() && messageNum < currentFolderIndex.size()
2495 	    && folderCallback)
2496 	{
2497 		folderCallback->messageChanged(messageNum);
2498 
2499 		if (!DESTROYED() && folderCallback)
2500 			folderCallback->saveSnapshot("POP3");
2501 	}
2502 
2503 }
2504 
genericMarkRead(size_t messageNumber)2505 void mail::pop3::genericMarkRead(size_t messageNumber)
2506 {
2507 	if (messageNumber < currentFolderIndex.size() &&
2508 	    currentFolderIndex[messageNumber].unread)
2509 	{
2510 		MONITOR(mail::pop3);
2511 
2512 		currentFolderIndex[messageNumber].unread=false;
2513 		if (folderCallback)
2514 			folderCallback->messageChanged(messageNumber);
2515 		if (! DESTROYED() && folderCallback)
2516 			folderCallback->saveSnapshot("POP3");
2517 
2518 	}
2519 }
2520 
updateFolderIndexFlags(const vector<size_t> & messages,bool doFlip,bool enableDisable,const mail::messageInfo & flags,mail::callback & callback)2521 void mail::pop3::updateFolderIndexFlags(const vector<size_t> &messages,
2522 					bool doFlip,
2523 					bool enableDisable,
2524 					const mail::messageInfo &flags,
2525 					mail::callback &callback)
2526 {
2527 	vector<size_t>::const_iterator b, e;
2528 
2529 	b=messages.begin();
2530 	e=messages.end();
2531 
2532 	size_t n=currentFolderIndex.size();
2533 
2534 	while (b != e)
2535 	{
2536 		size_t i= *b++;
2537 
2538 		if (i < n)
2539 		{
2540 #define DOFLAG(dummy, field, dummy2) \
2541 			if (flags.field) \
2542 			{ \
2543 				currentFolderIndex[i].field=\
2544 					doFlip ? !currentFolderIndex[i].field\
2545 					       : enableDisable; \
2546 			}
2547 
2548 			LIBMAIL_MSGFLAGS;
2549 #undef DOFLAG
2550 		}
2551 	}
2552 
2553 	b=messages.begin();
2554 	e=messages.end();
2555 
2556 	MONITOR(mail::pop3);
2557 
2558 	while (!DESTROYED() && b != e)
2559 	{
2560 		size_t i= *b++;
2561 
2562 		if (i < n && folderCallback)
2563 			folderCallback->messageChanged(i);
2564 	}
2565 
2566 	if (!DESTROYED())
2567 		folderCallback->saveSnapshot("POP3");
2568 
2569 	callback.success("OK");
2570 }
2571 
getFolderKeywordInfo(size_t n,set<string> & kwSet)2572 void mail::pop3::getFolderKeywordInfo(size_t n, set<string> &kwSet)
2573 {
2574 	kwSet.clear();
2575 
2576 	if (n < currentFolderIndex.size())
2577 		currentFolderIndex[n].keywords.getFlags(kwSet);
2578 }
2579 
updateKeywords(const std::vector<size_t> & messages,const std::set<std::string> & keywords,bool setOrChange,bool changeTo,callback & cb)2580 void mail::pop3::updateKeywords(const std::vector<size_t> &messages,
2581 				const std::set<std::string> &keywords,
2582 				bool setOrChange,
2583 				// false: set, true: see changeTo
2584 				bool changeTo,
2585 				callback &cb)
2586 {
2587 	MONITOR(mail::pop3);
2588 
2589 	genericUpdateKeywords(messages, keywords, setOrChange, changeTo,
2590 			      folderCallback, this, cb);
2591 
2592 	if (!DESTROYED())
2593 		folderCallback->saveSnapshot("POP3");
2594 }
2595 
genericProcessKeyword(size_t messageNumber,updateKeywordHelper & helper)2596 bool mail::pop3::genericProcessKeyword(size_t messageNumber,
2597 				       updateKeywordHelper &helper)
2598 {
2599 	if (messageNumber < currentFolderIndex.size())
2600 		helper.doUpdateKeyword(currentFolderIndex[messageNumber]
2601 				       .keywords, keywordHashtable);
2602 	return true;
2603 }
2604 
updateFolderIndexInfo(mail::callback & callback)2605 void mail::pop3::updateFolderIndexInfo(mail::callback &callback)
2606 {
2607 	installTask(new UpdateTask(*this, callback));
2608 }
2609 
removeMessages(const std::vector<size_t> & messages,callback & cb)2610 void mail::pop3::removeMessages(const std::vector<size_t> &messages,
2611 				callback &cb)
2612 {
2613 	genericRemoveMessages(this, messages, cb);
2614 }
2615 
copyMessagesTo(const vector<size_t> & messages,mail::folder * copyTo,mail::callback & callback)2616 void mail::pop3::copyMessagesTo(const vector<size_t> &messages,
2617 				mail::folder *copyTo,
2618 				mail::callback &callback)
2619 {
2620 	mail::copyMessages::copy(this, messages, copyTo, callback);
2621 }
2622 
searchMessages(const mail::searchParams & searchInfo,mail::searchCallback & callback)2623 void mail::pop3::searchMessages(const mail::searchParams &searchInfo,
2624 				mail::searchCallback &callback)
2625 {
2626 	switch (searchInfo.criteria) {
2627 	case searchParams::larger:
2628 	case searchParams::smaller:
2629 	case searchParams::body:
2630 	case searchParams::text:
2631 		callback.fail("Not implemented.");
2632 		return;
2633 	default:
2634 		break;
2635 	}
2636 	mail::searchMessages::search(callback, searchInfo, this);
2637 }
2638 
getParentFolder(callback::folderList & callback1,callback & callback2)2639 void mail::pop3Folder::getParentFolder(callback::folderList &callback1,
2640 				       callback &callback2) const
2641 {
2642 	vector<const mail::folder *> dummy;
2643 
2644 	dummy.push_back(this);
2645 
2646 	callback1.success(dummy);
2647 	callback2.success("OK");
2648 }
2649 
readFolderInfo(mail::callback::folderInfo & callback1,mail::callback & callback2)2650 void mail::pop3::readFolderInfo( mail::callback::folderInfo &callback1,
2651 				 mail::callback &callback2)
2652 {
2653 	callback1.messageCount=currentFolderIndex.size();
2654 	callback1.unreadCount=0;
2655 
2656 	size_t i=0;
2657 
2658 	for (i=0; i<callback1.messageCount; i++)
2659 		if (currentFolderIndex[i].unread)
2660 			++callback1.unreadCount;
2661 
2662 	callback1.success();
2663 	callback2.success("OK");
2664 }
2665 
open(mail::callback & openCallback,mail::callback::folder & folderCallbackArg,mail::snapshot * restoreSnapshot)2666 void mail::pop3::open(mail::callback &openCallback,
2667 		      mail::callback::folder &folderCallbackArg,
2668 		      mail::snapshot *restoreSnapshot)
2669 
2670 {
2671 
2672 	if (restoreSnapshot)
2673 	{
2674 		string snapshotId;
2675 		size_t nmessages;
2676 
2677 		folderCallback=NULL;
2678 
2679 		restoreSnapshot->getSnapshotInfo(snapshotId, nmessages);
2680 
2681 		if (snapshotId.size() > 0)
2682 		{
2683 			currentFolderIndex.clear();
2684 			currentFolderIndex.resize(nmessages);
2685 			restoreAborted=false;
2686 			restoreSnapshot->restoreSnapshot(*this);
2687 
2688 			if (!restoreAborted)
2689 			{
2690 				vector<pop3MessageInfo>::iterator b, e;
2691 
2692 				b=currentFolderIndex.begin();
2693 				e=currentFolderIndex.end();
2694 
2695 				// Sanity check:
2696 
2697 				while (b != e)
2698 				{
2699 					if ( b->uid.size() == 0)
2700 					{
2701 						restoreAborted=true;
2702 						break;
2703 					}
2704 					b++;
2705 				}
2706 			}
2707 
2708 			if (!restoreAborted)
2709 			{
2710 				folderCallback= &folderCallbackArg;
2711 				reconcileFolderIndex();
2712 				openCallback.success("OK");
2713 				return;
2714 			}
2715 
2716 			// Rebuild folder index.
2717 
2718 			currentFolderIndex.clear();
2719 			reconcileFolderIndex();
2720 		}
2721 	}
2722 
2723 	folderCallback= &folderCallbackArg;
2724 	openCallback.success("OK");
2725 }
2726 
restoreIndex(size_t msgNum,const mail::messageInfo & info)2727 void mail::pop3::restoreIndex(size_t msgNum,
2728 			      const mail::messageInfo &info)
2729 {
2730 	if (msgNum < currentFolderIndex.size())
2731 		(static_cast<mail::messageInfo &>
2732 		 (currentFolderIndex[msgNum]))=info;
2733 }
2734 
restoreKeywords(size_t msgNum,const std::set<std::string> & kwSet)2735 void mail::pop3::restoreKeywords(size_t msgNum,
2736 				 const std::set<std::string> &kwSet)
2737 {
2738 	if (msgNum < currentFolderIndex.size())
2739 		currentFolderIndex[msgNum].keywords
2740 			.setFlags(keywordHashtable, kwSet);
2741 }
2742 
abortRestore()2743 void mail::pop3::abortRestore()
2744 {
2745 	restoreAborted=true;
2746 }
2747 
2748 
2749 
2750 //
2751 // Reconcile the virtual folder index to reflect what's actually on the
2752 // server.
2753 //
2754 
reconcileFolderIndex()2755 bool mail::pop3::reconcileFolderIndex()
2756 {
2757 	set<size_t> messagesSeen;
2758 
2759 	map<string, int>::iterator ub, ue;
2760 
2761 	size_t i;
2762 
2763 	bool needSnapshot=false;
2764 
2765 	// Make a list of all msgs that exist (and don't exist any more)
2766 
2767 	mail::expungeList removedList;
2768 
2769 	for (i=currentFolderIndex.size(); i; )
2770 	{
2771 		string uid=currentFolderIndex[--i].uid;
2772 
2773 		if (uidlMap.count( uid ) == 0)
2774 		{
2775 			removedList << i;
2776 			needSnapshot=true;
2777 		}
2778 		else
2779 			messagesSeen.insert(uidlMap.find(uid)->second);
2780 	}
2781 
2782 	// Send expunge notices
2783 
2784 	MONITOR(mail::pop3);
2785 
2786 	mail::expungeList::iterator expungeIterator;
2787 
2788 	for (expungeIterator=removedList.end();
2789 	     expungeIterator != removedList.begin(); )
2790 	{
2791 		--expungeIterator;
2792 		currentFolderIndex.erase(currentFolderIndex.begin() +
2793 					 expungeIterator->first,
2794 					 currentFolderIndex.begin() +
2795 					 expungeIterator->second+1);
2796 	}
2797 
2798 	if (folderCallback)
2799 		removedList >> folderCallback;
2800 
2801 	// Now check for new messages.
2802 
2803 	if (!DESTROYED())
2804 	{
2805 		vector<string> newUids;
2806 
2807 		ub=uidlMap.begin();
2808 		ue=uidlMap.end();
2809 
2810 		// sortNewMail() will end up sorting newUids chronologically.
2811 
2812 		while (ub != ue)
2813 		{
2814 			if (messagesSeen.count(ub->second) == 0)
2815 				newUids.push_back(ub->first);
2816 			ub++;
2817 		}
2818 		sort(newUids.begin(), newUids.end(),
2819 		     CheckNewMailTask::sortNewMail(uidlMap));
2820 
2821 		vector<string>::iterator b, e;
2822 
2823 		b=newUids.begin(), e=newUids.end();
2824 
2825 		while (b != e)
2826 		{
2827 			pop3MessageInfo newInfo;
2828 
2829 			// We'll have to assume that all msgs are unread.
2830 
2831 			newInfo.unread=true;
2832 			newInfo.uid= *b++;
2833 			currentFolderIndex.push_back(newInfo);
2834 		}
2835 
2836 		if (newUids.size() > 0 && folderCallback)
2837 			// Note folderCallback is NULL on account open!
2838 		{
2839 			needSnapshot=true;
2840 			folderCallback->newMessages();
2841 		}
2842 	}
2843 	return needSnapshot;
2844 }
2845 
2846 //////////////////////
2847 //
2848 // Stubs, overridden by mail::pop3maildrop::pop3acct
2849 
ispop3maildrop()2850 bool mail::pop3::ispop3maildrop()
2851 {
2852 	return false;
2853 }
2854 
pop3maildropreset()2855 void mail::pop3::pop3maildropreset()
2856 {
2857 }
2858 
newDownloadMsg()2859 mail::addMessage *mail::pop3::newDownloadMsg()
2860 {
2861 	return NULL;
2862 }
2863 
commitDownloadedMsgs()2864 string mail::pop3::commitDownloadedMsgs()
2865 {
2866 	return "";
2867 }
2868