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