2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 2001 Jens Jahnke <jan0sch@gmx.net>
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.
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.
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
33 #include <fcntl.h> /* FIXME */
39 #include "procheader.h"
45 #include "prefs_common.h"
46 #include "passphrase.h"
47 #include "select-keys.h"
48 #include "sigstatus.h"
53 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
55 static char *content_names[] = {
57 "Content-Disposition",
58 "Content-Transfer-Encoding",
62 static char *mime_version_name[] = {
68 struct passphrase_cb_info_s {
74 sig_status_to_string (GpgmeSigStat status)
79 case GPGME_SIG_STAT_NONE:
80 result = _("Oops: Signature not verified");
82 case GPGME_SIG_STAT_NOSIG:
83 result = _("No signature found");
85 case GPGME_SIG_STAT_GOOD:
86 result = _("Good signature");
88 case GPGME_SIG_STAT_BAD:
89 result = _("BAD signature");
91 case GPGME_SIG_STAT_NOKEY:
92 result = _("No public key to verify the signature");
94 case GPGME_SIG_STAT_ERROR:
95 result = _("Error verifying the signature");
97 case GPGME_SIG_STAT_DIFF:
98 result = _("Different results for signatures");
101 result = _("Error: Unknown status");
109 sig_status_with_name (GpgmeSigStat status)
114 case GPGME_SIG_STAT_NONE:
115 result = _("Oops: Signature not verified");
117 case GPGME_SIG_STAT_NOSIG:
118 result = _("No signature found");
120 case GPGME_SIG_STAT_GOOD:
121 result = _("Good signature from \"%s\"");
123 case GPGME_SIG_STAT_BAD:
124 result = _("BAD signature from \"%s\"");
126 case GPGME_SIG_STAT_NOKEY:
127 result = _("No public key to verify the signature");
129 case GPGME_SIG_STAT_ERROR:
130 result = _("Error verifying the signature");
132 case GPGME_SIG_STAT_DIFF:
133 result = _("Different results for signatures");
136 result = _("Error: Unknown status");
144 sig_status_for_key(GString *str, GpgmeCtx ctx, GpgmeSigStat status,
145 GpgmeKey key, const gchar *fpr)
150 uid = gpgme_key_get_string_attr (key, GPGME_ATTR_USERID, NULL, idx);
152 g_string_sprintfa (str, "%s\n",
153 sig_status_to_string (status));
154 if ((fpr != NULL) && (*fpr != '\0'))
155 g_string_sprintfa (str, "Key fingerprint: %s\n", fpr);
156 g_string_append (str, _("Cannot find user ID for this key."));
159 g_string_sprintfa (str, sig_status_with_name (status), uid);
160 g_string_append (str, "\n");
163 uid = gpgme_key_get_string_attr (key, GPGME_ATTR_USERID,
167 g_string_sprintfa (str, _(" aka \"%s\"\n"),
173 sig_status_full (GpgmeCtx ctx)
182 struct tm *ctime_val;
186 str = g_string_new ("");
188 fpr = gpgme_get_sig_status (ctx, sig_idx, &status, &created);
189 while (fpr != NULL) {
191 ctime_val = localtime (&created);
192 strftime (ctime_str, sizeof (ctime_str), "%c",
194 g_string_sprintfa (str,
195 _("Signature made %s\n"),
198 err = gpgme_get_sig_key (ctx, sig_idx, &key);
200 g_string_sprintfa (str, "%s\n",
201 sig_status_to_string (status));
202 if ((fpr != NULL) && (*fpr != '\0'))
203 g_string_sprintfa (str,
204 _("Key fingerprint: %s\n"),
207 sig_status_for_key (str, ctx, status, key, fpr);
208 gpgme_key_unref (key);
210 g_string_append (str, "\n\n");
212 fpr = gpgme_get_sig_status (ctx, ++sig_idx, &status, &created);
216 g_string_free (str, FALSE);
220 static void dump_part ( MimeInfo *mimeinfo, FILE *fp )
222 unsigned int size = mimeinfo->size;
225 if (fseek (fp, mimeinfo->fpos, SEEK_SET)) {
226 g_warning ("dump_part: fseek error");
230 debug_print ("** --- begin dump_part ----");
231 while (size-- && (c = getc (fp)) != EOF)
234 g_warning ("dump_part: read error");
235 debug_print ("** --- end dump_part ----");
238 static void pgptext_fine_check_signature (MimeInfo *mimeinfo, FILE *fp)
242 GpgmeData text = NULL;
243 GpgmeSigStat status = GPGME_SIG_STAT_NONE;
244 GpgmegtkSigStatus statuswindow = NULL;
245 const char *result = NULL;
247 /* As this is the most simple solution, I prefer it. :-) */
248 statuswindow = gpgmegtk_sig_status_create ();
250 err = gpgme_new (&ctx);
252 g_warning ("gpgme_new failed: %s", gpgme_strerror (err));
256 err = gpgme_data_new_from_filepart (&text, NULL, fp,
257 mimeinfo->fpos, mimeinfo->size);
260 debug_print ("gpgme_data_new_from_filepart failed: %s",
261 gpgme_strerror (err));
265 /* Just pass the text to gpgme_op_verify to enable plain text stuff. */
266 err = gpgme_op_verify (ctx, text, NULL, &status);
268 debug_print ("gpgme_op_verify failed: %s", gpgme_strerror (err));
270 /* FIXME: check what the heck this sig_status_full stuff is.
271 * it should better go into sigstatus.c */
272 g_free (mimeinfo->sigstatus_full);
273 mimeinfo->sigstatus_full = sig_status_full (ctx);
276 result = gpgmegtk_sig_status_to_string(status);
277 debug_print("verification status: %s\n", result);
278 gpgmegtk_sig_status_update (statuswindow,ctx);
280 g_assert (!err); /* FIXME: Hey: this may indeed happen */
281 g_free (mimeinfo->sigstatus);
282 mimeinfo->sigstatus = g_strdup (result);
284 gpgme_data_release (text);
286 gpgmegtk_sig_status_destroy (statuswindow);
289 /* stolen from rfc2015.c */
291 gpg_name_cmp(const char *a, const char *b)
293 for( ; *a && *b; a++, b++) {
295 && toupper(*(unsigned char *)a) != toupper(*(unsigned char *)b))
303 passphrase_cb (void *opaque, const char *desc, void *r_hd)
305 struct passphrase_cb_info_s *info = opaque;
306 GpgmeCtx ctx = info ? info->c : NULL;
310 /* FIXME: cleanup by looking at *r_hd */
314 gpgmegtk_set_passphrase_grab (prefs_common.passphrase_grab);
315 debug_print ("requesting passphrase for `%s': ", desc);
316 pass = gpgmegtk_passphrase_mbox (desc);
318 debug_print ("cancel passphrase entry\n");
322 debug_print ("sending passphrase\n");
328 pgptext_decrypt (MimeInfo *partinfo, FILE *fp)
332 GpgmeData cipher = NULL, plain = NULL;
333 struct passphrase_cb_info_s info;
335 memset (&info, 0, sizeof info);
337 err = gpgme_new (&ctx);
339 debug_print ("gpgme_new failed: %s\n", gpgme_strerror (err));
343 err = gpgme_data_new_from_filepart (&cipher, NULL, fp,
344 partinfo->fpos, partinfo->size);
346 debug_print ("gpgme_data_new_from_filepart failed: %s\n",
347 gpgme_strerror (err));
351 err = gpgme_data_new (&plain);
353 debug_print ("gpgme_new failed: %s\n", gpgme_strerror (err));
357 if (!getenv("GPG_AGENT_INFO")) {
359 gpgme_set_passphrase_cb (ctx, passphrase_cb, &info);
362 err = gpgme_op_decrypt (ctx, cipher, plain);
365 gpgme_data_release (cipher);
367 debug_print ("decryption failed: %s\n", gpgme_strerror (err));
368 gpgme_data_release (plain);
372 debug_print ("decryption succeeded\n");
379 headerp(char *p, char **names)
388 if(p2[-1] == ' ' || p2[-1] == '\t') {
397 for(i = 0 ; names[i] != NULL; i++) {
398 if(!gpg_name_cmp (names[i], p))
403 return names[i] != NULL;
406 gboolean pgptext_has_signature (MsgInfo *msginfo, MimeInfo *mimeinfo)
409 gchar *file = NULL, *tmpchk, *tmpln, *tmpln_1;
411 gboolean has_begin_pgp_signed_msg = FALSE;
412 gboolean has_begin_pgp_sig = FALSE;
413 gboolean has_end_pgp_sig = FALSE;
414 gchar *check_begin_pgp_signed_msg = "-----BEGIN PGP SIGNED MESSAGE-----\n";
415 gchar *check_begin_pgp_signed_msg_1 = "-----BEGIN PGP SIGNED MESSAGE-----\r\n";
416 gchar *check_begin_pgp_sig = "-----BEGIN PGP SIGNATURE-----\n";
417 gchar *check_begin_pgp_sig_1 = "-----BEGIN PGP SIGNATURE-----\r\n";
418 gchar *check_end_pgp_sig = "-----END PGP SIGNATURE-----\n";
419 gchar *check_end_pgp_sig_1 = "-----END PGP SIGNATURE-----\r\n";
421 if ((!mimeinfo) || (!msginfo))
424 file = g_strdup_printf("%s", procmsg_get_message_file_path(msginfo));
426 if (mimeinfo->mime_type != MIME_TEXT) {
427 if ((fp = fopen(file, "r")) == NULL) {
428 FILE_OP_ERROR(file, "fopen");
432 if (mimeinfo->mime_type == MIME_MULTIPART) {
433 if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0)
435 while (fgets(buf, sizeof(buf), fp) != NULL)
436 if (buf[0] == '\r' || buf[0] == '\n') break;
438 /* now check for a pgptext signed message
439 * the strlen check catches quoted signatures */
440 while (fgets(buf, sizeof(buf), fp) != NULL) {
441 tmpchk = g_strnfill(sizeof(buf), '\n');
442 memmove(tmpchk, &buf, sizeof(buf));
444 tmpln = strstr(tmpchk, check_begin_pgp_signed_msg);
445 tmpln_1 = strstr(tmpchk, check_begin_pgp_signed_msg_1);
446 if (((tmpln != NULL) || (tmpln_1 != NULL)) && ((strlen(tmpchk) ==
447 strlen(tmpln)) || (strlen(tmpchk) == strlen(tmpln_1))) )
448 has_begin_pgp_signed_msg = TRUE;
450 tmpln = strstr(tmpchk, check_begin_pgp_sig);
451 tmpln_1 = strstr(tmpchk, check_begin_pgp_sig_1);
452 if (((tmpln != NULL) || (tmpln_1 != NULL)) && ((strlen(tmpchk) ==
453 strlen(tmpln)) || (strlen(tmpchk) == strlen(tmpln_1))) )
454 has_begin_pgp_sig = TRUE;
456 tmpln = strstr(tmpchk, check_end_pgp_sig);
457 tmpln_1 = strstr(tmpchk, check_end_pgp_sig_1);
458 if (((tmpln != NULL) || (tmpln_1 != NULL)) && ((strlen(tmpchk) ==
459 strlen(tmpln)) || (strlen(tmpchk) == strlen(tmpln_1))) )
460 has_end_pgp_sig = TRUE;
466 if ((fp = fopen(file, "r")) == NULL) {
467 FILE_OP_ERROR(file, "fopen");
471 if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0)
473 while (fgets(buf, sizeof(buf), fp) != NULL)
474 if (buf[0] == '\r' || buf[0] == '\n') break;
476 /* now check for a pgptext encrypted message */
477 while (fgets(buf, sizeof(buf), fp) != NULL) {
478 tmpchk = g_strnfill(sizeof(buf), '\n');
479 memmove(tmpchk, &buf, sizeof(buf));
481 if (strstr(tmpchk, check_begin_pgp_signed_msg) != NULL ||
482 strstr(tmpchk, check_begin_pgp_signed_msg_1) != NULL)
483 has_begin_pgp_signed_msg = TRUE;
484 if (strstr(tmpchk, check_begin_pgp_sig) != NULL ||
485 strstr(tmpchk, check_begin_pgp_sig_1) != NULL)
486 has_begin_pgp_sig = TRUE;
487 if (strstr(tmpchk, check_end_pgp_sig) != NULL ||
488 strstr(tmpchk, check_end_pgp_sig_1) != NULL)
489 has_end_pgp_sig = TRUE;
498 /* do we have a proper message? */
499 if (has_begin_pgp_signed_msg && has_begin_pgp_sig && has_end_pgp_sig) {
500 debug_print ("** pgptext signed message encountered\n");
506 void pgptext_check_signature (MimeInfo *mimeinfo, FILE *fp)
508 gchar *file, *tmpchk;
510 gboolean has_begin_pgp_signed_msg = FALSE;
511 gboolean has_begin_pgp_sig = FALSE;
512 gboolean has_end_pgp_sig = FALSE;
513 gchar *check_begin_pgp_signed_msg = "-----BEGIN PGP SIGNED MESSAGE-----\n";
514 gchar *check_begin_pgp_signed_msg_1 = "-----BEGIN PGP SIGNED
516 gchar *check_begin_pgp_sig = "-----BEGIN PGP SIGNATURE-----\n";
517 gchar *check_begin_pgp_sig_1 = "-----BEGIN PGP SIGNATURE-----\r\n";
518 gchar *check_end_pgp_sig = "-----END PGP SIGNATURE-----\n";
519 gchar *check_end_pgp_sig_1 = "-----END PGP SIGNATURE-----\r\n";
524 /* now we have to set fpos and size correctly */
526 /* FIXME: we should check for the correct mime type
527 * f.e. mime/text, application/pgp and so on...*/
528 /* if (mimeinfo->mime_type == MIME_TEXT) {*/
529 if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0)
531 while (fgets(buf, sizeof(buf), fp) != NULL) {
532 mimeinfo->fpos = mimeinfo->fpos + strlen(buf);
533 if (buf[0] == '\r' || buf[0] == '\n') break;
537 /* now check for fpos and size of the pgptext signed message */
538 mimeinfo->size = 0; /* init */
539 while (fgets(buf, sizeof(buf), fp) != NULL) {
540 tmpchk = g_strnfill(sizeof(buf), '\n');
541 memmove(tmpchk, &buf, sizeof(buf));
543 if (has_begin_pgp_signed_msg)
545 mimeinfo->size = mimeinfo->size + strlen(tmpchk);
547 if (strstr(tmpchk, check_begin_pgp_signed_msg) != NULL ||
548 strstr(tmpchk, check_begin_pgp_signed_msg_1) != NULL)
549 has_begin_pgp_signed_msg = TRUE;
550 else if (!has_begin_pgp_signed_msg)
551 /* set the beginning of the pgptext signed message */
552 mimeinfo->fpos = mimeinfo->fpos + strlen(tmpchk);
554 if (strstr(tmpchk, check_begin_pgp_sig) != NULL ||
555 strstr(tmpchk, check_begin_pgp_sig_1) != NULL)
556 has_begin_pgp_sig = TRUE;
558 if (strstr(tmpchk, check_end_pgp_sig) != NULL ||
559 strstr(tmpchk, check_end_pgp_sig_1) != NULL) {
560 has_end_pgp_sig = TRUE;
561 /* FIXME: Find out why the hell there are always 6[+1]
562 * chars less in our counter!*/
563 mimeinfo->size = mimeinfo->size + strlen(tmpchk) + 7;
571 debug_print ("** pgptext sig check...");
572 debug_print ("\tmimeinfo->fpos: %lu\tmimeinfo->size: %lu\n",
573 mimeinfo->fpos, mimeinfo->size);
574 dump_part (mimeinfo, fp);
577 pgptext_fine_check_signature (mimeinfo, fp);
580 int pgptext_is_encrypted (MimeInfo *mimeinfo, MsgInfo *msginfo)
583 gchar *file, *tmpchk;
585 gboolean has_begin_pgp_msg = FALSE;
586 gboolean has_end_pgp_msg = FALSE;
587 gchar *check_begin_pgp_msg = "-----BEGIN PGP MESSAGE-----\n";
588 gchar *check_begin_pgp_msg_1 = "-----BEGIN PGP MESSAGE-----\r\n";
589 gchar *check_end_pgp_msg = "-----END PGP MESSAGE-----\n";
590 gchar *check_end_pgp_msg_1 = "-----END PGP MESSAGE-----\r\n";
592 g_return_if_fail(msginfo != NULL);
597 if ((fp = procmsg_open_message(msginfo)) == NULL) return;
598 mimeinfo = procmime_scan_mime_header(fp);
600 if (!mimeinfo) return;
602 file = procmsg_get_message_file_path(msginfo);
603 g_return_if_fail(file != NULL);
605 if (mimeinfo->mime_type != MIME_TEXT) {
606 if ((fp = fopen(file, "r")) == NULL) {
607 FILE_OP_ERROR(file, "fopen");
611 if (mimeinfo->mime_type == MIME_MULTIPART) {
612 if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0)
614 while (fgets(buf, sizeof(buf), fp) != NULL)
615 if (buf[0] == '\r' || buf[0] == '\n') break;
617 /* now check for a pgptext encrypted message */
618 while (fgets(buf, sizeof(buf), fp) != NULL) {
619 tmpchk = g_strnfill(sizeof(buf), '\n');
620 memmove(tmpchk, &buf, sizeof(buf));
622 if (strstr(tmpchk, check_begin_pgp_msg) != NULL ||
623 strstr(tmpchk, check_begin_pgp_msg_1) != NULL)
624 has_begin_pgp_msg = TRUE;
625 if (strstr(tmpchk, check_end_pgp_msg) != NULL ||
626 strstr(tmpchk, check_end_pgp_msg_1) != NULL)
627 has_end_pgp_msg = TRUE;
633 if ((fp = fopen(file, "r")) == NULL) {
634 FILE_OP_ERROR(file, "fopen");
638 if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0)
640 while (fgets(buf, sizeof(buf), fp) != NULL)
641 if (buf[0] == '\r' || buf[0] == '\n') break;
643 /* now check for a pgptext encrypted message */
644 while (fgets(buf, sizeof(buf), fp) != NULL) {
645 tmpchk = g_strnfill(sizeof(buf), '\n');
646 memmove(tmpchk, &buf, sizeof(buf));
648 if (strstr(tmpchk, check_begin_pgp_msg) != NULL ||
649 strstr(tmpchk, check_begin_pgp_msg_1) != NULL)
650 has_begin_pgp_msg = TRUE;
651 if (strstr(tmpchk, check_end_pgp_msg) != NULL ||
652 strstr(tmpchk, check_end_pgp_msg_1) != NULL)
653 has_end_pgp_msg = TRUE;
662 /* do we have a proper message? */
663 if (has_begin_pgp_msg && has_end_pgp_msg)
669 void pgptext_decrypt_message (MsgInfo *msginfo, MimeInfo *mimeinfo, FILE *fp)
682 g_return_if_fail (mimeinfo->mime_type == MIME_TEXT);
684 debug_print ("text/plain with pgptext encountered\n");
686 partinfo = procmime_scan_message(msginfo);
689 if (fseek(fp, partinfo->fpos, SEEK_SET) < 0)
691 while (fgets(buf, sizeof(buf), fp) != NULL) {
692 partinfo->fpos = partinfo->fpos + strlen(buf);
693 if (buf[0] == '\r' || buf[0] == '\n') break;
696 while (fgets(buf, sizeof(buf), fp) != NULL)
697 partinfo->size = partinfo->size + strlen(buf);
699 plain = pgptext_decrypt (partinfo, fp);
701 msginfo->decryption_failed = 1;
705 fname = g_strdup_printf("%s%cplaintext.%08x",
706 get_mime_tmp_dir(), G_DIR_SEPARATOR, ++id);
708 if ((dstfp = fopen(fname, "w")) == NULL) {
709 FILE_OP_ERROR(fname, "fopen");
711 msginfo->decryption_failed = 1;
715 /* write the orginal header to the new file */
716 if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0)
719 while (fgets(buf, sizeof(buf), fp)) {
720 if (headerp (buf, content_names))
722 if (buf[0] == '\r' || buf[0] == '\n')
727 err = gpgme_data_rewind (plain);
729 debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
731 /* insert blank line to avoid some trouble... */
734 while (!(err = gpgme_data_read (plain, buf, sizeof(buf), &nread))) {
735 fwrite (buf, nread, 1, dstfp);
738 if (err != GPGME_EOF) {
739 debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
744 msginfo->plaintext_file = fname;
745 msginfo->decryption_failed = 0;
749 /* int pgptext_encrypt (const char *file, GSList *recp_list)
753 int pgptext_sign (const char *file, PrefsAccount *ac)
757 #endif /* USE_GPGME */