From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from localhost (localhost [127.0.0.1]) by olra.theworths.org (Postfix) with ESMTP id 9A95F431FD5 for ; Tue, 6 Mar 2012 10:48:58 -0800 (PST) X-Virus-Scanned: Debian amavisd-new at olra.theworths.org X-Spam-Flag: NO X-Spam-Score: -0.7 X-Spam-Level: X-Spam-Status: No, score=-0.7 tagged_above=-999 required=5 tests=[RCVD_IN_DNSWL_LOW=-0.7] autolearn=disabled Received: from olra.theworths.org ([127.0.0.1]) by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id qTHbuziACpFm for ; Tue, 6 Mar 2012 10:48:55 -0800 (PST) Received: from dmz-mailsec-scanner-3.mit.edu (DMZ-MAILSEC-SCANNER-3.MIT.EDU [18.9.25.14]) by olra.theworths.org (Postfix) with ESMTP id 8E9E7431FD2 for ; Tue, 6 Mar 2012 10:48:52 -0800 (PST) X-AuditID: 1209190e-b7f7c6d0000008c3-55-4f565c137a8b Received: from mailhub-auth-4.mit.edu ( [18.7.62.39]) by dmz-mailsec-scanner-3.mit.edu (Symantec Messaging Gateway) with SMTP id 16.70.02243.31C565F4; Tue, 6 Mar 2012 13:48:51 -0500 (EST) Received: from outgoing.mit.edu (OUTGOING-AUTH.MIT.EDU [18.7.22.103]) by mailhub-auth-4.mit.edu (8.13.8/8.9.2) with ESMTP id q26ImpKc020742; Tue, 6 Mar 2012 13:48:51 -0500 Received: from drake.mit.edu (firewall.royalsociety.org [193.63.75.2]) (authenticated bits=0) (User authenticated as amdragon@ATHENA.MIT.EDU) by outgoing.mit.edu (8.13.6/8.12.4) with ESMTP id q26ImmPx016468 (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=NOT); Tue, 6 Mar 2012 13:48:51 -0500 (EST) Received: from amthrax by drake.mit.edu with local (Exim 4.77) (envelope-from ) id 1S4zRI-0003pY-Ib; Tue, 06 Mar 2012 18:48:48 +0000 From: Austin Clements To: notmuch@notmuchmail.org Subject: [PATCH v2 7/8] show: Convert raw format to the new self-recursive style, properly support interior parts Date: Tue, 6 Mar 2012 18:48:43 +0000 Message-Id: <1331059724-14653-8-git-send-email-amdragon@mit.edu> X-Mailer: git-send-email 1.7.7.3 In-Reply-To: <1331059724-14653-1-git-send-email-amdragon@mit.edu> References: <1330752025-2542-1-git-send-email-amdragon@mit.edu> <1331059724-14653-1-git-send-email-amdragon@mit.edu> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFtrJIsWRmVeSWpSXmKPExsUixG6nriscE+ZvcKTF0mLPPi+L6zdnMjsw edw9zeXxbNUt5gCmKC6blNSczLLUIn27BK6MJfsXMRbcCa+40/ORuYFxq1sXIyeHhICJxLIF 91ghbDGJC/fWs3UxcnEICexjlFh+4DaUs55RYk//PkaQKiGBg0wSqydZQ9jzGSX+vBEGsdkE NCS27V8OViMiIC2x8+5soKkcHMwCThJtbV4gYWGBQonz/7eyg9gsAqoSCyYcZwaxeQUcJB58 OAp1hILEudXnwGo4BRwl9jzdBLW2TOLSzQssExj5FzAyrGKUTcmt0s1NzMwpTk3WLU5OzMtL LdI11svNLNFLTSndxAgKIU5Jvh2MXw8qHWIU4GBU4uG9qB/mL8SaWFZcmXuIUZKDSUmUNycK KMSXlJ9SmZFYnBFfVJqTWnyIUYKDWUmE99v7UH8h3pTEyqrUonyYlDQHi5I4r5rWOz8hgfTE ktTs1NSC1CKYrAwHh5IEb0Y00FDBotT01Iq0zJwShDQTByfIcB6g4UYgNbzFBYm5xZnpEPlT jIpS4rx+IAkBkERGaR5cLyzGXzGKA70izOsIUsUDTA9w3a+ABjMBDW5TDgYZXJKIkJJqYFwt x/I65cfeLPEthSxTfrysmPp6p0Y3+4yCtUc5rI8W7256KWn/yC/JL8k16mLOSkadLSdTi7fU ndjcc2WKGZvng6yCoxM95xn8l5xc5a/lPosvNmgHi//9PvltOzc6SPhVGfybkX4651TRRuGb FSf9DCeebgha8mE5N9OZaSav02oeZ5vdeKLEUpyRaKjFXFScCAA64X1LzAIAAA== X-BeenThere: notmuch@notmuchmail.org X-Mailman-Version: 2.1.13 Precedence: list List-Id: "Use and development of the notmuch mail system." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 06 Mar 2012 18:48:59 -0000 This is fully compatible for root and leaf parts, but now has proper support for interior parts. This requires some design decisions that were guided by what I would want if I were to save a part. Specifically: - Leaf parts are printed without headers and with transfer decoding. This is what makes sense for saving attachments. (Furthermore, the transfer decoding is necessary since, without the headers, the caller would not be able to interpret non-transfer-decoded output.) - Message parts are printed with their message headers, but without enclosing part headers. This is what makes sense for saving a message as a whole (which is a message part) and for saving attached messages. This is symmetric for whole messages and for attached messages, though we special-case the whole message for performance reasons (and corner-case correctness reasons: given malformed input, GMime may not be able to reproduce it from the parsed representation). - Multipart parts are printed with their headers and all child parts. It's not clear what the best thing to do for multipart is, but this was the most natural to implement and can be justified because such parts can't be interpreted without their headers. As an added benefit, we can move the special-case code for part 0 into the raw formatter. --- notmuch-show.c | 159 ++++++++++++++++++++++++-------------------------------- test/multipart | 74 +++++++++++++++++--------- 2 files changed, 116 insertions(+), 117 deletions(-) diff --git a/notmuch-show.c b/notmuch-show.c index 6f6052c..8ed6896 100644 --- a/notmuch-show.c +++ b/notmuch-show.c @@ -20,9 +20,6 @@ #include "notmuch-client.h" -static void -format_headers_message_part_text (GMimeMessage *message); - static notmuch_status_t format_part_text (const void *ctx, mime_node_t *node, int indent, const notmuch_show_params_t *params); @@ -56,23 +53,16 @@ static const notmuch_show_format_t format_mbox = { .message_set_end = "" }; -static void -format_part_content_raw (GMimeObject *part); +static notmuch_status_t +format_part_raw (unused (const void *ctx), mime_node_t *node, + unused (int indent), + unused (const notmuch_show_params_t *params)); static const notmuch_show_format_t format_raw = { - "", NULL, - "", NULL, - "", NULL, format_headers_message_part_text, "\n", - "", - NULL, - NULL, - NULL, - format_part_content_raw, - NULL, - "", - "", - "", "", - "" + .message_set_start = "", + .part = format_part_raw, + .message_set_sep = "", + .message_set_end = "" }; static const char * @@ -210,27 +200,6 @@ _is_from_line (const char *line) } static void -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); - if (recipients_string) - printf ("To: %s\n", - recipients_string); - recipients = g_mime_message_get_recipients (message, GMIME_RECIPIENT_TYPE_CC); - recipients_string = internet_address_list_to_string (recipients, 0); - if (recipients_string) - printf ("Cc: %s\n", - recipients_string); - printf ("Date: %s\n", g_mime_message_get_date_as_string (message)); -} - -static void format_headers_json (const void *ctx, GMimeMessage *message) { void *local = talloc_new (ctx); @@ -731,31 +700,82 @@ format_part_mbox (const void *ctx, mime_node_t *node, unused (int indent), return NOTMUCH_STATUS_SUCCESS; } -static void -format_part_content_raw (GMimeObject *part) +static notmuch_status_t +format_part_raw (unused (const void *ctx), mime_node_t *node, + unused (int indent), + unused (const notmuch_show_params_t *params)) { - if (! GMIME_IS_PART (part)) - return; + if (node->envelope_file) { + /* Special case the entire message to avoid MIME parsing. */ + const char *filename; + FILE *file; + size_t size; + char buf[4096]; + + filename = notmuch_message_get_filename (node->envelope_file); + if (filename == NULL) { + fprintf (stderr, "Error: Cannot get message filename.\n"); + return NOTMUCH_STATUS_FILE_ERROR; + } + + file = fopen (filename, "r"); + if (file == NULL) { + fprintf (stderr, "Error: Cannot open file %s: %s\n", filename, strerror (errno)); + return NOTMUCH_STATUS_FILE_ERROR; + } + + while (!feof (file)) { + size = fread (buf, 1, sizeof (buf), file); + if (ferror (file)) { + fprintf (stderr, "Error: Read failed from %s\n", filename); + fclose (file); + return NOTMUCH_STATUS_FILE_ERROR; + } + + if (fwrite (buf, size, 1, stdout) != 1) { + fprintf (stderr, "Error: Write failed\n"); + fclose (file); + return NOTMUCH_STATUS_FILE_ERROR; + } + } + + fclose (file); + return NOTMUCH_STATUS_SUCCESS; + } GMimeStream *stream_stdout; GMimeStream *stream_filter = NULL; - GMimeDataWrapper *wrapper; stream_stdout = g_mime_stream_file_new (stdout); g_mime_stream_file_set_owner (GMIME_STREAM_FILE (stream_stdout), FALSE); stream_filter = g_mime_stream_filter_new (stream_stdout); - wrapper = g_mime_part_get_content_object (GMIME_PART (part)); + if (GMIME_IS_PART (node->part)) { + /* For leaf parts, we emit only the transfer-decoded + * body. */ + GMimeDataWrapper *wrapper; + wrapper = g_mime_part_get_content_object (GMIME_PART (node->part)); - if (wrapper && stream_filter) - g_mime_data_wrapper_write_to_stream (wrapper, stream_filter); + if (wrapper && stream_filter) + g_mime_data_wrapper_write_to_stream (wrapper, stream_filter); + } else { + /* Write out the whole part. For message parts (the root + * part and embedded message parts), this will be the + * message including its headers (but not the + * encapsulating part's headers). For multipart parts, + * this will include the headers. */ + if (stream_filter) + g_mime_object_write_to_stream (node->part, stream_filter); + } if (stream_filter) g_object_unref (stream_filter); if (stream_stdout) g_object_unref(stream_stdout); + + return NOTMUCH_STATUS_SUCCESS; } static notmuch_status_t @@ -891,50 +911,7 @@ do_show_single (void *ctx, notmuch_message_set_flag (message, NOTMUCH_MESSAGE_FLAG_MATCH, 1); - /* Special case for --format=raw of full single message, just cat out file */ - if (params->raw && 0 == params->part) { - - const char *filename; - FILE *file; - size_t size; - char buf[4096]; - - filename = notmuch_message_get_filename (message); - if (filename == NULL) { - fprintf (stderr, "Error: Cannot message filename.\n"); - return 1; - } - - file = fopen (filename, "r"); - if (file == NULL) { - fprintf (stderr, "Error: Cannot open file %s: %s\n", filename, strerror (errno)); - return 1; - } - - while (!feof (file)) { - size = fread (buf, 1, sizeof (buf), file); - 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); - - return 0; - - } else { - - return show_message (ctx, format, message, 0, params) != NOTMUCH_STATUS_SUCCESS; - - } + return show_message (ctx, format, message, 0, params) != NOTMUCH_STATUS_SUCCESS; } /* Formatted output of threads */ diff --git a/test/multipart b/test/multipart index 475c6a2..c1a5702 100755 --- a/test/multipart +++ b/test/multipart @@ -450,58 +450,80 @@ test_expect_equal_file OUTPUT "${MAIL_DIR}"/multipart test_begin_subtest "--format=raw --part=1, message body" notmuch show --format=raw --part=1 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT -# output should *not* include newline -echo >>OUTPUT -cat <EXPECTED -Subject: html message -From: Carl Worth -To: cworth@cworth.org -Date: Fri, 05 Jan 2001 15:42:57 +0000 - -

