ascii-armored pgp messages: make decryption and signature verification work again
[claws.git] / src / pgptext.c
index 53a1f08c6017dad066568d8b277d9354636c1306..8ff9ae32006f59778b10a5f5e8ee9def885cc0bd 100644 (file)
@@ -30,6 +30,7 @@
 #include <string.h>
 #include <locale.h>
 #include <ctype.h>
+#include <fcntl.h>     /* FIXME */
 
 #include <gpgme.h>
 
@@ -69,6 +70,222 @@ struct passphrase_cb_info_s {
     int did_it;
 };
 
+static const gchar *
+sig_status_to_string (GpgmeSigStat status)
+{
+    const gchar *result;
+
+    switch (status) {
+      case GPGME_SIG_STAT_NONE:
+        result = _("Oops: Signature not verified");
+        break;
+      case GPGME_SIG_STAT_NOSIG:
+        result = _("No signature found");
+        break;
+      case GPGME_SIG_STAT_GOOD:
+        result = _("Good signature");
+        break;
+      case GPGME_SIG_STAT_BAD:
+        result = _("BAD signature");
+        break;
+      case GPGME_SIG_STAT_NOKEY:
+        result = _("No public key to verify the signature");
+        break;
+      case GPGME_SIG_STAT_ERROR:
+        result = _("Error verifying the signature");
+        break;
+      case GPGME_SIG_STAT_DIFF:
+        result = _("Different results for signatures");
+        break;
+      default:
+       result = _("Error: Unknown status");
+       break;
+    }
+
+    return result;
+}
+
+static const gchar *
+sig_status_with_name (GpgmeSigStat status)
+{
+    const gchar *result;
+
+    switch (status) {
+      case GPGME_SIG_STAT_NONE:
+        result = _("Oops: Signature not verified");
+        break;
+      case GPGME_SIG_STAT_NOSIG:
+        result = _("No signature found");
+        break;
+      case GPGME_SIG_STAT_GOOD:
+        result = _("Good signature from \"%s\"");
+        break;
+      case GPGME_SIG_STAT_BAD:
+        result = _("BAD signature  from \"%s\"");
+        break;
+      case GPGME_SIG_STAT_NOKEY:
+        result = _("No public key to verify the signature");
+        break;
+      case GPGME_SIG_STAT_ERROR:
+        result = _("Error verifying the signature");
+        break;
+      case GPGME_SIG_STAT_DIFF:
+        result = _("Different results for signatures");
+        break;
+      default:
+       result = _("Error: Unknown status");
+       break;
+    }
+
+    return result;
+}
+
+static void
+sig_status_for_key(GString *str, GpgmeCtx ctx, GpgmeSigStat status, 
+                  GpgmeKey key, const gchar *fpr)
+{
+       gint idx = 0;
+       const char *uid;
+
+       uid = gpgme_key_get_string_attr (key, GPGME_ATTR_USERID, NULL, idx);
+       if (uid == NULL) {
+               g_string_sprintfa (str, "%s\n",
+                                  sig_status_to_string (status));
+               if ((fpr != NULL) && (*fpr != '\0'))
+                       g_string_sprintfa (str, "Key fingerprint: %s\n", fpr);
+               g_string_append (str, _("Cannot find user ID for this key."));
+               return;
+       }
+       g_string_sprintfa (str, sig_status_with_name (status), uid);
+       g_string_append (str, "\n");
+
+       while (1) {
+               uid = gpgme_key_get_string_attr (key, GPGME_ATTR_USERID,
+                                                NULL, ++idx);
+               if (uid == NULL)
+                       break;
+               g_string_sprintfa (str, _("                aka \"%s\"\n"),
+                                  uid);
+       }
+}
+
+static gchar *
+sig_status_full (GpgmeCtx ctx)
+{
+       GString *str;
+       gint sig_idx = 0;
+       GpgmeError err;
+       GpgmeSigStat status;
+       GpgmeKey key;
+       const char *fpr;
+       time_t created;
+       struct tm *ctime_val;
+       char ctime_str[80];
+       gchar *retval;
+
+       str = g_string_new ("");
+
+       fpr = gpgme_get_sig_status (ctx, sig_idx, &status, &created);
+       while (fpr != NULL) {
+               if (created != 0) {
+                       ctime_val = localtime (&created);
+                       strftime (ctime_str, sizeof (ctime_str), "%c", 
+                                 ctime_val);
+                       g_string_sprintfa (str,
+                                          _("Signature made %s\n"),
+                                          ctime_str);
+               }
+               err = gpgme_get_sig_key (ctx, sig_idx, &key);
+               if (err != 0) {
+                       g_string_sprintfa (str, "%s\n",
+                                          sig_status_to_string (status));
+                       if ((fpr != NULL) && (*fpr != '\0'))
+                               g_string_sprintfa (str,
+                                                  _("Key fingerprint: %s\n"),
+                                                  fpr);
+               } else {
+                       sig_status_for_key (str, ctx, status, key, fpr);
+                       gpgme_key_unref (key);
+               }
+               g_string_append (str, "\n\n");
+
+               fpr = gpgme_get_sig_status (ctx, ++sig_idx, &status, &created);
+       }
+
+       retval = str->str;
+       g_string_free (str, FALSE);
+       return retval;
+}
+
+static void dump_part ( MimeInfo *mimeinfo, FILE *fp )
+{
+    unsigned int size = mimeinfo->size;
+    int c;
+
+    if (fseek (fp, mimeinfo->fpos, SEEK_SET)) {
+        g_warning ("dump_part: fseek error");
+        return;
+    }
+
+    debug_print ("** --- begin dump_part ----");
+    while (size-- && (c = getc (fp)) != EOF)
+        putc (c, stderr);
+    if (ferror (fp))
+        g_warning ("dump_part: read error");
+    debug_print ("** --- end dump_part ----");
+}
+
+static void pgptext_fine_check_signature (MimeInfo *mimeinfo, FILE *fp)
+{
+    GpgmeCtx ctx = NULL;
+    GpgmeError err;
+    GpgmeData text = NULL;
+    GpgmeSigStat status = GPGME_SIG_STAT_NONE;
+    GpgmegtkSigStatus statuswindow = NULL;
+    const char *result = NULL;
+
+       /* As this is the most simple solution, I prefer it. :-) */
+       statuswindow = gpgmegtk_sig_status_create ();
+
+    err = gpgme_new (&ctx);
+    if (err) {
+       g_warning ("gpgme_new failed: %s", gpgme_strerror (err));
+       goto leave;
+    }
+
+    err = gpgme_data_new_from_filepart (&text, NULL, fp,
+                                       mimeinfo->fpos, mimeinfo->size);
+
+    if (err) {
+        debug_print ("gpgme_data_new_from_filepart failed: %s",
+                  gpgme_strerror (err));
+        goto leave;
+    }
+
+       /* Just pass the text to gpgme_op_verify to enable plain text stuff. */
+    err = gpgme_op_verify (ctx, text, NULL, &status);
+    if (err)
+        debug_print ("gpgme_op_verify failed: %s", gpgme_strerror (err));
+
+    /* FIXME: check what the heck this sig_status_full stuff is.
+     * it should better go into sigstatus.c */
+    g_free (mimeinfo->sigstatus_full);
+    mimeinfo->sigstatus_full = sig_status_full (ctx);
+
+leave:
+    result = gpgmegtk_sig_status_to_string(status);
+    debug_print("verification status: %s\n", result);
+    gpgmegtk_sig_status_update (statuswindow,ctx);
+
+    g_assert (!err); /* FIXME: Hey: this may indeed happen */
+    g_free (mimeinfo->sigstatus);
+    mimeinfo->sigstatus = g_strdup (result);
+
+    gpgme_data_release (text);
+    gpgme_release (ctx);
+    gpgmegtk_sig_status_destroy (statuswindow);
+}
+
 /* stolen from rfc2015.c */
 static int
 gpg_name_cmp(const char *a, const char *b)
