52
49
struct imapc_client_mailbox *box;
54
ARRAY_DEFINE(streams, struct imapc_command_stream);
51
ARRAY(struct imapc_command_stream) streams;
56
53
imapc_command_callback_t *callback;
95
93
struct imapc_client_mailbox *selecting_box, *selected_box;
96
94
enum imapc_connection_state state;
95
char *disconnect_reason;
98
97
enum imapc_capability capabilities;
99
98
char **capabilities_list;
110
109
struct ip_addr *ips;
112
111
struct imapc_connection_literal literal;
113
ARRAY_DEFINE(literal_files, struct imapc_arg_file);
112
ARRAY(struct imapc_arg_file) literal_files;
115
114
unsigned int idling:1;
116
115
unsigned int idle_stopping:1;
117
116
unsigned int idle_plus_waiting:1;
118
unsigned int handshake_failed:1;
121
119
static void imapc_connection_capability_cb(const struct imapc_command_reply *reply,
162
160
if (--conn->refcount > 0)
163
i_assert(conn->disconnect_reason == NULL);
165
165
if (conn->capabilities_list != NULL)
166
166
p_strsplit_free(default_pool, conn->capabilities_list);
167
167
array_free(&conn->cmd_send_queue);
187
187
conn->to = io_loop_move_timeout(&conn->to);
188
188
if (conn->output != NULL)
189
189
o_stream_switch_ioloop(conn->output);
190
if (conn->dns_lookup != NULL)
191
dns_lookup_switch_ioloop(conn->dns_lookup);
191
193
if (conn->client->ioloop == NULL && conn->to_output != NULL) {
192
194
/* we're only once moving the to_output to the main ioloop,
302
304
case IMAPC_CONNECTION_STATE_DISCONNECTED:
303
305
memset(&reply, 0, sizeof(reply));
304
306
reply.state = IMAPC_COMMAND_STATE_DISCONNECTED;
305
reply.text_without_resp = reply.text_full =
306
"Disconnected from server";
307
reply.text_full = "Disconnected from server";
308
if (conn->disconnect_reason != NULL) {
309
reply.text_full = t_strdup_printf("%s: %s",
310
reply.text_full, conn->disconnect_reason);
311
i_free_and_null(conn->disconnect_reason);
313
reply.text_without_resp = reply.text_full;
307
314
imapc_login_callback(conn, &reply);
309
316
conn->idling = FALSE;
347
354
bool reconnecting = conn->selected_box != NULL &&
348
355
conn->selected_box->reconnecting;
357
if (conn->state == IMAPC_CONNECTION_STATE_DISCONNECTED)
353
360
if (conn->client->set.debug)
354
361
i_debug("imapc(%s): Disconnected", conn->name);
363
if (conn->dns_lookup != NULL)
364
dns_lookup_abort(&conn->dns_lookup);
356
365
imapc_connection_lfiles_free(conn);
357
366
imapc_connection_literal_reset(&conn->literal);
358
367
if (conn->to != NULL)
359
368
timeout_remove(&conn->to);
360
369
if (conn->to_output != NULL)
361
370
timeout_remove(&conn->to_output);
362
imap_parser_unref(&conn->parser);
363
io_remove(&conn->io);
371
if (conn->parser != NULL)
372
imap_parser_unref(&conn->parser);
373
if (conn->io != NULL)
374
io_remove(&conn->io);
364
375
if (conn->ssl_iostream != NULL)
365
376
ssl_iostream_unref(&conn->ssl_iostream);
366
i_stream_destroy(&conn->input);
367
o_stream_destroy(&conn->output);
368
net_disconnect(conn->fd);
377
if (conn->fd != -1) {
378
i_stream_destroy(&conn->input);
379
o_stream_destroy(&conn->output);
380
net_disconnect(conn->fd);
371
384
imapc_connection_set_state(conn, IMAPC_CONNECTION_STATE_DISCONNECTED);
372
385
imapc_connection_abort_commands(conn, NULL, reconnecting);
900
913
value = imap_args_to_str(imap_args);
901
914
if (imapc_connection_parse_capability(conn, value) < 0)
916
} else if (strcasecmp(name, "BYE") == 0) {
917
i_free(conn->disconnect_reason);
918
conn->disconnect_reason = i_strdup(imap_args_to_str(imap_args));
905
921
reply.name = name;
1048
1064
imapc_connection_disconnect(conn);
1067
if (reply.state == IMAPC_COMMAND_STATE_NO &&
1068
(cmd->flags & IMAPC_COMMAND_FLAG_SELECT) != 0 &&
1069
conn->selected_box != NULL) {
1070
/* EXAMINE/SELECT failed: mailbox is no longer selected */
1071
imapc_connection_unselect(conn->selected_box);
1051
1074
imapc_connection_input_reset(conn);
1052
1075
imapc_command_reply_free(cmd, &reply);
1053
1076
imapc_command_send_more(conn);
1116
1139
/* disconnected */
1117
if (conn->ssl_iostream == NULL) {
1140
if (conn->disconnect_reason != NULL) {
1141
i_error("imapc(%s): Server disconnected with message: %s",
1142
conn->name, conn->disconnect_reason);
1143
} else if (conn->ssl_iostream == NULL) {
1118
1144
i_error("imapc(%s): Server disconnected unexpectedly",
1120
} else if (!conn->handshake_failed) {
1121
1147
errstr = ssl_iostream_get_last_error(conn->ssl_iostream);
1122
1148
if (errstr == NULL) {
1123
1149
errstr = conn->input->stream_errno == 0 ? "EOF" :
1131
1157
imapc_connection_unref(&conn);
1134
static int imapc_connection_ssl_handshaked(void *context)
1160
static int imapc_connection_ssl_handshaked(const char **error_r, void *context)
1136
1162
struct imapc_connection *conn = context;
1138
if (!conn->client->set.ssl_verify) {
1139
/* skip certificate checks */
1141
} else if (!ssl_iostream_has_valid_client_cert(conn->ssl_iostream)) {
1142
if (!ssl_iostream_has_broken_client_cert(conn->ssl_iostream)) {
1143
i_error("imapc(%s): SSL certificate not received",
1146
i_error("imapc(%s): Received invalid SSL certificate",
1149
} else if (ssl_iostream_cert_match_name(conn->ssl_iostream,
1150
conn->client->set.host) < 0) {
1151
i_error("imapc(%s): SSL certificate doesn't match host name",
1165
if (ssl_iostream_check_cert_validity(conn->ssl_iostream,
1166
conn->client->set.host, &error) == 0) {
1154
1167
if (conn->client->set.debug) {
1155
1168
i_debug("imapc(%s): SSL handshake successful",
1172
} else if (!conn->client->set.ssl_verify) {
1173
if (conn->client->set.debug) {
1174
i_debug("imapc(%s): SSL handshake successful, "
1175
"ignoring invalid certificate: %s",
1160
conn->handshake_failed = TRUE;
1161
i_stream_close(conn->input);
1165
1185
static int imapc_connection_ssl_init(struct imapc_connection *conn)
1167
1187
struct ssl_iostream_settings ssl_set;
1168
1188
struct stat st;
1171
1191
if (conn->client->ssl_ctx == NULL) {
1172
1192
i_error("imapc(%s): No SSL context", conn->name);
1193
1213
conn->output = conn->raw_output;
1196
source = t_strdup_printf("imapc(%s): ", conn->name);
1197
if (io_stream_create_ssl(conn->client->ssl_ctx, source, &ssl_set,
1198
&conn->input, &conn->output,
1199
&conn->ssl_iostream) < 0) {
1200
i_error("imapc(%s): Couldn't initialize SSL client",
1216
if (io_stream_create_ssl_client(conn->client->ssl_ctx,
1217
conn->client->set.host,
1218
&ssl_set, &conn->input, &conn->output,
1219
&conn->ssl_iostream, &error) < 0) {
1220
i_error("imapc(%s): Couldn't initialize SSL client: %s",
1204
1224
ssl_iostream_set_handshake_callback(conn->ssl_iostream,
1213
1233
if (*conn->client->set.rawlog_dir != '\0' &&
1214
1234
stat(conn->client->set.rawlog_dir, &st) == 0) {
1215
(void)iostream_rawlog_create(conn->client->set.rawlog_dir,
1216
&conn->input, &conn->output);
1235
iostream_rawlog_create(conn->client->set.rawlog_dir,
1236
&conn->input, &conn->output);
1219
1239
imap_parser_set_streams(conn->parser, conn->input, NULL);
1250
1270
case IMAPC_CONNECTION_STATE_CONNECTING:
1251
1271
i_error("imapc(%s): connect(%s, %u) timed out after %u seconds",
1252
1272
conn->name, net_ip2addr(ip), conn->client->set.port,
1253
IMAPC_CONNECT_TIMEOUT_MSECS/1000);
1273
conn->client->set.connect_timeout_msecs/1000);
1255
1275
case IMAPC_CONNECTION_STATE_AUTHENTICATING:
1256
1276
i_error("imapc(%s): Authentication timed out after %u seconds",
1257
conn->name, IMAPC_CONNECT_TIMEOUT_MSECS/1000);
1277
conn->name, conn->client->set.connect_timeout_msecs/1000);
1307
1327
conn->input = conn->raw_input = i_stream_create_fd(fd, (size_t)-1, FALSE);
1308
1328
conn->output = conn->raw_output = o_stream_create_fd(fd, (size_t)-1, FALSE);
1329
o_stream_set_no_error_handling(conn->output, TRUE);
1310
1331
if (*conn->client->set.rawlog_dir != '\0' &&
1311
1332
conn->client->set.ssl_mode != IMAPC_CLIENT_SSL_MODE_IMMEDIATE &&
1312
1333
stat(conn->client->set.rawlog_dir, &st) == 0) {
1313
(void)iostream_rawlog_create(conn->client->set.rawlog_dir,
1314
&conn->input, &conn->output);
1334
iostream_rawlog_create(conn->client->set.rawlog_dir,
1335
&conn->input, &conn->output);
1317
1338
o_stream_set_flush_callback(conn->output, imapc_connection_output,
1319
1340
conn->io = io_add(fd, IO_WRITE, imapc_connection_connected, conn);
1320
1341
conn->parser = imap_parser_create(conn->input, NULL, (size_t)-1);
1321
conn->to = timeout_add(IMAPC_CONNECT_TIMEOUT_MSECS,
1342
conn->to = timeout_add(conn->client->set.connect_timeout_msecs,
1322
1343
imapc_connection_timeout, conn);
1323
1344
conn->to_output = timeout_add(conn->client->set.max_idle_time*1000,
1324
1345
imapc_connection_reset_idle, conn);
1332
1353
imapc_connection_dns_callback(const struct dns_lookup_result *result,
1354
struct imapc_connection *conn)
1335
struct imapc_connection *conn = context;
1356
conn->dns_lookup = NULL;
1337
1358
if (result->ret != 0) {
1338
1359
i_error("imapc(%s): dns_lookup(%s) failed: %s",
1375
1396
memset(&dns_set, 0, sizeof(dns_set));
1376
1397
dns_set.dns_client_socket_path =
1377
1398
conn->client->set.dns_client_socket_path;
1378
dns_set.timeout_msecs = IMAPC_DNS_LOOKUP_TIMEOUT_MSECS;
1399
dns_set.timeout_msecs = conn->client->set.connect_timeout_msecs;
1380
1401
imapc_connection_set_state(conn, IMAPC_CONNECTION_STATE_CONNECTING);
1381
1402
if (conn->ips_count == 0 &&
1401
1422
if (conn->ips_count == 0) {
1402
1423
(void)dns_lookup(conn->client->set.host, &dns_set,
1403
imapc_connection_dns_callback, conn);
1424
imapc_connection_dns_callback, conn,
1405
1427
imapc_connection_connect_next_ip(conn);
1639
1661
data = CONST_PTR_OFFSET(cmd->data->data, cmd->send_pos);
1640
1662
size = end_pos - cmd->send_pos;
1641
o_stream_send(conn->output, data, size);
1663
o_stream_nsend(conn->output, data, size);
1642
1664
cmd->send_pos = end_pos;
1644
1666
if (cmd->send_pos == cmd->data->used) {
1668
1690
if ((conn->idling || conn->idle_plus_waiting) && !conn->idle_stopping) {
1669
1691
conn->idle_stopping = TRUE;
1670
o_stream_send_str(conn->output, "DONE\r\n");
1692
o_stream_nsend_str(conn->output, "DONE\r\n");
1689
1711
/* add timeout for commands if there's not one yet
1690
1712
(pre-login has its own timeout) */
1691
1713
if (conn->to == NULL) {
1692
conn->to = timeout_add(IMAPC_COMMAND_TIMEOUT_MSECS,
1714
conn->to = timeout_add(conn->client->set.cmd_timeout_msecs,
1693
1715
imapc_command_timeout, conn);
1817
1839
const char *arg = va_arg(args, const char *);
1819
1841
if (!need_literal(arg))
1820
imap_dquote_append(cmd->data, arg);
1842
imap_append_quoted(cmd->data, arg);
1821
1843
else if ((cmd->conn->capabilities &
1822
1844
IMAPC_CAPABILITY_LITERALPLUS) != 0) {
1823
1845
str_printfa(cmd->data, "{%"PRIuSIZE_T"+}\r\n%s",