1 /* Copyright (c) 2011-2018 Dovecot authors, see the included COPYING file */
2
3 #include "lib.h"
4 #include "ioloop.h"
5 #include "net.h"
6 #include "istream.h"
7 #include "ostream.h"
8 #include "base64.h"
9 #include "write-full.h"
10 #include "str.h"
11 #include "time-util.h"
12 #include "dns-lookup.h"
13 #include "dsasl-client.h"
14 #include "iostream-rawlog.h"
15 #include "iostream-ssl.h"
16 #include "imap-quote.h"
17 #include "imap-util.h"
18 #include "imap-parser.h"
19 #include "imapc-client-private.h"
20 #include "imapc-connection.h"
21
22 #include <unistd.h>
23 #include <ctype.h>
24
25 #define IMAPC_COMMAND_STATE_AUTHENTICATE_CONTINUE 10000
26 #define IMAPC_MAX_INLINE_LITERAL_SIZE (1024*32)
27 /* If LOGOUT reply takes longer than this, disconnect. */
28 #define IMAPC_LOGOUT_TIMEOUT_MSECS 5000
29
30 enum imapc_input_state {
31 IMAPC_INPUT_STATE_NONE = 0,
32 IMAPC_INPUT_STATE_PLUS,
33 IMAPC_INPUT_STATE_UNTAGGED,
34 IMAPC_INPUT_STATE_UNTAGGED_NUM,
35 IMAPC_INPUT_STATE_TAGGED
36 };
37
38 struct imapc_command_stream {
39 unsigned int pos;
40 uoff_t size;
41 struct istream *input;
42 };
43
44 struct imapc_command {
45 pool_t pool;
46 buffer_t *data;
47 unsigned int send_pos;
48 unsigned int tag;
49
50 enum imapc_command_flags flags;
51 struct imapc_connection *conn;
52 /* If non-NULL, points to the mailbox where this command should be
53 executed */
54 struct imapc_client_mailbox *box;
55
56 ARRAY(struct imapc_command_stream) streams;
57
58 imapc_command_callback_t *callback;
59 void *context;
60
61 /* This is the AUTHENTICATE command */
62 bool authenticate:1;
63 /* This is the IDLE command */
64 bool idle:1;
65 /* Waiting for '+' literal reply before we can continue */
66 bool wait_for_literal:1;
67 /* Command is fully sent to server */
68 bool sent:1;
69 };
70 ARRAY_DEFINE_TYPE(imapc_command, struct imapc_command *);
71
72 struct imapc_connection_literal {
73 char *temp_path;
74 int fd;
75 uoff_t bytes_left;
76
77 const struct imap_arg *parent_arg;
78 unsigned int list_idx;
79 };
80
81 struct imapc_connection {
82 struct imapc_client *client;
83 char *name;
84 int refcount;
85
86 int fd;
87 struct io *io;
88 struct istream *input, *raw_input;
89 struct ostream *output, *raw_output;
90 struct imap_parser *parser;
91 struct timeout *to;
92 struct timeout *to_output;
93 struct dns_lookup *dns_lookup;
94 struct dsasl_client *sasl_client;
95
96 struct ssl_iostream *ssl_iostream;
97
98 int (*input_callback)(struct imapc_connection *conn);
99 enum imapc_input_state input_state;
100 unsigned int cur_tag;
101 uint32_t cur_num;
102 struct timeval last_connect;
103 unsigned int reconnect_count;
104
105 struct imapc_client_mailbox *selecting_box, *selected_box;
106 enum imapc_connection_state state;
107 char *disconnect_reason;
108
109 enum imapc_capability capabilities;
110 char **capabilities_list;
111
112 imapc_command_callback_t *login_callback;
113 void *login_context;
114
115 /* commands pending in queue to be sent */
116 ARRAY_TYPE(imapc_command) cmd_send_queue;
117 /* commands that have been sent, waiting for their tagged reply */
118 ARRAY_TYPE(imapc_command) cmd_wait_list;
119 /* commands that were already sent, but were aborted since (due to
120 unselecting mailbox). */
121 ARRAY_TYPE(seq_range) aborted_cmd_tags;
122 unsigned int reconnect_command_count;
123
124 unsigned int ips_count, prev_connect_idx;
125 struct ip_addr *ips;
126
127 struct imapc_connection_literal literal;
128 ARRAY(struct imapc_arg_file) literal_files;
129
130 unsigned int throttle_msecs;
131 unsigned int throttle_shrink_msecs;
132 unsigned int last_successful_throttle_msecs;
133 bool throttle_pending;
134 struct timeval throttle_end_timeval;
135 struct timeout *to_throttle, *to_throttle_shrink;
136
137 bool reconnecting:1;
138 bool reconnect_waiting:1;
139 bool reconnect_ok:1;
140 bool idling:1;
141 bool idle_stopping:1;
142 bool idle_plus_waiting:1;
143 bool select_waiting_reply:1;
144 };
145
146 static void imapc_connection_capability_cb(const struct imapc_command_reply *reply,
147 void *context);
148 static int imapc_connection_output(struct imapc_connection *conn);
149 static int imapc_connection_ssl_init(struct imapc_connection *conn);
150 static void imapc_command_free(struct imapc_command *cmd);
151 static void imapc_command_send_more(struct imapc_connection *conn);
152 static void
153 imapc_login_callback(struct imapc_connection *conn,
154 const struct imapc_command_reply *reply);
155
156 static void
imapc_auth_ok(struct imapc_connection * conn)157 imapc_auth_ok(struct imapc_connection *conn)
158 {
159 if (conn->client->set.debug)
160 i_debug("imapc(%s): Authenticated successfully", conn->name);
161
162 if (conn->client->state_change_callback == NULL)
163 return;
164
165 conn->client->state_change_callback(conn->client->state_change_context,
166 IMAPC_STATE_CHANGE_AUTH_OK, NULL);
167 }
168
169 static void
imapc_auth_failed(struct imapc_connection * conn,const struct imapc_command_reply * _reply,const char * error)170 imapc_auth_failed(struct imapc_connection *conn, const struct imapc_command_reply *_reply,
171 const char *error)
172 {
173 struct imapc_command_reply reply = *_reply;
174
175 reply.text_without_resp = reply.text_full =
176 t_strdup_printf("Authentication failed: %s", error);
177 if (reply.state != IMAPC_COMMAND_STATE_DISCONNECTED) {
178 reply.state = IMAPC_COMMAND_STATE_AUTH_FAILED;
179 i_error("imapc(%s): %s", conn->name, reply.text_full);
180 }
181 imapc_login_callback(conn, &reply);
182
183 if (conn->client->state_change_callback == NULL)
184 return;
185
186 conn->client->state_change_callback(conn->client->state_change_context,
187 IMAPC_STATE_CHANGE_AUTH_FAILED,
188 error);
189 }
190
191 struct imapc_connection *
imapc_connection_init(struct imapc_client * client,imapc_command_callback_t * login_callback,void * login_context)192 imapc_connection_init(struct imapc_client *client,
193 imapc_command_callback_t *login_callback,
194 void *login_context)
195 {
196 struct imapc_connection *conn;
197
198 conn = i_new(struct imapc_connection, 1);
199 conn->refcount = 1;
200 conn->client = client;
201 conn->login_callback = login_callback;
202 conn->login_context = login_context;
203 conn->fd = -1;
204 conn->name = i_strdup_printf("%s:%u", client->set.host,
205 client->set.port);
206 conn->literal.fd = -1;
207 conn->reconnect_ok = (client->set.connect_retry_count>0);
208 i_array_init(&conn->cmd_send_queue, 8);
209 i_array_init(&conn->cmd_wait_list, 32);
210 i_array_init(&conn->literal_files, 4);
211 i_array_init(&conn->aborted_cmd_tags, 8);
212
213 if (client->set.debug)
214 i_debug("imapc(%s): Created new connection", conn->name);
215
216 imapc_client_ref(client);
217 return conn;
218 }
219
imapc_connection_ref(struct imapc_connection * conn)220 static void imapc_connection_ref(struct imapc_connection *conn)
221 {
222 i_assert(conn->refcount > 0);
223
224 conn->refcount++;
225 }
226
imapc_connection_unref(struct imapc_connection ** _conn)227 static void imapc_connection_unref(struct imapc_connection **_conn)
228 {
229 struct imapc_connection *conn = *_conn;
230
231 i_assert(conn->refcount > 0);
232
233 *_conn = NULL;
234 if (--conn->refcount > 0)
235 return;
236
237 i_assert(conn->disconnect_reason == NULL);
238
239 if (conn->capabilities_list != NULL)
240 p_strsplit_free(default_pool, conn->capabilities_list);
241 array_free(&conn->cmd_send_queue);
242 array_free(&conn->cmd_wait_list);
243 array_free(&conn->literal_files);
244 array_free(&conn->aborted_cmd_tags);
245 imapc_client_unref(&conn->client);
246 i_free(conn->ips);
247 i_free(conn->name);
248 i_free(conn);
249 }
250
imapc_connection_deinit(struct imapc_connection ** _conn)251 void imapc_connection_deinit(struct imapc_connection **_conn)
252 {
253 imapc_connection_disconnect(*_conn);
254 imapc_connection_unref(_conn);
255 }
256
imapc_connection_ioloop_changed(struct imapc_connection * conn)257 void imapc_connection_ioloop_changed(struct imapc_connection *conn)
258 {
259 if (conn->io != NULL)
260 conn->io = io_loop_move_io(&conn->io);
261 if (conn->to != NULL)
262 conn->to = io_loop_move_timeout(&conn->to);
263 if (conn->to_throttle != NULL)
264 conn->to_throttle = io_loop_move_timeout(&conn->to_throttle);
265 if (conn->to_throttle_shrink != NULL)
266 conn->to_throttle_shrink = io_loop_move_timeout(&conn->to_throttle_shrink);
267 if (conn->output != NULL)
268 o_stream_switch_ioloop(conn->output);
269 if (conn->dns_lookup != NULL)
270 dns_lookup_switch_ioloop(conn->dns_lookup);
271
272 if (conn->client->ioloop == NULL && conn->to_output != NULL) {
273 /* we're only once moving the to_output to the main ioloop,
274 since timeout moves currently also reset the timeout.
275 (the rest of the times this is a no-op) */
276 conn->to_output = io_loop_move_timeout(&conn->to_output);
277 }
278 }
279
imapc_command_get_readable(struct imapc_command * cmd)280 static const char *imapc_command_get_readable(struct imapc_command *cmd)
281 {
282 string_t *str = t_str_new(256);
283 const unsigned char *data = cmd->data->data;
284 unsigned int i;
285
286 for (i = 0; i < cmd->data->used; i++) {
287 if (data[i] != '\r' && data[i] != '\n')
288 str_append_c(str, data[i]);
289 }
290 return str_c(str);
291 }
292
293 static void
imapc_connection_abort_commands_array(ARRAY_TYPE (imapc_command)* cmd_array,ARRAY_TYPE (imapc_command)* dest_array,struct imapc_client_mailbox * only_box,bool keep_retriable)294 imapc_connection_abort_commands_array(ARRAY_TYPE(imapc_command) *cmd_array,
295 ARRAY_TYPE(imapc_command) *dest_array,
296 struct imapc_client_mailbox *only_box,
297 bool keep_retriable)
298 {
299 struct imapc_command *cmd;
300 unsigned int i;
301
302 for (i = 0; i < array_count(cmd_array); ) {
303 cmd = array_idx_elem(cmd_array, i);
304
305 if (cmd->box != only_box && only_box != NULL)
306 i++;
307 else if (keep_retriable &&
308 (cmd->flags & IMAPC_COMMAND_FLAG_RETRIABLE) != 0) {
309 cmd->send_pos = 0;
310 cmd->wait_for_literal = 0;
311 cmd->flags |= IMAPC_COMMAND_FLAG_RECONNECTED;
312 i++;
313 } else {
314 array_delete(cmd_array, i, 1);
315 array_push_back(dest_array, &cmd);
316 }
317 }
318 }
319
imapc_connection_abort_commands(struct imapc_connection * conn,struct imapc_client_mailbox * only_box,bool keep_retriable)320 void imapc_connection_abort_commands(struct imapc_connection *conn,
321 struct imapc_client_mailbox *only_box,
322 bool keep_retriable)
323 {
324 struct imapc_command *cmd;
325 ARRAY_TYPE(imapc_command) tmp_array;
326 struct imapc_command_reply reply;
327
328 t_array_init(&tmp_array, 8);
329 imapc_connection_abort_commands_array(&conn->cmd_wait_list, &tmp_array,
330 only_box, keep_retriable);
331 imapc_connection_abort_commands_array(&conn->cmd_send_queue, &tmp_array,
332 only_box, keep_retriable);
333
334 if (array_count(&conn->cmd_wait_list) > 0 && only_box == NULL) {
335 /* need to move all the waiting commands to send queue */
336 array_append_array(&conn->cmd_wait_list,
337 &conn->cmd_send_queue);
338 array_clear(&conn->cmd_send_queue);
339 array_append_array(&conn->cmd_send_queue,
340 &conn->cmd_wait_list);
341 array_clear(&conn->cmd_wait_list);
342 }
343
344 /* abort the commands. we'll do it here later so that if the
345 callback recurses us back here we don't crash */
346 i_zero(&reply);
347 reply.state = IMAPC_COMMAND_STATE_DISCONNECTED;
348 if (only_box != NULL) {
349 reply.text_without_resp = reply.text_full =
350 "Unselecting mailbox";
351 } else {
352 reply.text_without_resp = reply.text_full =
353 "Disconnected from server";
354 }
355 array_foreach_elem(&tmp_array, cmd) {
356 if (cmd->sent && conn->state == IMAPC_CONNECTION_STATE_DONE) {
357 /* We're not disconnected, so the reply will still
358 come. Remember that it needs to be ignored. */
359 seq_range_array_add(&conn->aborted_cmd_tags, cmd->tag);
360 }
361 cmd->callback(&reply, cmd->context);
362 imapc_command_free(cmd);
363 }
364 if (array_count(&conn->cmd_wait_list) == 0)
365 timeout_remove(&conn->to);
366 }
367
368 static void
imapc_login_callback(struct imapc_connection * conn,const struct imapc_command_reply * reply)369 imapc_login_callback(struct imapc_connection *conn,
370 const struct imapc_command_reply *reply)
371 {
372 if (conn->login_callback != NULL)
373 conn->login_callback(reply, conn->login_context);
374 }
375
imapc_connection_set_state(struct imapc_connection * conn,enum imapc_connection_state state)376 static void imapc_connection_set_state(struct imapc_connection *conn,
377 enum imapc_connection_state state)
378 {
379 struct imapc_command_reply reply;
380
381 conn->state = state;
382
383 switch (state) {
384 case IMAPC_CONNECTION_STATE_DISCONNECTED:
385 i_zero(&reply);
386 reply.state = IMAPC_COMMAND_STATE_DISCONNECTED;
387 reply.text_full = "Disconnected from server";
388 if (conn->disconnect_reason != NULL) {
389 reply.text_full = t_strdup_printf("%s: %s",
390 reply.text_full, conn->disconnect_reason);
391 i_free_and_null(conn->disconnect_reason);
392 }
393 reply.text_without_resp = reply.text_full;
394 if (!conn->reconnecting) {
395 imapc_login_callback(conn, &reply);
396 i_free(conn->ips);
397 conn->ips_count = 0;
398 }
399 array_clear(&conn->aborted_cmd_tags);
400 conn->idling = FALSE;
401 conn->idle_plus_waiting = FALSE;
402 conn->idle_stopping = FALSE;
403
404 conn->select_waiting_reply = FALSE;
405 conn->selecting_box = NULL;
406 conn->selected_box = NULL;
407 /* fall through */
408 case IMAPC_CONNECTION_STATE_DONE:
409 /* if we came from imapc_client_get_capabilities(), stop so
410 it can finish up and not just hang indefinitely. */
411 if (conn->client->stop_on_state_finish && !conn->reconnecting)
412 imapc_client_stop(conn->client);
413 break;
414 default:
415 break;
416 }
417 }
418
imapc_connection_lfiles_free(struct imapc_connection * conn)419 static void imapc_connection_lfiles_free(struct imapc_connection *conn)
420 {
421 struct imapc_arg_file *lfile;
422
423 array_foreach_modifiable(&conn->literal_files, lfile) {
424 if (close(lfile->fd) < 0)
425 i_error("imapc: close(literal file) failed: %m");
426 }
427 array_clear(&conn->literal_files);
428 }
429
430 static void
imapc_connection_literal_reset(struct imapc_connection_literal * literal)431 imapc_connection_literal_reset(struct imapc_connection_literal *literal)
432 {
433 i_close_fd_path(&literal->fd, literal->temp_path);
434 i_free_and_null(literal->temp_path);
435
436 i_zero(literal);
437 literal->fd = -1;
438 }
439
imapc_connection_disconnect_full(struct imapc_connection * conn,bool reconnecting)440 void imapc_connection_disconnect_full(struct imapc_connection *conn,
441 bool reconnecting)
442 {
443 /* timeout may be set also in disconnected state */
444 timeout_remove(&conn->to);
445 conn->reconnecting = reconnecting;
446
447 if (conn->state == IMAPC_CONNECTION_STATE_DISCONNECTED) {
448 i_assert(array_count(&conn->cmd_wait_list) == 0);
449 return;
450 }
451
452 if (conn->client->set.debug)
453 i_debug("imapc(%s): Disconnected", conn->name);
454
455 if (conn->dns_lookup != NULL)
456 dns_lookup_abort(&conn->dns_lookup);
457 imapc_connection_lfiles_free(conn);
458 imapc_connection_literal_reset(&conn->literal);
459 timeout_remove(&conn->to_output);
460 timeout_remove(&conn->to_throttle);
461 timeout_remove(&conn->to_throttle_shrink);
462 if (conn->parser != NULL)
463 imap_parser_unref(&conn->parser);
464 io_remove(&conn->io);
465 ssl_iostream_destroy(&conn->ssl_iostream);
466 if (conn->fd != -1) {
467 i_stream_destroy(&conn->input);
468 o_stream_destroy(&conn->output);
469 net_disconnect(conn->fd);
470 conn->fd = -1;
471 }
472
473 /* get capabilities again after reconnection. this is especially
474 important because post-login capabilities often do not contain AUTH=
475 capabilities. */
476 conn->capabilities = 0;
477 if (conn->capabilities_list != NULL) {
478 p_strsplit_free(default_pool, conn->capabilities_list);
479 conn->capabilities_list = NULL;
480 }
481
482 imapc_connection_set_state(conn, IMAPC_CONNECTION_STATE_DISCONNECTED);
483 imapc_connection_abort_commands(conn, NULL, reconnecting);
484
485 if (!reconnecting) {
486 imapc_client_try_stop(conn->client);
487 }
488 }
489
imapc_connection_set_no_reconnect(struct imapc_connection * conn)490 void imapc_connection_set_no_reconnect(struct imapc_connection *conn)
491 {
492 conn->reconnect_ok = FALSE;
493 }
494
imapc_connection_disconnect(struct imapc_connection * conn)495 void imapc_connection_disconnect(struct imapc_connection *conn)
496 {
497 imapc_connection_disconnect_full(conn, FALSE);
498 }
499
imapc_connection_set_disconnected(struct imapc_connection * conn)500 static void imapc_connection_set_disconnected(struct imapc_connection *conn)
501 {
502 imapc_connection_set_state(conn, IMAPC_CONNECTION_STATE_DISCONNECTED);
503 imapc_connection_abort_commands(conn, NULL, FALSE);
504 }
505
imapc_connection_can_reconnect(struct imapc_connection * conn)506 static bool imapc_connection_can_reconnect(struct imapc_connection *conn)
507 {
508 if (conn->client->logging_out)
509 return FALSE;
510 if (conn->client->set.connect_retry_count == 0 ||
511 (conn->client->set.connect_retry_count < UINT_MAX &&
512 conn->reconnect_count >= conn->client->set.connect_retry_count))
513 return FALSE;
514
515 if (conn->selected_box != NULL)
516 return imapc_client_mailbox_can_reconnect(conn->selected_box);
517 else {
518 return conn->reconnect_command_count == 0 &&
519 conn->reconnect_ok;
520 }
521 }
522
imapc_connection_reconnect(struct imapc_connection * conn)523 static void imapc_connection_reconnect(struct imapc_connection *conn)
524 {
525 conn->reconnect_ok = FALSE;
526 conn->reconnect_waiting = FALSE;
527
528 if (conn->selected_box != NULL) {
529 i_assert(!conn->selected_box->reconnecting);
530 conn->selected_box->reconnecting = TRUE;
531 /* if we fail again, avoid reconnecting immediately. if the
532 server is broken we could just get into an infinitely
533 failing reconnection loop. */
534 conn->selected_box->reconnect_ok = FALSE;
535 }
536 imapc_connection_disconnect_full(conn, TRUE);
537 imapc_connection_connect(conn);
538 }
539
imapc_connection_try_reconnect(struct imapc_connection * conn,const char * errstr,unsigned int delay_msecs,bool connect_error)540 void imapc_connection_try_reconnect(struct imapc_connection *conn,
541 const char *errstr,
542 unsigned int delay_msecs,
543 bool connect_error)
544 {
545 /* Try the next IP address only for connect() problems. */
546 if (conn->prev_connect_idx + 1 < conn->ips_count && connect_error) {
547 i_warning("imapc(%s): %s - trying the next IP", conn->name, errstr);
548 conn->reconnect_ok = TRUE;
549 imapc_connection_disconnect_full(conn, TRUE);
550 imapc_connection_connect(conn);
551 return;
552 }
553
554 if (!imapc_connection_can_reconnect(conn)) {
555 i_error("imapc(%s): %s - disconnecting", conn->name, errstr);
556 imapc_connection_disconnect(conn);
557 } else {
558 conn->reconnecting = TRUE;
559 i_warning("imapc(%s): %s - reconnecting (delay %u ms)", conn->name, errstr, delay_msecs);
560 if (delay_msecs == 0)
561 imapc_connection_reconnect(conn);
562 else {
563 imapc_connection_disconnect_full(conn, TRUE);
564 conn->to = timeout_add(delay_msecs, imapc_connection_reconnect, conn);
565 conn->reconnect_count++;
566 conn->reconnect_waiting = TRUE;
567 }
568 }
569 }
570
571 static void ATTR_FORMAT(2, 3)
imapc_connection_input_error(struct imapc_connection * conn,const char * fmt,...)572 imapc_connection_input_error(struct imapc_connection *conn,
573 const char *fmt, ...)
574 {
575 va_list va;
576
577 va_start(va, fmt);
578 i_error("imapc(%s): Server sent invalid input: %s",
579 conn->name, t_strdup_vprintf(fmt, va));
580 imapc_connection_disconnect(conn);
581 va_end(va);
582 }
583
last_arg_is_fetch_body(const struct imap_arg * args,const struct imap_arg ** parent_arg_r,unsigned int * idx_r)584 static bool last_arg_is_fetch_body(const struct imap_arg *args,
585 const struct imap_arg **parent_arg_r,
586 unsigned int *idx_r)
587 {
588 const struct imap_arg *list;
589 const char *name;
590 unsigned int count;
591
592 if (args[0].type == IMAP_ARG_ATOM &&
593 imap_arg_atom_equals(&args[1], "FETCH") &&
594 imap_arg_get_list_full(&args[2], &list, &count) && count >= 2 &&
595 list[count].type == IMAP_ARG_LITERAL_SIZE &&
596 imap_arg_get_atom(&list[count-1], &name) &&
597 strncasecmp(name, "BODY[", 5) == 0) {
598 *parent_arg_r = &args[2];
599 *idx_r = count;
600 return TRUE;
601 }
602 return FALSE;
603 }
604
605 static int
imapc_connection_read_literal_init(struct imapc_connection * conn,uoff_t size,const struct imap_arg * args)606 imapc_connection_read_literal_init(struct imapc_connection *conn, uoff_t size,
607 const struct imap_arg *args)
608 {
609 const char *path;
610 const struct imap_arg *parent_arg;
611 unsigned int idx;
612
613 i_assert(conn->literal.fd == -1);
614
615 if (size <= IMAPC_MAX_INLINE_LITERAL_SIZE ||
616 !last_arg_is_fetch_body(args, &parent_arg, &idx)) {
617 /* read the literal directly into parser */
618 return 0;
619 }
620
621 conn->literal.fd = imapc_client_create_temp_fd(conn->client, &path);
622 if (conn->literal.fd == -1)
623 return -1;
624 conn->literal.temp_path = i_strdup(path);
625 conn->literal.bytes_left = size;
626 conn->literal.parent_arg = parent_arg;
627 conn->literal.list_idx = idx;
628 return 1;
629 }
630
imapc_connection_read_literal(struct imapc_connection * conn)631 static int imapc_connection_read_literal(struct imapc_connection *conn)
632 {
633 struct imapc_arg_file *lfile;
634 const unsigned char *data;
635 size_t size;
636
637 if (conn->literal.bytes_left == 0)
638 return 1;
639
640 data = i_stream_get_data(conn->input, &size);
641 if (size > conn->literal.bytes_left)
642 size = conn->literal.bytes_left;
643 if (size > 0) {
644 if (write_full(conn->literal.fd, data, size) < 0) {
645 i_error("imapc(%s): write(%s) failed: %m",
646 conn->name, conn->literal.temp_path);
647 imapc_connection_disconnect(conn);
648 return -1;
649 }
650 i_stream_skip(conn->input, size);
651 conn->literal.bytes_left -= size;
652 }
653 if (conn->literal.bytes_left > 0)
654 return 0;
655
656 /* finished */
657 lfile = array_append_space(&conn->literal_files);
658 lfile->fd = conn->literal.fd;
659 lfile->parent_arg = conn->literal.parent_arg;
660 lfile->list_idx = conn->literal.list_idx;
661
662 conn->literal.fd = -1;
663 imapc_connection_literal_reset(&conn->literal);
664 return 1;
665 }
666
667 static int
imapc_connection_read_line_more(struct imapc_connection * conn,const struct imap_arg ** imap_args_r)668 imapc_connection_read_line_more(struct imapc_connection *conn,
669 const struct imap_arg **imap_args_r)
670 {
671 uoff_t literal_size;
672 int ret;
673
674 if ((ret = imapc_connection_read_literal(conn)) <= 0)
675 return ret;
676
677 ret = imap_parser_read_args(conn->parser, 0,
678 IMAP_PARSE_FLAG_LITERAL_SIZE |
679 IMAP_PARSE_FLAG_ATOM_ALLCHARS |
680 IMAP_PARSE_FLAG_LITERAL8 |
681 IMAP_PARSE_FLAG_SERVER_TEXT, imap_args_r);
682 if (ret == -2) {
683 /* need more data */
684 return 0;
685 }
686 if (ret < 0) {
687 enum imap_parser_error parser_error;
688 const char *err_msg = imap_parser_get_error(conn->parser, &parser_error);
689 if (parser_error != IMAP_PARSE_ERROR_BAD_SYNTAX)
690 imapc_connection_input_error(conn, "Error parsing input: %s", err_msg);
691 else
692 i_error("Error parsing input: %s", err_msg);
693 return -1;
694 }
695
696 if (imap_parser_get_literal_size(conn->parser, &literal_size)) {
697 if (imapc_connection_read_literal_init(conn, literal_size,
698 *imap_args_r) <= 0) {
699 imap_parser_read_last_literal(conn->parser);
700 return 2;
701 }
702 return imapc_connection_read_line_more(conn, imap_args_r);
703 }
704 return 1;
705 }
706
707 static int
imapc_connection_read_line(struct imapc_connection * conn,const struct imap_arg ** imap_args_r)708 imapc_connection_read_line(struct imapc_connection *conn,
709 const struct imap_arg **imap_args_r)
710 {
711 const unsigned char *data;
712 size_t size;
713 int ret;
714
715 while ((ret = imapc_connection_read_line_more(conn, imap_args_r)) == 2)
716 ;
717
718 if (ret > 0) {
719 data = i_stream_get_data(conn->input, &size);
720 if (size >= 2 && data[0] == '\r' && data[1] == '\n')
721 i_stream_skip(conn->input, 2);
722 else if (size >= 1 && data[0] == '\n')
723 i_stream_skip(conn->input, 1);
724 else
725 i_panic("imapc: Missing LF from input line");
726 } else if (ret < 0) {
727 data = i_stream_get_data(conn->input, &size);
728 unsigned char *lf = memchr(data, '\n', size);
729 if (lf != NULL)
730 i_stream_skip(conn->input, (lf - data) + 1);
731 }
732 return ret;
733 }
734
735 static int
imapc_connection_parse_capability(struct imapc_connection * conn,const char * value)736 imapc_connection_parse_capability(struct imapc_connection *conn,
737 const char *value)
738 {
739 const char *const *tmp;
740 unsigned int i;
741
742 if (conn->client->set.debug) {
743 i_debug("imapc(%s): Server capabilities: %s",
744 conn->name, value);
745 }
746
747 conn->capabilities = 0;
748 if (conn->capabilities_list != NULL)
749 p_strsplit_free(default_pool, conn->capabilities_list);
750 conn->capabilities_list = p_strsplit(default_pool, value, " ");
751
752 for (tmp = t_strsplit(value, " "); *tmp != NULL; tmp++) {
753 for (i = 0; imapc_capability_names[i].name != NULL; i++) {
754 const struct imapc_capability_name *cap =
755 &imapc_capability_names[i];
756
757 if (strcasecmp(*tmp, cap->name) == 0) {
758 conn->capabilities |= cap->capability;
759 break;
760 }
761 }
762 }
763
764 if ((conn->capabilities & IMAPC_CAPABILITY_IMAP4REV1) == 0) {
765 imapc_connection_input_error(conn,
766 "CAPABILITY list is missing IMAP4REV1");
767 return -1;
768 }
769 return 0;
770 }
771
772 static int
imapc_connection_handle_resp_text_code(struct imapc_connection * conn,const char * key,const char * value)773 imapc_connection_handle_resp_text_code(struct imapc_connection *conn,
774 const char *key, const char *value)
775 {
776 if (strcasecmp(key, "CAPABILITY") == 0) {
777 if (imapc_connection_parse_capability(conn, value) < 0)
778 return -1;
779 }
780 if (strcasecmp(key, "CLOSED") == 0) {
781 /* QRESYNC: SELECTing another mailbox */
782 if (conn->selecting_box != NULL) {
783 conn->selected_box = conn->selecting_box;
784 conn->selecting_box = NULL;
785 }
786 }
787 return 0;
788 }
789
790 static int
imapc_connection_handle_resp_text(struct imapc_connection * conn,const char * text,const char ** key_r,const char ** value_r)791 imapc_connection_handle_resp_text(struct imapc_connection *conn,
792 const char *text,
793 const char **key_r, const char **value_r)
794 {
795 const char *p, *value;
796
797 i_assert(text[0] == '[');
798
799 p = strchr(text, ']');
800 if (p == NULL) {
801 imapc_connection_input_error(conn, "Missing ']' in resp-text");
802 return -1;
803 }
804 text = t_strdup_until(text + 1, p);
805 value = strchr(text, ' ');
806 if (value != NULL) {
807 *key_r = t_strdup_until(text, value);
808 *value_r = value + 1;
809 } else {
810 *key_r = text;
811 *value_r = "";
812 }
813 return imapc_connection_handle_resp_text_code(conn, *key_r, *value_r);
814 }
815
816 static int
imapc_connection_handle_imap_resp_text(struct imapc_connection * conn,const struct imap_arg * args,const char ** key_r,const char ** value_r)817 imapc_connection_handle_imap_resp_text(struct imapc_connection *conn,
818 const struct imap_arg *args,
819 const char **key_r, const char **value_r)
820 {
821 const char *text;
822
823 if (args->type != IMAP_ARG_ATOM)
824 return 0;
825
826 text = imap_args_to_str(args);
827 if (*text != '[') {
828 if (*text == '\0') {
829 imapc_connection_input_error(conn,
830 "Missing text in resp-text");
831 return -1;
832 }
833 return 0;
834 }
835 return imapc_connection_handle_resp_text(conn, text, key_r, value_r);
836 }
837
need_literal(const char * str)838 static bool need_literal(const char *str)
839 {
840 unsigned int i;
841
842 for (i = 0; str[i] != '\0'; i++) {
843 unsigned char c = str[i];
844
845 if ((c & 0x80) != 0 || c == '\r' || c == '\n')
846 return TRUE;
847 }
848 return FALSE;
849 }
850
imapc_connection_input_reset(struct imapc_connection * conn)851 static void imapc_connection_input_reset(struct imapc_connection *conn)
852 {
853 conn->input_state = IMAPC_INPUT_STATE_NONE;
854 conn->cur_tag = 0;
855 conn->cur_num = 0;
856 if (conn->parser != NULL)
857 imap_parser_reset(conn->parser);
858 imapc_connection_lfiles_free(conn);
859 }
860
861 static void
imapc_connection_auth_finish(struct imapc_connection * conn,const struct imapc_command_reply * reply)862 imapc_connection_auth_finish(struct imapc_connection *conn,
863 const struct imapc_command_reply *reply)
864 {
865 if (reply->state != IMAPC_COMMAND_STATE_OK) {
866 imapc_auth_failed(conn, reply, reply->text_full);
867 imapc_connection_disconnect(conn);
868 return;
869 }
870
871 imapc_auth_ok(conn);
872
873 i_assert(array_count(&conn->cmd_wait_list) == 0);
874 timeout_remove(&conn->to);
875 imapc_connection_set_state(conn, IMAPC_CONNECTION_STATE_DONE);
876 imapc_login_callback(conn, reply);
877
878 imapc_command_send_more(conn);
879 }
880
imapc_connection_login_cb(const struct imapc_command_reply * reply,void * context)881 static void imapc_connection_login_cb(const struct imapc_command_reply *reply,
882 void *context)
883 {
884 struct imapc_connection *conn = context;
885
886 imapc_connection_auth_finish(conn, reply);
887 }
888
889 static void
imapc_connection_proxyauth_login_cb(const struct imapc_command_reply * reply,void * context)890 imapc_connection_proxyauth_login_cb(const struct imapc_command_reply *reply,
891 void *context)
892 {
893 struct imapc_connection *conn = context;
894 const struct imapc_client_settings *set = &conn->client->set;
895 struct imapc_command *cmd;
896
897 if (reply->state == IMAPC_COMMAND_STATE_OK) {
898 cmd = imapc_connection_cmd(conn, imapc_connection_login_cb,
899 conn);
900 imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_PRELOGIN);
901 imapc_command_sendf(cmd, "PROXYAUTH %s", set->username);
902 imapc_command_send_more(conn);
903 } else {
904 imapc_connection_auth_finish(conn, reply);
905 }
906 }
907
908 static void
imapc_connection_authenticate_cb(const struct imapc_command_reply * reply,void * context)909 imapc_connection_authenticate_cb(const struct imapc_command_reply *reply,
910 void *context)
911 {
912 struct imapc_connection *conn = context;
913 const unsigned char *sasl_output;
914 size_t input_len, sasl_output_len;
915 buffer_t *buf;
916 const char *error;
917
918 if ((int)reply->state != IMAPC_COMMAND_STATE_AUTHENTICATE_CONTINUE) {
919 dsasl_client_free(&conn->sasl_client);
920 imapc_connection_auth_finish(conn, reply);
921 return;
922 }
923
924 input_len = strlen(reply->text_full);
925 buf = t_buffer_create(MAX_BASE64_DECODED_SIZE(input_len));
926 if (base64_decode(reply->text_full, input_len, NULL, buf) < 0) {
927 imapc_auth_failed(conn, reply,
928 t_strdup_printf("Server sent non-base64 input for AUTHENTICATE: %s",
929 reply->text_full));
930 } else if (dsasl_client_input(conn->sasl_client, buf->data, buf->used, &error) < 0) {
931 imapc_auth_failed(conn, reply, error);
932 } else if (dsasl_client_output(conn->sasl_client, &sasl_output,
933 &sasl_output_len, &error) < 0) {
934 imapc_auth_failed(conn, reply, error);
935 } else {
936 string_t *imap_output =
937 t_str_new(MAX_BASE64_ENCODED_SIZE(sasl_output_len)+2);
938 base64_encode(sasl_output, sasl_output_len, imap_output);
939 str_append(imap_output, "\r\n");
940 o_stream_nsend(conn->output, str_data(imap_output),
941 str_len(imap_output));
942 return;
943 }
944 imapc_connection_disconnect(conn);
945 }
946
imapc_connection_have_auth(struct imapc_connection * conn,const char * mech_name)947 static bool imapc_connection_have_auth(struct imapc_connection *conn,
948 const char *mech_name)
949 {
950 char *const *capa;
951
952 for (capa = conn->capabilities_list; *capa != NULL; capa++) {
953 if (strncasecmp(*capa, "AUTH=", 5) == 0 &&
954 strcasecmp((*capa)+5, mech_name) == 0)
955 return TRUE;
956 }
957 return FALSE;
958 }
959
960 static int
imapc_connection_get_sasl_mech(struct imapc_connection * conn,const struct dsasl_client_mech ** mech_r,const char ** error_r)961 imapc_connection_get_sasl_mech(struct imapc_connection *conn,
962 const struct dsasl_client_mech **mech_r,
963 const char **error_r)
964 {
965 const struct imapc_client_settings *set = &conn->client->set;
966 const char *const *mechanisms =
967 t_strsplit_spaces(set->sasl_mechanisms, ", ");
968
969 /* find one of the specified SASL mechanisms */
970 for (; *mechanisms != NULL; mechanisms++) {
971 if (imapc_connection_have_auth(conn, *mechanisms)) {
972 *mech_r = dsasl_client_mech_find(*mechanisms);
973 if (*mech_r != NULL)
974 return 0;
975
976 *error_r = t_strdup_printf(
977 "Support for SASL method '%s' is missing", *mechanisms);
978 return -1;
979 }
980 }
981 *error_r = t_strdup_printf("IMAP server doesn't support any of the requested SASL mechanisms: %s",
982 set->sasl_mechanisms);
983 return -1;
984 }
985
imapc_connection_authenticate(struct imapc_connection * conn)986 static void imapc_connection_authenticate(struct imapc_connection *conn)
987 {
988 const struct imapc_client_settings *set = &conn->client->set;
989 struct imapc_command *cmd;
990 struct dsasl_client_settings sasl_set;
991 const struct dsasl_client_mech *sasl_mech = NULL;
992 const char *error;
993
994 if (conn->client->set.debug) {
995 if (set->master_user == NULL) {
996 i_debug("imapc(%s): Authenticating as %s",
997 conn->name, set->username);
998 } else {
999 i_debug("imapc(%s): Authenticating as %s for user %s",
1000 conn->name, set->master_user, set->username);
1001 }
1002 }
1003
1004 if (set->sasl_mechanisms != NULL && set->sasl_mechanisms[0] != '\0') {
1005 if (imapc_connection_get_sasl_mech(conn, &sasl_mech, &error) < 0) {
1006 struct imapc_command_reply reply;
1007 i_zero(&reply);
1008 reply.state = IMAPC_COMMAND_STATE_DISCONNECTED;
1009 reply.text_full = "";
1010 imapc_auth_failed(conn, &reply, error);
1011 imapc_connection_disconnect(conn);
1012 return;
1013 }
1014 }
1015
1016 if (set->use_proxyauth && set->master_user != NULL) {
1017 /* We can use LOGIN command */
1018 cmd = imapc_connection_cmd(conn, imapc_connection_proxyauth_login_cb,
1019 conn);
1020 imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_PRELOGIN);
1021 imapc_command_sendf(cmd, "LOGIN %s %s",
1022 set->master_user, set->password);
1023 return;
1024 }
1025 if (sasl_mech == NULL &&
1026 ((set->master_user == NULL &&
1027 !need_literal(set->username) && !need_literal(set->password)) ||
1028 (conn->capabilities & IMAPC_CAPABILITY_AUTH_PLAIN) == 0)) {
1029 /* We can use LOGIN command */
1030 cmd = imapc_connection_cmd(conn, imapc_connection_login_cb,
1031 conn);
1032 imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_PRELOGIN);
1033 imapc_command_sendf(cmd, "LOGIN %s %s",
1034 set->username, set->password);
1035 return;
1036 }
1037
1038 i_zero(&sasl_set);
1039 if (set->master_user == NULL)
1040 sasl_set.authid = set->username;
1041 else {
1042 sasl_set.authid = set->master_user;
1043 sasl_set.authzid = set->username;
1044 }
1045 sasl_set.password = set->password;
1046
1047 if (sasl_mech == NULL)
1048 sasl_mech = &dsasl_client_mech_plain;
1049 conn->sasl_client = dsasl_client_new(sasl_mech, &sasl_set);
1050
1051 cmd = imapc_connection_cmd(conn, imapc_connection_authenticate_cb, conn);
1052 cmd->authenticate = TRUE;
1053 imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_PRELOGIN);
1054
1055 if ((conn->capabilities & IMAPC_CAPABILITY_SASL_IR) != 0) {
1056 const unsigned char *sasl_output;
1057 size_t sasl_output_len;
1058 string_t *sasl_output_base64;
1059 const char *error;
1060
1061 if (dsasl_client_output(conn->sasl_client, &sasl_output,
1062 &sasl_output_len, &error) < 0) {
1063 i_error("imapc(%s): Failed to create initial SASL reply: %s",
1064 conn->name, error);
1065 imapc_connection_disconnect(conn);
1066 return;
1067 }
1068 sasl_output_base64 = t_str_new(MAX_BASE64_ENCODED_SIZE(sasl_output_len));
1069 base64_encode(sasl_output, sasl_output_len, sasl_output_base64);
1070
1071 imapc_command_sendf(cmd, "AUTHENTICATE %1s %1s",
1072 dsasl_client_mech_get_name(sasl_mech),
1073 str_c(sasl_output_base64));
1074 } else {
1075 imapc_command_sendf(cmd, "AUTHENTICATE %1s",
1076 dsasl_client_mech_get_name(sasl_mech));
1077 }
1078 }
1079
1080 static void
imapc_connection_starttls_cb(const struct imapc_command_reply * reply,void * context)1081 imapc_connection_starttls_cb(const struct imapc_command_reply *reply,
1082 void *context)
1083 {
1084 struct imapc_connection *conn = context;
1085 struct imapc_command *cmd;
1086
1087 if (reply->state != IMAPC_COMMAND_STATE_OK) {
1088 imapc_connection_input_error(conn, "STARTTLS failed: %s",
1089 reply->text_full);
1090 return;
1091 }
1092
1093 if (imapc_connection_ssl_init(conn) < 0)
1094 imapc_connection_disconnect(conn);
1095 else {
1096 /* get updated capabilities */
1097 cmd = imapc_connection_cmd(conn, imapc_connection_capability_cb,
1098 conn);
1099 imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_PRELOGIN);
1100 imapc_command_send(cmd, "CAPABILITY");
1101 }
1102 }
1103
1104 static void
imapc_connection_id_callback(const struct imapc_command_reply * reply ATTR_UNUSED,void * context ATTR_UNUSED)1105 imapc_connection_id_callback(const struct imapc_command_reply *reply ATTR_UNUSED,
1106 void *context ATTR_UNUSED)
1107 {
1108 }
1109
imapc_connection_send_id(struct imapc_connection * conn)1110 static void imapc_connection_send_id(struct imapc_connection *conn)
1111 {
1112 static unsigned int global_id_counter = 0;
1113 struct imapc_command *cmd;
1114
1115 if ((conn->capabilities & IMAPC_CAPABILITY_ID) == 0 ||
1116 conn->client->set.session_id_prefix == NULL)
1117 return;
1118
1119 cmd = imapc_connection_cmd(conn, imapc_connection_id_callback, conn);
1120 imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_PRELOGIN);
1121 imapc_command_send(cmd, t_strdup_printf(
1122 "ID (\"name\" \"Dovecot\" \"x-session-ext-id\" \"%s-%u\")",
1123 conn->client->set.session_id_prefix, ++global_id_counter));
1124 }
1125
imapc_connection_starttls(struct imapc_connection * conn)1126 static void imapc_connection_starttls(struct imapc_connection *conn)
1127 {
1128 struct imapc_command *cmd;
1129
1130 if (conn->client->set.ssl_mode == IMAPC_CLIENT_SSL_MODE_STARTTLS &&
1131 conn->ssl_iostream == NULL) {
1132 if ((conn->capabilities & IMAPC_CAPABILITY_STARTTLS) == 0) {
1133 i_error("imapc(%s): Requested STARTTLS, "
1134 "but server doesn't support it",
1135 conn->name);
1136 imapc_connection_disconnect(conn);
1137 return;
1138 }
1139 cmd = imapc_connection_cmd(conn, imapc_connection_starttls_cb,
1140 conn);
1141 imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_PRELOGIN);
1142 imapc_command_send(cmd, "STARTTLS");
1143 return;
1144 }
1145 imapc_connection_send_id(conn);
1146 imapc_connection_authenticate(conn);
1147 }
1148
1149 static void
imapc_connection_capability_cb(const struct imapc_command_reply * reply,void * context)1150 imapc_connection_capability_cb(const struct imapc_command_reply *reply,
1151 void *context)
1152 {
1153 struct imapc_connection *conn = context;
1154
1155 if (reply->state != IMAPC_COMMAND_STATE_OK) {
1156 imapc_connection_input_error(conn,
1157 "Failed to get capabilities: %s", reply->text_full);
1158 } else if (conn->capabilities == 0) {
1159 imapc_connection_input_error(conn,
1160 "Capabilities not returned by server");
1161 } else {
1162 imapc_connection_starttls(conn);
1163 }
1164 }
1165
imapc_connection_input_banner(struct imapc_connection * conn)1166 static int imapc_connection_input_banner(struct imapc_connection *conn)
1167 {
1168 const struct imap_arg *imap_args;
1169 const char *key, *value;
1170 struct imapc_command *cmd;
1171 int ret;
1172
1173 if ((ret = imapc_connection_read_line(conn, &imap_args)) <= 0)
1174 return ret;
1175 /* we already verified that the banner beigns with OK */
1176 i_assert(imap_arg_atom_equals(imap_args, "OK"));
1177 imap_args++;
1178
1179 if (imapc_connection_handle_imap_resp_text(conn, imap_args,
1180 &key, &value) < 0)
1181 return -1;
1182 imapc_connection_set_state(conn, IMAPC_CONNECTION_STATE_AUTHENTICATING);
1183
1184 if (conn->capabilities == 0) {
1185 /* capabilities weren't sent in the banner. ask for them. */
1186 cmd = imapc_connection_cmd(conn, imapc_connection_capability_cb,
1187 conn);
1188 imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_PRELOGIN);
1189 imapc_command_send(cmd, "CAPABILITY");
1190 } else {
1191 imapc_connection_starttls(conn);
1192 }
1193 conn->input_callback = NULL;
1194 imapc_connection_input_reset(conn);
1195 return 1;
1196 }
1197
imapc_connection_input_untagged(struct imapc_connection * conn)1198 static int imapc_connection_input_untagged(struct imapc_connection *conn)
1199 {
1200 const struct imap_arg *imap_args;
1201 const unsigned char *data;
1202 size_t size;
1203 const char *name, *value;
1204 struct imap_parser *parser;
1205 struct imapc_untagged_reply reply;
1206 int ret;
1207
1208 if (conn->state == IMAPC_CONNECTION_STATE_CONNECTING) {
1209 /* input banner */
1210 data = i_stream_get_data(conn->input, &size);
1211 if (size < 3 && memchr(data, '\n', size) == NULL)
1212 return 0;
1213 if (i_memcasecmp(data, "OK ", 3) != 0) {
1214 imapc_connection_input_error(conn,
1215 "Banner doesn't begin with OK: %s",
1216 t_strcut(t_strndup(data, size), '\n'));
1217 return -1;
1218 }
1219 conn->input_callback = imapc_connection_input_banner;
1220 return 1;
1221 }
1222
1223 if ((ret = imapc_connection_read_line(conn, &imap_args)) == 0)
1224 return 0;
1225 else if (ret < 0) {
1226 imapc_connection_input_reset(conn);
1227 return 1;
1228 }
1229 if (!imap_arg_get_atom(&imap_args[0], &name)) {
1230 imapc_connection_input_error(conn, "Invalid untagged reply");
1231 return -1;
1232 }
1233 imap_args++;
1234
1235 if (conn->input_state == IMAPC_INPUT_STATE_UNTAGGED &&
1236 str_to_uint32(name, &conn->cur_num) == 0) {
1237 /* <seq> <event> */
1238 conn->input_state = IMAPC_INPUT_STATE_UNTAGGED_NUM;
1239 if (!imap_arg_get_atom(&imap_args[0], &name)) {
1240 imapc_connection_input_error(conn,
1241 "Invalid untagged reply");
1242 return -1;
1243 }
1244 imap_args++;
1245 }
1246 i_zero(&reply);
1247
1248 if (strcasecmp(name, "OK") == 0) {
1249 if (imapc_connection_handle_imap_resp_text(conn, imap_args,
1250 &reply.resp_text_key,
1251 &reply.resp_text_value) < 0)
1252 return -1;
1253 } else if (strcasecmp(name, "CAPABILITY") == 0) {
1254 value = imap_args_to_str(imap_args);
1255 if (imapc_connection_parse_capability(conn, value) < 0)
1256 return -1;
1257 } else if (strcasecmp(name, "BYE") == 0) {
1258 i_free(conn->disconnect_reason);
1259 conn->disconnect_reason = i_strdup(imap_args_to_str(imap_args));
1260 }
1261
1262 reply.name = name;
1263 reply.num = conn->cur_num;
1264 reply.args = imap_args;
1265 reply.file_args = array_get(&conn->literal_files,
1266 &reply.file_args_count);
1267
1268 if (conn->selected_box != NULL) {
1269 reply.untagged_box_context =
1270 conn->selected_box->untagged_box_context;
1271 }
1272
1273 /* the callback may disconnect and destroy the parser */
1274 parser = conn->parser;
1275 imap_parser_ref(parser);
1276 conn->client->untagged_callback(&reply, conn->client->untagged_context);
1277 imap_parser_unref(&parser);
1278 imapc_connection_input_reset(conn);
1279 return 1;
1280 }
1281
imapc_connection_input_plus(struct imapc_connection * conn)1282 static int imapc_connection_input_plus(struct imapc_connection *conn)
1283 {
1284 struct imapc_command *const *cmds;
1285 unsigned int cmds_count;
1286 const char *line;
1287
1288 if ((line = i_stream_next_line(conn->input)) == NULL)
1289 return 0;
1290
1291 cmds = array_get(&conn->cmd_send_queue, &cmds_count);
1292 if (conn->idle_plus_waiting) {
1293 /* "+ idling" reply for IDLE command */
1294 conn->idle_plus_waiting = FALSE;
1295 conn->idling = TRUE;
1296 /* no timing out while IDLEing */
1297 if (conn->to != NULL && !conn->idle_stopping)
1298 timeout_remove(&conn->to);
1299 } else if (cmds_count > 0 && cmds[0]->wait_for_literal) {
1300 /* reply for literal */
1301 cmds[0]->wait_for_literal = FALSE;
1302 imapc_command_send_more(conn);
1303 } else {
1304 cmds = array_get(&conn->cmd_wait_list, &cmds_count);
1305 if (cmds_count > 0 && cmds[0]->authenticate) {
1306 /* continue AUTHENTICATE */
1307 struct imapc_command_reply reply;
1308
1309 i_zero(&reply);
1310 reply.state = (enum imapc_command_state)IMAPC_COMMAND_STATE_AUTHENTICATE_CONTINUE;
1311 reply.text_full = line;
1312 cmds[0]->callback(&reply, cmds[0]->context);
1313 } else {
1314 imapc_connection_input_error(conn, "Unexpected '+': %s", line);
1315 return -1;
1316 }
1317 }
1318
1319 imapc_connection_input_reset(conn);
1320 return 1;
1321 }
1322
1323 static void
imapc_connection_throttle_shrink_timeout(struct imapc_connection * conn)1324 imapc_connection_throttle_shrink_timeout(struct imapc_connection *conn)
1325 {
1326 if (conn->throttle_msecs <= 1)
1327 conn->throttle_msecs = 0;
1328 else
1329 conn->throttle_msecs = conn->throttle_msecs*3 / 4;
1330
1331 if (conn->throttle_shrink_msecs <= conn->client->set.throttle_set.shrink_min_msecs)
1332 conn->throttle_shrink_msecs = 0;
1333 else
1334 conn->throttle_shrink_msecs = conn->throttle_shrink_msecs*3 / 4;
1335
1336 timeout_remove(&conn->to_throttle_shrink);
1337 if (conn->throttle_shrink_msecs > 0) {
1338 conn->to_throttle_shrink =
1339 timeout_add(conn->throttle_shrink_msecs,
1340 imapc_connection_throttle_shrink_timeout, conn);
1341 }
1342 }
1343
1344 static void
imapc_connection_throttle(struct imapc_connection * conn,const struct imapc_command_reply * reply)1345 imapc_connection_throttle(struct imapc_connection *conn,
1346 const struct imapc_command_reply *reply)
1347 {
1348 timeout_remove(&conn->to_throttle);
1349
1350 /* If GMail returns [THROTTLED], start slowing down commands.
1351 Unfortunately this isn't a nice resp-text-code, but just
1352 appended at the end of the line (although we kind of support
1353 it as resp-text-code also in here if it's uppercased). */
1354 if (strstr(reply->text_full, "[THROTTLED]") != NULL) {
1355 if (conn->throttle_msecs == 0)
1356 conn->throttle_msecs = conn->client->set.throttle_set.init_msecs;
1357 else if (conn->throttle_msecs < conn->last_successful_throttle_msecs)
1358 conn->throttle_msecs = conn->last_successful_throttle_msecs;
1359 else {
1360 conn->throttle_msecs *= 2;
1361 if (conn->throttle_msecs > conn->client->set.throttle_set.max_msecs)
1362 conn->throttle_msecs = conn->client->set.throttle_set.max_msecs;
1363 }
1364 if (conn->throttle_shrink_msecs == 0)
1365 conn->throttle_shrink_msecs = conn->client->set.throttle_set.shrink_min_msecs;
1366 else
1367 conn->throttle_shrink_msecs *= 2;
1368 if (conn->to_throttle_shrink != NULL)
1369 timeout_reset(conn->to_throttle_shrink);
1370 } else {
1371 if (conn->throttle_shrink_msecs > 0 &&
1372 conn->to_throttle_shrink == NULL) {
1373 conn->to_throttle_shrink =
1374 timeout_add(conn->throttle_shrink_msecs,
1375 imapc_connection_throttle_shrink_timeout, conn);
1376 }
1377 conn->last_successful_throttle_msecs = conn->throttle_msecs;
1378 }
1379
1380 if (conn->throttle_msecs > 0) {
1381 conn->throttle_end_timeval = ioloop_timeval;
1382 timeval_add_msecs(&conn->throttle_end_timeval,
1383 conn->throttle_msecs);
1384 conn->throttle_pending = TRUE;
1385 }
1386 }
1387
1388 static void
imapc_command_reply_free(struct imapc_command * cmd,const struct imapc_command_reply * reply)1389 imapc_command_reply_free(struct imapc_command *cmd,
1390 const struct imapc_command_reply *reply)
1391 {
1392 cmd->callback(reply, cmd->context);
1393 imapc_command_free(cmd);
1394 }
1395
imapc_connection_input_tagged(struct imapc_connection * conn)1396 static int imapc_connection_input_tagged(struct imapc_connection *conn)
1397 {
1398 struct imapc_command *const *cmds, *cmd = NULL;
1399 unsigned int i, count;
1400 char *line, *linep;
1401 const char *p;
1402 struct imapc_command_reply reply;
1403
1404 line = i_stream_next_line(conn->input);
1405 if (line == NULL)
1406 return 0;
1407 /* make sure reply texts stays valid if input stream gets freed */
1408 line = t_strdup_noconst(line);
1409
1410 i_zero(&reply);
1411
1412 linep = strchr(line, ' ');
1413 if (linep == NULL)
1414 reply.text_full = "";
1415 else {
1416 *linep = '\0';
1417 reply.text_full = linep + 1;
1418 }
1419
1420 if (strcasecmp(line, "ok") == 0)
1421 reply.state = IMAPC_COMMAND_STATE_OK;
1422 else if (strcasecmp(line, "no") == 0)
1423 reply.state = IMAPC_COMMAND_STATE_NO;
1424 else if (strcasecmp(line, "bad") == 0)
1425 reply.state = IMAPC_COMMAND_STATE_BAD;
1426 else {
1427 imapc_connection_input_error(conn,
1428 "Invalid state in tagged reply: %u %s %s",
1429 conn->cur_tag, line, reply.text_full);
1430 return -1;
1431 }
1432
1433 if (reply.text_full[0] == '[') {
1434 /* get resp-text */
1435 if (imapc_connection_handle_resp_text(conn, reply.text_full,
1436 &reply.resp_text_key,
1437 &reply.resp_text_value) < 0)
1438 return -1;
1439
1440 p = i_strchr_to_next(reply.text_full, ']');
1441 i_assert(p != NULL);
1442 reply.text_without_resp = p;
1443 if (reply.text_without_resp[0] == ' ')
1444 reply.text_without_resp++;
1445 } else {
1446 reply.text_without_resp = reply.text_full;
1447 }
1448 /* if we've pipelined multiple commands, handle [THROTTLED] reply
1449 from only one of them */
1450 if (!conn->throttle_pending)
1451 imapc_connection_throttle(conn, &reply);
1452
1453 /* find the command. it's either the first command in send queue
1454 (literal failed) or somewhere in wait list. */
1455 cmds = array_get(&conn->cmd_send_queue, &count);
1456 if (count > 0 && cmds[0]->tag == conn->cur_tag) {
1457 cmd = cmds[0];
1458 array_pop_front(&conn->cmd_send_queue);
1459 } else {
1460 cmds = array_get(&conn->cmd_wait_list, &count);
1461 for (i = 0; i < count; i++) {
1462 if (cmds[i]->tag == conn->cur_tag) {
1463 cmd = cmds[i];
1464 array_delete(&conn->cmd_wait_list, i, 1);
1465 break;
1466 }
1467 }
1468 }
1469 if (array_count(&conn->cmd_wait_list) == 0 &&
1470 array_count(&conn->cmd_send_queue) == 0 &&
1471 conn->state == IMAPC_CONNECTION_STATE_DONE && conn->to != NULL)
1472 timeout_remove(&conn->to);
1473
1474 if (cmd == NULL) {
1475 if (seq_range_exists(&conn->aborted_cmd_tags, conn->cur_tag)) {
1476 /* sent command was already aborted - ignore it */
1477 seq_range_array_remove(&conn->aborted_cmd_tags,
1478 conn->cur_tag);
1479 imapc_connection_input_reset(conn);
1480 return 1;
1481 }
1482 imapc_connection_input_error(conn,
1483 "Unknown tag in a reply: %u %s %s",
1484 conn->cur_tag, line, reply.text_full);
1485 return -1;
1486 }
1487 if ((cmd->flags & IMAPC_COMMAND_FLAG_SELECT) != 0)
1488 conn->select_waiting_reply = FALSE;
1489
1490 if (reply.state == IMAPC_COMMAND_STATE_BAD) {
1491 i_error("imapc(%s): Command '%s' failed with BAD: %u %s",
1492 conn->name, imapc_command_get_readable(cmd),
1493 conn->cur_tag, reply.text_full);
1494 imapc_connection_disconnect(conn);
1495 }
1496
1497 if (reply.state == IMAPC_COMMAND_STATE_NO &&
1498 (cmd->flags & IMAPC_COMMAND_FLAG_SELECT) != 0 &&
1499 conn->selected_box != NULL) {
1500 /* EXAMINE/SELECT failed: mailbox is no longer selected */
1501 imapc_connection_unselect(conn->selected_box);
1502 }
1503
1504 if (conn->reconnect_command_count > 0 &&
1505 (cmd->flags & IMAPC_COMMAND_FLAG_RECONNECTED) != 0) {
1506 i_assert(conn->reconnect_command_count > 0);
1507 if (--conn->reconnect_command_count == 0) {
1508 /* we've received replies for all the commands started
1509 before reconnection. if we get disconnected now, we
1510 can safely reconnect without worrying about infinite
1511 reconnect loops. */
1512 if (conn->selected_box != NULL)
1513 conn->selected_box->reconnect_ok = TRUE;
1514 }
1515 }
1516 if (conn->reconnect_command_count == 0) {
1517 /* we've successfully received replies to some commands. */
1518 conn->reconnect_ok = TRUE;
1519 }
1520 imapc_connection_input_reset(conn);
1521 imapc_command_reply_free(cmd, &reply);
1522 imapc_command_send_more(conn);
1523 return 1;
1524 }
1525
imapc_connection_input_one(struct imapc_connection * conn)1526 static int imapc_connection_input_one(struct imapc_connection *conn)
1527 {
1528 const char *tag;
1529 int ret = -1;
1530
1531 if (conn->input_callback != NULL)
1532 return conn->input_callback(conn);
1533
1534 switch (conn->input_state) {
1535 case IMAPC_INPUT_STATE_NONE:
1536 tag = imap_parser_read_word(conn->parser);
1537 if (tag == NULL)
1538 return 0;
1539
1540 if (strcmp(tag, "*") == 0) {
1541 conn->input_state = IMAPC_INPUT_STATE_UNTAGGED;
1542 conn->cur_num = 0;
1543 ret = imapc_connection_input_untagged(conn);
1544 } else if (strcmp(tag, "+") == 0) {
1545 conn->input_state = IMAPC_INPUT_STATE_PLUS;
1546 ret = imapc_connection_input_plus(conn);
1547 } else {
1548 conn->input_state = IMAPC_INPUT_STATE_TAGGED;
1549 if (str_to_uint(tag, &conn->cur_tag) < 0 ||
1550 conn->cur_tag == 0) {
1551 imapc_connection_input_error(conn,
1552 "Invalid command tag: %s", tag);
1553 ret = -1;
1554 } else {
1555 ret = imapc_connection_input_tagged(conn);
1556 }
1557 }
1558 break;
1559 case IMAPC_INPUT_STATE_PLUS:
1560 ret = imapc_connection_input_plus(conn);
1561 break;
1562 case IMAPC_INPUT_STATE_UNTAGGED:
1563 case IMAPC_INPUT_STATE_UNTAGGED_NUM:
1564 ret = imapc_connection_input_untagged(conn);
1565 break;
1566 case IMAPC_INPUT_STATE_TAGGED:
1567 ret = imapc_connection_input_tagged(conn);
1568 break;
1569 }
1570 return ret;
1571 }
1572
imapc_connection_input(struct imapc_connection * conn)1573 static void imapc_connection_input(struct imapc_connection *conn)
1574 {
1575 const char *errstr;
1576 string_t *str;
1577 ssize_t ret = 0;
1578
1579 /* we need to read as much as we can with SSL streams to avoid
1580 hanging */
1581 imapc_connection_ref(conn);
1582 while (conn->input != NULL && (ret = i_stream_read(conn->input)) > 0)
1583 imapc_connection_input_pending(conn);
1584
1585 if (ret < 0 && conn->client->logging_out &&
1586 conn->disconnect_reason != NULL) {
1587 /* expected disconnection */
1588 imapc_connection_disconnect(conn);
1589 } else if (ret < 0) {
1590 /* disconnected or buffer full */
1591 str = t_str_new(128);
1592 if (conn->disconnect_reason != NULL) {
1593 str_printfa(str, "Server disconnected with message: %s",
1594 conn->disconnect_reason);
1595 } else if (ret == -2) {
1596 str_printfa(str, "Server sent too large input "
1597 "(buffer full at %zu)",
1598 i_stream_get_data_size(conn->input));
1599 } else if (conn->ssl_iostream == NULL) {
1600 errstr = conn->input->stream_errno == 0 ? "EOF" :
1601 i_stream_get_error(conn->input);
1602 str_printfa(str, "Server disconnected unexpectedly: %s",
1603 errstr);
1604 } else {
1605 errstr = ssl_iostream_get_last_error(conn->ssl_iostream);
1606 if (errstr == NULL) {
1607 errstr = conn->input->stream_errno == 0 ? "EOF" :
1608 i_stream_get_error(conn->input);
1609 }
1610 str_printfa(str, "Server disconnected unexpectedly: %s",
1611 errstr);
1612 }
1613 imapc_connection_try_reconnect(conn, str_c(str), 0, FALSE);
1614 }
1615 imapc_connection_unref(&conn);
1616 }
1617
imapc_connection_ssl_handshaked(const char ** error_r,void * context)1618 static int imapc_connection_ssl_handshaked(const char **error_r, void *context)
1619 {
1620 struct imapc_connection *conn = context;
1621 const char *error;
1622
1623 if (ssl_iostream_check_cert_validity(conn->ssl_iostream,
1624 conn->client->set.host, &error) == 0) {
1625 if (conn->client->set.debug) {
1626 i_debug("imapc(%s): SSL handshake successful",
1627 conn->name);
1628 }
1629 return 0;
1630 } else if (conn->client->set.ssl_set.allow_invalid_cert) {
1631 if (conn->client->set.debug) {
1632 i_debug("imapc(%s): SSL handshake successful, "
1633 "ignoring invalid certificate: %s",
1634 conn->name, error);
1635 }
1636 return 0;
1637 } else {
1638 *error_r = error;
1639 return -1;
1640 }
1641 }
1642
imapc_connection_ssl_init(struct imapc_connection * conn)1643 static int imapc_connection_ssl_init(struct imapc_connection *conn)
1644 {
1645 const char *error;
1646
1647 if (conn->client->ssl_ctx == NULL) {
1648 i_error("imapc(%s): No SSL context", conn->name);
1649 return -1;
1650 }
1651
1652 if (conn->client->set.debug)
1653 i_debug("imapc(%s): Starting SSL handshake", conn->name);
1654
1655 if (conn->raw_input != conn->input) {
1656 /* recreate rawlog after STARTTLS */
1657 i_stream_ref(conn->raw_input);
1658 o_stream_ref(conn->raw_output);
1659 i_stream_destroy(&conn->input);
1660 o_stream_destroy(&conn->output);
1661 conn->input = conn->raw_input;
1662 conn->output = conn->raw_output;
1663 }
1664
1665 io_remove(&conn->io);
1666 if (io_stream_create_ssl_client(conn->client->ssl_ctx,
1667 conn->client->set.host,
1668 &conn->client->set.ssl_set,
1669 &conn->input, &conn->output,
1670 &conn->ssl_iostream, &error) < 0) {
1671 i_error("imapc(%s): Couldn't initialize SSL client: %s",
1672 conn->name, error);
1673 return -1;
1674 }
1675 conn->io = io_add_istream(conn->input, imapc_connection_input, conn);
1676 ssl_iostream_set_handshake_callback(conn->ssl_iostream,
1677 imapc_connection_ssl_handshaked,
1678 conn);
1679 if (ssl_iostream_handshake(conn->ssl_iostream) < 0) {
1680 i_error("imapc(%s): SSL handshake failed: %s", conn->name,
1681 ssl_iostream_get_last_error(conn->ssl_iostream));
1682 return -1;
1683 }
1684
1685 if (*conn->client->set.rawlog_dir != '\0') {
1686 iostream_rawlog_create(conn->client->set.rawlog_dir,
1687 &conn->input, &conn->output);
1688 }
1689
1690 imap_parser_set_streams(conn->parser, conn->input, NULL);
1691 return 0;
1692 }
1693
imapc_connection_connected(struct imapc_connection * conn)1694 static int imapc_connection_connected(struct imapc_connection *conn)
1695 {
1696 const struct ip_addr *ip = &conn->ips[conn->prev_connect_idx];
1697 struct ip_addr local_ip;
1698 in_port_t local_port;
1699 int err;
1700
1701 i_assert(conn->io == NULL);
1702
1703 err = net_geterror(conn->fd);
1704 if (err != 0) {
1705 imapc_connection_try_reconnect(conn, t_strdup_printf(
1706 "connect(%s, %u) failed: %s",
1707 net_ip2addr(ip), conn->client->set.port,
1708 strerror(err)), conn->client->set.connect_retry_interval_msecs, TRUE);
1709 return -1;
1710 }
1711 if (net_getsockname(conn->fd, &local_ip, &local_port) < 0)
1712 local_port = 0;
1713 i_info("imapc(%s): Connected to %s:%u (local %s:%u)", conn->name,
1714 net_ip2addr(ip), conn->client->set.port,
1715 net_ip2addr(&local_ip), local_port);
1716 conn->io = io_add(conn->fd, IO_READ, imapc_connection_input, conn);
1717 o_stream_set_flush_callback(conn->output, imapc_connection_output,
1718 conn);
1719
1720 if (conn->client->set.ssl_mode == IMAPC_CLIENT_SSL_MODE_IMMEDIATE) {
1721 if (imapc_connection_ssl_init(conn) < 0)
1722 imapc_connection_disconnect(conn);
1723 }
1724 return imapc_connection_output(conn);
1725 }
1726
imapc_connection_timeout(struct imapc_connection * conn)1727 static void imapc_connection_timeout(struct imapc_connection *conn)
1728 {
1729 const struct ip_addr *ip = &conn->ips[conn->prev_connect_idx];
1730 const char *errstr;
1731 bool connect_error = FALSE;
1732
1733 switch (conn->state) {
1734 case IMAPC_CONNECTION_STATE_CONNECTING:
1735 errstr = t_strdup_printf("connect(%s, %u) timed out after %u seconds",
1736 net_ip2addr(ip), conn->client->set.port,
1737 conn->client->set.connect_timeout_msecs/1000);
1738 connect_error = TRUE;
1739 break;
1740 case IMAPC_CONNECTION_STATE_AUTHENTICATING:
1741 errstr = t_strdup_printf("Authentication timed out after %u seconds",
1742 conn->client->set.connect_timeout_msecs/1000);
1743 break;
1744 default:
1745 i_unreached();
1746 }
1747 imapc_connection_try_reconnect(conn, errstr, 0, connect_error);
1748 }
1749
1750 static void
imapc_noop_callback(const struct imapc_command_reply * reply ATTR_UNUSED,void * context ATTR_UNUSED)1751 imapc_noop_callback(const struct imapc_command_reply *reply ATTR_UNUSED,
1752 void *context ATTR_UNUSED)
1753 {
1754 }
1755
1756 static void
imapc_reidle_callback(const struct imapc_command_reply * reply ATTR_UNUSED,void * context)1757 imapc_reidle_callback(const struct imapc_command_reply *reply ATTR_UNUSED,
1758 void *context)
1759 {
1760 struct imapc_connection *conn = context;
1761
1762 imapc_connection_idle(conn);
1763 }
1764
imapc_connection_reset_idle(struct imapc_connection * conn)1765 static void imapc_connection_reset_idle(struct imapc_connection *conn)
1766 {
1767 struct imapc_command *cmd;
1768
1769 if (conn->idling)
1770 cmd = imapc_connection_cmd(conn, imapc_reidle_callback, conn);
1771 else if (array_count(&conn->cmd_wait_list) == 0)
1772 cmd = imapc_connection_cmd(conn, imapc_noop_callback, NULL);
1773 else {
1774 /* IMAP command reply is taking a long time */
1775 return;
1776 }
1777 imapc_command_send(cmd, "NOOP");
1778 }
1779
imapc_connection_connect_next_ip(struct imapc_connection * conn)1780 static void imapc_connection_connect_next_ip(struct imapc_connection *conn)
1781 {
1782 const struct ip_addr *ip = NULL;
1783 unsigned int i;
1784 int fd;
1785
1786 i_assert(conn->client->set.max_idle_time > 0);
1787
1788 for (i = 0; i<conn->ips_count;) {
1789 conn->prev_connect_idx = (conn->prev_connect_idx+1) % conn->ips_count;
1790 ip = &conn->ips[conn->prev_connect_idx];
1791 fd = net_connect_ip(ip, conn->client->set.port, NULL);
1792 if (fd != -1)
1793 break;
1794
1795 /* failed to connect to one of the IPs immediately
1796 (e.g. IPv6 address without connectivity). try all IPs
1797 before failing completely. */
1798 i_error("net_connect_ip(%s:%u) failed: %m",
1799 net_ip2addr(ip), conn->client->set.port);
1800 if (conn->prev_connect_idx+1 == conn->ips_count) {
1801 imapc_connection_try_reconnect(conn, "No more IP address(es) to try",
1802 conn->client->set.connect_retry_interval_msecs, TRUE);
1803 return;
1804 }
1805 }
1806
1807 i_assert(ip != NULL);
1808
1809 conn->fd = fd;
1810 conn->input = conn->raw_input =
1811 i_stream_create_fd(fd, conn->client->set.max_line_length);
1812 conn->output = conn->raw_output = o_stream_create_fd(fd, SIZE_MAX);
1813 o_stream_set_no_error_handling(conn->output, TRUE);
1814
1815 if (*conn->client->set.rawlog_dir != '\0' &&
1816 conn->client->set.ssl_mode != IMAPC_CLIENT_SSL_MODE_IMMEDIATE) {
1817 iostream_rawlog_create(conn->client->set.rawlog_dir,
1818 &conn->input, &conn->output);
1819 }
1820
1821 o_stream_set_flush_pending(conn->output, TRUE);
1822 o_stream_set_flush_callback(conn->output, imapc_connection_connected,
1823 conn);
1824 conn->parser = imap_parser_create(conn->input, NULL,
1825 conn->client->set.max_line_length);
1826 conn->to = timeout_add(conn->client->set.connect_timeout_msecs,
1827 imapc_connection_timeout, conn);
1828 conn->to_output = timeout_add(conn->client->set.max_idle_time*1000,
1829 imapc_connection_reset_idle, conn);
1830 if (conn->client->set.debug) {
1831 i_debug("imapc(%s): Connecting to %s:%u", conn->name,
1832 net_ip2addr(ip), conn->client->set.port);
1833 }
1834 }
1835
1836 static void
imapc_connection_dns_callback(const struct dns_lookup_result * result,struct imapc_connection * conn)1837 imapc_connection_dns_callback(const struct dns_lookup_result *result,
1838 struct imapc_connection *conn)
1839 {
1840 conn->dns_lookup = NULL;
1841
1842 if (result->ret != 0) {
1843 i_error("imapc(%s): dns_lookup(%s) failed: %s",
1844 conn->name, conn->client->set.host, result->error);
1845 imapc_connection_set_disconnected(conn);
1846 return;
1847 }
1848
1849 i_assert(result->ips_count > 0);
1850 conn->ips_count = result->ips_count;
1851 conn->ips = i_new(struct ip_addr, conn->ips_count);
1852 memcpy(conn->ips, result->ips, sizeof(*conn->ips) * conn->ips_count);
1853 conn->prev_connect_idx = conn->ips_count - 1;
1854
1855 imapc_connection_connect_next_ip(conn);
1856 }
1857
imapc_connection_connect(struct imapc_connection * conn)1858 void imapc_connection_connect(struct imapc_connection *conn)
1859 {
1860 struct dns_lookup_settings dns_set;
1861 struct ip_addr ip, *ips;
1862 unsigned int ips_count;
1863 int ret;
1864
1865 if (conn->fd != -1 || conn->dns_lookup != NULL)
1866 return;
1867 if (conn->reconnect_waiting) {
1868 /* wait for the reconnection delay to finish before
1869 doing anything. */
1870 return;
1871 }
1872
1873 conn->reconnecting = FALSE;
1874 /* if we get disconnected before we've finished all the pending
1875 commands, don't reconnect */
1876 conn->reconnect_command_count = array_count(&conn->cmd_wait_list) +
1877 array_count(&conn->cmd_send_queue);
1878
1879 imapc_connection_input_reset(conn);
1880 conn->last_connect = ioloop_timeval;
1881
1882 if (conn->client->set.debug) {
1883 i_debug("imapc(%s): Looking up IP address "
1884 "(reconnect_ok=%s, last_connect=%ld)", conn->name,
1885 (conn->reconnect_ok ? "true" : "false"),
1886 (long)conn->last_connect.tv_sec);
1887 }
1888
1889 i_zero(&dns_set);
1890 dns_set.dns_client_socket_path =
1891 conn->client->set.dns_client_socket_path;
1892 dns_set.timeout_msecs = conn->client->set.connect_timeout_msecs;
1893 dns_set.event_parent = conn->client->event;
1894
1895 imapc_connection_set_state(conn, IMAPC_CONNECTION_STATE_CONNECTING);
1896 if (conn->ips_count > 0) {
1897 /* do nothing */
1898 } else if (net_addr2ip(conn->client->set.host, &ip) == 0) {
1899 conn->ips_count = 1;
1900 conn->ips = i_new(struct ip_addr, conn->ips_count);
1901 conn->ips[0] = ip;
1902 } else if (*dns_set.dns_client_socket_path == '\0') {
1903 ret = net_gethostbyname(conn->client->set.host,
1904 &ips, &ips_count);
1905 if (ret != 0) {
1906 i_error("imapc(%s): net_gethostbyname(%s) failed: %s",
1907 conn->name, conn->client->set.host,
1908 net_gethosterror(ret));
1909 imapc_connection_set_disconnected(conn);
1910 return;
1911 }
1912 conn->ips_count = ips_count;
1913 conn->ips = i_new(struct ip_addr, ips_count);
1914 memcpy(conn->ips, ips, ips_count * sizeof(*ips));
1915 } else {
1916 (void)dns_lookup(conn->client->set.host, &dns_set,
1917 imapc_connection_dns_callback, conn,
1918 &conn->dns_lookup);
1919 return;
1920 }
1921 imapc_connection_connect_next_ip(conn);
1922 }
1923
imapc_connection_input_pending(struct imapc_connection * conn)1924 void imapc_connection_input_pending(struct imapc_connection *conn)
1925 {
1926 int ret = 1;
1927
1928 if (conn->input == NULL)
1929 return;
1930
1931 if (conn->to != NULL && !conn->idle_stopping)
1932 timeout_reset(conn->to);
1933
1934 o_stream_cork(conn->output);
1935 while (ret > 0 && conn->input != NULL) {
1936 T_BEGIN {
1937 ret = imapc_connection_input_one(conn);
1938 } T_END;
1939 }
1940
1941 if (conn->output != NULL)
1942 o_stream_uncork(conn->output);
1943 }
1944
1945 static struct imapc_command *
imapc_command_begin(imapc_command_callback_t * callback,void * context)1946 imapc_command_begin(imapc_command_callback_t *callback, void *context)
1947 {
1948 struct imapc_command *cmd;
1949 pool_t pool;
1950
1951 i_assert(callback != NULL);
1952
1953 pool = pool_alloconly_create("imapc command", 2048);
1954 cmd = p_new(pool, struct imapc_command, 1);
1955 cmd->pool = pool;
1956 cmd->callback = callback;
1957 cmd->context = context;
1958
1959 /* use a globally unique tag counter so looking at rawlogs is
1960 somewhat easier */
1961 if (++imapc_client_cmd_tag_counter == 0)
1962 imapc_client_cmd_tag_counter++;
1963 cmd->tag = imapc_client_cmd_tag_counter;
1964 return cmd;
1965 }
1966
imapc_command_free(struct imapc_command * cmd)1967 static void imapc_command_free(struct imapc_command *cmd)
1968 {
1969 struct imapc_command_stream *stream;
1970
1971 if (array_is_created(&cmd->streams)) {
1972 array_foreach_modifiable(&cmd->streams, stream)
1973 i_stream_unref(&stream->input);
1974 }
1975 pool_unref(&cmd->pool);
1976 }
1977
imapc_command_get_tag(struct imapc_command * cmd)1978 const char *imapc_command_get_tag(struct imapc_command *cmd)
1979 {
1980 return t_strdup_printf("%u", cmd->tag);
1981 }
1982
imapc_command_abort(struct imapc_command ** _cmd)1983 void imapc_command_abort(struct imapc_command **_cmd)
1984 {
1985 struct imapc_command *cmd = *_cmd;
1986
1987 *_cmd = NULL;
1988 imapc_command_free(cmd);
1989 }
1990
imapc_command_timeout(struct imapc_connection * conn)1991 static void imapc_command_timeout(struct imapc_connection *conn)
1992 {
1993 struct imapc_command *const *cmds;
1994 unsigned int count;
1995
1996 cmds = array_get(&conn->cmd_wait_list, &count);
1997 i_assert(count > 0);
1998
1999 imapc_connection_try_reconnect(conn, t_strdup_printf(
2000 "Command '%s' timed out", imapc_command_get_readable(cmds[0])), 0, FALSE);
2001 }
2002
2003 static bool
parse_sync_literal(const unsigned char * data,unsigned int pos,unsigned int * value_r)2004 parse_sync_literal(const unsigned char *data, unsigned int pos,
2005 unsigned int *value_r)
2006 {
2007 unsigned int value = 0, mul = 1;
2008
2009 /* data should contain "{size}\r\n" and pos points after \n */
2010 if (pos <= 4 || data[pos-1] != '\n' || data[pos-2] != '\r' ||
2011 data[pos-3] != '}' || !i_isdigit(data[pos-4]))
2012 return FALSE;
2013 pos -= 4;
2014
2015 do {
2016 value += (data[pos] - '0') * mul;
2017 mul = mul*10;
2018 pos--;
2019 } while (pos > 0 && i_isdigit(data[pos]));
2020
2021 if (pos == 0 || data[pos] != '{')
2022 return FALSE;
2023
2024 *value_r = value;
2025 return TRUE;
2026 }
2027
imapc_command_send_finished(struct imapc_connection * conn,struct imapc_command * cmd)2028 static void imapc_command_send_finished(struct imapc_connection *conn,
2029 struct imapc_command *cmd)
2030 {
2031 struct imapc_command *const *cmdp;
2032
2033 i_assert(conn->to != NULL);
2034
2035 if (cmd->idle)
2036 conn->idle_plus_waiting = TRUE;
2037 cmd->sent = TRUE;
2038
2039 /* everything sent. move command to wait list. */
2040 cmdp = array_front(&conn->cmd_send_queue);
2041 i_assert(*cmdp == cmd);
2042 array_pop_front(&conn->cmd_send_queue);
2043 array_push_back(&conn->cmd_wait_list, &cmd);
2044
2045 /* send the next command in queue */
2046 imapc_command_send_more(conn);
2047 }
2048
2049 static struct imapc_command_stream *
imapc_command_get_sending_stream(struct imapc_command * cmd)2050 imapc_command_get_sending_stream(struct imapc_command *cmd)
2051 {
2052 struct imapc_command_stream *stream;
2053
2054 if (!array_is_created(&cmd->streams) || array_count(&cmd->streams) == 0)
2055 return NULL;
2056
2057 stream = array_front_modifiable(&cmd->streams);
2058 if (stream->pos != cmd->send_pos)
2059 return NULL;
2060 return stream;
2061 }
2062
imapc_command_try_send_stream(struct imapc_connection * conn,struct imapc_command * cmd)2063 static int imapc_command_try_send_stream(struct imapc_connection *conn,
2064 struct imapc_command *cmd)
2065 {
2066 struct imapc_command_stream *stream;
2067 enum ostream_send_istream_result res;
2068
2069 stream = imapc_command_get_sending_stream(cmd);
2070 if (stream == NULL)
2071 return -2;
2072
2073 /* we're sending the stream now */
2074 o_stream_set_max_buffer_size(conn->output, 0);
2075 res = o_stream_send_istream(conn->output, stream->input);
2076 o_stream_set_max_buffer_size(conn->output, SIZE_MAX);
2077
2078 switch (res) {
2079 case OSTREAM_SEND_ISTREAM_RESULT_FINISHED:
2080 break;
2081 case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT:
2082 i_unreached();
2083 case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT:
2084 i_assert(stream->input->v_offset < stream->size);
2085 return 0;
2086 case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT:
2087 i_error("imapc: read(%s) failed: %s",
2088 i_stream_get_name(stream->input),
2089 i_stream_get_error(stream->input));
2090 return -1;
2091 case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT:
2092 /* disconnected */
2093 return -1;
2094 }
2095 i_assert(stream->input->v_offset == stream->size);
2096
2097 /* finished with the stream */
2098 i_stream_unref(&stream->input);
2099 array_pop_front(&cmd->streams);
2100
2101 i_assert(cmd->send_pos != cmd->data->used);
2102 return 1;
2103 }
2104
imapc_connection_set_selecting(struct imapc_client_mailbox * box)2105 static void imapc_connection_set_selecting(struct imapc_client_mailbox *box)
2106 {
2107 struct imapc_connection *conn = box->conn;
2108
2109 i_assert(conn->selecting_box == NULL);
2110
2111 if (conn->selected_box != NULL &&
2112 (conn->capabilities & IMAPC_CAPABILITY_QRESYNC) != 0) {
2113 /* server will send a [CLOSED] once selected mailbox is
2114 closed */
2115 conn->selecting_box = box;
2116 } else {
2117 /* we'll have to assume that all the future untagged messages
2118 are for the mailbox we're selecting */
2119 conn->selected_box = box;
2120 }
2121 conn->select_waiting_reply = TRUE;
2122 }
2123
imapc_connection_is_throttled(struct imapc_connection * conn)2124 static bool imapc_connection_is_throttled(struct imapc_connection *conn)
2125 {
2126 timeout_remove(&conn->to_throttle);
2127
2128 if (conn->throttle_msecs == 0) {
2129 /* we haven't received [THROTTLED] recently */
2130 return FALSE;
2131 }
2132 if (array_count(&conn->cmd_wait_list) > 0) {
2133 /* wait until we have received the existing commands' tagged
2134 replies to see if we're still throttled */
2135 return TRUE;
2136 }
2137 if (timeval_cmp(&ioloop_timeval, &conn->throttle_end_timeval) >= 0) {
2138 /* we reached the throttle timeout - send the next command */
2139 conn->throttle_pending = FALSE;
2140 return FALSE;
2141 }
2142
2143 /* we're still being throttled - wait for it to end */
2144 conn->to_throttle = timeout_add_absolute(&conn->throttle_end_timeval,
2145 imapc_command_send_more, conn);
2146 return TRUE;
2147 }
2148
imapc_command_send_more(struct imapc_connection * conn)2149 static void imapc_command_send_more(struct imapc_connection *conn)
2150 {
2151 struct imapc_command *const *cmds, *cmd;
2152 struct imapc_command_reply reply;
2153 const unsigned char *p, *data;
2154 unsigned int count, size;
2155 size_t seek_pos, start_pos, end_pos;
2156 int ret;
2157
2158 if (imapc_connection_is_throttled(conn))
2159 return;
2160
2161 cmds = array_get(&conn->cmd_send_queue, &count);
2162 if (count == 0)
2163 return;
2164 cmd = cmds[0];
2165
2166 if ((cmd->flags & IMAPC_COMMAND_FLAG_PRELOGIN) == 0 &&
2167 conn->state != IMAPC_CONNECTION_STATE_DONE) {
2168 /* wait until we're fully connected */
2169 return;
2170 }
2171 if ((cmd->flags & IMAPC_COMMAND_FLAG_LOGOUT) != 0 &&
2172 array_count(&conn->cmd_wait_list) > 0) {
2173 /* wait until existing commands have finished */
2174 return;
2175 }
2176 if (conn->select_waiting_reply) {
2177 /* wait for SELECT to finish */
2178 return;
2179 }
2180 if (cmd->wait_for_literal) {
2181 /* wait until we received '+' */
2182 return;
2183 }
2184
2185 i_assert(cmd->send_pos < cmd->data->used);
2186
2187 if (cmd->box == NULL) {
2188 /* non-mailbox command */
2189 } else if (cmd->send_pos == 0 &&
2190 (cmd->flags & IMAPC_COMMAND_FLAG_SELECT) != 0) {
2191 /* SELECT/EXAMINE command */
2192 imapc_connection_set_selecting(cmd->box);
2193 } else if (!imapc_client_mailbox_is_opened(cmd->box)) {
2194 if (cmd->box->reconnecting) {
2195 /* wait for SELECT/EXAMINE */
2196 return;
2197 }
2198 /* shouldn't normally happen */
2199 i_zero(&reply);
2200 reply.text_without_resp = reply.text_full = "Mailbox not open";
2201 reply.state = IMAPC_COMMAND_STATE_DISCONNECTED;
2202
2203 array_pop_front(&conn->cmd_send_queue);
2204 imapc_command_reply_free(cmd, &reply);
2205 imapc_command_send_more(conn);
2206 return;
2207 }
2208
2209 /* add timeout for commands if there's not one yet
2210 (pre-login has its own timeout) */
2211 if ((cmd->flags & IMAPC_COMMAND_FLAG_LOGOUT) != 0) {
2212 /* LOGOUT has a shorter timeout */
2213 timeout_remove(&conn->to);
2214 conn->to = timeout_add(IMAPC_LOGOUT_TIMEOUT_MSECS,
2215 imapc_command_timeout, conn);
2216 } else if (conn->to == NULL) {
2217 conn->to = timeout_add(conn->client->set.cmd_timeout_msecs,
2218 imapc_command_timeout, conn);
2219 }
2220
2221 timeout_reset(conn->to_output);
2222 if ((ret = imapc_command_try_send_stream(conn, cmd)) == 0)
2223 return;
2224 if (ret == -1) {
2225 i_zero(&reply);
2226 reply.text_without_resp = reply.text_full = "Mailbox not open";
2227 reply.state = IMAPC_COMMAND_STATE_DISCONNECTED;
2228
2229 array_pop_front(&conn->cmd_send_queue);
2230 imapc_command_reply_free(cmd, &reply);
2231 imapc_command_send_more(conn);
2232 return;
2233 }
2234
2235 seek_pos = cmd->send_pos;
2236 if (seek_pos != 0 && ret == -2) {
2237 /* skip over the literal. we can also get here from
2238 AUTHENTICATE command, which doesn't use a literal */
2239 if (parse_sync_literal(cmd->data->data, seek_pos, &size)) {
2240 seek_pos += size;
2241 i_assert(seek_pos <= cmd->data->used);
2242 }
2243 }
2244
2245 do {
2246 start_pos = seek_pos;
2247 p = memchr(CONST_PTR_OFFSET(cmd->data->data, seek_pos), '\n',
2248 cmd->data->used - seek_pos);
2249 i_assert(p != NULL);
2250
2251 seek_pos = p - (const unsigned char *)cmd->data->data + 1;
2252 /* keep going for LITERAL+ command */
2253 } while (start_pos + 3 < seek_pos &&
2254 p[-1] == '\r' && p[-2] == '}' && p[-3] == '+');
2255 end_pos = seek_pos;
2256
2257 data = CONST_PTR_OFFSET(cmd->data->data, cmd->send_pos);
2258 size = end_pos - cmd->send_pos;
2259 o_stream_nsend(conn->output, data, size);
2260 cmd->send_pos = end_pos;
2261
2262 if (cmd->send_pos == cmd->data->used) {
2263 i_assert(!array_is_created(&cmd->streams) ||
2264 array_count(&cmd->streams) == 0);
2265 imapc_command_send_finished(conn, cmd);
2266 } else {
2267 cmd->wait_for_literal = TRUE;
2268 }
2269 }
2270
imapc_connection_send_idle_done(struct imapc_connection * conn)2271 static void imapc_connection_send_idle_done(struct imapc_connection *conn)
2272 {
2273 if ((conn->idling || conn->idle_plus_waiting) && !conn->idle_stopping) {
2274 conn->idle_stopping = TRUE;
2275 o_stream_nsend_str(conn->output, "DONE\r\n");
2276 if (conn->to == NULL) {
2277 conn->to = timeout_add(conn->client->set.cmd_timeout_msecs,
2278 imapc_command_timeout, conn);
2279 }
2280 }
2281 }
2282
imapc_connection_cmd_send(struct imapc_command * cmd)2283 static void imapc_connection_cmd_send(struct imapc_command *cmd)
2284 {
2285 struct imapc_connection *conn = cmd->conn;
2286 struct imapc_command *const *cmds;
2287 unsigned int i, count;
2288
2289 imapc_connection_send_idle_done(conn);
2290
2291 i_assert((cmd->flags & IMAPC_COMMAND_FLAG_RECONNECTED) == 0);
2292
2293 if ((cmd->flags & IMAPC_COMMAND_FLAG_PRELOGIN) != 0 &&
2294 conn->state == IMAPC_CONNECTION_STATE_AUTHENTICATING) {
2295 /* pre-login commands get inserted before everything else */
2296 array_push_front(&conn->cmd_send_queue, &cmd);
2297 imapc_command_send_more(conn);
2298 return;
2299 }
2300
2301 /* add the command just before retried commands */
2302 cmds = array_get(&conn->cmd_send_queue, &count);
2303 for (i = count; i > 0; i--) {
2304 if ((cmds[i-1]->flags & IMAPC_COMMAND_FLAG_RECONNECTED) == 0)
2305 break;
2306 }
2307 array_insert(&conn->cmd_send_queue, i, &cmd, 1);
2308 imapc_command_send_more(conn);
2309 }
2310
imapc_connection_output(struct imapc_connection * conn)2311 static int imapc_connection_output(struct imapc_connection *conn)
2312 {
2313 struct imapc_command *const *cmds;
2314 unsigned int count;
2315 int ret;
2316
2317 if (conn->to != NULL)
2318 timeout_reset(conn->to);
2319
2320 if ((ret = o_stream_flush(conn->output)) < 0)
2321 return 1;
2322
2323 imapc_connection_ref(conn);
2324 cmds = array_get(&conn->cmd_send_queue, &count);
2325 if (count > 0) {
2326 if (imapc_command_get_sending_stream(cmds[0]) != NULL &&
2327 !cmds[0]->wait_for_literal) {
2328 /* we're sending a stream. send more. */
2329 imapc_command_send_more(conn);
2330 }
2331 }
2332 imapc_connection_unref(&conn);
2333 return ret;
2334 }
2335
2336 struct imapc_command *
imapc_connection_cmd(struct imapc_connection * conn,imapc_command_callback_t * callback,void * context)2337 imapc_connection_cmd(struct imapc_connection *conn,
2338 imapc_command_callback_t *callback, void *context)
2339 {
2340 struct imapc_command *cmd;
2341
2342 cmd = imapc_command_begin(callback, context);
2343 cmd->conn = conn;
2344 return cmd;
2345 }
2346
imapc_command_set_flags(struct imapc_command * cmd,enum imapc_command_flags flags)2347 void imapc_command_set_flags(struct imapc_command *cmd,
2348 enum imapc_command_flags flags)
2349 {
2350 cmd->flags = flags;
2351 }
2352
imapc_command_set_mailbox(struct imapc_command * cmd,struct imapc_client_mailbox * box)2353 void imapc_command_set_mailbox(struct imapc_command *cmd,
2354 struct imapc_client_mailbox *box)
2355 {
2356 cmd->box = box;
2357 }
2358
imapc_command_connection_is_selected(struct imapc_command * cmd)2359 bool imapc_command_connection_is_selected(struct imapc_command *cmd)
2360 {
2361 return cmd->conn->selected_box != NULL ||
2362 cmd->conn->selecting_box != NULL;
2363 }
2364
imapc_command_send(struct imapc_command * cmd,const char * cmd_str)2365 void imapc_command_send(struct imapc_command *cmd, const char *cmd_str)
2366 {
2367 size_t len = strlen(cmd_str);
2368
2369 cmd->data = str_new(cmd->pool, 6 + len + 2);
2370 str_printfa(cmd->data, "%u %s\r\n", cmd->tag, cmd_str);
2371 imapc_connection_cmd_send(cmd);
2372 }
2373
imapc_command_sendf(struct imapc_command * cmd,const char * cmd_fmt,...)2374 void imapc_command_sendf(struct imapc_command *cmd, const char *cmd_fmt, ...)
2375 {
2376 va_list args;
2377
2378 va_start(args, cmd_fmt);
2379 imapc_command_sendvf(cmd, cmd_fmt, args);
2380 va_end(args);
2381 }
2382
imapc_command_sendvf(struct imapc_command * cmd,const char * cmd_fmt,va_list args)2383 void imapc_command_sendvf(struct imapc_command *cmd,
2384 const char *cmd_fmt, va_list args)
2385 {
2386 unsigned int i;
2387
2388 cmd->data = str_new(cmd->pool, 128);
2389 str_printfa(cmd->data, "%u ", cmd->tag);
2390
2391 for (i = 0; cmd_fmt[i] != '\0'; i++) {
2392 if (cmd_fmt[i] != '%') {
2393 str_append_c(cmd->data, cmd_fmt[i]);
2394 continue;
2395 }
2396
2397 switch (cmd_fmt[++i]) {
2398 case '\0':
2399 i_unreached();
2400 case 'u': {
2401 unsigned int arg = va_arg(args, unsigned int);
2402
2403 str_printfa(cmd->data, "%u", arg);
2404 break;
2405 }
2406 case 'p': {
2407 struct istream *input = va_arg(args, struct istream *);
2408 struct imapc_command_stream *s;
2409 uoff_t size;
2410
2411 if (!array_is_created(&cmd->streams))
2412 p_array_init(&cmd->streams, cmd->pool, 2);
2413 if (i_stream_get_size(input, TRUE, &size) < 0)
2414 size = 0;
2415 str_printfa(cmd->data, "{%"PRIuUOFF_T"}\r\n", size);
2416 s = array_append_space(&cmd->streams);
2417 s->pos = str_len(cmd->data);
2418 s->size = size;
2419 s->input = input;
2420 i_stream_ref(input);
2421 break;
2422 }
2423 case 's': {
2424 const char *arg = va_arg(args, const char *);
2425
2426 if (!need_literal(arg))
2427 imap_append_quoted(cmd->data, arg);
2428 else if ((cmd->conn->capabilities &
2429 IMAPC_CAPABILITY_LITERALPLUS) != 0) {
2430 str_printfa(cmd->data, "{%zu+}\r\n%s",
2431 strlen(arg), arg);
2432 } else {
2433 str_printfa(cmd->data, "{%zu}\r\n%s",
2434 strlen(arg), arg);
2435 }
2436 break;
2437 }
2438 case '1': {
2439 /* %1s - no quoting */
2440 const char *arg = va_arg(args, const char *);
2441
2442 i++;
2443 i_assert(cmd_fmt[i] == 's');
2444 str_append(cmd->data, arg);
2445 break;
2446 }
2447 }
2448 }
2449 str_append(cmd->data, "\r\n");
2450
2451 imapc_connection_cmd_send(cmd);
2452 }
2453
2454 enum imapc_connection_state
imapc_connection_get_state(struct imapc_connection * conn)2455 imapc_connection_get_state(struct imapc_connection *conn)
2456 {
2457 return conn->state;
2458 }
2459
2460 enum imapc_capability
imapc_connection_get_capabilities(struct imapc_connection * conn)2461 imapc_connection_get_capabilities(struct imapc_connection *conn)
2462 {
2463 return conn->capabilities;
2464 }
2465
imapc_connection_unselect(struct imapc_client_mailbox * box)2466 void imapc_connection_unselect(struct imapc_client_mailbox *box)
2467 {
2468 struct imapc_connection *conn = box->conn;
2469
2470 if (conn->selected_box != NULL || conn->selecting_box != NULL) {
2471 i_assert(conn->selected_box == box ||
2472 conn->selecting_box == box);
2473
2474 conn->selected_box = NULL;
2475 conn->selecting_box = NULL;
2476 }
2477 imapc_connection_send_idle_done(conn);
2478 imapc_connection_abort_commands(conn, box, FALSE);
2479 }
2480
2481 struct imapc_client_mailbox *
imapc_connection_get_mailbox(struct imapc_connection * conn)2482 imapc_connection_get_mailbox(struct imapc_connection *conn)
2483 {
2484 if (conn->selecting_box != NULL)
2485 return conn->selecting_box;
2486 return conn->selected_box;
2487 }
2488
2489 static void
imapc_connection_idle_callback(const struct imapc_command_reply * reply ATTR_UNUSED,void * context)2490 imapc_connection_idle_callback(const struct imapc_command_reply *reply ATTR_UNUSED,
2491 void *context)
2492 {
2493 struct imapc_connection *conn = context;
2494
2495 conn->idling = FALSE;
2496 conn->idle_plus_waiting = FALSE;
2497 conn->idle_stopping = FALSE;
2498 }
2499
imapc_connection_idle(struct imapc_connection * conn)2500 void imapc_connection_idle(struct imapc_connection *conn)
2501 {
2502 struct imapc_command *cmd;
2503
2504 if (array_count(&conn->cmd_send_queue) != 0 ||
2505 array_count(&conn->cmd_wait_list) != 0 ||
2506 conn->idling || conn->idle_plus_waiting ||
2507 (conn->capabilities & IMAPC_CAPABILITY_IDLE) == 0)
2508 return;
2509
2510 cmd = imapc_connection_cmd(conn, imapc_connection_idle_callback, conn);
2511 cmd->idle = TRUE;
2512 imapc_command_send(cmd, "IDLE");
2513 }
2514