1 /*
2 ** Copyright 2003-2008, Double Precision Inc.
3 **
4 ** See COPYING for distribution information.
5 */
6 
7 #include "config.h"
8 #include "myserver.H"
9 #include "myservercallback.H"
10 #include "myserverlogincallback.H"
11 #include "myserverpromptinfo.H"
12 #include "myfolder.H"
13 #include "cursesmessage.H"
14 #include "ctrlchandler.H"
15 #include "typeahead.H"
16 #include "hierarchy.H"
17 #include "curses/cursesscreen.H"
18 #include "curses/cursesstatusbar.H"
19 #include "libmail/misc.H"
20 #include "gettext.H"
21 #include <time.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <pwd.h>
25 #include <unistd.h>
26 #include <poll.h>
27 
28 #include <iostream>
29 
30 using namespace std;
31 
32 extern CursesStatusBar *statusBar;
33 extern CursesScreen *cursesScreen;
34 
35 vector<myServer *> myServer::server_list;
36 Hierarchy myServer::hierarchy;
37 
38 unsigned myServer::cmdcount=0;
39 string myServer::customHeaders="";
40 
41 Certificates *myServer::certs;
42 
43 extern void folderIndexScreen(void *);
44 extern void hierarchyScreen(void *);
45 
46 int myServer::pollFDForRefreshMessageCount = -1;
47 int myServer::pollFDForRefreshMessageCountWriteDirection = -1;
48 CursesHierarchy* myServer::cursesHierarchyForRefreshing = NULL;
49 
myServer(string name,string urlArg)50 myServer::myServer(string name, string urlArg)
51 	: serverName(name), url(urlArg), mailCheckInterval(300), server(NULL),
52 	  currentFolder(NULL), hierarchyEntry(NULL)
53 {
54 	server_list.push_back(this); // Register ourselves.
55 }
56 
getServerByUrl(string url)57 myServer *myServer::getServerByUrl(string url)
58 {
59 	vector<myServer *>::iterator b=server_list.begin(),
60 		e=server_list.end();
61 
62 	while (b != e)
63 	{
64 		myServer *s= *b++;
65 
66 		if (s->url == url)
67 			return s;
68 	}
69 	return NULL;
70 }
71 
~myServer()72 myServer::~myServer()
73 {
74 	vector<myServer *>::iterator b=server_list.begin(),
75 		e=server_list.end();
76 
77 	while (b != e)
78 	{
79 		if ( *b == this ) // Deregister ourselves.
80 		{
81 			server_list.erase(b, b+1);
82 			break;
83 		}
84 		b++;
85 	}
86 	disconnect();
87 }
88 
89 //
90 // Forcible disconnect from the server.  Destroy the current folder object,
91 // if any, and the server object.
92 
disconnect()93 void myServer::disconnect()
94 {
95 	// Take care to null-out all the pointers before destroying the
96 	// objects, in order to prevent infinite.
97 
98 	if (currentFolder)
99 	{
100 		myFolder *f=currentFolder;
101 		currentFolder=NULL;
102 		delete f;
103 	}
104 
105 	mail::account *oldServer=server;
106 
107 	server=NULL;
108 	cancelTimer();
109 	if (oldServer)
110 		delete oldServer;
111 
112 	disconnectTasks();
113 }
114 
115 // libmail callback -- disconnected from the server.
116 
disconnected(const char * errmsg)117 void myServer::disconnected(const char *errmsg)
118 {
119 	string errmsgBuf=errmsg;
120 
121 	bool wasConnected=server != NULL;
122 
123 	disconnect(); // Clean everything up.
124 
125 	Curses::keepgoing=false;	// Terminate event loop
126 	nextScreen= &hierarchyScreen;
127 	nextScreenArg=NULL;
128 
129 	if (statusBar->prompting())
130 		statusBar->fieldAbort(); // Abort out of any prompt.
131 
132 	if (wasConnected &&  // If NULL, explicit disconnect.
133 	    errmsgBuf.size() > 0)
134 	{
135 		statusBar->status(Gettext(_("%1% - DISCONNECTED: %2%"))
136 				  << serverName
137 				  << errmsgBuf, statusBar->DISCONNECTED);
138 		statusBar->beepError();
139 	}
140 
141 	if (hierarchyEntry)
142 	{
143 		hierarchy.drawEraseBelow(hierarchyEntry, true);
144 		serverDescr="";
145 		hierarchyEntry->clear();
146 		hierarchy.drawEraseBelow(hierarchyEntry);
147 	}
148 }
149 
servererror(const char * errmsg)150 void myServer::servererror(const char *errmsg)
151 {
152 	string errmsgBuf=Gettext(_("%1% - ALERT: %2%"))
153 		<< serverName << errmsg;
154 
155 	statusBar->status(errmsgBuf, statusBar->SERVERERROR);
156 	statusBar->beepError();
157 }
158 
159 //
160 // The main event loop.  Tasks:  A) Process mail::account events,
161 // B) process keyboard events.
162 
163 // Part B is only done if no mail::account operations are pending
164 // (callback is NULL).  The eventloop continues until
165 //
166 // A) A terminal keyboard event is processed, or
167 // B) A mail::account operation succeeded
168 //
169 
170 void (*myServer::nextScreen)(void *);
171 void *myServer::nextScreenArg;
172 
eventloop(myServer::Callback * callback)173 bool myServer::eventloop(myServer::Callback *callback)
174 {
175 	const char *err_flag=NULL;
176 
177 	bool eventLoopCalled=false;
178 	// We need to make sure that mail::account::process gets called at
179 	// least once, so that libmail has an opportunity to process "runlater"
180 	// events.
181 
182 	Curses::keepgoing=true;
183 
184 	if (callback == NULL)
185 		cursesScreen->draw();
186 	else
187 		callback->interrupted=false;
188 
189 	nextScreen=NULL;
190 
191 	Timer throbber;
192 
193 	vector<struct pollfd> fds;
194 	int ioTimeout;
195 
196 	while (callback || Curses::keepgoing)
197 	{
198 		if (callback)
199 			if (callback->succeeded || callback->failed
200 			    || callback->interrupted)
201 				if (eventLoopCalled)
202 					break;
203 
204 		if (callback && throbber.expired())
205 		{
206 			statusBar->busy();
207 			throbber.setTimer(1);
208 		}
209 
210 		bool alarmCalled;
211 
212 		{
213 			struct timeval tv=Timer::getNextTimeout(alarmCalled);
214 
215 			if (tv.tv_sec == 0 && tv.tv_usec == 0)
216 				tv.tv_sec= 60 * 60; // No alarms scheduled.
217 
218 			ioTimeout=tv.tv_sec * 1000 + tv.tv_usec / 1000;
219 		}
220 
221 		mail::account::process(fds, ioTimeout);
222 		eventLoopCalled=true;
223 
224 		if (!callback && !Curses::keepgoing)
225 			break;
226 
227 		Curses::Key k1=
228 			(callback == NULL && !Typeahead::typeahead->empty()
229 			 ? Typeahead::typeahead->pop() // Saved typeahead
230 			 : cursesScreen->getKey());
231 
232 		if (k1.nokey())
233 		{
234 			mail::pollfd pfd;
235 
236 			pfd.fd=0;
237 			pfd.events=mail::pollread;
238 			pfd.revents=0;
239 			fds.push_back(pfd);
240 		}
241 
242 		if (callback == NULL || (k1.plain() && k1.ukey == '\x03'))
243 		{
244 			if (!k1.nokey())
245 			{
246 				bool rc=Curses::processKey(k1);
247 
248 				if (!rc)
249 				{
250 					rc=0;
251 				}
252 
253 				if (rc)
254 				{
255 					while (k1.plain() && k1 == '\x03' &&
256 					       !Typeahead::typeahead->empty())
257 						Typeahead::typeahead->pop();
258 					// CTRL-C kills typeahead buffer
259 				}
260 				else if (callback != NULL &&
261 					 !(k1.plain() &&
262 					   k1.ukey == '\x03'))
263 				{
264 					// Command in progress, save typeahead
265 
266 					Typeahead::typeahead->push(k1);
267 				}
268 				continue;
269 			}
270 		}
271 		else if (callback && !k1.nokey())
272 			Typeahead::typeahead->push(k1);
273 
274 		if (callback && (callback->succeeded || callback->failed
275 				 || callback->interrupted))
276 			break;
277 
278 		if (alarmCalled)
279 			continue; // Something might've happened...
280 
281 		// poll() ignores file descriptors less than 0, thus the
282 		// `pollFDForRefresh` can always be added regardless of the
283 		// feature being used or not.
284 		struct pollfd pollFDForRefresh = {
285 			myServer::pollFDForRefreshMessageCount,
286 			POLLIN,
287 			0
288 		};
289 		fds.push_back(pollFDForRefresh);
290 
291 		if (mail::account::poll(fds, ioTimeout) < 0)
292 		{
293 			if (errno != EINTR)
294 			{
295 				err_flag="select";
296 				break;
297 			}
298 		}
299 
300 		char buf[128];
301 		int pollev = fds.back().revents;
302 		fds.pop_back();
303 		if((pollev & POLLIN) && read(
304 				myServer::pollFDForRefreshMessageCount,
305 					buf, sizeof(buf)) > 0 &&
306 				myServer::pollFDForRefreshMessageCount != -1 &&
307 				myServer::cursesHierarchyForRefreshing != NULL)
308 			myServer::cursesHierarchyForRefreshing->
309 				refreshAllFolders();
310 	}
311 
312 	statusBar->notbusy();
313 
314 	if (err_flag)
315 	{
316 		statusBar->status(err_flag);
317 		statusBar->beepError();
318 		return false;
319 	}
320 	else if (callback)
321 	{
322 		// If a command just finished, check for anything that got
323 		// expunged.
324 
325 		vector<myServer *>::iterator b=server_list.begin(),
326 			e=server_list.end();
327 
328 		while (b != e)
329 		{
330 			myServer *p= *b++;
331 
332 			if (p->currentFolder)
333 				p->currentFolder->checkExpunged();
334 		}
335 
336 		if (!callback->noreport && !callback->interrupted)
337 		{
338 			statusBar->status(callback->msg,
339 					  callback->succeeded
340 					  ? CursesStatusBar::NORMAL:
341 					  callback->getLevel());
342 
343 			if (!callback->succeeded)
344 				statusBar->beepError();
345 		}
346 
347 		return callback->succeeded;
348 	}
349 	return true;
350 }
351 
eventloop()352 void myServer::eventloop()
353 {
354 	eventloop(NULL);
355 }
356 
eventloop(myServer::Callback & callback)357 bool myServer::eventloop(myServer::Callback &callback)
358 {
359 	bool rc;
360 
361 	++cmdcount;
362 
363 	try {
364 		CtrlCHandler::lastCtrlC=0;
365 
366 		rc=eventloop(&callback);
367 		--cmdcount;
368 	} catch (...) {
369 		--cmdcount;
370 		LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
371 	}
372 	return rc;
373 }
374 
find_cert_by_id(std::vector<std::string> & certArg)375 void myServer::find_cert_by_id(std::vector<std::string> &certArg)
376 {
377 	find_cert_by_id(certArg, certificate);
378 }
379 
find_cert_by_id(std::vector<std::string> & certArg,std::string certificate)380 void myServer::find_cert_by_id(std::vector<std::string> &certArg,
381 			       std::string certificate)
382 {
383 	certArg.clear();
384 	if (certificate.size() > 0)
385 	{
386 		std::map<std::string, Certificates::cert>::iterator
387 			p=certs->certs.find(certificate);
388 
389 		if (p != certs->certs.end())
390 		{
391 			certArg.reserve(1);
392 			certArg.push_back(p->second.cert);
393 		}
394 	}
395 }
396 
find_cert_by_url(std::string url,std::vector<std::string> & certArg)397 void myServer::find_cert_by_url(std::string url,
398 				std::vector<std::string> &certArg)
399 {
400 	certArg.clear();
401 
402 	std::vector<myServer *>::iterator b, e;
403 
404 	for (b=server_list.begin(), e=server_list.end(); b != e; ++b)
405 		if ( (*b)->url == url && (*b)->certificate.size() > 0)
406 		{
407 			(*b)->find_cert_by_id(certArg);
408 			break;
409 		}
410 }
411 
412 /////////////////////////////////////////////////////////////////////////
413 //
414 // Log in to the server.  The are several available mechanism.
415 //
416 // 1) An explicit password is supplied.
417 //
418 // 2) A callaback object is provided, to prompt for userid and password
419 
login(string passwordStr)420 bool myServer::login(string passwordStr)
421 {
422 	return login(passwordStr, NULL);
423 }
424 
login(myServerLoginCallback & loginPrompt)425 bool myServer::login(myServerLoginCallback &loginPrompt)
426 {
427 	return login("", &loginPrompt);
428 }
429 
login(string passwordStr,myServerLoginCallback * loginPrompt)430 bool myServer::login(string passwordStr,
431 		     myServerLoginCallback *loginPrompt)
432 {
433 	Callback callback(CursesStatusBar::LOGINERROR);
434 
435 	if (loginPrompt)
436 		loginPrompt->myCallback= &callback;
437 
438 	mail::account::openInfo loginInfo;
439 
440 	loginInfo.url=url;
441 	loginInfo.pwd=passwordStr;
442 	find_cert_by_id(loginInfo.certificates);
443 	loginInfo.loginCallbackObj=loginPrompt;
444 
445 	loginInfo.extraString=getConfigDir() + "/" + newsrc;
446 
447 	if (loginPrompt)
448 	{
449 		loginPrompt->reset();
450 		loginPrompt->myCallback= &callback;
451 	}
452 
453 	server= mail::account::open(loginInfo, callback, *this);
454 
455 	if (!server)
456 	{
457 		statusBar->clearstatus();
458 		statusBar->status(callback.msg);
459 		return false;
460 	}
461 
462 	for (;;)
463 	{
464 		if (loginPrompt && callback.interrupted) // Libmail wants pwd
465 		{
466 			loginPrompt->promptPassword(serverName, passwordStr);
467 		}
468 
469 		statusBar->clearstatus();
470 		statusBar->status(Gettext(_("Connecting to %1%..."))
471 				  << serverName);
472 
473 		bool rc=eventloop(callback);
474 
475 		if (loginPrompt && callback.interrupted) // Libmail wants pwd
476 			continue;
477 
478 		if (!rc)
479 			return false;
480 		break;
481 	}
482 
483 	// Logged in.
484 
485 	password=passwordStr; // Remember the password.
486 
487 	{
488 		mail::loginInfo urlDecodeInfo;
489 
490 		if (mail::loginUrlDecode(url, urlDecodeInfo))
491 		{
492 			map<string, string>::iterator
493 				p=urlDecodeInfo.options.find("check");
494 
495 			if (p != urlDecodeInfo.options.end())
496 			{
497 				istringstream i(p->second);
498 
499 				i >> mailCheckInterval;
500 
501 				if (mailCheckInterval < 10)
502 					mailCheckInterval=10;
503 
504 				if (mailCheckInterval > 20 * 60)
505 					mailCheckInterval= 20 * 60;
506 			}
507 		}
508 	}
509 
510 	return true;
511 }
512 
513 // Log out all accounts.
514 
logout()515 void myServer::logout()
516 {
517 	vector<myServer *>::iterator b=server_list.begin(),
518 		e=server_list.end();
519 
520 	myServer *s;
521 
522 	while (b != e)
523 	{
524 		s= *b++;
525 
526 		s->serverLogout();
527 	}
528 }
529 
530 // Logout this account.
531 
serverLogout()532 void myServer::serverLogout()
533 {
534 	cancelTimer();
535 
536 	if (server)
537 	{
538 		if (currentFolder)
539 		{
540 			currentFolder->quiesce();
541 			currentFolder->isClosing=true;
542 		}
543 
544 		Callback callback;
545 
546 		server->logout(callback);
547 		eventloop(callback);
548 		disconnect();
549 	}
550 }
551 
getHomeDir()552 string myServer::getHomeDir()
553 {
554 	string homedir=getenv("HOME");
555 
556 	if (homedir.size() == 0)
557 	{
558 		struct passwd *pw=getpwuid(getuid());
559 
560 		if (pw)
561 			homedir=pw->pw_dir;
562 		else
563 			homedir="";
564 	}
565 
566 	return homedir;
567 }
568 
updateServerConfiguration(string name,string value)569 bool myServer::updateServerConfiguration(string name, string value)
570 {
571 	map<string, string>::iterator i=server_configuration.find(name);
572 
573 	if (i != server_configuration.end())
574 	{
575 		if (value.size() > 0 && i->second == value)
576 			return false;
577 
578 		server_configuration.erase(i);
579 		if (value.size() == 0)
580 			return true;
581 	}
582 	else if (value.size() == 0)
583 		return false;
584 
585 	server_configuration.insert(make_pair(name, value));
586 	return true;
587 }
588 
getServerConfiguration(string name)589 string myServer::getServerConfiguration(string name)
590 {
591 	if (server_configuration.count(name) > 0)
592 		return server_configuration.find(name)->second;
593 
594 	return "";
595 }
596 
updateFolderConfiguration(const mail::folder * folder,string name,string value)597 bool myServer::updateFolderConfiguration(const mail::folder *folder,
598 					 string name,
599 					 string value)
600 {
601 	return updateFolderConfiguration(folder->getPath(), name, value);
602 }
603 
updateFolderConfiguration(string p,string name,string value)604 bool myServer::updateFolderConfiguration(string p,
605 					 string name,
606 					 string value)
607 {
608 	if (folder_configuration.count(p) == 0)
609 	{
610 		map<string, string> dummy;
611 
612 		folder_configuration.insert(make_pair(p, dummy));
613 	}
614 
615 	map<string, string> &m=folder_configuration.find(p)->second;
616 
617 	map<string, string>::iterator i=m.find(name);
618 
619 	if (i != m.end())
620 	{
621 		if (i->second == value)
622 			return false;
623 
624 		m.erase(i);
625 	}
626 	m.insert(make_pair(name, value));
627 	return true;
628 }
629 
getFolderConfiguration(const mail::folder * folder,string name)630 string myServer::getFolderConfiguration(const mail::folder *folder,
631 					string name)
632 {
633 	return getFolderConfiguration(folder->getPath(), name);
634 }
635 
getFolderConfiguration(string p,string name)636 string myServer::getFolderConfiguration(string p,
637 					string name)
638 {
639 	if (folder_configuration.count(p) > 0)
640 	{
641 		map<string, string> &h=folder_configuration.find(p)->second;
642 
643 		if (h.count(name) > 0)
644 			return (h.find(name)->second);
645 	}
646 
647 	return "";
648 }
649 
saveFolderConfiguration()650 void myServer::saveFolderConfiguration()
651 {
652 	saveconfig(mail::account::isRemoteUrl(url));
653 }
654 
655 //
656 // Opened a hierarchy.  Find any stale configuration entries, for a folder
657 // that cannot possibly exist, and get rid of it.
658 
openedHierarchy(Hierarchy::Entry * todo)659 void myServer::openedHierarchy(Hierarchy::Entry *todo)
660 {
661 	vector<mail::folder *> folderVector;
662 
663 	vector<Hierarchy::Entry *>::iterator
664 		cb=todo->begin(), ce=todo->end();
665 
666 	while (cb != ce)
667 	{
668 		Hierarchy::Folder *f= (*cb)->toFolder();
669 
670 		if (f)
671 		{
672 			mail::folder *mf=f->getFolder();
673 
674 			if (mf)
675 				folderVector.push_back(mf);
676 		}
677 
678 		cb++;
679 	}
680 
681 	openedHierarchy(todo, folderVector);
682 }
683 
openedHierarchy(Hierarchy::Entry * todo,vector<mail::folder * > & folderList)684 void myServer::openedHierarchy(Hierarchy::Entry *todo,
685 			       vector<mail::folder *> &folderList)
686 {
687 	bool dirty=false;
688 
689 	Hierarchy::Folder *parentFolder=todo->toFolder();
690 
691 	// todo may also be a Hierarchy::Server.  After opening a mail account
692 	// delete any stale configuration data for any folder that's not in
693 	// any of the top level hierarchies defined for this account.
694 	// In this case toFolder() returns a NULL.
695 
696 	const mail::folder *pf = parentFolder ? parentFolder->getFolder():NULL;
697 
698 	map<string, map<string, string> >::iterator
699 		b=folder_configuration.begin(),
700 		e=folder_configuration.end(), save;
701 
702 	while (b != e)
703 	{
704 		if (pf != NULL)
705 		{
706 			if (pf->getPath() == b->first)
707 			{
708 				b++;
709 				continue; // 'tis OK
710 			}
711 
712 			if (!pf->isParentOf(b->first))
713 			{
714 				b++;
715 				continue; // Some other hierarchy, OK
716 			}
717 		}
718 
719 		vector<mail::folder *>::iterator
720 			cb=folderList.begin(), ce=folderList.end();
721 
722 		while (cb != ce)
723 		{
724 			mail::folder *mf= *cb;
725 
726 			if (mf->getPath() == b->first
727 			    ||
728 			    mf->isParentOf(b->first))
729 			{
730 				break;
731 			}
732 			cb++;
733 		}
734 
735 		save=b;
736 		b++;
737 
738 		if (cb == ce)
739 		{
740 			dirty=true;
741 			folder_configuration.erase(save);
742 		}
743 	}
744 
745 	if (todo->size() > 0)
746 	{
747 		Hierarchy &h=todo->getHierarchy();
748 
749 		if (h.display)
750 			h.display->visible(*todo->begin());
751 	}
752 	if (dirty)
753 		saveFolderConfiguration();
754 }
755 
756 // New account, initialize the default set of folders.
757 
initializeHierarchy()758 void myServer::initializeHierarchy()
759 {
760 	hierarchy.root.clear();
761 
762 	vector<myServer *>::iterator
763 		b=server_list.begin(), e=server_list.end();
764 
765 	while (b != e)
766 	{
767 		(*b)->addHierarchy(false);
768 
769 		if ( (*b)->server != NULL)
770 		{
771 			(*b)->serverDescr=
772 				(*b)->server
773 				->getCapability(LIBMAIL_SERVERDESCR);
774 
775 			(*b)->openedHierarchy( (*b)->hierarchyEntry );
776 			// Purge any deleted top level hierarchies
777 		}
778 
779 		b++;
780 	}
781 
782 	saveconfig();
783 
784 	Hierarchy::EntryIteratorAssignRow assign_row(1);
785 
786 	hierarchy.root.prefixIterate(assign_row);
787 }
788 
789 //
790 // Install server's toplevelfolders.
791 //
792 
addHierarchy(bool assignRows)793 void myServer::addHierarchy(bool assignRows)
794 {
795 	myServer *lastServer=NULL;
796 
797 	if (assignRows)
798 	{
799 		vector<myServer *>::iterator b, e;
800 
801 		b=server_list.begin();
802 		e=server_list.end();
803 
804 		while (b != e)
805 		{
806 			lastServer= *--e;
807 
808 			if (lastServer->hierarchyEntry)
809 				break;
810 			lastServer=NULL;
811 		}
812 	}
813 
814 	Hierarchy::Server *hs=new Hierarchy::Server(hierarchy,
815 						    &hierarchy.root, this);
816 	if (!hs)
817 		outofmemory();
818 
819 	hierarchyEntry=hs;
820 	addTopLevelFolders();
821 
822 	if (assignRows)
823 	{
824 		Hierarchy::EntryIteratorAssignRow
825 			assign_row( lastServer ? lastServer->hierarchyEntry
826 				    ->getRow(): 1);
827 
828 		if (lastServer)
829 			lastServer->hierarchyEntry->
830 				prefixIterateAfter(assign_row);
831 		else
832 			hs->prefixIterate(assign_row);
833 	}
834 }
835 
showHierarchy()836 void myServer::showHierarchy()
837 {
838 	if (hierarchyEntry == NULL) // WTF???
839 		return;
840 
841 	hierarchy.drawEraseBelow(hierarchyEntry, true);
842 	hierarchyEntry->clear();
843 
844 	addTopLevelFolders();
845 	hierarchy.drawEraseBelow(hierarchyEntry);
846 	openedHierarchy(hierarchyEntry);
847 	saveFolderConfiguration();
848 }
849 
850 //
851 // Folder configuration changed.
852 
updateHierarchy()853 void myServer::updateHierarchy()
854 {
855 	vector<const mail::folder *> folderList;
856 
857 	vector<Hierarchy::Entry *>::iterator b=hierarchyEntry->begin(),
858 		e=hierarchyEntry->end();
859 
860 	while (b != e)
861 	{
862 		Hierarchy::Folder *f= (*b++)->toFolder();
863 
864 		if (f != NULL)
865 			folderList.push_back(f->getFolder());
866 	}
867 
868 	topLevelFolders.success(folderList, false);
869 	openedHierarchy(hierarchyEntry);
870 	saveconfig(mail::account::isRemoteUrl(url));
871 }
872 
addTopLevelFolderRedraw(string path)873 void myServer::addTopLevelFolderRedraw(string path)
874 {
875 	hierarchy.drawEraseBelow(hierarchyEntry, true);
876 	topLevelFolders.add(path);
877 	addTopLevelFolder(path);
878 	hierarchy.drawEraseBelow(hierarchyEntry, false);
879 	saveFolderConfiguration();
880 }
881 
addTopLevelFolders()882 void myServer::addTopLevelFolders()
883 {
884 	serverDescr=server ? server->getCapability(LIBMAIL_SERVERDESCR) : "";
885 
886 	if (server == NULL)
887 		return;
888 
889 	myReadFolders::iterator fb=topLevelFolders.begin(),
890 		fe=topLevelFolders.end();
891 
892 	while (fb != fe)
893 		addTopLevelFolder( *fb++ );
894 	setTimer(mailCheckInterval);
895 }
896 
addTopLevelFolder(string path)897 void myServer::addTopLevelFolder(string path)
898 {
899 	mail::folder *f= server->folderFromString(path);
900 
901 	if (!f)
902 		outofmemory();
903 
904 	Hierarchy::Folder *hf;
905 
906 	try {
907 		hf=new Hierarchy::Folder(hierarchy, hierarchyEntry, f);
908 		delete f;
909 	} catch (...)
910 	{
911 		delete f;
912 		LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
913 	}
914 	if (!hf)
915 		outofmemory();
916 
917 	hf->updateInfo(this, false);
918 }
919 
alarm()920 void myServer::alarm()
921 {
922 	vector<Hierarchy::Entry *>::iterator b=hierarchyEntry->begin();
923 	vector<Hierarchy::Entry *>::iterator e=hierarchyEntry->end();
924 
925 	while (b != e)
926 	{
927 		Hierarchy::Folder *f= (*b++)->toFolder();
928 
929 		if (f)
930 			f->updateInfo(this, false);
931 	}
932 
933 	setTimer(mailCheckInterval);
934 }
935 
openFolder(const mail::folder * f,bool autoOpenDraft)936 void myServer::openFolder(const mail::folder *f,
937 			  bool autoOpenDraft)
938 	// True to autoopen a single message in the draft folder.
939 {
940 	if (server == NULL)
941 	{
942 		statusBar->beepError();
943 		return;
944 	}
945 
946 	if (!currentFolder || currentFolder->mustReopen ||
947 	    currentFolder->getFolder()->getPath() != f->getPath())
948 	{
949 		if (currentFolder) // EXPUNGE it
950 		{
951 			currentFolder->mustReopen=true; // Fallback, if error
952 
953 			myServer::Callback expungeCallback;
954 
955 			expungeCallback.noreport=true;
956 
957 			statusBar->clearstatus();
958 			statusBar->status(Gettext(_("Purging %1%..."))
959 					  << currentFolder->getFolder()
960 					  ->getName());
961 
962 			if (currentFolder->mymessage)
963 				delete currentFolder->mymessage;
964 
965 			currentFolder->isExpungingDrafts=true; // SHUT UP
966 
967 			server->updateFolderIndexInfo(expungeCallback);
968 
969 			myServer::eventloop(expungeCallback);
970 
971 			finishCheckingNewMail();
972 
973 			if (!server)
974 				return; // Server crashed
975 			currentFolder->isClosing=true;
976 		}
977 
978 		statusBar->clearstatus();
979 		statusBar->status(Gettext(_("Opening %1%..."))
980 				  << f->getName());
981 
982 		myFolder *newFolder=new myFolder(this, f);
983 
984 		if (!newFolder)
985 			outofmemory();
986 
987 		try {
988 			myFolder::RestoreSnapshot restoreSnapshot(newFolder);
989 			// Find any saved folder snapshot
990 
991 			Callback select_callback;
992 
993 			f->open(select_callback, &restoreSnapshot, *newFolder);
994 
995 			if (!eventloop(select_callback))
996 			{
997 				delete newFolder;
998 				if (currentFolder)
999 					currentFolder->mustReopen=true;
1000 				return;
1001 			}
1002 			if (currentFolder)
1003 				delete currentFolder;
1004 			currentFolder=newFolder;
1005 		} catch (...)
1006 		{
1007 			if (currentFolder)
1008 				currentFolder->mustReopen=true;
1009 			delete newFolder;
1010 		}
1011 
1012 		if (!currentFolder)
1013 			return;
1014 
1015 		if (!currentFolder->init())
1016 		{
1017 			if (currentFolder)
1018 				currentFolder->mustReopen=true;
1019 			return;
1020 		}
1021 	}
1022 
1023 	nextScreen= &folderIndexScreen;
1024 	nextScreenArg=currentFolder;
1025 	Curses::keepgoing=false;
1026 
1027 	if (autoOpenDraft)
1028 	{
1029 		if (currentFolder->size() == 1)
1030 		{
1031 			Curses::keepgoing=true;
1032 			currentFolder->goDraft();
1033 		}
1034 	}
1035 }
1036 
finishCheckingNewMail()1037 void myServer::finishCheckingNewMail()
1038 {
1039 	// An EXPUNGE, or a new mail check may kick off new mail processing,
1040 	// which we want to allow to run to its natural end.
1041 
1042 	while (server && currentFolder && currentFolder->isCheckingNewMail())
1043 	{
1044 		myServer::Callback noopCallback;
1045 
1046 		server->checkNewMail(noopCallback);
1047 		myServer::eventloop(noopCallback);
1048 	}
1049 }
1050 
checkNewMail()1051 void myServer::checkNewMail()
1052 {
1053 	myServer::Callback noopCallback;
1054 
1055 	server->checkNewMail(noopCallback);
1056 	myServer::eventloop(noopCallback);
1057 
1058 
1059 	finishCheckingNewMail();
1060 }
1061 
setPollForRefreshMessageCount(string fn)1062 void myServer::setPollForRefreshMessageCount(string fn)
1063 {
1064 	if((myServer::pollFDForRefreshMessageCount =
1065 		open(fn.c_str(), O_RDONLY | O_CLOEXEC | O_NONBLOCK)) < 0)
1066 	{
1067 		perror("Failed to open FIFO for reading");
1068 		exit(1);
1069 	}
1070 	// Open another file descriptor for writing such that the pipe is
1071 	// kept open regardless of any external application closing it.
1072 	if((myServer::pollFDForRefreshMessageCountWriteDirection =
1073 		open(fn.c_str(), O_WRONLY | O_CLOEXEC)) < 0)
1074 	{
1075 		perror("Failed to open FIFO for writing");
1076 		exit(1);
1077 	}
1078 }
1079 
closePollForRefreshMessageCount()1080 void myServer::closePollForRefreshMessageCount()
1081 {
1082 	if(myServer::pollFDForRefreshMessageCount != -1)
1083 	{
1084 		// POSIX logic: close returns 0 on success thus true on fail...
1085 		if(close(myServer::pollFDForRefreshMessageCount))
1086 			perror("Failed to close message count update file "
1087 			       "descriptor");
1088 		close(myServer::pollFDForRefreshMessageCountWriteDirection);
1089 		myServer::pollFDForRefreshMessageCount = -1;
1090 	}
1091 }
1092 
setCursesHierarchyPointerForRefreshing(CursesHierarchy * h)1093 void myServer::setCursesHierarchyPointerForRefreshing(CursesHierarchy* h)
1094 {
1095 	myServer::cursesHierarchyForRefreshing = h;
1096 }
1097