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