1 /* Copyright (c) 2008-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "imap-match.h"
5 #include "mailbox-tree.h"
6 #include "mailbox-list-private.h"
7 #include "index-storage.h"
8 #include "shared-storage.h"
9 
10 extern struct mailbox_list shared_mailbox_list;
11 
shared_list_alloc(void)12 static struct mailbox_list *shared_list_alloc(void)
13 {
14 	struct mailbox_list *list;
15 	pool_t pool;
16 
17 	pool = pool_alloconly_create("shared list", 2048);
18 	list = p_new(pool, struct mailbox_list, 1);
19 	*list = shared_mailbox_list;
20 	list->pool = pool;
21 	return list;
22 }
23 
shared_list_deinit(struct mailbox_list * list)24 static void shared_list_deinit(struct mailbox_list *list)
25 {
26 	pool_unref(&list->pool);
27 }
28 
shared_list_copy_error(struct mailbox_list * shared_list,struct mail_namespace * backend_ns)29 static void shared_list_copy_error(struct mailbox_list *shared_list,
30 				   struct mail_namespace *backend_ns)
31 {
32 	const char *str;
33 	enum mail_error error;
34 
35 	str = mailbox_list_get_last_error(backend_ns->list, &error);
36 	mailbox_list_set_error(shared_list, error, str);
37 }
38 
39 static int
shared_get_storage(struct mailbox_list ** list,const char * vname,struct mail_storage ** storage_r)40 shared_get_storage(struct mailbox_list **list, const char *vname,
41 		   struct mail_storage **storage_r)
42 {
43 	struct mail_namespace *ns = (*list)->ns;
44 	const char *name;
45 
46 	name = mailbox_list_get_storage_name(*list, vname);
47 	if (*name == '\0' && (ns->flags & NAMESPACE_FLAG_AUTOCREATED) == 0) {
48 		/* trying to access the shared/ prefix itself */
49 		*storage_r = ns->storage;
50 		return 0;
51 	}
52 
53 	if (shared_storage_get_namespace(&ns, &name) < 0)
54 		return -1;
55 	*list = ns->list;
56 	return mailbox_list_get_storage(list, vname, storage_r);
57 }
58 
shared_list_get_hierarchy_sep(struct mailbox_list * list ATTR_UNUSED)59 static char shared_list_get_hierarchy_sep(struct mailbox_list *list ATTR_UNUSED)
60 {
61 	return '/';
62 }
63 
64 static int
shared_list_get_path(struct mailbox_list * list,const char * name,enum mailbox_list_path_type type,const char ** path_r)65 shared_list_get_path(struct mailbox_list *list, const char *name,
66 		     enum mailbox_list_path_type type, const char **path_r)
67 {
68 	struct mail_namespace *ns = list->ns;
69 
70 	if (mail_namespace_get_default_storage(list->ns) == NULL ||
71 	    name == NULL ||
72 	    shared_storage_get_namespace(&ns, &name) < 0) {
73 		/* we don't have a directory we can use. */
74 		*path_r = NULL;
75 		return 0;
76 	}
77 	return mailbox_list_get_path(ns->list, name, type, path_r);
78 }
79 
80 static const char *
shared_list_get_temp_prefix(struct mailbox_list * list,bool global ATTR_UNUSED)81 shared_list_get_temp_prefix(struct mailbox_list *list, bool global ATTR_UNUSED)
82 {
83 	i_panic("shared mailbox list: Can't return a temp prefix for '%s'",
84 		list->ns->prefix);
85 }
86 
87 static const char *
shared_list_join_refpattern(struct mailbox_list * list,const char * ref,const char * pattern)88 shared_list_join_refpattern(struct mailbox_list *list,
89 			    const char *ref, const char *pattern)
90 {
91 	struct mail_namespace *ns = list->ns;
92 	const char *ns_ref, *prefix = list->ns->prefix;
93 	size_t prefix_len = strlen(prefix);
94 
95 	if (*ref != '\0' && str_begins(ref, prefix))
96 		ns_ref = ref + prefix_len;
97 	else
98 		ns_ref = NULL;
99 
100 	if (ns_ref != NULL && *ns_ref != '\0' &&
101 	    shared_storage_get_namespace(&ns, &ns_ref) == 0)
102 		return mailbox_list_join_refpattern(ns->list, ref, pattern);
103 
104 	/* fallback to default behavior */
105 	if (*ref != '\0')
106 		pattern = t_strconcat(ref, pattern, NULL);
107 	return pattern;
108 }
109 
110 static void
shared_list_create_missing_namespaces(struct mailbox_list * list,const char * const * patterns)111 shared_list_create_missing_namespaces(struct mailbox_list *list,
112 				      const char *const *patterns)
113 {
114 	struct mail_namespace *ns;
115 	char sep = mail_namespace_get_sep(list->ns);
116 	const char *list_pat, *name;
117 	unsigned int i;
118 
119 	for (i = 0; patterns[i] != NULL; i++) {
120 		const char *last = NULL, *p;
121 
122 		/* we'll require that the pattern begins with the list's
123 		   namespace prefix. we could also handle other patterns
124 		   (e.g. %/user/%), but it's more of a theoretical problem. */
125 		if (strncmp(list->ns->prefix, patterns[i],
126 			    list->ns->prefix_len) != 0)
127 			continue;
128 		list_pat = patterns[i] + list->ns->prefix_len;
129 
130 		for (p = list_pat; *p != '\0'; p++) {
131 			if (*p == '%' || *p == '*')
132 				break;
133 			if (*p == sep)
134 				last = p;
135 		}
136 		if (last != NULL) {
137 			ns = list->ns;
138 			name = t_strdup_until(list_pat, last);
139 			(void)shared_storage_get_namespace(&ns, &name);
140 		}
141 	}
142 }
143 
144 static struct mailbox_list_iterate_context *
shared_list_iter_init(struct mailbox_list * list,const char * const * patterns,enum mailbox_list_iter_flags flags)145 shared_list_iter_init(struct mailbox_list *list, const char *const *patterns,
146 		      enum mailbox_list_iter_flags flags)
147 {
148 	struct mailbox_list_iterate_context *ctx;
149 	pool_t pool;
150 	char sep = mail_namespace_get_sep(list->ns);
151 
152 	pool = pool_alloconly_create("mailbox list shared iter", 1024);
153 	ctx = p_new(pool, struct mailbox_list_iterate_context, 1);
154 	ctx->pool = pool;
155 	ctx->list = list;
156 	ctx->flags = flags;
157 	ctx->glob = imap_match_init_multiple(pool, patterns, FALSE, sep);
158 	array_create(&ctx->module_contexts, pool, sizeof(void *), 5);
159 
160 	if ((flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0 &&
161 	    (list->ns->flags & NAMESPACE_FLAG_AUTOCREATED) == 0) T_BEGIN {
162 		shared_list_create_missing_namespaces(list, patterns);
163 	} T_END;
164 	return ctx;
165 }
166 
167 static const struct mailbox_info *
shared_list_iter_next(struct mailbox_list_iterate_context * ctx ATTR_UNUSED)168 shared_list_iter_next(struct mailbox_list_iterate_context *ctx ATTR_UNUSED)
169 {
170 	return NULL;
171 }
172 
shared_list_iter_deinit(struct mailbox_list_iterate_context * ctx)173 static int shared_list_iter_deinit(struct mailbox_list_iterate_context *ctx)
174 {
175 	pool_unref(&ctx->pool);
176 	return 0;
177 }
178 
179 static int
shared_list_subscriptions_refresh(struct mailbox_list * src_list,struct mailbox_list * dest_list)180 shared_list_subscriptions_refresh(struct mailbox_list *src_list,
181 				  struct mailbox_list *dest_list)
182 {
183 	char sep;
184 
185 	if (dest_list->subscriptions == NULL) {
186 		sep = mail_namespace_get_sep(src_list->ns);
187 		dest_list->subscriptions = mailbox_tree_init(sep);
188 	}
189 	return 0;
190 }
191 
shared_list_set_subscribed(struct mailbox_list * list,const char * name,bool set)192 static int shared_list_set_subscribed(struct mailbox_list *list,
193 				      const char *name, bool set)
194 {
195 	struct mail_namespace *ns = list->ns;
196 	int ret;
197 
198 	if (shared_storage_get_namespace(&ns, &name) < 0)
199 		return -1;
200 	ret = mailbox_list_set_subscribed(ns->list, name, set);
201 	if (ret < 0)
202 		shared_list_copy_error(list, ns);
203 	return ret;
204 }
205 
206 static int
shared_list_delete_mailbox(struct mailbox_list * list,const char * name)207 shared_list_delete_mailbox(struct mailbox_list *list, const char *name)
208 {
209 	struct mail_namespace *ns = list->ns;
210 	int ret;
211 
212 	if (shared_storage_get_namespace(&ns, &name) < 0)
213 		return -1;
214 	ret = ns->list->v.delete_mailbox(ns->list, name);
215 	if (ret < 0)
216 		shared_list_copy_error(list, ns);
217 	return ret;
218 }
219 
220 static int
shared_list_delete_dir(struct mailbox_list * list,const char * name)221 shared_list_delete_dir(struct mailbox_list *list, const char *name)
222 {
223 	struct mail_namespace *ns = list->ns;
224 	int ret;
225 
226 	if (shared_storage_get_namespace(&ns, &name) < 0)
227 		return -1;
228 	ret = mailbox_list_delete_dir(ns->list, name);
229 	if (ret < 0)
230 		shared_list_copy_error(list, ns);
231 	return ret;
232 }
233 
234 static int
shared_list_delete_symlink(struct mailbox_list * list,const char * name)235 shared_list_delete_symlink(struct mailbox_list *list, const char *name)
236 {
237 	struct mail_namespace *ns = list->ns;
238 	int ret;
239 
240 	if (shared_storage_get_namespace(&ns, &name) < 0)
241 		return -1;
242 	ret = mailbox_list_delete_symlink(ns->list, name);
243 	if (ret < 0)
244 		shared_list_copy_error(list, ns);
245 	return ret;
246 }
247 
shared_list_rename_get_ns(struct mailbox_list * oldlist,const char ** oldname,struct mailbox_list * newlist,const char ** newname,struct mail_namespace ** ns_r)248 static int shared_list_rename_get_ns(struct mailbox_list *oldlist,
249 				     const char **oldname,
250 				     struct mailbox_list *newlist,
251 				     const char **newname,
252 				     struct mail_namespace **ns_r)
253 {
254 	struct mail_namespace *old_ns = oldlist->ns, *new_ns = newlist->ns;
255 
256 	if (shared_storage_get_namespace(&old_ns, oldname) < 0 ||
257 	    shared_storage_get_namespace(&new_ns, newname) < 0)
258 		return -1;
259 	if (old_ns != new_ns) {
260 		mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE,
261 			"Can't rename shared mailboxes across storages.");
262 		return -1;
263 	}
264 	*ns_r = old_ns;
265 	return 0;
266 }
267 
268 static int
shared_list_rename_mailbox(struct mailbox_list * oldlist,const char * oldname,struct mailbox_list * newlist,const char * newname)269 shared_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname,
270 			   struct mailbox_list *newlist, const char *newname)
271 {
272 	struct mail_namespace *ns;
273 	int ret;
274 
275 	if (shared_list_rename_get_ns(oldlist, &oldname,
276 				      newlist, &newname, &ns) < 0)
277 		return -1;
278 
279 	ret = ns->list->v.rename_mailbox(ns->list, oldname, ns->list, newname);
280 	if (ret < 0)
281 		shared_list_copy_error(oldlist, ns);
282 	return ret;
283 }
284 
285 struct mailbox_list shared_mailbox_list = {
286 	.name = "shared",
287 	.props = 0,
288 	.mailbox_name_max_length = MAILBOX_LIST_NAME_MAX_LENGTH,
289 
290 	.v = {
291 		.alloc = shared_list_alloc,
292 		.deinit = shared_list_deinit,
293 		.get_storage = shared_get_storage,
294 		.get_hierarchy_sep = shared_list_get_hierarchy_sep,
295 		.get_vname = mailbox_list_default_get_vname,
296 		.get_storage_name = mailbox_list_default_get_storage_name,
297 		.get_path = shared_list_get_path,
298 		.get_temp_prefix = shared_list_get_temp_prefix,
299 		.join_refpattern = shared_list_join_refpattern,
300 		.iter_init = shared_list_iter_init,
301 		.iter_next = shared_list_iter_next,
302 		.iter_deinit = shared_list_iter_deinit,
303 		.subscriptions_refresh = shared_list_subscriptions_refresh,
304 		.set_subscribed = shared_list_set_subscribed,
305 		.delete_mailbox = shared_list_delete_mailbox,
306 		.delete_dir = shared_list_delete_dir,
307 		.delete_symlink = shared_list_delete_symlink,
308 		.rename_mailbox = shared_list_rename_mailbox,
309 	}
310 };
311