1 /*
2 ** Copyright 2002-2011, Double Precision Inc.
3 **
4 ** See COPYING for distribution information.
5 */
6 #include "libmail_config.h"
7 #include "misc.H"
8 #include "smtp.H"
9 #include "driver.H"
10 #include "search.H"
11 #include "imaphmac.H"
12 #include "base64.H"
13 #include "smtpfolder.H"
14 #include "tcpd/spipe.h"
15 #include <courier-unicode.h>
16 #include "libcouriertls.h"
17
18 #include <errno.h>
19
20 #include <sstream>
21 #include <iomanip>
22
23 #include <sys/types.h>
24 #include <signal.h>
25 #if HAVE_SYS_WAIT_H
26 #include <sys/wait.h>
27 #endif
28 #ifndef WEXITSTATUS
29 #define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
30 #endif
31 #ifndef WIFEXITED
32 #define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
33 #endif
34
35 using namespace std;
36
37 #define SMTPTIMEOUT 60
38
39 #define INACTIVITY_TIMEOUT 30
40
41 #define DIGIT(c) (strchr("0123456789", (c)))
42
43
44 /////////////////////////////////////////////////////////////////////////////
45
46 LIBMAIL_START
47
open_smtp(mail::account * & accountRet,mail::account::openInfo & oi,mail::callback & callback,mail::callback::disconnect & disconnectCallback)48 static bool open_smtp(mail::account *&accountRet,
49 mail::account::openInfo &oi,
50 mail::callback &callback,
51 mail::callback::disconnect &disconnectCallback)
52 {
53 mail::loginInfo nntpLoginInfo;
54
55 if (!mail::loginUrlDecode(oi.url, nntpLoginInfo))
56 return false;
57
58 if (nntpLoginInfo.method != "smtp" &&
59 nntpLoginInfo.method != "smtps" &&
60 nntpLoginInfo.method != "sendmail")
61 return false;
62
63 accountRet=new mail::smtp(oi.url, oi.pwd, oi.certificates, callback,
64 disconnectCallback,
65 oi.loginCallbackObj);
66 return true;
67 }
68
smtp_remote(string url,bool & flag)69 static bool smtp_remote(string url, bool &flag)
70 {
71 mail::loginInfo nntpLoginInfo;
72
73 if (!mail::loginUrlDecode(url, nntpLoginInfo))
74 return false;
75
76 if (nntpLoginInfo.method != "smtp" &&
77 nntpLoginInfo.method != "smtps")
78 return false;
79
80 flag=true;
81 return true;
82 }
83
84 driver smtp_driver= { &open_smtp, &smtp_remote };
85
86 LIBMAIL_END
87
88 ///////////////////////////////////////////////////////////////////////////
89 //
90 // Helper class sends outbound message
91 //
92 ///////////////////////////////////////////////////////////////////////////
93
Blast(mail::smtp * smtpArg)94 mail::smtp::Blast::Blast(mail::smtp *smtpArg)
95 : eofSent(false), mySmtp(smtpArg), lastChar('\n'),
96 bytesTotal(0), bytesDone(0)
97 {
98 }
99
~Blast()100 mail::smtp::Blast::~Blast()
101 {
102 if (mySmtp)
103 mySmtp->myBlaster=NULL;
104 }
105
fillWriteBuffer()106 bool mail::smtp::Blast::fillWriteBuffer()
107 {
108 if (!mySmtp)
109 {
110 if (!eofSent)
111 {
112 eofSent=true;
113 writebuffer += "\r\n.\r\n";
114 return true;
115 }
116 return false;
117 }
118
119 mySmtp->installHandler(mySmtp->responseHandler);
120 // Reset the timeout every time the write buffer gets filled
121
122 char buffer[BUFSIZ];
123
124 size_t i;
125
126 FILE *fp=mySmtp->sendQueue.front().message;
127
128 for (i=0; i<sizeof(buffer); i++)
129 {
130 int ch=getc(fp);
131
132 if (ch == EOF)
133 {
134 writebuffer += string(buffer, buffer+i);
135 if (lastChar != '\n')
136 writebuffer += "\r\n";
137 writebuffer += ".\r\n";
138
139 mail::callback *c=mySmtp->sendQueue.front().callback;
140
141 if (c)
142 c->reportProgress(bytesTotal, bytesTotal,
143 0, 1);
144
145 mySmtp->myBlaster=NULL;
146 mySmtp=NULL;
147 eofSent=true;
148 return true;
149 }
150
151 ++bytesDone;
152
153 if (lastChar == '\n' && ch == '.')
154 {
155 ungetc(ch, fp); // Leading .s are doubled.
156 --bytesDone;
157 }
158
159 if (ch == '\n' && lastChar != '\r')
160 {
161 ungetc(ch, fp);
162 --bytesDone;
163 ch='\r';
164 }
165
166 buffer[i]=lastChar=ch;
167 }
168
169 mail::callback *c=mySmtp->sendQueue.front().callback;
170
171 if (c)
172 c->reportProgress(bytesDone, bytesTotal, 0, 1);
173
174 writebuffer += string(buffer, buffer+sizeof(buffer));
175 return true;
176 }
177
178 // Something arrived from the server.
179
socketRead(const string & readbuffer)180 int mail::smtp::socketRead(const string &readbuffer)
181 {
182 size_t n=readbuffer.find('\n');
183
184 if (n == std::string::npos)
185 return 0;
186
187 string l=readbuffer.substr(0, n++);
188
189 size_t cr;
190
191 while ((cr=l.find('\r')) != std::string::npos)
192 l=l.substr(0, cr) + l.substr(cr+1);
193
194 // Collect multiline SMTP replies. Keep only the last 30 lines
195 // received.
196
197 smtpResponse.push_back(l);
198
199 if (smtpResponse.size() > 30)
200 smtpResponse.erase(smtpResponse.begin());
201
202 string::iterator b=l.begin(), e=l.end();
203 int numCode=0;
204
205 while (b != e && isdigit((int)(unsigned char)*b))
206 numCode=numCode * 10 + (*b++ - '0');
207
208 if (b == l.begin()+3)
209 {
210 if (b == e || unicode_isspace((unsigned char)*b))
211 {
212 string s="";
213
214 list<string>::iterator b, e;
215
216 b=smtpResponse.begin();
217 e=smtpResponse.end();
218
219 while (b != e)
220 {
221 if (s.size() > 0) s += "\n";
222
223 s += *b++;
224 }
225
226 smtpResponse.clear();
227
228 if (responseHandler != 0)
229 (this->*responseHandler)(numCode, s);
230 else
231 fatalError=true;
232 // SMTP msg when no handler installed, something's
233 // gone wrong.
234
235 }
236 }
237
238 return n;
239 }
240
installHandler(void (mail::smtp::* handler)(int,string))241 void mail::smtp::installHandler(void (mail::smtp::*handler)(int,
242 string))
243 {
244 responseHandler=handler;
245 responseTimeout=0;
246
247 if (handler != 0)
248 {
249 time(&responseTimeout);
250 responseTimeout += SMTPTIMEOUT;
251 }
252 }
253
254
disconnect(const char * reason)255 void mail::smtp::disconnect(const char *reason)
256 {
257 while (!sendQueue.empty())
258 {
259 struct messageQueueInfo &info=sendQueue.front();
260
261 info.callback->fail(reason);
262 fclose(info.message);
263 sendQueue.pop();
264 }
265 pingTimeout=0;
266
267 if (getfd() >= 0)
268 {
269 string errmsg=socketDisconnect();
270
271 if (errmsg.size() == 0)
272 errmsg=reason;
273
274 if (orderlyShutdown)
275 success(errmsg);
276 else if (smtpLoginInfo.callbackPtr)
277 error(errmsg);
278 else
279 disconnect_callback.disconnected(errmsg.c_str());
280 }
281 }
282
resumed()283 void mail::smtp::resumed()
284 {
285 if (responseHandler != 0)
286 {
287 time(&responseTimeout);
288 responseTimeout += SMTPTIMEOUT;
289 }
290 }
291
292
handler(vector<pollfd> & fds,int & ioTimeout)293 void mail::smtp::handler(vector<pollfd> &fds, int &ioTimeout)
294 {
295 if (fatalError)
296 return;
297
298 int fd_save=getfd();
299
300 time_t now;
301
302 time(&now);
303
304 if (responseHandler != 0)
305 {
306 if (now >= responseTimeout)
307 {
308 fatalError=true;
309 ioTimeout=0;
310 (this->*responseHandler)(500, "500 Server timed out.");
311 return;
312 }
313
314 if (ioTimeout >= (responseTimeout - now) * 1000)
315 {
316 ioTimeout=(responseTimeout - now)*1000;
317 }
318 }
319
320 if (pingTimeout)
321 {
322 if (pingTimeout < now)
323 {
324 mail::smtpInfo dummy;
325
326 send(NULL, dummy, NULL, false);
327 }
328 else
329 {
330 if (ioTimeout >= (pingTimeout - now)*1000)
331 {
332 ioTimeout=(pingTimeout - now)*1000;
333 }
334 }
335 }
336
337
338 mail::fd::process(fds, ioTimeout);
339
340 if (getfd() < 0)
341 {
342 size_t i;
343
344 for (i=fds.size(); i; )
345 {
346 --i;
347
348 if (fds[i].fd == fd_save)
349 {
350 fds.erase(fds.begin()+i, fds.begin()+i+1);
351 break;
352 }
353 }
354 }
355 }
356
smtp(string url,string passwd,std::vector<std::string> & certificates,mail::callback & callback,mail::callback::disconnect & disconnectCallback,loginCallback * loginCallbackFunc)357 mail::smtp::smtp(string url, string passwd,
358 std::vector<std::string> &certificates,
359 mail::callback &callback,
360 mail::callback::disconnect &disconnectCallback,
361 loginCallback *loginCallbackFunc)
362 : mail::fd(disconnectCallback, certificates), orderlyShutdown(false),
363 ready2send(false), fatalError(true), pingTimeout(0)
364 {
365 responseHandler=NULL;
366 smtpLoginInfo.callbackPtr= &callback;
367 smtpLoginInfo.loginCallbackFunc=loginCallbackFunc;
368
369 if (!loginUrlDecode(url, smtpLoginInfo))
370 {
371 smtpLoginInfo.callbackPtr->fail("Invalid SMTP URL.");
372 return;
373 }
374
375 if (smtpLoginInfo.method == "sendmail") // Local sendmail
376 {
377 smtpLoginInfo.use_ssl=false;
378 smtpLoginInfo.uid="";
379 smtpLoginInfo.pwd="";
380
381 string errmsg;
382
383 int pipefd[2];
384
385 if (libmail_streampipe(pipefd) < 0)
386 {
387 smtpLoginInfo.callbackPtr->fail(strerror(errno));
388 return;
389 }
390
391 pid_t pid1=fork();
392
393 if (pid1 < 0)
394 {
395 close(pipefd[0]);
396 close(pipefd[1]);
397 smtpLoginInfo.callbackPtr->fail(strerror(errno));
398 return;
399 }
400
401 if (pid1 == 0)
402 {
403 close(0);
404 close(1);
405 errno=EIO;
406 if (dup(pipefd[1]) != 0 || dup(pipefd[1]) != 1)
407 {
408 perror("500 dup");
409 exit(1);
410 }
411 close(pipefd[0]);
412 close(pipefd[1]);
413
414 pid1=fork();
415 if (pid1 < 0)
416 {
417 printf("500 fork() failed.\r\n");
418 exit(1);
419 }
420
421 if (pid1)
422 exit(0);
423 execl(SENDMAIL, SENDMAIL, "-bs", (char *)NULL);
424
425 printf("500 %s: %s\n", SENDMAIL, strerror(errno));
426 exit(0);
427 }
428
429 close(pipefd[1]);
430 int waitstat;
431 pid_t pid2;
432
433 while ((pid2=wait(&waitstat)) != pid1)
434 {
435 if (kill(pid1, 0))
436 break;
437 }
438
439
440 try {
441 errmsg=socketAttach(pipefd[0]);
442 } catch (...) {
443 close(pipefd[0]);
444 LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
445 }
446 }
447 else
448 {
449 smtpLoginInfo.use_ssl= smtpLoginInfo.method == "smtps";
450
451 if (passwd.size() > 0)
452 smtpLoginInfo.pwd=passwd;
453
454 string errmsg=socketConnect(smtpLoginInfo, "smtp", "smtps");
455
456 if (errmsg.size() > 0)
457 {
458 smtpLoginInfo.callbackPtr->fail(errmsg);
459 return;
460 }
461 }
462
463 fatalError=false;
464 hmac_method_index=0;
465 installHandler(&mail::smtp::greetingResponse);
466 }
467
468 // Login failed
469
error(string errMsg)470 void mail::smtp::error(string errMsg)
471 {
472 mail::callback *c=smtpLoginInfo.callbackPtr;
473
474 smtpLoginInfo.callbackPtr=NULL;
475
476 if (c)
477 c->fail(errMsg);
478
479 }
480
481 // Login succeeded
482
success(string errMsg)483 void mail::smtp::success(string errMsg)
484 {
485 mail::callback *c=smtpLoginInfo.callbackPtr;
486
487 smtpLoginInfo.callbackPtr=NULL;
488
489 if (c)
490 c->success(errMsg);
491 }
492
493 // Send EHLO/HELO
494
howdy(const char * hi)495 void mail::smtp::howdy(const char *hi)
496 {
497 char buffer[512];
498
499 buffer[sizeof(buffer)-1]=0;
500
501 if (gethostname(buffer, sizeof(buffer)-1) < 0)
502 strcpy(buffer, "localhost");
503
504 socketWrite(string(hi) + buffer + "\r\n");
505 }
506
greetingResponse(int n,string s)507 void mail::smtp::greetingResponse(int n, string s)
508 {
509 switch (n / 100) {
510 case 1:
511 case 2:
512 case 3:
513 break;
514 default:
515 error(s);
516 return;
517 }
518
519 installHandler(&mail::smtp::ehloResponse);
520 howdy("EHLO ");
521 }
522
ehloResponse(int n,string s)523 void mail::smtp::ehloResponse(int n, string s)
524 {
525 switch (n / 100) {
526 case 1:
527 case 2:
528 case 3:
529 break;
530 default: // Maybe HELO will work
531 installHandler(&mail::smtp::heloResponse);
532 howdy("HELO ");
533 return;
534 }
535
536 // Parse EHLO capability list
537
538 bool firstLine=true;
539
540 while (s.size() > 0)
541 {
542 string line;
543
544 size_t p=s.find('\n');
545
546 if (p == std::string::npos)
547 {
548 line=s;
549 s="";
550 }
551 else
552 {
553 line=s.substr(0, p);
554 s=s.substr(p+1);
555 }
556
557 if (firstLine)
558 {
559 firstLine=false;
560 continue;
561 }
562
563 while ((p=s.find('\r')) != std::string::npos)
564 s=s.substr(0, p) + s.substr(p+1);
565
566 for (p=0; p<line.size(); p++)
567 if (!DIGIT((int)(unsigned char)line[p]))
568 break;
569
570 if (p < line.size())
571 p++;
572
573 // Put capability to uppercase
574
575 line=unicode::iconvert::convert_tocase(line.substr(p),
576 "iso-8859-1",
577 unicode_uc);
578
579 for (p=0; p<line.size(); p++)
580 if (unicode_isspace((unsigned char)line[p]) ||
581 line[p] == '=')
582 break;
583
584 string capa=line.substr(0, p);
585
586 // Save SASL authentication methods as capabilities
587 // AUTH=method
588
589 if (capa == "AUTH" || capa == "XSECURITY")
590 {
591 if (p < line.size())
592 p++;
593
594 line=line.substr(p);
595
596 while (line.size() > 0)
597 {
598 for (p=0; p<line.size(); p++)
599 if (unicode_isspace((unsigned char)
600 line[p]) ||
601 line[p] == ',')
602 break;
603
604 if (p > 0)
605 {
606 capabilities.insert(capa + "=" +
607 line.substr(0, p));
608 }
609 if (p < line.size())
610 p++;
611 line=line.substr(p);
612 }
613 }
614 else
615 capabilities.insert(capa);
616 }
617 starttls();
618 }
619
heloResponse(int n,string s)620 void mail::smtp::heloResponse(int n, string s)
621 {
622 switch (n / 100) {
623 case 1:
624 case 2:
625 case 3:
626 starttls();
627 return;
628 }
629
630 error(s);
631 }
632
starttls()633 void mail::smtp::starttls()
634 {
635 #if HAVE_LIBCOURIERTLS
636
637 if (smtpLoginInfo.use_ssl // Already SSL-encrypted
638 || smtpLoginInfo.options.count("notls") > 0
639 || !hasCapability("STARTTLS")
640 || socketEncrypted())
641 {
642 begin_auth();
643 return;
644 }
645
646 installHandler(&mail::smtp::starttlsResponse);
647
648 socketWrite("STARTTLS\r\n");
649 #else
650 begin_auth();
651 return;
652 #endif
653 }
654
starttlsResponse(int n,string s)655 void mail::smtp::starttlsResponse(int n, string s)
656 {
657 switch (n / 100) {
658 case 1:
659 case 2:
660 if (!socketBeginEncryption(smtpLoginInfo))
661 {
662 smtpLoginInfo.callbackPtr=NULL;
663 return;
664 }
665 capabilities.clear();
666 greetingResponse(n, s);
667 return;
668 }
669
670 error(s);
671 }
672
begin_auth()673 void mail::smtp::begin_auth()
674 {
675 if (!hasCapability("AUTH=EXTERNAL"))
676 {
677 begin_auth_nonexternal();
678 return;
679 }
680 installHandler(&mail::smtp::auth_external_response);
681 socketWrite("AUTH EXTERNAL =\r\n");
682 }
683
auth_external_response(int n,string s)684 void mail::smtp::auth_external_response(int n, string s)
685 {
686 switch (n / 100) {
687 case 1:
688 case 2:
689 case 3:
690 authenticated();
691 return;
692 }
693 begin_auth_nonexternal();
694 }
695
begin_auth_nonexternal()696 void mail::smtp::begin_auth_nonexternal()
697 {
698 int i;
699
700 if (smtpLoginInfo.uid.size() == 0)
701 {
702 authenticate_hmac();
703 return;
704 }
705
706 for (i=0; mail::imaphmac::hmac_methods[i]; ++i)
707 {
708 if (hasCapability(string("AUTH=CRAM-") +
709 mail::imaphmac
710 ::hmac_methods[hmac_method_index]->getName()
711 ))
712 break;
713 }
714
715 if (mail::imaphmac::hmac_methods[i] ||
716 hasCapability("AUTH=LOGIN"))
717 {
718 if (smtpLoginInfo.pwd.size() == 0)
719 {
720 currentCallback=smtpLoginInfo.loginCallbackFunc;
721
722 if (currentCallback)
723 {
724 currentCallback->target=this;
725 currentCallback->getPassword();
726 return;
727 }
728 }
729 }
730 authenticate_hmac();
731 }
732
loginInfoCallback(std::string str)733 void mail::smtp::loginInfoCallback(std::string str)
734 {
735 currentCallback=NULL;
736 smtpLoginInfo.pwd=str;
737 authenticate_hmac();
738 }
739
loginInfoCallbackCancel()740 void mail::smtp::loginInfoCallbackCancel()
741 {
742 currentCallback=NULL;
743 error("Login cancelled");
744 }
745
authenticate_hmac()746 void mail::smtp::authenticate_hmac()
747 {
748 for (;;)
749 {
750 if (mail::imaphmac::hmac_methods[hmac_method_index] == NULL ||
751 smtpLoginInfo.uid.size() == 0)
752 {
753 authenticate_login(); // Exhausted SASL, forget it.
754 return;
755 }
756
757 if (hasCapability(string("AUTH=CRAM-") +
758 mail::imaphmac
759 ::hmac_methods[hmac_method_index]->getName()
760 ))
761 break;
762 hmac_method_index++;
763 }
764
765 installHandler(&mail::smtp::hmacResponse);
766
767 socketWrite(string("AUTH CRAM-") +
768 mail::imaphmac::hmac_methods[hmac_method_index]->getName()
769 + "\r\n");
770 }
771
hmacResponse(int n,string s)772 void mail::smtp::hmacResponse(int n, string s)
773 {
774 switch (n / 100) {
775 case 1:
776 case 2:
777 authenticated();
778 return;
779 }
780
781 if ((n / 100) != 3 || s.size() < 4)
782 {
783 ++hmac_method_index;
784 authenticate_hmac();
785 return;
786 }
787
788 s=mail::decodebase64str(s.substr(4));
789 s=(*mail::imaphmac::hmac_methods[hmac_method_index])(smtpLoginInfo
790 .pwd, s);
791
792 string sHex;
793
794 {
795 ostringstream hexEncode;
796
797 hexEncode << hex;
798
799 string::iterator b=s.begin();
800 string::iterator e=s.end();
801
802 while (b != e)
803 hexEncode << setw(2) << setfill('0')
804 << (int)(unsigned char)*b++;
805
806 sHex=hexEncode.str();
807 }
808
809 s=mail::encodebase64str(smtpLoginInfo.uid + " " + sHex);
810 socketWrite(s + "\r\n");
811 }
812
authenticate_login()813 void mail::smtp::authenticate_login()
814 {
815 if (!hasCapability("AUTH=LOGIN") || smtpLoginInfo.uid.size() == 0 ||
816 smtpLoginInfo.options.count("cram"))
817 {
818 authenticated();
819 return;
820 }
821
822 installHandler(&mail::smtp::loginUseridResponse);
823
824 socketWrite("AUTH LOGIN\r\n");
825 }
826
loginUseridResponse(int n,string msg)827 void mail::smtp::loginUseridResponse(int n, string msg)
828 {
829 if ((n / 100) != 3)
830 {
831 error(msg);
832 return;
833 }
834
835 installHandler(&mail::smtp::loginPasswdResponse);
836 socketWrite(string(mail::encodebase64str(smtpLoginInfo.uid))
837 + "\r\n");
838 }
839
loginPasswdResponse(int n,string msg)840 void mail::smtp::loginPasswdResponse(int n, string msg)
841 {
842 if ((n / 100) != 3)
843 {
844 error(msg);
845 return;
846 }
847
848 installHandler(&mail::smtp::loginResponse);
849 socketWrite(string(mail::encodebase64str(smtpLoginInfo.pwd))
850 + "\r\n");
851 }
852
loginResponse(int n,string msg)853 void mail::smtp::loginResponse(int n, string msg)
854 {
855 if ((n / 100) != 2)
856 {
857 error(msg);
858 return;
859 }
860
861 authenticated();
862 }
863
authenticated()864 void mail::smtp::authenticated()
865 {
866 ready2send=true;
867 if (!sendQueue.empty())
868 startNextMessage(); // Already something's queued up.
869 else
870 pingTimeout=time(NULL) + INACTIVITY_TIMEOUT;
871 success("Connected to " + smtpLoginInfo.server);
872 }
873
~smtp()874 mail::smtp::~smtp()
875 {
876 disconnect("Connection forcibly aborted.");
877 }
878
logout(mail::callback & callback)879 void mail::smtp::logout(mail::callback &callback)
880 {
881 if (fatalError)
882 {
883 callback.success("Server timed out.");
884 return;
885 }
886
887 smtpLoginInfo.callbackPtr= &callback;
888 orderlyShutdown=true;
889 installHandler(&mail::smtp::quitResponse);
890
891 socketWrite("QUIT\r\n");
892 }
893
quitResponse(int n,string s)894 void mail::smtp::quitResponse(int n, string s)
895 {
896 if (!socketEndEncryption())
897 {
898 socketDisconnect();
899 success(s);
900 }
901 }
902
checkNewMail(mail::callback & callback)903 void mail::smtp::checkNewMail(mail::callback &callback) // STUB
904 {
905 callback.success("OK");
906 }
907
hasCapability(string capability)908 bool mail::smtp::hasCapability(string capability)
909 {
910 if (capability == LIBMAIL_SINGLEFOLDER)
911 return false;
912
913 return capabilities.count(capability) > 0;
914 }
915
getCapability(string name)916 string mail::smtp::getCapability(string name)
917 {
918 upper(name);
919
920 if (name == LIBMAIL_SERVERTYPE)
921 {
922 return "smtp";
923 }
924
925 if (name == LIBMAIL_SERVERDESCR)
926 {
927 return "ESMTP server";
928 }
929
930 if (name == LIBMAIL_SINGLEFOLDER)
931 return "";
932
933 return capabilities.count(name) > 0 ? "1":"";
934 }
935
936
937 // Not implemented
938
folderFromString(string)939 mail::folder *mail::smtp::folderFromString(string)
940 {
941 errno=EINVAL;
942 return NULL;
943 }
944
945 // Not implemented
946
readTopLevelFolders(mail::callback::folderList & callback1,mail::callback & callback2)947 void mail::smtp::readTopLevelFolders(mail::callback::folderList &callback1,
948 mail::callback &callback2)
949 {
950 vector<const mail::folder *> dummy;
951
952 callback1.success(dummy);
953 callback2.success("OK");
954 }
955
956 // Not implemented
957
findFolder(string folder,class mail::callback::folderList & callback1,class mail::callback & callback2)958 void mail::smtp::findFolder(string folder,
959 class mail::callback::folderList &callback1,
960 class mail::callback &callback2)
961 {
962 callback2.fail("Folder not found.");
963 }
964
965 // This is why we're here:
966
getSendFolder(const mail::smtpInfo & info,const mail::folder * folderArg,string & errmsg)967 mail::folder *mail::smtp::getSendFolder(const mail::smtpInfo &info,
968 const mail::folder *folderArg,
969 string &errmsg)
970 {
971 if (folderArg)
972 {
973 return mail::account::getSendFolder(info, folderArg, errmsg);
974 }
975
976 if (info.recipients.size() == 0)
977 {
978 errno=ENOENT;
979 errmsg="Empty recipient list.";
980 return NULL;
981 }
982
983 mail::smtpFolder *folder=new mail::smtpFolder(this, info);
984
985 if (!folder)
986 {
987 errmsg=strerror(errno);
988 return NULL;
989 }
990 return folder;
991 }
992
993
readMessageAttributes(const vector<size_t> & messages,MessageAttributes attributes,mail::callback::message & callback)994 void mail::smtp::readMessageAttributes(const vector<size_t> &messages,
995 MessageAttributes attributes,
996 mail::callback::message &callback)
997 {
998 callback.fail("Invalid message number.");
999 }
1000
1001
readMessageContent(const vector<size_t> & messages,bool peek,enum mail::readMode readType,mail::callback::message & callback)1002 void mail::smtp::readMessageContent(const vector<size_t> &messages,
1003 bool peek,
1004 enum mail::readMode readType,
1005 mail::callback::message &callback)
1006 {
1007 callback.fail("Invalid message number.");
1008 }
1009
readMessageContent(size_t messageNum,bool peek,const mail::mimestruct & msginfo,enum mail::readMode readType,mail::callback::message & callback)1010 void mail::smtp::readMessageContent(size_t messageNum,
1011 bool peek,
1012 const mail::mimestruct &msginfo,
1013 enum mail::readMode readType,
1014 mail::callback::message &callback)
1015 {
1016 callback.fail("Invalid message number.");
1017 }
1018
readMessageContentDecoded(size_t messageNum,bool peek,const mail::mimestruct & msginfo,mail::callback::message & callback)1019 void mail::smtp::readMessageContentDecoded(size_t messageNum,
1020 bool peek,
1021 const mail::mimestruct &msginfo,
1022 mail::callback::message &callback)
1023 {
1024 callback.fail("Invalid message number.");
1025 }
1026
getFolderIndexSize()1027 size_t mail::smtp::getFolderIndexSize()
1028 {
1029 return 0;
1030 }
1031
getFolderIndexInfo(size_t messageNum)1032 mail::messageInfo mail::smtp::getFolderIndexInfo(size_t messageNum)
1033 {
1034 return mail::messageInfo();
1035 }
1036
saveFolderIndexInfo(size_t messageNumber,const mail::messageInfo & info,mail::callback & callback)1037 void mail::smtp::saveFolderIndexInfo(size_t messageNumber,
1038 const mail::messageInfo &info,
1039 mail::callback &callback)
1040 {
1041 callback.fail("Invalid message number.");
1042 }
1043
updateFolderIndexFlags(const vector<size_t> & messages,bool doFlip,bool enableDisable,const mail::messageInfo & flags,mail::callback & callback)1044 void mail::smtp::updateFolderIndexFlags(const vector<size_t> &messages,
1045 bool doFlip,
1046 bool enableDisable,
1047 const mail::messageInfo &flags,
1048 mail::callback &callback)
1049 {
1050 callback.fail("Invalid message number.");
1051 }
1052
updateFolderIndexInfo(mail::callback & callback)1053 void mail::smtp::updateFolderIndexInfo(mail::callback &callback)
1054 {
1055 callback.success("OK");
1056 }
1057
removeMessages(const std::vector<size_t> & messages,mail::callback & cb)1058 void mail::smtp::removeMessages(const std::vector<size_t> &messages,
1059 mail::callback &cb)
1060 {
1061 updateFolderIndexInfo(cb);
1062 }
1063
copyMessagesTo(const vector<size_t> & messages,mail::folder * copyTo,mail::callback & callback)1064 void mail::smtp::copyMessagesTo(const vector<size_t> &messages,
1065 mail::folder *copyTo,
1066 mail::callback &callback)
1067 {
1068 callback.fail("Invalid message number.");
1069 }
1070
searchMessages(const mail::searchParams & searchInfo,mail::searchCallback & callback)1071 void mail::smtp::searchMessages(const mail::searchParams &searchInfo,
1072 mail::searchCallback &callback)
1073 {
1074 vector<size_t> empty;
1075
1076 callback.success(empty);
1077 }
1078
1079 //////////////////////////////////////////////////////////////////////////////
1080 //
1081 // Another message is ready to go out. Called by mail::smtpFolder::go,
1082 // and one other place.
1083
send(FILE * tmpfile,mail::smtpInfo & info,mail::callback * callback,bool flag8bit)1084 void mail::smtp::send(FILE *tmpfile, mail::smtpInfo &info,
1085 mail::callback *callback, bool flag8bit)
1086 {
1087 pingTimeout=0;
1088
1089 struct messageQueueInfo msgInfo;
1090
1091 msgInfo.message=tmpfile;
1092 msgInfo.messageInfo=info;
1093 msgInfo.callback=callback;
1094 msgInfo.flag8bit=flag8bit;
1095
1096 bool wasEmpty=sendQueue.empty();
1097
1098 sendQueue.push(msgInfo);
1099
1100 if (wasEmpty && ready2send)
1101 startNextMessage();
1102 }
1103
startNextMessage()1104 void mail::smtp::startNextMessage()
1105 {
1106 if (fatalError)
1107 {
1108 messageProcessed(500, "500 A fatal error occurred while connected to remote server.");
1109 return;
1110 }
1111
1112 socketWrite("RSET\r\n");
1113 installHandler(&mail::smtp::rsetResponse);
1114 }
1115
rsetResponse(int result,string message)1116 void mail::smtp::rsetResponse(int result, string message)
1117 {
1118 struct messageQueueInfo &info=sendQueue.front();
1119
1120 if (info.message == NULL) // Just a NO-OP ping.
1121 {
1122 sendQueue.pop();
1123
1124 if (!sendQueue.empty())
1125 startNextMessage();
1126 else
1127 pingTimeout=time(NULL) + INACTIVITY_TIMEOUT;
1128 return;
1129 }
1130
1131 switch (result / 100) {
1132 case 1:
1133 case 2:
1134 case 3:
1135 break;
1136 default:
1137 messageProcessed(result, message);
1138 return;
1139 }
1140
1141 if (!validString(info.messageInfo.sender))
1142 return;
1143
1144 {
1145 vector<string>::iterator b=info.messageInfo.recipients.begin(),
1146 e=info.messageInfo.recipients.end();
1147
1148 while (b != e)
1149 if (!validString(*b++))
1150 return;
1151 }
1152
1153 {
1154 map<string, string>::iterator
1155 b=info.messageInfo.options.begin(),
1156 e=info.messageInfo.options.end();
1157
1158 while (b != e)
1159 if (!validString((*b++).second))
1160 return;
1161 }
1162
1163 string mailfrom="MAIL FROM:<" + info.messageInfo.sender + ">";
1164
1165 if (hasCapability("8BITMIME"))
1166 mailfrom += info.flag8bit ? " BODY=8BITMIME":" BODY=7BIT";
1167
1168 if (hasCapability("SIZE"))
1169 {
1170 long pos;
1171
1172 if (fseek(info.message, 0L, SEEK_END) < 0 ||
1173 (pos=ftell(info.message)) < 0)
1174 {
1175 messageProcessed(500, strerror(errno));
1176 return;
1177 }
1178
1179 pos= pos < 1024 * 1024 ? pos * 70 / 68: pos/68 * 70;
1180
1181 string buffer;
1182
1183 {
1184 ostringstream o;
1185
1186 o << pos;
1187 buffer=o.str();
1188 }
1189
1190 mailfrom += " SIZE=";
1191 mailfrom += buffer;
1192 }
1193
1194 if (info.messageInfo.options.count("DSN") > 0 ||
1195 info.messageInfo.options.count("RET") > 0)
1196 {
1197 if (!hasCapability("DSN"))
1198 {
1199 messageProcessed(500, "500 This mail server does not support delivery status notifications.");
1200 return;
1201 }
1202 }
1203
1204 if (hasCapability("DSN"))
1205 {
1206 if (info.messageInfo.options.count("RET") > 0)
1207 mailfrom += " RET=" + info.messageInfo.options
1208 .find("RET")->second;
1209 }
1210
1211 if (hasCapability("XVERP=COURIER"))
1212 {
1213 if (info.messageInfo.options.count("VERP") > 0)
1214 mailfrom += " VERP";
1215 }
1216
1217 if (hasCapability("SMTPUTF8"))
1218 {
1219 mailfrom += " SMTPUTF8";
1220 }
1221
1222 if (info.messageInfo.options.count("SECURITY") > 0)
1223 {
1224 string sec=info.messageInfo.options.find("SECURITY")->second;
1225
1226 if (info.messageInfo.options.count("XSECURITY=" + sec) == 0)
1227 {
1228 messageProcessed(500, "500 requested security not available.");
1229 return;
1230 }
1231
1232 mailfrom += " SECURITY=" + sec;
1233 }
1234
1235 pipelineCmd=mailfrom;
1236
1237 mailfrom += "\r\n";
1238
1239 pipelineCount=0;
1240 installHandler(&mail::smtp::mailfromResponse);
1241 socketWrite(mailfrom);
1242 }
1243
1244 // RCPT TO handler in pipeline mode.
1245
pipelinedResponse(int result,string message)1246 void mail::smtp::pipelinedResponse(int result, string message)
1247 {
1248 switch (result / 100) {
1249 case 1:
1250 case 2:
1251 case 3:
1252 break;
1253 default:
1254 if (pipelineError.size() == 0)
1255 pipelineError=sendQueue.front().messageInfo
1256 .recipients[rcptCount] + ": " + message;
1257 break;
1258 }
1259
1260 ++rcptCount;
1261 if (--pipelineCount || fatalError)
1262 return; // More on the way
1263
1264 if (pipelineError.size() > 0)
1265 messageProcessed(500, pipelineError);
1266 else
1267 {
1268 installHandler(&mail::smtp::dataResponse);
1269 socketWrite("DATA\r\n");
1270 }
1271 }
1272
1273 // MAIL FROM response. RCPT TO response in non-pipelined mode.
1274
mailfromResponse(int result,string message)1275 void mail::smtp::mailfromResponse(int result, string message)
1276 {
1277 switch (result / 100) {
1278 case 1:
1279 case 2:
1280 case 3:
1281 break;
1282 default:
1283 messageProcessed(result, pipelineCmd + ": " + message);
1284 return;
1285 }
1286
1287 struct messageQueueInfo &info=sendQueue.front();
1288
1289 rcptCount=0;
1290 if (pipelineCount == 0 && hasCapability("PIPELINING") &&
1291 smtpLoginInfo.options.count("nopipelining") == 0)
1292 {
1293 string rcptto="";
1294
1295 vector<string>::iterator b=info.messageInfo.recipients.begin(),
1296 e=info.messageInfo.recipients.end();
1297
1298 pipelineError="";
1299
1300 while (b != e)
1301 {
1302 rcptto += "RCPT TO:<" + *b++ + ">";
1303
1304 if (info.messageInfo.options.count("DSN") > 0)
1305 rcptto += " NOTIFY="
1306 + info.messageInfo.options.find("DSN")
1307 ->second;
1308 rcptto += "\r\n";
1309 ++pipelineCount;
1310 }
1311
1312 installHandler(&mail::smtp::pipelinedResponse);
1313 socketWrite(rcptto);
1314 return;
1315 }
1316
1317 if (pipelineCount < info.messageInfo.recipients.size())
1318 {
1319 pipelineCmd=info.messageInfo.recipients[pipelineCount++];
1320 string rcptto= "RCPT TO:<" + pipelineCmd + ">";
1321
1322 if (info.messageInfo.options.count("DSN") > 0)
1323 rcptto += " NOTIFY="
1324 + info.messageInfo.options.find("DSN")
1325 ->second;
1326
1327 rcptto += "\r\n";
1328 socketWrite(rcptto);
1329 return;
1330 }
1331
1332 installHandler(&mail::smtp::dataResponse);
1333 socketWrite("DATA\r\n");
1334 }
1335
1336 // 1st DATA response
1337
dataResponse(int result,string message)1338 void mail::smtp::dataResponse(int result, string message)
1339 {
1340 switch (result / 100) {
1341 case 1:
1342 case 2:
1343 case 3:
1344 break;
1345 default:
1346 messageProcessed(result, message);
1347 break;
1348 }
1349
1350 long fpos;
1351
1352 if ( fseek(sendQueue.front().message, 0L, SEEK_END) < 0 ||
1353 (fpos=ftell(sendQueue.front().message)) < 0 ||
1354 fseek(sendQueue.front().message, 0L, SEEK_SET) < 0 ||
1355 (myBlaster=new Blast(this)) == NULL)
1356
1357 {
1358 fatalError=true;
1359 messageProcessed(500, strerror(errno));
1360 return;
1361 }
1362
1363 myBlaster->bytesTotal=fpos;
1364 installHandler(&mail::smtp::data2Response);
1365 socketWrite(myBlaster);
1366 }
1367
data2Response(int result,string message)1368 void mail::smtp::data2Response(int result, string message)
1369 {
1370 if (myBlaster)
1371 myBlaster->mySmtp=NULL;
1372 installHandler(NULL); // Nothing more.
1373 messageProcessed(result, message);
1374 }
1375
validString(string s)1376 bool mail::smtp::validString(string s)
1377 {
1378 string::iterator b=s.begin(), e=s.end();
1379
1380 while (b != e)
1381 {
1382 if ((int)(unsigned char)*b <= ' ' || *b == '>' || *b == '<')
1383 {
1384 messageProcessed(500, "500 Invalid character in message sender, recipient, or options.");
1385 return false;
1386 }
1387 b++;
1388 }
1389 return true;
1390 }
1391
messageProcessed(int result,string message)1392 void mail::smtp::messageProcessed(int result, string message)
1393 {
1394 struct messageQueueInfo &info=sendQueue.front();
1395
1396 mail::callback *callback=info.callback;
1397 fclose(info.message);
1398
1399 try {
1400 sendQueue.pop();
1401 if (!sendQueue.empty())
1402 startNextMessage();
1403 else
1404 pingTimeout=time(NULL) + INACTIVITY_TIMEOUT;
1405
1406 switch (result / 100) {
1407 case 1:
1408 case 2:
1409 case 3:
1410 callback->success(message);
1411 break;
1412 default:
1413 callback->fail(message);
1414 break;
1415 }
1416
1417 } catch (...) {
1418 callback->fail("An exception has occurred while processing message.");
1419 }
1420 }
1421
translatePath(string path)1422 string mail::smtp::translatePath(string path)
1423 {
1424 return "INBOX"; // NOOP
1425 }
1426