@@ -173,7 +390,7 @@ headerp(char *p, char **names)
     }
 
     if(!names[0])
-        return 1;  
+        return 1;
 
     c = *p2;
     *p2 = 0;
@@ -186,25 +403,178 @@ headerp(char *p, char **names)
     return names[i] != NULL;
 }
 
-MimeInfo * pgptext_find_signature (MimeInfo *mimeinfo)
+gboolean pgptext_has_signature (MsgInfo *msginfo, MimeInfo *mimeinfo)
 {
-}
+       FILE *fp;
+       gchar *file = NULL, *tmpchk, *tmpln, *tmpln_1;
+       gchar buf[BUFFSIZE];
+       gboolean has_begin_pgp_signed_msg = FALSE;
+       gboolean has_begin_pgp_sig = FALSE;
+       gboolean has_end_pgp_sig = FALSE;
+       gchar *check_begin_pgp_signed_msg = "-----BEGIN PGP SIGNED MESSAGE-----\n";
+       gchar *check_begin_pgp_signed_msg_1 = "-----BEGIN PGP SIGNED MESSAGE-----\r\n";
+       gchar *check_begin_pgp_sig = "-----BEGIN PGP SIGNATURE-----\n";
+       gchar *check_begin_pgp_sig_1 = "-----BEGIN PGP SIGNATURE-----\r\n";
+       gchar *check_end_pgp_sig = "-----END PGP SIGNATURE-----\n";
+       gchar *check_end_pgp_sig_1 = "-----END PGP SIGNATURE-----\r\n";
+
+       if ((!mimeinfo) || (!msginfo))
+               return FALSE;
+       
+       file = g_strdup_printf("%s", procmsg_get_message_file_path(msginfo));
+       
+       if (mimeinfo->mime_type != MIME_TEXT) {
+               if ((fp = fopen(file, "r")) == NULL) {
+                       FILE_OP_ERROR(file, "fopen");
+                       return;
+               }
+               /* skip headers */
+               if (mimeinfo->mime_type == MIME_MULTIPART) {
+                       if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0)
+                       perror("fseek");
+                       while (fgets(buf, sizeof(buf), fp) != NULL)
+                               if (buf[0] == '\r' || buf[0] == '\n') break;
+               }
+               /* now check for a pgptext signed message
+                * the strlen check catches quoted signatures */
+               while (fgets(buf, sizeof(buf), fp) != NULL) {
+                       tmpchk = g_strnfill(sizeof(buf), '\n');
+                       memmove(tmpchk, &buf, sizeof(buf));
+                       
+                       tmpln = strstr(tmpchk, check_begin_pgp_signed_msg);
+                       tmpln_1 = strstr(tmpchk, check_begin_pgp_signed_msg_1);
+                       if (((tmpln != NULL) || (tmpln_1 != NULL)) && ((strlen(tmpchk) ==
+                             strlen(tmpln)) || (strlen(tmpchk) == strlen(tmpln_1))) )
+                               has_begin_pgp_signed_msg = TRUE;
+                       
+                       tmpln = strstr(tmpchk, check_begin_pgp_sig);
+                       tmpln_1 = strstr(tmpchk, check_begin_pgp_sig_1);
+                       if (((tmpln != NULL) || (tmpln_1 != NULL)) && ((strlen(tmpchk) ==
+                             strlen(tmpln)) || (strlen(tmpchk) == strlen(tmpln_1))) )
+                               has_begin_pgp_sig = TRUE;
+                       
+                       tmpln = strstr(tmpchk, check_end_pgp_sig);
+                       tmpln_1 = strstr(tmpchk, check_end_pgp_sig_1);
+                       if (((tmpln != NULL) || (tmpln_1 != NULL)) && ((strlen(tmpchk) ==
+                             strlen(tmpln)) || (strlen(tmpchk) == strlen(tmpln_1))) )
+                               has_end_pgp_sig = TRUE;
+                       
+                       g_free(tmpchk);
+               }
+               fclose(fp);
+       } else {
+               if ((fp = fopen(file, "r")) == NULL) {
+                       FILE_OP_ERROR(file, "fopen");
+                       return;
+               }
+               /* skip headers */
+               if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0)
+               perror("fseek");
+               while (fgets(buf, sizeof(buf), fp) != NULL)
+                       if (buf[0] == '\r' || buf[0] == '\n') break;
+               
+               /* now check for a pgptext encrypted message */
+               while (fgets(buf, sizeof(buf), fp) != NULL) {
+                       tmpchk = g_strnfill(sizeof(buf), '\n');
+                       memmove(tmpchk, &buf, sizeof(buf));
 
-gboolean pgptext_has_signature (MimeInfo *mimeinfo)
-{
-       /*
-        * check for the following strings:
-        *   -----BEGIN PGP SIGNED MESSAGE-----
-        *   ----- ????
-        *   -----BEGIN PGP SIGNATURE-----
-        *   -----END PGP SIGNATURE-----
-        */
+                       if (strstr(tmpchk, check_begin_pgp_signed_msg) != NULL ||
+                                       strstr(tmpchk, check_begin_pgp_signed_msg_1) != NULL)
+                               has_begin_pgp_signed_msg = TRUE;
+                       if (strstr(tmpchk, check_begin_pgp_sig) != NULL ||
+                                       strstr(tmpchk, check_begin_pgp_sig_1) != NULL)
+                               has_begin_pgp_sig = TRUE;
+                       if (strstr(tmpchk, check_end_pgp_sig) != NULL ||
+                                       strstr(tmpchk, check_end_pgp_sig_1) != NULL)
+                               has_end_pgp_sig = TRUE;
+
+                       g_free(tmpchk);
+               }
+               fclose(fp);
+       }
+       
+       g_free(file);
        
