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