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