static void lmtp_client_input(struct lmtp_client *client) { const char *line; int ret; lmtp_client_ref(client); o_stream_cork(client->output); while ((line = i_stream_read_next_line(client->input)) != NULL) { T_BEGIN { ret = lmtp_client_input_line(client, line); } T_END; if (ret < 0) { o_stream_uncork(client->output); lmtp_client_unref(&client); return; } if (ret > 0) str_truncate(client->input_multiline, 0); } if (client->input->stream_errno == ENOBUFS) { lmtp_client_fail(client, "501 5.5.4 Command reply line too long"); } else if (client->input->stream_errno != 0) { errno = client->input->stream_errno; i_error("lmtp client: read() failed: %m"); lmtp_client_fail(client, ERRSTR_TEMP_REMOTE_FAILURE " (read failure)"); } else if (client->input->eof) { lmtp_client_fail(client, ERRSTR_TEMP_REMOTE_FAILURE " (disconnected in input)"); } o_stream_uncork(client->output); lmtp_client_unref(&client); }
static void lmtp_client_dns_done(const struct dns_lookup_result *result, struct lmtp_client *client) { if (result->ret != 0) { i_error("lmtp client: DNS lookup of %s failed: %s", client->host, result->error); lmtp_client_fail(client, ERRSTR_TEMP_REMOTE_FAILURE " (DNS lookup)"); } else { client->ip = result->ips[0]; if (lmtp_client_connect(client) < 0) { lmtp_client_fail(client, ERRSTR_TEMP_REMOTE_FAILURE " (connect)"); } } }
static void lmtp_client_connect_timeout(struct lmtp_client *client) { i_error("lmtp client: connect(%s, %u) failed: Timed out in %u secs", client->host, client->port, client->set.timeout_secs); lmtp_client_fail(client, ERRSTR_TEMP_REMOTE_FAILURE " (connect timeout)"); }
static void lmtp_client_timeout(struct lmtp_client *client) { const char *line; line = t_strdup_printf(ERRSTR_TEMP_REMOTE_FAILURE " (Timed out after %u secs while waiting for reply to %s)", client->set.timeout_secs, lmtp_client_state_to_string(client)); lmtp_client_fail(client, line); }
static int lmtp_client_send_data_cmd(struct lmtp_client *client) { if (client->rcpt_next_receive_idx < array_count(&client->recipients)) return 0; if (client->global_fail_string != NULL || !client->rcpt_to_successes) { lmtp_client_fail(client, client->global_fail_string); return -1; } else { client->input_state++; o_stream_nsend_str(client->output, "DATA\r\n"); return 0; } }
static void lmtp_client_input(struct lmtp_client *client) { const char *line; lmtp_client_ref(client); while ((line = i_stream_read_next_line(client->input)) != NULL) { if (lmtp_client_input_line(client, line) < 0) { lmtp_client_unref(&client); return; } } if (client->input->stream_errno != 0) { errno = client->input->stream_errno; i_error("lmtp client: read() failed: %m"); lmtp_client_fail(client, ERRSTR_TEMP_REMOTE_FAILURE " (read failure)"); } else if (client->input->eof) { lmtp_client_fail(client, ERRSTR_TEMP_REMOTE_FAILURE " (disconnected in input)"); } lmtp_client_unref(&client); }
static int lmtp_client_output(struct lmtp_client *client) { int ret; lmtp_client_ref(client); o_stream_cork(client->output); if ((ret = o_stream_flush(client->output)) < 0) lmtp_client_fail(client, ERRSTR_TEMP_REMOTE_FAILURE " (disconnected in output)"); else if (client->input_state == LMTP_INPUT_STATE_DATA) lmtp_client_send_data(client); o_stream_uncork(client->output); lmtp_client_unref(&client); return ret; }
static void lmtp_client_wait_connect(struct lmtp_client *client) { int err; err = net_geterror(client->fd); if (err != 0) { i_error("lmtp client: connect(%s, %u) failed: %s", client->host, client->port, strerror(err)); lmtp_client_fail(client, ERRSTR_TEMP_REMOTE_FAILURE " (connect)"); return; } io_remove(&client->io); client->io = io_add(client->fd, IO_READ, lmtp_client_input, client); lmtp_client_input(client); }
static int lmtp_client_input_line(struct lmtp_client *client, const char *line) { int ret, reply_code = 0; if ((ret = lmtp_input_get_reply_code(line, &reply_code, client->input_multiline)) <= 0) { if (ret == 0) return 0; lmtp_client_fail(client, line); return -1; } switch (client->input_state) { case LMTP_INPUT_STATE_GREET: case LMTP_INPUT_STATE_XCLIENT: if (reply_code != 220) { lmtp_client_fail(client, line); return -1; } lmtp_client_send_handshake(client); client->input_state = LMTP_INPUT_STATE_LHLO; break; case LMTP_INPUT_STATE_LHLO: case LMTP_INPUT_STATE_MAIL_FROM: if (reply_code != 250) { lmtp_client_fail(client, line); return -1; } str_append(client->input_multiline, line); lmtp_client_parse_capabilities(client, str_c(client->input_multiline)); if (client->input_state == LMTP_INPUT_STATE_LHLO && lmtp_client_send_xclient(client)) { client->input_state = LMTP_INPUT_STATE_XCLIENT; client->xclient_sent = TRUE; break; } if (client->input_state == LMTP_INPUT_STATE_LHLO) { o_stream_nsend_str(client->output, t_strdup_printf("MAIL FROM:%s\r\n", client->set.mail_from)); } client->input_state++; lmtp_client_send_rcpts(client); break; case LMTP_INPUT_STATE_RCPT_TO: lmtp_client_rcpt_next(client, line); if (client->data_input == NULL) break; if (lmtp_client_send_data_cmd(client) < 0) return -1; break; case LMTP_INPUT_STATE_DATA_CONTINUE: /* Start sending DATA */ if (strncmp(line, "354", 3) != 0) { lmtp_client_fail(client, line); return -1; } client->input_state++; if (client->data_header != NULL) o_stream_nsend_str(client->output, client->data_header); lmtp_client_send_data(client); break; case LMTP_INPUT_STATE_DATA: /* DATA replies */ if (lmtp_client_data_next(client, line) < 0) return -1; break; } return 1; }
static int lmtp_client_send_data(struct lmtp_client *client) { const unsigned char *data; unsigned char add; size_t i, size; bool sent_bytes = FALSE; int ret; if (client->output_finished) return 0; while ((ret = i_stream_read_more(client->data_input, &data, &size)) > 0) { add = '\0'; for (i = 0; i < size; i++) { if (data[i] == '\n') { if ((i == 0 && client->output_last != '\r') || (i > 0 && data[i-1] != '\r')) { /* missing CR */ add = '\r'; break; } } else if (data[i] == '.' && ((i == 0 && client->output_last == '\n') || (i > 0 && data[i-1] == '\n'))) { /* escape the dot */ add = '.'; break; } } if (i > 0) { if (o_stream_send(client->output, data, i) < 0) break; client->output_last = data[i-1]; i_stream_skip(client->data_input, i); sent_bytes = TRUE; } if (o_stream_get_buffer_used_size(client->output) >= 4096) { if ((ret = o_stream_flush(client->output)) < 0) break; if (ret == 0) { /* continue later */ o_stream_set_flush_pending(client->output, TRUE); return 0; } } if (add != '\0') { if (o_stream_send(client->output, &add, 1) < 0) break; client->output_last = add; } } if (client->data_input->stream_errno != 0) { i_error("lmtp client: read(%s) failed: %s", i_stream_get_name(client->data_input), i_stream_get_error(client->data_input)); lmtp_client_fail(client, "451 4.3.0 Internal failure while reading DATA input"); return -1; } if (sent_bytes && client->data_output_callback != NULL) client->data_output_callback(client->data_output_context); if (ret == 0 || ret == -2) { /* -2 can happen with tee istreams */ return 0; } if (client->output_last != '\n') { /* didn't end with CRLF */ o_stream_nsend(client->output, "\r\n", 2); } o_stream_nsend(client->output, ".\r\n", 3); client->output_finished = TRUE; io_loop_time_refresh(); client->times.data_sent = ioloop_timeval; return 0; }