1 /*
2 ** Copyright 2003-2008, Double Precision Inc.
3 **
4 ** See COPYING for distribution information.
5 */
6
7 #include "config.h"
8 #include "curses/mycurses.H"
9 #include "curses/cursesmainscreen.H"
10 #include "curses/cursesstatusbar.H"
11 #include "curseshierarchy.H"
12 #include "gettext.H"
13 #include "filter.H"
14 #include "passwordlist.H"
15 #include "libmail/mail.H"
16 #include "libmail/misc.H"
17 #include "libmail/maildir.H"
18 #include "libmail/logininfo.H"
19 #include "myfolder.H"
20 #include "mymessage.H"
21 #include "myserver.H"
22 #include "myservercallback.H"
23 #include "myserverpromptinfo.H"
24 #include "myserverlogincallback.H"
25 #include "myserverremoteconfig.H"
26 #include "opensubfolders.H"
27 #include "specialfolder.H"
28 #include "globalkeys.H"
29 #include "addressbook.H"
30 #include "colors.H"
31 #include "acl.H"
32 #include <errno.h>
33 #include <set>
34 #include <algorithm>
35
36 using namespace std;
37
38 extern struct CustomColor color_fl_accountName,
39 color_fl_accountType,
40 color_fl_folderName,
41 color_fl_folderDirName,
42 color_fl_messageCount;
43
44 extern Gettext::Key key_ADDFOLDER;
45 extern Gettext::Key key_RENAME;
46 extern Gettext::Key key_DELFOLDER;
47 extern Gettext::Key key_FILTER;
48 extern Gettext::Key key_EDIT;
49 extern Gettext::Key key_SPECIAL;
50 extern Gettext::Key key_ABORT;
51 extern Gettext::Key key_ADDDIRECTORY;
52 extern Gettext::Key key_DOWN;
53 extern Gettext::Key key_UP;
54
55 extern Gettext::Key key_ADDRESSBOOK;
56 extern Gettext::Key key_DRAFTS;
57 extern Gettext::Key key_REMOTE;
58 extern Gettext::Key key_SENT;
59 extern Gettext::Key key_TOP;
60 extern Gettext::Key key_RESET;
61 extern Gettext::Key key_PERMS;
62 extern Gettext::Key key_PERMVIEW;
63 extern Gettext::Key key_PERMEDIT;
64
65 extern CursesStatusBar *statusBar;
66 extern CursesMainScreen *mainScreen;
67
68 extern void hierarchyScreen(void *);
69 extern void mainMenu(void *);
70 extern void editAccountScreen(void *);
71 extern void quitScreen(void *);
72 extern bool isMaildir(string);
73
74 extern char32_t ucplus, ucasterisk;
75
DrawIterator(CursesHierarchy * meArg,bool doEraseArg,bool allArg)76 CursesHierarchy::DrawIterator::DrawIterator(CursesHierarchy *meArg,
77 bool doEraseArg,
78 bool allArg)
79 : me(meArg), doErase(doEraseArg), all(allArg)
80 {
81 }
82
~DrawIterator()83 CursesHierarchy::DrawIterator::~DrawIterator()
84 {
85 }
86
visit(Hierarchy::Folder * f)87 bool CursesHierarchy::DrawIterator::visit(Hierarchy::Folder *f)
88 {
89 bool rc=me->drawErase(f, doErase);
90
91 if (all)
92 rc=true;
93 return rc;
94 }
95
visit(Hierarchy::Server * s)96 bool CursesHierarchy::DrawIterator::visit(Hierarchy::Server *s)
97 {
98 bool rc=me->drawErase(s, doErase);
99
100 if (all)
101 rc=true;
102 return rc;
103 }
104
105 /////////////////////
106
KeyHandlerIterator(CursesHierarchy * meArg,const Curses::Key & keyArg)107 CursesHierarchy::KeyHandlerIterator::KeyHandlerIterator(CursesHierarchy *meArg,
108 const Curses::Key
109 &keyArg)
110 : me(meArg), key(keyArg), consumed(false)
111 {
112 }
113
~KeyHandlerIterator()114 CursesHierarchy::KeyHandlerIterator::~KeyHandlerIterator()
115 {
116 }
117
visit(Hierarchy::Folder * f)118 bool CursesHierarchy::KeyHandlerIterator::visit(Hierarchy::Folder *f)
119 {
120 consumed=me->processKeyInFocus(f, key);
121 return true;
122 }
123
visit(Hierarchy::Server * s)124 bool CursesHierarchy::KeyHandlerIterator::visit(Hierarchy::Server *s)
125 {
126 consumed=me->processKeyInFocus(s, key);
127 return true;
128 }
129
130
131
132
133 /////////////////////
134
135 int CursesHierarchy::currentRowNum=0;
136
CursesHierarchy(Hierarchy * h,CursesContainer * parent)137 CursesHierarchy::CursesHierarchy(Hierarchy *h, CursesContainer *parent) :
138 Curses(parent), HierarchyDisplay(h),
139 CursesKeyHandler(PRI_SCREENHANDLER),
140 selectingFolder(false),
141 folderSelected(NULL),
142 serverSelected(NULL)
143 {
144 h->screenOpened();
145 }
146
~CursesHierarchy()147 CursesHierarchy::~CursesHierarchy()
148 {
149 }
150
drawErase(Hierarchy::Folder * folder,bool doErase)151 bool CursesHierarchy::drawErase(Hierarchy::Folder *folder, bool doErase)
152 {
153 const mail::folder *f=folder->getFolder();
154
155 Hierarchy::Server *s=folder->getParent()->toServer();
156
157 size_t n=folder->getMessageCount();
158 size_t u=folder->getUnreadCount();
159
160 string render1, render2;
161
162 int color1;
163
164 if (s && s->getServer()->server
165 && s->getServer()->server->hasCapability(LIBMAIL_SINGLEFOLDER))
166 {
167 const char *fmt;
168
169 color1=color_fl_folderName.fcolor;
170
171 if (n || u)
172 {
173 fmt=u ? _("%1% messages, %2% unread")
174 : _("%1% messages");
175 }
176 else
177 {
178 fmt=_("*** empty ***");
179 }
180
181 render1=_("Folder: ");
182 render2=Gettext(fmt) << n << u;
183 }
184 else
185 {
186 const char32_t *pfix=f->hasSubFolders() || !f->hasMessages()
187 ? f->hasMessages() ? &ucasterisk:&ucplus:NULL;
188
189 color1=color_fl_folderDirName.fcolor;
190
191 if (pfix == 0)
192 color1=color_fl_folderName.fcolor;
193 else
194 {
195 std::u32string buf;
196
197 buf.push_back(*pfix);
198
199 render1=unicode::iconvert::convert(buf,
200 unicode_default_chset()
201 );
202 render1 += " ";
203 }
204
205 render1 += f->getName();
206 render1 += " ";
207
208 if (n || u)
209 render2 = Gettext( u ?
210 _("(%1% messages, %2% unread)"):
211 _("(%1% messages)")) << n << u;
212 }
213
214 return drawErase(render1, color1, render2,
215 color_fl_messageCount.fcolor,
216 folder, doErase);
217 }
218
drawErase(Hierarchy::Server * server,bool doErase)219 bool CursesHierarchy::drawErase(Hierarchy::Server *server, bool doErase)
220 {
221 string name= Gettext("%1%: ") <<
222 server->getServer()->serverName;
223
224 return drawErase(name,
225 color_fl_accountName.fcolor,
226 server->getServer()->serverDescr,
227 color_fl_accountType.fcolor,
228 server, doErase);
229 }
230
drawErase(string render1,int color1,string render2,int color2,Hierarchy::Entry * e,bool doErase)231 bool CursesHierarchy::drawErase(string render1,
232 int color1,
233 string render2,
234 int color2,
235 Hierarchy::Entry *e,
236 bool doErase)
237 {
238 u32string w1, w2;
239
240 unicode::iconvert::convert(render1, unicode_default_chset(), w1);
241 unicode::iconvert::convert(render2, unicode_default_chset(), w2);
242
243 widecharbuf wcbuf1, wcbuf2;
244
245 wcbuf1.init_unicode(w1.begin(), w1.end());
246 wcbuf2.init_unicode(w2.begin(), w2.end());
247
248 wcbuf1.expandtabs(0);
249 wcbuf2.expandtabs(0);
250
251 w1.clear();
252 w2.clear();
253 wcbuf1.tounicode(w1);
254 wcbuf2.tounicode(w2);
255
256 Curses::CursesAttr attribute1;
257 Curses::CursesAttr attribute2;
258
259 attribute1.setFgColor(color1);
260 attribute2.setFgColor(color2);
261
262 if (doErase)
263 {
264 w1.clear();
265 w1.insert(w1.end(), wcbuf1.wcwidth(0), ' ');
266 w2.clear();
267 w2.insert(w2.end(), wcbuf2.wcwidth(0), ' ');
268 }
269 else if (currentRowNum == e->getRow())
270 {
271 attribute1.setReverse();
272 attribute2.setReverse();
273 }
274
275 int col1=e->getNestingLevel()*2+2;
276
277 bool flag1=writeText(w1, e->getRow(), col1, attribute1);
278
279 col1 += wcbuf1.wcwidth(0);
280
281 bool flag2=writeText(w2, e->getRow(), col1, attribute2);
282
283 return (flag1 && flag2);
284 }
285
286 // Inherited from Curses:
287
getWidth()288 int CursesHierarchy::getWidth() const
289 {
290 return getScreenWidth();
291 }
292
getHeight()293 int CursesHierarchy::getHeight() const
294 {
295 return getHierarchy()->getLastRow()->getRow()+1;
296 }
297
draw()298 void CursesHierarchy::draw()
299 {
300 DrawIterator iterator(this, false, true);
301
302 getHierarchy()->root.prefixIterate(iterator);
303 }
304
draw(Hierarchy::Entry * e,bool doErase)305 void CursesHierarchy::draw(Hierarchy::Entry *e, bool doErase)
306 {
307 DrawIterator iterator(this, doErase, false);
308
309 e->visit(iterator);
310 }
311
isFocusable()312 bool CursesHierarchy::isFocusable()
313 {
314 return true;
315 }
316
317 // Navigate folder hierarchy
318
processKeyInFocus(const Curses::Key & key)319 bool CursesHierarchy::processKeyInFocus(const Curses::Key &key)
320 {
321 Hierarchy::Entry *oldEntry=getHierarchy()->getEntry(currentRowNum);
322
323 if (key == key.UP || key == key.PGUP)
324 {
325 size_t sh;
326 size_t firstShownRow;
327
328 getVerticalViewport(firstShownRow, sh);
329
330 if (sh < 1)
331 sh=1;
332
333 if ((size_t)currentRowNum < sh)
334 sh=currentRowNum;
335 else if (sh > 1)
336 --sh;
337
338 if (key == key.UP)
339 sh=1;
340
341 Hierarchy::Entry *newEntry=NULL;
342 size_t n=currentRowNum;
343
344 while (n > 0)
345 {
346 --n;
347
348 if (sh > 0)
349 --sh;
350
351 Hierarchy::Entry *e=getHierarchy()->getEntry(n);
352
353 if (e != NULL)
354 {
355 newEntry=e;
356 currentRowNum=n;
357 if (sh == 0)
358 break;
359 }
360 }
361
362 if (newEntry)
363 {
364 draw(oldEntry, false);
365 draw(newEntry, false);
366 return true;
367 }
368 else
369 {
370 Curses *p=getParent();
371
372 if (p)
373 p->scrollTo(0);
374 }
375 return true;
376 }
377
378 if (key == key.DOWN || key == key.PGDN)
379 {
380 size_t h=getHeight();
381 size_t n=currentRowNum;
382
383 size_t firstRow, sh;
384
385 getVerticalViewport(firstRow, sh);
386
387 if (sh < 1)
388 sh=1;
389
390 if (firstRow + sh - 1 > (size_t)currentRowNum)
391 sh=firstRow + sh - 1 - currentRowNum;
392 else if (sh > 1)
393 --sh;
394
395 if (key == key.DOWN)
396 sh=1;
397
398 Hierarchy::Entry *newEntry=NULL;
399
400 while (++n < h)
401 {
402 if (sh > 0)
403 --sh;
404
405 Hierarchy::Entry *e=getHierarchy()->getEntry(n);
406
407 if (e != NULL)
408 {
409 newEntry=e;
410 currentRowNum=n;
411 if (sh == 0)
412 break;
413 }
414 }
415
416 if (newEntry)
417 {
418 draw(oldEntry, false);
419 draw(newEntry, false);
420 return true;
421 }
422
423 return true;
424 }
425
426 if (!oldEntry)
427 return false;
428
429 // Over to the other processKeyInFocus() functions...
430
431 KeyHandlerIterator kh(this, key);
432
433 oldEntry->visit(kh);
434 return kh.isConsumed();
435 }
436
processKeyInFocus(Hierarchy::Folder * f,const Curses::Key & key)437 bool CursesHierarchy::processKeyInFocus(Hierarchy::Folder *f,
438 const Curses::Key &key)
439 {
440 if (key == key.ENTER)
441 {
442 if (f->getFolder()->hasMessages())
443 {
444 Hierarchy::Entry *e=f;
445
446 while ((e=e->getParent()) != NULL)
447 {
448 Hierarchy::Server *s=e->toServer();
449
450 if (s != NULL)
451 {
452 if (selectingFolder)
453 {
454 folderSelected=f->getFolder();
455 serverSelected=s->getServer();
456
457 if (folderSelected &&
458 serverSelected)
459 {
460 Curses::keepgoing
461 =false;
462 return true;
463 }
464 folderSelected=NULL;
465 serverSelected=NULL;
466 break;
467 }
468
469 s->getServer()->
470 openFolder(f->getFolder());
471 break;
472 }
473 }
474 }
475 else
476 {
477 if (f->size() > 0)
478 closeSubFolders(f);
479 else
480 openSubFolders(f);
481 }
482 return true;
483 }
484
485 if (key == '+' || key == '=')
486 {
487 if (f->size() == 0)
488 openSubFolders(f);
489 return true;
490 }
491
492 if (key == '-')
493 {
494 if (f->size() > 0)
495 closeSubFolders(f);
496 return true;
497 }
498 return false;
499 }
500
501 // Helper class that returns my Folder object.
502
getCurrentFolder()503 Hierarchy::Folder *CursesHierarchy::getCurrentFolder()
504 {
505 Hierarchy::Entry *e=getHierarchy()->getEntry(currentRowNum);
506
507 if (!e)
508 return NULL;
509
510 return e->toFolder();
511 }
512
513 // Helper class that finds my server object.
514
515 class CursesHierarchyFindServerIterator : public Hierarchy::EntryIterator {
516 public:
517 myServer *server;
518
519 CursesHierarchyFindServerIterator();
520 ~CursesHierarchyFindServerIterator();
521
522 bool visit(Hierarchy::Server *s);
523 static myServer *find(Hierarchy::Entry *e);
524 };
525
CursesHierarchyFindServerIterator()526 CursesHierarchyFindServerIterator::CursesHierarchyFindServerIterator()
527 : server(NULL)
528 {
529 }
530
~CursesHierarchyFindServerIterator()531 CursesHierarchyFindServerIterator::~CursesHierarchyFindServerIterator()
532 {
533 }
534
visit(Hierarchy::Server * s)535 bool CursesHierarchyFindServerIterator::visit(Hierarchy::Server *s)
536 {
537 server=s->getServer();
538 return true;
539 }
540
find(Hierarchy::Entry * e)541 myServer *CursesHierarchyFindServerIterator::find(Hierarchy::Entry *e)
542 {
543 CursesHierarchyFindServerIterator iterator;
544
545 while (e != NULL && iterator.server == NULL)
546 {
547 e->visit(iterator);
548 e=e->getParent();
549 }
550 return iterator.server;
551 }
552
openSubFolders(Hierarchy::Folder * f)553 void CursesHierarchy::openSubFolders(Hierarchy::Folder *f)
554 {
555 myServer *s=CursesHierarchyFindServerIterator::find(f);
556
557 if (!s)
558 return;
559
560 statusBar->clearstatus();
561 statusBar->status(Gettext(_("Opening %1%..."))
562 << f->getFolder()->getName());
563
564 OpenSubFoldersCallback openSubfolders;
565 {
566 myServer::Callback callback;
567 f->getFolder()->readSubFolders(openSubfolders, callback);
568 myServer::eventloop(callback);
569 }
570
571 sort(openSubfolders.folders.begin(),
572 openSubfolders.folders.end(),
573 mail::folder::sort(false));
574
575 vector<mail::folder *>::iterator
576 b=openSubfolders.folders.begin(),
577 e=openSubfolders.folders.end();
578
579 drawEraseBelow(f, true);
580
581 b=openSubfolders.folders.begin();
582 e=openSubfolders.folders.end();
583
584 vector<Hierarchy::Folder *> folders;
585
586 Hierarchy::Server *parentServer=f->getServer();
587
588 set<string> topmostFolders;
589
590 if (parentServer) // Opening top-level folders.
591 {
592 vector<Hierarchy::Entry *>::iterator tb=parentServer->begin(),
593 te=parentServer->end();
594
595 while (tb != te)
596 {
597 Hierarchy::Folder *f= (*tb)->toFolder();
598
599 tb++;
600
601 if (f)
602 topmostFolders.insert(f->getFolder()
603 ->getPath());
604 }
605 }
606
607 while (b != e)
608 {
609 string fid= (*b)->getPath();
610
611 // Do not show folders listed at the topmost level
612
613 if (topmostFolders.count(fid) > 0)
614 {
615 b++;
616 continue;
617 }
618
619 Hierarchy::Folder *child=
620 new Hierarchy::Folder(myServer::hierarchy, f,
621 *b++);
622
623 if (!child)
624 outofmemory();
625 folders.push_back(child);
626 }
627
628 drawEraseBelow(f, false); // Redraw the display below.
629
630 s->openedHierarchy(f, openSubfolders.folders);
631
632 vector<Hierarchy::Folder *>::iterator sb=folders.begin(),
633 se=folders.end();
634
635 while (sb != se)
636 {
637 (*sb++)->updateInfo(s, true); // Get folder sizes
638 }
639 }
640
641 //
642 // Make sure that 'e' is visible. This is usually needed after opening
643 // subfolders. If the parent folder is on the last line of the display then
644 // there's no immediate visual indication that subfolders were opened.
645 // Fix this by temporarily fudging the focus to be on the first opened
646 // subfolder, if its off the screen then getCursorPosition() will scroll it.
647 // Afterwards restore cursor position where it was.
648
visible(Hierarchy::Entry * e)649 void CursesHierarchy::visible(Hierarchy::Entry *e)
650 {
651 Hierarchy::Entry *oldEntry=getHierarchy()->getEntry(currentRowNum);
652
653 if (!oldEntry)
654 return;
655
656 int saveRowNum=currentRowNum;
657
658 int dummyrow;
659 int dummycol;
660
661 currentRowNum=e->getRow();
662 getCursorPosition(dummyrow, dummycol);
663 currentRowNum=saveRowNum;
664 draw( e, false);
665 draw( oldEntry, false);
666 }
667
closeSubFolders(Hierarchy::Folder * f)668 void CursesHierarchy::closeSubFolders(Hierarchy::Folder *f)
669 {
670 drawEraseBelow(f, true);
671 f->clear();
672 drawEraseBelow(f, false);
673 }
674
675 //
676 // Make sure that this server is logged into.
677
autologin(myServer * ss)678 bool CursesHierarchy::autologin(myServer *ss)
679 {
680 if (ss->server)
681 return true;
682
683 string savedPwd;
684
685 if (!PasswordList::passwordList.check(ss->url, savedPwd) ||
686 !ss->login(savedPwd))
687 {
688 ss->disconnect();
689 PasswordList::passwordList.remove(ss->url,
690 _("Login failed"));
691
692 myServerLoginCallback loginCallback;
693
694 if (!ss->login(loginCallback))
695 {
696 ss->disconnect();
697 statusBar->beepError();
698 return false;
699 }
700
701 PasswordList::passwordList.save(ss->url, ss->password);
702 }
703
704 return true;
705 }
706
processKeyInFocus(Hierarchy::Server * s,const Curses::Key & key)707 bool CursesHierarchy::processKeyInFocus(Hierarchy::Server *s,
708 const Curses::Key &key)
709 {
710 if (key == key.ENTER)
711 {
712 bool doShowHierarchy=false;
713
714 //
715 // Automatically open the folder of a SINGLEFOLDER account.
716 // Now, we used to call ss->showHierarchy() immediately
717 // after autologin() returned true. What happened was that
718 // this resulted in a new line being shown on the screen,
719 // that contained the number of messages in the folder.
720 // Hierarchy::Folder::processKeyInFocus() would then get
721 // called next, which opens the folder.
722 //
723 // The initial reaction is to hit enter again, after the
724 // folder size is displayed; however the folder open process
725 // is already kicked off automatically, and is already in
726 // progress.
727 //
728 // To fix this, CursesMainScreen::Lock structure was created
729 // which suppresses screen output, and ss->showHierarchy
730 // is called while the output lock is being held (in the case
731 // where the account is a SINGLEFOLDER account. This looks
732 // much nicer.
733 //
734
735 myServer *ss=s->getServer();
736
737 if (ss->server == NULL ||
738 s->size() == 0)
739 {
740 if (autologin(ss))
741 doShowHierarchy=true;
742 else
743 return true;
744 }
745
746 // Automatically open the folder in single-folder accounts.
747
748 if (ss->server &&
749 ss->server->hasCapability(LIBMAIL_SINGLEFOLDER))
750 {
751 CursesMainScreen::Lock updateLock(mainScreen);
752
753 if (doShowHierarchy)
754 ss->showHierarchy();
755
756 if (ss->server && s->size() == 1)
757 {
758 Hierarchy::Folder *f=(*s->begin())->toFolder();
759
760 if (f)
761 processKeyInFocus(f, key);
762 }
763 }
764 else
765 {
766 if (doShowHierarchy)
767 ss->showHierarchy();
768 }
769
770 return true;
771 }
772
773 return false;
774 }
775
getCursorPosition(int & row,int & col)776 int CursesHierarchy::getCursorPosition(int &row, int &col)
777 {
778 Hierarchy::Entry *e;
779 bool refocus=false;
780
781 // Try to adjust cursor position
782
783 while ((e=getHierarchy()->getEntry(currentRowNum)) == NULL)
784 {
785 if (currentRowNum == 0)
786 {
787 while (currentRowNum < 10)
788 {
789 e=getHierarchy()->getEntry(++currentRowNum);
790 if (e)
791 {
792 refocus=true;
793 break;
794 }
795 }
796 if (e == NULL)
797 currentRowNum=0;
798 break;
799 }
800 --currentRowNum;
801 refocus=true;
802 }
803
804 if (refocus && e)
805 draw(e);
806
807 row=currentRowNum;
808 if (e)
809 {
810 col=e->getNestingLevel() * 2 + 1;
811
812 Curses::getCursorPosition(row, col);
813 return 0;
814 }
815 col=0;
816 Curses::getCursorPosition(row, col);
817 return 0;
818 }
819
820 //
821 // On entry to the curses screen, try to reposition the cursor where we left
822 // off.
823
requestFocus()824 void CursesHierarchy::requestFocus()
825 {
826 Hierarchy::Entry *e=getHierarchy()->getEntry(currentRowNum);
827
828 if (e)
829 {
830 if (e->toFolder() != NULL || e->toServer() != NULL)
831 {
832 Curses::requestFocus();
833 return;
834 }
835 }
836
837 vector<Hierarchy::Entry *>::iterator sb, se, fb, fe;
838
839 sb=myServer::hierarchy.root.begin();
840 se=myServer::hierarchy.root.end();
841
842 while (sb != se)
843 {
844 fb=(*sb)->begin();
845 fe=(*sb)->end();
846
847 while (fb != fe)
848 {
849 currentRowNum=(*fb)->getRow();
850 Curses::requestFocus();
851 return;
852 }
853
854 sb++;
855 }
856
857 currentRowNum=0;
858 Curses::requestFocus();
859 }
860
861 //
862 // This node's children were added or removed. We need to move a portion of
863 // the hierarchy display.
864 //
865
drawEraseBelow(Hierarchy::Entry * e,bool doErase)866 void CursesHierarchy::drawEraseBelow(Hierarchy::Entry *e, bool doErase)
867 {
868 if (doErase)
869 {
870 // When we're about to do something (first step is to erase
871 // the entry) make sure that's where the cursor is.
872
873 currentRowNum=e->getRow();
874 int dummyRow, dummyCol;
875 getCursorPosition(dummyRow, dummyCol); // Perhaps a scroll?
876 }
877 else // After we did something, renumber rows.
878 {
879 Hierarchy::EntryIteratorAssignRow iterator(e->getRow());
880
881 e->prefixIterateAfter(iterator);
882 }
883
884 DrawIterator iterator(this, doErase, false);
885
886 e->prefixIterateAfter( iterator );
887 }
888
889 ///////////////////////////////////////////////////////////////////////////
890
891 class CursesHierarchyAddFolderKeyHandler : public CursesKeyHandler {
892 public:
893 bool addDirectory;
894
895 CursesHierarchyAddFolderKeyHandler();
896 ~CursesHierarchyAddFolderKeyHandler();
897 bool processKey(const Curses::Key &key);
898 bool listKeys( vector< pair<string, string> > &list);
899 };
900
901
902
CursesHierarchyAddFolderKeyHandler()903 CursesHierarchyAddFolderKeyHandler::CursesHierarchyAddFolderKeyHandler()
904 : CursesKeyHandler(PRI_STATUSOVERRIDEHANDLER),
905 addDirectory(false)
906 {
907 }
908
~CursesHierarchyAddFolderKeyHandler()909 CursesHierarchyAddFolderKeyHandler::~CursesHierarchyAddFolderKeyHandler()
910 {
911 }
912
processKey(const Curses::Key & key)913 bool CursesHierarchyAddFolderKeyHandler::processKey(const Curses::Key &key)
914 {
915 if (key == key_ADDDIRECTORY)
916 {
917 addDirectory=true;
918 statusBar->fieldEnter();
919 return true;
920 }
921
922 return false;
923 }
924
925 bool CursesHierarchyAddFolderKeyHandler
listKeys(vector<pair<string,string>> & list)926 ::listKeys(vector< pair<string, string> > &list)
927 {
928 list.push_back( make_pair(Gettext::
929 keyname(_("ADDDIRECTORY:^D")),
930 _("Add Folder Directory")));
931 return true;
932 }
933
934 ///////////
935 //
936 // Helper class to reverify folder's existence after a DELETE
937
938
939 class CursesHierarchyDeleteFolderVerifyCallback :
940 public mail::callback::folderList {
941
942 CursesHierarchy *me;
943 Hierarchy::Folder *parent, *deleted;
944
945 public:
946 CursesHierarchyDeleteFolderVerifyCallback(CursesHierarchy *meArg,
947 Hierarchy::Folder *parentArg,
948 Hierarchy::Folder
949 *deletedArg);
950 ~CursesHierarchyDeleteFolderVerifyCallback();
951 void success(const vector<const mail::folder *> &folders);
952 };
953
954 CursesHierarchyDeleteFolderVerifyCallback::
CursesHierarchyDeleteFolderVerifyCallback(CursesHierarchy * meArg,Hierarchy::Folder * parentArg,Hierarchy::Folder * deletedArg)955 CursesHierarchyDeleteFolderVerifyCallback(CursesHierarchy *meArg,
956 Hierarchy::Folder *parentArg,
957 Hierarchy::Folder *deletedArg)
958 : me(meArg), parent(parentArg), deleted(deletedArg)
959 {
960 }
961
962 CursesHierarchyDeleteFolderVerifyCallback::
~CursesHierarchyDeleteFolderVerifyCallback()963 ~CursesHierarchyDeleteFolderVerifyCallback()
964 {
965 }
966
967 void CursesHierarchyDeleteFolderVerifyCallback
success(const vector<const mail::folder * > & folders)968 ::success(const vector<const mail::folder *> &folders)
969 {
970 vector<const mail::folder *>::const_iterator b=folders.begin(),
971 e=folders.end();
972
973 const mail::folder *updatedFolder=NULL;
974
975 while (b != e)
976 {
977 const mail::folder *f= *b++;
978 if ( f->getName() ==
979 deleted->getFolder()->getName())
980 {
981 updatedFolder=f;
982 break;
983 }
984 }
985
986 me->processDeletedFolder(parent, deleted, updatedFolder);
987 }
988
visit(Hierarchy::Folder * f)989 bool CursesHierarchy::RefreshIterator::visit(Hierarchy::Folder* f)
990 {
991 if(f != NULL && this->cs != NULL)
992 f->updateInfo(this->cs, true);
993 return true;
994 }
995
visit(Hierarchy::Server * s)996 bool CursesHierarchy::RefreshIterator::visit(Hierarchy::Server* s)
997 {
998 if(s != NULL)
999 this->cs = s->getServer();
1000 return true;
1001 }
1002
refreshAllFolders()1003 void CursesHierarchy::refreshAllFolders()
1004 {
1005 RefreshIterator iterator;
1006 getHierarchy()->root.prefixIterate(iterator);
1007 }
1008
1009 ///////////
processKey(const Curses::Key & key)1010 bool CursesHierarchy::processKey(const Curses::Key &key)
1011 {
1012 if (selectingFolder)
1013 {
1014 if (key == key_ABORT)
1015 {
1016 folderSelected=NULL;
1017 Curses::keepgoing=false;
1018 return true;
1019 }
1020
1021 return false;
1022 }
1023
1024 if (key == key_ADDFOLDER)
1025 {
1026 bool adddir;
1027 string folder;
1028
1029 Hierarchy::Folder *f=getCurrentFolder();
1030
1031 myServer *serverPtr;
1032
1033 if (f == NULL) // Add shortcut to a server manually.
1034 {
1035 Hierarchy::Entry *e=getHierarchy()
1036 ->getEntry(currentRowNum);
1037
1038 if (!e || !e->toServer())
1039 return true;
1040
1041 myServer::promptInfo response=myServer
1042 ::prompt(myServer::
1043 promptInfo(Gettext(_("Add folder/directory in %1%: "))
1044 << (serverPtr=e->toServer()
1045 ->getServer())
1046 ->serverName));
1047 if (response.aborted() || serverPtr->server == NULL)
1048 return true;
1049
1050 string path=response;
1051
1052 myServer *ms=e->toServer()->getServer();
1053
1054 if (ms->server == NULL && !autologin(ms))
1055 return true;
1056
1057 if (path.size() > 0)
1058 {
1059 path=ms->server->translatePath(path);
1060
1061 if (path.size() == 0)
1062 {
1063 statusBar->status(Gettext(_("Invalid folder name: %s"))
1064 << strerror(errno));
1065 statusBar->beepError();
1066 return true;
1067 }
1068 }
1069
1070 myServer::Callback callback;
1071 myServer::CreateFolderCallback searchCallback;
1072
1073 ms->server->findFolder(path,
1074 searchCallback, callback);
1075
1076 if (!myServer::eventloop(callback))
1077 return true;
1078
1079 if (searchCallback.folderFound == NULL)
1080 {
1081 statusBar->status(_("Folder not found."));
1082 statusBar->beepError();
1083 return true;
1084 }
1085
1086 if (searchCallback.folderFound->hasMessages() &&
1087 searchCallback.folderFound->hasSubFolders())
1088 {
1089 response=
1090 myServer
1091 ::prompt(myServer::
1092 promptInfo(Gettext(_("Add %1% as a folder directory? (Y/N) "))
1093 << (string)response
1094 ).yesno());
1095
1096 if (response.aborted()
1097 || serverPtr->server == NULL)
1098 return true;
1099
1100 if ((string)response == "Y")
1101 searchCallback.folderFound
1102 ->hasMessages(false);
1103 else
1104 searchCallback.folderFound
1105 ->hasSubFolders(false);
1106
1107 }
1108 drawEraseBelow(ms->hierarchyEntry, true);
1109
1110 Hierarchy::Folder *hf=
1111 new Hierarchy::Folder(e->getHierarchy(),
1112 ms->hierarchyEntry,
1113 searchCallback
1114 .folderFound);
1115
1116 if (!hf)
1117 outofmemory();
1118
1119 drawEraseBelow(ms->hierarchyEntry, false);
1120 ms->updateHierarchy();
1121 hf->updateInfo(ms, false);
1122 return true;
1123 }
1124 else
1125 {
1126 Hierarchy::Server *s=f->getServer();
1127
1128 if (!s || !(serverPtr=s->getServer()))
1129 return true;
1130 }
1131
1132 if (!f->getFolder()->hasSubFolders() &&
1133 f->size() == 0)
1134 {
1135 // "A" on a top-level folder (not a folder directory)
1136 // is a noop.
1137
1138 Hierarchy::Entry *e=f->getParent();
1139
1140 if (e == NULL || (f=e->toFolder()) == NULL)
1141 return true;
1142 }
1143
1144 {
1145 CursesHierarchyAddFolderKeyHandler add_key_handler;
1146
1147 myServer::promptInfo response=
1148 myServer
1149 ::prompt(myServer
1150 ::promptInfo(Gettext(_("Create new folder in %1%: "))
1151 << f->getFolder()->getName()
1152 ));
1153
1154 if (response.aborted() || serverPtr->server == NULL)
1155 return true;
1156 folder=response;
1157 adddir=add_key_handler.addDirectory;
1158 }
1159
1160 if (adddir)
1161 {
1162 myServer::promptInfo response=
1163 myServer
1164 ::prompt(myServer
1165 ::promptInfo(Gettext(_("Create new folder directory in %1%: "))
1166 << f->getFolder()->getName()
1167 )
1168 .initialValue(folder));
1169
1170 if (response.aborted() || serverPtr->server == NULL)
1171 return true;
1172 folder=response;
1173 }
1174
1175 if (folder.size() == 0)
1176 return true;
1177
1178 if (f->size() == 0) // Make sure it's open now...
1179 openSubFolders(f);
1180
1181 statusBar->clearstatus();
1182
1183 // If this folder already exists, we must now be creating
1184 // a folder that both has subfolders, and messages.
1185 // Search for an existing folder with the same name.
1186
1187 vector<Hierarchy::Entry *>::iterator b=f->begin(), e=f->end();
1188
1189 Hierarchy::Folder *oldFolder=NULL;
1190
1191 bool hasSubfolders=false;
1192 bool hasMessages=false;
1193
1194 while (b != e)
1195 {
1196 Hierarchy::Entry *entryPtr= *b++;
1197
1198 Hierarchy::Folder *p= entryPtr->toFolder();
1199
1200 if (p != NULL &&
1201 folder == p->getFolder()->getName())
1202 {
1203 oldFolder=p;
1204 hasMessages=p->getFolder()->hasMessages();
1205 hasSubfolders=p->getFolder()->hasSubFolders();
1206 break;
1207 }
1208 }
1209
1210 if (adddir)
1211 hasSubfolders=true;
1212 else
1213 hasMessages=true;
1214
1215
1216 myServer::Callback callback;
1217 myServer::CreateFolderCallback newsubfolder;
1218
1219 f->getFolder()->createSubFolder(folder, adddir,
1220 newsubfolder, callback);
1221
1222 if (!myServer::eventloop(callback)
1223 || newsubfolder.folderFound == NULL)
1224 return true;
1225
1226 mail::folder *newFolder=newsubfolder.folderFound;
1227
1228 // Now, update the hierarchy tree.
1229
1230 newFolder->hasMessages(hasMessages);
1231 newFolder->hasSubFolders(hasSubfolders);
1232
1233 drawEraseBelow(f, true);
1234
1235 Hierarchy::Folder *addFolder=
1236 new Hierarchy::Folder(f->getHierarchy(),
1237 f, newFolder);
1238
1239 if (!addFolder)
1240 outofmemory();
1241
1242 if (oldFolder)
1243 {
1244 addFolder->setMessageCount(oldFolder->
1245 getMessageCount());
1246 addFolder->setUnreadCount(oldFolder->
1247 getUnreadCount());
1248
1249 delete oldFolder;
1250 }
1251
1252 f->resortChildren();
1253 drawEraseBelow(f, false);
1254
1255 Hierarchy::Entry *oldCursor
1256 =getHierarchy()->getEntry(currentRowNum);
1257
1258 currentRowNum=addFolder->getRow();
1259
1260 if (oldCursor)
1261 draw(oldCursor, false);
1262 draw(addFolder, false);
1263
1264 return true;
1265 }
1266
1267 if (key == key_RENAME)
1268 {
1269 Hierarchy::Folder *oldFolder=getCurrentFolder(), *parent;
1270 Hierarchy::Entry *parentEntry;
1271
1272 // Cannot rename top level folders.
1273 if (!oldFolder || !(parentEntry=oldFolder->getParent()) ||
1274 !(parent=parentEntry->toFolder()))
1275 {
1276 statusBar->status("Cannot rename this folder.");
1277 statusBar->beepError();
1278 return true;
1279 }
1280
1281 Hierarchy::Server *s=oldFolder->getServer();
1282 myServer *serverPtr;
1283
1284 if (!s || !(serverPtr=s->getServer()))
1285 return true;
1286
1287 myServer::promptInfo response=
1288 myServer
1289 ::prompt(myServer::
1290 promptInfo(_("New name: ")));
1291
1292 if (response.aborted() || serverPtr->server == NULL)
1293 return true;
1294
1295 string newname=(string)response;
1296
1297 if (newname.size() == 0)
1298 return true;
1299
1300 myServer::Callback callback;
1301 myServer::CreateFolderCallback newsubfolder;
1302
1303 oldFolder->getFolder()->renameFolder(parent->getFolder(),
1304 newname,
1305 newsubfolder, callback);
1306
1307 if (!myServer::eventloop(callback)
1308 || newsubfolder.folderFound == NULL)
1309 return true;
1310
1311 Hierarchy::Entry *oldCursor
1312 =getHierarchy()->getEntry(currentRowNum);
1313
1314 drawEraseBelow(parentEntry, true);
1315
1316 if (oldCursor)
1317 draw(oldCursor, true);
1318
1319 Hierarchy::Folder *addFolder=
1320 new Hierarchy::Folder(parentEntry->getHierarchy(),
1321 parentEntry,
1322 newsubfolder.folderFound);
1323
1324 delete oldFolder;
1325
1326 parent->resortChildren();
1327 drawEraseBelow(parent, false);
1328
1329 oldCursor=getHierarchy()->getEntry(currentRowNum);
1330
1331 currentRowNum=addFolder->getRow();
1332
1333 draw(addFolder, false);
1334 if (oldCursor)
1335 draw(oldCursor, false);
1336
1337 return true;
1338 }
1339
1340 if (key == key_PERMS)
1341 {
1342 Hierarchy::Folder *currentFolder=getCurrentFolder();
1343 Hierarchy::Server *s;
1344
1345 if (!currentFolder ||
1346 !(s=currentFolder->getServer()))
1347 return true;
1348
1349 myServer::promptInfo prompt=
1350 myServer::promptInfo(_("Permissions: (V/E) ")
1351 )
1352 .option(key_PERMVIEW,
1353 _("V"),
1354 _("View permissions"))
1355 .option(key_PERMEDIT,
1356 _("E"),
1357 _("Edit permissions"));
1358
1359 prompt=myServer::prompt(prompt);
1360
1361 if (prompt.abortflag)
1362 {
1363 return true;
1364 }
1365
1366 currentFolder=getCurrentFolder();
1367
1368 if (!currentFolder ||
1369 !(s=currentFolder->getServer()))
1370 return true;
1371
1372 u32string ka;
1373
1374 unicode::iconvert::convert((string)prompt,
1375 unicode_default_chset(), ka);
1376
1377 if (ka.size() == 0)
1378 return true;
1379
1380 if (key_PERMVIEW == ka[0])
1381 {
1382 statusBar->clearstatus();
1383 statusBar->status(_("Checking folder permissions..."));
1384
1385 string perms;
1386 myServer::Callback callback;
1387
1388 currentFolder->getFolder()->
1389 getMyRights(callback, perms);
1390
1391 if (!myServer::eventloop(callback))
1392 return true;
1393
1394 string permsVerbose="";
1395 size_t i;
1396
1397 for (i=0; i<perms.size(); i++)
1398 {
1399 if (permsVerbose.size() > 0)
1400 permsVerbose += ", ";
1401 permsVerbose += Acl(perms.substr(i, 1))
1402 .getDescription();
1403 }
1404 if (permsVerbose.size() == 0)
1405 permsVerbose=_("No permissions");
1406 statusBar->clearstatus();
1407 statusBar->
1408 status(Gettext(_("You have the following "
1409 "permissions on this folder:"
1410 "\n\n%1%."))
1411 << permsVerbose);
1412 return true;
1413 }
1414
1415 if (key_PERMEDIT == ka[0])
1416 {
1417 Curses::keepgoing=false;
1418 myServer::nextScreen=&Acl::editScreen;
1419 myServer::nextScreenArg=currentFolder;
1420 return true;
1421 }
1422 return true;
1423 }
1424
1425 if (key == key_RESET)
1426 {
1427 const char * const fldnames[]=
1428 {"FROM", "REPLY-TO", "FCC", "CUSTOM", "SIGNKEY", NULL};
1429
1430 Hierarchy::Folder *currentFolder=getCurrentFolder();
1431 Hierarchy::Server *s;
1432
1433 if (!currentFolder ||
1434 !(s=currentFolder->getServer()))
1435
1436 {
1437 statusBar->clearstatus();
1438 statusBar->status(_("This folder does not contain"
1439 " messages."));
1440 statusBar->beepError();
1441 return true;
1442 }
1443
1444 myServer::promptInfo prompt=
1445 myServer::promptInfo(Gettext(_("Reset memorized "
1446 "headers for %1%? "
1447 "(Y/N) ")) <<
1448 currentFolder->getFolder()->
1449 getName()).yesno();
1450
1451 prompt=myServer::prompt(prompt);
1452
1453 if (prompt.abortflag || (string)prompt != "Y" ||
1454 myServer::nextScreen)
1455 return true;
1456
1457 size_t i=0;
1458
1459 for (i=0; fldnames[i]; i++)
1460 s->getServer()->
1461 updateFolderConfiguration(currentFolder
1462 ->getFolder(),
1463 fldnames[i], "");
1464 myServer::saveconfig();
1465
1466 statusBar->clearstatus();
1467 statusBar->status(_("Memorized headers cleared."));
1468 return true;
1469 }
1470
1471
1472 if (key == key_DELFOLDER)
1473 {
1474 Hierarchy::Entry *e=getHierarchy()->getEntry(currentRowNum);
1475
1476 Hierarchy::Server *server;
1477
1478 myServer *serverPtr;
1479
1480 if (e && (server=e->toServer()) != NULL) // Deleting server
1481 {
1482 serverPtr=server->getServer();
1483
1484 if (!serverPtr)
1485 return true;
1486
1487 if (myServer::server_list.size() <= 1)
1488 {
1489 statusBar->clearstatus();
1490 statusBar->status(_("Cannot remove the only mail account."));
1491 statusBar->beepError();
1492 return true;
1493 }
1494
1495 myServer::promptInfo response=
1496 myServer
1497 ::prompt(myServer::
1498 promptInfo(Gettext(_("Remove account %1%? (Y/N) "))
1499 << server->getServer()
1500 ->serverName).yesno());
1501
1502 if ((string)response == "Y")
1503 {
1504 Hierarchy::Entry *parent=e->getParent();
1505
1506 if (parent)
1507 {
1508 // Find the previous server entry.
1509
1510 vector<Hierarchy::Entry *>::iterator
1511 ce=parent->begin(),
1512 cb=parent->end();
1513
1514 Hierarchy::Entry *erasePtr=NULL;
1515 Hierarchy::Entry *drawPtr=NULL;
1516
1517 while (ce != cb)
1518 {
1519 Hierarchy::Entry *ptr=*ce++;
1520
1521 if (ptr == e)
1522 break;
1523 erasePtr=drawPtr=ptr;
1524 }
1525
1526 if (!erasePtr)
1527 {
1528 erasePtr=e;
1529
1530 ce=parent->begin();
1531 cb=parent->end();
1532
1533 while (ce != cb)
1534 {
1535 Hierarchy::Entry
1536 *ptr=*ce++;
1537
1538 if (ptr == e)
1539 continue;
1540 drawPtr=ptr;
1541 break;
1542 }
1543 }
1544
1545 string auxfile=serverPtr->newsrc;
1546
1547 serverPtr->disconnect();
1548
1549 CursesMainScreen::Lock
1550 logoutLock(mainScreen);
1551 int rowSave=erasePtr->getRow();
1552
1553 // If nothing else has this URL,
1554 // delete all special folders for
1555 // this URL
1556
1557 vector<myServer *>::iterator
1558 b=myServer::server_list
1559 .begin(),
1560 e=myServer::server_list.end();
1561 size_t cnt=0;
1562
1563 while (b != e)
1564 {
1565 if ((*b)->url==serverPtr->url)
1566 ++cnt;
1567 b++;
1568 }
1569
1570 if (cnt < 2)
1571 SpecialFolder
1572 ::deleteAccount(serverPtr
1573 ->url);
1574
1575 drawEraseBelow(erasePtr, true);
1576 PasswordList::passwordList.remove
1577 (serverPtr->url, "");
1578 bool wasRemote=mail::account
1579 ::isRemoteUrl(serverPtr->url);
1580 delete server;
1581 delete serverPtr;
1582 myServer::saveconfig(wasRemote);
1583 drawPtr->setRow(rowSave);
1584 drawEraseBelow(drawPtr, false);
1585
1586 if (auxfile.size() > 0)
1587 {
1588 if (unlink(auxfile.c_str()) < 0
1589 && errno == EISDIR)
1590 {
1591 mail::maildir::
1592 maildirdestroy
1593 (auxfile);
1594 }
1595 }
1596 }
1597 }
1598 return true;
1599 }
1600
1601 Hierarchy::Folder *f=getCurrentFolder();
1602
1603 if (f == NULL || (server=f->getServer()) == NULL ||
1604 (serverPtr=server->getServer()) == NULL)
1605 return true;
1606
1607 e=f->getParent();
1608
1609 Hierarchy::Folder *parent;
1610
1611 if (e && (server=e->toServer()))
1612 {
1613 myServer::promptInfo response=
1614 myServer
1615 ::prompt(myServer::
1616 promptInfo(Gettext(_("Remove %1% from %2%? (Y/N) "))
1617 << f->getFolder()
1618 ->getName()
1619 << server->getServer()
1620 ->serverName).yesno());
1621
1622 if ((string)response == "Y" && serverPtr->server)
1623 {
1624 drawEraseBelow(server, true);
1625 delete f;
1626 drawEraseBelow(server, false);
1627 server->getServer()->updateHierarchy();
1628 }
1629 statusBar->clearstatus();
1630 return true;
1631 }
1632
1633 if (e == NULL || (parent=e->toFolder()) == NULL)
1634 return true;
1635
1636 Hierarchy::Folder *deletedFolder=NULL;
1637
1638 if (f->getFolder()->hasSubFolders() ||
1639 !f->getFolder()->hasMessages())
1640 {
1641 myServer::promptInfo response=
1642 myServer
1643 ::prompt(myServer
1644 ::promptInfo(Gettext(_("Delete folder directory %1%? (Y/N) "))
1645 << f->getFolder()->getName()
1646 )
1647 .yesno());
1648
1649 if (!serverPtr->server)
1650 return true;
1651
1652 if ((string)response == "Y")
1653 {
1654 statusBar->clearstatus();
1655 myServer::Callback callback;
1656
1657 f->getFolder()->destroy(callback, true);
1658 if (!myServer::eventloop(callback))
1659 return true;
1660 deletedFolder=f;
1661 }
1662 }
1663
1664 if (!deletedFolder && f->getFolder()->hasMessages())
1665 {
1666 myServer::promptInfo response=
1667 myServer
1668 ::prompt(myServer
1669 ::promptInfo(Gettext(_("Delete folder %1%? (Y/N) "))
1670 << f->getFolder()->getName()
1671 )
1672 .yesno());
1673
1674 if (!serverPtr->server)
1675 return true;
1676
1677 if ((string)response == "Y")
1678 {
1679 statusBar->clearstatus();
1680 myServer::Callback callback;
1681
1682 f->getFolder()->destroy(callback, false);
1683 if (!myServer::eventloop(callback))
1684 return true;
1685 deletedFolder=f;
1686 }
1687 }
1688
1689 if (!deletedFolder)
1690 return true;
1691
1692 myServer::Callback callback;
1693 CursesHierarchyDeleteFolderVerifyCallback
1694 verify(this, parent, deletedFolder);
1695
1696 parent->getFolder()->readSubFolders(verify, callback);
1697
1698 myServer::eventloop(callback);
1699 return true;
1700 }
1701
1702 if (key == key_FILTER)
1703 {
1704 Hierarchy::Folder *f=getCurrentFolder();
1705
1706 if (f)
1707 {
1708 Curses::keepgoing=false;
1709 myServer::nextScreen=&Filter::filterEditScreen;
1710 myServer::nextScreenArg=f;
1711 return true;
1712 }
1713
1714 statusBar->clearstatus();
1715 statusBar->status(_("Only a folder may be filtered."));
1716 statusBar->beepError();
1717 return true;
1718 }
1719
1720 if (key == key_UP) // CTRL-U
1721 {
1722 Hierarchy::Folder *f=getCurrentFolder();
1723 Hierarchy::Entry *e;
1724
1725 if (f)
1726 {
1727 Hierarchy::Entry *e=f->getParent();
1728
1729 if (e && e->toServer())
1730 {
1731 f->MoveUp();
1732 e->toServer()->getServer()->updateHierarchy();
1733 return true;
1734 }
1735 }
1736 else if ((e=getHierarchy()->getEntry(currentRowNum)) != NULL
1737 && e->toServer())
1738 {
1739 myServer *ms=e->toServer()->getServer();
1740
1741 vector<myServer *>::iterator msl=
1742 find(myServer::server_list.begin(),
1743 myServer::server_list.end(), ms);
1744
1745 if (msl != myServer::server_list.end() &&
1746 msl != myServer::server_list.begin())
1747 {
1748 // Move the server structures
1749
1750 vector<myServer *>::iterator msp=msl;
1751
1752 --msp;
1753
1754 myServer *swap= *msp;
1755
1756 *msp=*msl;
1757
1758 *msl=swap;
1759
1760 // Move the stuff on screen.
1761
1762 ms->hierarchyEntry->MoveUp();
1763 myServer::saveconfig();
1764 }
1765 return true;
1766 }
1767 statusBar->clearstatus();
1768 statusBar->status(_("Only top level folders or servers may be rearranged."));
1769 statusBar->beepError();
1770 return true;
1771 }
1772
1773 if (key == key_DOWN) // CTRL-D
1774 {
1775 Hierarchy::Folder *f=getCurrentFolder();
1776 Hierarchy::Entry *e;
1777
1778 if (f)
1779 {
1780 Hierarchy::Entry *e=f->getParent();
1781
1782 if (e && e->toServer())
1783 {
1784 f->MoveDown();
1785 e->toServer()->getServer()->updateHierarchy();
1786
1787 Hierarchy::Folder *ff=getCurrentFolder();
1788
1789 // Follow the cursor
1790 currentRowNum=f->getRow();
1791 drawErase(f, false);
1792 drawErase(ff, false);
1793 return true;
1794 }
1795 }
1796 else if ((e=getHierarchy()->getEntry(currentRowNum)) != NULL
1797 && e->toServer())
1798 {
1799 myServer *ms=e->toServer()->getServer();
1800
1801 vector<myServer *>::iterator msl=
1802 find(myServer::server_list.begin(),
1803 myServer::server_list.end(), ms);
1804
1805 if (msl != myServer::server_list.end())
1806 {
1807 // Move the server structure
1808
1809 vector<myServer *>::iterator msp=msl;
1810
1811 msp++;
1812
1813 if (msp != myServer::server_list.end())
1814 {
1815 myServer *swap= *msp;
1816
1817 *msp=*msl;
1818
1819 *msl=swap;
1820
1821 // Move what's on the screen.
1822 ms->hierarchyEntry->MoveDown();
1823
1824 currentRowNum=ms->hierarchyEntry
1825 ->getRow();
1826
1827 drawErase(swap->hierarchyEntry, false);
1828 drawErase(ms->hierarchyEntry, false);
1829 myServer::saveconfig();
1830 }
1831 }
1832 return true;
1833 }
1834 statusBar->clearstatus();
1835 statusBar->status(_("Only top level folders or servers may be rearranged."));
1836 statusBar->beepError();
1837 return true;
1838
1839 }
1840
1841 if (key == key_EDIT)
1842 {
1843 Hierarchy::Entry *e=getHierarchy()->getEntry(currentRowNum);
1844
1845 if (!e)
1846 return true;
1847
1848 Hierarchy::Server *s=e->toServer();
1849
1850 if (!s)
1851 {
1852 statusBar->clearstatus();
1853 statusBar->status(_("Current selection not an account."));
1854 statusBar->beepError();
1855 return true;
1856 }
1857
1858 string url=s->getServer()->url;
1859
1860 {
1861 mail::loginInfo loginInfo;
1862
1863 if (mail::loginUrlDecode(url, loginInfo) &&
1864 (loginInfo.method == "imap" ||
1865 loginInfo.method == "imaps" ||
1866 loginInfo.method == "pop3" ||
1867 loginInfo.method == "pop3s" ||
1868 loginInfo.method == "pop3maildrop" ||
1869 loginInfo.method == "pop3maildrops" ||
1870 loginInfo.method == "nntp" ||
1871 loginInfo.method == "nntps"))
1872 {
1873 myServer::nextScreen= &editAccountScreen;
1874 myServer::nextScreenArg=s->getServer();
1875 Curses::keepgoing=false;
1876 return true;
1877 }
1878 }
1879 statusBar->clearstatus();
1880 statusBar->status(_("This is a local mail account. "
1881 "Create another local account from "
1882 "the main menu."));
1883 statusBar->beepError();
1884 return true;
1885 }
1886
1887 if (key == key_SPECIAL)
1888 {
1889 Hierarchy::Folder *f=getCurrentFolder();
1890
1891 if (!f)
1892 {
1893 statusBar->clearstatus();
1894 statusBar->status(_("Current selection not a folder."));
1895 statusBar->beepError();
1896 return true;
1897 }
1898
1899 myServer *serverPtr=CursesHierarchyFindServerIterator::find(f);
1900
1901 if (!serverPtr)
1902 return true;
1903
1904 mail::folder *folder=f->getFolder()->clone();
1905
1906 if (!folder)
1907 outofmemory();
1908
1909 try {
1910 myServer::promptInfo prompt=
1911 myServer::prompt(myServer::promptInfo(_("Use folder as: "))
1912 .option(key_ADDRESSBOOK,
1913 Gettext::keyname(_("ADDRESSBOOK_K:A")),
1914 _("Address Book"))
1915 .option(key_DRAFTS,
1916 Gettext::keyname(_("DRAFTS_K:D")),
1917 _("Drafts"))
1918 .option(key_SENT,
1919 Gettext::keyname(_("SENT_K:S")),
1920 _("Sent mail"))
1921 .option(key_TOP,
1922 Gettext::keyname(_("TOP_K:T")),
1923 _("Top level folder"))
1924 .option(key_REMOTE,
1925 Gettext::keyname(_("REMOTE_K:R")),
1926 _("Remote configuration"))
1927 );
1928
1929 if (prompt.abortflag)
1930 {
1931 delete folder;
1932 return true;
1933 }
1934
1935 u32string ka;
1936
1937 unicode::iconvert::convert(((string)prompt),
1938 unicode_default_chset(),
1939 ka);
1940
1941 if (ka.size() == 0 || !serverPtr->server)
1942 {
1943 delete folder;
1944 return true;
1945 }
1946
1947 if (key_ADDRESSBOOK == ka[0])
1948 {
1949 myServer::promptInfo prompt=
1950 myServer::prompt(myServer::promptInfo(_("Name of this address book: "))
1951 .initialValue(Gettext(_("%1% on %2%")) <<
1952 folder->getName() << serverPtr->serverName)
1953 );
1954
1955 string name=prompt;
1956
1957 if (prompt.abortflag || name.size() == 0)
1958 {
1959 delete folder;
1960 return true;
1961 }
1962
1963 AddressBook *abook=
1964 new AddressBook();
1965
1966 if (!abook)
1967 outofmemory();
1968
1969 try {
1970 abook->init(name,
1971 serverPtr->url,
1972 folder->toString());
1973
1974 AddressBook::addressBookList
1975 .insert(AddressBook::
1976 addressBookList.end(),
1977 abook);
1978 } catch (...) {
1979 delete abook;
1980 LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
1981 }
1982
1983 delete folder;
1984 myServer::saveconfig();
1985 statusBar->clearstatus();
1986 statusBar->status(_("Address Book created."));
1987 return true;
1988
1989 }
1990
1991 if (key_TOP == ka[0])
1992 {
1993 serverPtr->addTopLevelFolderRedraw(folder->
1994 toString());
1995 delete folder;
1996 return true;
1997 }
1998
1999 if (!folder->hasMessages())
2000 {
2001 statusBar->clearstatus();
2002 statusBar->status(_("This folder cannot store messages, only subfolders."));
2003 statusBar->beepError();
2004 delete folder;
2005 return true;
2006 }
2007
2008 if (key_DRAFTS == ka[0])
2009 {
2010 SpecialFolder &f=SpecialFolder::folders
2011 .find(DRAFTS)->second;
2012
2013 f.setSingleFolder(serverPtr->url,
2014 folder->toString());
2015 }
2016
2017 if (key_SENT == ka[0])
2018 {
2019 myServer::promptInfo prompt=
2020 myServer::prompt(myServer::promptInfo(_("Nickname for this sent mail folder: "))
2021 .initialValue(Gettext(_("%1% in %2%"))
2022 << folder->getName()
2023 << serverPtr->serverName));
2024
2025 string nameStr=prompt;
2026
2027 if (prompt.abortflag || nameStr.size() == 0)
2028 {
2029 delete folder;
2030 return true;
2031 }
2032
2033
2034 SpecialFolder &f=SpecialFolder::folders
2035 .find(SENT)->second;
2036
2037 nameStr=Gettext::toutf8(nameStr);
2038
2039 if (f.findFolder(nameStr))
2040 {
2041 statusBar->clearstatus();
2042 statusBar->status(_("This nickname already exists."));
2043 statusBar->beepError();
2044 delete folder;
2045 return true;
2046 }
2047
2048 f.addFolder(nameStr, serverPtr->url,
2049 folder->toString());
2050 }
2051
2052 if (key_REMOTE == ka[0])
2053 {
2054 myServer::promptInfo prompt=
2055 myServer::prompt(myServer::promptInfo(_("Import remote configuration (requires restart)? (Y/N): "))
2056 .yesno());
2057
2058
2059 if (prompt.abortflag)
2060 {
2061 delete folder;
2062 return true;
2063 }
2064
2065 myServer::remoteConfigURL=serverPtr->url;
2066 myServer::remoteConfigPassword=
2067 serverPtr->password;
2068 myServer::remoteConfigFolder=folder->getPath();
2069
2070 if (myServer::remoteConfigAccount)
2071 {
2072 myServer::remoteConfigAccount
2073 ->logout();
2074 delete myServer::remoteConfigAccount;
2075 myServer::remoteConfigAccount=NULL;
2076 }
2077
2078 if ((myServer::remoteConfigAccount=
2079 new myServer::remoteConfig()) == NULL)
2080 {
2081 LIBMAIL_THROW((strerror(errno)));
2082 }
2083
2084 if ((string)prompt == "Y")
2085 {
2086 myServer::saveconfig(false, true);
2087
2088 try {
2089 quitScreen(NULL);
2090 } catch (...) {
2091 }
2092
2093 throw "RESET";
2094 }
2095 }
2096 myServer::saveconfig(true);
2097 delete folder;
2098 } catch (...) {
2099 delete folder;
2100 LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
2101 }
2102 return true;
2103 }
2104
2105 Hierarchy::Entry *e=getHierarchy()
2106 ->getEntry(currentRowNum);
2107
2108 myServer *s=e ? CursesHierarchyFindServerIterator::find(e):NULL;
2109
2110 if (GlobalKeys::processKey(key, GlobalKeys::LISTFOLDERS, s))
2111 return true;
2112
2113 return false;
2114 }
2115
2116 //
2117 // Folder's been deleted, supposedly, and we've listed the parent hierarchy.
2118 // Figure out how to update the display.
2119 //
2120
processDeletedFolder(Hierarchy::Folder * parent,Hierarchy::Folder * deleted,const mail::folder * updated)2121 void CursesHierarchy::processDeletedFolder(Hierarchy::Folder *parent,
2122 Hierarchy::Folder *deleted,
2123 const mail::folder *updated)
2124 {
2125 drawEraseBelow(parent, true);
2126
2127 if (updated) // Folder still exists
2128 deleted->updateFolder(updated);
2129 else
2130 {
2131 delete deleted;
2132 deleted=NULL;
2133 }
2134
2135 parent->resortChildren();
2136 drawEraseBelow(parent, false);
2137
2138 Hierarchy::Entry *oldCursor
2139 =getHierarchy()->getEntry(currentRowNum);
2140
2141 if (deleted == NULL)
2142 deleted=parent;
2143
2144 currentRowNum=deleted->getRow();
2145
2146 if (oldCursor)
2147 draw(oldCursor, false);
2148 draw(deleted, false);
2149 }
2150
listKeys(vector<pair<string,string>> & list)2151 bool CursesHierarchy::listKeys( vector< pair<string, string> > &list)
2152 {
2153 list.push_back( make_pair(Gettext::keyname(_("HIER_K:+/-")),
2154 _("Subfolders")));
2155
2156 if (selectingFolder)
2157 {
2158 list.push_back( make_pair(Gettext::keyname(_("ENTER_K:ENTER")),
2159 _("Select folder")));
2160 list.push_back( make_pair(Gettext::keyname(_("ABORT_K:^C")),
2161 _("Cancel")));
2162 return true;
2163 }
2164
2165 GlobalKeys::listKeys(list, GlobalKeys::LISTFOLDERS);
2166
2167 list.push_back( make_pair(Gettext::keyname(_("EDIT_K:E")),
2168 _("Edit Acct")));
2169 list.push_back( make_pair(Gettext::keyname(_("FILTER_K:F")),
2170 _("Filter")));
2171 list.push_back( make_pair(Gettext::keyname(_("ADDFOLDER_K:A")),
2172 _("Add")));
2173 list.push_back( make_pair(Gettext::keyname(_("DELFOLDER_K:D")),
2174 _("Delete")));
2175 list.push_back( make_pair(Gettext::keyname(_("MOVEUP:^U")),
2176 _("Move up")));
2177 list.push_back( make_pair(Gettext::keyname(_("MOVEDN:^D")),
2178 _("Move down")));
2179 list.push_back( make_pair(Gettext::keyname(_("SPECIAL:U")),
2180 _("Use special")));
2181 list.push_back( make_pair(Gettext::keyname(_("RENAME:R")),
2182 _("Rename")));
2183 list.push_back( make_pair(Gettext::keyname(_("RESET:^R")),
2184 _("Reset hdrs")));
2185 list.push_back( make_pair(Gettext::keyname(_("PERMS:P")),
2186 _("Permissions")));
2187 return false;
2188 }
2189