changeset 22782:db50f01b8373

lib-storage: Set keyword based on attachment presence when saving If attachment is detected, use $HasAttachment, if not use $HasNoAttachment
author Aki Tuomi <aki.tuomi@dovecot.fi>
date Sat, 11 Nov 2017 10:07:42 +0200
parents 4b60d9bdc1a7
children 751eb2d43bc7
files src/lib-storage/mail-storage-private.h src/lib-storage/mail-storage.c src/lib-storage/mail-storage.h src/lib-storage/mail.c
diffstat 4 files changed, 95 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-storage/mail-storage-private.h	Mon Nov 20 10:09:23 2017 +0200
+++ b/src/lib-storage/mail-storage-private.h	Sat Nov 11 10:07:42 2017 +0200
@@ -778,6 +778,12 @@
 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.
--- a/src/lib-storage/mail-storage.c	Mon Nov 20 10:09:23 2017 +0200
+++ b/src/lib-storage/mail-storage.c	Sat Nov 11 10:07:42 2017 +0200
@@ -2163,7 +2163,8 @@
 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;
@@ -2180,6 +2181,12 @@
 		/* 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;
 }
 
@@ -2387,6 +2394,8 @@
 {
 	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;
@@ -2416,6 +2425,11 @@
 			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);
--- a/src/lib-storage/mail-storage.h	Mon Nov 20 10:09:23 2017 +0200
+++ b/src/lib-storage/mail-storage.h	Sat Nov 11 10:07:42 2017 +0200
@@ -15,6 +15,9 @@
 /* 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,
--- a/src/lib-storage/mail.c	Mon Nov 20 10:09:23 2017 +0200
+++ b/src/lib-storage/mail.c	Sat Nov 11 10:07:42 2017 +0200
@@ -10,6 +10,7 @@
 #include "hostpid.h"
 #include "mail-cache.h"
 #include "mail-storage-private.h"
+#include "message-part-data.h"
 
 #include <time.h>
 
@@ -474,3 +475,73 @@
 			      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_storage_set_critical(mail->box->storage,
+			"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);
+}