-       return 0;
+       /* do we have a proper message? */
+       if (has_begin_pgp_signed_msg && has_begin_pgp_sig && has_end_pgp_sig) {
+               debug_print ("** pgptext signed message encountered\n");
+               return TRUE;
+       } else
+               return FALSE;
 }
 
 void pgptext_check_signature (MimeInfo *mimeinfo, FILE *fp)
 {
+       gchar *file, *tmpchk;
+       gchar buf[BUFFSIZE];
+       gboolean has_begin_pgp_signed_msg = FALSE;
+       gboolean has_begin_pgp_sig = FALSE;
+       gboolean has_end_pgp_sig = FALSE;
+       gchar *check_begin_pgp_signed_msg = "-----BEGIN PGP SIGNED MESSAGE-----\n";
+       gchar *check_begin_pgp_signed_msg_1 = "-----BEGIN PGP SIGNED
+MESSAGE-----\r\n";
+       gchar *check_begin_pgp_sig = "-----BEGIN PGP SIGNATURE-----\n";
+       gchar *check_begin_pgp_sig_1 = "-----BEGIN PGP SIGNATURE-----\r\n";
+       gchar *check_end_pgp_sig = "-----END PGP SIGNATURE-----\n";
+       gchar *check_end_pgp_sig_1 = "-----END PGP SIGNATURE-----\r\n";
+
+    if (!mimeinfo)
+        return;
+       
+       /* now we have to set fpos and size correctly */
+       /* skip headers */
+       /* FIXME: we should check for the correct mime type
+        * f.e. mime/text, application/pgp and so on...*/
+/*     if (mimeinfo->mime_type == MIME_TEXT) {*/
+               if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0)
+               perror("fseek");
+               while (fgets(buf, sizeof(buf), fp) != NULL) {
+                       mimeinfo->fpos = mimeinfo->fpos + strlen(buf);
+                       if (buf[0] == '\r' || buf[0] == '\n') break;
+               }
+/*     }*/
+
+       /* now check for fpos and size of the pgptext signed message */
+       mimeinfo->size = 0;     /* init */
+       while (fgets(buf, sizeof(buf), fp) != NULL) {
+               tmpchk = g_strnfill(sizeof(buf), '\n');
+               memmove(tmpchk, &buf, sizeof(buf));
+
+               if (has_begin_pgp_signed_msg)
+                       /* get the size */
+                       mimeinfo->size = mimeinfo->size + strlen(tmpchk);
+
+               if (strstr(tmpchk, check_begin_pgp_signed_msg) != NULL ||
+                               strstr(tmpchk, check_begin_pgp_signed_msg_1) != NULL)
+                       has_begin_pgp_signed_msg = TRUE;
+               else if (!has_begin_pgp_signed_msg)
+                       /* set the beginning of the pgptext signed message */
+                       mimeinfo->fpos = mimeinfo->fpos + strlen(tmpchk);
+
+               if (strstr(tmpchk, check_begin_pgp_sig) != NULL ||
+                               strstr(tmpchk, check_begin_pgp_sig_1) != NULL)
+                       has_begin_pgp_sig = TRUE;
+
+               if (strstr(tmpchk, check_end_pgp_sig) != NULL ||
+                               strstr(tmpchk, check_end_pgp_sig_1) != NULL) {
+                       has_end_pgp_sig = TRUE;
+                       /* FIXME: Find out why the hell there are always 6[+1] 
+                        * chars less in our counter!*/
+                       mimeinfo->size = mimeinfo->size + strlen(tmpchk) + 7;
+                       break;
+               }
+
+               g_free(tmpchk);
+       }
+       
+#if 0
+       debug_print ("** pgptext sig check...");
+       debug_print ("\tmimeinfo->fpos: %lu\tmimeinfo->size: %lu\n",
+                       mimeinfo->fpos, mimeinfo->size);
+       dump_part (mimeinfo, fp);
+#endif
+
+       pgptext_fine_check_signature (mimeinfo, fp);
 }
 
 int pgptext_is_encrypted (MimeInfo *mimeinfo, MsgInfo *msginfo)