This is an embedded message, with a multipart/alternative part.

-This is an embedded message, with a multipart/alternative part. -This is a text attachment. -And this message is signed. - --Carl ------BEGIN PGP SIGNATURE----- -Version: GnuPG v1.4.11 (GNU/Linux) - -iEYEARECAAYFAk3SA/gACgkQ6JDdNq8qSWj0sACghqVJEQJUs3yV8zbTzhgnSIcD -W6cAmQE4dcYrx/LPLtYLZm1jsGauE5hE -=zkga ------END PGP SIGNATURE----- -EOF -test_expect_equal_file OUTPUT EXPECTED +test_expect_equal_file OUTPUT "${MAIL_DIR}"/multipart test_begin_subtest "--format=raw --part=2, multipart/mixed" notmuch show --format=raw --part=2 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT cat <EXPECTED -Subject: html message +Content-Type: multipart/mixed; boundary="=-=-=" + +--=-=-= +Content-Type: message/rfc822 +Content-Disposition: inline + From: Carl Worth To: cworth@cworth.org +Subject: html message Date: Fri, 05 Jan 2001 15:42:57 +0000 +User-Agent: Notmuch/0.5 (http://notmuchmail.org) Emacs/23.3.1 (i486-pc-linux-gnu) +Message-ID: <87liy5ap01.fsf@yoom.home.cworth.org> +MIME-Version: 1.0 +Content-Type: multipart/alternative; boundary="==-=-==" + +--==-=-== +Content-Type: text/html

This is an embedded message, with a multipart/alternative part.

+ +--==-=-== +Content-Type: text/plain + This is an embedded message, with a multipart/alternative part. + +--==-=-==-- + +--=-=-= +Content-Disposition: attachment; filename=attachment + This is a text attachment. + +--=-=-= + And this message is signed. -Carl + +--=-=-=-- EOF test_expect_equal_file OUTPUT EXPECTED test_begin_subtest "--format=raw --part=3, rfc822 part" -test_subtest_known_broken - notmuch show --format=raw --part=3 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT test_expect_equal_file OUTPUT embedded_message test_begin_subtest "--format=raw --part=4, rfc822's multipart" notmuch show --format=raw --part=4 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT cat <EXPECTED +From: Carl Worth +To: cworth@cworth.org +Subject: html message +Date: Fri, 05 Jan 2001 15:42:57 +0000 +User-Agent: Notmuch/0.5 (http://notmuchmail.org) Emacs/23.3.1 (i486-pc-linux-gnu) +Message-ID: <87liy5ap01.fsf@yoom.home.cworth.org> +MIME-Version: 1.0 +Content-Type: multipart/alternative; boundary="==-=-==" + +--==-=-== +Content-Type: text/html +

This is an embedded message, with a multipart/alternative part.

+ +--==-=-== +Content-Type: text/plain + This is an embedded message, with a multipart/alternative part. + +--==-=-==-- EOF test_expect_equal_file OUTPUT EXPECTED -- 1.7.7.3