Return-Path: X-Original-To: notmuch@notmuchmail.org Delivered-To: notmuch@notmuchmail.org Received: from localhost (localhost [127.0.0.1]) by olra.theworths.org (Postfix) with ESMTP id 9FFA5431FC1 for ; Fri, 16 Apr 2010 13:52:02 -0700 (PDT) X-Virus-Scanned: Debian amavisd-new at olra.theworths.org X-Spam-Flag: NO X-Spam-Score: -1.9 X-Spam-Level: X-Spam-Status: No, score=-1.9 tagged_above=-999 required=5 tests=[BAYES_00=-1.9] autolearn=ham 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 WemCDZ5ZSTyw for ; Fri, 16 Apr 2010 13:52:00 -0700 (PDT) Received: from mail.hohndel.org (mail.hohndel.org [65.23.157.147]) by olra.theworths.org (Postfix) with ESMTP id 501724196F4 for ; Fri, 16 Apr 2010 13:51:57 -0700 (PDT) Received: by mail.hohndel.org (Postfix, from userid 112) id 0DAC0340FC; Fri, 16 Apr 2010 16:51:57 -0400 (EDT) Received: from x200.gr8dns.org (unknown [65.23.157.147]) by mail.hohndel.org (Postfix) with ESMTP id 1E634340FA; Fri, 16 Apr 2010 16:51:53 -0400 (EDT) Received: by x200.gr8dns.org (Postfix, from userid 500) id B8CD3C0162; Fri, 16 Apr 2010 13:51:52 -0700 (PDT) From: Dirk Hohndel To: Subject: [PATCH 1/2] Add interface to obtain the concatenation of all instances of a specified header Date: Fri, 16 Apr 2010 13:51:41 -0700 Message-Id: <1271451102-11336-2-git-send-email-hohndel@infradead.org> X-Mailer: git-send-email 1.6.6.1 In-Reply-To: <1271451102-11336-1-git-send-email-hohndel@infradead.org> References: <1271451102-11336-1-git-send-email-hohndel@infradead.org> 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: Fri, 16 Apr 2010 20:52:02 -0000 notmuch_message_get_header only returns the first instance of the specified header in a message. notmuch_message_get_concat_header concatenates the values from ALL instances of that header in a message. This is useful for example to get the full delivery path as captured in all of the Received: headers. Signed-off-by: Dirk Hohndel --- lib/database.cc | 14 +++++++------- lib/message-file.c | 49 +++++++++++++++++++++++++++++++++++-------------- lib/message.cc | 12 +++++++++++- lib/notmuch-private.h | 2 +- lib/notmuch.h | 16 ++++++++++++++++ 5 files changed, 70 insertions(+), 23 deletions(-) diff --git a/lib/database.cc b/lib/database.cc index 6842faf..d706263 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -1289,11 +1289,11 @@ _notmuch_database_link_message_to_parents (notmuch_database_t *notmuch, parents = g_hash_table_new_full (g_str_hash, g_str_equal, _my_talloc_free_for_g_hash, NULL); - refs = notmuch_message_file_get_header (message_file, "references"); + refs = notmuch_message_file_get_header (message_file, "references", 0); parse_references (message, notmuch_message_get_message_id (message), parents, refs); - in_reply_to = notmuch_message_file_get_header (message_file, "in-reply-to"); + in_reply_to = notmuch_message_file_get_header (message_file, "in-reply-to", 0); parse_references (message, notmuch_message_get_message_id (message), parents, in_reply_to); @@ -1506,9 +1506,9 @@ notmuch_database_add_message (notmuch_database_t *notmuch, * let's make sure that what we're looking at looks like an * actual email message. */ - from = notmuch_message_file_get_header (message_file, "from"); - subject = notmuch_message_file_get_header (message_file, "subject"); - to = notmuch_message_file_get_header (message_file, "to"); + from = notmuch_message_file_get_header (message_file, "from", 0); + subject = notmuch_message_file_get_header (message_file, "subject", 0); + to = notmuch_message_file_get_header (message_file, "to", 0); if ((from == NULL || *from == '\0') && (subject == NULL || *subject == '\0') && @@ -1521,7 +1521,7 @@ notmuch_database_add_message (notmuch_database_t *notmuch, /* Now that we're sure it's mail, the first order of business * is to find a message ID (or else create one ourselves). */ - header = notmuch_message_file_get_header (message_file, "message-id"); + header = notmuch_message_file_get_header (message_file, "message-id", 0); if (header && *header != '\0') { message_id = _parse_message_id (message_file, header, NULL); @@ -1580,7 +1580,7 @@ notmuch_database_add_message (notmuch_database_t *notmuch, if (ret) goto DONE; - date = notmuch_message_file_get_header (message_file, "date"); + date = notmuch_message_file_get_header (message_file, "date", 0); _notmuch_message_set_date (message, date); _notmuch_message_index_file (message, filename); diff --git a/lib/message-file.c b/lib/message-file.c index 0c152a3..a01adbb 100644 --- a/lib/message-file.c +++ b/lib/message-file.c @@ -209,15 +209,21 @@ copy_header_unfolding (header_value_closure_t *value, /* As a special-case, a value of NULL for header_desired will force * the entire header to be parsed if it is not parsed already. This is - * used by the _notmuch_message_file_get_headers_end function. */ + * used by the _notmuch_message_file_get_headers_end function. + * If concat is 'true' then it parses the whole message and + * concatenates all instances of the header in question. This is + * currently used to get a complete Received: header when analyzing + * the path the mail has taken from sender to recipient. + */ const char * notmuch_message_file_get_header (notmuch_message_file_t *message, - const char *header_desired) + const char *header_desired, + int concat) { int contains; - char *header, *decoded_value; + char *header, *decoded_value, *header_sofar, *combined_header; const char *s, *colon; - int match; + int match, newhdr, hdrsofar; static int initialized = 0; if (! initialized) { @@ -227,7 +233,7 @@ notmuch_message_file_get_header (notmuch_message_file_t *message, message->parsing_started = 1; - if (header_desired == NULL) + if (concat || header_desired == NULL) contains = 0; else contains = g_hash_table_lookup_extended (message->headers, @@ -237,6 +243,9 @@ notmuch_message_file_get_header (notmuch_message_file_t *message, if (contains && decoded_value) return decoded_value; + if (concat) + message->parsing_finished = 0; + if (message->parsing_finished) return ""; @@ -312,20 +321,32 @@ notmuch_message_file_get_header (notmuch_message_file_t *message, NEXT_HEADER_LINE (&message->value); - if (header_desired == 0) + if (concat || header_desired == NULL) match = 0; else match = (strcasecmp (header, header_desired) == 0); decoded_value = g_mime_utils_header_decode_text (message->value.str); - if (g_hash_table_lookup (message->headers, header) == NULL) { - /* Only insert if we don't have a value for this header, yet. - * This way we always return the FIRST instance of any header - * we search for - * FIXME: we should be returning ALL instances of a header - * or at least provide a way to iterate over them - */ - g_hash_table_insert (message->headers, header, decoded_value); + header_sofar = (char *)g_hash_table_lookup (message->headers, header); + if (concat) { + if (header_sofar == NULL) { + /* Only insert if we don't have a value for this header, yet. */ + g_hash_table_insert (message->headers, header, decoded_value); + } else { + /* the caller wants them all concatenated */ + newhdr = strlen(decoded_value); + hdrsofar = strlen(header_sofar); + combined_header = xmalloc(hdrsofar + newhdr + 2); + strncpy(combined_header,header_sofar,hdrsofar); + *(combined_header+hdrsofar) = ' '; + strncpy(combined_header+hdrsofar+1,decoded_value,newhdr+1); + g_hash_table_insert (message->headers, header, combined_header); + } + } else { + if (header_sofar == NULL) { + /* Only insert if we don't have a value for this header, yet. */ + g_hash_table_insert (message->headers, header, decoded_value); + } } if (match) return decoded_value; diff --git a/lib/message.cc b/lib/message.cc index 721c9a6..fb8fe95 100644 --- a/lib/message.cc +++ b/lib/message.cc @@ -264,7 +264,17 @@ notmuch_message_get_header (notmuch_message_t *message, const char *header) if (message->message_file == NULL) return NULL; - return notmuch_message_file_get_header (message->message_file, header); + return notmuch_message_file_get_header (message->message_file, header, 0); +} + +const char * +notmuch_message_get_concat_header (notmuch_message_t *message, const char *header) +{ + _notmuch_message_ensure_message_file (message); + if (message->message_file == NULL) + return NULL; + + return notmuch_message_file_get_header (message->message_file, header, 1); } /* Return the message ID from the In-Reply-To header of 'message'. diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h index d52d84d..9f8a10a 100644 --- a/lib/notmuch-private.h +++ b/lib/notmuch-private.h @@ -342,7 +342,7 @@ notmuch_message_file_restrict_headersv (notmuch_message_file_t *message, */ const char * notmuch_message_file_get_header (notmuch_message_file_t *message, - const char *header); + const char *header, int concat); /* messages.c */ diff --git a/lib/notmuch.h b/lib/notmuch.h index a7e66dd..d77eb5c 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -787,6 +787,22 @@ notmuch_message_get_date (notmuch_message_t *message); const char * notmuch_message_get_header (notmuch_message_t *message, const char *header); +/* Get the concatenated value of all instances of the specified header + * from 'message'. + * + * The value will be read from the actual message file, not from the + * notmuch database. The header name is case insensitive. + * + * The returned string belongs to the message so should not be + * modified or freed by the caller (nor should it be referenced after + * the message is destroyed). + * + * Returns an empty string ("") if the message does not contain a + * header line matching 'header'. Returns NULL if any error occurs. + */ +const char * +notmuch_message_get_concat_header (notmuch_message_t *message, const char *header); + /* Get the tags for 'message', returning a notmuch_tags_t object which * can be used to iterate over all tags. * -- 1.6.6.1