@@ -215,13 +585,15 @@ int pgptext_is_encrypted (MimeInfo *mimeinfo, MsgInfo *msginfo)
        gboolean has_begin_pgp_msg = FALSE;
        gboolean has_end_pgp_msg = FALSE;
        gchar *check_begin_pgp_msg = "-----BEGIN PGP MESSAGE-----\n";
+       gchar *check_begin_pgp_msg_1 = "-----BEGIN PGP MESSAGE-----\r\n";
        gchar *check_end_pgp_msg = "-----END PGP MESSAGE-----\n";
+       gchar *check_end_pgp_msg_1 = "-----END PGP MESSAGE-----\r\n";
        
        g_return_if_fail(msginfo != NULL);
        
        if (!mimeinfo)
                return 0;
-       
+
        if ((fp = procmsg_open_message(msginfo)) == NULL) return;
        mimeinfo = procmime_scan_mime_header(fp);
        fclose(fp);
@@ -247,11 +619,13 @@ int pgptext_is_encrypted (MimeInfo *mimeinfo, MsgInfo *msginfo)
                        tmpchk = g_strnfill(sizeof(buf), '\n');
                        memmove(tmpchk, &buf, sizeof(buf));
                        
-                       if (strstr(tmpchk, check_begin_pgp_msg) != NULL)
+                       if (strstr(tmpchk, check_begin_pgp_msg) != NULL || 
+                           strstr(tmpchk, check_begin_pgp_msg_1) != NULL)
                                has_begin_pgp_msg = TRUE;
