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