]> git.notmuchmail.org Git - notmuch/blobdiff - notmuch-show.c
test: Add `test_emacs_expect_t'.
[notmuch] / notmuch-show.c
index 19fb49f29b89172b63eed772ab79297e986904a1..dec799c7a0ed91801f951868b38b6b3544317ae7 100644 (file)
@@ -42,7 +42,7 @@ static void
 format_part_end_text (GMimeObject *part);
 
 static const notmuch_show_format_t format_text = {
-    "",
+    "", NULL,
        "\fmessage{ ", format_message_text,
            "\fheader{\n", format_headers_text, format_headers_message_part_text, "\fheader}\n",
            "\fbody{\n",
@@ -76,7 +76,11 @@ static void
 format_part_encstatus_json (int status);
 
 static void
+#ifdef GMIME_ATLEAST_26
+format_part_sigstatus_json (GMimeSignatureList* siglist);
+#else
 format_part_sigstatus_json (const GMimeSignatureValidity* validity);
+#endif
 
 static void
 format_part_content_json (GMimeObject *part);
@@ -85,7 +89,7 @@ static void
 format_part_end_json (GMimeObject *part);
 
 static const notmuch_show_format_t format_json = {
-    "[",
+    "[", NULL,
        "{", format_message_json,
            "\"headers\": {", format_headers_json, format_headers_message_part_json, "}",
            ", \"body\": [",
@@ -106,7 +110,7 @@ format_message_mbox (const void *ctx,
                     unused (int indent));
 
 static const notmuch_show_format_t format_mbox = {
-    "",
+    "", NULL,
         "", format_message_mbox,
             "", NULL, NULL, "",
             "",
@@ -125,7 +129,7 @@ static void
 format_part_content_raw (GMimeObject *part);
 
 static const notmuch_show_format_t format_raw = {
-    "",
+    "", NULL,
        "", NULL,
            "", NULL, format_headers_message_part_text, "\n",
             "",
@@ -360,6 +364,7 @@ format_headers_message_part_text (GMimeMessage *message)
     InternetAddressList *recipients;
     const char *recipients_string;
 
+    printf ("Subject: %s\n", g_mime_message_get_subject (message));
     printf ("From: %s\n", g_mime_message_get_sender (message));
     recipients = g_mime_message_get_recipients (message, GMIME_RECIPIENT_TYPE_TO);
     recipients_string = internet_address_list_to_string (recipients, 0);
@@ -371,7 +376,6 @@ format_headers_message_part_text (GMimeMessage *message)
     if (recipients_string)
        printf ("Cc: %s\n",
                recipients_string);
-    printf ("Subject: %s\n", g_mime_message_get_subject (message));
     printf ("Date: %s\n", g_mime_message_get_date_as_string (message));
 }
 
@@ -486,6 +490,21 @@ show_text_part_content (GMimeObject *part, GMimeStream *stream_out)
        g_object_unref(stream_filter);
 }
 
+#ifdef GMIME_ATLEAST_26
+static const char*
+signature_status_to_string (GMimeSignatureStatus x)
+{
+    switch (x) {
+    case GMIME_SIGNATURE_STATUS_GOOD:
+       return "good";
+    case GMIME_SIGNATURE_STATUS_BAD:
+       return "bad";
+    case GMIME_SIGNATURE_STATUS_ERROR:
+       return "error";
+    }
+    return "unknown";
+}
+#else
 static const char*
 signer_status_to_string (GMimeSignerStatus x)
 {
@@ -501,6 +520,7 @@ signer_status_to_string (GMimeSignerStatus x)
     }
     return "unknown";
 }
+#endif
 
 static void
 format_part_start_text (GMimeObject *part, int *part_count)
@@ -592,6 +612,73 @@ format_part_encstatus_json (int status)
     printf ("}]");
 }
 
+#ifdef GMIME_ATLEAST_26
+static void
+format_part_sigstatus_json (GMimeSignatureList *siglist)
+{
+    printf (", \"sigstatus\": [");
+
+    if (!siglist) {
+       printf ("]");
+       return;
+    }
+
+    void *ctx_quote = talloc_new (NULL);
+    int i;
+    for (i = 0; i < g_mime_signature_list_length (siglist); i++) {
+       GMimeSignature *signature = g_mime_signature_list_get_signature (siglist, i);
+
+       if (i > 0)
+           printf (", ");
+
+       printf ("{");
+
+       /* status */
+       GMimeSignatureStatus status = g_mime_signature_get_status (signature);
+       printf ("\"status\": %s",
+               json_quote_str (ctx_quote,
+                               signature_status_to_string (status)));
+
+       GMimeCertificate *certificate = g_mime_signature_get_certificate (signature);
+       if (status == GMIME_SIGNATURE_STATUS_GOOD) {
+           if (certificate)
+               printf (", \"fingerprint\": %s", json_quote_str (ctx_quote, g_mime_certificate_get_fingerprint (certificate)));
+           /* these dates are seconds since the epoch; should we
+            * provide a more human-readable format string? */
+           time_t created = g_mime_signature_get_created (signature);
+           if (created != -1)
+               printf (", \"created\": %d", (int) created);
+           time_t expires = g_mime_signature_get_expires (signature);
+           if (expires > 0)
+               printf (", \"expires\": %d", (int) expires);
+           /* output user id only if validity is FULL or ULTIMATE. */
+           /* note that gmime is using the term "trust" here, which
+            * is WRONG.  It's actually user id "validity". */
+           if (certificate) {
+               const char *name = g_mime_certificate_get_name (certificate);
+               GMimeCertificateTrust trust = g_mime_certificate_get_trust (certificate);
+               if (name && (trust == GMIME_CERTIFICATE_TRUST_FULLY || trust == GMIME_CERTIFICATE_TRUST_ULTIMATE))
+                   printf (", \"userid\": %s", json_quote_str (ctx_quote, name));
+           }
+       } else if (certificate) {
+           const char *key_id = g_mime_certificate_get_key_id (certificate);
+           if (key_id)
+               printf (", \"keyid\": %s", json_quote_str (ctx_quote, key_id));
+       }
+
+       GMimeSignatureError errors = g_mime_signature_get_errors (signature);
+       if (errors != GMIME_SIGNATURE_ERROR_NONE) {
+           printf (", \"errors\": %d", errors);
+       }
+
+       printf ("}");
+     }
+
+    printf ("]");
+
+    talloc_free (ctx_quote);
+}
+#else
 static void
 format_part_sigstatus_json (const GMimeSignatureValidity* validity)
 {
@@ -641,7 +728,7 @@ format_part_sigstatus_json (const GMimeSignatureValidity* validity)
                printf (", \"keyid\": %s", json_quote_str (ctx_quote, signer->keyid));
        }
        if (signer->errors != GMIME_SIGNER_ERROR_NONE) {
-           printf (", \"errors\": %x", signer->errors);
+           printf (", \"errors\": %d", signer->errors);
        }
 
        printf ("}");
@@ -652,6 +739,7 @@ format_part_sigstatus_json (const GMimeSignatureValidity* validity)
 
     talloc_free (ctx_quote);
 }
+#endif
 
 static void
 format_part_content_json (GMimeObject *part)
@@ -675,13 +763,31 @@ format_part_content_json (GMimeObject *part)
            printf (", \"filename\": %s", json_quote_str (ctx, filename));
     }
 
-    if (g_mime_content_type_is_type (content_type, "text", "*") &&
-       !g_mime_content_type_is_type (content_type, "text", "html"))
+    if (g_mime_content_type_is_type (content_type, "text", "*"))
     {
-       show_text_part_content (part, stream_memory);
-       part_content = g_mime_stream_mem_get_byte_array (GMIME_STREAM_MEM (stream_memory));
+       /* For non-HTML text parts, we include the content in the
+        * JSON. Since JSON must be Unicode, we handle charset
+        * decoding here and do not report a charset to the caller.
+        * For text/html parts, we do not include the content. If a
+        * caller is interested in text/html parts, it should retrieve
+        * them separately and they will not be decoded. Since this
+        * makes charset decoding the responsibility on the caller, we
+        * report the charset for text/html parts.
+        */
+       if (g_mime_content_type_is_type (content_type, "text", "html"))
+       {
+           const char *content_charset = g_mime_object_get_content_type_parameter (GMIME_OBJECT (part), "charset");
+
+           if (content_charset != NULL)
+               printf (", \"content-charset\": %s", json_quote_str (ctx, content_charset));
+       }
+       else
+       {
+           show_text_part_content (part, stream_memory);
+           part_content = g_mime_stream_mem_get_byte_array (GMIME_STREAM_MEM (stream_memory));
 
-       printf (", \"content\": %s", json_quote_chararray (ctx, (char *) part_content->data, part_content->len));
+           printf (", \"content\": %s", json_quote_chararray (ctx, (char *) part_content->data, part_content->len));
+       }
     }
     else if (g_mime_content_type_is_type (content_type, "multipart", "*"))
     {
@@ -744,6 +850,19 @@ show_message (void *ctx,
              int indent,
              notmuch_show_params_t *params)
 {
+    if (format->part) {
+       void *local = talloc_new (ctx);
+       mime_node_t *root, *part;
+
+       if (mime_node_open (local, message, params->cryptoctx, params->decrypt,
+                           &root) == NOTMUCH_STATUS_SUCCESS &&
+           (part = mime_node_seek_dfs (root, (params->part < 0 ?
+                                              0 : params->part))))
+           format->part (local, part, indent, params);
+       talloc_free (local);
+       return;
+    }
+
     if (params->part <= 0) {
        fputs (format->message_start, stdout);
        if (format->message)
@@ -758,8 +877,7 @@ show_message (void *ctx,
     }
 
     if (format->part_content)
-       show_message_body (notmuch_message_get_filename (message),
-                          format, params);
+       show_message_body (message, format, params);
 
     if (params->part <= 0) {
        fputs (format->body_end, stdout);
@@ -866,7 +984,17 @@ do_show_single (void *ctx,
 
        while (!feof (file)) {
            size = fread (buf, 1, sizeof (buf), file);
-           (void) fwrite (buf, size, 1, stdout);
+           if (ferror (file)) {
+               fprintf (stderr, "Error: Read failed from %s\n", filename);
+               fclose (file);
+               return 1;
+           }
+
+           if (fwrite (buf, size, 1, stdout) != 1) {
+               fprintf (stderr, "Error: Write failed\n");
+               fclose (file);
+               return 1;
+           }
        }
 
        fclose (file);
@@ -973,13 +1101,20 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[]))
        } else if ((STRNCMP_LITERAL (argv[i], "--verify") == 0) ||
                   (STRNCMP_LITERAL (argv[i], "--decrypt") == 0)) {
            if (params.cryptoctx == NULL) {
+#ifdef GMIME_ATLEAST_26
+               /* TODO: GMimePasswordRequestFunc */
+               if (NULL == (params.cryptoctx = g_mime_gpg_context_new(NULL, "gpg")))
+#else
                GMimeSession* session = g_object_new(g_mime_session_get_type(), NULL);
                if (NULL == (params.cryptoctx = g_mime_gpg_context_new(session, "gpg")))
+#endif
                    fprintf (stderr, "Failed to construct gpg context.\n");
                else
                    g_mime_gpg_context_set_always_trust((GMimeGpgContext*)params.cryptoctx, FALSE);
+#ifndef GMIME_ATLEAST_26
                g_object_unref (session);
                session = NULL;
+#endif
            }
            if (STRNCMP_LITERAL (argv[i], "--decrypt") == 0)
                params.decrypt = 1;