commands.c revision ed89b9556292b6d7c2551086a4958f4ed601bda9
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen#define ERRSTR_TEMP_MAILBOX_FAIL "451 4.3.0 <%s> Temporary internal error"
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen#define ERRSTR_TEMP_USERDB_FAIL_PREFIX "451 4.3.0 <%s> "
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen ERRSTR_TEMP_USERDB_FAIL_PREFIX "Temporary user lookup failure"
a50f8b9c6196b0f0bb0a7aac3ae9c12b1c42b93bTimo Sirainen#define LMTP_PROXY_DEFAULT_TIMEOUT_MSECS (1000*125)
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainenint cmd_lhlo(struct client *client, const char *args)
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen const char *p;
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen client_send_line(client, "501 Missing hostname");
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen /* domain / address-literal */
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen rfc822_parser_init(&parser, (const unsigned char *)args, strlen(args),
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen client_send_line(client, "250-%s", client->my_domain);
4fbf59562594dbbbe037f8d4c480dbf88f3fc708Timo Sirainen if (master_service_ssl_is_enabled(master_service) &&
a566d9f8797dd66b4d5432a20ab2b9f7c8f76102Timo Sirainen client_send_line(client, "250-XCLIENT ADDR PORT TTL TIMEOUT");
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen client_send_line(client, "250-ENHANCEDSTATUSCODES");
4fbf59562594dbbbe037f8d4c480dbf88f3fc708Timo Sirainen struct ostream *plain_output = client->output;
4fbf59562594dbbbe037f8d4c480dbf88f3fc708Timo Sirainen "443 5.5.1 TLS is already active.\r\n");
4fbf59562594dbbbe037f8d4c480dbf88f3fc708Timo Sirainen i_error("TLS initialization failed: %s", error);
4fbf59562594dbbbe037f8d4c480dbf88f3fc708Timo Sirainen "454 4.7.0 Internal error, TLS not available.\r\n");
4fbf59562594dbbbe037f8d4c480dbf88f3fc708Timo Sirainen "220 2.0.0 Begin TLS negotiation now.\r\n");
4fbf59562594dbbbe037f8d4c480dbf88f3fc708Timo Sirainen if (ssl_iostream_handshake(client->ssl_iostream) < 0) {
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainenstatic int parse_address(const char *str, const char **address_r,
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen const char **rest_r)
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen /* "quoted-string"@domain */
38f624b427aa8b6fad3765e6efd97c85a7f97a09Timo Sirainenstatic const char *
38f624b427aa8b6fad3765e6efd97c85a7f97a09Timo Sirainenparse_xtext(struct client *client, const char *value)
38f624b427aa8b6fad3765e6efd97c85a7f97a09Timo Sirainen const char *p;
38f624b427aa8b6fad3765e6efd97c85a7f97a09Timo Sirainen unsigned int i;
38f624b427aa8b6fad3765e6efd97c85a7f97a09Timo Sirainen hexchar = ASCII "+" immediately followed by two upper case
38f624b427aa8b6fad3765e6efd97c85a7f97a09Timo Sirainen hexadecimal digits
38f624b427aa8b6fad3765e6efd97c85a7f97a09Timo Sirainen if (value[i] == '+' && value[i+1] != '\0' && value[i+2] != '\0') {
38f624b427aa8b6fad3765e6efd97c85a7f97a09Timo Sirainen str_append_c(str, hex2dec((const void *)(value+i+1), 2));
38f624b427aa8b6fad3765e6efd97c85a7f97a09Timo Sirainen return p_strdup(client->state_pool, str_c(str));
6de95f63d4590814354fa5e35e92946c58d4562eTimo Sirainenstatic void lmtp_anvil_init(void)
6de95f63d4590814354fa5e35e92946c58d4562eTimo Sirainen const char *path = t_strdup_printf("%s/anvil", base_dir);
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainenint cmd_mail(struct client *client, const char *args)
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen client_send_line(client, "503 5.5.1 MAIL already given");
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");
e5b06a21b5f857d7037fd6ff41ba3bd449d1232aTimo Sirainen client->state.mail_from = p_strdup(client->state_pool, addr);
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen p_array_init(&client->state.rcpt_to, client->state_pool, 64);
d65cc3312d3126d34b51ae8eccd6b48215d50029Timo Sirainen client_state_set(client, "MAIL FROM", client->state.mail_from);
6de95f63d4590814354fa5e35e92946c58d4562eTimo Sirainen if (client->lmtp_set->lmtp_user_concurrency_limit > 0) {
6de95f63d4590814354fa5e35e92946c58d4562eTimo Sirainen /* connect to anvil before dropping privileges */
6b6011c2242e470b41316f92512b282b5e306dacTimo Sirainen client->state.mail_from_timeval = ioloop_timeval;
3322dfd56c9cae5015a33bab4c80124c08c3d9caTimo Sirainenclient_proxy_rcpt_parse_fields(struct lmtp_proxy_rcpt_settings *set,
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen const char *const *args, const char **address)
009217abb57a24a4076092e8e4e165545747839eStephan Bosch i_error("proxy: Invalid port number %s", value);
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Bosch } else if (strcmp(key, "proxy_timeout") == 0) {
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Bosch if (str_to_uint(value, &set->timeout_msecs) < 0) {
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Bosch i_error("proxy: Invalid proxy_timeout value %s", value);
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen /* changing the username */
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen /* just ignore it */
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainenclient_proxy_is_ourself(const struct client *client,
d2a2e0b6d302c17c33638a6fd48bd665a1c81e46Timo Sirainenstatic const char *
972c9172e9e6a0fc6053efb3d2ee9d354b67727fLennart Welleraddress_add_detail(const char *username, char delim_c,
d2a2e0b6d302c17c33638a6fd48bd665a1c81e46Timo Sirainen return t_strconcat(username, delim, detail, NULL);
d2a2e0b6d302c17c33638a6fd48bd665a1c81e46Timo Sirainen return t_strconcat(username, delim, detail, domain, NULL);
9a935c34e98ba7a9cc90784ceb63b2fbdab4105fTimo Sirainenstatic bool client_proxy_rcpt(struct client *client, const char *address,
972c9172e9e6a0fc6053efb3d2ee9d354b67727fLennart Weller const char *username, const char *detail, char delim,
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen const char *args, *const *fields, *errstr, *orig_username = username;
86fafb22c02f4b85a9d59f2b60059a48286286fbTimo Sirainen mail_storage_service_init_settings(storage_service, &input);
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen info.service = master_service_get_name(master_service);
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen pool = pool_alloconly_create("auth lookup", 1024);
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen auth_conn = mail_storage_service_get_auth_conn(storage_service);
9a935c34e98ba7a9cc90784ceb63b2fbdab4105fTimo Sirainen ret = auth_master_pass_lookup(auth_conn, username, &info,
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen errstr = ret < 0 && fields[0] != NULL ? t_strdup(fields[0]) :
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen t_strdup_printf(ERRSTR_TEMP_USERDB_FAIL, address);
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen /* user not found from passdb. try userdb also. */
eed0a07ecb946ec9d021f5b413fb33eb36e135fdTimo Sirainen set.timeout_msecs = LMTP_PROXY_DEFAULT_TIMEOUT_MSECS;
9a935c34e98ba7a9cc90784ceb63b2fbdab4105fTimo Sirainen if (!client_proxy_rcpt_parse_fields(&set, fields, &username)) {
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainen /* not proxying this user */
d2a2e0b6d302c17c33638a6fd48bd665a1c81e46Timo Sirainen /* username changed. change the address as well */
972c9172e9e6a0fc6053efb3d2ee9d354b67727fLennart Weller address = address_add_detail(username, delim, detail);
d2a2e0b6d302c17c33638a6fd48bd665a1c81e46Timo Sirainen } else if (client_proxy_is_ourself(client, &set)) {
9a935c34e98ba7a9cc90784ceb63b2fbdab4105fTimo Sirainen i_error("Proxying to <%s> loops to itself", username);
ddbdc644a15f56f4b43596f1b8c0fc196c101445Timo Sirainen i_error("Proxying to <%s> appears to be looping (TTL=0)",
ddbdc644a15f56f4b43596f1b8c0fc196c101445Timo Sirainen "Proxying appears to be looping (TTL=0)",
7aa2dd189748ed57c2fb8d8d9b7cb3c4ac2f7ea5Timo Sirainen if (array_count(&client->state.rcpt_to) != 0) {
7aa2dd189748ed57c2fb8d8d9b7cb3c4ac2f7ea5Timo Sirainen "Can't handle mixed proxy/non-proxy destinations",
3322dfd56c9cae5015a33bab4c80124c08c3d9caTimo Sirainen proxy_set.dns_client_socket_path = dns_client_socket_path;
d163fac99c7ca5b2ce2edfa83a1b2922cf66aeacTimo Sirainen proxy_set.session_id = client->state.session_id;
3322dfd56c9cae5015a33bab4c80124c08c3d9caTimo Sirainen client->proxy = lmtp_proxy_init(&proxy_set, client->output);
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 == '@')
e7b1f9db0128384b152b58ea65303d66484c9d7bTimo Sirainenstatic void lmtp_address_translate(struct client *client, const char **address)
e7b1f9db0128384b152b58ea65303d66484c9d7bTimo Sirainen const char *transpos = client->lmtp_set->lmtp_address_translate;
e7b1f9db0128384b152b58ea65303d66484c9d7bTimo Sirainen /* check that string matches up to the first '%' */
e7b1f9db0128384b152b58ea65303d66484c9d7bTimo Sirainen /* find where the next string starts */
170834eda87ac8e6b8b6577738ef373fc68f117dTimo Sirainenclient_send_line_overquota(struct client *client,
170834eda87ac8e6b8b6577738ef373fc68f117dTimo Sirainen const struct mail_recipient *rcpt, const char *error)
170834eda87ac8e6b8b6577738ef373fc68f117dTimo Sirainen mail_storage_service_user_get_set(rcpt->service_user)[1];
170834eda87ac8e6b8b6577738ef373fc68f117dTimo Sirainen client_send_line(client, "%s <%s> %s", lda_set->quota_full_tempfail ?
170834eda87ac8e6b8b6577738ef373fc68f117dTimo Sirainen "452 4.2.2" : "552 5.2.2", rcpt->address, error);
02e8b5f55210f413853998a0ba5832eb3f9a6dc6Timo Sirainenlmtp_rcpt_to_is_over_quota(struct client *client,
2092da86f3a332e8d7eae1300a3b9852fed8f2f8Sergey Kitov /* mail user will be created second time when mail is saved,
2092da86f3a332e8d7eae1300a3b9852fed8f2f8Sergey Kitov so it's session_id needs to be different,
2092da86f3a332e8d7eae1300a3b9852fed8f2f8Sergey Kitov but second time session_id needs to be the same as rcpt session_id and
2092da86f3a332e8d7eae1300a3b9852fed8f2f8Sergey Kitov mail user session id for the first rcpt should not overlap with session id
2092da86f3a332e8d7eae1300a3b9852fed8f2f8Sergey Kitov of the second recipient, so add custom ":quota" suffix to the session_id without
2092da86f3a332e8d7eae1300a3b9852fed8f2f8Sergey Kitov session_id counter increment, so next time mail user will get
2092da86f3a332e8d7eae1300a3b9852fed8f2f8Sergey Kitov the same session id as rcpt */
2092da86f3a332e8d7eae1300a3b9852fed8f2f8Sergey Kitov ret = mail_storage_service_next_with_session_suffix(storage_service,
61d3fd14828b68d789f3df73d1dbed56e37b7931Timo Sirainen i_error("Failed to initialize user %s: %s", rcpt->address, errstr);
02e8b5f55210f413853998a0ba5832eb3f9a6dc6Timo Sirainen ns = mail_namespace_find_inbox(user->namespaces);
02e8b5f55210f413853998a0ba5832eb3f9a6dc6Timo Sirainen ret = mailbox_get_status(box, STATUS_CHECK_OVER_QUOTA, &status);
170834eda87ac8e6b8b6577738ef373fc68f117dTimo Sirainen client_send_line_overquota(client, rcpt, errstr);
8d2e45a300932ae6c49d2c9bf4b6fe7820cc25a8Martti Rannanjärvi i_error("mailbox_get_status(%s, STATUS_CHECK_OVER_QUOTA) "
8d2e45a300932ae6c49d2c9bf4b6fe7820cc25a8Martti Rannanjärvi mailbox_get_last_internal_error(box, NULL));
bc37bab86f0ae7b9d7149d2418340b88e6ef212fTimo Sirainenstatic bool cmd_rcpt_finish(struct client *client, struct mail_recipient *rcpt)
bc37bab86f0ae7b9d7149d2418340b88e6ef212fTimo Sirainen if ((ret = lmtp_rcpt_to_is_over_quota(client, rcpt)) != 0) {
bc37bab86f0ae7b9d7149d2418340b88e6ef212fTimo Sirainen client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
eb318ea05532d2e54ed3bfc89bc15dcf1adae838Timo Sirainen mail_storage_service_user_unref(&rcpt->service_user);
bc37bab86f0ae7b9d7149d2418340b88e6ef212fTimo Sirainen array_append(&client->state.rcpt_to, &rcpt, 1);
42abccd9b2a5a4190bd3c14ec2dcc10d51c0f491Timo Sirainenstatic void rcpt_anvil_lookup_callback(const char *reply, void *context)
553a131eddb46d97967fd9610408b7f0829eab6fTimo Sirainen const struct mail_storage_service_input *input;
3edfbca796edb8f1efeb48da916e109850adfc0fTimo Sirainen unsigned int parallel_count = 0;
42abccd9b2a5a4190bd3c14ec2dcc10d51c0f491Timo Sirainen /* lookup failed */
ced943b0a9b49a5be38516302fe1631c1883debaTimo Sirainen } else if (str_to_uint(reply, ¶llel_count) < 0) {
42abccd9b2a5a4190bd3c14ec2dcc10d51c0f491Timo Sirainen i_error("Invalid reply from anvil: %s", reply);
bc37bab86f0ae7b9d7149d2418340b88e6ef212fTimo Sirainen if (parallel_count >= client->lmtp_set->lmtp_user_concurrency_limit) {
bc37bab86f0ae7b9d7149d2418340b88e6ef212fTimo Sirainen client_send_line(client, ERRSTR_TEMP_USERDB_FAIL_PREFIX
bc37bab86f0ae7b9d7149d2418340b88e6ef212fTimo Sirainen "Too many concurrent deliveries for user",
eb318ea05532d2e54ed3bfc89bc15dcf1adae838Timo Sirainen mail_storage_service_user_unref(&rcpt->service_user);
553a131eddb46d97967fd9610408b7f0829eab6fTimo Sirainen input = mail_storage_service_user_get_input(rcpt->service_user);
553a131eddb46d97967fd9610408b7f0829eab6fTimo Sirainen master_service_anvil_send(master_service, t_strconcat(
553a131eddb46d97967fd9610408b7f0829eab6fTimo Sirainen "CONNECT\t", my_pid, "\t", master_service_get_name(master_service),
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainenint cmd_rcpt(struct client *client, const char *args)
f6008a666d9c3df66fd8a437369133e8519b4e24Timo Sirainen const char *params, *address, *username, *detail;
38f624b427aa8b6fad3765e6efd97c85a7f97a09Timo Sirainen const char *const *argv;
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen client_send_line(client, "503 5.5.1 MAIL needed first");
38f624b427aa8b6fad3765e6efd97c85a7f97a09Timo Sirainen parse_address(args + 3, &address, ¶ms) < 0) {
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen client_send_line(client, "501 5.5.4 Invalid parameters");
b02e7bac5b8b19795884229f64e6fbbd93a0d2deTimo Sirainen rcpt = p_new(client->state_pool, struct mail_recipient, 1);
b02e7bac5b8b19795884229f64e6fbbd93a0d2deTimo Sirainen rcpt->params.dsn_orcpt = parse_xtext(client, *argv + 6);
38f624b427aa8b6fad3765e6efd97c85a7f97a09Timo Sirainen client_send_line(client, "501 5.5.4 Unsupported options");
133a609acde637dd8f517903f9db978d96b7097eAki Tuomi message_detail_address_parse(client->unexpanded_lda_set->recipient_delimiter,
972c9172e9e6a0fc6053efb3d2ee9d354b67727fLennart Weller if (client_proxy_rcpt(client, address, username, detail, delim,
aeea3dbd1f4031634f7b318614adf51dcfc79f42Timo Sirainen /* Use a unique session_id for each mail delivery. This is especially
aeea3dbd1f4031634f7b318614adf51dcfc79f42Timo Sirainen important for stats process to not see duplicate sessions. */
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen ret = mail_storage_service_lookup(storage_service, &input,
f6008a666d9c3df66fd8a437369133e8519b4e24Timo Sirainen i_error("Failed to lookup user %s: %s", username, error);
f6008a666d9c3df66fd8a437369133e8519b4e24Timo Sirainen client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL, address);
9a935c34e98ba7a9cc90784ceb63b2fbdab4105fTimo Sirainen "550 5.1.1 <%s> User doesn't exist: %s",
a795ff053b41be95cb94a33cd5501f8d2e843e20Timo Sirainen /* NOTE: if this restriction is ever removed, we'll also need
a795ff053b41be95cb94a33cd5501f8d2e843e20Timo Sirainen to send different message bodies to local and proxy
a795ff053b41be95cb94a33cd5501f8d2e843e20Timo Sirainen (with and without Return-Path: header) */
a795ff053b41be95cb94a33cd5501f8d2e843e20Timo Sirainen "Can't handle mixed proxy/non-proxy destinations",
eb318ea05532d2e54ed3bfc89bc15dcf1adae838Timo Sirainen mail_storage_service_user_unref(&rcpt->service_user);
b02e7bac5b8b19795884229f64e6fbbd93a0d2deTimo Sirainen rcpt->address = p_strdup(client->state_pool, address);
b02e7bac5b8b19795884229f64e6fbbd93a0d2deTimo Sirainen rcpt->detail = p_strdup(client->state_pool, detail);
ced943b0a9b49a5be38516302fe1631c1883debaTimo Sirainen if (client->lmtp_set->lmtp_user_concurrency_limit == 0) {
d03980c62bca1d8501d67e5cf3b6f9904d166dd4Timo Sirainen /* NOTE: username may change as the result of the userdb
d03980c62bca1d8501d67e5cf3b6f9904d166dd4Timo Sirainen lookup. Look up the new one via service_user. */
d03980c62bca1d8501d67e5cf3b6f9904d166dd4Timo Sirainen const struct mail_storage_service_input *input =
d03980c62bca1d8501d67e5cf3b6f9904d166dd4Timo Sirainen mail_storage_service_user_get_input(rcpt->service_user);
42abccd9b2a5a4190bd3c14ec2dcc10d51c0f491Timo Sirainen rcpt->anvil_query = anvil_client_query(anvil, query,
ced943b0a9b49a5be38516302fe1631c1883debaTimo Sirainen /* stop processing further commands while anvil query is
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainenint cmd_quit(struct client *client, const char *args ATTR_UNUSED)
2ba9851abbaffe9592c06c0060dce40bb6da3b78Timo Sirainen /* don't log the (state name) for successful QUITs */
2ba9851abbaffe9592c06c0060dce40bb6da3b78Timo Sirainen i_info("Disconnect from %s: Successful quit", client_remote_id(client));
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)
38f624b427aa8b6fad3765e6efd97c85a7f97a09Timo Sirainenstatic bool orcpt_get_valid_rfc822(const char *orcpt, const char **addr_r)
38f624b427aa8b6fad3765e6efd97c85a7f97a09Timo Sirainen if (orcpt == NULL || strncasecmp(orcpt, "rfc822;", 7) != 0)
38f624b427aa8b6fad3765e6efd97c85a7f97a09Timo Sirainen /* FIXME: we should verify the address further */
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainenclient_deliver(struct client *client, const struct mail_recipient *rcpt,
ee77aba32f3d05ecd87cca445c77e1f1095035e2Timo Sirainen struct mail *src_mail, struct mail_deliver_session *session)
9a935c34e98ba7a9cc90784ceb63b2fbdab4105fTimo Sirainen const struct mail_storage_service_input *input;
9a935c34e98ba7a9cc90784ceb63b2fbdab4105fTimo Sirainen input = mail_storage_service_user_get_input(rcpt->service_user);
7ffa9e9809f10884a9167c19c09c980e3c1792b1Timo Sirainen mail_set = mail_storage_service_user_get_mail_set(rcpt->service_user);
a566d9f8797dd66b4d5432a20ab2b9f7c8f76102Timo Sirainen set_parser = mail_storage_service_user_get_settings_parser(rcpt->service_user);
7ffa9e9809f10884a9167c19c09c980e3c1792b1Timo Sirainen mail_set->mail_max_lock_timeout > client->proxy_timeout_secs)) {
a566d9f8797dd66b4d5432a20ab2b9f7c8f76102Timo Sirainen /* set lock timeout waits to be less than when proxy has
a566d9f8797dd66b4d5432a20ab2b9f7c8f76102Timo Sirainen advertised that it's going to timeout the connection.
a566d9f8797dd66b4d5432a20ab2b9f7c8f76102Timo Sirainen this avoids duplicate deliveries in case the delivery
a566d9f8797dd66b4d5432a20ab2b9f7c8f76102Timo Sirainen succeeds after the proxy has already disconnected from us. */
167dbb662c2ddedeb7b34383c18bdcf0537c0c84Timo Sirainen line = t_strdup_printf("mail_max_lock_timeout=%us",
a566d9f8797dd66b4d5432a20ab2b9f7c8f76102Timo Sirainen if (settings_parse_line(set_parser, line) < 0)
da0420f1b5d5f5ab1079d1204d70dd51866ce025Timo Sirainen /* get the timestamp before user is created, since it starts the I/O */
ca4526e3b5fbf5ea3dd477a2098522a44c9ac52cTimo Sirainen i_set_failure_prefix("lmtp(%s, %s): ", my_pid, username);
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen if (mail_storage_service_next(storage_service, rcpt->service_user,
61d3fd14828b68d789f3df73d1dbed56e37b7931Timo Sirainen i_error("Failed to initialize user: %s", error);
9a935c34e98ba7a9cc90784ceb63b2fbdab4105fTimo Sirainen client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen sets = mail_storage_service_user_get_set(rcpt->service_user);
ed89b9556292b6d7c2551086a4958f4ed601bda9Stephan Bosch var_table = mail_user_var_expand_table(client->state.dest_user);
0f5dc4da3982053036be65190e44bf28a67b1ca2Timo Sirainen if (settings_var_expand(&lda_setting_parser_info, lda_set, client->pool,
0f5dc4da3982053036be65190e44bf28a67b1ca2Timo Sirainen i_error("Failed to expand settings: %s", error);
0f5dc4da3982053036be65190e44bf28a67b1ca2Timo Sirainen client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
0f5dc4da3982053036be65190e44bf28a67b1ca2Timo Sirainen if (var_expand_with_funcs(str, client->state.dest_user->set->mail_log_prefix,
0f5dc4da3982053036be65190e44bf28a67b1ca2Timo Sirainen i_error("Failed to expand mail_log_prefix=%s: %s",
0f5dc4da3982053036be65190e44bf28a67b1ca2Timo Sirainen client->state.dest_user->set->mail_log_prefix, error);
0f5dc4da3982053036be65190e44bf28a67b1ca2Timo Sirainen client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
d25877a69c21fcd004f77bbfb1b8d0895d78e4ddTimo Sirainen dctx.timeout_secs = LDA_SUBMISSION_TIMEOUT_SECS;
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen dctx.src_envelope_sender = client->state.mail_from;
6b6011c2242e470b41316f92512b282b5e306dacTimo Sirainen timeval_diff_msecs(&client->state.data_end_timeval,
da0420f1b5d5f5ab1079d1204d70dd51866ce025Timo Sirainen dctx.delivery_time_started = delivery_time_started;
38f624b427aa8b6fad3765e6efd97c85a7f97a09Timo Sirainen if (orcpt_get_valid_rfc822(rcpt->params.dsn_orcpt, &dctx.dest_addr)) {
38f624b427aa8b6fad3765e6efd97c85a7f97a09Timo Sirainen /* used ORCPT */
38f624b427aa8b6fad3765e6efd97c85a7f97a09Timo Sirainen } else if (*dctx.set->lda_original_recipient_header != '\0') {
ee77aba32f3d05ecd87cca445c77e1f1095035e2Timo Sirainen dctx.dest_addr = mail_deliver_get_address(src_mail,
16dfe1f9c1c1a5abc5e5a12efa25619586f7e4e6Timo Sirainen !client->lmtp_set->lmtp_save_to_detail_mailbox)
ee77aba32f3d05ecd87cca445c77e1f1095035e2Timo Sirainen ns = mail_namespace_find_inbox(dctx.dest_user->namespaces);
ee77aba32f3d05ecd87cca445c77e1f1095035e2Timo Sirainen dctx.save_dest_mail = array_count(&client->state.rcpt_to) > 1 &&
ee77aba32f3d05ecd87cca445c77e1f1095035e2Timo Sirainen i_assert(client->state.first_saved_mail == NULL);
ee77aba32f3d05ecd87cca445c77e1f1095035e2Timo Sirainen client->state.first_saved_mail = dctx.dest_mail;
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen client_send_line(client, "250 2.0.0 <%s> %s Saved",
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen error = mail_storage_get_last_error(storage, &mail_error);
170834eda87ac8e6b8b6577738ef373fc68f117dTimo Sirainen client_send_line_overquota(client, rcpt, error);
b9dce659b9135c87c7708b2bb0f14e8742db7e15Timo Sirainen /* This shouldn't happen */
b9dce659b9135c87c7708b2bb0f14e8742db7e15Timo Sirainen i_error("BUG: Saving failed to unknown storage");
b9dce659b9135c87c7708b2bb0f14e8742db7e15Timo Sirainen client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
0c9dda2bad67db38d67d92d64839a14214f7d293Timo Sirainenstatic bool client_rcpt_to_is_last(struct client *client)
0c9dda2bad67db38d67d92d64839a14214f7d293Timo Sirainen return client->state.rcpt_idx >= array_count(&client->state.rcpt_to);
bad08efc4476d3ab6f13b10e8b78e05014a93ba7Sergey Kitovstatic uid_t client_deliver_to_rcpts(struct client *client,
ee77aba32f3d05ecd87cca445c77e1f1095035e2Timo Sirainen unsigned int count;
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen rcpts = array_get(&client->state.rcpt_to, &count);
b02e7bac5b8b19795884229f64e6fbbd93a0d2deTimo Sirainen ret = client_deliver(client, rcpts[client->state.rcpt_idx],
bad08efc4476d3ab6f13b10e8b78e05014a93ba7Sergey Kitov /* succeeded and mail_user is not saved in first_saved_mail */
bad08efc4476d3ab6f13b10e8b78e05014a93ba7Sergey Kitov if ((ret == 0 &&
bad08efc4476d3ab6f13b10e8b78e05014a93ba7Sergey Kitov client->state.first_saved_mail == src_mail)) ||
bad08efc4476d3ab6f13b10e8b78e05014a93ba7Sergey Kitov /* failed. try the next one. */
bad08efc4476d3ab6f13b10e8b78e05014a93ba7Sergey Kitov (ret != 0 && client->state.dest_user != NULL)) {
0c9dda2bad67db38d67d92d64839a14214f7d293Timo Sirainen mail_user_autoexpunge(client->state.dest_user);
bad08efc4476d3ab6f13b10e8b78e05014a93ba7Sergey Kitov } else if (ret == 0) {
bad08efc4476d3ab6f13b10e8b78e05014a93ba7Sergey Kitov /* use the first saved message to save it elsewhere too.
bad08efc4476d3ab6f13b10e8b78e05014a93ba7Sergey Kitov this might allow hard linking the files.
bad08efc4476d3ab6f13b10e8b78e05014a93ba7Sergey Kitov mail_user is saved in first_saved_mail,
bad08efc4476d3ab6f13b10e8b78e05014a93ba7Sergey Kitov will be unreferenced later on */
efb0def040a3e98da05ea77c170cd6711a9c8747Timo Sirainenstatic void client_rcpt_fail_all(struct client *client)
b02e7bac5b8b19795884229f64e6fbbd93a0d2deTimo Sirainen array_foreach(&client->state.rcpt_to, rcptp) {
9a935c34e98ba7a9cc90784ceb63b2fbdab4105fTimo Sirainen client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainenstatic struct istream *client_get_input(struct client *client)
9deb15b8dec1a593940d72532ececc673c6b48ffTimo Sirainen inputs[0] = i_stream_create_from_data(state->added_headers,
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen inputs[1] = i_stream_create_fd(state->mail_data_fd,
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen inputs[1] = i_stream_create_from_data(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_header_lookup_ctx *headers_ctx;
15dd1857d40544e2c356d4f1c87c42a0579c665cTimo Sirainen if (raw_mailbox_alloc_stream(client->raw_mail_user, input,
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen i_error("Can't open delivery mail as raw: %s",
bf7dc750b95039981c0e9d728f313d50cf38a156Martti Rannanjärvi mailbox_get_last_internal_error(box, &error));
0dab9cb35a976c49b28a11e28d5570f5191f1a7aMartti Rannanjärvi trans = mailbox_transaction_begin(box, 0, __func__);
315ce5be539bfe8bc7777ab0654499c49583cea2Timo Sirainen headers_ctx = mailbox_header_lookup_init(box, wanted_headers);
8aa60989067046e35c4809a616b46473fce0113eTimo Sirainen client->state.raw_mail = mail_alloc(trans, 0, headers_ctx);
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainenclient_input_data_write_local(struct client *client, struct istream *input)
bad08efc4476d3ab6f13b10e8b78e05014a93ba7Sergey Kitov first_uid = client_deliver_to_rcpts(client, session);
ee77aba32f3d05ecd87cca445c77e1f1095035e2Timo Sirainen struct mail *mail = client->state.first_saved_mail;
ee77aba32f3d05ecd87cca445c77e1f1095035e2Timo Sirainen struct mailbox_transaction_context *trans = mail->transaction;
ee77aba32f3d05ecd87cca445c77e1f1095035e2Timo Sirainen /* just in case these functions are going to write anything,
ee77aba32f3d05ecd87cca445c77e1f1095035e2Timo Sirainen change uid back to user's own one */
75e789ce60fba8e9ad07f51a8fd186fa65823328Timo Sirainen /* switch back to running as root, since that's what we're
75e789ce60fba8e9ad07f51a8fd186fa65823328Timo Sirainen practically doing anyway. it's also important in case we
75e789ce60fba8e9ad07f51a8fd186fa65823328Timo Sirainen lose e.g. config connection and need to reconnect to it. */
ffaa309c211897ab875bbe0b093bc7e709bb1e5dTimo Sirainen /* enable core dumping again. we need to chdir also to
ffaa309c211897ab875bbe0b093bc7e709bb1e5dTimo Sirainen root-owned directory to get core dumps. */
c2ebc8f28b5504f280cd5d4adfe57ed70f9a7d83Timo Sirainenstatic void client_input_data_finish(struct client *client)
9deb15b8dec1a593940d72532ececc673c6b48ffTimo Sirainenstatic const char *client_get_added_headers(struct client *client)
9deb15b8dec1a593940d72532ececc673c6b48ffTimo Sirainen if (array_count(&client->state.rcpt_to) == 1) {
1ac7b6969120bfdb15497663229f0dd168ae4ab8Timo Sirainen sets = mail_storage_service_user_get_set((*rcptp)->service_user);
1ac7b6969120bfdb15497663229f0dd168ae4ab8Timo Sirainen switch (lmtp_set->parsed_lmtp_hdr_delivery_address) {
1ac7b6969120bfdb15497663229f0dd168ae4ab8Timo Sirainen if (!orcpt_get_valid_rfc822((*rcptp)->params.dsn_orcpt,
7f250fe7eede5f1d17c5e177c7f2dc0dd742e224Timo Sirainen /* don't set Return-Path when proxying so it won't get added twice */
7f250fe7eede5f1d17c5e177c7f2dc0dd742e224Timo Sirainen if (array_count(&client->state.rcpt_to) > 0) {
a192134d1aaf3ff73bff040c7cd9ab3e9a8fe1ddTimo Sirainen str_printfa(str, "Delivered-To: %s\r\n", rcpt_to);
fcd443a32b01c4da131f36649d5a5fa5f8452dcfTimo Sirainen str_printfa(str, "Received: from %s", client->lhlo);
0dc719a06fc1bbe11238762f9beb0c1faa4a2b75Timo Sirainen ssl_iostream_get_security_string(client->ssl_iostream));
eed03830015b7138b9d4522e72bef650aa24b45fTimo Sirainen str_printfa(str, "; %s\r\n", message_date_create(ioloop_time));
42abccd9b2a5a4190bd3c14ec2dcc10d51c0f491Timo Sirainenstatic void client_input_data_write(struct client *client)
fb340f9dfd8ca3a66ef6830c047333a220e8f9b3Timo Sirainen /* stop handling client input until saving/proxying is finished */
6b6011c2242e470b41316f92512b282b5e306dacTimo Sirainen client->state.data_end_timeval = ioloop_timeval;
dd3ccdbb29dad006f7781ea138a5ba39727963c4Timo Sirainenstatic int client_input_add_file(struct client *client,
dd3ccdbb29dad006f7781ea138a5ba39727963c4Timo Sirainen /* continue writing to file */
b5babd4ea611616f0b3b2b9fb5917c78d1999a7bTimo Sirainen /* move everything to a temporary file. */
4afd50dff16661684ad2acddad7284bcd8c564dbTimo Sirainen mail_user_set_get_temp_prefix(path, client->raw_mail_user->set);
dd3ccdbb29dad006f7781ea138a5ba39727963c4Timo Sirainen fd = safe_mkstemp_hostpid(path, 0600, (uid_t)-1, (gid_t)-1);
4afd50dff16661684ad2acddad7284bcd8c564dbTimo Sirainen i_error("Temp file creation to %s failed: %m", str_c(path));
dd3ccdbb29dad006f7781ea138a5ba39727963c4Timo Sirainen /* we just want the fd, unlink it */
dd3ccdbb29dad006f7781ea138a5ba39727963c4Timo Sirainen /* shouldn't happen.. */
2dad3ab8045be38711f277f065b76f76bb5f63f8Timo Sirainen state->mail_data_output = o_stream_create_fd_file(fd, 0, FALSE);
61d89b0ca0300e230c7882b71e8360edf62d9cd0Timo Sirainen o_stream_set_name(state->mail_data_output, str_c(path));
61d89b0ca0300e230c7882b71e8360edf62d9cd0Timo Sirainen state->mail_data->data, state->mail_data->used);
61d89b0ca0300e230c7882b71e8360edf62d9cd0Timo Sirainen o_stream_nsend(client->state.mail_data_output, data, size);
61d89b0ca0300e230c7882b71e8360edf62d9cd0Timo Sirainen if (o_stream_nfinish(client->state.mail_data_output) < 0) {
61d89b0ca0300e230c7882b71e8360edf62d9cd0Timo Sirainen o_stream_get_error(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");
85f8811e47003717620cbe066bb05494124308faTimo Sirainen /* client probably disconnected */
3bb61142ca8dc0c71efd430fa8f805a4c7262aa8Timo Sirainen /* the ending "." line was seen. begin saving the mail. */
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");
9deb15b8dec1a593940d72532ececc673c6b48ffTimo Sirainen p_strdup(client->state_pool, client_get_added_headers(client));
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);
75e2c45e2e60ba22728b7520818aa0a191f8ca3dTimo Sirainen /* send the DATA reply immediately before we start handling any data */
701c9fc6dc06415bbf15490125e001d9db8c7cbdTimo Sirainen client->io = io_add(client->fd_in, IO_READ, client_input_data, client);
b015d3d3af5b1c0fd526f31d78229bf9f633db56Timo Sirainenint cmd_xclient(struct client *client, const char *args)
b015d3d3af5b1c0fd526f31d78229bf9f633db56Timo Sirainen const char *const *tmp;
009217abb57a24a4076092e8e4e165545747839eStephan Bosch unsigned int ttl = UINT_MAX, timeout_secs = 0;
b015d3d3af5b1c0fd526f31d78229bf9f633db56Timo Sirainen client_send_line(client, "550 You are not from trusted IP");
b015d3d3af5b1c0fd526f31d78229bf9f633db56Timo Sirainen for (tmp = t_strsplit(args, " "); *tmp != NULL; tmp++) {
b015d3d3af5b1c0fd526f31d78229bf9f633db56Timo Sirainen } else if (strncasecmp(*tmp, "PORT=", 5) == 0) {
ddbdc644a15f56f4b43596f1b8c0fc196c101445Timo Sirainen } else if (strncasecmp(*tmp, "TTL=", 4) == 0) {
a566d9f8797dd66b4d5432a20ab2b9f7c8f76102Timo Sirainen } else if (strncasecmp(*tmp, "TIMEOUT=", 8) == 0) {
b015d3d3af5b1c0fd526f31d78229bf9f633db56Timo Sirainen client_send_line(client, "501 Invalid parameters");
b015d3d3af5b1c0fd526f31d78229bf9f633db56Timo Sirainen /* args ok, set them and reset the state */