commands.c revision a338794c56fc9674121e262fcb67c3dc1da31436
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen/* Copyright (c) 2009 Dovecot authors, see the included COPYING file */
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen#define ERRSTR_TEMP_MAILBOX_FAIL "451 4.3.0 <%s> Temporary internal error"
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen#define ERRSTR_TEMP_USERDB_FAIL "451 4.3.0 <%s> Temporary user lookup failure"
eed0a07ecb946ec9d021f5b413fb33eb36e135fdTimo Sirainen#define LMTP_PROXY_DEFAULT_TIMEOUT_MSECS (1000*30)
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainenint cmd_lhlo(struct client *client, const char *args ATTR_UNUSED)
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen client_send_line(client, "250-%s", client->my_domain);
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen client_send_line(client, "250-ENHANCEDSTATUSCODES");
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainenint cmd_mail(struct client *client, const char *args)
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen unsigned int len;
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen client_send_line(client, "503 5.5.1 MAIL already given");
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen if (strncasecmp(addr, "FROM:<", 6) != 0 || addr[len-1] != '>') {
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen client_send_line(client, "501 5.5.4 Invalid parameters");
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen else if (strcasecmp(*argv, "BODY=8BITMIME") == 0)
65514ab6ccc1889e1667211fddb0cca4b51017dfTimo Sirainen "501 5.5.4 Unsupported options");
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen p_strndup(client->state_pool, addr + 6, len - 7);
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen p_array_init(&client->state.rcpt_to, client->state_pool, 64);
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainenstatic bool rcpt_is_duplicate(struct client *client, const char *name)
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainenclient_proxy_rcpt_parse_fields(struct lmtp_proxy_settings *set,
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen const char *const *args, const char **address)
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen /* changing the username */
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen /* just ignore it */
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainenclient_proxy_is_ourself(const struct client *client,
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainenstatic bool client_proxy_rcpt(struct client *client, const char *address)
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen const char *args, *const *fields, *orig_address = address;
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen info.service = master_service_get_name(master_service);
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen pool = pool_alloconly_create("auth lookup", 1024);
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen auth_conn = mail_storage_service_multi_get_auth_conn(multi_service);
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen ret = auth_master_pass_lookup(auth_conn, address, &info,
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen client_send_line(client, ERRSTR_TEMP_USERDB_FAIL,
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen /* user not found from passdb. try userdb also. */
eed0a07ecb946ec9d021f5b413fb33eb36e135fdTimo Sirainen set.timeout_msecs = LMTP_PROXY_DEFAULT_TIMEOUT_MSECS;
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen if (!client_proxy_rcpt_parse_fields(&set, fields, &address)) {
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen /* not proxying this user */
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen i_error("Proxying to <%s> loops to itself", address);
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen client_send_line(client, "554 5.4.6 Proxying loops to itself");
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen client->proxy = lmtp_proxy_init(client->set->hostname,
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen lmtp_proxy_mail_from(client->proxy, t_strdup_printf(
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen if (lmtp_proxy_add_rcpt(client->proxy, address, &set) < 0)
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen client_send_line(client, ERRSTR_TEMP_REMOTE_FAILURE);
a338794c56fc9674121e262fcb67c3dc1da31436Timo Sirainenstatic const char *lmtp_unescape_address(const char *name)
a338794c56fc9674121e262fcb67c3dc1da31436Timo Sirainen const char *p;
a338794c56fc9674121e262fcb67c3dc1da31436Timo Sirainen /* quoted-string local-part. drop the quotes unless there's a
a338794c56fc9674121e262fcb67c3dc1da31436Timo Sirainen '@' character inside or there's an error. */
a338794c56fc9674121e262fcb67c3dc1da31436Timo Sirainen if (*p == '\0')
a338794c56fc9674121e262fcb67c3dc1da31436Timo Sirainen if (*p == '\\') {
a338794c56fc9674121e262fcb67c3dc1da31436Timo Sirainen if (*p == '@')
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainenint cmd_rcpt(struct client *client, const char *args)
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen const char *name, *error = NULL, *addr, *const *argv;
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen unsigned int len;
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen client_send_line(client, "503 5.5.1 MAIL needed first");
65514ab6ccc1889e1667211fddb0cca4b51017dfTimo Sirainen if (strncasecmp(addr, "TO:<", 4) != 0 || addr[len-1] != '>') {
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen client_send_line(client, "501 5.5.4 Invalid parameters");
a338794c56fc9674121e262fcb67c3dc1da31436Timo Sirainen name = lmtp_unescape_address(t_strndup(addr + 4, len - 5));
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen client_send_line(client, "250 2.1.5 OK, ignoring duplicate");
65514ab6ccc1889e1667211fddb0cca4b51017dfTimo Sirainen client_send_line(client, "501 5.5.4 Unsupported options");
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen ret = mail_storage_service_multi_lookup(multi_service, &input,
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen client_send_line(client, ERRSTR_TEMP_USERDB_FAIL, name);
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen rcpt.name = p_strdup(client->state_pool, name);
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen array_append(&client->state.rcpt_to, &rcpt, 1);
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainenint cmd_quit(struct client *client, const char *args ATTR_UNUSED)
ca598afa51b9c8ffd7c1460ac3c8bfa313c6d4afTimo Sirainen client_destroy(client, "221 2.0.0", "Client quit");
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainenint cmd_vrfy(struct client *client, const char *args ATTR_UNUSED)
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen client_send_line(client, "252 2.3.3 Try RCPT instead");
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainenint cmd_rset(struct client *client, const char *args ATTR_UNUSED)
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainenint cmd_noop(struct client *client, const char *args ATTR_UNUSED)
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainenclient_deliver(struct client *client, const struct mail_recipient *rcpt,
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen i_set_failure_prefix(t_strdup_printf("lmtp(%s): ", rcpt->name));
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen if (mail_storage_service_multi_next(multi_service, rcpt->multi_user,
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL, rcpt->name);
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen sets = mail_storage_service_multi_user_get_set(rcpt->multi_user);
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen dctx.pool = pool_alloconly_create("mail delivery", 1024);
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen dctx.src_envelope_sender = client->state.mail_from;
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen dctx.save_dest_mail = array_count(&client->state.rcpt_to) > 1 &&
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen i_assert(client->state.first_saved_mail == NULL);
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen client->state.first_saved_mail = dctx.dest_mail;
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen client_send_line(client, "250 2.0.0 <%s> Saved", rcpt->name);
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen /* This shouldn't happen */
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen i_error("BUG: Saving failed to unknown storage");
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen error = mail_storage_get_last_error(storage, &mail_error);
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainenstatic bool client_deliver_next(struct client *client, struct mail *src_mail)
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen unsigned int count;
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen rcpts = array_get(&client->state.rcpt_to, &count);
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen ret = client_deliver(client, &rcpts[client->state.rcpt_idx],
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen /* failed. try the next one. */
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainenstatic void client_rcpt_fail_all(struct client *client)
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL, rcpt->name);
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainenstatic struct istream *client_get_input(struct client *client)
dd3ccdbb29dad006f7781ea138a5ba39727963c4Timo Sirainen o_stream_unref(&client->state.mail_data_output);
dd3ccdbb29dad006f7781ea138a5ba39727963c4Timo Sirainen input = i_stream_create_fd(client->state.mail_data_fd,
d4379e01c47bd39dab2368529c6b17d51346d7a5Timo Sirainen i_stream_set_init_buffer_size(input, MAIL_READ_FULL_BLOCK_SIZE);
dd3ccdbb29dad006f7781ea138a5ba39727963c4Timo Sirainen input = i_stream_create_from_data(client->state.mail_data->data,
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainenstatic int client_open_raw_mail(struct client *client, struct istream *input)
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen static const char *wanted_headers[] = {
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen "From", "To", "Message-ID", "Subject", "Return-Path",
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen struct mailbox_list *raw_list = client->raw_mail_user->namespaces->list;
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen struct mailbox_header_lookup_ctx *headers_ctx;
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen mailbox_alloc(raw_list, "Dovecot Delivery Mail", input,
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen i_error("Can't open delivery mail as raw: %s",
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen mail_storage_get_last_error(box->storage, &error));
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen raw_box->envelope_sender = client->state.mail_from;
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen client->state.raw_trans = mailbox_transaction_begin(box, 0);
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen headers_ctx = mailbox_header_lookup_init(box, wanted_headers);
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen client->state.raw_mail = mail_alloc(client->state.raw_trans,
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainenclient_input_data_write_local(struct client *client, struct istream *input)
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen /* save the message to the first recipient's mailbox */
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen /* use the first saved message to save it elsewhere too.
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen this might allow hard linking the files. */
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen struct mail *mail = client->state.first_saved_mail;
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen struct mailbox_transaction_context *trans = mail->transaction;
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainenstatic void client_input_data_finish(struct client *client)
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen client->io = io_add(client->fd_in, IO_READ, client_input, client);
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainenstatic bool client_input_data_write(struct client *client)
dd3ccdbb29dad006f7781ea138a5ba39727963c4Timo Sirainenstatic int client_input_add_file(struct client *client,
dd3ccdbb29dad006f7781ea138a5ba39727963c4Timo Sirainen /* continue writing to file */
dd3ccdbb29dad006f7781ea138a5ba39727963c4Timo Sirainen if (o_stream_send(client->state.mail_data_output,
dd3ccdbb29dad006f7781ea138a5ba39727963c4Timo Sirainen /* move everything to a temporary file. FIXME: it really shouldn't
dd3ccdbb29dad006f7781ea138a5ba39727963c4Timo Sirainen be in /tmp.. */
dd3ccdbb29dad006f7781ea138a5ba39727963c4Timo Sirainen fd = safe_mkstemp_hostpid(path, 0600, (uid_t)-1, (gid_t)-1);
dd3ccdbb29dad006f7781ea138a5ba39727963c4Timo Sirainen /* we just want the fd, unlink it */
dd3ccdbb29dad006f7781ea138a5ba39727963c4Timo Sirainen /* shouldn't happen.. */
dd3ccdbb29dad006f7781ea138a5ba39727963c4Timo Sirainen i_error("unlink(%s) failed: %m", str_c(path));
dd3ccdbb29dad006f7781ea138a5ba39727963c4Timo Sirainen client->state.mail_data_output = o_stream_create_fd_file(fd, 0, FALSE);
dd3ccdbb29dad006f7781ea138a5ba39727963c4Timo Sirainen o_stream_cork(client->state.mail_data_output);
dd3ccdbb29dad006f7781ea138a5ba39727963c4Timo Sirainen if (o_stream_send(client->state.mail_data_output,
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainenclient_input_add(struct client *client, const unsigned char *data, size_t size)
dd3ccdbb29dad006f7781ea138a5ba39727963c4Timo Sirainen buffer_append(client->state.mail_data, data, size);
dd3ccdbb29dad006f7781ea138a5ba39727963c4Timo Sirainen return client_input_add_file(client, data, size);
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainenstatic void client_input_data_handle(struct client *client)
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen const unsigned char *data;
c4390dad33b03dd51ba2a475f550347c86ebdb9aTimo Sirainen while ((ret = i_stream_read(client->dot_input)) > 0 || ret == -2) {
c4390dad33b03dd51ba2a475f550347c86ebdb9aTimo Sirainen data = i_stream_get_data(client->dot_input, &size);
c4390dad33b03dd51ba2a475f550347c86ebdb9aTimo Sirainen if (client_input_add(client, data, size) < 0) {
c4390dad33b03dd51ba2a475f550347c86ebdb9aTimo Sirainen "Temporary internal failure");
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainenstatic void client_input_data(struct client *client)
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainenint cmd_data(struct client *client, const char *args ATTR_UNUSED)
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen client_send_line(client, "503 5.5.1 MAIL needed first");
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen if (array_count(&client->state.rcpt_to) == 0 && client->proxy == NULL) {
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen client_send_line(client, "554 5.5.1 No valid recipients");
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen client->state.mail_data = buffer_create_dynamic(default_pool, 1024*64);
c4390dad33b03dd51ba2a475f550347c86ebdb9aTimo Sirainen client->dot_input = i_stream_create_dot(client->input, TRUE);
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen if (array_count(&client->state.rcpt_to) == 0) {