├── 95-deleted_to_trash_plugin.conf ├── ebuilds ├── dovecot_deleted_to_trash-0.7.ebuild ├── dovecot_deleted_to_trash-9999.ebuild └── dovecot_deleted_to_trash-0.6.ebuild ├── README.md ├── src ├── deleted-to-trash-plugin.h └── deleted-to-trash-plugin.c ├── Makefile └── dovecot-deleted-to-trash.spec /95-deleted_to_trash_plugin.conf: -------------------------------------------------------------------------------- 1 | # https://github.com/lexbrugman/dovecot_deleted_to_trash 2 | 3 | protocol imap { 4 | mail_plugins = $mail_plugins deleted_to_trash 5 | } 6 | plugin { 7 | deleted_to_trash_folder = Trash 8 | } 9 | -------------------------------------------------------------------------------- /ebuilds/dovecot_deleted_to_trash-0.7.ebuild: -------------------------------------------------------------------------------- 1 | EAPI="7" 2 | 3 | DESCRIPTION="Deleted to trash IMAP plugin for Dovecot" 4 | HOMEPAGE="http://wiki.dovecot.org/Plugins/deleted-to-trash" 5 | RESTRICT="nomirror" 6 | if [[ ${PV} == "9999" ]] ; then 7 | EGIT_REPO_URI="https://github.com/lexbrugman/${PN}.git" 8 | inherit git-r3 autotools 9 | else 10 | SRC_URI="https://github.com/lexbrugman/${PN}/archive/v${PV}.tar.gz -> ${P}.tar.gz" 11 | fi 12 | 13 | LICENSE="" 14 | KEYWORDS="~amd64 ~x86" 15 | SLOT="0" 16 | 17 | IUSE="" 18 | DEPEND="=net-mail/dovecot-2*" 19 | 20 | src_unpack() { 21 | default 22 | [[ ${PV} == "9999" ]] && git-2_src_unpack 23 | } 24 | 25 | src_install() { 26 | emake DESTDIR="${D}" DOVECOT_IMAP_PLUGIN_PATH="/usr/$(get_libdir)/dovecot" install || die 27 | emake DESTDIR="${D}" configure || die 28 | } 29 | -------------------------------------------------------------------------------- /ebuilds/dovecot_deleted_to_trash-9999.ebuild: -------------------------------------------------------------------------------- 1 | EAPI="7" 2 | 3 | DESCRIPTION="Deleted to trash IMAP plugin for Dovecot" 4 | HOMEPAGE="http://wiki.dovecot.org/Plugins/deleted-to-trash" 5 | RESTRICT="nomirror" 6 | if [[ ${PV} == "9999" ]] ; then 7 | EGIT_REPO_URI="https://github.com/lexbrugman/${PN}.git" 8 | inherit git-r3 autotools 9 | else 10 | SRC_URI="https://github.com/lexbrugman/${PN}/archive/v${PV}.tar.gz -> ${P}.tar.gz" 11 | fi 12 | 13 | LICENSE="" 14 | KEYWORDS="~amd64 ~x86" 15 | SLOT="0" 16 | 17 | IUSE="" 18 | DEPEND="=net-mail/dovecot-2*" 19 | 20 | src_unpack() { 21 | default 22 | [[ ${PV} == "9999" ]] && git-2_src_unpack 23 | } 24 | 25 | src_install() { 26 | emake DESTDIR="${D}" DOVECOT_IMAP_PLUGIN_PATH="/usr/$(get_libdir)/dovecot" install || die 27 | emake DESTDIR="${D}" configure || die 28 | } 29 | -------------------------------------------------------------------------------- /ebuilds/dovecot_deleted_to_trash-0.6.ebuild: -------------------------------------------------------------------------------- 1 | EAPI="7" 2 | 3 | DESCRIPTION="Deleted to trash IMAP plugin for Dovecot" 4 | HOMEPAGE="http://wiki.dovecot.org/Plugins/deleted-to-trash" 5 | RESTRICT="nomirror" 6 | if [[ ${PV} == "9999" ]] ; then 7 | EGIT_REPO_URI="https://github.com/lexbrugman/${PN}.git" 8 | inherit git-r3 autotools 9 | else 10 | SRC_URI="https://github.com/lexbrugman/${PN}/archive/v${PV}.tar.gz -> ${P}.tar.gz" 11 | fi 12 | 13 | LICENSE="" 14 | KEYWORDS="~amd64 ~x86" 15 | SLOT="0" 16 | 17 | IUSE="" 18 | DEPEND="=net-mail/dovecot-2* 19 | !!>=net-mail/dovecot-2.3" 20 | 21 | src_unpack() { 22 | default 23 | [[ ${PV} == "9999" ]] && git-2_src_unpack 24 | } 25 | 26 | src_install() { 27 | emake DESTDIR="${D}" DOVECOT_IMAP_PLUGIN_PATH="/usr/$(get_libdir)/dovecot" install || die 28 | emake DESTDIR="${D}" configure || die 29 | } 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | dovecot_deleted_to_trash 2 | ======================== 3 | 4 | Purpose 5 | ======= 6 | 7 | The purpose of this deleted_to_trash-plugin is to fix behavior with IMAP clients (like outlook) that don't work well with dovecot, specifically not copying deleted email to your Trash folder automatically. So, what this plugin does is fixing this behavior by copying deleted items to the Trash folder. The plugin needs to tell the difference between "move" and "delete" actions on the client side, since both actions mark the original email as deleted. For a "move" case, we don't copy the email to the trash folder. 8 | 9 | Installation 10 | ============ 11 | 12 | Download the sourcecode. 13 | 14 | Edit the Makefile to match your environment: 15 | 16 | # Dovecot's header directory 17 | DOVECOT_INC_PATH = /usr/include/dovecot 18 | # Dovecot's IMAP plugin path 19 | DOVECOT_IMAP_PLUGIN_PATH = /usr/lib/dovecot/modules 20 | 21 | Run: 22 | 23 | make 24 | sudo make install 25 | 26 | Configuration 27 | ============= 28 | 29 | Edit the 95-deleted_to_trash_plugin.conf file and run: 30 | 31 | sudo make configure 32 | 33 | Restart Dovecot, and that's all. 34 | -------------------------------------------------------------------------------- /src/deleted-to-trash-plugin.h: -------------------------------------------------------------------------------- 1 | #ifndef __DELETED_TO_TRASH_PLUGIN_H 2 | #define __DELETED_TO_TRASH_PLUGIN_H 3 | 4 | #include "lib.h" 5 | #include "mail-storage-private.h" 6 | #include "mail-namespace.h" 7 | #include "mailbox-list-private.h" 8 | 9 | #define DEFAULT_TRASH_FOLDER "Trash" 10 | #define TRASH_LIST_INITSIZE 128 11 | 12 | #define DELETED_TO_TRASH_CONTEXT(obj) MODULE_CONTEXT(obj, deleted_to_trash_storage_module) 13 | #define DELETED_TO_TRASH_MAIL_CONTEXT(obj) MODULE_CONTEXT(obj, deleted_to_trash_mail_module) 14 | 15 | 16 | ARRAY_DEFINE_TYPE(mail_ids, unsigned int); 17 | 18 | struct last_copy_info 19 | { 20 | void *transaction_context; 21 | ARRAY_TYPE(mail_ids) mail_id; 22 | char *src_mailbox_name; 23 | }; 24 | 25 | /* defined by imap, pop3, lda */ 26 | #ifdef DOVECOT_ABI_VERSION 27 | const char *deleted_to_trash_plugin_version = DOVECOT_ABI_VERSION; 28 | #else 29 | const char *deleted_to_trash_plugin_version = DOVECOT_VERSION; 30 | #endif 31 | 32 | static MODULE_CONTEXT_DEFINE_INIT(deleted_to_trash_storage_module, &mail_storage_module_register); 33 | static MODULE_CONTEXT_DEFINE_INIT(deleted_to_trash_mail_module, &mail_module_register); 34 | 35 | static struct last_copy_info last_copy; 36 | 37 | const char *trashfolder_name = NULL; 38 | 39 | void deleted_to_trash_plugin_init(struct module *module); 40 | void deleted_to_trash_plugin_deinit(void); 41 | void set_trashfolder_name(struct mail_user *user); 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for deleted_to_trash 2 | 3 | #### configuration begin #### 4 | 5 | # Dovecot's header directory 6 | DOVECOT_INC_PATH = /usr/include/dovecot 7 | # Dovecot's IMAP plugin path 8 | DOVECOT_IMAP_PLUGIN_PATH ?= /usr/lib/dovecot/modules 9 | # Dovecot's config path 10 | DOVECOT_CONFIG_PATH = /etc/dovecot/conf.d 11 | 12 | ## usually no need to configure anything below this line ## 13 | 14 | # plugin source & target name # 15 | PLUGIN_NAME = lib_deleted_to_trash_plugin.so 16 | 17 | # config file 18 | PLUGIN_CONFIG_FILE = 95-deleted_to_trash_plugin.conf 19 | 20 | #### configuration end #### 21 | 22 | SRCDIR = src 23 | SOURCES := $(wildcard $(SRCDIR)/*.c) 24 | 25 | .PHONY: all build install configure clean 26 | 27 | all: build 28 | 29 | build: ${PLUGIN_NAME} 30 | 31 | ${PLUGIN_NAME}: ${SOURCES} 32 | $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) \ 33 | -fPIC -shared -Wall \ 34 | -I${DOVECOT_INC_PATH} \ 35 | -I${DOVECOT_INC_PATH}/src \ 36 | -I${DOVECOT_INC_PATH}/src/lib \ 37 | -I${DOVECOT_INC_PATH}/src/lib-storage \ 38 | -I${DOVECOT_INC_PATH}/src/lib-mail \ 39 | -I${DOVECOT_INC_PATH}/src/lib-imap \ 40 | -I${DOVECOT_INC_PATH}/src/lib-index \ 41 | -DHAVE_CONFIG_H \ 42 | $< -o $@ 43 | 44 | install: install_plugin 45 | 46 | install_plugin: ${PLUGIN_NAME} 47 | install -d ${DESTDIR}/${DOVECOT_IMAP_PLUGIN_PATH} 48 | install $< ${DESTDIR}/${DOVECOT_IMAP_PLUGIN_PATH} 49 | 50 | configure: 51 | install -d ${DESTDIR}/${DOVECOT_CONFIG_PATH} 52 | install -m 644 ${PLUGIN_CONFIG_FILE} ${DESTDIR}/${DOVECOT_CONFIG_PATH} 53 | 54 | clean: 55 | $(RM) ${PLUGIN_NAME} 56 | 57 | # EOF 58 | -------------------------------------------------------------------------------- /dovecot-deleted-to-trash.spec: -------------------------------------------------------------------------------- 1 | # do not build debuginfo 2 | #%global debug_package %{nil} 3 | 4 | Name: dovecot-deleted-to-trash 5 | Version: 0.7 6 | Release: 2%{?dist} 7 | Summary: Dovecot plugin: copy deleted item to Trash folder 8 | 9 | Group: Networking/Daemons 10 | Source: %{name}-%{version}.tar.gz 11 | 12 | License: GPL 13 | 14 | BuildRequires: dovecot-devel 15 | Requires: dovecot >= 2.0.15 16 | 17 | 18 | %description 19 | The purpose of this deleted_to_trash-plugin is that IMAP client,such as outlook doesn't work well with dovecot, it can not copy deleted email to Trash folder automatically. So, this plugin is to copy deleted item to Trash folder. Also, need to tell the difference between "move" and "delete" action on Outlook side, since both action deleted_to_trash marks the original email as deleted. for a "move" case, we don't copy to the trash folder. 20 | 21 | %prep 22 | %setup -q 23 | 24 | %build 25 | make 26 | 27 | %install 28 | make install DESTDIR=%{buildroot} DOVECOT_IMAP_PLUGIN_PATH=%{_libdir}/dovecot 29 | mv %{buildroot}/%{_libdir}/dovecot/lib_deleted_to_trash_plugin.so %{buildroot}/%{_libdir}/dovecot/lib89_deleted_to_trash_plugin.so 30 | 31 | %files 32 | %{_libdir}/dovecot/lib89_deleted_to_trash_plugin.so 33 | %doc 95-deleted_to_trash_plugin.conf 34 | 35 | %changelog 36 | * Mon Mar 30 2015 Davide Principi - 0.6-2 37 | - Load deleted_to_trash before antispam plugin. Bug #3095 [NethServer] 38 | 39 | * Tue Jan 27 2014 Edoardo Spadoni - 0.6 40 | - Update to version 0.6 41 | 42 | * Wed Aug 01 2012 Giacomo Sanchietti - 0.1-1 43 | - First release 44 | -------------------------------------------------------------------------------- /src/deleted-to-trash-plugin.c: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2008 steven.xu@lba.ca, 2010 Lex Brugman */ 2 | /* Adapted to dovecot 2.0.9 by Emerson Pinter 2011-01-28 19:24 -0200 */ 3 | /* Adapted to dovecot 2.1.9 by Emerson Pinter 2012-09-27 17:10 -0300 */ 4 | #include "deleted-to-trash-plugin.h" 5 | #include "array.h" 6 | #include "str.h" 7 | #include "str-sanitize.h" 8 | #include "mail-storage-hooks.h" 9 | 10 | #include 11 | 12 | struct mail_namespace *get_users_inbox_namespace(struct mail_user *user) 13 | { 14 | struct mail_namespace *ns = NULL; 15 | struct mail_namespace *curns = NULL; 16 | 17 | /* find the inbox namespace */ 18 | for (curns = user->namespaces; curns != NULL; curns = curns->next) 19 | { 20 | if (curns->flags & NAMESPACE_FLAG_INBOX_USER) 21 | { 22 | ns = curns; 23 | break; 24 | } 25 | } 26 | 27 | return ns; 28 | } 29 | 30 | static int search_deleted_id(struct mail *_mail) 31 | { 32 | unsigned int deleted_id = _mail->uid; 33 | int ret = 0; 34 | 35 | /** 36 | * only if the copy and delete box are the same, otherwise, we need to reset the last copy data 37 | * since copy always follows a delete in IMAP (when moving an email from one folder to another folder). 38 | */ 39 | if (last_copy.src_mailbox_name != NULL && strcmp(_mail->box->name, last_copy.src_mailbox_name) == 0) 40 | { 41 | unsigned int count = 0; 42 | unsigned int i = 0; 43 | unsigned int const *mail_ids = NULL; 44 | 45 | mail_ids = array_get(&last_copy.mail_id, &count); 46 | for (i = 0; i < count; i++) 47 | { 48 | if (mail_ids[i] == deleted_id) 49 | { 50 | ret = 1; 51 | break; 52 | } 53 | } 54 | } 55 | else 56 | { 57 | if (array_count(&last_copy.mail_id) > 0) 58 | { 59 | array_clear(&last_copy.mail_id); 60 | } 61 | if (last_copy.src_mailbox_name != NULL) 62 | { 63 | i_free_and_null(last_copy.src_mailbox_name); 64 | } 65 | } 66 | 67 | return ret; 68 | } 69 | 70 | static struct mailbox *mailbox_open_or_create(struct mailbox_list *list, const char *name, const char **error_r) 71 | { 72 | struct mailbox *box; 73 | enum mail_error error; 74 | 75 | box = mailbox_alloc(list, name, MAILBOX_FLAG_NO_INDEX_FILES); 76 | if (mailbox_open(box) == 0) 77 | { 78 | *error_r = NULL; 79 | return box; 80 | } 81 | 82 | *error_r = mail_storage_get_last_error(mailbox_get_storage(box), &error); 83 | if (error != MAIL_ERROR_NOTFOUND) 84 | { 85 | mailbox_free(&box); 86 | return NULL; 87 | } 88 | 89 | /* try creating and re-opening it. */ 90 | if (mailbox_create(box, NULL, FALSE) < 0 || mailbox_open(box) < 0) 91 | { 92 | *error_r = mail_storage_get_last_error(mailbox_get_storage(box), NULL); 93 | mailbox_free(&box); 94 | return NULL; 95 | } 96 | return box; 97 | } 98 | 99 | static int copy_deleted_mail_to_trash(struct mail *_mail) 100 | { 101 | int ret; 102 | struct mailbox *trash_box = NULL; 103 | struct mail_namespace *ns = NULL; 104 | const char *error; 105 | 106 | ns = get_users_inbox_namespace(_mail->box->list->ns->user); 107 | trash_box = mailbox_open_or_create(ns->list, trashfolder_name, &error); 108 | 109 | if (trash_box != NULL) 110 | { 111 | i_debug("opening %s succeeded", trashfolder_name); 112 | struct mailbox_transaction_context *dest_trans; 113 | struct mail_save_context *save_ctx; 114 | struct mail_keywords *keywords; 115 | const char *const *keywords_list; 116 | 117 | #if DOVECOT_VERSION_MAJOR == 2 && DOVECOT_VERSION_MINOR >= 3 118 | dest_trans = mailbox_transaction_begin(trash_box, MAILBOX_TRANSACTION_FLAG_EXTERNAL, __func__); 119 | #else 120 | dest_trans = mailbox_transaction_begin(trash_box, MAILBOX_TRANSACTION_FLAG_EXTERNAL); 121 | #endif 122 | 123 | keywords_list = mail_get_keywords(_mail); 124 | keywords = str_array_length(keywords_list) == 0 ? NULL : mailbox_keywords_create_valid(trash_box, keywords_list); 125 | /* dovecot won't set the delete flag at this moment */ 126 | save_ctx = mailbox_save_alloc(dest_trans); 127 | /* remove the deleted flag for the new copy */ 128 | enum mail_flags flags = mail_get_flags(_mail); 129 | flags &= ~MAIL_DELETED; 130 | /* copy the message to the Trash folder */ 131 | mailbox_save_set_flags(save_ctx, flags, keywords); 132 | ret = mailbox_copy(&save_ctx, _mail); 133 | if (keywords != NULL) mailbox_keywords_unref(&keywords); 134 | 135 | if (ret < 0) 136 | { 137 | mailbox_transaction_rollback(&dest_trans); 138 | i_debug("copying to %s failed", trashfolder_name); 139 | } 140 | else 141 | { 142 | ret = mailbox_transaction_commit(&dest_trans); 143 | i_debug("copying to %s succeeded", trashfolder_name); 144 | } 145 | 146 | mailbox_free(&trash_box); 147 | } 148 | else 149 | { 150 | i_debug("opening %s failed", trashfolder_name); 151 | ret = -1; 152 | } 153 | 154 | return ret; 155 | } 156 | 157 | static void deleted_to_trash_mail_update_flags(struct mail *_mail, enum modify_type modify_type, enum mail_flags flags) 158 | { 159 | struct mail_private *mail = (struct mail_private *) _mail; 160 | union mail_module_context *lmail = DELETED_TO_TRASH_MAIL_CONTEXT(mail); 161 | enum mail_flags old_flags, new_flags; 162 | 163 | old_flags = mail_get_flags(_mail); 164 | lmail->super.update_flags(_mail, modify_type, flags); 165 | 166 | new_flags = old_flags; 167 | switch (modify_type) 168 | { 169 | case MODIFY_ADD: 170 | new_flags |= flags; 171 | break; 172 | case MODIFY_REMOVE: 173 | new_flags &= ~flags; 174 | break; 175 | case MODIFY_REPLACE: 176 | new_flags = flags; 177 | break; 178 | } 179 | 180 | if (((old_flags ^ new_flags) & MAIL_DELETED) != 0) 181 | { 182 | struct mail_namespace *ns = NULL; 183 | ns = get_users_inbox_namespace(_mail->box->list->ns->user); 184 | 185 | /* marked as deleted and not deleted from the Trash folder */ 186 | if (new_flags & MAIL_DELETED && !(strcmp(_mail->box->name, trashfolder_name) == 0 && strcmp(_mail->box->list->ns->prefix, ns->prefix) == 0)) 187 | { 188 | if (search_deleted_id(_mail)) 189 | { 190 | i_debug("email uid=%d was already copied to %s", _mail->uid, trashfolder_name); 191 | } 192 | else 193 | { 194 | if (copy_deleted_mail_to_trash(_mail) < 0) 195 | { 196 | i_fatal("failed to copy %d to %s", _mail->uid, trashfolder_name); 197 | } 198 | } 199 | } 200 | } 201 | } 202 | 203 | void set_trashfolder_name(struct mail_user *user) 204 | { 205 | trashfolder_name = mail_user_plugin_getenv(user, "deleted_to_trash_folder"); 206 | if (trashfolder_name == NULL) trashfolder_name = DEFAULT_TRASH_FOLDER; 207 | } 208 | 209 | static int deleted_to_trash_copy(struct mail_save_context *save_ctx, struct mail *mail) 210 | { 211 | int ret = 0; 212 | 213 | /* one IMAP copy command can contain many mails ID's, so, this function will be called multiple times */ 214 | union mailbox_module_context *lbox = DELETED_TO_TRASH_CONTEXT(save_ctx->transaction->box); 215 | ret = lbox->super.copy(save_ctx, mail); 216 | if (ret >= 0) 217 | { 218 | /* if the copy transaction context changes, a new copy could have been started, so add additional conditions to check the folder name */ 219 | i_debug("from %s to %s, previous action from %s", mail->box->name, save_ctx->transaction->box->name, last_copy.src_mailbox_name); 220 | if (last_copy.transaction_context == save_ctx->transaction && last_copy.src_mailbox_name != NULL && strcmp(last_copy.src_mailbox_name, mail->box->name) == 0) 221 | { 222 | array_append(&last_copy.mail_id, &mail->uid, 1); 223 | i_debug("nr %i", array_count(&last_copy.mail_id)); 224 | } 225 | else 226 | { /* if copying from Trash to some other folder, we don't mark it */ 227 | last_copy.transaction_context = save_ctx->transaction; 228 | if (array_count(&last_copy.mail_id) > 0) 229 | { 230 | array_clear(&last_copy.mail_id); 231 | } 232 | 233 | if (strcmp(mail->box->name, trashfolder_name) != 0) 234 | { 235 | array_append(&last_copy.mail_id, &mail->uid, 1); 236 | last_copy.src_mailbox_name = i_strdup(mail->box->name); 237 | } 238 | else 239 | { 240 | i_debug("from %s!", trashfolder_name); 241 | } 242 | } 243 | } 244 | 245 | return ret; 246 | } 247 | 248 | static int deleted_to_trash_transaction_commit(struct mailbox_transaction_context *t, struct mail_transaction_commit_changes *changes_r) 249 | { 250 | union mailbox_module_context *lbox = DELETED_TO_TRASH_CONTEXT(t->box); 251 | if (last_copy.transaction_context == t) 252 | { 253 | last_copy.transaction_context = NULL; 254 | } 255 | int ret = lbox->super.transaction_commit(t, changes_r); 256 | 257 | return ret; 258 | } 259 | 260 | static void deleted_to_trash_transaction_rollback(struct mailbox_transaction_context *t) 261 | { 262 | union mailbox_module_context *lbox = DELETED_TO_TRASH_CONTEXT(t->box); 263 | if (last_copy.transaction_context == t) 264 | { 265 | last_copy.transaction_context = NULL; 266 | } 267 | lbox->super.transaction_rollback(t); 268 | } 269 | 270 | static void deleted_to_trash_mailbox_allocated(struct mailbox *box) 271 | { 272 | struct mailbox_vfuncs *v = box->vlast; 273 | union mailbox_module_context *lbox; 274 | set_trashfolder_name(box->list->ns->user); 275 | 276 | lbox = p_new(box->pool, union mailbox_module_context, 1); 277 | lbox->super = *v; 278 | box->vlast = &lbox->super; 279 | 280 | v->copy = deleted_to_trash_copy; 281 | v->transaction_commit = deleted_to_trash_transaction_commit; 282 | v->transaction_rollback = deleted_to_trash_transaction_rollback; 283 | MODULE_CONTEXT_SET_SELF(box, deleted_to_trash_storage_module, lbox); 284 | } 285 | 286 | static void deleted_to_trash_mail_allocated(struct mail *_mail) 287 | { 288 | struct mail_private *mail = (struct mail_private *) _mail; 289 | struct mail_vfuncs *v = mail->vlast; 290 | union mail_module_context *lmail; 291 | set_trashfolder_name(_mail->box->list->ns->user); 292 | 293 | lmail = p_new(mail->pool, union mail_module_context, 1); 294 | lmail->super = *v; 295 | mail->vlast = &lmail->super; 296 | 297 | v->update_flags = deleted_to_trash_mail_update_flags; 298 | MODULE_CONTEXT_SET_SELF(mail, deleted_to_trash_mail_module, lmail); 299 | } 300 | 301 | static struct mail_storage_hooks deleted_to_trash_mail_storage_hooks = { 302 | .mailbox_allocated = deleted_to_trash_mailbox_allocated, 303 | .mail_allocated = deleted_to_trash_mail_allocated 304 | }; 305 | 306 | void deleted_to_trash_plugin_init(struct module *module) 307 | { 308 | last_copy.transaction_context = NULL; 309 | i_array_init(&last_copy.mail_id, TRASH_LIST_INITSIZE); 310 | last_copy.src_mailbox_name = NULL; 311 | mail_storage_hooks_add(module, &deleted_to_trash_mail_storage_hooks); 312 | } 313 | 314 | void deleted_to_trash_plugin_deinit(void) 315 | { 316 | if (last_copy.src_mailbox_name != NULL) 317 | { 318 | i_free(last_copy.src_mailbox_name); 319 | } 320 | array_free(&last_copy.mail_id); 321 | mail_storage_hooks_remove(&deleted_to_trash_mail_storage_hooks); 322 | } 323 | --------------------------------------------------------------------------------