1 /* gpgmetools.h - Additional gpgme support functions for GPA.
2    Copyright (C) 2002 Miguel Coca.
3    Copyright (C) 2005, 2008, 2009, 2012, 2014, 2015 g10 Code GmbH.
4 
5    This file is part of GPA
6 
7    GPA is free software; you can redistribute it and/or modify it
8    under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11 
12    GPA is distributed in the hope that it will be useful, but WITHOUT
13    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
15    License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 
22 /* A set of auxiliary functions for common tasks related to GPGME */
23 
24 #include <config.h>
25 
26 #include <errno.h>
27 #include <ctype.h>
28 #include <stdarg.h>
29 
30 #include "gpa.h"
31 #include "gtktools.h"
32 #include "gpgmetools.h"
33 
34 #include <fcntl.h>
35 #ifdef G_OS_UNIX
36 #include <unistd.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <sys/wait.h>
40 #else
41 #include <io.h>
42 #endif
43 #ifdef G_OS_WIN32
44 #include <windows.h>
45 #endif
46 
47 #include <glib/gstdio.h>
48 
49 #ifndef O_BINARY
50 #ifdef _O_BINARY
51 #define O_BINARY	_O_BINARY
52 #else
53 #define O_BINARY	0
54 #endif
55 #endif
56 
57 /* Helper to strip the path from a source file name.  This helps to
58    avoid showing long filenames in case of VPATH builds.  */
59 static const char *
strip_path(const char * file)60 strip_path (const char *file)
61 {
62   const char *s = strrchr (file, '/');
63   return s? s+1:file;
64 }
65 
66 
67 
68 /* Report an unexpected error in GPGME and quit the application.  */
69 void
_gpa_gpgme_error(gpg_error_t err,const char * file,int line)70 _gpa_gpgme_error (gpg_error_t err, const char *file, int line)
71 {
72   gchar *message = g_strdup_printf (_("Fatal Error in GPGME library\n"
73                                       "(invoked from file %s, line %i):\n\n"
74                                       "\t%s\n\n"
75                                       "The application will be terminated"),
76                                     strip_path (file), line,
77                                     gpgme_strerror (err));
78   gpa_window_error (message, NULL);
79   g_free (message);
80   exit (EXIT_FAILURE);
81 }
82 
83 
84 /* (Please use the gpa_gpgme_warn macros).  */
85 void
_gpa_gpgme_warn(gpg_error_t err,const char * desc,GpaContext * ctx,const char * file,int line)86 _gpa_gpgme_warn (gpg_error_t err, const char *desc, GpaContext *ctx,
87                  const char *file, int line)
88 {
89   char *argbuf = NULL;
90   const char *arg;
91 
92   if (desc && (!err || gpg_err_code (err) == GPG_ERR_GENERAL))
93     arg = desc;
94   else if (desc)
95     {
96       argbuf = g_strdup_printf ("%s (%s)", gpgme_strerror (err), desc);
97       arg = argbuf;
98     }
99   else
100     arg = gpgme_strerror (err);
101 
102   gpa_show_warn (NULL, ctx,
103                  _("The GPGME library returned an unexpected\n"
104                    "error at %s:%d. The error was:\n\n"
105                    "\t%s\n\n"
106                    "This is either an installation problem or a bug in %s.\n"
107                    "%s will now try to recover from this error."),
108                  strip_path (file), line, arg, GPA_NAME, GPA_NAME);
109   g_free (argbuf);
110 }
111 
112 
113 /* Initialize a gpgme_ctx_t for use with GPA.  */
114 gpgme_ctx_t
gpa_gpgme_new(void)115 gpa_gpgme_new (void)
116 {
117   gpgme_ctx_t ctx;
118   gpg_error_t err;
119 
120   /* g_assert (!"using gpa_gpgme_new"); */
121   err = gpgme_new (&ctx);
122   if (gpg_err_code (err) != GPG_ERR_NO_ERROR)
123     gpa_gpgme_error (err);
124 
125   if (! cms_hack)
126     gpgme_set_passphrase_cb (ctx, gpa_passphrase_cb, NULL);
127 
128   return ctx;
129 }
130 
131 
132 /* Write the contents of the gpgme_data_t object to the file.
133    Receives a filehandle instead of the filename, so that the caller
134    can make sure the file is accesible before putting anything into
135    data.  This is only used for a TMP file, thus it is okay to
136    terminate the application on error. */
137 void
dump_data_to_file(gpgme_data_t data,FILE * file)138 dump_data_to_file (gpgme_data_t data, FILE *file)
139 {
140   char buffer[128];
141   int nread;
142 
143   nread = gpgme_data_seek (data, 0, SEEK_SET);
144   if (nread == -1)
145     {
146       gpa_window_error (strerror (errno), NULL);
147       exit (EXIT_FAILURE);
148     }
149   while ((nread = gpgme_data_read (data, buffer, sizeof (buffer))) > 0)
150     fwrite (buffer, nread, 1, file);
151   if (nread == -1)
152     {
153       gpa_window_error (strerror (errno), NULL);
154       exit (EXIT_FAILURE);
155     }
156   return;
157 }
158 
159 
160 static char *
check_overwriting(const char * filename,GtkWidget * parent)161 check_overwriting (const char *filename, GtkWidget *parent)
162 {
163   GtkWidget *dialog;
164   int response;
165   GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_SAVE;
166   char *filename_used = xstrdup (filename);
167 
168   while (1)
169     {
170       /* If the file exists, ask before overwriting.  */
171       if (! g_file_test (filename_used, G_FILE_TEST_EXISTS))
172         return filename_used;
173 
174       dialog = gtk_message_dialog_new
175         (GTK_WINDOW (parent), GTK_DIALOG_MODAL,
176          GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE,
177          _("The file %s already exists.\n"
178            "Do you want to overwrite it?"), filename_used);
179       gtk_dialog_add_buttons (GTK_DIALOG (dialog),
180                               _("_Yes"), GTK_RESPONSE_YES,
181                               _("_No"), GTK_RESPONSE_NO,
182                               _("_Use a different filename"), 1,
183                               NULL);
184 
185       response = gtk_dialog_run (GTK_DIALOG (dialog));
186       gtk_widget_destroy (dialog);
187       if (response == GTK_RESPONSE_YES)
188         return filename_used;
189       if (response == GTK_RESPONSE_NO)
190         {
191           xfree (filename_used);
192           return NULL;
193         }
194 
195       /* Use a different filename.  */
196       dialog = gtk_file_chooser_dialog_new
197         ("Open File", GTK_WINDOW (parent), action,
198          _("_Cancel"), GTK_RESPONSE_CANCEL,
199          _("_Open"), GTK_RESPONSE_ACCEPT,
200          NULL);
201       response = gtk_dialog_run (GTK_DIALOG (dialog));
202       if (response == GTK_RESPONSE_ACCEPT)
203         {
204           GtkFileChooser *chooser = GTK_FILE_CHOOSER (dialog);
205           filename_used = gtk_file_chooser_get_filename (chooser);
206         }
207 
208       gtk_widget_destroy (dialog);
209     }
210 }
211 
212 /* Not really a gpgme function, but needed in most places
213    dump_data_to_file is used.  Opens a file for writing, asking the
214    user to overwrite if it exists and reporting any errors.  Returns
215    NULL on failure, but you can assume the user has been informed of
216    the error (or maybe he just didn't want to overwrite!).  */
217 FILE *
gpa_fopen(const char * filename,GtkWidget * parent,char ** filename_used)218 gpa_fopen (const char *filename, GtkWidget *parent, char **filename_used)
219 {
220   FILE *target;
221 
222   *filename_used = check_overwriting (filename, parent);
223   if (! *filename_used)
224     return NULL;
225   target = g_fopen (*filename_used, "w");
226   if (!target)
227     {
228       gchar *message;
229       message = g_strdup_printf ("%s: %s", *filename_used, strerror(errno));
230       gpa_window_error (message, parent);
231       g_free (message);
232     }
233   return target;
234 }
235 
236 
237 int
gpa_open_output_direct(const char * filename,gpgme_data_t * data,GtkWidget * parent)238 gpa_open_output_direct (const char *filename, gpgme_data_t *data,
239 			GtkWidget *parent)
240 {
241   int target = -1;
242   gpg_error_t err;
243 
244   target = g_open (filename,
245 		   O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
246   if (target == -1)
247     {
248       gchar *message;
249       message = g_strdup_printf ("%s: %s", filename, strerror(errno));
250       gpa_window_error (message, parent);
251       g_free (message);
252     }
253   err = gpgme_data_new_from_fd (data, target);
254   if (gpg_err_code (err) != GPG_ERR_NO_ERROR)
255     {
256       close (target);
257       target = -1;
258     }
259 
260   return target;
261 }
262 
263 
264 int
gpa_open_output(const char * filename,gpgme_data_t * data,GtkWidget * parent,char ** filename_used)265 gpa_open_output (const char *filename, gpgme_data_t *data, GtkWidget *parent,
266                  char **filename_used)
267 {
268   int res;
269 
270   *filename_used = check_overwriting (filename, parent);
271   if (! *filename_used)
272     return -1;
273 
274   res = gpa_open_output_direct (*filename_used, data, parent);
275   return res;
276 }
277 
278 
279 int
gpa_open_input(const char * filename,gpgme_data_t * data,GtkWidget * parent)280 gpa_open_input (const char *filename, gpgme_data_t *data, GtkWidget *parent)
281 {
282   gpg_error_t err;
283   int target = -1;
284 
285   target = g_open (filename, O_RDONLY | O_BINARY, 0);
286   if (target == -1)
287     {
288       gchar *message;
289       message = g_strdup_printf ("%s: %s", filename, strerror(errno));
290       gpa_window_error (message, parent);
291       g_free (message);
292     }
293   err = gpgme_data_new_from_fd (data, target);
294   if (gpg_err_code (err) != GPG_ERR_NO_ERROR)
295     {
296       close (target);
297       target = -1;
298     }
299 
300   return target;
301 }
302 
303 
304 /* Do a gpgme_data_new_from_file and report any GPGME_File_Error to
305    the user.  */
306 gpg_error_t
gpa_gpgme_data_new_from_file(gpgme_data_t * data,const char * filename,GtkWidget * parent)307 gpa_gpgme_data_new_from_file (gpgme_data_t *data,
308 			      const char *filename,
309 			      GtkWidget *parent)
310 {
311   gpg_error_t err;
312   err = gpgme_data_new_from_file (data, filename, 1);
313   if (gpg_err_code_to_errno (err) != 0)
314     {
315       gchar *message;
316       message = g_strdup_printf ("%s: %s", filename, strerror(errno));
317       gpa_window_error (message, NULL);
318       g_free (message);
319     }
320   return err;
321 }
322 
323 
324 /* Write the contents of the gpgme_data_t into the clipboard.  Assumes
325    that the data is ASCII.  Return 0 on success.  */
326 int
dump_data_to_clipboard(gpgme_data_t data,GtkClipboard * clipboard)327 dump_data_to_clipboard (gpgme_data_t data, GtkClipboard *clipboard)
328 {
329   char buffer[512];
330   int nread;
331   gchar *text = NULL;
332   size_t len = 0;
333 
334   nread = gpgme_data_seek (data, 0, SEEK_SET);
335   if (nread == -1)
336     {
337       gpa_window_error (strerror (errno), NULL);
338       return -1;
339     }
340   while ((nread = gpgme_data_read (data, buffer, sizeof (buffer))) > 0)
341     {
342       text = g_realloc (text, len + nread + 1);
343       strncpy (text + len, buffer, nread);
344       len += nread;
345     }
346   if (nread == -1)
347     {
348       gpa_window_error (strerror (errno), NULL);
349       return -1;
350     }
351 
352   gtk_clipboard_set_text (clipboard, text, (int)len);
353   g_free (text);
354   return 0;
355 }
356 
357 
358 /* Assemble the parameter string for gpgme_op_genkey for GnuPG.  We
359    don't need worry about the user ID being UTF-8 as long as we are
360    using GTK+2, because all user input is UTF-8 in it.  */
361 static gchar *
build_genkey_parms(gpa_keygen_para_t * params)362 build_genkey_parms (gpa_keygen_para_t *params)
363 {
364   gchar *string;
365   const char *key_algo;
366   gchar *subkeys = NULL;
367   gchar *name = NULL;
368   gchar *email = NULL;
369   gchar *comment = NULL;
370   gchar *expire = NULL;
371 
372   /* Choose which keys and subkeys to generate.  */
373   switch (params->algo)
374     {
375     case GPA_KEYGEN_ALGO_RSA_RSA:
376       key_algo = "RSA";
377       subkeys = g_strdup_printf ("Subkey-Type: RSA\n"
378                                  "Subkey-Length: %d\n"
379                                  "Subkey-Usage: encrypt\n", params->keysize);
380       break;
381     case GPA_KEYGEN_ALGO_RSA_ELGAMAL:
382       key_algo = "RSA";
383       subkeys = g_strdup_printf ("Subkey-Type: ELG-E\n"
384                                  "Subkey-Length: %d\n"
385                                  "Subkey-Usage: encrypt\n", params->keysize);
386       break;
387     case GPA_KEYGEN_ALGO_RSA:
388       key_algo = "RSA";
389       break;
390     case GPA_KEYGEN_ALGO_DSA_ELGAMAL:
391       key_algo = "DSA";
392       subkeys = g_strdup_printf ("Subkey-Type: ELG-E\n"
393                                  "Subkey-Length: %i\n"
394                                  "Subkey-Usage: encrypt\n", params->keysize);
395       break;
396     case GPA_KEYGEN_ALGO_DSA:
397       key_algo = "DSA";
398       break;
399     default:
400       /* Can't happen */
401       return NULL;
402     }
403 
404   /* Construct the user ID.  */
405   if (params->name && params->name[0])
406     name = g_strdup_printf ("Name-Real: %s\n", params->name);
407   if (params->email && params->email[0])
408     email = g_strdup_printf ("Name-Email: %s\n", params->email);
409   if (params->comment && params->comment[0])
410     comment = g_strdup_printf ("Name-Comment: %s\n", params->comment);
411 
412   /* Build the expiration date string if needed */
413   if (g_date_valid (&params->expire))
414     expire = g_strdup_printf ("Expire-Date: %04d-%02d-%02d\n",
415                               g_date_get_year (&params->expire),
416                               g_date_get_month (&params->expire),
417                               g_date_get_day (&params->expire));
418 
419   /* Assemble the final parameter string */
420   string = g_strdup_printf ("<GnupgKeyParms format=\"internal\">\n"
421                             "Key-Type: %s\n"
422                             "Key-Length: %i\n"
423                             "Key-Usage: sign\n"
424                             "%s" /* Subkeys */
425                             "%s" /* Name */
426                             "%s" /* Email */
427                             "%s" /* Comment */
428                             "%s" /* Expiration date */
429                             "%%ask-passphrase\n"
430                             "</GnupgKeyParms>\n",
431                             key_algo,
432                             params->keysize,
433                             subkeys? subkeys : "",
434                             name? name:"",
435                             email? email : "",
436                             comment? comment : "",
437                             expire? expire : "");
438 
439   /* Free auxiliary strings if they are not empty */
440   g_free (subkeys);
441   g_free (name);
442   g_free (email);
443   g_free (comment);
444   g_free (expire);
445 
446   return string;
447 }
448 
449 /* Begin generation of a key with the given parameters.  It prepares
450    the parameters required by GPGME and returns whatever
451    gpgme_op_genkey_start returns.  */
452 gpg_error_t
gpa_generate_key_start(gpgme_ctx_t ctx,gpa_keygen_para_t * params)453 gpa_generate_key_start (gpgme_ctx_t ctx, gpa_keygen_para_t *params)
454 {
455   gchar *parm_string;
456   gpg_error_t err;
457 
458   parm_string = build_genkey_parms (params);
459   err = gpgme_op_genkey_start (ctx, parm_string, NULL, NULL);
460   g_free (parm_string);
461 
462   return err;
463 }
464 
465 
466 /* Retrieve the path to the GPG executable.  */
467 static const gchar *
get_gpg_path(void)468 get_gpg_path (void)
469 {
470   gpgme_engine_info_t engine;
471 
472   gpgme_get_engine_info (&engine);
473   while (engine)
474     {
475       if (engine->protocol == GPGME_PROTOCOL_OpenPGP)
476 	return engine->file_name;
477       engine = engine->next;
478     }
479   return NULL;
480 }
481 
482 
483 /* Retrieve the path to the GPGSM executable.  */
484 static const gchar *
get_gpgsm_path(void)485 get_gpgsm_path (void)
486 {
487   gpgme_engine_info_t engine;
488 
489   gpgme_get_engine_info (&engine);
490   while (engine)
491     {
492       if (engine->protocol == GPGME_PROTOCOL_CMS)
493 	return engine->file_name;
494       engine = engine->next;
495     }
496   return NULL;
497 }
498 
499 
500 /* Retrieve the path to the GPGCONF executable.  */
501 static const gchar *
get_gpgconf_path(void)502 get_gpgconf_path (void)
503 {
504   gpgme_engine_info_t engine;
505 
506   gpgme_get_engine_info (&engine);
507   while (engine)
508     {
509       if (engine->protocol == GPGME_PROTOCOL_GPGCONF)
510 	return engine->file_name;
511       engine = engine->next;
512     }
513   return NULL;
514 }
515 
516 
517 /* Retrieve the path to the GPG-CONNECT-AGENT executable.  Note that
518    the caller must free the returned string.  */
519 static char *
get_gpg_connect_agent_path(void)520 get_gpg_connect_agent_path (void)
521 {
522   const char *gpgconf;
523   char *fname, *p;
524 
525   gpgconf = get_gpgconf_path ();
526   if (!gpgconf)
527     return NULL;
528 
529 #ifdef G_OS_WIN32
530 # define NEWNAME "gpg-connect-agent.exe"
531 #else
532 # define NEWNAME "gpg-connect-agent"
533 #endif
534 
535   fname = g_malloc (strlen (gpgconf) + strlen (NEWNAME) + 1);
536   strcpy (fname, gpgconf);
537 #ifdef G_OS_WIN32
538   for (p=fname; *p; p++)
539     if (*p == '\\')
540       *p = '/';
541 #endif /*G_OS_WIN32*/
542   p = strrchr (fname, '/');
543   if (p)
544     p++;
545   else
546     p = fname;
547   strcpy (p, NEWNAME);
548 
549 #undef NEWNAME
550   return fname;
551 }
552 
553 
554 /* Backup a key. It exports both the public and secret keys to a file.
555    IS_X509 tells the function that the fingerprint is from an X.509
556    key.  Returns TRUE on success and FALSE on error. It displays
557    errors to the user.  */
558 gboolean
gpa_backup_key(const gchar * fpr,const char * filename,int is_x509)559 gpa_backup_key (const gchar *fpr, const char *filename, int is_x509)
560 {
561   const char *header_argv[] =
562     {
563       "", "--batch", "--no-tty", "--fingerprint",
564       (char*) fpr, NULL
565     };
566   const char *pub_argv[] =
567     {
568       "", "--batch", "--no-tty", "--armor", "--export",
569       (char*) fpr, NULL
570     };
571   const char *sec_argv[] =
572     {
573       "", "--batch", "--no-tty", "--armor", "--export-secret-key",
574       (char*) fpr, NULL
575     };
576   const char *seccms_argv[] =
577     {
578       "", "--batch", "--no-tty", "--armor", "--export-secret-key-p12",
579       (char*) fpr, NULL
580     };
581   gpg_error_t err;
582   FILE *fp;
583   gpgme_data_t dfp = NULL;
584   const char *pgm;
585   gpgme_ctx_t ctx = NULL;
586   int result = FALSE;
587 
588   /* Get the gpg path.  */
589   if (is_x509)
590     pgm = get_gpgsm_path ();
591   else
592     pgm = get_gpg_path ();
593   g_return_val_if_fail (pgm && *pgm, FALSE);
594 
595   /* Open the file */
596   {
597     mode_t mask = umask (0077);
598     fp = g_fopen (filename, "w");
599     umask (mask);
600   }
601   if (!fp)
602     {
603       gchar message[256];
604       g_snprintf (message, sizeof(message), "%s: %s",
605 		  filename, strerror(errno));
606       gpa_window_error (message, NULL);
607       return FALSE;
608     }
609 
610   fputs (_(
611     "************************************************************************\n"
612     "* WARNING: This file is a backup of your secret key. Please keep it in *\n"
613     "* a safe place.                                                        *\n"
614     "************************************************************************\n"
615     "\n"), fp);
616 
617   fputs (_("The key backed up in this file is:\n\n"), fp);
618   fflush (fp);
619 
620   err = gpgme_data_new_from_stream (&dfp, fp);
621   if (err)
622     {
623       g_message ("error creating data object '%s': %s",
624                  filename, gpg_strerror (err));
625       goto leave;
626     }
627 
628   ctx = gpa_gpgme_new ();
629   gpgme_set_protocol (ctx, GPGME_PROTOCOL_SPAWN);
630 
631   err = gpgme_op_spawn (ctx, pgm, header_argv, NULL, dfp, NULL,
632                         GPGME_SPAWN_DETACHED|GPGME_SPAWN_ALLOW_SET_FG);
633   if (err)
634     {
635       g_message ("error running '%s' (1): %s", pgm, gpg_strerror (err));
636       goto leave;
637     }
638   gpgme_data_write (dfp, "\n", 1);
639 
640   err = gpgme_op_spawn (ctx, pgm, pub_argv, NULL, dfp, NULL,
641                         GPGME_SPAWN_DETACHED|GPGME_SPAWN_ALLOW_SET_FG);
642   if (err)
643     {
644       g_message ("error running '%s' (2): %s", pgm, gpg_strerror (err));
645       goto leave;
646     }
647   gpgme_data_write (dfp, "\n", 1);
648 
649   err = gpgme_op_spawn (ctx, pgm, is_x509? seccms_argv : sec_argv,
650                         NULL, dfp, NULL,
651                         GPGME_SPAWN_DETACHED|GPGME_SPAWN_ALLOW_SET_FG);
652   if (err)
653     {
654       g_message ("error running '%s' (3): %s", pgm, gpg_strerror (err));
655       goto leave;
656     }
657 
658   result = TRUE;
659 
660  leave:
661   gpgme_release (ctx);
662   gpgme_data_release (dfp);
663   fclose (fp);
664   return result;
665 }
666 
667 
668 void
gpa_keygen_para_free(gpa_keygen_para_t * params)669 gpa_keygen_para_free (gpa_keygen_para_t *params)
670 {
671   if (params)
672     {
673       g_free (params->name);
674       g_free (params->email);
675       g_free (params->comment);
676       g_free (params->r_error_desc);
677       g_free (params);
678     }
679 }
680 
681 
682 gpa_keygen_para_t *
gpa_keygen_para_new(void)683 gpa_keygen_para_new (void)
684 {
685   gpa_keygen_para_t *params = xcalloc (1, sizeof *params);
686   g_date_clear (&params->expire, 1);
687   return params;
688 }
689 
690 
691 
692 /* Ownertrust strings.  */
693 const gchar *
gpa_key_ownertrust_string(gpgme_key_t key)694 gpa_key_ownertrust_string (gpgme_key_t key)
695 {
696   if (key->protocol == GPGME_PROTOCOL_CMS)
697     return "";
698 
699   switch (key->owner_trust)
700     {
701     case GPGME_VALIDITY_UNKNOWN:
702     case GPGME_VALIDITY_UNDEFINED:
703     default:
704       return _("Unknown");
705       break;
706     case GPGME_VALIDITY_NEVER:
707       return _("Never");
708       break;
709     case GPGME_VALIDITY_MARGINAL:
710       return _("Marginal");
711       break;
712     case GPGME_VALIDITY_FULL:
713       return _("Full");
714       break;
715     case GPGME_VALIDITY_ULTIMATE:
716       return _("Ultimate");
717       break;
718     }
719 }
720 
721 
722 /* Key validity strings.  */
723 const gchar *
gpa_key_validity_string(gpgme_key_t key)724 gpa_key_validity_string (gpgme_key_t key)
725 {
726   if (!key->uids)
727     return _("Unknown");
728   switch (key->uids->validity)
729     {
730     case GPGME_VALIDITY_UNKNOWN:
731     case GPGME_VALIDITY_UNDEFINED:
732     case GPGME_VALIDITY_NEVER:
733     case GPGME_VALIDITY_MARGINAL:
734     default:
735       if (key->subkeys->revoked)
736 	return _("Revoked");
737       else if (key->subkeys->expired)
738 	return _("Expired");
739       else if (key->subkeys->disabled)
740 	return _("Disabled");
741       else if (key->subkeys->invalid)
742 	return _("Incomplete");
743       else
744 	return _("Unknown");
745       break;
746     case GPGME_VALIDITY_FULL:
747     case GPGME_VALIDITY_ULTIMATE:
748       return _("Fully Valid");
749     }
750 }
751 
752 
753 /* UID validity strings.  */
754 const gchar *
gpa_uid_validity_string(gpgme_user_id_t uid)755 gpa_uid_validity_string (gpgme_user_id_t uid)
756 {
757   if (uid->revoked)
758     return _("Revoked");
759   else if (uid->invalid)
760     return _("Invalid");
761 
762   switch (uid->validity)
763     {
764     case GPGME_VALIDITY_UNKNOWN:
765     case GPGME_VALIDITY_UNDEFINED:return _("Unknown");
766     case GPGME_VALIDITY_NEVER:    return _("Faked");
767     case GPGME_VALIDITY_MARGINAL: return _("Marginal");
768     case GPGME_VALIDITY_FULL:     return _("Fully");
769     case GPGME_VALIDITY_ULTIMATE: return _("Ultimate");
770     default: return "[?]";
771     }
772 }
773 
774 
775 static GtkWidget *
passphrase_question_label(const char * uid_hint,const char * passphrase_info,int prev_was_bad)776 passphrase_question_label (const char *uid_hint,
777 			   const char *passphrase_info,
778 			   int prev_was_bad)
779 {
780   GtkWidget *label;
781   gchar *input;
782   gchar *text;
783   gchar *keyid, *userid;
784   gint i;
785 
786   /* Just in case this is called without a user id hint we return a
787      simple text to avoid a crash.  */
788   if (!uid_hint)
789     return gtk_label_new ("Passphrase?");
790 
791   input = g_strdup (uid_hint);
792   /* The first word in the hint is the key ID */
793   keyid = input;
794   for (i = 0; input[i] != ' '; i++)
795     {
796     }
797   input[i++] = '\0';
798   /* The rest of the hint is the user ID */
799   userid = input+i;
800   /* Build the label widget */
801   if (!prev_was_bad)
802     {
803       text = g_strdup_printf ("%s\n\n%s %s\n%s %s",
804                               _("Please enter the passphrase for"
805                                 " the following key:"),
806                               _("User Name:"), userid,
807                               _("Key ID:"), keyid);
808     }
809   else
810     {
811       text = g_strdup_printf ("%s\n\n%s %s\n%s %s",
812                               _("Wrong passphrase, please try again:"),
813                               _("User Name:"), userid,
814                               _("Key ID:"), keyid);
815     }
816   label = gtk_label_new (text);
817   g_free (input);
818   g_free (text);
819   return label;
820 }
821 
822 
823 /* This is the function called by GPGME when it wants a passphrase.  */
824 gpg_error_t
gpa_passphrase_cb(void * hook,const char * uid_hint,const char * passphrase_info,int prev_was_bad,int fd)825 gpa_passphrase_cb (void *hook, const char *uid_hint,
826 		   const char *passphrase_info,
827 		   int prev_was_bad, int fd)
828 {
829   GtkWidget * dialog;
830   GtkWidget * hbox;
831   GtkWidget * vbox;
832   GtkWidget * entry;
833   GtkWidget * pixmap;
834   GtkResponseType response;
835   gchar *passphrase;
836 
837   dialog = gtk_dialog_new_with_buttons (_("Enter Passphrase"),
838 					NULL, GTK_DIALOG_MODAL,
839                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
840 					GTK_STOCK_OK, GTK_RESPONSE_OK,
841 					NULL);
842   gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
843                                            GTK_RESPONSE_OK,
844                                            GTK_RESPONSE_CANCEL,
845                                            -1);
846 
847   hbox = gtk_hbox_new (FALSE, 0);
848   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox,
849 		      TRUE, FALSE, 10);
850   pixmap = gtk_image_new_from_stock (GTK_STOCK_DIALOG_QUESTION,
851 				     GTK_ICON_SIZE_DIALOG);
852   gtk_box_pack_start (GTK_BOX (hbox), pixmap, TRUE, FALSE, 10);
853   vbox = gtk_vbox_new (TRUE, 0);
854   gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, FALSE, 10);
855   /* The default button is OK */
856   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
857   /* Set the contents of the dialog */
858   gtk_box_pack_start_defaults (GTK_BOX (vbox),
859 			       passphrase_question_label (uid_hint,
860 							  passphrase_info,
861 							  prev_was_bad));
862   entry = gtk_entry_new ();
863   gtk_entry_set_visibility (GTK_ENTRY (entry), FALSE);
864   gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
865   gtk_box_pack_start (GTK_BOX (vbox), entry, TRUE, FALSE, 10);
866   gtk_widget_grab_focus (entry);
867   /* Run the dialog */
868   gtk_widget_show_all (dialog);
869   response = gtk_dialog_run (GTK_DIALOG (dialog));
870   passphrase = g_strdup_printf ("%s\n",
871 				gtk_entry_get_text (GTK_ENTRY (entry)));
872   gtk_widget_destroy (dialog);
873   if (response == GTK_RESPONSE_OK)
874     {
875       int passphrase_len = strlen (passphrase);
876 #ifdef G_OS_WIN32
877       DWORD res;
878 
879       if (WriteFile ((HANDLE) _get_osfhandle (fd), passphrase,
880 		     passphrase_len, &res, NULL) == 0
881 	  || res < passphrase_len)
882 	{
883 	  g_free (passphrase);
884 	  return gpg_error (gpg_err_code_from_errno (EIO));
885 	}
886       else
887 	return gpg_error (GPG_ERR_NO_ERROR);
888 #else
889       int res;
890       res = write (fd, passphrase, passphrase_len);
891       g_free (passphrase);
892       if (res == -1)
893 	return gpg_error (gpg_err_code_from_errno (errno));
894       else if (res < passphrase_len)
895 	return gpg_error (gpg_err_code_from_errno (EIO));
896       else
897 	return gpg_error (GPG_ERR_NO_ERROR);
898 #endif
899     }
900   else
901     {
902       g_free (passphrase);
903       return gpg_error (GPG_ERR_CANCELED);
904     }
905 }
906 
907 
908 /* Convenience functions to access key attributes, which need to be
909    filtered before being displayed to the user.  */
910 
911 /* Return the user ID, making sure it is properly UTF-8 encoded.
912    Allocates a new string, which must be freed with g_free ().  */
913 static gchar *
string_to_utf8(const gchar * string)914 string_to_utf8 (const gchar *string)
915 {
916   const char *s;
917 
918   if (!string)
919     return NULL;
920 
921   /* Due to a bug in old and not so old PGP versions user IDs have
922      been copied verbatim into the key.  Thus many users with Umlauts
923      et al. in their name will see their names garbled.  Although this
924      is not an issue for me (;-)), I have a couple of friends with
925      Umlauts in their name, so let's try to make their life easier by
926      detecting invalid encodings and convert that to Latin-1.  We use
927      this even for X.509 because it may make things even better given
928      all the invalid encodings often found in X.509 certificates.  */
929   for (s = string; *s && !(*s & 0x80); s++)
930     ;
931   if (*s && ((s[1] & 0xc0) == 0x80) && ( ((*s & 0xe0) == 0xc0)
932                                          || ((*s & 0xf0) == 0xe0)
933                                          || ((*s & 0xf8) == 0xf0)
934                                          || ((*s & 0xfc) == 0xf8)
935                                          || ((*s & 0xfe) == 0xfc)) )
936     {
937       /* Possible utf-8 character followed by continuation byte.
938          Although this might still be Latin-1 we better assume that it
939          is valid utf-8. */
940       return g_strdup (string);
941      }
942   else if (*s && !strchr (string, 0xc3))
943     {
944       /* No 0xC3 character in the string; assume that it is Latin-1.  */
945       return g_convert (string, -1, "UTF-8", "ISO-8859-1", NULL, NULL, NULL);
946     }
947   else
948     {
949       /* Everything else is assumed to be UTF-8.  We do this even that
950          we know the encoding is not valid.  However as we only test
951          the first non-ascii character, valid encodings might
952          follow.  */
953       return g_strdup (string);
954     }
955 }
956 
957 
958 gchar *
gpa_gpgme_key_get_userid(gpgme_user_id_t uid)959 gpa_gpgme_key_get_userid (gpgme_user_id_t uid)
960 {
961   gchar *uid_utf8;
962 
963   if (!uid)
964     return g_strdup (_("[None]"));
965 
966   uid_utf8 = string_to_utf8 (uid->uid);
967 
968   /* Tag revoked UID's*/
969   if (uid->revoked)
970     {
971       /* FIXME: I think, this code should be simply disabled. Even if
972 	 the uid U is revoked, it is "U", not "[Revoked] U". Side
973 	 note: adding this prefix obviously breaks sorting by
974 	 uid. -moritz */
975       gchar *tmp = g_strdup_printf ("[%s] %s", _("Revoked"), uid_utf8);
976       g_free (uid_utf8);
977       uid_utf8 = tmp;
978     }
979   return uid_utf8;
980 }
981 
982 
983 /* Return the key fingerprint, properly formatted according to the key
984    version.  Allocates a new string, which must be freed with
985    g_free().  This is based on code from GPAPA's
986    extract_fingerprint.  */
987 gchar *
gpa_gpgme_key_format_fingerprint(const char * fpraw)988 gpa_gpgme_key_format_fingerprint (const char *fpraw)
989 {
990   if (strlen (fpraw) == 32 )  /* v3 */
991     {
992       char *fp = g_malloc (strlen (fpraw) + 16 + 1);
993       const char *r = fpraw;
994       char *q = fp;
995       gint c = 0;
996       while (*r)
997 	{
998 	  *q++ = *r++;
999 	  c++;
1000 	  if (c < 32)
1001 	    {
1002 	      if (c % 2 == 0)
1003 		*q++ = ' ';
1004 	      if (c % 16 == 0)
1005 		*q++ = ' ';
1006 	    }
1007 	}
1008       *q = 0;
1009       return fp;
1010     }
1011   else
1012     {
1013       char *fp = g_malloc (strlen (fpraw) + 10 + 1);
1014       const char *r = fpraw;
1015       char *q = fp;
1016       gint c = 0;
1017       while (*r)
1018 	{
1019 	  *q++ = *r++;
1020 	  c++;
1021 	  if (c < 40)
1022 	    {
1023 	      if (c % 4 == 0)
1024 		*q++ = ' ';
1025 	      if (c % 20 == 0)
1026 		*q++ = ' ';
1027 	    }
1028 	}
1029       *q = 0;
1030       return fp;
1031     }
1032 }
1033 
1034 
1035 /* Return the short key ID of the indicated key.  The returned string
1036    is valid as long as the key is valid.  */
1037 const gchar *
gpa_gpgme_key_get_short_keyid(gpgme_key_t key)1038 gpa_gpgme_key_get_short_keyid (gpgme_key_t key)
1039 {
1040   const char *keyid = key->subkeys->keyid;
1041   if (!keyid)
1042     return NULL;
1043   else
1044     return keyid + 8;
1045 }
1046 
1047 
1048 /* Convenience function to access key signature attibutes, much like
1049    the previous ones.  */
1050 
1051 /* Return the user ID, making sure it is properly UTF-8 encoded.
1052    Allocates a new string, which must be freed with g_free().  */
1053 gchar *
gpa_gpgme_key_sig_get_userid(gpgme_key_sig_t sig)1054 gpa_gpgme_key_sig_get_userid (gpgme_key_sig_t sig)
1055 {
1056   if (!sig->uid || !*sig->uid)
1057     /* Duplicate it to make sure it can be g_free'd.  */
1058     return g_strdup (_("[Unknown user ID]"));
1059   else
1060     return string_to_utf8 (sig->uid);
1061 }
1062 
1063 
1064 /* Return the short key ID of the indicated key.  The returned string
1065    is valid as long as the key is valid.  */
1066 const gchar *
gpa_gpgme_key_sig_get_short_keyid(gpgme_key_sig_t sig)1067 gpa_gpgme_key_sig_get_short_keyid (gpgme_key_sig_t sig)
1068 {
1069   return sig->keyid + 8;
1070 }
1071 
1072 /* Return a string with the status of the key signature.  */
1073 const gchar *
gpa_gpgme_key_sig_get_sig_status(gpgme_key_sig_t sig,GHashTable * revoked)1074 gpa_gpgme_key_sig_get_sig_status (gpgme_key_sig_t sig,
1075 				  GHashTable *revoked)
1076 {
1077   const gchar *status;
1078   switch (sig->status)
1079     {
1080     case GPGME_SIG_STAT_GOOD:
1081       status = _("Valid");
1082       break;
1083     case GPGME_SIG_STAT_BAD:
1084       status = _("Bad");
1085     default:
1086       status = _("Unknown");
1087     }
1088   if (sig->expired)
1089     {
1090       status = _("Expired");
1091     }
1092   else if (g_hash_table_lookup (revoked, sig->keyid))
1093     {
1094       status = _("Revoked");
1095     }
1096   return status;
1097 }
1098 
1099 
1100 /* Return a string with the level of the key signature.  */
1101 const gchar *
gpa_gpgme_key_sig_get_level(gpgme_key_sig_t sig)1102 gpa_gpgme_key_sig_get_level (gpgme_key_sig_t sig)
1103 {
1104   switch (sig->sig_class)
1105     {
1106     case 0x10:
1107       return _("Generic");
1108       break;
1109     case 0x11:
1110       return _("Persona");
1111       break;
1112     case 0x12:
1113       return _("Casual");
1114       break;
1115     case 0x13:
1116       return _("Positive");
1117       break;
1118     default:
1119       return _("Unknown");
1120       break;
1121     }
1122 }
1123 
1124 
1125 /* Return a human readable string with the status of the signature
1126    SIG.  If R_KEYDESC is not NULL, the description of the key
1127    (e.g.. the user ID) will be stored as a malloced string at that
1128    address; if no key is known, NULL will be stored.  If R_KEY is not
1129    NULL, a key object will be stored at that address; NULL if no key
1130    is known.  CTX is used as helper to figure out the key
1131    description.  */
1132 char *
gpa_gpgme_get_signature_desc(gpgme_ctx_t ctx,gpgme_signature_t sig,char ** r_keydesc,gpgme_key_t * r_key)1133 gpa_gpgme_get_signature_desc (gpgme_ctx_t ctx, gpgme_signature_t sig,
1134                               char **r_keydesc, gpgme_key_t *r_key)
1135 {
1136   gpgme_key_t key = NULL;
1137   char *keydesc = NULL;
1138   char *sigdesc;
1139   const char *sigstatus;
1140 
1141   sigstatus = sig->status? gpg_strerror (sig->status) : "";
1142 
1143   if (sig->fpr && ctx)
1144     {
1145       gpgme_get_key (ctx, sig->fpr, &key, 0);
1146       if (key)
1147         keydesc = gpa_gpgme_key_get_userid (key->uids);
1148     }
1149 
1150   if (sig->summary & GPGME_SIGSUM_RED)
1151     {
1152       if (keydesc && *sigstatus)
1153         sigdesc = g_strdup_printf (_("Bad signature by %s: %s"),
1154                                    keydesc, sigstatus);
1155       else if (keydesc)
1156         sigdesc = g_strdup_printf (_("Bad signature by %s"),
1157                                    keydesc);
1158       else if (sig->fpr && *sigstatus)
1159         sigdesc = g_strdup_printf (_("Bad signature by unknown key "
1160                                      "%s: %s"), sig->fpr, sigstatus);
1161       else if (sig->fpr)
1162         sigdesc = g_strdup_printf (_("Bad signature by unknown key "
1163                                      "%s"), sig->fpr);
1164       else if (*sigstatus)
1165         sigdesc = g_strdup_printf (_("Bad signature by unknown key: "
1166                                      "%s"), sigstatus);
1167       else
1168         sigdesc = g_strdup_printf (_("Bad signature by unknown key"));
1169     }
1170   else if (sig->summary & GPGME_SIGSUM_VALID)
1171     {
1172       if (keydesc && *sigstatus)
1173         sigdesc = g_strdup_printf (_("Good signature by %s: %s"),
1174                                    keydesc, sigstatus);
1175       else if (keydesc)
1176         sigdesc = g_strdup_printf (_("Good signature by %s"),
1177                                    keydesc);
1178       else if (sig->fpr && *sigstatus)
1179         sigdesc = g_strdup_printf (_("Good signature by unknown key "
1180                                      "%s: %s"), sig->fpr, sigstatus);
1181       else if (sig->fpr)
1182         sigdesc = g_strdup_printf (_("Good signature by unknown key "
1183                                      "%s"), sig->fpr);
1184       else if (*sigstatus)
1185         sigdesc = g_strdup_printf (_("Good signature by unknown key: "
1186                                      "%s"), sigstatus);
1187       else
1188         sigdesc = g_strdup_printf (_("Good signature by unknown key"));
1189     }
1190   else
1191     {
1192       if (keydesc && *sigstatus)
1193         sigdesc = g_strdup_printf (_("Uncertain signature by %s: %s"),
1194                                    keydesc, sigstatus);
1195       else if (keydesc)
1196         sigdesc = g_strdup_printf (_("Uncertain signature by %s"),
1197                                    keydesc);
1198       else if (sig->fpr && *sigstatus)
1199         sigdesc = g_strdup_printf (_("Uncertain signature by unknown key "
1200                                      "%s: %s"), sig->fpr, sigstatus);
1201       else if (sig->fpr)
1202         sigdesc = g_strdup_printf (_("Uncertain signature by unknown key "
1203                                      "%s"), sig->fpr);
1204       else if (*sigstatus)
1205         sigdesc = g_strdup_printf (_("Uncertain signature by unknown "
1206                                      "key: %s"), sigstatus);
1207       else
1208         sigdesc = g_strdup_printf (_("Uncertain signature by unknown "
1209                                      "key"));
1210     }
1211 
1212 
1213   if (r_keydesc)
1214     *r_keydesc = keydesc;
1215   else
1216     g_free (keydesc);
1217 
1218   if (r_key)
1219     *r_key = key;
1220   else
1221     gpgme_key_unref (key);
1222 
1223   return sigdesc;
1224 }
1225 
1226 
1227 
1228 /* Return a string listing the capabilities of a key.  */
1229 const gchar *
gpa_get_key_capabilities_text(gpgme_key_t key)1230 gpa_get_key_capabilities_text (gpgme_key_t key)
1231 {
1232   if (key->can_certify)
1233     {
1234       if (key->can_sign)
1235 	{
1236 	  if (key->can_encrypt)
1237 	    return _("The key can be used for certification, signing "
1238 		     "and encryption.");
1239 	  else
1240 	    return _("The key can be used for certification and "
1241 		     "signing, but not for encryption.");
1242 	}
1243       else
1244 	{
1245 	  if (key->can_encrypt)
1246 	    return _("The key can be used for certification and "
1247 		     "encryption.");
1248 	  else
1249 	    return _("The key can be used only for certification.");
1250 	}
1251     }
1252   else
1253     {
1254       if (key->can_sign)
1255 	{
1256 	  if (key->can_encrypt)
1257 	    return _("The key can be used only for signing and "
1258 		     "encryption, but not for certification.");
1259 	  else
1260 	    return _("The key can be used only for signing.");
1261 	}
1262       else
1263 	{
1264 	  if (key->can_encrypt)
1265 	    return _("The key can be used only for encryption.");
1266 	  else
1267 	    /* Can't happen, even for subkeys.  */
1268 	      return _("This key is useless.");
1269 	}
1270     }
1271 }
1272 
1273 
1274 /* Update the result structure RESULT using the gpgme result INFO and
1275    the FILES and BAD_FILES counter.  */
1276 void
gpa_gpgme_update_import_results(gpa_import_result_t result,unsigned int files,unsigned int bad_files,gpgme_import_result_t info)1277 gpa_gpgme_update_import_results (gpa_import_result_t result,
1278                                  unsigned int files, unsigned int bad_files,
1279                                  gpgme_import_result_t info)
1280 {
1281   result->files     += files;
1282   result->bad_files += bad_files;
1283   if (info)
1284     {
1285       result->considered       += info->considered;
1286       result->imported         += info->imported;
1287       result->unchanged        += info->unchanged;
1288       result->secret_read      += info->secret_read;
1289       result->secret_imported  += info->secret_imported;
1290       result->secret_unchanged += info->secret_unchanged;
1291     }
1292 }
1293 
1294 
1295 void
gpa_gpgme_show_import_results(GtkWidget * parent,gpa_import_result_t result)1296 gpa_gpgme_show_import_results (GtkWidget *parent, gpa_import_result_t result)
1297 {
1298   char *buf1, *buf2;
1299 
1300   if (result->files)
1301     buf2 = g_strdup_printf (_("%u file(s) read\n"
1302                               "%u file(s) with errors"),
1303                             result->files,
1304                             result->bad_files);
1305   else
1306     buf2 = NULL;
1307 
1308 
1309   if (!result->considered)
1310     gpa_show_warn (parent, NULL, "%s%s%s",
1311                       _("No keys were found."),
1312                       buf2? "\n":"",
1313                       buf2? buf2:"");
1314   else
1315     {
1316       buf1 = g_strdup_printf (_("%i public keys read\n"
1317                                 "%i public keys imported\n"
1318                                 "%i public keys unchanged\n"
1319                                 "%i secret keys read\n"
1320                                 "%i secret keys imported\n"
1321                                 "%i secret keys unchanged"),
1322                               result->considered,
1323                               result->imported,
1324                               result->unchanged,
1325                               result->secret_read,
1326                               result->secret_imported,
1327                               result->secret_unchanged);
1328 
1329       gpa_show_info (parent,
1330                      "%s%s%s",
1331                      buf1,
1332                      buf2? "\n":"",
1333                      buf2? buf2:"");
1334       g_free (buf1);
1335     }
1336 
1337   g_free (buf2);
1338 }
1339 
1340 
1341 /* Return a copy of the key array.  */
1342 gpgme_key_t *
gpa_gpgme_copy_keyarray(gpgme_key_t * keys)1343 gpa_gpgme_copy_keyarray (gpgme_key_t *keys)
1344 {
1345   gpgme_key_t *newarray;
1346   int idx;
1347 
1348   if (!keys)
1349     return NULL;
1350 
1351   for (idx=0; keys[idx]; idx++)
1352     ;
1353   idx++;
1354   newarray = g_new (gpgme_key_t, idx);
1355   for (idx=0; keys[idx]; idx++)
1356     {
1357       gpgme_key_ref (keys[idx]);
1358       newarray[idx] = keys[idx];
1359     }
1360   newarray[idx] = NULL;
1361 
1362   return newarray;
1363 }
1364 
1365 
1366 /* Release all keys in the array KEYS as well as ARRAY itself.  */
1367 void
gpa_gpgme_release_keyarray(gpgme_key_t * keys)1368 gpa_gpgme_release_keyarray (gpgme_key_t *keys)
1369 {
1370   if (keys)
1371     {
1372       int idx;
1373 
1374       for (idx=0; keys[idx]; idx++)
1375         gpgme_key_unref (keys[idx]);
1376       g_free (keys);
1377     }
1378 }
1379 
1380 
1381 
1382 
1383 
1384 /* Read the next number in the version string STR and return it in
1385    *NUMBER.  Return a pointer to the tail of STR after parsing, or
1386    *NULL if the version string was invalid.  */
1387 static const char *
parse_version_number(const char * str,int * number)1388 parse_version_number (const char *str, int *number)
1389 {
1390 #define MAXVAL ((INT_MAX - 10) / 10)
1391   int val = 0;
1392 
1393   /* Leading zeros are not allowed.  */
1394   if (*str == '0' && isascii (str[1]) && isdigit (str[1]))
1395     return NULL;
1396 
1397   while (isascii (*str) && isdigit (*str) && val <= MAXVAL)
1398     {
1399       val *= 10;
1400       val += *(str++) - '0';
1401     }
1402   *number = val;
1403   return val > MAXVAL ? NULL : str;
1404 #undef MAXVAL
1405 }
1406 
1407 
1408 /* Parse the version string STR in the format MAJOR.MINOR.MICRO (for
1409    example, 9.3.2) and return the components in MAJOR, MINOR and MICRO
1410    as integers.  The function returns the tail of the string that
1411    follows the version number.  This might be te empty string if there
1412    is nothing following the version number, or a patchlevel.  The
1413    function returns NULL if the version string is not valid.  */
1414 static const char *
parse_version_string(const char * str,int * major,int * minor,int * micro)1415 parse_version_string (const char *str, int *major, int *minor, int *micro)
1416 {
1417   str = parse_version_number (str, major);
1418   if (!str || *str != '.')
1419     return NULL;
1420   str++;
1421 
1422   str = parse_version_number (str, minor);
1423   if (!str || *str != '.')
1424     return NULL;
1425   str++;
1426 
1427   str = parse_version_number (str, micro);
1428   if (!str)
1429     return NULL;
1430 
1431   /* A patchlevel might follow.  */
1432   return str;
1433 }
1434 
1435 
1436 /* Return true if MY_VERSION is at least REQ_VERSION, and false
1437    otherwise.  */
1438 static int
compare_version_strings(const char * my_version,const char * rq_version)1439 compare_version_strings (const char *my_version,
1440 			 const char *rq_version)
1441 {
1442   int my_major, my_minor, my_micro;
1443   int rq_major, rq_minor, rq_micro;
1444   const char *my_plvl, *rq_plvl;
1445 
1446   if (!rq_version)
1447     return 1;
1448   if (!my_version)
1449     return 0;
1450 
1451   my_plvl = parse_version_string (my_version, &my_major, &my_minor, &my_micro);
1452   if (!my_plvl)
1453     return 0;
1454 
1455   rq_plvl = parse_version_string (rq_version, &rq_major, &rq_minor, &rq_micro);
1456   if (!rq_plvl)
1457     return 0;
1458 
1459   if (my_major > rq_major
1460       || (my_major == rq_major && my_minor > rq_minor)
1461       || (my_major == rq_major && my_minor == rq_minor
1462 	  && my_micro > rq_micro)
1463       || (my_major == rq_major && my_minor == rq_minor
1464 	  && my_micro == rq_micro && strcmp (my_plvl, rq_plvl) >= 0))
1465     return 1;
1466 
1467   return 0;
1468 }
1469 
1470 
1471 /* Try switching to the gpg2 backend or the one given by filename.  */
1472 /* Return 1 if the gpg engine has at least version NEED_VERSION,
1473    otherwise 0.  */
1474 int
is_gpg_version_at_least(const char * need_version)1475 is_gpg_version_at_least (const char *need_version)
1476 {
1477   gpgme_engine_info_t engine;
1478 
1479   gpgme_get_engine_info (&engine);
1480   while (engine)
1481     {
1482       if (engine->protocol == GPGME_PROTOCOL_OpenPGP)
1483         return !!compare_version_strings (engine->version, need_version);
1484       engine = engine->next;
1485     }
1486   return 0; /* No gpg-engine available. */
1487 }
1488 
1489 
1490 /* Structure used to communicate with gpg_simple_stdio_cb.  */
1491 struct gpg_simple_stdio_parm_s
1492 {
1493   gboolean (*cb)(void *opaque, char *line);
1494   void *cb_arg;
1495   GString *string;
1496   int only_status_lines;
1497 };
1498 
1499 /* Helper for gpa_start_simple_gpg_command.  */
1500 static gboolean
gpg_simple_stdio_cb(GIOChannel * channel,GIOCondition condition,void * user_data)1501 gpg_simple_stdio_cb (GIOChannel *channel, GIOCondition condition,
1502                      void *user_data)
1503 {
1504   struct gpg_simple_stdio_parm_s *parm = user_data;
1505   GIOStatus status;
1506   char *line, *p;
1507 
1508   if ((condition & G_IO_IN))
1509     {
1510       /* We don't use a while but an if because that allows to update
1511          progress bars nicely.  A bit slower, but no real problem.  */
1512       if ((status = g_io_channel_read_line_string
1513            (channel, parm->string, NULL, NULL)) == G_IO_STATUS_NORMAL)
1514         {
1515           line = parm->string->str;
1516 
1517           /* Strip line terminator.  */
1518           p = strchr (line, '\n');
1519           if (p)
1520             {
1521               if (p > line && p[-1] == '\r')
1522                 p[-1] = 0;
1523               else
1524                 *p = 0;
1525             }
1526 
1527           /* We care only about status lines.  */
1528           if (parm->only_status_lines && !strncmp (line, "[GNUPG:] ", 9))
1529             {
1530               line += 9;
1531 
1532               /* Call user callback.  */
1533               if (parm->cb && !parm->cb (parm->cb_arg, line))
1534                 {
1535                   /* User requested EOF.  */
1536                   goto cleanup;
1537                 }
1538               /* Return direct so that we do not run into the G_IO_HUP
1539                  check.  This is required to read all buffered input.  */
1540               return TRUE;  /* Keep on watching this channel. */
1541             }
1542           else if (!parm->only_status_lines)
1543             {
1544               /* Call user callback.  */
1545               if (parm->cb && !parm->cb (parm->cb_arg, line))
1546                 {
1547                   /* User requested EOF.  */
1548                   goto cleanup;
1549                 }
1550 
1551               return TRUE;  /* Keep on watching this channel. */
1552             }
1553         }
1554       if (status != G_IO_STATUS_NORMAL && status != G_IO_STATUS_AGAIN )
1555         {
1556           /* Error or EOF.  */
1557           goto cleanup;
1558         }
1559     }
1560   if ((condition & G_IO_HUP))
1561     {
1562       goto cleanup;
1563     }
1564 
1565   return TRUE;  /* Keep on watching this channel. */
1566 
1567  cleanup:
1568   if (parm->cb)
1569     parm->cb (parm->cb_arg, NULL);  /* Tell user about EOF.  */
1570   g_string_free (parm->string, TRUE);
1571   xfree (parm);
1572   g_io_channel_unref (channel);  /* Close channel.  */
1573   return FALSE;  /* Remove us from the watch list.  */
1574 }
1575 
1576 
1577 /* Run gpg asynchronously with the given arguments and return a gpg
1578    error code on error.  The list of arguments are all of type (const
1579    char*) and end with a NULL argument (FIRST_ARG may already be NULL,
1580    but that does not make any sense).  STDIN and STDOUT are connected
1581    to /dev/null.  No more than 20 arguments may be given.
1582 
1583    Because the protocol GPGME_PROTOCOL_ASSUAN makes no sense here, it
1584    is used to call gpg-connect-agent.
1585 
1586    If the function returns success the provided callback CB is called
1587    for each line received on stdout (respective stderr if USE_STADERR
1588    is true).  EOF is send to this callback by passing a LINE as NULL.
1589    The callback may use this for cleanup.  If the callback returns
1590    FALSE, an EOF is forced so that the callback is called once more
1591    with LINE set to NULL.
1592 
1593    This function is used to run
1594 
1595     gpgsm --learn-card
1596     gpg-connect-agent NOP /bye
1597 
1598    The problem is that under Windows g_spawn does not allow to specify
1599    flags for the underlying CreateProcess.  Thus it is not possible to
1600    create a detached process (i.e. without a console); the result is
1601    that a console window pops up.  I can see two solutions: (1) Use a
1602    wrapper process to start them detached, or (2) move the required
1603    function into GPGME and use that new API.
1604   */
1605 gpg_error_t
gpa_start_simple_gpg_command(gboolean (* cb)(void * opaque,char * line),void * cb_arg,gpgme_protocol_t protocol,int use_stderr,const char * first_arg,...)1606 gpa_start_simple_gpg_command (gboolean (*cb)(void *opaque, char *line),
1607                               void *cb_arg, gpgme_protocol_t protocol,
1608                               int use_stderr,
1609                               const char *first_arg, ...)
1610 {
1611   char *argv[24];
1612   int argc;
1613   int fd_stdio;
1614   GIOChannel *channel;
1615   struct gpg_simple_stdio_parm_s *parm = NULL;
1616   char *freeme = NULL;
1617 
1618   if (protocol == GPGME_PROTOCOL_OpenPGP)
1619     argv[0] = (char*)get_gpg_path ();
1620   else if (protocol == GPGME_PROTOCOL_CMS)
1621     argv[0] = (char*)get_gpgsm_path ();
1622   else if (protocol == GPGME_PROTOCOL_GPGCONF)
1623     argv[0] = (char*)get_gpgconf_path ();
1624   else if (protocol == GPGME_PROTOCOL_ASSUAN)
1625     argv[0] = freeme = get_gpg_connect_agent_path ();
1626   else
1627     argv[0] = NULL;
1628 
1629   if (!argv[0])
1630     {
1631       gpa_window_error (_("A required engine component is not installed."),
1632                         NULL);
1633       return gpg_error (GPG_ERR_INV_ARG);
1634     }
1635 
1636   argc = 1;
1637   if (protocol != GPGME_PROTOCOL_GPGCONF
1638       && protocol != GPGME_PROTOCOL_ASSUAN)
1639     {
1640       argv[argc++] = (char*)"--status-fd";
1641       argv[argc++] = (char*)"2";
1642     }
1643   argv[argc++] = (char*)first_arg;
1644   if (first_arg)
1645     {
1646       va_list arg_ptr;
1647       const char *s;
1648 
1649       va_start (arg_ptr, first_arg);
1650       while (argc < DIM (argv)-1 && (s=va_arg (arg_ptr, const char *)))
1651         argv[argc++] = (char*)s;
1652       va_end (arg_ptr);
1653       argv[argc] = NULL;
1654       g_return_val_if_fail (argc < DIM (argv), gpg_error (GPG_ERR_INV_ARG));
1655     }
1656 
1657   parm = g_try_malloc (sizeof *parm);
1658   if (!parm)
1659     return gpg_error_from_syserror ();
1660   parm->cb = cb;
1661   parm->cb_arg = cb_arg;
1662   parm->string = g_string_sized_new (200);
1663   parm->only_status_lines = use_stderr;
1664 
1665   if (!g_spawn_async_with_pipes (NULL, argv, NULL,
1666                                  (use_stderr
1667                                   ? G_SPAWN_STDOUT_TO_DEV_NULL
1668                                   : G_SPAWN_STDERR_TO_DEV_NULL),
1669                                  NULL, NULL, NULL,
1670                                  NULL,
1671                                  use_stderr? NULL : &fd_stdio,
1672                                  use_stderr? &fd_stdio : NULL,
1673                                  NULL))
1674     {
1675       gpa_window_error (_("Calling the crypto engine program failed."), NULL);
1676       xfree (parm);
1677       g_free (freeme);
1678       return gpg_error (GPG_ERR_GENERAL);
1679     }
1680   g_free (freeme);
1681 #ifdef G_OS_WIN32
1682   channel = g_io_channel_win32_new_fd (fd_stdio);
1683 #else
1684   channel = g_io_channel_unix_new (fd_stdio);
1685 #endif
1686   g_io_channel_set_encoding (channel, NULL, NULL);
1687   /* Note that we need a buffered channel, so that we can use the read
1688      line function.  */
1689   g_io_channel_set_close_on_unref (channel, TRUE);
1690 
1691   /* Create a watch for the channel.  */
1692   if (!g_io_add_watch (channel, (G_IO_IN|G_IO_HUP),
1693                        gpg_simple_stdio_cb, parm))
1694     {
1695       g_debug ("error creating watch for gpg command");
1696       g_io_channel_unref (channel);
1697       xfree (parm);
1698       return gpg_error (GPG_ERR_GENERAL);
1699     }
1700 
1701   return 0;
1702 }
1703 
1704 
1705 /* Try to start the gpg-agent if it has not yet been started.
1706    Starting the agent works in the background.  Thus if the function
1707    returns, it is not sure that the agent is now running.  */
1708 void
gpa_start_agent(void)1709 gpa_start_agent (void)
1710 {
1711   gpg_error_t err;
1712   gpgme_ctx_t ctx;
1713   char *pgm;
1714   const char *argv[3];
1715 
1716   pgm = get_gpg_connect_agent_path ();
1717   if (!pgm)
1718     {
1719       g_message ("tool to start the agent is not available");
1720       return;
1721     }
1722 
1723   ctx = gpa_gpgme_new ();
1724   gpgme_set_protocol (ctx, GPGME_PROTOCOL_SPAWN);
1725   argv[0] = "";   /* Auto-insert the basename.  */
1726   argv[1] = "NOP";
1727   argv[2] = NULL;
1728   err = gpgme_op_spawn (ctx, pgm, argv, NULL, NULL, NULL, GPGME_SPAWN_DETACHED);
1729   if (err)
1730     g_message ("error running '%s': %s", pgm, gpg_strerror (err));
1731   g_free (pgm);
1732   gpgme_release (ctx);
1733 
1734 }
1735 
1736 
1737 
1738 /* Fucntions matching the user id verification isn gpg's key generation.  */
1739 
1740 const char *
gpa_validate_gpg_name(const char * name)1741 gpa_validate_gpg_name (const char *name)
1742 {
1743   const char *result = NULL;
1744 
1745   if (!name || !*name)
1746     result = _("You must enter a name.");
1747   else if (strpbrk (name, "<>"))
1748     result = _("Invalid character in name.");
1749   else if (g_ascii_isdigit (*name))
1750     result = _("Name may not start with a digit.");
1751   else if (g_utf8_strlen (name, -1) < 5)
1752     result = _("Name is too short.");
1753 
1754   return result;
1755 }
1756 
1757 
1758 /* Check whether the string has characters not valid in an RFC-822
1759    address.  To cope with OpenPGP we allow non-ascii characters
1760    so that for example umlauts are legal in an email address.  An
1761    OpenPGP user ID must be utf-8 encoded but there is no strict
1762    requirement for RFC-822.  Thus to avoid IDNA encoding we put the
1763    address verbatim as utf-8 into the user ID under the assumption
1764    that mail programs handle IDNA at a lower level and take OpenPGP
1765    user IDs as utf-8.  */
1766 static int
has_invalid_email_chars(const char * s)1767 has_invalid_email_chars (const char *s)
1768 {
1769   int at_seen = 0;
1770   const char *valid_chars=
1771     "01234567890_-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1772 
1773   for ( ; *s; s++ )
1774     {
1775       if ((*s & 0x80))
1776         continue; /* We only care about ASCII.  */
1777       if (*s == '@')
1778         at_seen=1;
1779       else if (!at_seen && !( !!strchr( valid_chars, *s ) || *s == '+' ))
1780         return 1;
1781       else if (at_seen && !strchr (valid_chars, *s))
1782         return 1;
1783     }
1784   return 0;
1785 }
1786 
1787 
1788 static int
string_count_chr(const char * string,int c)1789 string_count_chr (const char *string, int c)
1790 {
1791   int count;
1792 
1793   for (count=0; *string; string++ )
1794     if ( *string == c )
1795       count++;
1796   return count;
1797 }
1798 
1799 
1800 /* Check whether NAME represents a valid mailbox according to RFC822
1801    except for non-ascii utf-8 characters. Returns true if so. */
1802 static int
is_valid_mailbox(const char * name)1803 is_valid_mailbox (const char *name)
1804 {
1805   return !( !name
1806             || !*name
1807             || has_invalid_email_chars (name)
1808             || string_count_chr (name,'@') != 1
1809             || *name == '@'
1810             || name[strlen(name)-1] == '@'
1811             || name[strlen(name)-1] == '.'
1812             || strstr (name, "..") );
1813 }
1814 
1815 
1816 const char *
gpa_validate_gpg_email(const char * email)1817 gpa_validate_gpg_email (const char *email)
1818 {
1819   const char *result = NULL;
1820 
1821   if (!email || !*email)
1822     ;
1823   else if (!is_valid_mailbox (email))
1824     result = _("Email address is not valid.");
1825 
1826   return result;
1827 }
1828 
1829 
1830 const char *
gpa_validate_gpg_comment(const char * comment)1831 gpa_validate_gpg_comment (const char *comment)
1832 {
1833   const char *result = NULL;
1834 
1835   if (!comment || !*comment)
1836     ;
1837   else if (strpbrk (comment, "()"))
1838     result = _("Invalid character in comments.");
1839 
1840   return result;
1841 }
1842 
1843