1 /*
2 ** Copyright 2003-2011, Double Precision Inc.
3 **
4 ** See COPYING for distribution information.
5 */
6 
7 #include "gpg.H"
8 #include "gpglib/gpglib.h"
9 #include "gpglib/gpg.h"
10 #include "curses/cursescontainer.H"
11 #include "curses/curseskeyhandler.H"
12 #include "curses/curseslabel.H"
13 #include "curses/cursesbutton.H"
14 #include "curses/cursesvscroll.H"
15 #include "curses/cursesmainscreen.H"
16 #include "init.H"
17 #include <unistd.h>
18 #include "gettext.H"
19 #include "myserver.H"
20 #include <vector>
21 #include <set>
22 #include <algorithm>
23 #include <errno.h>
24 
25 extern char ucheck[];
26 
gpgprog()27 const char *gpgprog()
28 {
29 	return GPG;
30 }
31 #undef GPG
32 
33 extern Gettext::Key key_TOGGLESPACE;
34 extern Gettext::Key key_ABORT;
35 
36 GPG GPG::gpg;
37 
Key(std::string f,std::string s,std::string l,bool i)38 GPG::Key::Key(std::string f, std::string s, std::string l, bool i)
39 	: fingerprint(f), shortname(s), longname(l), invalid(i)
40 {
41 	size_t dummy;
42 
43 	mail::address::fromString(s, address, dummy);
44 }
45 
~Key()46 GPG::Key::~Key()
47 {
48 }
49 
getDescription(std::vector<std::string> & descrArray,size_t width)50 void GPG::Key::getDescription(std::vector<std::string> &descrArray,
51 			      size_t width)
52 {
53 	std::string keyDescr=longname;
54 
55 	do
56 	{
57 		std::u32string unicode_buf;
58 
59 		size_t p=keyDescr.find('\n');
60 
61 		if (p == std::string::npos)
62 		{
63 			unicode::iconvert::convert(keyDescr,
64 						unicode_default_chset(),
65 						unicode_buf);
66 			keyDescr="";
67 		}
68 		else
69 		{
70 			unicode::iconvert::convert(keyDescr.substr(0, p),
71 						unicode_default_chset(),
72 						unicode_buf);
73 			keyDescr=keyDescr.substr(p+1);
74 		}
75 
76 		std::vector< std::u32string > wrapped_text;
77 
78 		std::back_insert_iterator<std::vector< std::u32string > >
79 			insert_iter(wrapped_text);
80 
81 		unicodewordwrap(unicode_buf.begin(),
82 				unicode_buf.end(),
83 				unicoderewrapnone(),
84 				insert_iter,
85 				width,
86 				true);
87 
88 		for (std::vector< std::u32string >::const_iterator
89 			     b(wrapped_text.begin()),
90 			     e(wrapped_text.end()); b != e; ++b)
91 		{
92 			descrArray.push_back(unicode::iconvert
93 					     ::convert(*b,
94 						       unicode_default_chset())
95 					     );
96 		}
97 	} while (keyDescr.size() > 0);
98 }
99 
100 // Provide a description of columns in longname
101 
getDescriptionColumns(std::vector<std::pair<std::string,int>> & columns)102 void GPG::Key::getDescriptionColumns(std::vector<std::pair<std::string, int> > &columns)
103 {
104 	columns.clear();
105 	columns.reserve(4);
106 	columns.push_back(std::make_pair(std::string(_("Key Type        ")), 0));
107 	columns.push_back(std::make_pair(std::string(_("Created   ")), 18));
108 	columns.push_back(std::make_pair(std::string(_("Expires   ")), 29));
109 	columns.push_back(std::make_pair(std::string(_("Description")), 40));
110 }
111 
GPG()112 GPG::GPG()
113 {
114 }
115 
~GPG()116 GPG::~GPG()
117 {
118 }
119 
gpg_installed()120 bool GPG::gpg_installed()
121 {
122 	if (access(gpgprog(), X_OK))
123 		return false;
124 
125 	return true;
126 }
127 
128 // Initialize the list of available keys.  First, obtain the list
129 // using libmail_gpg_listkeys, then sort it.
130 
131 class GPG::initHelper {
132 public:
133 
134 	initHelper();
135 	~initHelper();
136 
137 	std::vector<GPG::Key> keys;
138 
139 	// ... Then sort it by address.
140 
141 	std::vector< std::pair<std::string, GPG::Key *> > sortedKeys;
142 
143 	class sort {
144 	public:
145 		sort();
146 		~sort();
147 		bool operator()(const std::pair<std::string, GPG::Key *> &,
148 				const std::pair<std::string, GPG::Key *> &);
149 	};
150 
151 	static int save_key(const char *fingerprint,
152 			    const char *shortname,
153 			    const char *longname,
154 			    int invalid_flag,
155 			    struct gpg_list_info *va);
156 
157 	void operator>>( std::vector<Key> &);
158 };
159 
initHelper()160 GPG::initHelper::initHelper()
161 {
162 }
163 
~initHelper()164 GPG::initHelper::~initHelper()
165 {
166 }
167 
save_key(const char * fingerprint,const char * shortname,const char * longname,int invalid_flag,struct gpg_list_info * va)168 int GPG::initHelper::save_key(const char *fingerprint,
169 			      const char *shortname,
170 			      const char *longname,
171 			      int invalid_flag,
172 			      struct gpg_list_info *va)
173 {
174 	GPG::initHelper *h=(GPG::initHelper *)va->voidarg;
175 
176 	h->keys.push_back(GPG::Key(fingerprint,
177 				   shortname,
178 				   longname,
179 				   invalid_flag != 0));
180 	return 0;
181 }
182 
183 
sort()184 GPG::initHelper::sort::sort()
185 {
186 }
187 
~sort()188 GPG::initHelper::sort::~sort()
189 {
190 }
191 
operator()192 bool GPG::initHelper::sort::operator()(const std::pair<std::string, GPG::Key *> &a,
193 				       const std::pair<std::string, GPG::Key *> &b)
194 {
195 	if (a.first == b.first)
196 		return a.second->longname < b.second->longname;
197 
198 	return a.first < b.first;
199 }
200 
201 void GPG::initHelper::operator>>( std::vector<Key> &keyList)
202 {
203 	sortedKeys.clear();
204 	sortedKeys.reserve(keys.size());
205 
206 	std::vector<GPG::Key>::iterator kb=keys.begin(), ke=keys.end();
207 
208 	while (kb != ke)
209 	{
210 		// Extract the address from longname
211 
212 		std::vector<mail::address> addrVec;
213 		size_t dummy;
214 		mail::address::fromString(kb->longname, addrVec, dummy);
215 
216 		std::string s;
217 
218 		if (addrVec.size() > 0)
219 			s=addrVec[0].getAddr();
220 
221 		sortedKeys.push_back(std::make_pair(s, &*kb));
222 		++kb;
223 	}
224 
225 	keyList.clear();
226 	keyList.reserve(keys.size());
227 
228 	std::sort(sortedKeys.begin(), sortedKeys.end(), sort());
229 
230 	std::vector< std::pair<std::string, GPG::Key *> >::iterator
231 		b=sortedKeys.begin(),
232 		e=sortedKeys.end();
233 
234 	while (b != e)
235 	{
236 		keyList.push_back( *b->second );
237 		b++;
238 	}
239 }
240 
save_errmsg(const char * dummy,size_t dummy2,void * va)241 static int save_errmsg(const char *dummy, size_t dummy2, void *va)
242 {
243 	return 0;
244 }
245 
init()246 void GPG::init()
247 {
248 	if (!gpg_installed())
249 		return;
250 
251 	struct gpg_list_info gli;
252 
253 	memset(&gli, 0, sizeof(gli));
254 	gli.charset=unicode_default_chset();
255 	gli.disabled_msg= _(" (disabled)");
256 	gli.revoked_msg= _(" (removed)");
257 	gli.expired_msg= _(" (disabled)");
258 	gli.group_msg= _("Group: @");
259 
260 	{
261 		initHelper pub;
262 		gli.voidarg= &pub;
263 
264 		libmail_gpg_listkeys(NULL, 0, &initHelper::save_key,
265 				     save_errmsg, &gli);
266 		libmail_gpg_listgroups(NULL, &initHelper::save_key,
267 				       &gli);
268 		pub >> public_keys;
269 	}
270 
271 	{
272 		initHelper sec;
273 		gli.voidarg= &sec;
274 
275 		libmail_gpg_listkeys(NULL, 1, &initHelper::save_key,
276 				     save_errmsg, &gli);
277 
278 		sec >> secret_keys;
279 	}
280 }
281 
282 
get_secret_key(std::string fingerprint)283 GPG::Key_iterator GPG::get_secret_key(std::string fingerprint)
284 {
285 	return get_key(secret_keys, fingerprint);
286 }
287 
get_public_key(std::string fingerprint)288 GPG::Key_iterator GPG::get_public_key(std::string fingerprint)
289 {
290 	return get_key(public_keys, fingerprint);
291 }
292 
find_secret_keys(std::vector<mail::address> & addresses,std::vector<Key_iterator> & keys)293 void GPG::find_secret_keys(std::vector<mail::address> &addresses,
294 			  std::vector<Key_iterator> &keys)
295 {
296 	return find_keys(secret_keys, addresses, keys);
297 }
298 
find_public_keys(std::vector<mail::address> & addresses,std::vector<Key_iterator> & keys)299 void GPG::find_public_keys(std::vector<mail::address> &addresses,
300 			   std::vector<Key_iterator> &keys)
301 {
302 	return find_keys(public_keys, addresses, keys);
303 }
304 
get_key(std::vector<GPG::Key> & keys,std::string fingerprint)305 GPG::Key_iterator GPG::get_key(std::vector<GPG::Key> &keys, std::string fingerprint)
306 {
307 	Key_iterator b=keys.begin(), e=keys.end();
308 
309 	while (b != e)
310 	{
311 		if (b->fingerprint == fingerprint)
312 			break;
313 		b++;
314 	}
315 
316 	return b;
317 }
318 
find_keys(std::vector<Key> & keys,std::vector<mail::address> & address_a,std::vector<Key_iterator> & keys_out)319 void GPG::find_keys(std::vector<Key> &keys, std::vector<mail::address> &address_a,
320 		   std::vector<Key_iterator> &keys_out)
321 {
322 	Key_iterator b=keys.begin(), e=keys.end();
323 
324 	while (b != e)
325 	{
326 		std::vector<mail::address>::iterator ba=address_a.begin(),
327 			ea=address_a.end();
328 
329 		while (ba != ea)
330 		{
331 			if (ba->getAddr().size() > 0)
332 			{
333 				std::vector<mail::address>::iterator
334 					bk=b->address.begin(),
335 					ek=b->address.end();
336 
337 				while (bk != ek)
338 				{
339 					if (*bk == *ba)
340 						break;
341 					bk++;
342 				}
343 
344 				if (bk != ek)
345 				{
346 					keys_out.push_back(b);
347 					break;
348 				}
349 			}
350 			ba++;
351 		}
352 		b++;
353 	}
354 }
355 
356 ////////////////////////////////////////////////////////////////////////////
357 //
358 // The popup dialog.
359 
360 class GPG::dialog : public CursesContainer, public CursesKeyHandler {
361 
362 	std::u32string utitle;
363 	std::vector<GPG::Key> &keys;
364 
365 	class Keylist : public CursesVScroll {
366 		GPG::dialog *parent;
367 		size_t currentRow;
368 
369 		void drawKey(size_t);
370 
371 	public:
372 		bool multiMode;
373 		std::set<size_t> selectedKeys;
374 
375 		Keylist(GPG::dialog *parentArg,
376 			std::string fingerprintArg);
377 		Keylist(GPG::dialog *parentArg,
378 			std::vector<std::string> &selectedFingerprints);
379 		~Keylist();
380 		// This is a child of CursesFileReq, and its size is
381 		// automatically extended to the bottom of its parent.
382 		//  Its width is the same as the parent's width.
383 
384 		int getHeight() const;
385 		int getWidth() const;
386 
387 		bool isFocusable(); // Yes we are.
388 
389 		void focusGained(); // Move to the first row
390 		void focusLost();   // Turn off cursor
391 		void draw();
392 
393 		// Even though this is a CursesContainer subclass, its focus
394 		// behavior must be the same as Curses's focus behavior
395 		// (the default CursesContainer implementation doesn't work,
396 		// because this object does not have any children).
397 
398 		Curses *getPrevFocus();
399 		Curses *getNextFocus();
400 
401 		int getCursorPosition(int &row, int &col);
402 		bool processKeyInFocus(const Key &key);
403 
getCurrentRow()404 		size_t getCurrentRow() const { return currentRow; }
405 	};
406 
407 	Keylist keylist;
408 	bool closing;
409 	std::string fingerprint;
410 	std::string cancelDescr;
411 	std::string enterDescr;
412 	void init(std::string);
413 
414 public:
415 	dialog(std::vector<GPG::Key> &keys, std::string fingerprintArg,
416 	       std::string title, std::string cancelDescrArg, std::string enterDescrArg);
417 	dialog(std::vector<GPG::Key> &keys, std::vector<std::string> &fingerprints,
418 	       std::string title, std::string cancelDescrArg, std::string enterDescrArg);
419 	~dialog();
420 
orderlyClose()421 	bool orderlyClose() const { return closing; }
422 
423 	bool isDialog() const;	// Yes we are
424 	void resized();
425 	void draw();
426 	void requestFocus();
427 	int getWidth() const;
428 	int getHeight() const;
429 
430 	void selected(size_t);
431 
string()432 	operator std::string()
433 	{
434 		return fingerprint;
435 	}
436 
437 	void operator>>(std::vector<std::string> &);
438 
439 private:
440 	bool processKey(const Curses::Key &key);
441 	bool listKeys( std::vector< std::pair<std::string, std::string> > &list);
442 };
443 
Keylist(GPG::dialog * parentArg,std::string fingerprint)444 GPG::dialog::Keylist::Keylist(GPG::dialog *parentArg,
445 			      std::string fingerprint)
446 	: CursesVScroll(parentArg),
447 	  parent(parentArg), currentRow(0), multiMode(false)
448 {
449 	std::vector<GPG::Key>::iterator b=parentArg->keys.begin(),
450 		e=parentArg->keys.end();
451 	size_t r=0;
452 
453 	while (b != e)
454 	{
455 		if (b->fingerprint == fingerprint)
456 			currentRow=r;
457 
458 		r++;
459 		b++;
460 	}
461 }
462 
Keylist(GPG::dialog * parentArg,std::vector<std::string> & selectedFingerprints)463 GPG::dialog::Keylist::Keylist(GPG::dialog *parentArg,
464 			      std::vector<std::string> &selectedFingerprints)
465 	: CursesVScroll(parentArg),
466 	  parent(parentArg), currentRow(0), multiMode(true)
467 {
468 	std::set<std::string> fingerprintSet;
469 
470 	fingerprintSet.insert(selectedFingerprints.begin(),
471 			      selectedFingerprints.end());
472 
473 	size_t n=0;
474 
475 	for (n=0; n<parentArg->keys.size(); n++)
476 		if (fingerprintSet.count(parent->keys[n].fingerprint) > 0)
477 			selectedKeys.insert(n);
478 }
479 
~Keylist()480 GPG::dialog::Keylist::~Keylist()
481 {
482 }
483 
484 void GPG::dialog::operator>> (std::vector<std::string> &array)
485 {
486 	array.clear();
487 
488 	std::set<size_t>::iterator b=keylist.selectedKeys.begin(),
489 		e=keylist.selectedKeys.end();
490 
491 	while (b != e)
492 		array.push_back( keys[*b++].fingerprint);
493 
494 	// If user hit ENTER without specifying a single key, use the one
495 	// highlighted by the cursor.
496 
497 	if (array.size() == 0 && fingerprint.size() > 0)
498 		array.push_back( fingerprint );
499 }
500 
getHeight()501 int GPG::dialog::Keylist::getHeight() const
502 {
503 	int h=parent->getHeight();
504 	int r=getRow();
505 
506 	return (r < h ? h-r:1);
507 }
508 
getWidth()509 int GPG::dialog::Keylist::getWidth() const
510 {
511 	return parent->getWidth();
512 }
513 
isFocusable()514 bool GPG::dialog::Keylist::isFocusable()
515 {
516 	return true;
517 }
518 
519 
focusGained()520 void GPG::dialog::Keylist::focusGained()
521 {
522 	draw();
523 }
524 
focusLost()525 void GPG::dialog::Keylist::focusLost()
526 {
527 	draw();
528 }
529 
draw()530 void GPG::dialog::Keylist::draw()
531 {
532 	size_t n, wh;
533 	size_t i;
534 
535 	getVerticalViewport(n, wh);
536 
537 	for (i=0; i<wh; i++)
538 		drawKey(i+n);
539 }
540 
drawKey(size_t n)541 void GPG::dialog::Keylist::drawKey(size_t n)
542 {
543 	std::u32string line;
544 
545 	if (n < parent->keys.size())
546 	{
547 		unicode::iconvert::convert(parent->keys[n].shortname,
548 					unicode_default_chset(),
549 					line);
550 		line.insert(line.begin(), ' ');
551 		line.insert(line.begin(), ' ');
552 	}
553 
554 	CursesAttr attr;
555 
556 	if (selectedKeys.count(n) > 0 && line.size() > 0)
557 	{
558 		std::u32string uc;
559 
560 		unicode::iconvert::convert(std::string(ucheck),
561 					unicode_default_chset(), uc);
562 
563 		if (uc.size() > 0)
564 		    line[0]=uc[0];
565 
566 		attr.setHighlight();
567 	}
568 
569 	if (n == currentRow)
570 		attr.setReverse();
571 
572 	{
573 		widecharbuf wc;
574 
575 		wc.init_unicode(line.begin(), line.end());
576 
577 		writeText(wc.get_unicode_fixedwidth(getWidth(), 0),
578 			  n, 0, attr);
579 	}
580 }
581 
582 // Even though this is a CursesContainer subclass, its focus
583 // behavior must be the same as Curses's focus behavior
584 // (the default CursesContainer implementation doesn't work,
585 // because this object does not have any children).
586 
getPrevFocus()587 Curses *GPG::dialog::Keylist::getPrevFocus()
588 {
589 	return Curses::getPrevFocus();
590 }
591 
getNextFocus()592 Curses *GPG::dialog::Keylist::getNextFocus()
593 {
594 	return Curses::getNextFocus();
595 }
596 
getCursorPosition(int & row,int & col)597 int GPG::dialog::Keylist::getCursorPosition(int &row, int &col)
598 {
599 	if (currentRow >= parent->keys.size())
600 	{
601 		row=0;
602 		col=0;
603 		CursesVScroll::getCursorPosition(row, col);
604 		return 0;
605 	}
606 
607 
608 	row=currentRow;
609 	col=0;
610 	CursesVScroll::getCursorPosition(row, col);
611 	return 0;
612 }
613 
processKeyInFocus(const Key & key)614 bool GPG::dialog::Keylist::processKeyInFocus(const Key &key)
615 {
616 	if (key == key.ENTER)
617 	{
618 		if (currentRow < parent->keys.size())
619 		{
620 			parent->selected(currentRow);
621 			return true;
622 		}
623 	}
624 
625 
626 	if (key == key_TOGGLESPACE)
627 	{
628 		if (multiMode)
629 		{
630 			if (currentRow < parent->keys.size())
631 			{
632 				std::set<size_t>::iterator p=
633 					selectedKeys.find(currentRow);
634 
635 				if (p == selectedKeys.end())
636 					selectedKeys.insert(currentRow);
637 				else
638 					selectedKeys.erase(p);
639 			}
640 			draw();
641 			return true;
642 		}
643 	}
644 
645 	if (key == key.UP)
646 	{
647 		if (currentRow > 0)
648 		{
649 			--currentRow;
650 			parent->draw();
651 		}
652 		return true;
653 	}
654 
655 	if (key == key.DOWN)
656 	{
657 		if (currentRow + 1 < parent->keys.size())
658 		{
659 			++currentRow;
660 			parent->draw();
661 		}
662 		return true;
663 	}
664 
665 	if (key == key.PGUP)
666 	{
667 		size_t n, wh;
668 
669 		getVerticalViewport(n, wh);
670 
671 		if (currentRow > 0)
672 		{
673 			currentRow -= currentRow < wh ? currentRow:wh;
674 			parent->draw();
675 		}
676 		return true;
677 	}
678 
679 	if (key == key.PGDN)
680 	{
681 		size_t n, wh;
682 
683 		getVerticalViewport(n, wh);
684 
685 		currentRow += wh;
686 
687 		if (currentRow >= parent->keys.size())
688 		{
689 			currentRow=parent->keys.size();
690 
691 			if (currentRow)
692 				--currentRow;
693 		}
694 		parent->draw();
695 		return true;
696 	}
697 
698 	return false;
699 }
700 
dialog(std::vector<GPG::Key> & keysArg,std::string fingerprintArg,std::string title,std::string cancelDescrArg,std::string enterDescrArg)701 GPG::dialog::dialog(std::vector<GPG::Key> &keysArg, std::string fingerprintArg,
702 		    std::string title, std::string cancelDescrArg, std::string enterDescrArg)
703 	: CursesContainer(mainScreen), CursesKeyHandler(PRI_DIALOGHANDLER),
704 	  keys(keysArg),
705 	  keylist(this, fingerprintArg), closing(false),
706 	  cancelDescr(cancelDescrArg), enterDescr(enterDescrArg)
707 {
708 	init(title);
709 }
710 
dialog(std::vector<GPG::Key> & keysArg,std::vector<std::string> & fingerprints,std::string title,std::string cancelDescrArg,std::string enterDescrArg)711 GPG::dialog::dialog(std::vector<GPG::Key> &keysArg, std::vector<std::string> &fingerprints,
712 		    std::string title, std::string cancelDescrArg, std::string enterDescrArg)
713 	: CursesContainer(mainScreen), CursesKeyHandler(PRI_DIALOGHANDLER),
714 	  keys(keysArg),
715 	  keylist(this, fingerprints), closing(false),
716 	  cancelDescr(cancelDescrArg), enterDescr(enterDescrArg)
717 {
718 	init(title);
719 }
720 
init(std::string titleArg)721 void GPG::dialog::init(std::string titleArg)
722 {
723 	keylist.setRow(11);
724 
725 	unicode::iconvert::convert(titleArg, unicode_default_chset(), utitle);
726 }
727 
~dialog()728 GPG::dialog::~dialog()
729 {
730 	if (closing)
731 		Curses::keepgoing=true;
732 }
733 
isDialog()734 bool GPG::dialog::isDialog() const
735 {
736 	return true;
737 }
738 
resized()739 void GPG::dialog::resized()
740 {
741 	CursesContainer::resized();
742 }
743 
draw()744 void GPG::dialog::draw()
745 {
746 	CursesContainer::draw();
747 
748 	size_t r=keylist.getCurrentRow();
749 
750 	std::string keyDescr;
751 
752 	std::vector<std::string> rows;
753 
754 	if (r < keys.size())
755 		keys[r].getDescription(rows, getWidth());
756 
757 	Curses::CursesAttr attr;
758 
759 	std::vector<std::pair<std::string, int> > columns;
760 
761 	GPG::Key::getDescriptionColumns(columns);
762 
763 	CursesAttr columnAttr;
764 
765 	columnAttr.setUnderline();
766 
767 	std::vector<std::pair<std::string, int> >::iterator
768 		cb=columns.begin(), ce=columns.end();
769 
770 	while (cb != ce)
771 	{
772 		writeText(cb->first.c_str(), 3, cb->second, columnAttr);
773 		++cb;
774 	}
775 
776 	for (r=0; r<6; r++)
777 	{
778 		widecharbuf wc;
779 
780 		if (r < rows.size())
781 			wc.init_string(rows[r]);
782 
783 		writeText(wc.get_unicode_fixedwidth(getWidth(), 0),
784 			  r+4, 0, attr);
785 	}
786 
787 	std::u32string w;
788 
789 	w.insert(w.end(), getWidth(), '-');
790 	writeText(w, r+3, 0, attr);
791 
792 	size_t ww=getWidth();
793 
794 	{
795 		widecharbuf wc;
796 
797 		wc.init_unicode(utitle.begin(), utitle.end());
798 		wc.expandtabs(0);
799 
800 		size_t grapheme_width=wc.wcwidth(0);
801 
802 		if (grapheme_width)
803 			--grapheme_width;
804 
805 		ww= grapheme_width > ww ? 0: ww-grapheme_width;
806 
807 		Curses::CursesAttr wAttr;
808 
809 		std::u32string s;
810 
811 		wc.tounicode(s);
812 
813 		writeText(s, 1, ww/2, wAttr);
814 	}
815 }
816 
requestFocus()817 void GPG::dialog::requestFocus()
818 {
819 	keylist.requestFocus();
820 }
821 
getWidth()822 int GPG::dialog::getWidth() const
823 {
824 	return mainScreen->getWidth();
825 }
826 
getHeight()827 int GPG::dialog::getHeight() const
828 {
829 	return mainScreen->getHeight();
830 }
831 
listKeys(std::vector<std::pair<std::string,std::string>> & list)832 bool GPG::dialog::listKeys( std::vector< std::pair<std::string, std::string> >
833 			    &list)
834 {
835 	list.push_back( std::make_pair(Gettext::keyname(_("ABORT:^C")),
836 				  cancelDescr));
837 
838 	list.push_back( std::make_pair(Gettext::keyname(_("ENTER:Enter")),
839 				  enterDescr));
840 
841 	if (keylist.multiMode)
842 		list.push_back( std::make_pair(Gettext::keyname(_("SPACE:Space")),
843 					  _("Select/Unselect")));
844 
845 	return true;
846 }
847 
processKey(const Curses::Key & key)848 bool GPG::dialog::processKey(const Curses::Key &key)
849 {
850 
851 	if (key == key_ABORT)
852 	{
853 		closing=true;
854 		Curses::keepgoing=false;
855 		if (keylist.multiMode)
856 			keylist.selectedKeys.clear();
857 		return true;
858 	}
859 	return key.plain() && (unsigned char)key.ukey == key.ukey &&
860 		(unsigned char)key.ukey < ' ';
861 }
862 
selected(size_t r)863 void GPG::dialog::selected(size_t r)
864 {
865 	fingerprint=keys[r].fingerprint;
866 	Curses::keepgoing=false;
867 	closing=true;
868 }
869 
select_private_key(std::string fingerprint,std::string title,std::string prompt,std::string enterDescr,std::string abortDescr)870 std::string GPG::select_private_key(std::string fingerprint,
871 			       std::string title,
872 			       std::string prompt,
873 			       std::string enterDescr,
874 			       std::string abortDescr)
875 {
876 	return select_key(secret_keys, fingerprint, title, prompt,
877 			  enterDescr, abortDescr);
878 }
879 
select_public_key(std::string fingerprint,std::string title,std::string prompt,std::string enterDescr,std::string abortDescr)880 std::string GPG::select_public_key(std::string fingerprint,
881 			      std::string title,
882 			      std::string prompt,
883 			      std::string enterDescr,
884 			      std::string abortDescr)
885 {
886 	return select_key(public_keys, fingerprint, title, prompt,
887 			  enterDescr, abortDescr);
888 }
889 
select_key(std::vector<Key> & keyVec,std::string fingerprint,std::string title,std::string prompt,std::string enterDescr,std::string abortDescr)890 std::string GPG::select_key( std::vector<Key> &keyVec, std::string fingerprint,
891 			std::string title, std::string prompt,
892 			std::string enterDescr,
893 			std::string abortDescr)
894 {
895 	if (keyVec.size() == 0)
896 	{
897 		statusBar->clearstatus();
898 		statusBar->status(_("No keys are installed."));
899 		return "";
900 	}
901 
902 	myServer::nextScreen=NULL;
903 
904 	std::string s;
905 
906 	{
907 		GPG::dialog myKeylist(keyVec, fingerprint, title,
908 				      abortDescr, enterDescr);
909 		// _("SELECT PRIVATE KEY"));
910 
911 		myKeylist.requestFocus();
912 
913 		statusBar->clearstatus();
914 		statusBar->status(prompt);
915 
916 		myServer::eventloop();
917 		mainScreen->erase();
918 
919 		if (!myKeylist.orderlyClose())
920 			return ""; // Some kind of an interrupt
921 
922 		s=myKeylist;
923 		Curses::keepgoing=true;
924 	}
925 
926 	mainScreen->draw();
927 	return s;
928 }
929 
select_public_keys(std::vector<std::string> & fingerprints,std::string title,std::string prompt,std::string enterDescr,std::string abortDescr)930 void GPG::select_public_keys(std::vector<std::string> &fingerprints,
931 			     std::string title,
932 			     std::string prompt,
933 			     std::string enterDescr,
934 			     std::string abortDescr)
935 {
936 	if (public_keys.size() == 0)
937 	{
938 		statusBar->clearstatus();
939 		statusBar->status(_("No keys are installed."));
940 		return;
941 	}
942 
943 	myServer::nextScreen=NULL;
944 
945 	{
946 		GPG::dialog myKeylist(public_keys, fingerprints, title,
947 				      abortDescr, enterDescr);
948 
949 		myKeylist.requestFocus();
950 
951 		statusBar->clearstatus();
952 		statusBar->status(prompt);
953 
954 		myServer::eventloop();
955 		mainScreen->erase();
956 
957 		if (!myKeylist.orderlyClose())
958 			return; // Some kind of an interrupt
959 
960 		Curses::keepgoing=true;
961 
962 		myKeylist >> fingerprints;
963 	}
964 
965 	mainScreen->draw();
966 }
967 
968 /////////////////////////////////////////////////////////////////////////
969 //
970 // Common code creates libmail_gpg_info.argv.  Since this is C++, we
971 // try not to do dynamic alloc.  The caller supplies the std::vector<std::string>
972 // arguments, and we allocate memory using C++ vectors.  The caller supplies
973 // two vectors, as follows.  The final argv array is argv_cp
974 
create_argv(std::vector<std::string> & argv,std::vector<std::vector<char>> & argv_ptr,std::vector<char * > & argv_cp)975 void GPG::create_argv(std::vector<std::string> &argv,
976 		      std::vector< std::vector<char> > &argv_ptr,
977 		      std::vector<char *> &argv_cp)
978 {
979 	std::vector<std::string>::iterator ekb, eke;
980 
981 	ekb=argv.begin();
982 	eke=argv.end();
983 
984 	while (ekb != eke)
985 	{
986 		std::vector<char> vc;
987 
988 		vc.insert(vc.end(), ekb->begin(), ekb->end());
989 		vc.push_back(0);
990 
991 		argv_ptr.push_back(vc);
992 		++ekb;
993 	}
994 
995 	std::vector< std::vector<char> >::iterator ab=argv_ptr.begin(),
996 		ae=argv_ptr.end();
997 
998 	while (ab != ae)
999 	{
1000 		argv_cp.push_back( &(*ab)[0]);
1001 		++ab;
1002 	}
1003 }
1004 
1005 // Export a key
1006 
1007 class GPG::exportKeyHelper {
1008 
1009 	std::ostream &oStream;
1010 
1011 	std::string errmsg;
1012 
1013 public:
1014 	exportKeyHelper(std::ostream &oStreamArg);
1015 	~exportKeyHelper();
1016 
1017 	static int out_func(const char *p, size_t n, void *);
1018 	static int err_func(const char *p, size_t n, void *);
1019 
1020 	int out_func(const char *p, size_t n);
1021 	int err_func(const char *p, size_t n);
1022 
string()1023 	operator std::string() const { return errmsg; }
1024 };
1025 
exportKeyHelper(std::ostream & oStreamArg)1026 GPG::exportKeyHelper::exportKeyHelper(std::ostream &oStreamArg)
1027 	: oStream(oStreamArg)
1028 {
1029 }
1030 
~exportKeyHelper()1031 GPG::exportKeyHelper::~exportKeyHelper()
1032 {
1033 }
1034 
out_func(const char * p,size_t n,void * vp)1035 int GPG::exportKeyHelper::out_func(const char *p, size_t n, void *vp)
1036 {
1037 	return ( ((GPG::exportKeyHelper *)vp)->out_func(p, n));
1038 }
1039 
err_func(const char * p,size_t n,void * vp)1040 int GPG::exportKeyHelper::err_func(const char *p, size_t n, void *vp)
1041 {
1042 	return ( ((GPG::exportKeyHelper *)vp)->err_func(p, n));
1043 }
1044 
out_func(const char * p,size_t n)1045 int GPG::exportKeyHelper::out_func(const char *p, size_t n)
1046 {
1047 	return oStream.write(p, n).fail() ? -1:0;
1048 }
1049 
err_func(const char * p,size_t n)1050 int GPG::exportKeyHelper::err_func(const char *p, size_t n)
1051 {
1052 	errmsg += std::string(p, p+n);
1053 	return 0;
1054 }
1055 
exportKey(std::string fingerprint,bool secret,std::ostream & oStream)1056 std::string GPG::exportKey(std::string fingerprint,
1057 		      bool secret,
1058 		      std::ostream &oStream)
1059 {
1060 	exportKeyHelper h(oStream);
1061 
1062 	if (libmail_gpg_exportkey("", secret ? 1:0, fingerprint.c_str(),
1063 				  &GPG::exportKeyHelper::out_func,
1064 				  &GPG::exportKeyHelper::err_func, &h) == 0)
1065 		return "";
1066 
1067 	return h;
1068 }
1069 
1070 //////////////////////////////////////////////////////////////////////////
1071 //
1072 // A dialog confirming a key selection.
1073 
1074 class GPG::confirmHelper : public CursesContainer {
1075 
1076 	CursesLabel prompt;
1077 
1078 	class keyDisplay : public CursesContainer {
1079 
1080 		std::vector<CursesLabel *> labels;
1081 	public:
1082 		keyDisplay(CursesContainer *parent, GPG::Key &key);
1083 		~keyDisplay();
1084 
1085 	private:
1086 		CursesLabel *add(std::string, CursesAttr);
1087 	};
1088 
1089 	keyDisplay key1, key2;
1090 
1091 	CursesButtonRedirect<GPG::confirmHelper> okButton, cancelButton;
1092 
1093 	bool returnCode;
1094 
1095 public:
1096 	confirmHelper(CursesContainer *parent,
1097 		      std::string promptStr,
1098 		      GPG::Key &selectedKey,
1099 		      GPG::Key *key2,
1100 		      std::string okDescr,
1101 		      std::string cancelDescr);
1102 
1103 	bool isDialog() const;
1104 	int getWidth() const;
1105 
1106 	operator bool() const;
1107 	~confirmHelper();
1108 
1109 private:
1110 	void doOk();
1111 	void doCancel();
1112 
1113 };
1114 
keyDisplay(CursesContainer * parent,GPG::Key & key)1115 GPG::confirmHelper::keyDisplay::keyDisplay(CursesContainer *parent,
1116 					   GPG::Key &key)
1117 	: CursesContainer(parent)
1118 {
1119 	std::vector<std::pair<std::string, int> > columns;
1120 
1121 	GPG::Key::getDescriptionColumns(columns);
1122 
1123 	std::vector<std::pair<std::string, int> >::iterator cb=columns.begin(),
1124 		ce=columns.end();
1125 
1126 	{
1127 		CursesAttr headingAttr;
1128 
1129 		headingAttr.setUnderline();
1130 
1131 		while (cb != ce)
1132 		{
1133 			CursesLabel *l=add(cb->first, headingAttr);
1134 
1135 			l->setCol(cb->second);
1136 			++cb;
1137 		}
1138 	}
1139 
1140 	std::vector<std::string> description;
1141 
1142 	key.getDescription(description, parent ? parent->getWidth():80);
1143 
1144 	size_t i;
1145 	CursesAttr norm_attr;
1146 
1147 	for (i=0; i<description.size() && i < 10; i++)
1148 		add(description[i], norm_attr)->setRow(i+1);
1149 }
1150 
~keyDisplay()1151 GPG::confirmHelper::keyDisplay::~keyDisplay()
1152 {
1153 	std::vector<CursesLabel *>::iterator b=labels.begin(), e=labels.end();
1154 
1155 	while (b != e)
1156 	{
1157 		if (*b)
1158 			delete *b;
1159 		*b=NULL;
1160 		++b;
1161 	}
1162 }
1163 
add(std::string text,CursesAttr attr)1164 CursesLabel *GPG::confirmHelper::keyDisplay::add(std::string text, CursesAttr attr)
1165 {
1166 	CursesLabel *p=new CursesLabel(this, text, attr);
1167 
1168 	if (!p)
1169 		throw strerror(errno);
1170 
1171 	try {
1172 		labels.push_back(p);
1173 	} catch (...) {
1174 		delete p;
1175 		throw;
1176 	}
1177 	return p;
1178 }
1179 
confirmHelper(CursesContainer * parent,std::string promptStr,GPG::Key & selectedKey,GPG::Key * key2Arg,std::string okDescr,std::string cancelDescr)1180 GPG::confirmHelper::confirmHelper(CursesContainer *parent,
1181 				  std::string promptStr,
1182 				  GPG::Key &selectedKey,
1183 				  GPG::Key *key2Arg,
1184 				  std::string okDescr,
1185 				  std::string cancelDescr)
1186 	: CursesContainer(parent),
1187 	  prompt(this, promptStr),
1188 
1189 	  key1(this, selectedKey),
1190 	  key2(key2Arg ? this:NULL, key2Arg ? *key2Arg:selectedKey),
1191 
1192 	  okButton(this, okDescr),
1193 	  cancelButton(this, cancelDescr),
1194 	  returnCode(false)
1195 {
1196 	prompt.setRow(1);
1197 	key1.setRow(3);
1198 
1199 	size_t r=5+key1.getHeight();
1200 
1201 	if (key2Arg)
1202 	{
1203 		key2.setRow(r);
1204 		r += key2.getHeight() + 1;
1205 	}
1206 	okButton.setRow(r);
1207 	cancelButton.setRow(okButton.getRow() + 2);
1208 
1209 	okButton.setCol(4);
1210 	cancelButton.setCol(4);
1211 
1212 	okButton=this;
1213 	cancelButton=this;
1214 
1215 	okButton=&GPG::confirmHelper::doOk;
1216 	cancelButton=&GPG::confirmHelper::doCancel;
1217 
1218 	okButton.requestFocus();
1219 }
1220 
1221 
isDialog()1222 bool GPG::confirmHelper::isDialog() const
1223 {
1224 	return true;
1225 }
1226 
getWidth()1227 int GPG::confirmHelper::getWidth() const
1228 {
1229 	return getParent()->getWidth();
1230 }
1231 
1232 GPG::confirmHelper::operator bool() const
1233 {
1234 	return returnCode;
1235 }
1236 
~confirmHelper()1237 GPG::confirmHelper::~confirmHelper()
1238 {
1239 }
1240 
doOk()1241 void GPG::confirmHelper::doOk()
1242 {
1243 	returnCode=true;
1244 	Curses::keepgoing=false;
1245 }
1246 
doCancel()1247 void GPG::confirmHelper::doCancel()
1248 {
1249 	Curses::keepgoing=false;
1250 }
1251 
confirmKeySelection(std::string prompt,Key & selectedKey,Key * key2,std::string okDescr,std::string cancelDescr)1252 bool GPG::confirmKeySelection(std::string prompt,
1253 			      Key &selectedKey,
1254 			      Key *key2,
1255 
1256 			      std::string okDescr,
1257 			      std::string cancelDescr)
1258 {
1259 	bool rc;
1260 
1261 	{
1262 		confirmHelper helperDialog(mainScreen, prompt, selectedKey,
1263 					   key2,
1264 					   okDescr,
1265 					   cancelDescr);
1266 
1267 		myServer::eventloop();
1268 		rc=helperDialog;
1269 		if (myServer::nextScreen)
1270 			return false; // Interrupted, for some reason.
1271 		Curses::keepgoing=true;
1272 	}
1273 
1274 	mainScreen->erase();
1275 	mainScreen->draw();
1276 	return rc;
1277 }
1278