1 /*
2 ** Copyright 2003-2009, Double Precision Inc.
3 **
4 ** See COPYING for distribution information.
5 */
6
7 #include "config.h"
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <locale.h>
11 #include <errno.h>
12 #include <fcntl.h>
13 #include <pwd.h>
14 #include <courier-unicode.h>
15
16 #include "curses/cursesscreen.H"
17 #include "curses/cursesbutton.H"
18 #include "curses/cursestitlebar.H"
19 #include "curses/cursesstatusbar.H"
20 #include "curses/cursesmainscreen.H"
21 #include "curses/cursesdialog.H"
22 #include "curses/curseslabel.H"
23 #include "curses/cursesfield.H"
24 #include "curses/curseskeyhandler.H"
25
26 #include "gettext.H"
27 #include "myserver.H"
28 #include "globalkeys.H"
29 #include "opendialog.H"
30 #include "savedialog.H"
31 #include "myservercallback.H"
32 #include "myserverpromptinfo.H"
33 #include "previousscreen.H"
34
35 #include "libmail/rfcaddr.H"
36 #include "libmail/misc.H"
37
38 #include "opensubfolders.H"
39 #include "addressbook.H"
40
41 #include "libmail/logininfo.H"
42 #include <fstream>
43 #include <vector>
44 #include <sstream>
45 #include <list>
46 #include <set>
47 #include <map>
48 #include <algorithm>
49
50 #include "addressbookinterfacemail.H"
51 #include "addressbookinterfaceldap.H"
52
53 using namespace std;
54
55 extern Gettext::Key key_DELADDRESSBOOKENTRY;
56 extern Gettext::Key key_RENAMEADDRESSBOOKENTRY;
57 extern Gettext::Key key_IMPORTADDRESSBOOKENTRY;
58 extern Gettext::Key key_EXPORTADDRESSBOOKENTRY;
59 extern Gettext::Key key_ADDADDRESSBOOK;
60 extern Gettext::Key key_DELETEADDRESSBOOK;
61 extern Gettext::Key key_ABORT;
62 extern Gettext::Key key_UP;
63 extern Gettext::Key key_DOWN;
64 extern Gettext::Key key_ADDLDAP;
65
66 extern CursesScreen *cursesScreen;
67 extern CursesMainScreen *mainScreen;
68 extern CursesTitleBar *titleBar;
69 extern CursesStatusBar *statusBar;
70
71 extern void mainMenu(void *);
72
73 void listaddressbookScreenImport(void *vp);
74 void addressbookIndexScreen(void *);
75 void addLdapScreen(void *);
76
77 list<AddressBook *> AddressBook::addressBookList;
78 string AddressBook::importName;
79 string AddressBook::importAddr;
80
81 AddressBook *AddressBook::defaultAddressBook=NULL;
82
AddressBook()83 AddressBook::AddressBook()
84 : myInterface(NULL),
85 iwantFocus(false)
86 {
87 }
88
~AddressBook()89 AddressBook::~AddressBook()
90 {
91 if (myInterface)
92 {
93 Interface *i=myInterface;
94
95 myInterface=NULL;
96 delete i;
97 }
98 }
99
100 class AddressBook::disconnect : public mail::callback::disconnect {
101 public:
102 disconnect();
103 ~disconnect();
104 void disconnected(const char *errmsg);
105 void servererror(const char *errmsg);
106 };
107
108
disconnect()109 AddressBook::disconnect::disconnect()
110 {
111 }
112
~disconnect()113 AddressBook::disconnect::~disconnect()
114 {
115 }
116
disconnected(const char * errmsg)117 void AddressBook::disconnect::disconnected(const char *errmsg)
118 {
119 }
120
servererror(const char * errmsg)121 void AddressBook::disconnect::servererror(const char *errmsg)
122 {
123 }
124
init()125 void AddressBook::init()
126 {
127 // Open the default address book first.
128
129 string afile=myServer::getConfigDir()+ "/addressbook";
130
131 int fd= ::open(afile.c_str(), O_RDWR|O_CREAT, 0600);
132
133 if (fd >= 0)
134 ::close(fd);
135
136 if (defaultAddressBook)
137 return; // Already initialized.
138
139 #if HAVE_OPENLDAP
140
141 const char *ldap_auto=getenv("CONE_AUTOLDAP");
142
143 if (ldap_auto && *ldap_auto)
144 {
145 std::string name=ldap_auto;
146 std::string server;
147 std::string suffix;
148
149 size_t p=name.find('=');
150
151 if (p != std::string::npos)
152 {
153 server=name.substr(p+1);
154 name=name.substr(0, p);
155
156 p=server.find('/');
157
158 if (p != std::string::npos)
159 {
160 suffix=server.substr(p+1);
161 server=server.substr(0, p);
162 }
163 std::list<AddressBook *>::iterator
164 b=addressBookList.begin(),
165 e=addressBookList.end();
166
167 while (b != e)
168 {
169 if ((*b)->name == name)
170 break;
171 ++b;
172 }
173
174 if (b == e)
175 {
176 AddressBook *abook=new AddressBook();
177
178 if (!abook)
179 outofmemory();
180
181 try {
182 abook->init(name,
183 "ldap://" + server,
184 suffix);
185
186 AddressBook::addressBookList
187 .insert(AddressBook::
188 addressBookList.end(),
189 abook);
190 } catch (...) {
191 delete abook;
192 LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
193 }
194
195 myServer::saveconfig();
196 }
197 }
198 }
199 #endif
200
201
202 defaultAddressBook=new AddressBook;
203
204 if (!defaultAddressBook)
205 outofmemory();
206
207 try {
208 string url="mbox:" + afile;
209
210 myServer::Callback login_callback;
211
212 mail::account::openInfo loginInfo;
213
214 loginInfo.url=url;
215
216 disconnect temp_disconnect;
217
218 mail::account *abook=mail::account::open(loginInfo,
219 login_callback,
220 temp_disconnect);
221
222 if (!abook)
223 outofmemory();
224
225 // Need to do some song-n-dance with LibMAIL in order to
226 // initialize the default address book folder.
227
228 try {
229 if (!myServer::eventloop(login_callback))
230 {
231 delete abook;
232 delete defaultAddressBook;
233 return;
234 }
235
236 OpenSubFoldersCallback readFolders;
237 myServer::Callback readfolders_callback;
238
239 abook->readTopLevelFolders(readFolders,
240 readfolders_callback);
241 if (!myServer::eventloop(login_callback))
242 {
243 delete abook;
244 delete defaultAddressBook;
245 return;
246 }
247
248 if (readFolders.folders.size() > 0)
249 {
250 defaultAddressBook->init(_("Local Address Book"),
251 url,
252 readFolders
253 .folders[0]
254 ->toString());
255
256 myServer::Callback logout_callback;
257
258 abook->logout(logout_callback);
259 myServer::eventloop(logout_callback);
260
261 delete abook;
262 abook=NULL;
263 }
264 else
265 {
266 delete defaultAddressBook;
267 defaultAddressBook=NULL;
268 }
269
270 } catch (...) {
271 if (abook)
272 delete abook;
273 LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
274 }
275 } catch (...) {
276 delete defaultAddressBook;
277 defaultAddressBook=NULL;
278 LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
279 }
280 }
281
init(string nameArg,string urlArg,string folderStrArg)282 void AddressBook::init(string nameArg,
283 string urlArg,
284 string folderStrArg)
285 {
286 name=nameArg;
287 url=urlArg;
288 folderStr=folderStrArg;
289 }
290
open()291 bool AddressBook::open()
292 {
293 if (myInterface && myInterface->closed())
294 close();
295
296 if (myInterface)
297 return true;
298
299 if (url.substr(0, 7) == "ldap://")
300 {
301 #if HAVE_OPENLDAP
302 Interface::LDAP *i=new Interface::LDAP(name);
303
304 if (!i)
305 outofmemory();
306
307 try {
308 if (i->openUrl(url, folderStr))
309 {
310 myInterface=i;
311 return true;
312 }
313 } catch (...)
314 {
315 delete i;
316 LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
317 }
318 delete i;
319 #endif
320 }
321 else
322 {
323 Interface::Mail *i=new Interface::Mail(name);
324
325 if (!i)
326 outofmemory();
327
328 try {
329 if (i->open(url, folderStr))
330 {
331 myInterface=i;
332 return true;
333 }
334 } catch (...)
335 {
336 delete i;
337 LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
338 }
339 delete i;
340 }
341 return false;
342 }
343
closeAllRemote()344 void AddressBook::closeAllRemote()
345 {
346 list<AddressBook *>::iterator b, e;
347
348 b=addressBookList.begin();
349 e=addressBookList.end();
350
351 while (b != e)
352 {
353 AddressBook *p= *b;
354
355 p->close();
356 b++;
357 }
358 }
359
closeAll()360 void AddressBook::closeAll()
361 {
362 if (defaultAddressBook)
363 {
364 defaultAddressBook->close();
365 delete defaultAddressBook;
366 defaultAddressBook=NULL;
367 }
368
369 list<AddressBook *>::iterator b, e;
370
371 while ((b=addressBookList.begin()) != (e=addressBookList.end()))
372 {
373 AddressBook *p= *b;
374
375 addressBookList.erase(b);
376
377 try {
378 p->close();
379 delete p;
380 } catch (...) {
381 delete p;
382 }
383 }
384 }
385
close()386 void AddressBook::close()
387 {
388 Interface *i=myInterface;
389
390 myInterface=NULL;
391
392 try {
393 if (i)
394 i->close();
395 } catch (...)
396 {
397 delete i;
398 LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
399 }
400
401 delete i;
402 }
403
404 // Add a new address book entry.
405
add(mail::addressbook::Entry & newEntry)406 bool AddressBook::add(mail::addressbook::Entry &newEntry)
407 {
408 if (!open())
409 return false;
410
411 return myInterface->add(newEntry,
412 currentRecord.second); // Old UID, if not empty
413 }
414
import(list<mail::addressbook::Entry> & newList)415 bool AddressBook::import(list<mail::addressbook::Entry> &newList)
416 {
417 if (!open())
418 return false;
419
420 return myInterface->import(newList);
421 }
422
423 // Delete an address book entry
424
del(string uid)425 bool AddressBook::del(string uid)
426 {
427 if (!open())
428 return false;
429
430 return myInterface->del(uid);
431 }
432
433 // Rename an address book entry
434
rename(std::string uid,std::string newnickname)435 bool AddressBook::rename(std::string uid,
436 std::string newnickname)
437 {
438 if (!open())
439 return false;
440
441 return myInterface->rename(uid, newnickname);
442 }
443
444 // Search the address book for a specific nickname.
searchNickname(std::string nickname,std::vector<mail::emailAddress> & addrListArg)445 bool AddressBook::searchNickname(std::string nickname,
446 std::vector<mail::emailAddress> &addrListArg)
447 {
448 if (!open())
449 return false;
450
451 return myInterface->searchNickname(nickname, addrListArg);
452 }
453
454 // List address book contents.
455
getIndex(std::list<std::pair<std::string,std::string>> & listArg)456 void AddressBook::getIndex( std::list< std::pair<std::string,
457 std::string> > &listArg)
458 {
459 if (!open())
460 return;
461
462 return myInterface->getIndex(listArg);
463 }
464
465 //
466 // Read the current address book entry into addrList:
467
getEntry(std::vector<mail::emailAddress> & addrList)468 bool AddressBook::getEntry(std::vector<mail::emailAddress> &addrList)
469 {
470 if (!open())
471 return true;
472
473 return myInterface->getEntry(currentRecord.second, addrList);
474 }
475
getEntry(std::string uid,std::vector<mail::emailAddress> & addrList)476 bool AddressBook::getEntry(std::string uid,
477 std::vector<mail::emailAddress> &addrList)
478 {
479 if (!open())
480 return true;
481
482 return myInterface->getEntry(uid, addrList);
483 }
484
485 //
486 // Replace addresses in the address list with anything found in
487 // the address books.
488
searchAll(vector<mail::emailAddress> & addrList,vector<mail::emailAddress> & retList)489 bool AddressBook::searchAll(vector<mail::emailAddress> &addrList, // Orig list
490 vector<mail::emailAddress> &retList) // Final list
491 {
492 set<string> addrSet; // Eliminate dupes
493
494 vector<mail::emailAddress>::iterator curAddr=addrList.begin(),
495 lastAddr=addrList.end();
496
497 for ( ; curAddr != lastAddr; curAddr++)
498 {
499 string s=curAddr->getAddr();
500
501 if (s.find('@') != std::string::npos)
502 {
503 // Not a nickname, a dupe?
504
505 if (addrSet.count( s ) == 0)
506 {
507 addrSet.insert(s);
508 retList.push_back( *curAddr);
509 }
510 continue;
511 }
512
513 // Go through each address book, see what we can find.
514
515 list<AddressBook *>::iterator ab=addressBookList.begin(),
516 ae=addressBookList.end();
517
518 vector<mail::emailAddress> addresses;
519
520 if (defaultAddressBook &&
521 !defaultAddressBook->searchNickname(curAddr->getAddr(),
522 addresses))
523 return false;
524
525 while (ab != ae && addresses.size() == 0)
526 {
527 if (! (*ab++)->searchNickname(curAddr->getAddr(), addresses))
528 return false;
529 }
530
531 if (addresses.size() == 0) // Not found, check the pw file
532 {
533 mail::emailAddress addr= *curAddr;
534
535 if (addr.getDisplayName("utf-8").size() == 0)
536 {
537 struct passwd *pw=getpwnam(addr.getAddr()
538 .c_str());
539
540 if (pw != NULL)
541 {
542 string nn=pw->pw_gecos;
543
544 size_t n=nn.find(',');
545
546 if (n != std::string::npos)
547 nn=nn.substr(0, n);
548
549 addr.setDisplayName(nn,
550 unicode_default_chset());
551 }
552 }
553
554 addr.setAddr(addr.getAddr() + "@"
555 + mail::hostname());
556
557 addresses.push_back(addr);
558 }
559
560 // Found addresses, check for dupes, add them.
561
562 vector<mail::emailAddress>::iterator addrb=addresses.begin(),
563 addre=addresses.end();
564
565 while (addrb != addre)
566 {
567 if (addrSet.count( addrb->getAddr() ) == 0)
568 {
569 addrSet.insert(addrb->getAddr());
570 retList.push_back( *addrb );
571 }
572
573 ++addrb;
574 }
575 }
576
577 return true;
578 }
579
580 ////////////////////////////////////////////////////////////////////////////
581 //
582 // Add new address book entry screen.
583
584 class AddAddressBookScreen : public CursesContainer,
585 public CursesKeyHandler {
586
587 AddressBook *addressBook;
588 CursesLabel title;
589 CursesDialog addDialog;
590
591 CursesLabel nickname_label;
592 CursesLabel name_label;
593 CursesLabel addr_label;
594
595 CursesField nickname_field;
596 CursesField name_field;
597 CursesField addr_field;
598
599 CursesButtonRedirect<AddAddressBookScreen> saveButton, cancelButton;
600
601 void save();
602 void cancel();
603
604 bool processKey(const Curses::Key &key);
605
606 public:
requestFocus()607 void requestFocus()
608 {
609 nickname_field.requestFocus();
610 }
611
612 AddAddressBookScreen(CursesMainScreen *,
613 AddressBook *);
614 ~AddAddressBookScreen();
615 };
616
AddAddressBookScreen(CursesMainScreen * mainScreen,AddressBook * addressBookArg)617 AddAddressBookScreen::AddAddressBookScreen(CursesMainScreen *mainScreen,
618 AddressBook *addressBookArg)
619 : CursesContainer(mainScreen),
620 CursesKeyHandler(PRI_SCREENHANDLER),
621 addressBook(addressBookArg),
622 title(this, _("Add Entry")),
623 addDialog(this),
624 nickname_label(NULL, _("Nickname: ")),
625 name_label(NULL, _("Name: ")),
626 addr_label(NULL, _("Address: ")),
627
628 nickname_field(NULL),
629 name_field(NULL),
630 addr_field(NULL),
631
632 saveButton(this, _("SAVE")),
633 cancelButton(this, _("CANCEL"))
634 {
635 setRow(4);
636 setAlignment(Curses::PARENTCENTER);
637
638 title.setAlignment(Curses::PARENTCENTER);
639
640 addDialog.setRow(2);
641
642 addDialog.addPrompt(&nickname_label, &nickname_field, 0);
643 addDialog.addPrompt(&name_label, &name_field, 1);
644 addDialog.addPrompt(&addr_label, &addr_field, 2);
645
646 titleBar->setTitles(_("ADD ENTRY"), "");
647
648 saveButton=this;
649 cancelButton=this;
650
651 saveButton= &AddAddressBookScreen::save;
652 cancelButton= &AddAddressBookScreen::cancel;
653
654 addDialog.addPrompt(NULL, &saveButton, 4);
655 addDialog.addPrompt(NULL, &cancelButton, 6);
656
657 vector<mail::emailAddress> addresses;
658
659 if (addressBook->currentRecord.second.size() > 0) // Edit exist rec
660 {
661 nickname_field.setText(addressBook->currentRecord.first);
662
663 if (!addressBook->getEntry(addresses))
664 addresses.clear();
665 }
666
667 // Get any defaults from the take addresses screen.
668
669 if (AddressBook::importAddr.size() > 0)
670 {
671 mail::emailAddress addr;
672
673 addr.setDisplayName(AddressBook::importName,
674 unicode_default_chset());
675 addr.setDisplayAddr(AddressBook::importAddr,
676 unicode_default_chset());
677
678 addresses.push_back(addr);
679 }
680
681 if (addresses.size() == 1)
682 {
683 name_field.setText(addresses[0]
684 .getDisplayName(unicode_default_chset()));
685 addresses[0].setDisplayName("", "utf-8");
686 }
687
688 // We want to decode MIME encoded addresses. Hack.
689
690 vector<mail::address> decodedAddresses;
691
692 decodedAddresses.reserve(addresses.size());
693
694 {
695 vector<mail::emailAddress>::iterator b, e;
696
697 for (b=addresses.begin(), e=addresses.end(); b != e; ++b)
698 decodedAddresses
699 .push_back(mail::address(b->getDisplayName(unicode_default_chset()),
700 b->getDisplayAddr(unicode_default_chset())));
701 }
702
703 string s=mail::address::toString("", decodedAddresses, 0);
704
705 string::iterator b=s.begin(), e=s.end();
706
707 while (b != e)
708 {
709 if (*b == '\n')
710 *b=' ';
711 b++;
712 }
713 addr_field.setText(s);
714 name_field.requestFocus();
715 }
716
processKey(const Curses::Key & key)717 bool AddAddressBookScreen::processKey(const Curses::Key &key)
718 {
719 if (key == key_ABORT)
720 {
721 keepgoing=false;
722 myServer::nextScreen= &mainMenu;
723 myServer::nextScreenArg=NULL;
724 PreviousScreen::previousScreen();
725 return true;
726 }
727 return false;
728 }
729
~AddAddressBookScreen()730 AddAddressBookScreen::~AddAddressBookScreen()
731 {
732 }
733
save()734 void AddAddressBookScreen::save()
735 {
736 size_t errindex;
737
738 mail::addressbook::Entry newEntry;
739
740 {
741 vector<mail::address> addrBuf;
742
743 if (!mail::address::fromString(addr_field.getText(),
744 addrBuf, errindex))
745 {
746 addr_field.requestFocus();
747 addr_field.setCursorPos(errindex);
748 addr_field.beepError();
749 return;
750 }
751
752 // entered addresses use the native character set, convert
753 // to MIME encoding using this hack
754
755 newEntry.addresses.clear();
756 newEntry.addresses.reserve(addrBuf.size());
757
758 vector<mail::address>::iterator b, e;
759
760 for (b=addrBuf.begin(), e=addrBuf.end(); b != e; ++b)
761 {
762 mail::emailAddress addr;
763
764 std::string errmsg;
765
766 errmsg=addr.setDisplayName(b->getName(),
767 unicode_default_chset());
768
769 if (errmsg == "")
770 errmsg=addr
771 .setDisplayAddr(b->getAddr(),
772 unicode_default_chset()
773 );
774
775 if (errmsg != "")
776 {
777 addr_field.requestFocus();
778 statusBar->clearstatus();
779 statusBar->status(errmsg,
780 statusBar->SYSERROR);
781 statusBar->beepError();
782 return;
783 }
784 newEntry.addresses.push_back(addr);
785 }
786 }
787
788 if (newEntry.addresses.size() == 0)
789 {
790 addr_field.requestFocus();
791 addr_field.beepError();
792 return;
793 }
794
795 newEntry.nickname=nickname_field.getText();
796
797 if (newEntry.addresses.size() == 1 &&
798 newEntry.addresses[0].getName().size() == 0)
799 {
800 newEntry.addresses[0].setDisplayName(name_field.getText(),
801 unicode_default_chset());
802 }
803
804 if (addressBook->add(newEntry))
805 {
806 keepgoing=false;
807 myServer::nextScreen= &addressbookIndexScreen;
808 myServer::nextScreenArg=addressBook;
809 return;
810 }
811 }
812
cancel()813 void AddAddressBookScreen::cancel()
814 {
815 keepgoing=false;
816 myServer::nextScreen= &mainMenu;
817 myServer::nextScreenArg=NULL;
818 PreviousScreen::previousScreen();
819 }
820
addressbookScreen(void * vp)821 void addressbookScreen(void *vp)
822 {
823 AddressBook *a=(AddressBook *)vp;
824
825 AddAddressBookScreen screen(mainScreen, a);
826
827 screen.requestFocus();
828 myServer::eventloop();
829 }
830
831 /////////////////////////////////////////////////////////////////////////////
832 //
833 // Address book Index
834 //
835
836 class AddressBookIndexScreen : public CursesContainer,
837 public CursesKeyHandler {
838
839 AddressBook *addressBook;
840 CursesLabel title;
841 CursesDialog indexDialog;
842
843 class indexButton : public CursesButton {
844 AddressBookIndexScreen *myScreen;
845 pair<string, string> myEntry;
846 // first: nickname, second: uid
847
848 bool processKeyInFocus(const Key &key);
849
850 public:
851 size_t indexNum;
852
853 indexButton(AddressBookIndexScreen *myScreenArg,
854 pair<string, string> myEntryArg);
855 ~indexButton();
856
857 void clicked();
858 };
859
860 vector<indexButton *> buttons;
861
862 class indexSort {
863 public:
864 bool operator()(const pair<string, string> &a,
865 const pair<string, string> &b);
866 };
867
868 bool processKey(const Curses::Key &key);
869 bool listKeys( vector< pair<string, string> > &list);
870 public:
requestFocus()871 void requestFocus()
872 {
873 if (buttons.size() > 0)
874 buttons[0]->requestFocus();
875 }
876
877 AddressBookIndexScreen(CursesMainScreen *, AddressBook *,
878 vector< pair<string, string> > &index);
879 ~AddressBookIndexScreen();
880
881 void clicked( pair<string, string> entryArg);
882 void deleteEntry(size_t n, string uid);
883 void renameEntry(size_t n, string uid, string newnickname);
884 };
885
indexButton(AddressBookIndexScreen * myScreenArg,pair<string,string> myEntryArg)886 AddressBookIndexScreen::indexButton::indexButton(AddressBookIndexScreen
887 *myScreenArg,
888 pair<string, string>
889 myEntryArg)
890 : CursesButton(myScreenArg, myEntryArg.first),
891 myScreen(myScreenArg),
892 myEntry(myEntryArg),
893 indexNum(0)
894 {
895 setStyle(MENU);
896 }
897
~indexButton()898 AddressBookIndexScreen::indexButton::~indexButton()
899 {
900 }
901
clicked()902 void AddressBookIndexScreen::indexButton::clicked()
903 {
904 myScreen->clicked(myEntry);
905 }
906
processKeyInFocus(const Key & key)907 bool AddressBookIndexScreen::indexButton::processKeyInFocus(const Key &key)
908 {
909 if (key == key_DELADDRESSBOOKENTRY)
910 {
911 myServer::promptInfo response=
912 myServer
913 ::promptInfo(Gettext(_("Delete address book entry for '%1%'? (Y/N) "))
914 << myEntry.first).yesno();
915
916 response=myServer::prompt(response);
917
918 if (response.aborted())
919 return true;
920
921 if ((string)response == "Y")
922 myScreen->deleteEntry(indexNum, myEntry.second);
923
924 return true;
925 }
926
927 if (key == key_RENAMEADDRESSBOOKENTRY)
928 {
929 myServer::promptInfo response=
930 myServer
931 ::promptInfo(_("New nickname (CTRL-C - cancel: "));
932
933 response=myServer::prompt(response);
934
935 if (response.aborted())
936 return true;
937
938 std::string n=response;
939
940 if (n.size() == 0)
941 return true;
942
943 myScreen->renameEntry(indexNum, myEntry.second, n);
944 return true;
945 }
946
947 return CursesButton::processKeyInFocus(key);
948 }
949
AddressBookIndexScreen(CursesMainScreen * parent,AddressBook * addressBookArg,vector<pair<string,string>> & indexArg)950 AddressBookIndexScreen::AddressBookIndexScreen(CursesMainScreen *parent,
951 AddressBook *addressBookArg,
952 vector< pair<string, string> >
953 &indexArg)
954 : CursesContainer(parent),
955 CursesKeyHandler(PRI_SCREENHANDLER),
956 addressBook(addressBookArg),
957 title(this, addressBookArg->getName()),
958 indexDialog(this)
959 {
960 setRow(4);
961 setAlignment(Curses::PARENTCENTER);
962 title.setAlignment(Curses::PARENTCENTER);
963
964 indexDialog.setRow(2);
965 titleBar->setTitles(_("ADDRESS BOOK INDEX"), "");
966
967 sort(indexArg.begin(), indexArg.end(), indexSort());
968
969 buttons.reserve(indexArg.size());
970 vector< pair<string, string> >::iterator b=indexArg.begin(),
971 e=indexArg.end();
972
973 while (b != e)
974 {
975 indexButton *bb=new indexButton(this, *b);
976 b++;
977
978 if (!bb)
979 outofmemory();
980
981 try {
982 bb->indexNum=buttons.size();
983 buttons.insert(buttons.end(), bb);
984 } catch (...) {
985 delete bb;
986 LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
987 }
988
989 indexDialog.addPrompt(NULL, bb);
990 }
991 }
992
renameEntry(size_t n,string uid,string newnickname)993 void AddressBookIndexScreen::renameEntry(size_t n, string uid,
994 string newnickname)
995 {
996 if (addressBook->rename(uid, newnickname))
997 {
998 keepgoing=false;
999 addressBook->currentRecord.second="";
1000 myServer::nextScreen= &addressbookIndexScreen;
1001 myServer::nextScreenArg=addressBook;
1002 }
1003 }
1004
deleteEntry(size_t n,string uid)1005 void AddressBookIndexScreen::deleteEntry(size_t n, string uid)
1006 {
1007 if (addressBook->del(uid))
1008 {
1009 indexButton *b=buttons[n];
1010
1011 buttons.erase(buttons.begin() + n);
1012
1013 delete b;
1014
1015 vector<indexButton *>::iterator p=buttons.begin() + n,
1016 e=buttons.end();
1017
1018 if (p != e)
1019 (*p)->requestFocus();
1020 else if (buttons.size() > 0)
1021 (*(buttons.end()-1))->requestFocus();
1022
1023 while (p != e)
1024 {
1025 (*p)->indexNum=n++;
1026 (*p)->setRow( (*p)->getRow() - 1);
1027
1028 p++;
1029 }
1030 }
1031 }
1032
1033 bool AddressBookIndexScreen
operator()1034 ::indexSort::operator()(const pair<string, string> &a,
1035 const pair<string, string> &b)
1036 {
1037 return (a.first.compare(b.first) < 0);
1038 }
1039
processKey(const Curses::Key & key)1040 bool AddressBookIndexScreen::processKey(const Curses::Key &key)
1041 {
1042 if (key == key_ABORT)
1043 {
1044 keepgoing=false;
1045 myServer::nextScreen= &mainMenu;
1046 myServer::nextScreenArg=NULL;
1047 PreviousScreen::previousScreen();
1048 return true;
1049 }
1050
1051 if (key == key_ADDADDRESSBOOK)
1052 {
1053 keepgoing=false;
1054 addressBook->currentRecord.second="";
1055 myServer::nextScreen= &addressbookScreen;
1056 myServer::nextScreenArg=addressBook;
1057 return true;
1058 }
1059
1060 if (key == key_IMPORTADDRESSBOOKENTRY)
1061 {
1062 string filename;
1063
1064 {
1065 OpenDialog open_dialog;
1066
1067 open_dialog.noMultiples();
1068
1069 open_dialog.requestFocus();
1070 myServer::eventloop();
1071
1072 vector<string> &filenameList=
1073 open_dialog.getFilenameList();
1074
1075 if (filenameList.size() == 0)
1076 filename="";
1077 else
1078 filename=filenameList[0];
1079
1080 mainScreen->erase();
1081 }
1082 mainScreen->draw();
1083 requestFocus();
1084
1085 if (filename.size() == 0)
1086 return true;
1087
1088 ifstream i(filename.c_str());
1089
1090 if (!i.is_open())
1091 {
1092 statusBar->clearstatus();
1093 statusBar->status(strerror(errno),
1094 statusBar->SYSERROR);
1095 statusBar->beepError();
1096 return true;
1097 }
1098
1099 set<string> nicknames;
1100
1101 {
1102 list<pair<string, string> > index;
1103
1104 addressBook->getIndex(index);
1105
1106 list<pair<string, string> >::iterator
1107 b=index.begin(), e=index.end();
1108
1109 while (b != e)
1110 {
1111 nicknames.insert(b->first);
1112 ++b;
1113 }
1114 }
1115
1116 statusBar->clearstatus();
1117 statusBar->status(_("Importing..."));
1118 std::string line;
1119 size_t linenum=0;
1120
1121 list<mail::addressbook::Entry> newList;
1122
1123 while (!std::getline(i, line).eof())
1124 {
1125 std::vector<mail::emailAddress> v;
1126 size_t n;
1127 std::string name;
1128
1129 ++linenum;
1130 if (!mail::address::fromString(line, v, n))
1131 {
1132 statusBar->clearstatus();
1133 statusBar->status(Gettext(_("Syntax error on line %1%"))
1134 << linenum);
1135 statusBar->beepError();
1136 return true;
1137 }
1138
1139 if (v.size() && v[0].getAddr().size() == 0)
1140 {
1141 // Nickname:
1142
1143 name=v[0].getName();
1144 v.erase(v.begin());
1145
1146 if (name.size() > 0)
1147 name=name.substr(0, name.size()-1);
1148 // Drop trailing colon.
1149 }
1150
1151 if (v.size() == 0)
1152 continue;
1153
1154 if (name.size() == 0)
1155 name=v[0].getName();
1156
1157 if (name.size() == 0)
1158 name=v[0].getAddr();
1159
1160 name=unicode::iconvert
1161 ::convert_tocase(Gettext::toutf8(name),
1162 "utf-8",
1163 unicode_lc);
1164
1165 size_t ip;
1166 ip=name.find(' ');
1167 if (ip != std::string::npos)
1168 name=name.substr(0, ip);
1169 ip=name.find('@');
1170 if (ip != std::string::npos)
1171 name=name.substr(0, ip);
1172
1173 std::string nCheck=name;
1174 size_t cnt=0;
1175 while (nicknames.count(nCheck))
1176 {
1177 std::ostringstream o;
1178
1179 o << name << ++cnt;
1180 nCheck=o.str();
1181 }
1182
1183 mail::addressbook::Entry newEntry;
1184 newEntry.nickname=nCheck;
1185 newEntry.addresses=v;
1186 newList.push_back(newEntry);
1187 nicknames.insert(nCheck);
1188 }
1189
1190 addressBook->import(newList);
1191
1192 keepgoing=false;
1193 addressBook->currentRecord.second="";
1194 myServer::nextScreen= &addressbookIndexScreen;
1195 myServer::nextScreenArg=addressBook;
1196 return true;
1197 }
1198
1199 if (key == key_EXPORTADDRESSBOOKENTRY)
1200 {
1201 std::string filename;
1202
1203 {
1204 SaveDialog save_dialog;
1205
1206 save_dialog.requestFocus();
1207 myServer::eventloop();
1208
1209 filename=save_dialog;
1210 }
1211
1212 mainScreen->erase();
1213 mainScreen->draw();
1214 requestFocus();
1215
1216 if (filename.size() == 0)
1217 return true;
1218
1219 ofstream o(filename.c_str());
1220
1221 if (!o.is_open())
1222 {
1223 statusBar->clearstatus();
1224 statusBar->status(strerror(errno),
1225 statusBar->SYSERROR);
1226 statusBar->beepError();
1227 return true;
1228 }
1229
1230 std::list<std::pair<std::string, std::string> > index;
1231
1232 addressBook->getIndex(index);
1233
1234 while (!index.empty())
1235 {
1236 std::vector<mail::emailAddress> v;
1237
1238 addressBook->getEntry(index.front().second, v);
1239
1240 std::string s=
1241 mail::address::toString(index.front().first
1242 + ": ", v);
1243
1244 std::string::iterator b=s.begin(), e=s.end();
1245
1246 while (b != e)
1247 {
1248 if (*b == '\n') *b=' ';
1249 ++b;
1250 }
1251
1252 o << s << std::endl;
1253 index.pop_front();
1254 }
1255
1256 if (o.flush().fail() || o.bad())
1257 {
1258 statusBar->clearstatus();
1259 statusBar->status(strerror(errno),
1260 statusBar->SYSERROR);
1261 statusBar->beepError();
1262 }
1263 return true;
1264 }
1265
1266 return GlobalKeys::processKey(key, GlobalKeys::ADDRESSBOOKINDEXSCREEN,
1267 NULL);
1268 }
1269
listKeys(vector<pair<string,string>> & list)1270 bool AddressBookIndexScreen::listKeys( vector< pair<string, string> > &list)
1271 {
1272 GlobalKeys::listKeys(list, GlobalKeys::ADDRESSBOOKINDEXSCREEN);
1273
1274 list.push_back(make_pair(Gettext::keyname(_("ADDADDRESSBOOK_K:A")),
1275 _("Add")));
1276 list.push_back(make_pair(Gettext::keyname(_("DELADDRESSBOOKENTRY_K:D"))
1277 , _("Delete")));
1278 list.push_back(make_pair(Gettext::keyname(_("RENAMEADDRESSBOOKENTRY_K:R"))
1279 , _("Rename")));
1280 list.push_back(make_pair(Gettext::keyname(_("IMPORTADDRESSBOOKENTRY_K:I"))
1281 , _("Import")));
1282 list.push_back(make_pair(Gettext::keyname(_("EXPORTADDRESSBOOKENTRY_K:E"))
1283 , _("Export")));
1284 return false;
1285 }
1286
~AddressBookIndexScreen()1287 AddressBookIndexScreen::~AddressBookIndexScreen()
1288 {
1289 vector<indexButton *>::iterator b=buttons.begin(), e=buttons.end();
1290
1291 while (b != e)
1292 {
1293 indexButton *p= *b;
1294
1295 *b++ = NULL;
1296
1297 if (p)
1298 delete p;
1299 }
1300 }
1301
clicked(pair<string,string> entryArg)1302 void AddressBookIndexScreen::clicked( pair<string, string> entryArg)
1303 {
1304 keepgoing=false;
1305 addressBook->currentRecord=entryArg;
1306 myServer::nextScreen= &addressbookScreen;
1307 myServer::nextScreenArg=addressBook;
1308 }
1309
addressbookIndexScreen(void * vp)1310 void addressbookIndexScreen(void *vp)
1311 {
1312 AddressBook *a=(AddressBook *)vp;
1313
1314 vector< pair<string, string> > folder_indexArray;
1315
1316 {
1317 list< pair<string, string> > folder_index;
1318
1319 a->getIndex(folder_index);
1320
1321 folder_indexArray.reserve(folder_index.size());
1322
1323 folder_indexArray.insert(folder_indexArray.end(),
1324 folder_index.begin(),
1325 folder_index.end());
1326 }
1327
1328 AddressBookIndexScreen screen(mainScreen, a, folder_indexArray);
1329
1330 screen.requestFocus();
1331 myServer::eventloop();
1332 }
1333
1334 /////////////////////////////////////////////////////////////////////////////
1335 //
1336 // Add LDAP directory screen
1337
1338 class AddLdapScreen : public CursesContainer,
1339 public CursesKeyHandler {
1340
1341 CursesLabel title;
1342 CursesDialog addDialog;
1343
1344 CursesLabel nickname_label;
1345 CursesLabel server_label;
1346 CursesLabel userid_label;
1347 CursesLabel password_label;
1348 CursesLabel suffix_label;
1349
1350 CursesField nickname_field;
1351 CursesField server_field;
1352 CursesField userid_field;
1353 CursesField password_field;
1354 CursesField suffix_field;
1355
1356 CursesButtonRedirect<AddLdapScreen> saveButton, cancelButton;
1357
1358 void save();
1359 void cancel();
1360
1361 public:
1362 AddLdapScreen(CursesMainScreen *mainScreen);
1363 ~AddLdapScreen();
1364
1365 bool processKey(const Curses::Key &key);
1366 bool listKeys( vector< pair<string, string> > &list);
1367 };
1368
1369
AddLdapScreen(CursesMainScreen * parent)1370 AddLdapScreen::AddLdapScreen(CursesMainScreen *parent)
1371 : CursesContainer(parent),
1372 CursesKeyHandler(PRI_SCREENHANDLER),
1373 title(this, _("Add LDAP directory")),
1374 addDialog(this),
1375 nickname_label(NULL, _("Directory name: ")),
1376 server_label(NULL, _("LDAP server hostname: ")),
1377 userid_label(NULL, _("LDAP userid (optional): ")),
1378 password_label(NULL, _("LDAP password (optional): ")),
1379 suffix_label(NULL, _("LDAP suffix/root: ")),
1380
1381 nickname_field(NULL),
1382 server_field(NULL),
1383 userid_field(NULL),
1384 password_field(NULL),
1385 suffix_field(NULL),
1386
1387 saveButton(this, _("SAVE")),
1388 cancelButton(this, _("CANCEL"))
1389 {
1390 setRow(4);
1391 setAlignment(Curses::PARENTCENTER);
1392 title.setAlignment(Curses::PARENTCENTER);
1393 addDialog.setRow(2);
1394 addDialog.addPrompt(&nickname_label, &nickname_field, 0);
1395 addDialog.addPrompt(&server_label, &server_field, 1);
1396 addDialog.addPrompt(&userid_label, &userid_field, 2);
1397 addDialog.addPrompt(&password_label, &password_field, 3);
1398 addDialog.addPrompt(&suffix_label, &suffix_field, 4);
1399 titleBar->setTitles(_("LDAP ADDRESSBOOK ADD"), "");
1400
1401 password_field.setPasswordChar();
1402 saveButton=this;
1403 cancelButton=this;
1404
1405 saveButton=&AddLdapScreen::save;
1406 cancelButton=&AddLdapScreen::cancel;
1407
1408 addDialog.addPrompt(NULL, &saveButton, 6);
1409 addDialog.addPrompt(NULL, &cancelButton, 8);
1410
1411 nickname_field.requestFocus();
1412 }
1413
~AddLdapScreen()1414 AddLdapScreen::~AddLdapScreen()
1415 {
1416 }
1417
processKey(const Curses::Key & key)1418 bool AddLdapScreen::processKey(const Curses::Key &key)
1419 {
1420 return GlobalKeys::processKey(key,
1421 GlobalKeys::ADDLDAPADDRESSBOOKSCREEN,
1422 NULL);
1423 }
1424
listKeys(vector<pair<string,string>> & list)1425 bool AddLdapScreen::listKeys( vector< pair<string, string> > &list)
1426 {
1427 GlobalKeys::listKeys(list, GlobalKeys::ADDLDAPADDRESSBOOKSCREEN);
1428 return false;
1429 }
1430
save()1431 void AddLdapScreen::save()
1432 {
1433 std::string name=nickname_field.getText();
1434 std::string server=server_field.getText();
1435 std::string userid=userid_field.getText();
1436 std::string password=password_field.getText();
1437 std::string suffix=suffix_field.getText();
1438
1439 if (name.size() == 0)
1440 {
1441 nickname_field.requestFocus();
1442 nickname_field.beepError();
1443 return;
1444 }
1445
1446 if (server.size() == 0)
1447 {
1448 server_field.requestFocus();
1449 server_field.beepError();
1450 return;
1451 }
1452
1453 /* TODO */
1454
1455 #if HAVE_OPENLDAP
1456
1457 {
1458 struct ldapsearch *s=l_search_alloc(server.c_str(),
1459 LDAP_PORT,
1460 userid.c_str(),
1461 password.c_str(),
1462 suffix.c_str());
1463
1464 if (s)
1465 {
1466 if (l_search_ping(s) == 0)
1467 {
1468 l_search_free(s);
1469
1470 AddressBook *abook=new AddressBook();
1471
1472 if (!abook)
1473 outofmemory();
1474
1475 try {
1476 std::string url="ldap://" + server;
1477
1478 abook->init(name,
1479 mail::loginUrlEncode
1480 ("ldap", server,
1481 userid,
1482 password),
1483 suffix);
1484
1485 AddressBook::addressBookList
1486 .insert(AddressBook::
1487 addressBookList.end(),
1488 abook);
1489 } catch (...) {
1490 delete abook;
1491 LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
1492 }
1493
1494 myServer::saveconfig();
1495 statusBar->clearstatus();
1496 statusBar->status(_("Address Book created."));
1497 cancel();
1498 return;
1499 }
1500 l_search_free(s);
1501 }
1502 statusBar->clearstatus();
1503 statusBar->status(Gettext(_("Connection to %1% failed: %2"))
1504 << name
1505 << strerror(errno),
1506 statusBar->NORMAL);
1507 statusBar->beepError();
1508 }
1509 #endif
1510 }
1511
cancel()1512 void AddLdapScreen::cancel()
1513 {
1514 keepgoing=false;
1515
1516 myServer::nextScreen= &listaddressbookScreenImport;
1517 myServer::nextScreenArg=NULL;
1518 }
1519
addLdapScreen(void * vp)1520 void addLdapScreen(void *vp)
1521 {
1522 AddLdapScreen als(mainScreen);
1523
1524 myServer::eventloop();
1525 }
1526
1527 /////////////////////////////////////////////////////////////////////////////
1528 //
1529 // List all address books
1530 //
1531
1532 class ListAddressBookScreen : public CursesContainer ,
1533 public CursesKeyHandler {
1534
1535 CursesLabel title;
1536 CursesDialog listDialog;
1537
1538 class addressbookButton : public CursesButton {
1539
1540 ListAddressBookScreen *parent;
1541
1542 public:
1543 AddressBook *addressBook;
1544 private:
1545 std::list<AddressBook *>::iterator listIterator;
1546
1547 bool processKeyInFocus(const Key &key);
1548
1549 public:
1550 addressbookButton(ListAddressBookScreen *parent,
1551 AddressBook *addressBook,
1552 list<AddressBook *>::iterator
1553 listIterator);
1554
1555 ~addressbookButton();
1556
1557 void clicked();
1558 };
1559
1560 list<addressbookButton *> buttons;
1561
1562 bool processKey(const Curses::Key &key);
1563 bool listKeys( vector< pair<string, string> > &list);
1564
1565 public:
1566 ListAddressBookScreen(CursesMainScreen *parent);
1567 ~ListAddressBookScreen();
1568
1569 void requestFocus();
1570
1571 void deleteAddressBook(list<AddressBook *>::iterator addressBook);
1572 void moveDown(list<AddressBook *>::iterator addressBook);
1573 void moveUp(list<AddressBook *>::iterator addressBook);
1574 };
1575
1576 ListAddressBookScreen::addressbookButton
addressbookButton(ListAddressBookScreen * parentArg,AddressBook * addressBookArg,list<AddressBook * >::iterator listIteratorArg)1577 ::addressbookButton(ListAddressBookScreen *parentArg,
1578 AddressBook *addressBookArg,
1579 list<AddressBook *>::iterator listIteratorArg)
1580 : CursesButton(NULL, addressBookArg->getName()),
1581 parent(parentArg),
1582 addressBook(addressBookArg),
1583 listIterator(listIteratorArg)
1584 {
1585 }
1586
~addressbookButton()1587 ListAddressBookScreen::addressbookButton::~addressbookButton()
1588 {
1589 }
1590
clicked()1591 void ListAddressBookScreen::addressbookButton::clicked()
1592 {
1593 keepgoing=false;
1594 myServer::nextScreen= &addressbookIndexScreen;
1595 myServer::nextScreenArg=addressBook;
1596 }
1597
processKeyInFocus(const Key & key)1598 bool ListAddressBookScreen::addressbookButton::processKeyInFocus(const
1599 Key &key)
1600 {
1601 if (listIterator != AddressBook::addressBookList.end())
1602 {
1603 if (key == key_DELETEADDRESSBOOK)
1604 {
1605 parent->deleteAddressBook(listIterator);
1606 return true;
1607 }
1608
1609 // Control relative search order:
1610
1611 if (key == key_DOWN) // CTRL-D
1612 {
1613 parent->moveDown(listIterator);
1614 return true;
1615 }
1616
1617 if (key == key_UP) // CTRL-U
1618 {
1619 parent->moveUp(listIterator);
1620 return true;
1621 }
1622 }
1623
1624 if (key == key_ADDLDAP)
1625 {
1626 keepgoing=false;
1627 myServer::nextScreen= &addLdapScreen;
1628 myServer::nextScreenArg=NULL;
1629 return true;
1630 }
1631
1632 return CursesButton::processKeyInFocus(key);
1633 }
1634
ListAddressBookScreen(CursesMainScreen * parent)1635 ListAddressBookScreen::ListAddressBookScreen(CursesMainScreen *parent)
1636 : CursesContainer(mainScreen),
1637 CursesKeyHandler(PRI_SCREENHANDLER),
1638 title(this, _("Address Book List")),
1639 listDialog(this)
1640 {
1641 setRow(4);
1642 setAlignment(Curses::PARENTCENTER);
1643 title.setAlignment(Curses::PARENTCENTER);
1644
1645 listDialog.setRow(2);
1646
1647 titleBar->setTitles(_("ADDRESS BOOKS"), "");
1648
1649 if (AddressBook::defaultAddressBook)
1650 {
1651 addressbookButton *bp=
1652 new addressbookButton(NULL,
1653 AddressBook::defaultAddressBook,
1654 AddressBook::addressBookList
1655 .end());
1656
1657 if (!bp)
1658 outofmemory();
1659
1660 try {
1661 buttons.insert(buttons.end(), bp);
1662 } catch (...) {
1663 delete bp;
1664 LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
1665 }
1666
1667 listDialog.addPrompt(NULL, bp);
1668 listDialog.addPrompt(NULL, NULL);
1669 }
1670
1671 list<AddressBook *>::iterator b=AddressBook::addressBookList.begin(),
1672 e=AddressBook::addressBookList.end();
1673
1674 while (b != e)
1675 {
1676 AddressBook *p= *b;
1677
1678 addressbookButton *bp=new addressbookButton(NULL, p, b);
1679
1680 b++;
1681
1682 if (!bp)
1683 outofmemory();
1684
1685 try {
1686 buttons.insert(buttons.end(), bp);
1687 } catch (...) {
1688 delete bp;
1689 LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
1690 }
1691
1692 listDialog.addPrompt(NULL, bp);
1693 listDialog.addPrompt(NULL, NULL);
1694 }
1695
1696 }
1697
requestFocus()1698 void ListAddressBookScreen::requestFocus()
1699 {
1700 list<addressbookButton *>::iterator b=buttons.begin(), e=buttons.end();
1701
1702 while (b != e)
1703 {
1704 if ( (*b)->addressBook->iwantFocus)
1705 {
1706 (*b)->addressBook->iwantFocus=false;
1707 (*b)->requestFocus();
1708 return;
1709 }
1710 ++b;
1711 }
1712
1713 b=buttons.begin();
1714 e=buttons.end();
1715
1716 if (b != e)
1717 (*b)->requestFocus();
1718 }
1719
~ListAddressBookScreen()1720 ListAddressBookScreen::~ListAddressBookScreen()
1721 {
1722 list<addressbookButton *>::iterator b=buttons.begin(), e=buttons.end();
1723
1724 while (b != e)
1725 {
1726 addressbookButton *bp= *b;
1727
1728 *b++=NULL;
1729
1730 if (bp)
1731 delete bp;
1732 }
1733 }
1734
processKey(const Curses::Key & key)1735 bool ListAddressBookScreen::processKey(const Curses::Key &key)
1736 {
1737 if (key == key_ABORT)
1738 {
1739 keepgoing=false;
1740 myServer::nextScreen= &mainMenu;
1741 myServer::nextScreenArg=NULL;
1742 PreviousScreen::previousScreen();
1743 return true;
1744 }
1745
1746 return GlobalKeys::processKey(key, GlobalKeys::ADDRESSBOOKLISTSCREEN,
1747 NULL);
1748 }
1749
listKeys(vector<pair<string,string>> & list)1750 bool ListAddressBookScreen::listKeys( vector< pair<string, string> > &list)
1751 {
1752 list.push_back(make_pair(Gettext::keyname(_("DELETEADDRESSBOOK:D")),
1753 _("Delete")));
1754 list.push_back( make_pair(Gettext::keyname(_("MOVEUP:^U")),
1755 _("Move up")));
1756 list.push_back( make_pair(Gettext::keyname(_("MOVEDN:^D")),
1757 _("Move down")));
1758 list.push_back( make_pair(Gettext::keyname(_("ADDLDAP:A")),
1759 _("Add LDAP server")));
1760
1761 GlobalKeys::listKeys(list, GlobalKeys::ADDRESSBOOKLISTSCREEN);
1762 return false;
1763 }
1764
1765
listaddressbookScreenImport(void * vp)1766 void listaddressbookScreenImport(void *vp)
1767 {
1768 ListAddressBookScreen screen(mainScreen);
1769
1770 screen.requestFocus();
1771 myServer::eventloop();
1772 }
1773
listaddressbookScreen(void * vp)1774 void listaddressbookScreen(void *vp)
1775 {
1776 AddressBook::importName="";
1777 AddressBook::importAddr="";
1778
1779 listaddressbookScreenImport(vp);
1780 }
1781
1782 void ListAddressBookScreen
deleteAddressBook(list<AddressBook * >::iterator addressBook)1783 ::deleteAddressBook(list<AddressBook *>::iterator addressBook)
1784 {
1785 myServer::promptInfo response=
1786 myServer::prompt(myServer
1787 ::promptInfo(Gettext(_("Delete address book \"%1%\"? (Y/N) "))
1788 << (*addressBook)->getName())
1789 .yesno());
1790
1791 if (response.abortflag || (string)response != "Y")
1792 return;
1793
1794 AddressBook *p= *addressBook;
1795
1796 p->close();
1797
1798 AddressBook::addressBookList.erase(addressBook);
1799 delete p;
1800
1801 myServer::saveconfig();
1802
1803 Curses::keepgoing=false;
1804 myServer::nextScreen= &listaddressbookScreenImport;
1805 myServer::nextScreenArg=NULL;
1806 }
1807
1808 void ListAddressBookScreen
moveDown(list<AddressBook * >::iterator ptr)1809 ::moveDown(list<AddressBook *>::iterator ptr)
1810 {
1811 list<AddressBook *>::iterator ptrNext=ptr;
1812
1813 ++ptrNext;
1814
1815 if (ptrNext == AddressBook::addressBookList.end())
1816 return;
1817
1818 (*ptr)->iwantFocus=true;
1819 AddressBook::addressBookList.insert(ptr, *ptrNext);
1820 AddressBook::addressBookList.erase(ptrNext);
1821 myServer::saveconfig();
1822
1823 Curses::keepgoing=false;
1824 myServer::nextScreen= &listaddressbookScreenImport;
1825 myServer::nextScreenArg=NULL;
1826 }
1827
1828 void ListAddressBookScreen
moveUp(list<AddressBook * >::iterator ptr)1829 ::moveUp(list<AddressBook *>::iterator ptr)
1830 {
1831 if (ptr == AddressBook::addressBookList.begin())
1832 return;
1833
1834 (*ptr)->iwantFocus=true;
1835
1836 list<AddressBook *>::iterator ptrPrev=ptr;
1837
1838 --ptrPrev;
1839
1840 AddressBook::addressBookList.insert(ptrPrev, *ptr);
1841 AddressBook::addressBookList.erase(ptr);
1842 myServer::saveconfig();
1843
1844 Curses::keepgoing=false;
1845 myServer::nextScreen= &listaddressbookScreenImport;
1846 myServer::nextScreenArg=NULL;
1847 }
1848
1849 //////////////////////////////////////////////////////////////////////////////
1850 //
1851 // Quick address book import
1852
1853 class AddressBookTakeScreen : public CursesContainer,
1854 public CursesKeyHandler {
1855
1856 CursesLabel title;
1857 CursesDialog indexDialog;
1858
1859 class addressButton : public CursesButton {
1860 AddressBookTakeScreen *myScreen;
1861
1862 string name;
1863 string addr;
1864
1865 public:
1866 addressButton(AddressBookTakeScreen *myScreenArg,
1867 mail::emailAddress addr);
1868 ~addressButton();
1869
1870 void clicked();
1871 };
1872
1873 vector<addressButton *> buttons;
1874
1875 bool processKey(const Curses::Key &key);
1876
1877 class indexSort {
1878 public:
1879 bool operator()(const mail::emailAddress &a,
1880 const mail::emailAddress &b);
1881 };
1882
1883 public:
requestFocus()1884 void requestFocus()
1885 {
1886 if (buttons.size() > 0)
1887 buttons[0]->requestFocus();
1888 }
1889
1890 AddressBookTakeScreen(CursesMainScreen *,
1891 vector< mail::emailAddress > &index);
1892 ~AddressBookTakeScreen();
1893 };
1894
AddressBookTakeScreen(CursesMainScreen * parent,vector<mail::emailAddress> & indexArg)1895 AddressBookTakeScreen::AddressBookTakeScreen(CursesMainScreen *parent,
1896 vector<mail::emailAddress>
1897 &indexArg)
1898 : CursesContainer(parent),
1899 CursesKeyHandler(PRI_SCREENHANDLER),
1900 title(this, "Take/Import Address"),
1901 indexDialog(this)
1902 {
1903 setRow(4);
1904 setAlignment(Curses::PARENTCENTER);
1905 title.setAlignment(Curses::PARENTCENTER);
1906
1907 indexDialog.setRow(2);
1908 titleBar->setTitles(_("TAKE ADDRESS"), "");
1909
1910 buttons.reserve(indexArg.size());
1911
1912 // Sort addresses to import
1913
1914 sort(indexArg.begin(), indexArg.end(), indexSort());
1915
1916 vector< mail::emailAddress >::iterator b=indexArg.begin(),
1917 e=indexArg.end();
1918
1919 while (b != e)
1920 {
1921 addressButton *bb=new addressButton(this, *b);
1922 b++;
1923
1924 if (!bb)
1925 outofmemory();
1926
1927 try {
1928 buttons.insert(buttons.end(), bb);
1929 } catch (...) {
1930 delete bb;
1931 LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
1932 }
1933
1934 indexDialog.addPrompt(NULL, bb);
1935 }
1936 }
1937
~AddressBookTakeScreen()1938 AddressBookTakeScreen::~AddressBookTakeScreen()
1939 {
1940 vector<addressButton *>::iterator b=buttons.begin(), e=buttons.end();
1941
1942 while (b != e)
1943 {
1944 addressButton *p= *b;
1945
1946 *b++ = NULL;
1947
1948 if (p)
1949 delete p;
1950 }
1951 }
1952
processKey(const Curses::Key & key)1953 bool AddressBookTakeScreen::processKey(const Curses::Key &key)
1954 {
1955 if (key == key_ABORT)
1956 {
1957 keepgoing=false;
1958 myServer::nextScreen= &mainMenu;
1959 myServer::nextScreenArg=NULL;
1960 PreviousScreen::previousScreen();
1961 return true;
1962 }
1963 return false;
1964 }
1965
1966 AddressBookTakeScreen::addressButton
addressButton(AddressBookTakeScreen * myScreenArg,mail::emailAddress addrArg)1967 ::addressButton(AddressBookTakeScreen *myScreenArg,
1968 mail::emailAddress addrArg)
1969 : CursesButton(myScreenArg,
1970 mail::address(addrArg.getDisplayName(unicode_default_chset()),
1971 addrArg.getDisplayAddr(unicode_default_chset())).toString()),
1972 name(addrArg.getDisplayName(unicode_default_chset())),
1973 addr(addrArg.getDisplayAddr(unicode_default_chset()))
1974 {
1975 setStyle(MENU);
1976 }
1977
~addressButton()1978 AddressBookTakeScreen::addressButton::~addressButton()
1979 {
1980 }
1981
clicked()1982 void AddressBookTakeScreen::addressButton::clicked()
1983 {
1984 AddressBook::importName=name;
1985 AddressBook::importAddr=addr;
1986
1987 Curses::keepgoing=false;
1988 myServer::nextScreen= &listaddressbookScreenImport;
1989 myServer::nextScreenArg=NULL;
1990 }
1991
takeScreen(void * arg)1992 void AddressBook::takeScreen(void *arg)
1993 {
1994 vector<mail::emailAddress> *addresses=
1995 (vector<mail::emailAddress> *)arg;
1996
1997 try {
1998 AddressBookTakeScreen takeScreen(mainScreen, *addresses);
1999
2000 delete addresses;
2001 addresses=NULL;
2002
2003 takeScreen.requestFocus();
2004 myServer::eventloop();
2005 } catch (...)
2006 {
2007 if (addresses)
2008 delete addresses;
2009 LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
2010 }
2011 }
2012
take(vector<mail::address> & addrListArg)2013 void AddressBook::take(vector<mail::address> &addrListArg)
2014 {
2015 vector<mail::emailAddress> *addrbuf;
2016
2017 addrbuf=new vector<mail::emailAddress>;
2018
2019 if (!addrbuf)
2020 LIBMAIL_THROW(strerror(errno));
2021
2022 try
2023 {
2024 map<string, mail::address *> addrMap;
2025
2026 {
2027 vector<mail::address>::iterator b=addrListArg.begin(),
2028 e=addrListArg.end();
2029
2030 while (b != e)
2031 {
2032 mail::address &a= *b++;
2033
2034 if (a.getAddr().size() == 0)
2035 continue;
2036
2037 addrMap.insert(make_pair(a.getAddr(), &a));
2038 }
2039 }
2040
2041 {
2042 addrbuf->reserve(addrMap.size());
2043
2044 map<string, mail::address *>::iterator
2045 b=addrMap.begin(),
2046 e=addrMap.end();
2047
2048 while (b != e)
2049 {
2050 addrbuf->push_back( *b->second );
2051 ++b;
2052 }
2053 }
2054 }
2055 catch (...)
2056 {
2057 delete addrbuf;
2058 LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
2059 }
2060
2061 if (addrbuf->size() == 0)
2062 {
2063 delete addrbuf;
2064
2065 statusBar->clearstatus();
2066 statusBar->status(_("No addresses found."),
2067 statusBar->NORMAL);
2068 statusBar->beepError();
2069 return;
2070 }
2071
2072 Curses::keepgoing=false;
2073 myServer::nextScreen= &takeScreen;
2074 myServer::nextScreenArg=addrbuf;
2075 }
2076
2077
updateAccount(string oldUrl,string newUrl)2078 void AddressBook::updateAccount(string oldUrl, string newUrl)
2079 {
2080 if (defaultAddressBook)
2081 defaultAddressBook->updateAccountChk(oldUrl, newUrl);
2082
2083 list<AddressBook *>::iterator b=addressBookList.begin(),
2084 e=addressBookList.end();
2085
2086 while (b != e)
2087 {
2088 (*b)->updateAccountChk(oldUrl, newUrl);
2089 b++;
2090 }
2091 }
2092
updateAccountChk(string oldUrl,string newUrl)2093 void AddressBook::updateAccountChk(string oldUrl, string newUrl)
2094 {
2095 if (url == oldUrl)
2096 url=newUrl;
2097 }
2098
2099
2100 /////////////////////////////////////////////////////////////////////////////
2101
2102 bool AddressBookTakeScreen
operator()2103 ::indexSort::operator()(const mail::emailAddress &a,
2104 const mail::emailAddress &b)
2105 {
2106 int n=a.getDisplayName("utf-8").compare(b.getDisplayName("utf-8"));
2107
2108 if (n)
2109 return n < 0;
2110
2111 return a.getDisplayAddr("utf-8").compare(b.getDisplayAddr("utf-8")) < 0;
2112 }
2113