From 39f339f79598296f7b31c5abde4d952e2c1262be Mon Sep 17 00:00:00 2001 From: Aki Tuomi Date: Sat, 11 Nov 2017 10:07:42 +0200 Subject: [PATCH] lib-storage: Set keyword based on attachment presence when saving If attachment is detected, use $HasAttachment, if not use $HasNoAttachment --- src/lib-storage/mail-storage-private.h | 6 +++ src/lib-storage/mail-storage.c | 16 +++++- src/lib-storage/mail-storage.h | 3 ++ src/lib-storage/mail.c | 70 ++++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 1 deletion(-) diff --git a/src/lib-storage/mail-storage-private.h b/src/lib-storage/mail-storage-private.h index 6a7e37c2fe..552163f0d5 100644 --- a/src/lib-storage/mail-storage-private.h +++ b/src/lib-storage/mail-storage-private.h @@ -774,6 +774,12 @@ bool mail_prefetch(struct mail *mail); void mail_set_aborted(struct mail *mail); void mail_set_expunged(struct mail *mail); void mail_set_seq_saving(struct mail *mail, uint32_t seq); +/* Returns true IF and only IF the mail has EITHER one of the + attachment keywords set. If it has both, or none, it will return FALSE. */ +bool mail_has_attachment_keywords(struct mail *mail); +/* Sets attachment keywords. */ +void mail_set_attachment_keywords(struct mail *mail); + void mailbox_set_deleted(struct mailbox *box); int mailbox_mark_index_deleted(struct mailbox *box, bool del); /* Easy wrapper for getting mailbox's MAILBOX_LIST_PATH_TYPE_MAILBOX. diff --git a/src/lib-storage/mail-storage.c b/src/lib-storage/mail-storage.c index 393c86b581..17bade8525 100644 --- a/src/lib-storage/mail-storage.c +++ b/src/lib-storage/mail-storage.c @@ -2232,7 +2232,8 @@ struct mail_save_context * mailbox_save_alloc(struct mailbox_transaction_context *t) { struct mail_save_context *ctx; - + const struct mail_storage_settings *mail_set = + mailbox_get_settings(t->box); T_BEGIN { ctx = t->box->v.save_alloc(t); } T_END; @@ -2249,6 +2250,12 @@ mailbox_save_alloc(struct mailbox_transaction_context *t) /* make sure the mail isn't used before mail_set_seq_saving() */ mailbox_save_dest_mail_close(ctx); } + + /* make sure parts get parsed early on */ + if (mail_set->parsed_mail_attachment_detection_add_flags_on_save) + mail_add_temp_wanted_fields(ctx->dest_mail, + MAIL_FETCH_MESSAGE_PARTS, NULL); + return ctx; } @@ -2442,6 +2449,8 @@ int mailbox_save_finish(struct mail_save_context **_ctx) { struct mail_save_context *ctx = *_ctx; struct mailbox_transaction_context *t = ctx->transaction; + const struct mail_storage_settings *mail_set = + mailbox_get_settings(t->box); /* we need to keep a copy of this because save_finish implementations will likely zero the data structure during cleanup */ struct mail_keywords *keywords = ctx->data.keywords; @@ -2471,6 +2480,11 @@ int mailbox_save_finish(struct mail_save_context **_ctx) mailbox_save_add_pvt_flags(t, pvt_flags); t->save_count++; } + + if (mail_set->parsed_mail_attachment_detection_add_flags_on_save && + !mail_has_attachment_keywords(ctx->dest_mail)) + mail_set_attachment_keywords(ctx->dest_mail); + if (keywords != NULL) mailbox_keywords_unref(&keywords); mailbox_save_context_reset(ctx, TRUE); diff --git a/src/lib-storage/mail-storage.h b/src/lib-storage/mail-storage.h index 08d1ea376d..46d4a6ecac 100644 --- a/src/lib-storage/mail-storage.h +++ b/src/lib-storage/mail-storage.h @@ -15,6 +15,9 @@ struct message_size; /* If some operation is taking long, call notify_ok every n seconds. */ #define MAIL_STORAGE_STAYALIVE_SECS 15 +#define MAIL_KEYWORD_HAS_ATTACHMENT "$HasAttachment" +#define MAIL_KEYWORD_HAS_NO_ATTACHMENT "$HasNoAttachment" + enum mail_storage_flags { /* Remember message headers' MD5 sum */ MAIL_STORAGE_FLAG_KEEP_HEADER_MD5 = 0x01, diff --git a/src/lib-storage/mail.c b/src/lib-storage/mail.c index 56970aaff0..ac9ca5a070 100644 --- a/src/lib-storage/mail.c +++ b/src/lib-storage/mail.c @@ -10,6 +10,7 @@ #include "hostpid.h" #include "mail-cache.h" #include "mail-storage-private.h" +#include "message-part-data.h" #include @@ -449,3 +450,72 @@ void mail_generate_guid_128_hash(const char *guid, guid_128_t guid_128_r) GUID_128_SIZE); } } + +static bool +mail_message_has_attachment(struct message_part *part, + const struct message_part_attachment_settings *set) +{ + for (; part != NULL; part = part->next) { + if (message_part_is_attachment(part, set) || + mail_message_has_attachment(part->children, set)) + return TRUE; + } + + return FALSE; +} + +bool mail_has_attachment_keywords(struct mail *mail) +{ + const char *const *kw = mail_get_keywords(mail); + return (str_array_icase_find(kw, MAIL_KEYWORD_HAS_ATTACHMENT) != + str_array_icase_find(kw, MAIL_KEYWORD_HAS_NO_ATTACHMENT)); +} + +void mail_set_attachment_keywords(struct mail *mail) +{ + const struct mail_storage_settings *mail_set = + mail_storage_get_settings(mailbox_get_storage(mail->box)); + + const char *const keyword_has_attachment[] = { + MAIL_KEYWORD_HAS_ATTACHMENT, + NULL, + }; + const char *const keyword_has_no_attachment[] = { + MAIL_KEYWORD_HAS_NO_ATTACHMENT, + NULL + }; + struct message_part_attachment_settings set = { + .content_type_filter = + mail_set->parsed_mail_attachment_content_type_filter, + .exclude_inlined = + mail_set->parsed_mail_attachment_exclude_inlined, + }; + struct mail_keywords *kw_has = NULL, *kw_has_not = NULL; + + /* walk all parts and see if there is an attachment */ + struct message_part *parts; + if (mail_get_parts(mail, &parts) < 0) { + mail_set_critical(mail, "Failed to add attachment keywords: " + "mail_get_parts() failed: %s", + mail_storage_get_last_internal_error(mail->box->storage, NULL)); + return; + } else if (mailbox_keywords_create(mail->box, keyword_has_attachment, &kw_has) < 0 || + mailbox_keywords_create(mail->box, keyword_has_no_attachment, &kw_has_not) < 0) { + if (mail_set->mail_debug) { + i_debug("Failed to add attachment keywords: mailbox_keyword_create(%s) failed: %s", + mailbox_get_vname(mail->box), + mail_storage_get_last_error(mail->box->storage, NULL)); + } + } else { + bool has_attachment = mail_message_has_attachment(parts, &set); + + /* make sure only one of the keywords gets set */ + mail_update_keywords(mail, MODIFY_REMOVE, has_attachment ? kw_has_not : kw_has); + mail_update_keywords(mail, MODIFY_ADD, has_attachment ? kw_has : kw_has_not); + } + + if (kw_has != NULL) + mailbox_keywords_unref(&kw_has); + if (kw_has_not != NULL) + mailbox_keywords_unref(&kw_has_not); +}