-                       if (strstr(tmpchk, check_end_pgp_msg) != NULL)
+                       if (strstr(tmpchk, check_end_pgp_msg) != NULL || 
+                           strstr(tmpchk, check_end_pgp_msg_1) != NULL)
                                has_end_pgp_msg = TRUE;
-                       
+
                        g_free(tmpchk);
                }
                fclose(fp);
@@ -271,17 +645,19 @@ int pgptext_is_encrypted (MimeInfo *mimeinfo, MsgInfo *msginfo)
                        tmpchk = g_strnfill(sizeof(buf), '\n');
                        memmove(tmpchk, &buf, sizeof(buf));
                        
-                       if (strstr(tmpchk, check_begin_pgp_msg) != NULL)
+                       if (strstr(tmpchk, check_begin_pgp_msg) != NULL || 
+                           strstr(tmpchk, check_begin_pgp_msg_1) != NULL)
                                has_begin_pgp_msg = TRUE;
-                       if (strstr(tmpchk, check_end_pgp_msg) != NULL)
+                       if (strstr(tmpchk, check_end_pgp_msg) != NULL || 
+                           strstr(tmpchk, check_end_pgp_msg_1) != NULL)
                                has_end_pgp_msg = TRUE;
-                       
+
                        g_free(tmpchk);
                }
                fclose(fp);
        }
        
-       g_free(file);   
+       g_free(file);
        
        /* do we have a proper message? */
        if (has_begin_pgp_msg && has_end_pgp_msg)
@@ -370,13 +746,13 @@ void pgptext_decrypt_message (MsgInfo *msginfo, MimeInfo *mimeinfo, FILE *fp)
 
 }
 
-int pgptext_encrypt (const char *file, GSList *recp_list)
+/* int pgptext_encrypt (const char *file, GSList *recp_list)
 {
 }
 
 int pgptext_sign (const char *file, PrefsAccount *ac)
 {
-}
+} */
 
 #endif /* USE_GPGME */