1 /*
2 ** Copyright 2002-2004, Double Precision Inc.
3 **
4 ** See COPYING for distribution information.
5 */
6 #include "mail.H"
7 #include "sync.H"
8 
9 #include "driver.H"
10 #include "mbox.H"
11 
12 #include "runlater.H"
13 #include "logininfo.H"
14 #include <iostream>
15 #include <errno.h>
16 #include <netinet/in.h>
17 #include <unistd.h>
18 #include <pwd.h>
19 #include <arpa/inet.h>
20 #include "rfc822/rfc822.h"
21 #include <courier-unicode.h>
22 #include "maildir/maildiraclt.h"
23 #include "misc.H"
24 #include <fstream>
25 
26 using namespace std;
27 
28 list<mail::account *> mail::account::mailaccount_list;
29 
30 //
31 // Initialize mail::folder's default.  mail::folder is a superclass for
32 // mail folder objects.  Each mail account type is expected to use a
33 // subclass for the actual implementation.
34 //
35 // Each mail::folder object is associated with a corresponding mail::account
36 // object.  The constructor saves a pointer, and provides methods that
37 // the subclass can use to determine whether the mail::account object still exists
38 
folder(mail::account * ptr)39 mail::folder::folder(mail::account *ptr) : myServer(ptr)
40 {
41 }
42 
~folder()43 mail::folder::~folder()
44 {
45 }
46 
47 //
48 // The first order of business for any mail::folder method call is to
49 // determine whether the mail::account object still exists.  This method is
50 // a shortcut for most methods that receive a mail::callback object.
51 // If the mail::account object was destroyed, callback's fail method is called
52 // appropriately.
53 
isDestroyed(mail::callback & callback)54 bool mail::folder::isDestroyed(mail::callback &callback) const
55 {
56 	if (myServer.isDestroyed())
57 	{
58 		callback.fail("Server connection closed.");
59 		return true;
60 	}
61 
62 	return false;
63 }
64 
isDestroyed()65 bool mail::folder::isDestroyed() const
66 {
67 	return myServer.isDestroyed();
68 }
69 
isNewsgroup()70 string mail::folder::isNewsgroup() const
71 {
72 	return "";
73 }
74 
getMyRights(mail::callback & getCallback,std::string & rights)75 void mail::folder::getMyRights(mail::callback &getCallback,
76 			       std::string &rights) const
77 {
78 	getCallback.fail("This server does not implement access control lists.");
79 
80 }
81 
getRights(mail::callback & getCallback,std::list<std::pair<std::string,std::string>> & rights)82 void mail::folder::getRights(mail::callback &getCallback,
83 			     std::list<std::pair<std::string, std::string> >
84 			     &rights) const
85 {
86 	rights.clear();
87 	getCallback.fail("This server does not implement access control lists.");
88 }
89 
setRights(mail::callback & setCallback,string & errorIdentifier,vector<std::string> & errorRights,string identifier,string rights)90 void mail::folder::setRights(mail::callback &setCallback,
91 			     string &errorIdentifier,
92 			     vector<std::string> &errorRights,
93 			     string identifier,
94 			     string rights) const
95 {
96 	errorIdentifier="";
97 	errorRights.clear();
98 	setCallback.fail("This server does not implement access control lists.");
99 }
100 
delRights(mail::callback & setCallback,string & errorIdentifier,vector<std::string> & errorRights,std::string identifier)101 void mail::folder::delRights(mail::callback &setCallback,
102 			     string &errorIdentifier,
103 			     vector<std::string> &errorRights,
104 			     std::string identifier) const
105 {
106 	errorIdentifier="";
107 	errorRights.clear();
108 	setCallback.fail("This server does not implement access control lists.");
109 }
110 
111 //
112 // callback::folder miscellania
113 
folder()114 mail::callback::folder::folder()
115 {
116 }
117 
~folder()118 mail::callback::folder::~folder()
119 {
120 }
121 
saveSnapshot(std::string snapshotId)122 void mail::callback::folder::saveSnapshot(std::string snapshotId)
123 {
124 }
125 
126 // mail::callback::folderList miscellania
127 
folderList()128 mail::callback::folderList::folderList()
129 {
130 }
131 
~folderList()132 mail::callback::folderList::~folderList()
133 {
134 }
135 
136 // mail::callback::folderInfo miscellania
137 
folderInfo()138 mail::callback::folderInfo::folderInfo()
139 	: messageCount(0), unreadCount(0), fastInfo(false)
140 {
141 }
142 
~folderInfo()143 mail::callback::folderInfo::~folderInfo()
144 {
145 }
146 
success()147 void mail::callback::folderInfo::success()
148 {
149 }
150 
151 // messageInfo miscellania
152 
messageInfo()153 mail::messageInfo::messageInfo()
154 {
155 	uid="";
156 	draft=replied=marked=deleted=unread=recent=false;
157 }
158 
messageInfo(string s)159 mail::messageInfo::messageInfo(string s)
160 {
161 	uid="";
162 	draft=replied=marked=deleted=unread=recent=false;
163 
164 	string::iterator b=s.begin(), e=s.end();
165 
166 	while (b != e)
167 	{
168 		switch (*b++) {
169 		case 'D':
170 			draft=true;
171 			break;
172 		case 'R':
173 			replied=true;
174 			break;
175 		case 'M':
176 			marked=true;
177 			break;
178 		case 'T':
179 			deleted=true;
180 			break;
181 		case 'U':
182 			unread=true;
183 			break;
184 		case 'C':
185 			recent=true;
186 			break;
187 		case ':':
188 			uid=string(b, e);
189 			b=e;
190 			break;
191 		}
192 	}
193 }
194 
~messageInfo()195 mail::messageInfo::~messageInfo()
196 {
197 }
198 
string()199 mail::messageInfo::operator string() const
200 {
201 	string s;
202 
203 	if (draft) s += "D";
204 	if (replied) s += "R";
205 	if (marked) s += "M";
206 	if (deleted) s += "T";
207 	if (unread) s += "U";
208 	if (recent) s += "C";
209 
210 	return s + ":" + uid;
211 }
212 
213 
214 //
215 // mail::account superclass constructor.  The main task at hand is to save
216 // a reference to the disconnect callback, for later use, and to initialize
217 // the default character set.
218 //
219 
account(mail::callback::disconnect & callbackArg)220 mail::account::account(mail::callback::disconnect &callbackArg)
221 	: disconnect_callback(callbackArg)
222 {
223 	my_mailaccount_p=mailaccount_list.insert(mailaccount_list.end(), this);
224 }
225 
~account()226 mail::account::~account()
227 {
228 	// We can't just remove (this) from mailaccount_list, because we
229 	// might be iterating mail:;account::process().
230 
231 	*my_mailaccount_p=NULL;
232 }
233 
234 //////////////////////////////////////////////////////////////////////////////
235 //
236 // Utility functions.
237 //
238 //////////////////////////////////////////////////////////////////////////////
239 
240 LIBMAIL_START
241 
hostname()242 string hostname()
243 {
244 	char h[512];
245 
246 	h[sizeof(h)-1]=0;
247 
248 	if (gethostname(h, sizeof(h)-1) < 0)
249 		strcpy(h, "localhost");
250 	return h;
251 }
252 
homedir()253 string homedir()
254 {
255 	const char *h=getenv("HOME");
256 
257 	struct passwd *pw=getpwuid(getuid());
258 
259 	if (!pw)
260 		return "";
261 
262 	if (h == NULL || !*h)
263 		h=pw->pw_dir;
264 
265 	return h;
266 }
267 
upper(string & w)268 void upper(string &w)
269 {
270 	for (string::iterator b=w.begin(), e=w.end(); b != e; ++b)
271 		if (*b >= 'a' && *b <= 'z')
272 			*b += 'A'-'a';
273 }
274 LIBMAIL_END
275 
276 static const char x[]="0123456789ABCDEF";
277 
encword(string w)278 static string encword(string w)
279 {
280 	string ww="";
281 
282 	string::iterator b=w.begin(), e=w.end(), c;
283 
284 	while (b != e)
285 	{
286 		for (c=b; c != e; c++)
287 			if ( *c == '@' || *c == '/' || *c == '%' || *c == ':')
288 				break;
289 
290 		ww.append(b, c);
291 
292 		b=c;
293 
294 		if (b != e)
295 		{
296 			ww += '%';
297 			ww += x[ (*b / 16) & 15 ];
298 			ww += x[ *b & 15];
299 			b++;
300 		}
301 	}
302 
303 	return ww;
304 }
305 
306 LIBMAIL_START
loginUrlEncode(string method,string server,string uid,string pwd)307 string loginUrlEncode(string method, string server, string uid,
308 		      string pwd)
309 {
310 	return (method + "://" +
311 		(uid.size() > 0 ? encword(uid)
312 		 + (pwd.size() > 0 ? ":" + encword(pwd):"") + "@":"")
313 		+ server);
314 }
315 LIBMAIL_END
316 
decword(string w)317 static string decword(string w)
318 {
319 	string ww="";
320 
321 	string::iterator b=w.begin(), e=w.end(), c;
322 
323 	while (b != e)
324 	{
325 		for (c=b; c != e; c++)
326 			if ( *c == '%' && e-c > 2)
327 				break;
328 
329 		ww.append(b, c);
330 
331 		b=c;
332 
333 		if (b != e)
334 		{
335 			b++;
336 
337 			const char *c1= strchr(x, *b++);
338 			const char *c2= strchr(x, *b++);
339 
340 			char c=0;
341 
342 			if (c1) c= (c1-x) * 16;
343 			if (c2) c += c2-x;
344 
345 			ww += c;
346 		}
347 	}
348 
349 	return ww;
350 }
351 
352 LIBMAIL_START
353 
354 // Parse url format: method://uid:pwd@server/option/option...
355 
loginUrlDecode(string url,mail::loginInfo & loginInfo)356 bool loginUrlDecode(string url, mail::loginInfo &loginInfo)
357 {
358 	size_t n=url.find(':');
359 
360 	if (n == std::string::npos)
361 		return false;
362 
363 	loginInfo.method=url.substr(0, n);
364 
365 	if (url.size() - n < 3 || url[n+1] != '/' || url[n+2] != '/')
366 		return false;
367 
368 	string serverStr=url.substr(n+3);
369 	string options="";
370 
371 	n=serverStr.find('/');
372 
373 	if (n != std::string::npos)
374 	{
375 		options=serverStr.substr(n+1);
376 		serverStr.erase(n);
377 	}
378 
379 	n=serverStr.rfind('@');
380 	string uidpwd="";
381 
382 	if (n != std::string::npos)
383 	{
384 		uidpwd=serverStr.substr(0, n);
385 		serverStr=serverStr.substr(n+1);
386 	}
387 	loginInfo.server=serverStr;
388 
389 	n=uidpwd.find(':');
390 
391 	string pwd="";
392 
393 	if (n != std::string::npos)
394 	{
395 		pwd=uidpwd.substr(n+1);
396 		uidpwd=uidpwd.substr(0, n);
397 	}
398 
399 	loginInfo.uid=decword(uidpwd);
400 	loginInfo.pwd=decword(pwd);
401 
402 	while (options.size() > 0)
403 	{
404 		n=options.find('/');
405 
406 		string option;
407 
408 		if (n == std::string::npos)
409 		{
410 			option=options;
411 			options="";
412 		}
413 		else
414 		{
415 			option=options.substr(0, n);
416 			options=options.substr(n+1);
417 		}
418 
419 		string optionVal;
420 
421 		n=option.find('=');
422 
423 		if (n != std::string::npos)
424 		{
425 			optionVal=option.substr(n+1);
426 			option=option.substr(0, n);
427 		}
428 
429 		loginInfo.options.insert(make_pair(option, optionVal));
430 	}
431 	return true;
432 }
433 
434 LIBMAIL_END
435 
openInfo()436 mail::account::openInfo::openInfo() : loginCallbackObj(NULL)
437 {
438 }
439 
~openInfo()440 mail::account::openInfo::~openInfo()
441 {
442 }
443 
open(mail::account::openInfo oi,mail::callback & callback,mail::callback::disconnect & disconnectCallback)444 mail::account *mail::account::open(mail::account::openInfo oi,
445 				   mail::callback &callback,
446 				   mail::callback::disconnect
447 				   &disconnectCallback)
448 {
449 	mail::driver **l=mail::driver::get_driver_list();
450 
451 	while (*l)
452 	{
453 		mail::account *a;
454 
455 		if ( (*(*l)->open_func)(a, oi, callback, disconnectCallback))
456 		{
457 			if (!a)
458 				callback.fail(strerror(errno));
459 			return a;
460 		}
461 
462 		l++;
463 	}
464 
465 	callback.fail("Invalid mail account URL.");
466 	return NULL;
467 }
468 
isRemoteUrl(std::string url)469 bool mail::account::isRemoteUrl(std::string url)
470 {
471 	mail::driver **l=mail::driver::get_driver_list();
472 
473 	while (*l)
474 	{
475 		bool flag;
476 
477 		if ( (*(*l)->isRemote_func)(url, flag))
478 			return flag;
479 
480 		l++;
481 	}
482 
483 	return false;
484 }
485 
486 // Default implementation of getSendFolder: no such luck.
487 
getSendFolder(const class mail::smtpInfo & info,const mail::folder * folder,string & errmsg)488 mail::folder *mail::account::getSendFolder(const class mail::smtpInfo &info,
489 					   const mail::folder *folder,
490 					   string &errmsg)
491 {
492 	errno=ENOENT;
493 	errmsg="Not implemented.";
494 	return NULL;
495 }
496 
497 //
498 // The main function.  Where everything good happens.
499 //
500 // mail::account::process calls each existing mail::account object's handler method.
501 //
process(vector<pollfd> & fds,int & timeout)502 void mail::account::process(vector<pollfd> &fds, int &timeout)
503 {
504 	list<mail::account *>::iterator b, e, cur;
505 
506 	fds.clear();
507 	b=mailaccount_list.begin();
508 	e=mailaccount_list.end();
509 
510 	while (b != e)
511 	{
512 		// mail::account may destroy itself, so increment the iterator
513 		// before invoking the method
514 
515 		cur=b;
516 		b++;
517 
518 		if (*cur == NULL)
519 			mailaccount_list.erase(cur);
520 		else
521 		{
522 			(*cur)->handler(fds, timeout);
523 		}
524 	}
525 
526 	// Postponed handlers.
527 	mail::runLater::checkRunLater(timeout);
528 }
529 
530 //
531 // Resume after suspend.
532 //
533 
resume()534 void mail::account::resume()
535 {
536 	list<mail::account *>::iterator b, e, cur;
537 
538 	b=mailaccount_list.begin();
539 	e=mailaccount_list.end();
540 
541 	while (b != e)
542 	{
543 		// mail::account may destroy itself, so increment the iterator
544 		// before invoking the method
545 
546 		cur=b;
547 		++b;
548 
549 		if (*cur)
550 			(*cur)->resumed();
551 	}
552 }
553 
updateNotify(bool enableDisable,callback & callbackArg)554 void mail::account::updateNotify(bool enableDisable, callback &callbackArg)
555 {
556 	callbackArg.success("OK");
557 }
558 
moveMessagesTo(const std::vector<size_t> & messages,mail::folder * copyTo,mail::callback & callback)559 void mail::account::moveMessagesTo(const std::vector<size_t> &messages,
560 				   mail::folder *copyTo,
561 				   mail::callback &callback)
562 {
563 	generic::genericMoveMessages(this, messages, copyTo, callback);
564 }
565 
566 #if 0
567 void mail::account::removeMessages(const std::vector<size_t> &messages,
568 				   callback &cb)
569 {
570 	cb.fail("TODO");
571 }
572 
573 #endif
574 
updateKeywords(const vector<size_t> & messages,const set<string> & keywords,bool setOrChange,bool changeTo,mail::callback & cb)575 void mail::account::updateKeywords(const vector<size_t> &messages,
576 				   const set<string> &keywords,
577 				   bool setOrChange,
578 				   // false: set, true: see changeTo
579 				   bool changeTo,
580 				   mail::callback &cb)
581 {
582 	cb.fail("Not implemented");
583 }
584 
updateKeywords(const vector<size_t> & messages,const vector<string> & keywords,bool setOrChange,bool changeTo,mail::callback & cb)585 void mail::account::updateKeywords(const vector<size_t> &messages,
586 				   const vector<string> &keywords,
587 				   bool setOrChange,
588 				   bool changeTo,
589 				   mail::callback &cb)
590 {
591 	set<string> s;
592 
593 	s.insert(keywords.begin(), keywords.end());
594 
595 	updateKeywords(messages, s, setOrChange, changeTo, cb);
596 }
597 
598 
updateKeywords(const vector<size_t> & messages,const list<string> & keywords,bool setOrChange,bool changeTo,mail::callback & cb)599 void mail::account::updateKeywords(const vector<size_t> &messages,
600 				   const list<string> &keywords,
601 				   bool setOrChange,
602 				   bool changeTo,
603 				   mail::callback &cb)
604 {
605 	set<string> s;
606 
607 	s.insert(keywords.begin(), keywords.end());
608 
609 	updateKeywords(messages, s, setOrChange, changeTo, cb);
610 }
611 
getFolderKeywordInfo(size_t messageNum,set<string> & keywordRet)612 void mail::account::getFolderKeywordInfo(size_t messageNum,
613 					 set<string> &keywordRet)
614 {
615 	keywordRet.clear();
616 }
617 
618 
619 
620 
621 //
622 // Must provide default methods for mail::callback::message, not all
623 // subclasses must implement every method.
624 //
625 
message()626 mail::callback::message::message()
627 {
628 }
629 
~message()630 mail::callback::message::~message()
631 {
632 }
633 
messageEnvelopeCallback(size_t n,const mail::envelope & env)634 void mail::callback::message::messageEnvelopeCallback(size_t n,
635 						      const mail::envelope
636 						      &env)
637 {
638 }
639 
messageReferencesCallback(size_t n,const vector<string> & references)640 void mail::callback::message::messageReferencesCallback(size_t n,
641 							const vector<string>
642 							&references)
643 {
644 }
645 
messageStructureCallback(size_t n,const mail::mimestruct & mstruct)646 void mail::callback::message::messageStructureCallback(size_t n,
647 							 const mail::mimestruct
648 							 &mstruct)
649 {
650 }
651 
messageTextCallback(size_t n,string text)652 void mail::callback::message::messageTextCallback(size_t n, string text)
653 {
654 }
655 
656 void mail::callback::message
messageArrivalDateCallback(size_t messageNumber,time_t datetime)657 ::messageArrivalDateCallback(size_t messageNumber,
658 			     time_t datetime)
659 {
660 }
661 
messageSizeCallback(size_t messageNumber,unsigned long messagesize)662 void mail::callback::message::messageSizeCallback(size_t messageNumber,
663 						    unsigned long messagesize)
664 {
665 }
666 
667 LIBMAIL_START
668 
toutf8(string s)669 string toutf8(string s)
670 {
671 	string u;
672 
673 	char *p=unicode_convert_toutf8(s.c_str(),
674 					 unicode_default_chset(), NULL);
675 
676 	try {
677 		u=p;
678 		free(p);
679 	} catch (...) {
680 		free(p);
681 		LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
682 	}
683 	return u;
684 }
685 
fromutf8(string s)686 string fromutf8(string s)
687 {
688 	string u;
689 
690 	char *p=unicode_convert_fromutf8(s.c_str(), unicode_default_chset(),
691 					   NULL);
692 
693 	try {
694 		u=p;
695 		free(p);
696 	} catch (...) {
697 		free(p);
698 		LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
699 	}
700 	return u;
701 }
702 LIBMAIL_END
703 
704 //
705 // Common code for mbox and maildir
706 //
707 
translatePathCommon(string path,const char * sep)708 string mail::mbox::translatePathCommon(string path,
709 				       const char *sep)
710 {
711 	string newpath;
712 
713 	do
714 	{
715 		string component;
716 
717 		size_t n=path.find('/');
718 
719 		if (n == std::string::npos)
720 		{
721 			component=path;
722 			path="";
723 		}
724 		else
725 		{
726 			component=path.substr(0, n);
727 			path=path.substr(n+1);
728 		}
729 
730 		u32string ucvec;
731 
732 		char32_t *uc;
733 		size_t ucsize;
734 		unicode_convert_handle_t h;
735 
736 		if ((h=unicode_convert_tou_init(unicode_default_chset(),
737 						  &uc, &ucsize, 1)) == NULL)
738 		{
739 			uc=NULL;
740 		}
741 		else
742 		{
743 			unicode_convert(h, component.c_str(),
744 					  component.size());
745 
746 			if (unicode_convert_deinit(h, NULL))
747 				uc=NULL;
748 		}
749 
750 		if (!uc)
751 		{
752 			errno=EINVAL;
753 			return "";
754 		}
755 
756 		try {
757 			size_t n;
758 
759 			for (n=0; uc[n]; n++)
760 				;
761 
762 			ucvec.insert(ucvec.end(), uc, uc+n);
763 
764 			for (n=0; n<ucvec.size(); )
765 			{
766 				if (ucvec[n] == '\\' && n+1 < ucvec.size())
767 				{
768 					ucvec.erase(ucvec.begin()+n,
769 						    ucvec.begin()+n+1);
770 					n++;
771 					continue;
772 				}
773 				if (ucvec[n] == '%')
774 				{
775 					char32_t ucnum=0;
776 					size_t o=n+1;
777 
778 					while (o < ucvec.size())
779 					{
780 						if ((unsigned char)ucvec[o]
781 						    != ucvec[o])
782 							break;
783 						if (!isdigit(ucvec[o]))
784 							break;
785 
786 						ucnum=ucnum * 10 +
787 							ucvec[o]-'0';
788 						++o;
789 					}
790 
791 					if (o < ucvec.size() &&
792 					    ucvec[o] == ';')
793 						++o;
794 
795 					ucvec[n++]=ucnum;
796 					ucvec.erase(ucvec.begin()+n,
797 						    ucvec.begin()+o);
798 					continue;
799 				}
800 				n++;
801 			}
802 		} catch (...) {
803 			free(uc);
804 			LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
805 		}
806 
807 		free(uc);
808 		std::string p(unicode::iconvert::convert(ucvec,
809 							 unicode_x_smap_modutf8));
810 
811 		if (newpath.size() > 0)
812 			newpath += sep;
813 		newpath += p;
814 	} while (path.size() > 0);
815 
816 	return newpath;
817 }
818