2006-08-04 [colin] 2.4.0cvs27
[claws.git] / src / plugins / pgpcore / sgpgme.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2006 Hiroyuki Yamamoto & the Sylpheed-Claws team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19  
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23  
24 #ifdef USE_GPGME
25
26 #include <time.h>
27 #include <gtk/gtk.h>
28 #include <gpgme.h>
29 #include <glib.h>
30 #include <glib/gi18n.h>
31 #include <stdio.h>
32 #include <errno.h>
33 #if HAVE_LOCALE_H
34 #  include <locale.h>
35 #endif
36
37 #include "sgpgme.h"
38 #include "privacy.h"
39 #include "prefs_common.h"
40 #include "utils.h"
41 #include "alertpanel.h"
42 #include "passphrase.h"
43 #include "prefs_gpg.h"
44 #include "select-keys.h"
45
46 static void sgpgme_disable_all(void)
47 {
48     /* FIXME: set a flag, so that we don't bother the user with failed
49      * gpgme messages */
50 }
51
52 gpgme_verify_result_t sgpgme_verify_signature(gpgme_ctx_t ctx, gpgme_data_t sig, 
53                                         gpgme_data_t plain, gpgme_data_t dummy)
54 {
55         gpgme_verify_result_t status = NULL;
56         gpgme_error_t err;
57
58         if ((err = gpgme_op_verify(ctx, sig, plain, dummy)) != GPG_ERR_NO_ERROR) {
59                 debug_print("op_verify err %s\n", gpgme_strerror(err));
60                 privacy_set_error(gpgme_strerror(err));
61                 return GINT_TO_POINTER(-GPG_ERR_SYSTEM_ERROR);
62                 
63         }
64         status = gpgme_op_verify_result(ctx);
65
66         return status;
67 }
68
69 SignatureStatus sgpgme_sigstat_gpgme_to_privacy(gpgme_ctx_t ctx, gpgme_verify_result_t status)
70 {
71         unsigned long validity = 0;
72         gpgme_signature_t sig = NULL;
73         
74         if (GPOINTER_TO_INT(status) == -GPG_ERR_SYSTEM_ERROR) {
75                 debug_print("system error\n");
76                 return SIGNATURE_CHECK_FAILED;
77         }
78
79         if (status == NULL) {
80                 debug_print("status == NULL\n");
81                 return SIGNATURE_UNCHECKED;
82         }
83         sig = status->signatures;
84
85         if (sig == NULL) {
86                 debug_print("sig == NULL\n");
87                 return SIGNATURE_UNCHECKED;
88         }
89         validity = sig->validity;
90
91         debug_print("err code %d\n", gpg_err_code(sig->status));
92         switch (gpg_err_code(sig->status)) {
93         case GPG_ERR_NO_ERROR:
94                 switch (gpg_err_code(sig->validity)) {
95                 case GPGME_VALIDITY_NEVER:
96                         return SIGNATURE_INVALID;
97                 case GPGME_VALIDITY_UNKNOWN:
98                 case GPGME_VALIDITY_UNDEFINED:
99                 case GPGME_VALIDITY_MARGINAL:
100                 case GPGME_VALIDITY_FULL:
101                 case GPGME_VALIDITY_ULTIMATE:
102                         return SIGNATURE_OK;
103                 default:
104                         return SIGNATURE_CHECK_FAILED;
105                 }
106         case GPG_ERR_SIG_EXPIRED:
107         case GPG_ERR_KEY_EXPIRED:
108                 return SIGNATURE_WARN;
109         case GPG_ERR_BAD_SIGNATURE:
110                 return SIGNATURE_INVALID;
111         case GPG_ERR_NO_PUBKEY:
112                 return SIGNATURE_CHECK_FAILED;
113         default:
114                 return SIGNATURE_CHECK_FAILED;
115         }
116         return SIGNATURE_CHECK_FAILED;
117 }
118
119 static const gchar *get_validity_str(unsigned long validity)
120 {
121         switch (gpg_err_code(validity)) {
122         case GPGME_VALIDITY_UNKNOWN:
123                 return _("Unknown");
124         case GPGME_VALIDITY_UNDEFINED:
125                 return _("Undefined");
126         case GPGME_VALIDITY_NEVER:
127                 return _("Never");
128         case GPGME_VALIDITY_MARGINAL:
129                 return _("Marginal");
130         case GPGME_VALIDITY_FULL:
131                 return _("Full");
132         case GPGME_VALIDITY_ULTIMATE:
133                 return _("Ultimate");
134         default:
135                 return _("Error");
136         }
137 }
138
139 static gchar *extract_name(const char *uid)
140 {
141         if (uid == NULL)
142                 return NULL;
143         if (!strncmp(uid, "CN=", 3)) {
144                 gchar *result = g_strdup(uid+3);
145                 if (strstr(result, ","))
146                         *(strstr(result, ",")) = '\0';
147                 return result;
148         } else if (strstr(uid, ",CN=")) {
149                 gchar *result = g_strdup(strstr(uid, ",CN=")+4);
150                 if (strstr(result, ","))
151                         *(strstr(result, ",")) = '\0';
152                 return result;
153         } else {
154                 return g_strdup(uid);
155         }
156 }
157 gchar *sgpgme_sigstat_info_short(gpgme_ctx_t ctx, gpgme_verify_result_t status)
158 {
159         gpgme_signature_t sig = NULL;
160         gchar *uname = NULL;
161         gpgme_key_t key;
162         gchar *result = NULL;
163         gpgme_error_t err = 0;
164         static gboolean warned = FALSE;
165
166         if (GPOINTER_TO_INT(status) == -GPG_ERR_SYSTEM_ERROR) {
167                 return g_strdup_printf(_("The signature can't be checked - %s"), privacy_get_error());
168         }
169
170         if (status == NULL) {
171                 return g_strdup(_("The signature has not been checked."));
172         }
173         sig = status->signatures;
174         if (sig == NULL) {
175                 return g_strdup(_("The signature has not been checked."));
176         }
177
178         err = gpgme_get_key(ctx, sig->fpr, &key, 0);
179         if (gpg_err_code(err) == GPG_ERR_NO_AGENT) {
180                 if (!warned)
181                         alertpanel_error(_("PGP Core: Can't get key - no gpg-agent running."));
182                 else
183                         g_warning(_("PGP Core: Can't get key - no gpg-agent running."));
184                 warned = TRUE;
185         }
186         if (key)
187                 uname = extract_name(key->uids->uid);
188         else
189                 uname = g_strdup("<?>");
190         switch (gpg_err_code(sig->status)) {
191         case GPG_ERR_NO_ERROR:
192                 switch (gpg_err_code(sig->validity)) {
193                 case GPGME_VALIDITY_MARGINAL:
194                 case GPGME_VALIDITY_FULL:
195                 case GPGME_VALIDITY_ULTIMATE:
196                         result = g_strdup_printf(_("Good signature from %s."), uname);
197                         break;
198                 case GPGME_VALIDITY_UNKNOWN:
199                 case GPGME_VALIDITY_UNDEFINED:
200                 case GPGME_VALIDITY_NEVER:
201                 default:
202                         result = g_strdup_printf(_("Good signature (untrusted) from %s."), uname);
203                         break;
204                 }
205                 break;
206         case GPG_ERR_SIG_EXPIRED:
207                 result = g_strdup_printf(_("Expired signature from %s."), uname);
208                 break;
209         case GPG_ERR_KEY_EXPIRED:
210                 result = g_strdup_printf(_("Expired key from %s."), uname);
211                 break;
212         case GPG_ERR_BAD_SIGNATURE:
213                 result = g_strdup_printf(_("Bad signature from %s."), uname);
214                 break;
215         case GPG_ERR_NO_PUBKEY: {
216                 gchar *id = g_strdup(sig->fpr + strlen(sig->fpr)-8);
217                 result = g_strdup_printf(_("Key 0x%s not available to verify this signature."), id);
218                 g_free(id);
219                 break;
220                 }
221         default:
222                 result = g_strdup(_("The signature has not been checked."));
223                 break;
224         }
225         if (result == NULL)
226                 result = g_strdup(_("Error"));
227         g_free(uname);
228         return result;
229 }
230
231 gchar *sgpgme_sigstat_info_full(gpgme_ctx_t ctx, gpgme_verify_result_t status)
232 {
233         gint i = 0;
234         gchar *ret;
235         GString *siginfo;
236         gpgme_signature_t sig = status->signatures;
237         
238         siginfo = g_string_sized_new(64);
239         while (sig) {
240                 gpgme_user_id_t user = NULL;
241                 gpgme_key_t key;
242
243                 const gchar *keytype, *keyid, *uid;
244                 
245                 gpgme_get_key(ctx, sig->fpr, &key, 0);
246
247                 if (key) {
248                         user = key->uids;
249                         keytype = gpgme_pubkey_algo_name(
250                                         key->subkeys->pubkey_algo);
251                         keyid = key->subkeys->keyid;
252                         uid = user->uid;
253                 } else {
254                         keytype = "?";
255                         keyid = "?";
256                         uid = "?";
257                 }
258                 g_string_append_printf(siginfo,
259                         _("Signature made using %s key ID %s\n"),
260                         keytype, keyid);
261                 
262                 switch (gpg_err_code(sig->status)) {
263                 case GPG_ERR_NO_ERROR:
264                 case GPG_ERR_KEY_EXPIRED:
265                         g_string_append_printf(siginfo,
266                                 _("Good signature from \"%s\" (Trust: %s)\n"),
267                                 uid, get_validity_str(sig->validity));
268                         break;
269                 case GPG_ERR_SIG_EXPIRED:
270                         g_string_append_printf(siginfo,
271                                 _("Expired signature from \"%s\"\n"),
272                                 uid);
273                         break;
274                 case GPG_ERR_BAD_SIGNATURE:
275                         g_string_append_printf(siginfo,
276                                 _("BAD signature from \"%s\"\n"),
277                                 uid);
278                         break;
279                 default:
280                         break;
281                 }
282                 if (sig->status != GPG_ERR_BAD_SIGNATURE) {
283                         gint j = 1;
284                         user = user ? user->next : NULL;
285                         while (user != NULL) {
286                                 g_string_append_printf(siginfo,
287                                         _("                aka \"%s\"\n"),
288                                         user->uid);
289                                 j++;
290                                 user = user->next;
291                         }
292                         g_string_append_printf(siginfo,
293                                 _("Primary key fingerprint: %s\n"), 
294                                 sig ? sig->fpr: "?");
295 #ifdef HAVE_GPGME_PKA_TRUST
296                         if (sig->pka_trust == 1 && sig->pka_address) {
297                                 g_string_append_printf(siginfo,
298                                    _("WARNING: Signer's address \"%s\" "
299                                       "does not match DNS entry\n"), 
300                                    sig->pka_address);
301                         }
302                         else if (sig->pka_trust == 2 && sig->pka_address) {
303                                 g_string_append_printf(siginfo,
304                                    _("Verified signer's address is \"%s\"\n"),
305                                    sig->pka_address);
306                                 /* FIXME: Compare the address to the
307                                  * From: address.  */
308                         }
309 #endif /*HAVE_GPGME_PKA_TRUST*/
310                 }
311
312                 g_string_append(siginfo, "\n");
313                 i++;
314                 sig = sig->next;
315         }
316
317         ret = siginfo->str;
318         g_string_free(siginfo, FALSE);
319         return ret;
320 }
321
322 gpgme_data_t sgpgme_data_from_mimeinfo(MimeInfo *mimeinfo)
323 {
324         gpgme_data_t data = NULL;
325         gpgme_error_t err;
326         FILE *fp = g_fopen(mimeinfo->data.filename, "rb");
327         gchar *tmp_file = NULL;
328
329         if (!fp) 
330                 return NULL;
331
332         tmp_file = get_tmp_file();
333         copy_file_part(fp, mimeinfo->offset, mimeinfo->length, tmp_file);
334         fclose(fp);
335         fp = NULL;
336         debug_print("tmp file %s\n", tmp_file);
337         
338         err = gpgme_data_new_from_file(&data, tmp_file, 1);
339         g_unlink(tmp_file);
340         g_free(tmp_file);
341
342         debug_print("data %p (%d %d)\n", (void *)&data, mimeinfo->offset, mimeinfo->length);
343         if (err) {
344                 debug_print ("gpgme_data_new_from_file failed: %s\n",
345                              gpgme_strerror (err));
346                 privacy_set_error(_("Couldn't get data from message, %s"), gpgme_strerror(err));
347                 return NULL;
348         }
349         return data;
350 }
351
352 gpgme_data_t sgpgme_decrypt_verify(gpgme_data_t cipher, gpgme_verify_result_t *status, gpgme_ctx_t ctx)
353 {
354         struct passphrase_cb_info_s info;
355         gpgme_data_t plain;
356         gpgme_error_t err;
357
358         memset (&info, 0, sizeof info);
359         
360         if ((err = gpgme_data_new(&plain)) != GPG_ERR_NO_ERROR) {
361                 gpgme_release(ctx);
362                 privacy_set_error(_("Couldn't initialize data, %s"), gpgme_strerror(err));
363                 return NULL;
364         }
365         
366         if (gpgme_get_protocol(ctx) == GPGME_PROTOCOL_OpenPGP) {
367                 if (!getenv("GPG_AGENT_INFO")) {
368                         info.c = ctx;
369                         gpgme_set_passphrase_cb (ctx, gpgmegtk_passphrase_cb, &info);
370                 }
371         } else {
372                 info.c = ctx;
373                 gpgme_set_passphrase_cb (ctx, NULL, &info);
374         }
375         
376         
377         if (gpgme_get_protocol(ctx) == GPGME_PROTOCOL_OpenPGP) {
378                 err = gpgme_op_decrypt_verify(ctx, cipher, plain);
379                 if (err != GPG_ERR_NO_ERROR) {
380                         debug_print("can't decrypt (%s)\n", gpgme_strerror(err));
381                         privacy_set_error("%s", gpgme_strerror(err));
382                         gpgmegtk_free_passphrase();
383                         gpgme_data_release(plain);
384                         return NULL;
385                 }
386
387                 err = gpgme_data_rewind(plain);
388                 if (err) {
389                         debug_print("can't seek (%d %d %s)\n", err, errno, strerror(errno));
390                 }
391
392                 debug_print("decrypted.\n");
393                 *status = gpgme_op_verify_result (ctx);
394         } else {
395                 err = gpgme_op_decrypt(ctx, cipher, plain);
396                 if (err != GPG_ERR_NO_ERROR) {
397                         debug_print("can't decrypt (%s)\n", gpgme_strerror(err));
398                         gpgmegtk_free_passphrase();
399                         gpgme_data_release(plain);
400                         return NULL;
401                 }
402
403                 err = gpgme_data_rewind(plain);
404                 if (err) {
405                         debug_print("can't seek (%d %d %s)\n", err, errno, strerror(errno));
406                 }
407
408                 debug_print("decrypted.\n");
409                 *status = gpgme_op_verify_result (ctx);
410         }
411         return plain;
412 }
413
414 gchar *sgpgme_get_encrypt_data(GSList *recp_names, gpgme_protocol_t proto)
415 {
416         SelectionResult result = KEY_SELECTION_CANCEL;
417         gpgme_key_t *keys = gpgmegtk_recipient_selection(recp_names, &result,
418                                 proto);
419         gchar *ret = NULL;
420         int i = 0;
421
422         if (!keys) {
423                 if (result == KEY_SELECTION_DONT)
424                         return g_strdup("_DONT_ENCRYPT_");
425                 else
426                         return NULL;
427         }
428         while (keys[i]) {
429                 gpgme_subkey_t skey = keys[i]->subkeys;
430                 gchar *fpr = skey->fpr;
431                 gchar *tmp = NULL;
432                 debug_print("adding %s\n", fpr);
433                 tmp = g_strconcat(ret?ret:"", fpr, " ", NULL);
434                 g_free(ret);
435                 ret = tmp;
436                 i++;
437         }
438         return ret;
439 }
440
441 gboolean sgpgme_setup_signers(gpgme_ctx_t ctx, PrefsAccount *account)
442 {
443         GPGAccountConfig *config;
444
445         gpgme_signers_clear(ctx);
446
447         config = prefs_gpg_account_get_config(account);
448
449         switch(config->sign_key) {
450         case SIGN_KEY_DEFAULT:
451                 debug_print("using default gnupg key\n");
452                 break;
453         case SIGN_KEY_BY_FROM:
454                 debug_print("using key for %s\n", account->address);
455                 break;
456         case SIGN_KEY_CUSTOM:
457                 debug_print("using key for %s\n", config->sign_key_id);
458                 break;
459         }
460
461         if (config->sign_key != SIGN_KEY_DEFAULT) {
462                 gchar *keyid;
463                 gpgme_key_t key, key2;
464                 gpgme_error_t err;
465
466                 if (config->sign_key == SIGN_KEY_BY_FROM)
467                         keyid = account->address;
468                 else if (config->sign_key == SIGN_KEY_CUSTOM)
469                         keyid = config->sign_key_id;
470                 else
471                         goto bail;
472
473                 err = gpgme_op_keylist_start(ctx, keyid, 1);
474                 if (!err)
475                         err = gpgme_op_keylist_next(ctx, &key);
476                 if (err) {
477                         g_warning("setup_signers start: %s", gpgme_strerror(err));
478                         privacy_set_error(_("Private key not found (%s)"), gpgme_strerror(err));
479                         goto bail;
480                 }
481                 
482                 err = gpgme_op_keylist_next(ctx, &key2);
483                 if (!err) {
484                         g_warning("ambiguous specification of private key '%s'\n",
485                                 keyid);
486                         privacy_set_error(_("Private key specification is ambiguous"));
487                         goto bail;
488                 }
489                 
490                 gpgme_op_keylist_end(ctx);
491                 err = gpgme_signers_add(ctx, key);
492                 gpgme_key_release(key);
493                 
494                 if (err) {
495                         g_warning("error adding secret key: %s\n", gpgme_strerror(err));
496                         privacy_set_error(_("Error setting private key: %s"), gpgme_strerror(err));
497                         goto bail;
498                 }
499         }
500
501         prefs_gpg_account_free_config(config);
502
503         return TRUE;
504 bail:
505         prefs_gpg_account_free_config(config);
506         return FALSE;
507 }
508
509 void sgpgme_init()
510 {
511         gpgme_engine_info_t engineInfo;
512         if (gpgme_check_version("1.0.0")) {
513 #ifdef LC_CTYPE
514                 gpgme_set_locale(NULL, LC_CTYPE, setlocale(LC_CTYPE, NULL));
515 #endif
516 #ifdef LC_MESSAGES
517                 gpgme_set_locale(NULL, LC_MESSAGES, setlocale(LC_MESSAGES, NULL));
518 #endif
519                 if (!gpgme_get_engine_info(&engineInfo)) {
520                         while (engineInfo) {
521                                 debug_print("GpgME Protocol: %s\n"
522                                             "Version: %s (req %s)\n"
523                                             "Executable: %s\n",
524                                         gpgme_get_protocol_name(engineInfo->protocol),
525                                         engineInfo->version ? engineInfo->version:"???",
526                                         engineInfo->req_version ? engineInfo->req_version:"???",
527                                         engineInfo->file_name ? engineInfo->file_name:"???");
528                                 if (engineInfo->protocol == GPGME_PROTOCOL_OpenPGP
529                                 &&  gpgme_engine_check_version(engineInfo->protocol) != 
530                                         GPG_ERR_NO_ERROR) {
531                                         if (engineInfo->file_name && !engineInfo->version) {
532                                                 alertpanel_error(_("Gpgme protocol '%s' is unusable: "
533                                                                    "Engine '%s' isn't installed properly."),
534                                                                    gpgme_get_protocol_name(engineInfo->protocol),
535                                                                    engineInfo->file_name);
536                                         } else if (engineInfo->file_name && engineInfo->version
537                                           && engineInfo->req_version) {
538                                                 alertpanel_error(_("Gpgme protocol '%s' is unusable: "
539                                                                    "Engine '%s' version %s is installed, "
540                                                                    "but version %s is required.\n"),
541                                                                    gpgme_get_protocol_name(engineInfo->protocol),
542                                                                    engineInfo->file_name,
543                                                                    engineInfo->version,
544                                                                    engineInfo->req_version);
545                                         } else {
546                                                 alertpanel_error(_("Gpgme protocol '%s' is unusable "
547                                                                    "(unknown problem)"),
548                                                                    gpgme_get_protocol_name(engineInfo->protocol));
549                                         }
550                                 }
551                                 engineInfo = engineInfo->next;
552                         }
553                 }
554         } else {
555                 sgpgme_disable_all();
556
557                 if (prefs_gpg_get_config()->gpg_warning) {
558                         AlertValue val;
559
560                         val = alertpanel_full
561                                 (_("Warning"),
562                                  _("GnuPG is not installed properly, or needs "
563                                  "to be upgraded.\n"
564                                  "OpenPGP support disabled."),
565                                  GTK_STOCK_CLOSE, NULL, NULL, TRUE, NULL,
566                                  ALERT_WARNING, G_ALERTDEFAULT);
567                         if (val & G_ALERTDISABLE)
568                                 prefs_gpg_get_config()->gpg_warning = FALSE;
569                 }
570         }
571 }
572
573 void sgpgme_done()
574 {
575         gpgmegtk_free_passphrase();
576 }
577
578 #endif /* USE_GPGME */