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