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