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