1 /* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */
2
3 #include "lib.h"
4 #include "array.h"
5 #include "hook-build.h"
6 #include "llist.h"
7 #include "module-dir.h"
8 #include "mail-user.h"
9 #include "mail-namespace.h"
10 #include "mail-storage-private.h"
11 #include "mailbox-list-private.h"
12
13 struct mail_storage_module_hooks {
14 struct module *module;
15 const struct mail_storage_hooks *hooks;
16 bool forced;
17 };
18
19 static ARRAY(struct mail_storage_module_hooks) module_hooks = ARRAY_INIT;
20 static ARRAY(const struct mail_storage_hooks *) internal_hooks = ARRAY_INIT;
21
mail_storage_hooks_init(void)22 void mail_storage_hooks_init(void)
23 {
24 if (!array_is_created(&module_hooks))
25 i_array_init(&module_hooks, 32);
26 i_array_init(&internal_hooks, 8);
27 }
28
mail_storage_hooks_deinit(void)29 void mail_storage_hooks_deinit(void)
30 {
31 /* allow calling this even if mail_storage_hooks_init() hasn't been
32 called, because e.g. doveadm plugins could call
33 mail_storage_hooks_add() even though mail storage is never
34 initialized. */
35 if (array_is_created(&internal_hooks))
36 array_free(&internal_hooks);
37 if (array_is_created(&module_hooks))
38 array_free(&module_hooks);
39 }
40
mail_storage_hooks_add(struct module * module,const struct mail_storage_hooks * hooks)41 void mail_storage_hooks_add(struct module *module,
42 const struct mail_storage_hooks *hooks)
43 {
44 struct mail_storage_module_hooks new_hook;
45
46 i_zero(&new_hook);
47 new_hook.module = module;
48 new_hook.hooks = hooks;
49
50 /* allow adding hooks before mail_storage_hooks_init() */
51 if (!array_is_created(&module_hooks))
52 i_array_init(&module_hooks, 32);
53 array_push_back(&module_hooks, &new_hook);
54 }
55
mail_storage_hooks_add_forced(struct module * module,const struct mail_storage_hooks * hooks)56 void mail_storage_hooks_add_forced(struct module *module,
57 const struct mail_storage_hooks *hooks)
58 {
59 struct mail_storage_module_hooks *hook;
60
61 mail_storage_hooks_add(module, hooks);
62 hook = array_back_modifiable(&module_hooks);
63 hook->forced = TRUE;
64 }
65
mail_storage_hooks_remove(const struct mail_storage_hooks * hooks)66 void mail_storage_hooks_remove(const struct mail_storage_hooks *hooks)
67 {
68 const struct mail_storage_module_hooks *module_hook;
69 unsigned int idx = UINT_MAX;
70
71 array_foreach(&module_hooks, module_hook) {
72 if (module_hook->hooks == hooks) {
73 idx = array_foreach_idx(&module_hooks, module_hook);
74 break;
75 }
76 }
77 i_assert(idx != UINT_MAX);
78
79 array_delete(&module_hooks, idx, 1);
80 }
81
mail_storage_hooks_add_internal(const struct mail_storage_hooks * hooks)82 void mail_storage_hooks_add_internal(const struct mail_storage_hooks *hooks)
83 {
84 const struct mail_storage_hooks *existing_hooks;
85
86 /* make sure we don't add duplicate hooks */
87 array_foreach_elem(&internal_hooks, existing_hooks)
88 i_assert(existing_hooks != hooks);
89 array_push_back(&internal_hooks, &hooks);
90 }
91
mail_storage_hooks_remove_internal(const struct mail_storage_hooks * hooks)92 void mail_storage_hooks_remove_internal(const struct mail_storage_hooks *hooks)
93 {
94 const struct mail_storage_hooks *const *old_hooks;
95 unsigned int idx = UINT_MAX;
96
97 array_foreach(&internal_hooks, old_hooks) {
98 if (*old_hooks == hooks) {
99 idx = array_foreach_idx(&internal_hooks, old_hooks);
100 break;
101 }
102 }
103 i_assert(idx != UINT_MAX);
104
105 array_delete(&internal_hooks, idx, 1);
106 }
107
108 static int
mail_storage_module_hooks_cmp(const struct mail_storage_module_hooks * h1,const struct mail_storage_module_hooks * h2)109 mail_storage_module_hooks_cmp(const struct mail_storage_module_hooks *h1,
110 const struct mail_storage_module_hooks *h2)
111 {
112 const char *s1 = h1->module->path, *s2 = h2->module->path;
113 const char *p;
114
115 p = strrchr(s1, '/');
116 if (p != NULL) s1 = p+1;
117 p = strrchr(s2, '/');
118 if (p != NULL) s2 = p+1;
119
120 if (str_begins(s1, "lib"))
121 s1 += 3;
122 if (str_begins(s2, "lib"))
123 s2 += 3;
124
125 return strcmp(s1, s2);
126 }
127
mail_user_add_plugin_hooks(struct mail_user * user)128 static void mail_user_add_plugin_hooks(struct mail_user *user)
129 {
130 const struct mail_storage_module_hooks *module_hook;
131 ARRAY(struct mail_storage_module_hooks) tmp_hooks;
132 const char *const *plugins, *name;
133
134 /* first get all hooks wanted by the user */
135 t_array_init(&tmp_hooks, array_count(&module_hooks));
136 plugins = t_strsplit_spaces(user->set->mail_plugins, ", ");
137 array_foreach(&module_hooks, module_hook) {
138 if (!module_hook->forced) {
139 name = module_get_plugin_name(module_hook->module);
140 if (!str_array_find(plugins, name))
141 continue;
142 }
143 array_push_back(&tmp_hooks, module_hook);
144 }
145
146 /* next we have to sort them by the modules' priority (based on name) */
147 array_sort(&tmp_hooks, mail_storage_module_hooks_cmp);
148
149 /* now that we have them in order, save them to user's hooks */
150 p_array_init(&user->hooks, user->pool,
151 array_count(&tmp_hooks) + array_count(&internal_hooks));
152 array_foreach(&tmp_hooks, module_hook)
153 array_push_back(&user->hooks, &module_hook->hooks);
154 array_append_array(&user->hooks, &internal_hooks);
155 }
156
hook_mail_user_created(struct mail_user * user)157 void hook_mail_user_created(struct mail_user *user)
158 {
159 const struct mail_storage_hooks *hooks;
160 struct hook_build_context *ctx;
161
162 mail_user_add_plugin_hooks(user);
163
164 ctx = hook_build_init((void *)&user->v, sizeof(user->v));
165 user->vlast = &user->v;
166 array_foreach_elem(&user->hooks, hooks) {
167 if (hooks->mail_user_created != NULL) T_BEGIN {
168 hooks->mail_user_created(user);
169 hook_build_update(ctx, user->vlast);
170 } T_END;
171 }
172 user->vlast = NULL;
173 hook_build_deinit(&ctx);
174 }
175
hook_mail_namespace_storage_added(struct mail_namespace * ns)176 void hook_mail_namespace_storage_added(struct mail_namespace *ns)
177 {
178 const struct mail_storage_hooks *hooks;
179
180 array_foreach_elem(&ns->user->hooks, hooks) {
181 if (hooks->mail_namespace_storage_added != NULL) T_BEGIN {
182 hooks->mail_namespace_storage_added(ns);
183 } T_END;
184 }
185 }
186
hook_mail_namespaces_created(struct mail_namespace * namespaces)187 void hook_mail_namespaces_created(struct mail_namespace *namespaces)
188 {
189 const struct mail_storage_hooks *hooks;
190
191 array_foreach_elem(&namespaces->user->hooks, hooks) {
192 if (namespaces->user->error != NULL)
193 break;
194 if (hooks->mail_namespaces_created != NULL) T_BEGIN {
195 hooks->mail_namespaces_created(namespaces);
196 } T_END;
197 }
198 }
199
hook_mail_namespaces_added(struct mail_namespace * namespaces)200 void hook_mail_namespaces_added(struct mail_namespace *namespaces)
201 {
202 const struct mail_storage_hooks *hooks;
203
204 array_foreach_elem(&namespaces->user->hooks, hooks) {
205 if (namespaces->user->error != NULL)
206 break;
207 if (hooks->mail_namespaces_added != NULL) T_BEGIN {
208 hooks->mail_namespaces_added(namespaces);
209 } T_END;
210 }
211 }
212
hook_mail_storage_created(struct mail_storage * storage)213 void hook_mail_storage_created(struct mail_storage *storage)
214 {
215 const struct mail_storage_hooks *hooks;
216 struct hook_build_context *ctx;
217
218 ctx = hook_build_init((void *)&storage->v, sizeof(storage->v));
219 storage->vlast = &storage->v;
220 array_foreach_elem(&storage->user->hooks, hooks) {
221 if (hooks->mail_storage_created != NULL) T_BEGIN {
222 hooks->mail_storage_created(storage);
223 hook_build_update(ctx, storage->vlast);
224 } T_END;
225 }
226 storage->vlast = NULL;
227 hook_build_deinit(&ctx);
228 }
229
hook_mailbox_list_created(struct mailbox_list * list)230 void hook_mailbox_list_created(struct mailbox_list *list)
231 {
232 const struct mail_storage_hooks *hooks;
233 struct hook_build_context *ctx;
234
235 ctx = hook_build_init((void *)&list->v, sizeof(list->v));
236 list->vlast = &list->v;
237 array_foreach_elem(&list->ns->user->hooks, hooks) {
238 if (hooks->mailbox_list_created != NULL) T_BEGIN {
239 hooks->mailbox_list_created(list);
240 hook_build_update(ctx, list->vlast);
241 } T_END;
242 }
243 list->vlast = NULL;
244 hook_build_deinit(&ctx);
245 }
246
hook_mailbox_allocated(struct mailbox * box)247 void hook_mailbox_allocated(struct mailbox *box)
248 {
249 const struct mail_storage_hooks *hooks;
250 struct hook_build_context *ctx;
251
252 ctx = hook_build_init((void *)&box->v, sizeof(box->v));
253 box->vlast = &box->v;
254 array_foreach_elem(&box->storage->user->hooks, hooks) {
255 if (hooks->mailbox_allocated != NULL) T_BEGIN {
256 hooks->mailbox_allocated(box);
257 hook_build_update(ctx, box->vlast);
258 } T_END;
259 }
260 box->vlast = NULL;
261 hook_build_deinit(&ctx);
262 }
263
hook_mailbox_opened(struct mailbox * box)264 void hook_mailbox_opened(struct mailbox *box)
265 {
266 const struct mail_storage_hooks *hooks;
267
268 array_foreach_elem(&box->storage->user->hooks, hooks) {
269 if (hooks->mailbox_opened != NULL) T_BEGIN {
270 hooks->mailbox_opened(box);
271 } T_END;
272 }
273 }
274
hook_mail_allocated(struct mail * mail)275 void hook_mail_allocated(struct mail *mail)
276 {
277 const struct mail_storage_hooks *hooks;
278 struct mail_private *pmail = (struct mail_private *)mail;
279 struct hook_build_context *ctx;
280
281 ctx = hook_build_init((void *)&pmail->v, sizeof(pmail->v));
282 pmail->vlast = &pmail->v;
283 array_foreach_elem(&mail->box->storage->user->hooks, hooks) {
284 if (hooks->mail_allocated != NULL) T_BEGIN {
285 hooks->mail_allocated(mail);
286 hook_build_update(ctx, pmail->vlast);
287 } T_END;
288 }
289 pmail->vlast = NULL;
290 hook_build_deinit(&ctx);
291 }
292