index: repair "Mixed Up" messages before indexing.

When encountering a message that has been mangled in the "mixed up"
way by an intermediate MTA, notmuch should instead repair it and index
the repaired form.

When it does this, it also associates the index.repaired=mixedup
property with the message.  If a problem is found with this repair
process, or an improved repair process is proposed later, this should
make it easy for people to reindex the relevant message.  The property
will also hopefully make it easier to diagnose this particular problem
in the future.

Signed-off-by: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
This commit is contained in:
Daniel Kahn Gillmor 2019-05-28 14:42:26 -04:00
parent 67666538b3
commit 4b1a8fd183
3 changed files with 23 additions and 7 deletions

View file

@ -127,6 +127,12 @@ of its normal activity.
found in that message, since it was able to index the built-in found in that message, since it was able to index the built-in
protected headers directly. protected headers directly.
``index.repaired=mixedup`` indicates the repair of a "Mixed Up"
encrypted PGP/MIME message, a mangling typically produced by
Microsoft's Exchange MTA. See
https://tools.ietf.org/html/draft-dkg-openpgp-pgpmime-message-mangling
for more information.
SEE ALSO SEE ALSO
======== ========

View file

@ -387,11 +387,20 @@ _index_mime_part (notmuch_message_t *message,
GMimeContentType *content_type; GMimeContentType *content_type;
char *body; char *body;
const char *charset; const char *charset;
GMimeObject *repaired_part = NULL;
if (! part) { if (! part) {
_notmuch_database_log (notmuch_message_get_database (message), _notmuch_database_log (notmuch_message_get_database (message),
"Warning: Not indexing empty mime part.\n"); "Warning: Not indexing empty mime part.\n");
return; goto DONE;
}
repaired_part = _notmuch_repair_mixed_up_mangled (part);
if (repaired_part) {
/* This was likely "Mixed Up" in transit! We will instead use
* the more likely-to-be-correct variant. */
notmuch_message_add_property (message, "index.repaired", "mixedup");
part = repaired_part;
} }
_index_content_type (message, part); _index_content_type (message, part);
@ -444,7 +453,7 @@ _index_mime_part (notmuch_message_t *message,
} }
_index_mime_part (message, indexopts, toindex, msg_crypto); _index_mime_part (message, indexopts, toindex, msg_crypto);
} }
return; goto DONE;
} }
if (GMIME_IS_MESSAGE_PART (part)) { if (GMIME_IS_MESSAGE_PART (part)) {
@ -454,14 +463,14 @@ _index_mime_part (notmuch_message_t *message,
_index_mime_part (message, indexopts, g_mime_message_get_mime_part (mime_message), msg_crypto); _index_mime_part (message, indexopts, g_mime_message_get_mime_part (mime_message), msg_crypto);
return; goto DONE;
} }
if (! (GMIME_IS_PART (part))) { if (! (GMIME_IS_PART (part))) {
_notmuch_database_log (notmuch_message_get_database (message), _notmuch_database_log (notmuch_message_get_database (message),
"Warning: Not indexing unknown mime part: %s.\n", "Warning: Not indexing unknown mime part: %s.\n",
g_type_name (G_OBJECT_TYPE (part))); g_type_name (G_OBJECT_TYPE (part)));
return; goto DONE;
} }
disposition = g_mime_object_get_content_disposition (part); disposition = g_mime_object_get_content_disposition (part);
@ -475,7 +484,7 @@ _index_mime_part (notmuch_message_t *message,
/* XXX: Would be nice to call out to something here to parse /* XXX: Would be nice to call out to something here to parse
* the attachment into text and then index that. */ * the attachment into text and then index that. */
return; goto DONE;
} }
byte_array = g_byte_array_new (); byte_array = g_byte_array_new ();
@ -521,6 +530,9 @@ _index_mime_part (notmuch_message_t *message,
free (body); free (body);
} }
DONE:
if (repaired_part)
g_object_unref (repaired_part);
} }
/* descend (if desired) into the cleartext part of an encrypted MIME /* descend (if desired) into the cleartext part of an encrypted MIME

View file

@ -21,7 +21,6 @@ test_json_nodes <<<"$output" \
'body:["original"]'"$bodytext" 'body:["original"]'"$bodytext"
test_begin_subtest "repaired 'Mixed-up' messages can be found with index.repaired=mixedup" test_begin_subtest "repaired 'Mixed-up' messages can be found with index.repaired=mixedup"
test_subtest_known_broken
output=$(notmuch search --output=messages property:index.repaired=mixedup) output=$(notmuch search --output=messages property:index.repaired=mixedup)
test_expect_equal "$output" id:mixed-up@mangling.notmuchmail.org test_expect_equal "$output" id:mixed-up@mangling.notmuchmail.org
@ -29,7 +28,6 @@ test_begin_subtest "index cleartext of 'Mixed-Up' mangled PGP/MIME message"
test_expect_success 'notmuch reindex --decrypt=true id:mixed-up@mangling.notmuchmail.org' test_expect_success 'notmuch reindex --decrypt=true id:mixed-up@mangling.notmuchmail.org'
test_begin_subtest "search cleartext of 'Mixed-Up' mangled PGP/MIME message" test_begin_subtest "search cleartext of 'Mixed-Up' mangled PGP/MIME message"
test_subtest_known_broken
output=$(notmuch search --output=messages body:password) output=$(notmuch search --output=messages body:password)
test_expect_equal "$output" id:mixed-up@mangling.notmuchmail.org test_expect_equal "$output" id:mixed-up@mangling.notmuchmail.org