diff --git a/sql/version-027.sql b/sql/version-027.sql new file mode 100644 index 00000000..f09f24f0 --- /dev/null +++ b/sql/version-027.sql @@ -0,0 +1,7 @@ +-- +-- Rebuild corrupted message ids. +-- + +UPDATE MessageTable +SET message_id = '<' || message_id || '>' +WHERE (message_id NOT LIKE '<%') AND (message_id NOT LIKE ' <%'); diff --git a/src/engine/imap-db/imap-db-account.vala b/src/engine/imap-db/imap-db-account.vala index 602b4c2d..99244dc2 100644 --- a/src/engine/imap-db/imap-db-account.vala +++ b/src/engine/imap-db/imap-db-account.vala @@ -494,8 +494,8 @@ private class Geary.ImapDB.Account : BaseObject { yield db.exec_transaction_async(Db.TransactionType.RO, (cx) => { Db.Statement stmt = cx.prepare("SELECT id FROM MessageTable WHERE message_id = ? OR in_reply_to = ?"); - stmt.bind_string(0, message_id.value); - stmt.bind_string(1, message_id.value); + stmt.bind_string(0, message_id.to_rfc822_string()); + stmt.bind_string(1, message_id.to_rfc822_string()); Db.Result result = stmt.exec(cancellable); while (!result.finished) { diff --git a/src/engine/imap-db/imap-db-message-row.vala b/src/engine/imap-db/imap-db-message-row.vala index 29dbd0b6..43e6c57e 100644 --- a/src/engine/imap-db/imap-db-message-row.vala +++ b/src/engine/imap-db/imap-db-message-row.vala @@ -209,7 +209,7 @@ private class Geary.ImapDB.MessageRow { } if (email.fields.is_all_set(Geary.Email.Field.REFERENCES)) { - message_id = (email.message_id != null) ? email.message_id.value : null; + message_id = (email.message_id != null) ? email.message_id.to_rfc822_string() : null; in_reply_to = (email.in_reply_to != null) ? email.in_reply_to.to_rfc822_string() : null; references = (email.references != null) ? email.references.to_rfc822_string() : null; diff --git a/test/engine/imap-db/imap-db-database-test.vala b/test/engine/imap-db/imap-db-database-test.vala index 1f03d4a9..29d38768 100644 --- a/test/engine/imap-db/imap-db-database-test.vala +++ b/test/engine/imap-db/imap-db-database-test.vala @@ -107,7 +107,7 @@ class Geary.ImapDB.DatabaseTest : TestCase { ); db.open.end(async_result()); - assert_equal(db.get_schema_version(), 26, "Post-upgrade version"); + assert_equal(db.get_schema_version(), 27, "Post-upgrade version"); // Since schema v22 deletes the re-creates all attachments, // attachment 12 should no longer exist on the file system and diff --git a/test/engine/rfc822/rfc822-message-data-test.vala b/test/engine/rfc822/rfc822-message-data-test.vala index acc08cb1..8c1eb157 100644 --- a/test/engine/rfc822/rfc822-message-data-test.vala +++ b/test/engine/rfc822/rfc822-message-data-test.vala @@ -15,6 +15,7 @@ class Geary.RFC822.MessageDataTest : TestCase { add_test("header_from_rfc822", header_from_rfc822); add_test("header_names_from_rfc822", header_names_from_rfc822); add_test("PreviewText.with_header", preview_text_with_header); + add_test("MessageIDList.from_rfc822_string", message_id_list_from_rfc822_string); } public void preview_text_with_header() throws GLib.Error { @@ -108,6 +109,110 @@ class Geary.RFC822.MessageDataTest : TestCase { assert_equal(neg_half_hour_tz.to_rfc822_string(), NEG_HALF_HOUR_TZ); } + public void message_id_list_from_rfc822_string() throws GLib.Error { + + // Standard variants + + assert_collection( + new MessageIDList.from_rfc822_string("").get_all(), + "" + ) + .size(1) + .contains(new MessageID("id@example.com")); + + assert_collection( + new MessageIDList.from_rfc822_string("").get_all(), + "" + ) + .size(2) + .contains(new MessageID("id1@example.com")) + .contains(new MessageID("id2@example.com")); + + assert_collection( + new MessageIDList.from_rfc822_string(" ").get_all(), + " " + ) + .size(2) + .contains(new MessageID("id1@example.com")) + .contains(new MessageID("id2@example.com")); + + // Parens as delim are invalid but seen in the wild + + assert_collection( + new MessageIDList.from_rfc822_string("(id@example.com)").get_all(), + "(id@example.com)" + ) + .size(1) + .contains(new MessageID("id@example.com")); + + assert_collection( + new MessageIDList.from_rfc822_string("(id1@example.com)(id2@example.com>").get_all(), + "(id1@example.com)(id2@example.com>" + ) + .size(2) + .contains(new MessageID("id1@example.com")) + .contains(new MessageID("id2@example.com")); + + assert_collection( + new MessageIDList.from_rfc822_string("(id1@example.com) (id2@example.com)").get_all(), + "(id1@example.com) (id2@example.com)" + ) + .size(2) + .contains(new MessageID("id1@example.com")) + .contains(new MessageID("id2@example.com")); + + // No delimiters + + assert_collection( + new MessageIDList.from_rfc822_string("id@example.com").get_all(), + "id@example.com" + ) + .size(1) + .contains(new MessageID("id@example.com")); + + assert_collection( + new MessageIDList.from_rfc822_string("id1@example.com id2@example.com").get_all(), + "id1@example.com id2@example.com" + ) + .size(2) + .contains(new MessageID("id1@example.com")) + .contains(new MessageID("id2@example.com")); + + // Comma-separated is invalid but seen in the wild + + assert_collection( + new MessageIDList.from_rfc822_string(",").get_all(), + "," + ) + .size(2) + .contains(new MessageID("id1@example.com")) + .contains(new MessageID("id2@example.com")); + + assert_collection( + new MessageIDList.from_rfc822_string(", ").get_all(), + ", " + ) + .size(2) + .contains(new MessageID("id1@example.com")) + .contains(new MessageID("id2@example.com")); + + assert_collection( + new MessageIDList.from_rfc822_string("(id1@example.com),(id2@example.com)").get_all(), + "(id1@example.com),(id2@example.com)" + ) + .size(2) + .contains(new MessageID("id1@example.com")) + .contains(new MessageID("id2@example.com")); + + assert_collection( + new MessageIDList.from_rfc822_string("(id1@example.com), (id2@example.com)").get_all(), + "(id1@example.com), (id2@example.com)" + ) + .size(2) + .contains(new MessageID("id1@example.com")) + .contains(new MessageID("id2@example.com")); + } + private const string HEADER_FIXTURE = """From: Test Subject: test