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 (¶ms->expire))
414 expire = g_strdup_printf ("Expire-Date: %04d-%02d-%02d\n",
415 g_date_get_year (¶ms->expire),
416 g_date_get_month (¶ms->expire),
417 g_date_get_day (¶ms->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 (¶ms->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