1 /* Copyright (c) 2011-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "ioloop.h"
5 #include "str.h"
6 #include "imap-arg.h"
7 #include "imap-resp-code.h"
8 #include "mailbox-tree.h"
9 #include "imapc-connection.h"
10 #include "imapc-msgmap.h"
11 #include "imapc-mail.h"
12 #include "imapc-list.h"
13 #include "imapc-search.h"
14 #include "imapc-sync.h"
15 #include "imapc-settings.h"
16 #include "imapc-storage.h"
17 
18 #define DNS_CLIENT_SOCKET_NAME "dns-client"
19 
20 struct imapc_open_context {
21 	struct imapc_mailbox *mbox;
22 	int ret;
23 };
24 
25 struct imapc_resp_code_map {
26 	const char *code;
27 	enum mail_error error;
28 };
29 
30 extern struct mail_storage imapc_storage;
31 extern struct mailbox imapc_mailbox;
32 
33 static struct event_category event_category_imapc = {
34 	.name = "imapc",
35 	.parent = &event_category_storage,
36 };
37 
38 static struct imapc_resp_code_map imapc_resp_code_map[] = {
39 	{ IMAP_RESP_CODE_UNAVAILABLE, MAIL_ERROR_TEMP },
40 	{ IMAP_RESP_CODE_AUTHFAILED, MAIL_ERROR_PERM },
41 	{ IMAP_RESP_CODE_AUTHZFAILED, MAIL_ERROR_PERM },
42 	{ IMAP_RESP_CODE_EXPIRED, MAIL_ERROR_PERM },
43 	{ IMAP_RESP_CODE_PRIVACYREQUIRED, MAIL_ERROR_PERM },
44 	{ IMAP_RESP_CODE_CONTACTADMIN, MAIL_ERROR_PERM },
45 	{ IMAP_RESP_CODE_NOPERM, MAIL_ERROR_PERM },
46 	{ IMAP_RESP_CODE_INUSE, MAIL_ERROR_INUSE },
47 	{ IMAP_RESP_CODE_EXPUNGEISSUED, MAIL_ERROR_EXPUNGED },
48 	{ IMAP_RESP_CODE_CORRUPTION, MAIL_ERROR_TEMP },
49 	{ IMAP_RESP_CODE_SERVERBUG, MAIL_ERROR_TEMP },
50 	/* { IMAP_RESP_CODE_CLIENTBUG, 0 }, */
51 	{ IMAP_RESP_CODE_CANNOT, MAIL_ERROR_NOTPOSSIBLE },
52 	{ IMAP_RESP_CODE_LIMIT, MAIL_ERROR_LIMIT },
53 	{ IMAP_RESP_CODE_OVERQUOTA, MAIL_ERROR_NOQUOTA },
54 	{ IMAP_RESP_CODE_ALREADYEXISTS, MAIL_ERROR_EXISTS },
55 	{ IMAP_RESP_CODE_NONEXISTENT, MAIL_ERROR_NOTFOUND }
56 };
57 
58 static void imapc_untagged_status(const struct imapc_untagged_reply *reply,
59 				  struct imapc_storage_client *client);
60 static void imapc_untagged_namespace(const struct imapc_untagged_reply *reply,
61 				     struct imapc_storage_client *client);
62 static int imapc_mailbox_run_status(struct mailbox *box,
63 				    enum mailbox_status_items items,
64 				    struct mailbox_status *status_r);
65 
imapc_resp_text_code_parse(const char * str,enum mail_error * error_r)66 bool imapc_resp_text_code_parse(const char *str, enum mail_error *error_r)
67 {
68 	unsigned int i;
69 
70 	if (str == NULL)
71 		return FALSE;
72 
73 	for (i = 0; i < N_ELEMENTS(imapc_resp_code_map); i++) {
74 		if (strcmp(imapc_resp_code_map[i].code, str) == 0) {
75 			*error_r = imapc_resp_code_map[i].error;
76 			return TRUE;
77 		}
78 	}
79 	return FALSE;
80 }
81 
imapc_mail_error_to_resp_text_code(enum mail_error error,const char ** str_r)82 bool imapc_mail_error_to_resp_text_code(enum mail_error error, const char **str_r)
83 {
84 	unsigned int i;
85 
86 	for (i = 0; i < N_ELEMENTS(imapc_resp_code_map); i++) {
87 		if (imapc_resp_code_map[i].error == error) {
88 			*str_r = imapc_resp_code_map[i].code;
89 			return TRUE;
90 		}
91 	}
92 	return FALSE;
93 }
94 
imapc_mailbox_has_modseqs(struct imapc_mailbox * mbox)95 bool imapc_mailbox_has_modseqs(struct imapc_mailbox *mbox)
96 {
97 	return (mbox->capabilities & (IMAPC_CAPABILITY_CONDSTORE |
98 				      IMAPC_CAPABILITY_QRESYNC)) != 0 &&
99 		IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_MODSEQ);
100 }
101 
imapc_storage_alloc(void)102 static struct mail_storage *imapc_storage_alloc(void)
103 {
104 	struct imapc_storage *storage;
105 	pool_t pool;
106 
107 	pool = pool_alloconly_create("imapc storage", 2048);
108 	storage = p_new(pool, struct imapc_storage, 1);
109 	storage->storage = imapc_storage;
110 	storage->storage.pool = pool;
111 	storage->root_ioloop = current_ioloop;
112 	return &storage->storage;
113 }
114 
imapc_copy_error_from_reply(struct imapc_storage * storage,enum mail_error default_error,const struct imapc_command_reply * reply)115 void imapc_copy_error_from_reply(struct imapc_storage *storage,
116 				 enum mail_error default_error,
117 				 const struct imapc_command_reply *reply)
118 {
119 	enum mail_error error;
120 
121 	if (imapc_resp_text_code_parse(reply->resp_text_key, &error)) {
122 		mail_storage_set_error(&storage->storage, error,
123 				       reply->text_without_resp);
124 	} else {
125 		mail_storage_set_error(&storage->storage, default_error,
126 				       reply->text_without_resp);
127 	}
128 }
129 
imapc_simple_context_init(struct imapc_simple_context * sctx,struct imapc_storage_client * client)130 void imapc_simple_context_init(struct imapc_simple_context *sctx,
131 			       struct imapc_storage_client *client)
132 {
133 	i_zero(sctx);
134 	sctx->client = client;
135 	sctx->ret = -2;
136 }
137 
imapc_simple_run(struct imapc_simple_context * sctx,struct imapc_command ** cmd)138 void imapc_simple_run(struct imapc_simple_context *sctx,
139 		      struct imapc_command **cmd)
140 {
141 	if (imapc_storage_client_handle_auth_failure(sctx->client)) {
142 		imapc_command_abort(cmd);
143 		imapc_client_logout(sctx->client->client);
144 		sctx->ret = -1;
145 	}
146 	*cmd = NULL;
147 	while (sctx->ret == -2)
148 		imapc_client_run(sctx->client->client);
149 }
150 
imapc_mailbox_run(struct imapc_mailbox * mbox)151 void imapc_mailbox_run(struct imapc_mailbox *mbox)
152 {
153 	imapc_mail_fetch_flush(mbox);
154 	imapc_mailbox_run_nofetch(mbox);
155 }
156 
imapc_mailbox_run_nofetch(struct imapc_mailbox * mbox)157 void imapc_mailbox_run_nofetch(struct imapc_mailbox *mbox)
158 {
159 	do {
160 		imapc_client_run(mbox->storage->client->client);
161 	} while (mbox->storage->reopen_count > 0 ||
162 		 mbox->state_fetching_uid1);
163 }
164 
imapc_simple_callback(const struct imapc_command_reply * reply,void * context)165 void imapc_simple_callback(const struct imapc_command_reply *reply,
166 			   void *context)
167 {
168 	struct imapc_simple_context *ctx = context;
169 
170 	if (reply->state == IMAPC_COMMAND_STATE_OK)
171 		ctx->ret = 0;
172 	else if (reply->state == IMAPC_COMMAND_STATE_NO) {
173 		imapc_copy_error_from_reply(ctx->client->_storage,
174 					    MAIL_ERROR_PARAMS, reply);
175 		ctx->ret = -1;
176 	} else if (imapc_storage_client_handle_auth_failure(ctx->client)) {
177 		ctx->ret = -1;
178 	} else if (reply->state == IMAPC_COMMAND_STATE_DISCONNECTED) {
179 		mail_storage_set_internal_error(&ctx->client->_storage->storage);
180 		ctx->ret = -1;
181 	} else {
182 		mail_storage_set_critical(&ctx->client->_storage->storage,
183 			"imapc: Command failed: %s", reply->text_full);
184 		ctx->ret = -1;
185 	}
186 	imapc_client_stop(ctx->client->client);
187 }
188 
imapc_mailbox_noop(struct imapc_mailbox * mbox)189 void imapc_mailbox_noop(struct imapc_mailbox *mbox)
190 {
191 	struct imapc_command *cmd;
192 	struct imapc_simple_context sctx;
193 
194 	if (mbox->client_box == NULL) {
195 		/* mailbox opening hasn't finished yet */
196 		return;
197 	}
198 
199 	imapc_simple_context_init(&sctx, mbox->storage->client);
200 	cmd = imapc_client_mailbox_cmd(mbox->client_box,
201 				       imapc_simple_callback, &sctx);
202 	imapc_command_send(cmd, "NOOP");
203 	imapc_simple_run(&sctx, &cmd);
204 }
205 
206 static void
imapc_storage_client_untagged_cb(const struct imapc_untagged_reply * reply,void * context)207 imapc_storage_client_untagged_cb(const struct imapc_untagged_reply *reply,
208 				 void *context)
209 {
210 	struct imapc_storage_client *client = context;
211 	struct imapc_mailbox *mbox = reply->untagged_box_context;
212 	const struct imapc_storage_event_callback *cb;
213 	const struct imapc_mailbox_event_callback *mcb;
214 
215 	array_foreach(&client->untagged_callbacks, cb) {
216 		if (strcasecmp(reply->name, cb->name) == 0)
217 			cb->callback(reply, client);
218 	}
219 
220 	if (mbox == NULL)
221 		return;
222 
223 	array_foreach(&mbox->untagged_callbacks, mcb) {
224 		if (strcasecmp(reply->name, mcb->name) == 0)
225 			mcb->callback(reply, mbox);
226 	}
227 
228 	if (reply->resp_text_key != NULL) {
229 		array_foreach(&mbox->resp_text_callbacks, mcb) {
230 			if (strcasecmp(reply->resp_text_key, mcb->name) == 0)
231 				mcb->callback(reply, mbox);
232 		}
233 	}
234 }
235 
236 static void
imapc_storage_client_login_callback(const struct imapc_command_reply * reply,void * context)237 imapc_storage_client_login_callback(const struct imapc_command_reply *reply,
238 				    void *context)
239 {
240 	struct imapc_storage_client *client = context;
241 
242 	client->auth_returned = TRUE;
243 	imapc_client_stop(client->client);
244 
245 	if (reply->state == IMAPC_COMMAND_STATE_OK)
246 		return;
247 	if (client->destroying &&
248 	    reply->state == IMAPC_COMMAND_STATE_DISCONNECTED) {
249 		/* user's work was finished before imapc login finished -
250 		   it's not an error */
251 		return;
252 	}
253 
254 	client->auth_failed_state = reply->state;
255 	client->auth_failed_reason = i_strdup(reply->text_full);
256 	if (!imapc_storage_client_handle_auth_failure(client))
257 		i_unreached();
258 }
259 
imapc_storage_client_handle_auth_failure(struct imapc_storage_client * client)260 bool imapc_storage_client_handle_auth_failure(struct imapc_storage_client *client)
261 {
262 	if (client->auth_failed_state == IMAPC_COMMAND_STATE_OK)
263 		return FALSE;
264 
265 	/* We need to set the error to either storage or to list, depending on
266 	   whether the caller is from mail-storage.h API or mailbox-list.h API.
267 	   We don't know here what the caller is though, so just set the error
268 	   to both of them. */
269 	if (client->_storage != NULL) {
270 		if (client->auth_failed_state == IMAPC_COMMAND_STATE_DISCONNECTED)
271 			mail_storage_set_internal_error(&client->_storage->storage);
272 		else {
273 			mail_storage_set_error(&client->_storage->storage,
274 				MAIL_ERROR_PERM, client->auth_failed_reason);
275 		}
276 	}
277 	if (client->_list != NULL) {
278 		if (client->auth_failed_state == IMAPC_COMMAND_STATE_DISCONNECTED)
279 			mailbox_list_set_internal_error(&client->_list->list);
280 		else {
281 			mailbox_list_set_error(&client->_list->list,
282 				MAIL_ERROR_PERM, client->auth_failed_reason);
283 		}
284 	}
285 	return TRUE;
286 }
287 
imapc_storage_client_login(struct imapc_storage_client * client,struct mail_user * user,const char * host)288 static void imapc_storage_client_login(struct imapc_storage_client *client,
289 				       struct mail_user *user, const char *host)
290 {
291 	imapc_client_login(client->client);
292 	if (!user->namespaces_created) {
293 		/* we're still initializing the user. wait for the
294 		   login to finish, so we can fail the user creation
295 		   if it fails. */
296 		while (!client->auth_returned)
297 			imapc_client_run(client->client);
298 		if (imapc_storage_client_handle_auth_failure(client)) {
299 			user->error = p_strdup_printf(user->pool,
300 				"imapc: Login to %s failed: %s",
301 				host, client->auth_failed_reason);
302 		}
303 	}
304 }
305 
imapc_storage_client_create(struct mail_namespace * ns,const struct imapc_settings * imapc_set,const struct mail_storage_settings * mail_set,struct imapc_storage_client ** client_r,const char ** error_r)306 int imapc_storage_client_create(struct mail_namespace *ns,
307 				const struct imapc_settings *imapc_set,
308 				const struct mail_storage_settings *mail_set,
309 				struct imapc_storage_client **client_r,
310 				const char **error_r)
311 {
312 	struct imapc_storage_client *client;
313 	struct imapc_client_settings set;
314 	string_t *str;
315 
316 	i_zero(&set);
317 	set.host = imapc_set->imapc_host;
318 	if (*set.host == '\0') {
319 		*error_r = "missing imapc_host";
320 		return -1;
321 	}
322 	set.port = imapc_set->imapc_port;
323 	if (imapc_set->imapc_user[0] != '\0')
324 		set.username = imapc_set->imapc_user;
325 	else if (ns->owner != NULL)
326 		set.username = ns->owner->username;
327 	else
328 		set.username = ns->user->username;
329 	set.master_user = imapc_set->imapc_master_user;
330 	set.password = imapc_set->imapc_password;
331 	if (*set.password == '\0') {
332 		*error_r = "missing imapc_password";
333 		return -1;
334 	}
335 	set.sasl_mechanisms = imapc_set->imapc_sasl_mechanisms;
336 	set.use_proxyauth = (imapc_set->parsed_features & IMAPC_FEATURE_PROXYAUTH) != 0;
337 	set.cmd_timeout_msecs = imapc_set->imapc_cmd_timeout * 1000;
338 	set.connect_retry_count = imapc_set->imapc_connection_retry_count;
339 	set.connect_retry_interval_msecs = imapc_set->imapc_connection_retry_interval;
340 	set.max_idle_time = imapc_set->imapc_max_idle_time;
341 	set.max_line_length = imapc_set->imapc_max_line_length;
342 	set.dns_client_socket_path = *ns->user->set->base_dir == '\0' ? "" :
343 		t_strconcat(ns->user->set->base_dir, "/",
344 			    DNS_CLIENT_SOCKET_NAME, NULL);
345 	set.debug = mail_set->mail_debug;
346 	set.rawlog_dir = mail_user_home_expand(ns->user,
347 					       imapc_set->imapc_rawlog_dir);
348 	if ((imapc_set->parsed_features & IMAPC_FEATURE_SEND_ID) != 0)
349 		set.session_id_prefix = ns->user->session_id;
350 
351 	str = t_str_new(128);
352 	mail_user_set_get_temp_prefix(str, ns->user->set);
353 	set.temp_path_prefix = str_c(str);
354 
355 	mail_user_init_ssl_client_settings(ns->user, &set.ssl_set);
356 	if (!imapc_set->imapc_ssl_verify)
357 		set.ssl_set.allow_invalid_cert = TRUE;
358 
359 	if (strcmp(imapc_set->imapc_ssl, "imaps") == 0)
360 		set.ssl_mode = IMAPC_CLIENT_SSL_MODE_IMMEDIATE;
361 	else if (strcmp(imapc_set->imapc_ssl, "starttls") == 0)
362 		set.ssl_mode = IMAPC_CLIENT_SSL_MODE_STARTTLS;
363 	else
364 		set.ssl_mode = IMAPC_CLIENT_SSL_MODE_NONE;
365 
366 	set.throttle_set.init_msecs = imapc_set->throttle_init_msecs;
367 	set.throttle_set.max_msecs = imapc_set->throttle_max_msecs;
368 	set.throttle_set.shrink_min_msecs = imapc_set->throttle_shrink_min_msecs;
369 
370 	client = i_new(struct imapc_storage_client, 1);
371 	client->refcount = 1;
372 	i_array_init(&client->untagged_callbacks, 16);
373 	/* FIXME: storage->event would be better, but we first get here when
374 	   creating mailbox_list, and storage doesn't even exist yet. */
375 	client->client = imapc_client_init(&set, ns->user->event);
376 	imapc_client_register_untagged(client->client,
377 				       imapc_storage_client_untagged_cb, client);
378 
379 	imapc_client_set_login_callback(client->client, imapc_storage_client_login_callback, client);
380 
381 	if ((ns->flags & NAMESPACE_FLAG_LIST_PREFIX) != 0 &&
382 	    (imapc_set->parsed_features & IMAPC_FEATURE_DELAY_LOGIN) == 0) {
383 		/* start logging in immediately */
384 		imapc_storage_client_login(client, ns->user, set.host);
385 	}
386 
387 	*client_r = client;
388 	return 0;
389 }
390 
imapc_storage_client_unref(struct imapc_storage_client ** _client)391 void imapc_storage_client_unref(struct imapc_storage_client **_client)
392 {
393 	struct imapc_storage_client *client = *_client;
394 	struct imapc_storage_event_callback *cb;
395 
396 	*_client = NULL;
397 
398 	i_assert(client->refcount > 0);
399 	if (--client->refcount > 0)
400 		return;
401 	imapc_client_deinit(&client->client);
402 	array_foreach_modifiable(&client->untagged_callbacks, cb)
403 		i_free(cb->name);
404 	array_free(&client->untagged_callbacks);
405 	i_free(client->auth_failed_reason);
406 	i_free(client);
407 }
408 
409 static int
imapc_storage_create(struct mail_storage * _storage,struct mail_namespace * ns,const char ** error_r)410 imapc_storage_create(struct mail_storage *_storage,
411 		     struct mail_namespace *ns,
412 		     const char **error_r)
413 {
414 	struct imapc_storage *storage = IMAPC_STORAGE(_storage);
415 	struct imapc_mailbox_list *imapc_list = NULL;
416 
417 	storage->set = mail_namespace_get_driver_settings(ns, _storage);
418 
419 	/* serialize all the settings */
420 	_storage->unique_root_dir = p_strdup_printf(_storage->pool,
421 						    "%s%s://(%s|%s):%s@%s:%u/%s mechs:%s features:%s "
422 						    "rawlog:%s cmd_timeout:%u maxidle:%u maxline:%zuu "
423 						    "pop3delflg:%s root_dir:%s",
424 						    storage->set->imapc_ssl,
425 						    storage->set->imapc_ssl_verify ? "(verify)" : "",
426 						    storage->set->imapc_user,
427 						    storage->set->imapc_master_user,
428 						    storage->set->imapc_password,
429 						    storage->set->imapc_host,
430 						    storage->set->imapc_port,
431 						    storage->set->imapc_list_prefix,
432 						    storage->set->imapc_sasl_mechanisms,
433 						    storage->set->imapc_features,
434 						    storage->set->imapc_rawlog_dir,
435 						    storage->set->imapc_cmd_timeout,
436 						    storage->set->imapc_max_idle_time,
437 						    (size_t) storage->set->imapc_max_line_length,
438 						    storage->set->pop3_deleted_flag,
439 						    ns->list->set.root_dir);
440 
441 	if (strcmp(ns->list->name, MAILBOX_LIST_NAME_IMAPC) == 0) {
442 		imapc_list = (struct imapc_mailbox_list *)ns->list;
443 		storage->client = imapc_list->client;
444 		storage->client->refcount++;
445 	} else {
446 		if (imapc_storage_client_create(ns, storage->set, _storage->set,
447 						&storage->client, error_r) < 0)
448 			return -1;
449 	}
450 	storage->client->_storage = storage;
451 	p_array_init(&storage->remote_namespaces, _storage->pool, 4);
452 	if (IMAPC_HAS_FEATURE(storage, IMAPC_FEATURE_FETCH_BODYSTRUCTURE)) {
453 		_storage->nonbody_access_fields |=
454 			MAIL_FETCH_IMAP_BODY | MAIL_FETCH_IMAP_BODYSTRUCTURE;
455 	}
456 
457 	imapc_storage_client_register_untagged(storage->client, "STATUS",
458 					       imapc_untagged_status);
459 	imapc_storage_client_register_untagged(storage->client, "NAMESPACE",
460 					       imapc_untagged_namespace);
461 
462 	return 0;
463 }
464 
imapc_storage_destroy(struct mail_storage * _storage)465 static void imapc_storage_destroy(struct mail_storage *_storage)
466 {
467 	struct imapc_storage *storage = IMAPC_STORAGE(_storage);
468 
469 	storage->client->destroying = TRUE;
470 
471 	/* make sure all pending commands are aborted before anything is
472 	   deinitialized */
473 	imapc_client_logout(storage->client->client);
474 
475 	imapc_storage_client_unref(&storage->client);
476 	index_storage_destroy(_storage);
477 }
478 
imapc_storage_client_register_untagged(struct imapc_storage_client * client,const char * name,imapc_storage_callback_t * callback)479 void imapc_storage_client_register_untagged(struct imapc_storage_client *client,
480 					    const char *name,
481 					    imapc_storage_callback_t *callback)
482 {
483 	struct imapc_storage_event_callback *cb;
484 
485 	cb = array_append_space(&client->untagged_callbacks);
486 	cb->name = i_strdup(name);
487 	cb->callback = callback;
488 }
489 
imapc_storage_client_unregister_untagged(struct imapc_storage_client * client,const char * name)490 void imapc_storage_client_unregister_untagged(struct imapc_storage_client *client,
491 					      const char *name)
492 {
493 	struct imapc_storage_event_callback *cb;
494 	unsigned int idx;
495 	array_foreach_modifiable(&client->untagged_callbacks, cb) {
496 		if (strcmp(cb->name, name) == 0) {
497 			 idx = array_foreach_idx(&client->untagged_callbacks, cb);
498 			 i_free(cb->name);
499 			 array_delete(&client->untagged_callbacks, idx, 1);
500 			 return;
501 		}
502 	}
503 	i_unreached();
504 }
505 
506 static void
imapc_storage_get_list_settings(const struct mail_namespace * ns ATTR_UNUSED,struct mailbox_list_settings * set)507 imapc_storage_get_list_settings(const struct mail_namespace *ns ATTR_UNUSED,
508 				struct mailbox_list_settings *set)
509 {
510 	if (set->layout == NULL)
511 		set->layout = MAILBOX_LIST_NAME_IMAPC;
512 	set->storage_name_escape_char = IMAPC_LIST_STORAGE_NAME_ESCAPE_CHAR;
513 	/* We want to have all imapc mailboxes accessible, so escape them if
514 	   necessary. */
515 	if (set->vname_escape_char == '\0')
516 		set->vname_escape_char = IMAPC_LIST_VNAME_ESCAPE_CHAR;
517 }
518 
519 static struct mailbox *
imapc_mailbox_alloc(struct mail_storage * storage,struct mailbox_list * list,const char * vname,enum mailbox_flags flags)520 imapc_mailbox_alloc(struct mail_storage *storage, struct mailbox_list *list,
521 		    const char *vname, enum mailbox_flags flags)
522 {
523 	struct imapc_mailbox *mbox;
524 	pool_t pool;
525 
526 	pool = pool_alloconly_create("imapc mailbox", 1024*4);
527 	mbox = p_new(pool, struct imapc_mailbox, 1);
528 	mbox->box = imapc_mailbox;
529 	mbox->box.pool = pool;
530 	mbox->box.storage = storage;
531 	mbox->box.list = list;
532 	mbox->box.mail_vfuncs = &imapc_mail_vfuncs;
533 
534 	index_storage_mailbox_alloc(&mbox->box, vname, flags, MAIL_INDEX_PREFIX);
535 
536 	mbox->storage = IMAPC_STORAGE(storage);
537 
538 	p_array_init(&mbox->untagged_callbacks, pool, 16);
539 	p_array_init(&mbox->resp_text_callbacks, pool, 16);
540 	p_array_init(&mbox->fetch_requests, pool, 16);
541 	p_array_init(&mbox->delayed_expunged_uids, pool, 16);
542 	mbox->pending_fetch_cmd = str_new(pool, 128);
543 	mbox->prev_mail_cache.fd = -1;
544 	imapc_mailbox_register_callbacks(mbox);
545 	return &mbox->box;
546 }
547 
imapc_mailbox_get_remote_name(struct imapc_mailbox * mbox)548 const char *imapc_mailbox_get_remote_name(struct imapc_mailbox *mbox)
549 {
550 	struct imapc_mailbox_list *list =
551 		container_of(mbox->box.list, struct imapc_mailbox_list, list);
552 
553 	if (strcmp(mbox->box.list->name, MAILBOX_LIST_NAME_IMAPC) != 0)
554 		return mbox->box.name;
555 	return imapc_list_storage_to_remote_name(list, mbox->box.name);
556 }
557 
558 static int
imapc_mailbox_exists(struct mailbox * box,bool auto_boxes,enum mailbox_existence * existence_r)559 imapc_mailbox_exists(struct mailbox *box, bool auto_boxes,
560 		     enum mailbox_existence *existence_r)
561 {
562 	if (auto_boxes && mailbox_is_autocreated(box)) {
563 		*existence_r = MAILBOX_EXISTENCE_SELECT;
564 		return 0;
565 	}
566 
567 	if (strcmp(box->list->name, MAILBOX_LIST_NAME_IMAPC) != 0) {
568 		if (box->inbox_any)
569 			*existence_r = MAILBOX_EXISTENCE_SELECT;
570 		else
571 			*existence_r = MAILBOX_EXISTENCE_NONE;
572 		return 0;
573 	}
574 
575 	enum mailbox_info_flags flags;
576 
577 	struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)box->list;
578 
579 	if (imapc_storage_client_handle_auth_failure(list->client)) {
580 		mail_storage_copy_list_error(box->storage, box->list);
581 		return -1;
582 	}
583 	if (imapc_list_get_mailbox_flags(box->list, box->name, &flags) < 0) {
584 		mail_storage_copy_list_error(box->storage, box->list);
585 		return -1;
586 	}
587 	if ((flags & MAILBOX_NONEXISTENT) != 0)
588 		*existence_r = MAILBOX_EXISTENCE_NONE;
589 	else if ((flags & MAILBOX_NOSELECT) != 0)
590 		*existence_r = MAILBOX_EXISTENCE_NOSELECT;
591 	else
592 		*existence_r = MAILBOX_EXISTENCE_SELECT;
593 	return 0;
594 }
595 
imapc_mailbox_want_examine(struct imapc_mailbox * mbox)596 static bool imapc_mailbox_want_examine(struct imapc_mailbox *mbox)
597 {
598 	if (IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_NO_EXAMINE)) {
599 		/* mainly a Courier-workaround: With POP3-only Maildir that
600 		   doesn't have UIDVALIDITY set, EXAMINE won't generate a
601 		   permanent UIDVALIDITY while SELECT will. */
602 		return FALSE;
603 	}
604 	return (mbox->box.flags & MAILBOX_FLAG_DROP_RECENT) == 0 &&
605 		((mbox->box.flags & MAILBOX_FLAG_READONLY) != 0 ||
606 		 (mbox->box.flags & MAILBOX_FLAG_SAVEONLY) != 0);
607 }
608 
609 static bool
imapc_mailbox_verify_select(struct imapc_mailbox * mbox,const char ** error_r)610 imapc_mailbox_verify_select(struct imapc_mailbox *mbox, const char **error_r)
611 {
612 	if (!mbox->exists_received)
613 		*error_r = "EXISTS not received";
614 	else if (mbox->sync_uid_validity == 0)
615 		*error_r = "UIDVALIDITY not received";
616 	else
617 		return TRUE;
618 	return FALSE;
619 }
620 
621 static void
imapc_mailbox_reopen_callback(const struct imapc_command_reply * reply,void * context)622 imapc_mailbox_reopen_callback(const struct imapc_command_reply *reply,
623 			      void *context)
624 {
625 	struct imapc_mailbox *mbox = context;
626 	const char *errmsg;
627 
628 	i_assert(mbox->storage->reopen_count > 0);
629 	mbox->storage->reopen_count--;
630 	mbox->selecting = FALSE;
631 	if (reply->state != IMAPC_COMMAND_STATE_OK)
632 		errmsg = reply->text_full;
633 	else if (imapc_mailbox_verify_select(mbox, &errmsg)) {
634 		imap_mailbox_select_finish(mbox);
635 		errmsg = NULL;
636 	}
637 
638 	if (errmsg != NULL) {
639 		imapc_client_mailbox_reconnect(mbox->client_box,
640 			t_strdup_printf("Reopening mailbox '%s' failed: %s",
641 					mbox->box.name, errmsg));
642 	}
643 
644 	imapc_client_stop(mbox->storage->client->client);
645 }
646 
imapc_mailbox_reopen(void * context)647 static void imapc_mailbox_reopen(void *context)
648 {
649 	struct imapc_mailbox *mbox = context;
650 	struct imapc_command *cmd;
651 
652 	/* we're reconnecting and need to reopen the mailbox */
653 	mbox->prev_skipped_rseq = 0;
654 	mbox->prev_skipped_uid = 0;
655 	imapc_msgmap_reset(imapc_client_mailbox_get_msgmap(mbox->client_box));
656 
657 	if (mbox->selecting) {
658 		/* We reconnected during the initial SELECT/EXAMINE. It'll be
659 		   automatically resent by lib-imap-client, so we don't need to
660 		   send it again here. */
661 		i_assert(!mbox->initial_sync_done);
662 		return;
663 	}
664 	if (!mbox->initial_sync_done) {
665 		/* Initial FETCH 1:* didn't fully succeed. We're reconnecting
666 		   and lib-imap-client is automatically resending it. But we
667 		   need to reset the sync_next_* state so that if any of the
668 		   mails are now expunged we won't get confused and crash. */
669 		mbox->sync_next_lseq = 1;
670 		mbox->sync_next_rseq = 1;
671 	}
672 
673 	mbox->state_fetched_success = FALSE;
674 	mbox->initial_sync_done = FALSE;
675 	mbox->selecting = TRUE;
676 	mbox->selected = FALSE;
677 	mbox->exists_received = FALSE;
678 
679 	cmd = imapc_client_mailbox_cmd(mbox->client_box,
680 				       imapc_mailbox_reopen_callback, mbox);
681 	imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_SELECT);
682 	if (imapc_mailbox_want_examine(mbox)) {
683 		imapc_command_sendf(cmd, "EXAMINE %s",
684 				    imapc_mailbox_get_remote_name(mbox));
685 	} else {
686 		imapc_command_sendf(cmd, "SELECT %s",
687 				    imapc_mailbox_get_remote_name(mbox));
688 	}
689 	mbox->storage->reopen_count++;
690 }
691 
692 static void
imapc_mailbox_open_callback(const struct imapc_command_reply * reply,void * context)693 imapc_mailbox_open_callback(const struct imapc_command_reply *reply,
694 			    void *context)
695 {
696 	struct imapc_open_context *ctx = context;
697 	const char *error;
698 
699 	ctx->mbox->selecting = FALSE;
700 	if (reply->state == IMAPC_COMMAND_STATE_OK) {
701 		if (!imapc_mailbox_verify_select(ctx->mbox, &error)) {
702 			mailbox_set_critical(&ctx->mbox->box,
703 				"imapc: Opening mailbox failed: %s", error);
704 			ctx->ret = -1;
705 		} else {
706 			imap_mailbox_select_finish(ctx->mbox);
707 			ctx->ret = 0;
708 		}
709 	} else if (reply->state == IMAPC_COMMAND_STATE_NO) {
710 		/* Unless the remote IMAP server supports sending
711 		   resp-text-code, we don't know if the NO reply is because
712 		   the mailbox doesn't exist or because of some internal error.
713 		   We'll default to assuming it doesn't exist, so e.g.
714 		   mailbox { auto=create } will auto-create missing mailboxes.
715 		   However, INBOX is a special mailbox, which is always
716 		   autocreated if it doesn't exist. This is true in both the
717 		   local Dovecot and the remote IMAP server. This means that
718 		   there's no point in trying to send CREATE INBOX to the
719 		   remote server. We'll avoid that by defaulting to temporary
720 		   failure with INBOX. */
721 		enum mail_error default_error =
722 			ctx->mbox->box.inbox_any ?
723 			MAIL_ERROR_TEMP : MAIL_ERROR_NOTFOUND;
724 		imapc_copy_error_from_reply(ctx->mbox->storage,
725 					    default_error, reply);
726 		ctx->ret = -1;
727 	} else if (imapc_storage_client_handle_auth_failure(ctx->mbox->storage->client)) {
728 		ctx->ret = -1;
729 	} else if (reply->state == IMAPC_COMMAND_STATE_DISCONNECTED) {
730 		ctx->ret = -1;
731 		mail_storage_set_internal_error(ctx->mbox->box.storage);
732 	} else {
733 		mailbox_set_critical(&ctx->mbox->box,
734 			"imapc: Opening mailbox failed: %s", reply->text_full);
735 		ctx->ret = -1;
736 	}
737 	imapc_client_stop(ctx->mbox->storage->client->client);
738 }
739 
imapc_mailbox_get_capabilities(struct imapc_mailbox * mbox)740 static int imapc_mailbox_get_capabilities(struct imapc_mailbox *mbox)
741 {
742 	/* If authentication failed, don't check again. */
743 	if (imapc_storage_client_handle_auth_failure(mbox->storage->client))
744 		return -1;
745 
746 	return imapc_client_get_capabilities(mbox->storage->client->client,
747 					     &mbox->capabilities);
748 
749 }
750 
imapc_mailbox_get_extensions(struct imapc_mailbox * mbox)751 static void imapc_mailbox_get_extensions(struct imapc_mailbox *mbox)
752 {
753 	if (mbox->guid_fetch_field_name == NULL) {
754 		/* see if we can get message GUIDs somehow */
755 		if ((mbox->capabilities & IMAPC_CAPABILITY_X_GM_EXT_1) != 0) {
756 			/* GMail */
757 			mbox->guid_fetch_field_name = "X-GM-MSGID";
758 		}
759 	}
760 }
761 
imapc_mailbox_select(struct imapc_mailbox * mbox)762 int imapc_mailbox_select(struct imapc_mailbox *mbox)
763 {
764 	struct imapc_command *cmd;
765 	struct imapc_open_context ctx;
766 
767 	i_assert(mbox->client_box == NULL);
768 
769 	if (imapc_mailbox_get_capabilities(mbox) < 0)
770 		return -1;
771 
772 	if (imapc_mailbox_has_modseqs(mbox)) {
773 		if (!array_is_created(&mbox->rseq_modseqs))
774 			i_array_init(&mbox->rseq_modseqs, 32);
775 		else
776 			array_clear(&mbox->rseq_modseqs);
777 	}
778 
779 	mbox->client_box =
780 		imapc_client_mailbox_open(mbox->storage->client->client, mbox);
781 	imapc_client_mailbox_set_reopen_cb(mbox->client_box,
782 					   imapc_mailbox_reopen, mbox);
783 
784 	imapc_mailbox_get_extensions(mbox);
785 
786 	mbox->selecting = TRUE;
787 	mbox->exists_received = FALSE;
788 	ctx.mbox = mbox;
789 	ctx.ret = -2;
790 	cmd = imapc_client_mailbox_cmd(mbox->client_box,
791 				       imapc_mailbox_open_callback, &ctx);
792 	imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_SELECT |
793 				IMAPC_COMMAND_FLAG_RETRIABLE);
794 	if (imapc_mailbox_want_examine(mbox)) {
795 		imapc_command_sendf(cmd, "EXAMINE %s",
796 			imapc_mailbox_get_remote_name(mbox));
797 	} else {
798 		imapc_command_sendf(cmd, "SELECT %s",
799 			imapc_mailbox_get_remote_name(mbox));
800 	}
801 
802 	while (ctx.ret == -2 || mbox->state_fetching_uid1)
803 		imapc_mailbox_run(mbox);
804 	if (!mbox->state_fetched_success)
805 		ctx.ret = -1;
806 	return ctx.ret;
807 }
808 
imapc_mailbox_open(struct mailbox * box)809 static int imapc_mailbox_open(struct mailbox *box)
810 {
811 	struct imapc_mailbox *mbox = IMAPC_MAILBOX(box);
812 
813 	if (index_storage_mailbox_open(box, FALSE) < 0)
814 		return -1;
815 
816 	if (box->deleting || (box->flags & MAILBOX_FLAG_SAVEONLY) != 0) {
817 		/* We don't actually want to SELECT the mailbox. */
818 		return 0;
819 	}
820 
821 	if (*box->name == '\0' &&
822 	    (box->list->ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0) {
823 		/* trying to open INBOX as the namespace prefix.
824 		   Don't allow this. */
825 		mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND,
826 				       "Mailbox isn't selectable");
827 		mailbox_close(box);
828 		return -1;
829 	}
830 
831 	if (imapc_mailbox_select(mbox) < 0) {
832 		mailbox_close(box);
833 		return -1;
834 	}
835 	return 0;
836 }
837 
imapc_mail_cache_free(struct imapc_mail_cache * cache)838 void imapc_mail_cache_free(struct imapc_mail_cache *cache)
839 {
840 	i_close_fd(&cache->fd);
841 	buffer_free(&cache->buf);
842 	cache->uid = 0;
843 }
844 
imapc_mailbox_close(struct mailbox * box)845 static void imapc_mailbox_close(struct mailbox *box)
846 {
847 	struct imapc_mailbox *mbox = IMAPC_MAILBOX(box);
848 	bool changes;
849 
850 	(void)imapc_mailbox_commit_delayed_trans(mbox, FALSE, &changes);
851 	imapc_mail_fetch_flush(mbox);
852 	if (mbox->client_box != NULL)
853 		imapc_client_mailbox_close(&mbox->client_box);
854 	if (array_is_created(&mbox->rseq_modseqs))
855 		array_free(&mbox->rseq_modseqs);
856 	if (mbox->sync_view != NULL)
857 		mail_index_view_close(&mbox->sync_view);
858 	timeout_remove(&mbox->to_idle_delay);
859 	timeout_remove(&mbox->to_idle_check);
860 	imapc_mail_cache_free(&mbox->prev_mail_cache);
861 	index_storage_mailbox_close(box);
862 }
863 
864 static int
imapc_mailbox_create(struct mailbox * box,const struct mailbox_update * update ATTR_UNUSED,bool directory)865 imapc_mailbox_create(struct mailbox *box,
866 		     const struct mailbox_update *update ATTR_UNUSED,
867 		     bool directory)
868 {
869 	struct imapc_mailbox *mbox = IMAPC_MAILBOX(box);
870 	struct imapc_command *cmd;
871 	struct imapc_simple_context sctx;
872 	const char *remote_name = imapc_mailbox_get_remote_name(mbox);
873 
874 	if (!directory)
875 		;
876 	else if (strcmp(box->list->name, MAILBOX_LIST_NAME_IMAPC) == 0) {
877 		struct imapc_mailbox_list *imapc_list =
878 			(struct imapc_mailbox_list *)box->list;
879 		remote_name = t_strdup_printf("%s%c", remote_name,
880 					      imapc_list->root_sep);
881 	} else {
882 		remote_name = t_strdup_printf("%s%c", remote_name,
883 			mailbox_list_get_hierarchy_sep(box->list));
884 	}
885 	imapc_simple_context_init(&sctx, mbox->storage->client);
886 	cmd = imapc_client_cmd(mbox->storage->client->client,
887 			       imapc_simple_callback, &sctx);
888 	imapc_command_sendf(cmd, "CREATE %s", remote_name);
889 	imapc_simple_run(&sctx, &cmd);
890 	return sctx.ret;
891 }
892 
imapc_mailbox_update(struct mailbox * box,const struct mailbox_update * update)893 static int imapc_mailbox_update(struct mailbox *box,
894 				const struct mailbox_update *update)
895 {
896 	if (!guid_128_is_empty(update->mailbox_guid) ||
897 	    update->uid_validity != 0 || update->min_next_uid != 0 ||
898 	    update->min_first_recent_uid != 0) {
899 		mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE,
900 				       "Not supported");
901 	}
902 	return index_storage_mailbox_update(box, update);
903 }
904 
imapc_untagged_status(const struct imapc_untagged_reply * reply,struct imapc_storage_client * client)905 static void imapc_untagged_status(const struct imapc_untagged_reply *reply,
906 				  struct imapc_storage_client *client)
907 {
908 	struct imapc_storage *storage = client->_storage;
909 	struct mailbox_status *status;
910 	const struct imap_arg *list;
911 	const char *remote_name, *key, *value;
912 	uint32_t num;
913 	unsigned int i;
914 
915 	if (!imap_arg_get_astring(&reply->args[0], &remote_name) ||
916 	    !imap_arg_get_list(&reply->args[1], &list))
917 		return;
918 
919 	if (storage->cur_status_box == NULL)
920 		return;
921 
922 	if (!imapc_mailbox_name_equals(storage->cur_status_box,
923 				       remote_name))
924 		return;
925 
926 	status = storage->cur_status;
927 	for (i = 0; list[i].type != IMAP_ARG_EOL; i += 2) {
928 		if (!imap_arg_get_atom(&list[i], &key) ||
929 		    !imap_arg_get_atom(&list[i+1], &value) ||
930 		    str_to_uint32(value, &num) < 0)
931 			return;
932 
933 		if (strcasecmp(key, "MESSAGES") == 0)
934 			status->messages = num;
935 		else if (strcasecmp(key, "RECENT") == 0)
936 			status->recent = num;
937 		else if (strcasecmp(key, "UIDNEXT") == 0)
938 			status->uidnext = num;
939 		else if (strcasecmp(key, "UIDVALIDITY") == 0)
940 			status->uidvalidity = num;
941 		else if (strcasecmp(key, "UNSEEN") == 0)
942 			status->unseen = num;
943 		else if (strcasecmp(key, "HIGHESTMODSEQ") == 0 &&
944 			 imapc_mailbox_has_modseqs(storage->cur_status_box))
945 			status->highest_modseq = num;
946 	}
947 }
948 
imapc_untagged_namespace(const struct imapc_untagged_reply * reply,struct imapc_storage_client * client)949 static void imapc_untagged_namespace(const struct imapc_untagged_reply *reply,
950 				     struct imapc_storage_client *client)
951 {
952 	struct imapc_storage *storage = client->_storage;
953 	static enum mail_namespace_type ns_types[] = {
954 		MAIL_NAMESPACE_TYPE_PRIVATE,
955 		MAIL_NAMESPACE_TYPE_SHARED,
956 		MAIL_NAMESPACE_TYPE_PUBLIC
957 	};
958 	struct imapc_namespace *ns;
959 	const struct imap_arg *list, *list2;
960 	const char *prefix, *sep;
961 	unsigned int i;
962 
963 	array_clear(&storage->remote_namespaces);
964 	for (i = 0; i < N_ELEMENTS(ns_types); i++) {
965 		if (reply->args[i].type == IMAP_ARG_NIL)
966 			continue;
967 		if (!imap_arg_get_list(&reply->args[i], &list))
968 			break;
969 
970 		for (; list->type != IMAP_ARG_EOL; list++) {
971 			if (!imap_arg_get_list(list, &list2) ||
972 			    !imap_arg_get_astring(&list2[0], &prefix) ||
973 			    !imap_arg_get_nstring(&list2[1], &sep))
974 				break;
975 
976 			ns = array_append_space(&storage->remote_namespaces);
977 			ns->prefix = p_strdup(storage->storage.pool, prefix);
978 			ns->separator = sep == NULL ? '\0' : sep[0];
979 			ns->type = ns_types[i];
980 		}
981 	}
982 }
983 
imapc_mailbox_get_selected_status(struct imapc_mailbox * mbox,enum mailbox_status_items items,struct mailbox_status * status_r)984 static void imapc_mailbox_get_selected_status(struct imapc_mailbox *mbox,
985 					      enum mailbox_status_items items,
986 					      struct mailbox_status *status_r)
987 {
988 	index_storage_get_open_status(&mbox->box, items, status_r);
989 	if ((items & STATUS_PERMANENT_FLAGS) != 0)
990 		status_r->permanent_flags = mbox->permanent_flags;
991 	if ((items & STATUS_FIRST_RECENT_UID) != 0)
992 		status_r->first_recent_uid = mbox->highest_nonrecent_uid + 1;
993 	if ((items & STATUS_HIGHESTMODSEQ) != 0) {
994 		/* FIXME: this doesn't work perfectly. we're now just returning
995 		   the HIGHESTMODSEQ from the current index, which may or may
996 		   not be correct. with QRESYNC enabled we could be returning
997 		   sync_highestmodseq, but that would require implementing
998 		   VANISHED replies. and without QRESYNC we'd have to issue
999 		   STATUS (HIGHESTMODSEQ), which isn't efficient since we get
1000 		   here constantly (after every IMAP command). */
1001 	}
1002 	if (imapc_mailbox_has_modseqs(mbox)) {
1003 		/* even if local indexes are only in memory, we still
1004 		   have modseqs on the IMAP server itself. */
1005 		status_r->nonpermanent_modseqs = FALSE;
1006 	}
1007 }
1008 
imapc_mailbox_delete(struct mailbox * box)1009 static int imapc_mailbox_delete(struct mailbox *box)
1010 {
1011 	box->delete_skip_empty_check = TRUE;
1012 	return index_storage_mailbox_delete(box);
1013 }
1014 
imapc_mailbox_run_status(struct mailbox * box,enum mailbox_status_items items,struct mailbox_status * status_r)1015 static int imapc_mailbox_run_status(struct mailbox *box,
1016 				    enum mailbox_status_items items,
1017 				    struct mailbox_status *status_r)
1018 {
1019 	struct imapc_mailbox *mbox = IMAPC_MAILBOX(box);
1020 	struct imapc_command *cmd;
1021 	struct imapc_simple_context sctx;
1022 	string_t *str;
1023 
1024 	if (imapc_mailbox_get_capabilities(mbox) < 0)
1025 		return -1;
1026 
1027 	str = t_str_new(256);
1028 	if ((items & STATUS_MESSAGES) != 0)
1029 		str_append(str, " MESSAGES");
1030 	if ((items & STATUS_RECENT) != 0)
1031 		str_append(str, " RECENT");
1032 	if ((items & STATUS_UIDNEXT) != 0)
1033 		str_append(str, " UIDNEXT");
1034 	if ((items & STATUS_UIDVALIDITY) != 0)
1035 		str_append(str, " UIDVALIDITY");
1036 	if ((items & STATUS_UNSEEN) != 0)
1037 		str_append(str, " UNSEEN");
1038 	if ((items & STATUS_HIGHESTMODSEQ) != 0 &&
1039 	    imapc_mailbox_has_modseqs(mbox))
1040 		str_append(str, " HIGHESTMODSEQ");
1041 
1042 	if (str_len(str) == 0) {
1043 		/* nothing requested */
1044 		return 0;
1045 	}
1046 
1047 	imapc_simple_context_init(&sctx, mbox->storage->client);
1048 	mbox->storage->cur_status_box = mbox;
1049 	mbox->storage->cur_status = status_r;
1050 	cmd = imapc_client_cmd(mbox->storage->client->client,
1051 			       imapc_simple_callback, &sctx);
1052 	imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_RETRIABLE);
1053 	imapc_command_sendf(cmd, "STATUS %s (%1s)",
1054 			    imapc_mailbox_get_remote_name(mbox), str_c(str)+1);
1055 	imapc_simple_run(&sctx, &cmd);
1056 	mbox->storage->cur_status_box = NULL;
1057 	mbox->storage->cur_status = NULL;
1058 	return sctx.ret;
1059 }
1060 
imapc_mailbox_get_status(struct mailbox * box,enum mailbox_status_items items,struct mailbox_status * status_r)1061 static int imapc_mailbox_get_status(struct mailbox *box,
1062 				    enum mailbox_status_items items,
1063 				    struct mailbox_status *status_r)
1064 {
1065 	struct imapc_mailbox *mbox = IMAPC_MAILBOX(box);
1066 
1067 	if (mbox->guid_fetch_field_name != NULL ||
1068 	    IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_GUID_FORCED))
1069 		status_r->have_guids = TRUE;
1070 
1071 	if (box->opened) {
1072 		imapc_mailbox_get_selected_status(mbox, items, status_r);
1073 	} else if ((items & (STATUS_FIRST_UNSEEN_SEQ | STATUS_KEYWORDS |
1074 			     STATUS_PERMANENT_FLAGS |
1075 			     STATUS_FIRST_RECENT_UID)) != 0) {
1076 		/* getting these requires opening the mailbox */
1077 		if (mailbox_open(box) < 0)
1078 			return -1;
1079 		imapc_mailbox_get_selected_status(mbox, items, status_r);
1080 	} else {
1081 		if (imapc_mailbox_run_status(box, items, status_r) < 0)
1082 			return -1;
1083 	}
1084 
1085 	if (box->opened && !box->deleting && (items & STATUS_UIDNEXT) != 0 &&
1086 	    mbox->sync_uid_next == 0) {
1087 		/* Courier-workaround, it doesn't send UIDNEXT on SELECT */
1088 		if (imapc_mailbox_run_status(box, STATUS_UIDNEXT, status_r) < 0)
1089 			return -1;
1090 	}
1091 	return 0;
1092 }
1093 
imapc_mailbox_get_namespaces(struct imapc_mailbox * mbox)1094 static int imapc_mailbox_get_namespaces(struct imapc_mailbox *mbox)
1095 {
1096 	struct imapc_storage *storage = mbox->storage;
1097 	struct imapc_command *cmd;
1098 	struct imapc_simple_context sctx;
1099 
1100 	if (storage->namespaces_requested)
1101 		return 0;
1102 
1103 	if (imapc_mailbox_get_capabilities(mbox) < 0)
1104 		return -1;
1105 	if ((mbox->capabilities & IMAPC_CAPABILITY_NAMESPACE) == 0) {
1106 		/* NAMESPACE capability not supported */
1107 		return 0;
1108 	}
1109 
1110 	imapc_simple_context_init(&sctx, storage->client);
1111 	cmd = imapc_client_cmd(storage->client->client,
1112 			       imapc_simple_callback, &sctx);
1113 	imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_RETRIABLE);
1114 	imapc_command_send(cmd, "NAMESPACE");
1115 	imapc_simple_run(&sctx, &cmd);
1116 
1117 	if (sctx.ret < 0)
1118 		return -1;
1119 	storage->namespaces_requested = TRUE;
1120 	return 0;
1121 }
1122 
1123 static const struct imapc_namespace *
imapc_namespace_find_mailbox(struct imapc_storage * storage,const char * remote_name)1124 imapc_namespace_find_mailbox(struct imapc_storage *storage,
1125 			     const char *remote_name)
1126 {
1127 	const struct imapc_namespace *ns, *best_ns = NULL;
1128 	size_t best_len = UINT_MAX, len;
1129 
1130 	array_foreach(&storage->remote_namespaces, ns) {
1131 		len = strlen(ns->prefix);
1132 		if (str_begins(remote_name, ns->prefix)) {
1133 			if (best_len > len) {
1134 				best_ns = ns;
1135 				best_len = len;
1136 			}
1137 		}
1138 	}
1139 	return best_ns;
1140 }
1141 
imapc_mailbox_get_metadata(struct mailbox * box,enum mailbox_metadata_items items,struct mailbox_metadata * metadata_r)1142 static int imapc_mailbox_get_metadata(struct mailbox *box,
1143 				      enum mailbox_metadata_items items,
1144 				      struct mailbox_metadata *metadata_r)
1145 {
1146 	struct imapc_mailbox *mbox = IMAPC_MAILBOX(box);
1147 	const struct imapc_namespace *ns;
1148 
1149 	if ((items & MAILBOX_METADATA_GUID) != 0) {
1150 		/* a bit ugly way to do this, but better than nothing for now.
1151 		   FIXME: if indexes are enabled, keep this there. */
1152 		mail_generate_guid_128_hash(box->name, metadata_r->guid);
1153 		items &= ENUM_NEGATE(MAILBOX_METADATA_GUID);
1154 	}
1155 	if ((items & MAILBOX_METADATA_BACKEND_NAMESPACE) != 0) {
1156 		if (imapc_mailbox_get_namespaces(mbox) < 0)
1157 			return -1;
1158 
1159 		const char *remote_name = imapc_mailbox_get_remote_name(mbox);
1160 		ns = imapc_namespace_find_mailbox(mbox->storage, remote_name);
1161 		if (ns != NULL) {
1162 			metadata_r->backend_ns_prefix = ns->prefix;
1163 			metadata_r->backend_ns_type = ns->type;
1164 		}
1165 		items &= ENUM_NEGATE(MAILBOX_METADATA_BACKEND_NAMESPACE);
1166 	}
1167 	if (items != 0) {
1168 		if (index_mailbox_get_metadata(box, items, metadata_r) < 0)
1169 			return -1;
1170 	}
1171 	return 0;
1172 }
1173 
imapc_noop_callback(const struct imapc_command_reply * reply,void * context)1174 static void imapc_noop_callback(const struct imapc_command_reply *reply,
1175 				void *context)
1176 
1177 {
1178 	struct imapc_storage *storage = context;
1179 
1180 	if (reply->state == IMAPC_COMMAND_STATE_OK)
1181 		;
1182 	else if (reply->state == IMAPC_COMMAND_STATE_NO)
1183 		imapc_copy_error_from_reply(storage, MAIL_ERROR_PARAMS, reply);
1184 	else if (reply->state == IMAPC_COMMAND_STATE_DISCONNECTED)
1185 		mail_storage_set_internal_error(&storage->storage);
1186 	else {
1187 		mail_storage_set_critical(&storage->storage,
1188 			"imapc: NOOP failed: %s", reply->text_full);
1189 	}
1190 }
1191 
imapc_idle_timeout(struct imapc_mailbox * mbox)1192 static void imapc_idle_timeout(struct imapc_mailbox *mbox)
1193 {
1194 	struct imapc_command *cmd;
1195 
1196 	cmd = imapc_client_mailbox_cmd(mbox->client_box,
1197 				       imapc_noop_callback, mbox->storage);
1198 	imapc_command_send(cmd, "NOOP");
1199 }
1200 
imapc_idle_noop_callback(const struct imapc_command_reply * reply,void * context)1201 static void imapc_idle_noop_callback(const struct imapc_command_reply *reply,
1202 				     void *context)
1203 
1204 {
1205 	struct imapc_mailbox *mbox = context;
1206 
1207 	imapc_noop_callback(reply, mbox->box.storage);
1208 	if (mbox->client_box != NULL)
1209 		imapc_client_mailbox_idle(mbox->client_box);
1210 }
1211 
imapc_notify_changes(struct mailbox * box)1212 static void imapc_notify_changes(struct mailbox *box)
1213 {
1214 	struct imapc_mailbox *mbox = IMAPC_MAILBOX(box);
1215 	const struct mail_storage_settings *set = box->storage->set;
1216 	struct imapc_command *cmd;
1217 
1218 	if (box->notify_callback == NULL) {
1219 		timeout_remove(&mbox->to_idle_check);
1220 		return;
1221 	}
1222 
1223 	if ((mbox->capabilities & IMAPC_CAPABILITY_IDLE) != 0) {
1224 		/* remote server is already in IDLE. but since some servers
1225 		   don't notice changes immediately, we'll force them to check
1226 		   here by sending a NOOP. this helps with clients that break
1227 		   IDLE when clicking "get mail". */
1228 		cmd = imapc_client_mailbox_cmd(mbox->client_box,
1229 					       imapc_idle_noop_callback, mbox);
1230 		imapc_command_send(cmd, "NOOP");
1231 	} else {
1232 		/* remote server doesn't support IDLE.
1233 		   check for changes with NOOP every once in a while. */
1234 		i_assert(!imapc_client_is_running(mbox->storage->client->client));
1235 		mbox->to_idle_check =
1236 			timeout_add(set->mailbox_idle_check_interval * 1000,
1237 				    imapc_idle_timeout, mbox);
1238 	}
1239 }
1240 
imapc_is_inconsistent(struct mailbox * box)1241 static bool imapc_is_inconsistent(struct mailbox *box)
1242 {
1243 	struct imapc_mailbox *mbox = IMAPC_MAILBOX(box);
1244 
1245 	if (box->view != NULL &&
1246 	    mail_index_view_is_inconsistent(box->view))
1247 		return TRUE;
1248 
1249 	return mbox->client_box == NULL ? FALSE :
1250 		!imapc_client_mailbox_is_opened(mbox->client_box);
1251 }
1252 
1253 struct mail_storage imapc_storage = {
1254 	.name = IMAPC_STORAGE_NAME,
1255 	.class_flags = MAIL_STORAGE_CLASS_FLAG_NO_ROOT |
1256 		       MAIL_STORAGE_CLASS_FLAG_UNIQUE_ROOT |
1257 		       MAIL_STORAGE_CLASS_FLAG_SECONDARY_INDEX,
1258 	.event_category = &event_category_imapc,
1259 
1260 	.v = {
1261 		imapc_get_setting_parser_info,
1262 		imapc_storage_alloc,
1263 		imapc_storage_create,
1264 		imapc_storage_destroy,
1265 		NULL,
1266 		imapc_storage_get_list_settings,
1267 		NULL,
1268 		imapc_mailbox_alloc,
1269 		NULL,
1270 		NULL,
1271 	}
1272 };
1273 
1274 struct mailbox imapc_mailbox = {
1275 	.v = {
1276 		index_storage_is_readonly,
1277 		index_storage_mailbox_enable,
1278 		imapc_mailbox_exists,
1279 		imapc_mailbox_open,
1280 		imapc_mailbox_close,
1281 		index_storage_mailbox_free,
1282 		imapc_mailbox_create,
1283 		imapc_mailbox_update,
1284 		imapc_mailbox_delete,
1285 		index_storage_mailbox_rename,
1286 		imapc_mailbox_get_status,
1287 		imapc_mailbox_get_metadata,
1288 		index_storage_set_subscribed,
1289 		index_storage_attribute_set,
1290 		index_storage_attribute_get,
1291 		index_storage_attribute_iter_init,
1292 		index_storage_attribute_iter_next,
1293 		index_storage_attribute_iter_deinit,
1294 		NULL,
1295 		NULL,
1296 		imapc_mailbox_sync_init,
1297 		index_mailbox_sync_next,
1298 		imapc_mailbox_sync_deinit,
1299 		NULL,
1300 		imapc_notify_changes,
1301 		index_transaction_begin,
1302 		index_transaction_commit,
1303 		index_transaction_rollback,
1304 		NULL,
1305 		imapc_mail_alloc,
1306 		imapc_search_init,
1307 		imapc_search_deinit,
1308 		index_storage_search_next_nonblock,
1309 		imapc_search_next_update_seq,
1310 		imapc_save_alloc,
1311 		imapc_save_begin,
1312 		imapc_save_continue,
1313 		imapc_save_finish,
1314 		imapc_save_cancel,
1315 		imapc_copy,
1316 		imapc_transaction_save_commit_pre,
1317 		imapc_transaction_save_commit_post,
1318 		imapc_transaction_save_rollback,
1319 		imapc_is_inconsistent
1320 	}
1321 };
1322