]> git.notmuchmail.org Git - notmuch/blobdiff - notmuch-show.c
cli/show: pass the siglist directly to the sigstatus sprinter
[notmuch] / notmuch-show.c
index 367536ff95328f3642800d91c62ae666a08b12f7..88699e90666d4715a302d78426352e36b57f1312 100644 (file)
@@ -196,7 +196,7 @@ _is_from_line (const char *line)
 
 void
 format_headers_sprinter (sprinter_t *sp, GMimeMessage *message,
-                        notmuch_bool_t reply)
+                        bool reply)
 {
     /* Any changes to the JSON or S-Expression format should be
      * reflected in the file devel/schemata. */
@@ -272,6 +272,7 @@ show_text_part_content (GMimeObject *part, GMimeStream *stream_out,
     GMimeContentType *content_type = g_mime_object_get_content_type (GMIME_OBJECT (part));
     GMimeStream *stream_filter = NULL;
     GMimeFilter *crlf_filter = NULL;
+    GMimeFilter *windows_filter = NULL;
     GMimeDataWrapper *wrapper;
     const char *charset;
 
@@ -282,13 +283,37 @@ show_text_part_content (GMimeObject *part, GMimeStream *stream_out,
     if (stream_out == NULL)
        return;
 
+    charset = g_mime_object_get_content_type_parameter (part, "charset");
+    charset = charset ? g_mime_charset_canon_name (charset) : NULL;
+    wrapper = g_mime_part_get_content_object (GMIME_PART (part));
+    if (wrapper && charset && !g_ascii_strncasecmp (charset, "iso-8859-", 9)) {
+       GMimeStream *null_stream = NULL;
+       GMimeStream *null_stream_filter = NULL;
+
+       /* Check for mislabeled Windows encoding */
+       null_stream = g_mime_stream_null_new ();
+       null_stream_filter = g_mime_stream_filter_new (null_stream);
+       windows_filter = g_mime_filter_windows_new (charset);
+       g_mime_stream_filter_add(GMIME_STREAM_FILTER (null_stream_filter),
+                                windows_filter);
+       g_mime_data_wrapper_write_to_stream (wrapper, null_stream_filter);
+       charset = g_mime_filter_windows_real_charset(
+           (GMimeFilterWindows *) windows_filter);
+
+       if (null_stream_filter)
+           g_object_unref (null_stream_filter);
+       if (null_stream)
+           g_object_unref (null_stream);
+       /* Keep a reference to windows_filter in order to prevent the
+        * charset string from deallocation. */
+    }
+
     stream_filter = g_mime_stream_filter_new (stream_out);
-    crlf_filter = g_mime_filter_crlf_new (FALSE, FALSE);
+    crlf_filter = g_mime_filter_crlf_new (false, false);
     g_mime_stream_filter_add(GMIME_STREAM_FILTER (stream_filter),
                             crlf_filter);
     g_object_unref (crlf_filter);
 
-    charset = g_mime_object_get_content_type_parameter (part, "charset");
     if (charset) {
        GMimeFilter *charset_filter;
        charset_filter = g_mime_filter_charset_new (charset, "UTF-8");
@@ -305,7 +330,7 @@ show_text_part_content (GMimeObject *part, GMimeStream *stream_out,
 
     if (flags & NOTMUCH_SHOW_TEXT_PART_REPLY) {
        GMimeFilter *reply_filter;
-       reply_filter = g_mime_filter_reply_new (TRUE);
+       reply_filter = g_mime_filter_reply_new (true);
        if (reply_filter) {
            g_mime_stream_filter_add (GMIME_STREAM_FILTER (stream_filter),
                                      reply_filter);
@@ -313,11 +338,12 @@ show_text_part_content (GMimeObject *part, GMimeStream *stream_out,
        }
     }
 
-    wrapper = g_mime_part_get_content_object (GMIME_PART (part));
     if (wrapper && stream_filter)
        g_mime_data_wrapper_write_to_stream (wrapper, stream_filter);
     if (stream_filter)
        g_object_unref(stream_filter);
+    if (windows_filter)
+       g_object_unref (windows_filter);
 }
 
 static const char*
@@ -350,7 +376,7 @@ do_format_signature_errors (sprinter_t *sp, struct key_map_struct *key_map,
     for (unsigned int i = 0; i < array_map_len; i++) {
        if (errors & key_map[i].bit) {
            sp->map_key (sp, key_map[i].string);
-           sp->boolean (sp, TRUE);
+           sp->boolean (sp, true);
        }
     }
 
@@ -403,13 +429,11 @@ format_signature_errors (sprinter_t *sp, GMimeSignature *signature)
 
 /* Signature status sprinter (GMime 2.6) */
 static void
-format_part_sigstatus_sprinter (sprinter_t *sp, mime_node_t *node)
+format_part_sigstatus_sprinter (sprinter_t *sp, GMimeSignatureList *siglist)
 {
     /* Any changes to the JSON or S-Expression format should be
      * reflected in the file devel/schemata. */
 
-    GMimeSignatureList *siglist = node->sig_list;
-
     sp->begin_list (sp);
 
     if (!siglist) {
@@ -446,15 +470,11 @@ format_part_sigstatus_sprinter (sprinter_t *sp, mime_node_t *node)
                sp->map_key (sp, "expires");
                sp->integer (sp, 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_uid (certificate);
-               GMimeCertificateTrust trust = g_mime_certificate_get_trust (certificate);
-               if (name && (trust == GMIME_CERTIFICATE_TRUST_FULLY || trust == GMIME_CERTIFICATE_TRUST_ULTIMATE)) {
+               const char *uid = g_mime_certificate_get_valid_userid (certificate);
+               if (uid) {
                    sp->map_key (sp, "userid");
-                   sp->string (sp, name);
+                   sp->string (sp, uid);
                }
            }
        } else if (certificate) {
@@ -490,7 +510,7 @@ format_part_text (const void *ctx, sprinter_t *sp, mime_node_t *node,
     GMimeObject *meta = node->envelope_part ?
        GMIME_OBJECT (node->envelope_part) : node->part;
     GMimeContentType *content_type = g_mime_object_get_content_type (meta);
-    const notmuch_bool_t leaf = GMIME_IS_PART (node->part);
+    const bool leaf = GMIME_IS_PART (node->part);
     GMimeStream *stream = params->out_stream;
     const char *part_type;
     int i;
@@ -552,12 +572,18 @@ format_part_text (const void *ctx, sprinter_t *sp, mime_node_t *node,
        g_mime_stream_printf (stream, "Date: %s\n", date_string);
        g_mime_stream_printf (stream, "\fheader}\n");
 
+       if (!params->output_body)
+       {
+           g_mime_stream_printf (stream, "\f%s}\n", part_type);
+           return NOTMUCH_STATUS_SUCCESS;
+       }
        g_mime_stream_printf (stream, "\fbody{\n");
     }
 
     if (leaf) {
        if (g_mime_content_type_is_type (content_type, "text", "*") &&
-           !g_mime_content_type_is_type (content_type, "text", "html"))
+           (params->include_html ||
+            ! g_mime_content_type_is_type (content_type, "text", "html")))
        {
            show_text_part_content (node->part, stream, 0);
        } else {
@@ -603,8 +629,8 @@ format_omitted_part_meta_sprinter (sprinter_t *sp, GMimeObject *meta, GMimePart
 
 void
 format_part_sprinter (const void *ctx, sprinter_t *sp, mime_node_t *node,
-                     notmuch_bool_t output_body,
-                     notmuch_bool_t include_html)
+                     bool output_body,
+                     bool include_html)
 {
     /* Any changes to the JSON or S-Expression format should be
      * reflected in the file devel/schemata. */
@@ -614,12 +640,12 @@ format_part_sprinter (const void *ctx, sprinter_t *sp, mime_node_t *node,
        format_message_sprinter (sp, node->envelope_file);
 
        sp->map_key (sp, "headers");
-       format_headers_sprinter (sp, GMIME_MESSAGE (node->part), FALSE);
+       format_headers_sprinter (sp, GMIME_MESSAGE (node->part), false);
 
        if (output_body) {
            sp->map_key (sp, "body");
            sp->begin_list (sp);
-           format_part_sprinter (ctx, sp, mime_node_child (node, 0), TRUE, include_html);
+           format_part_sprinter (ctx, sp, mime_node_child (node, 0), true, include_html);
            sp->end (sp);
        }
        sp->end (sp);
@@ -656,7 +682,7 @@ format_part_sprinter (const void *ctx, sprinter_t *sp, mime_node_t *node,
 
     if (node->verify_attempted) {
        sp->map_key (sp, "sigstatus");
-       format_part_sigstatus_sprinter (sp, node);
+       format_part_sigstatus_sprinter (sp, node->sig_list);
     }
 
     sp->map_key (sp, "content-type");
@@ -713,7 +739,7 @@ format_part_sprinter (const void *ctx, sprinter_t *sp, mime_node_t *node,
        sp->begin_map (sp);
 
        sp->map_key (sp, "headers");
-       format_headers_sprinter (sp, GMIME_MESSAGE (node->part), FALSE);
+       format_headers_sprinter (sp, GMIME_MESSAGE (node->part), false);
 
        sp->map_key (sp, "body");
        sp->begin_list (sp);
@@ -721,7 +747,7 @@ format_part_sprinter (const void *ctx, sprinter_t *sp, mime_node_t *node,
     }
 
     for (i = 0; i < node->nchildren; i++)
-       format_part_sprinter (ctx, sp, mime_node_child (node, i), TRUE, include_html);
+       format_part_sprinter (ctx, sp, mime_node_child (node, i), true, include_html);
 
     /* Close content structures */
     for (i = 0; i < nclose; i++)
@@ -877,6 +903,11 @@ show_message (void *ctx,
     void *local = talloc_new (ctx);
     mime_node_t *root, *part;
     notmuch_status_t status;
+    unsigned int session_keys = 0;
+    notmuch_status_t session_key_count_error = NOTMUCH_STATUS_SUCCESS;
+
+    if (params->crypto.decrypt == NOTMUCH_DECRYPT_TRUE)
+       session_key_count_error = notmuch_message_count_properties (message, "session-key", &session_keys);
 
     status = mime_node_open (local, message, &(params->crypto), &root);
     if (status)
@@ -884,6 +915,21 @@ show_message (void *ctx,
     part = mime_node_seek_dfs (root, (params->part < 0 ? 0 : params->part));
     if (part)
        status = format->part (local, sp, part, indent, params);
+#if HAVE_GMIME_SESSION_KEYS
+    if (params->crypto.decrypt == NOTMUCH_DECRYPT_TRUE && session_key_count_error == NOTMUCH_STATUS_SUCCESS) {
+       unsigned int new_session_keys = 0;
+       if (notmuch_message_count_properties (message, "session-key", &new_session_keys) == NOTMUCH_STATUS_SUCCESS &&
+           new_session_keys > session_keys) {
+           /* try a quiet re-indexing */
+           notmuch_indexopts_t *indexopts = notmuch_database_get_default_indexopts (notmuch_message_get_database (message));
+           if (indexopts) {
+               notmuch_indexopts_set_decrypt_policy (indexopts, NOTMUCH_DECRYPT_AUTO);
+               print_status_message ("Error re-indexing message with --decrypt=stash",
+                                     message, notmuch_message_reindex (message, indexopts));
+           }
+       }
+    }
+#endif
   DONE:
     talloc_free (local);
     return status;
@@ -898,8 +944,8 @@ show_messages (void *ctx,
               notmuch_show_params_t *params)
 {
     notmuch_message_t *message;
-    notmuch_bool_t match;
-    notmuch_bool_t excluded;
+    bool match;
+    bool excluded;
     int next_indent;
     notmuch_status_t status, res = NOTMUCH_STATUS_SUCCESS;
 
@@ -1081,16 +1127,14 @@ notmuch_show_command (notmuch_config_t *config, int argc, char *argv[])
     sprinter_t *sprinter;
     notmuch_show_params_t params = {
        .part = -1,
-       .omit_excluded = TRUE,
-       .output_body = TRUE,
+       .omit_excluded = true,
+       .output_body = true,
+       .crypto = { .decrypt = NOTMUCH_DECRYPT_AUTO },
     };
     int format = NOTMUCH_FORMAT_NOT_SPECIFIED;
-    int exclude = TRUE;
-
-    /* This value corresponds to neither true nor false being passed
-     * on the command line */
-    int entire_thread = -1;
-    notmuch_bool_t single_message;
+    bool exclude = true;
+    bool entire_thread_set = false;
+    bool single_message;
 
     notmuch_opt_desc_t options[] = {
        { .opt_keyword = &format, .name = "format", .keywords =
@@ -1102,9 +1146,16 @@ notmuch_show_command (notmuch_config_t *config, int argc, char *argv[])
                                  { 0, 0 } } },
        { .opt_int = &notmuch_format_version, .name = "format-version" },
        { .opt_bool = &exclude, .name = "exclude" },
-       { .opt_bool = &entire_thread, .name = "entire-thread" },
+       { .opt_bool = &params.entire_thread, .name = "entire-thread",
+         .present = &entire_thread_set },
        { .opt_int = &params.part, .name = "part" },
-       { .opt_bool = &params.crypto.decrypt, .name = "decrypt" },
+       { .opt_keyword = (int*)(&params.crypto.decrypt), .name = "decrypt",
+         .keyword_no_arg_value = "true", .keywords =
+         (notmuch_keyword_t []){ { "false", NOTMUCH_DECRYPT_FALSE },
+                                 { "auto", NOTMUCH_DECRYPT_AUTO },
+                                 { "true", NOTMUCH_DECRYPT_NOSTASH },
+                                 { "stash", NOTMUCH_DECRYPT_TRUE },
+                                 { 0, 0 } } },
        { .opt_bool = &params.crypto.verify, .name = "verify" },
        { .opt_bool = &params.output_body, .name = "body" },
        { .opt_bool = &params.include_html, .name = "include-html" },
@@ -1118,9 +1169,10 @@ notmuch_show_command (notmuch_config_t *config, int argc, char *argv[])
 
     notmuch_process_shared_options (argv[0]);
 
-    /* decryption implies verification */
-    if (params.crypto.decrypt)
-       params.crypto.verify = TRUE;
+    /* explicit decryption implies verification */
+    if (params.crypto.decrypt == NOTMUCH_DECRYPT_NOSTASH ||
+       params.crypto.decrypt == NOTMUCH_DECRYPT_TRUE)
+       params.crypto.verify = true;
 
     /* specifying a part implies single message display */
     single_message = params.part >= 0;
@@ -1140,36 +1192,35 @@ notmuch_show_command (notmuch_config_t *config, int argc, char *argv[])
        }
     } else if (format == NOTMUCH_FORMAT_RAW) {
        /* raw format only supports single message display */
-       single_message = TRUE;
+       single_message = true;
     }
 
     notmuch_exit_if_unsupported_format ();
 
-    /* Default is entire-thread = FALSE except for format=json and
+    /* Default is entire-thread = false except for format=json and
      * format=sexp. */
-    if (entire_thread != FALSE && entire_thread != TRUE) {
-       if (format == NOTMUCH_FORMAT_JSON || format == NOTMUCH_FORMAT_SEXP)
-           params.entire_thread = TRUE;
-       else
-           params.entire_thread = FALSE;
-    } else {
-       params.entire_thread = entire_thread;
-    }
+    if (! entire_thread_set &&
+       (format == NOTMUCH_FORMAT_JSON || format == NOTMUCH_FORMAT_SEXP))
+       params.entire_thread = true;
 
     if (!params.output_body) {
        if (params.part > 0) {
            fprintf (stderr, "Warning: --body=false is incompatible with --part > 0. Disabling.\n");
-           params.output_body = TRUE;
+           params.output_body = true;
        } else {
-           if (format != NOTMUCH_FORMAT_JSON && format != NOTMUCH_FORMAT_SEXP)
+           if (format != NOTMUCH_FORMAT_TEXT &&
+               format != NOTMUCH_FORMAT_JSON &&
+               format != NOTMUCH_FORMAT_SEXP)
                fprintf (stderr,
-                        "Warning: --body=false only implemented for format=json and format=sexp\n");
+                        "Warning: --body=false only implemented for format=text, format=json and format=sexp\n");
        }
     }
 
     if (params.include_html &&
-        (format != NOTMUCH_FORMAT_JSON && format != NOTMUCH_FORMAT_SEXP)) {
-       fprintf (stderr, "Warning: --include-html only implemented for format=json and format=sexp\n");
+        (format != NOTMUCH_FORMAT_TEXT &&
+        format != NOTMUCH_FORMAT_JSON &&
+        format != NOTMUCH_FORMAT_SEXP)) {
+       fprintf (stderr, "Warning: --include-html only implemented for format=text, format=json and format=sexp\n");
     }
 
     query_string = query_string_from_args (config, argc-opt_index, argv+opt_index);
@@ -1187,8 +1238,11 @@ notmuch_show_command (notmuch_config_t *config, int argc, char *argv[])
     params.crypto.gpgpath = notmuch_config_get_crypto_gpg_path (config);
 #endif
 
+    notmuch_database_mode_t mode = NOTMUCH_DATABASE_MODE_READ_ONLY;
+    if (params.crypto.decrypt == NOTMUCH_DECRYPT_TRUE)
+       mode = NOTMUCH_DATABASE_MODE_READ_WRITE;
     if (notmuch_database_open (notmuch_config_get_database_path (config),
-                              NOTMUCH_DATABASE_MODE_READ_ONLY, &notmuch))
+                              mode, &notmuch))
        return EXIT_FAILURE;
 
     notmuch_exit_if_unmatched_db_uuid (notmuch);
@@ -1229,9 +1283,9 @@ notmuch_show_command (notmuch_config_t *config, int argc, char *argv[])
            }
        }
 
-       if (exclude == FALSE) {
-           notmuch_query_set_omit_excluded (query, FALSE);
-           params.omit_excluded = FALSE;
+       if (exclude == false) {
+           notmuch_query_set_omit_excluded (query, false);
+           params.omit_excluded = false;
        }
 
        ret = do_show (config, query, formatter, sprinter, &params);
@@ -1241,7 +1295,7 @@ notmuch_show_command (notmuch_config_t *config, int argc, char *argv[])
     g_mime_stream_flush (params.out_stream);
     g_object_unref (params.out_stream);
 
-    notmuch_crypto_cleanup (&params.crypto);
+    _notmuch_crypto_cleanup (&params.crypto);
     notmuch_query_destroy (query);
     notmuch_database_destroy (notmuch);