commands.c revision 61d3fd14828b68d789f3df73d1dbed56e37b7931
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2009-2016 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)
42abccd9b2a5a4190bd3c14ec2dcc10d51c0f491Timo Sirainenstatic void client_input_data_write(struct client *client);
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 == '@')
9a935c34e98ba7a9cc90784ceb63b2fbdab4105fTimo Sirainenstatic void rcpt_address_parse(struct client *client, const char *address,
7b22071c71a99bcb71ee9fcfbb83ed5bf5e08a2dTimo Sirainen const char *p, *domain;
32efae185f1f86167b3f00ea84f8502940c6c677Timo Sirainen if (*client->unexpanded_lda_set->recipient_delimiter == '\0')
972c9172e9e6a0fc6053efb3d2ee9d354b67727fLennart Weller /* first character that matches the recipient_delimiter */
972c9172e9e6a0fc6053efb3d2ee9d354b67727fLennart Weller idx = strcspn(address, client->unexpanded_lda_set->recipient_delimiter);
972c9172e9e6a0fc6053efb3d2ee9d354b67727fLennart Weller p = address[idx] != '\0' ? address + idx : NULL;
7b22071c71a99bcb71ee9fcfbb83ed5bf5e08a2dTimo Sirainen if (p != NULL && (domain == NULL || p < domain)) {
9a935c34e98ba7a9cc90784ceb63b2fbdab4105fTimo Sirainen /* user+detail@domain */
7b22071c71a99bcb71ee9fcfbb83ed5bf5e08a2dTimo Sirainen *username_r = t_strconcat(*username_r, domain, NULL);
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 unsigned int len;
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,
02e8b5f55210f413853998a0ba5832eb3f9a6dc6Timo Sirainen ret = mail_storage_service_next(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);
42abccd9b2a5a4190bd3c14ec2dcc10d51c0f491Timo Sirainenstatic void rcpt_anvil_lookup_callback(const char *reply, void *context)
42abccd9b2a5a4190bd3c14ec2dcc10d51c0f491Timo Sirainen i_assert(rcpt->client->state.anvil_queries > 0);
42abccd9b2a5a4190bd3c14ec2dcc10d51c0f491Timo Sirainen /* lookup failed */
42abccd9b2a5a4190bd3c14ec2dcc10d51c0f491Timo Sirainen } else if (str_to_uint(reply, &rcpt->parallel_count) < 0) {
42abccd9b2a5a4190bd3c14ec2dcc10d51c0f491Timo Sirainen i_error("Invalid reply from anvil: %s", reply);
42abccd9b2a5a4190bd3c14ec2dcc10d51c0f491Timo Sirainen if (--rcpt->client->state.anvil_queries == 0 &&
42abccd9b2a5a4190bd3c14ec2dcc10d51c0f491Timo Sirainen rcpt->client->state.anvil_pending_data_write) {
42abccd9b2a5a4190bd3c14ec2dcc10d51c0f491Timo Sirainen /* DATA command was finished, but we were still waiting on
42abccd9b2a5a4190bd3c14ec2dcc10d51c0f491Timo Sirainen anvil before handling any users */
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");
972c9172e9e6a0fc6053efb3d2ee9d354b67727fLennart Weller rcpt_address_parse(client, address, &username, &delim, &detail);
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",
191a22ee13135fd752fcf03a43ef2f1b070937f5Timo Sirainen mail_storage_service_user_free(&rcpt->service_user);
b02e7bac5b8b19795884229f64e6fbbd93a0d2deTimo Sirainen rcpt->address = p_strdup(client->state_pool, address);
b02e7bac5b8b19795884229f64e6fbbd93a0d2deTimo Sirainen rcpt->detail = p_strdup(client->state_pool, detail);
191a22ee13135fd752fcf03a43ef2f1b070937f5Timo Sirainen if ((ret = lmtp_rcpt_to_is_over_quota(client, rcpt)) != 0) {
191a22ee13135fd752fcf03a43ef2f1b070937f5Timo Sirainen client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
191a22ee13135fd752fcf03a43ef2f1b070937f5Timo Sirainen mail_storage_service_user_free(&rcpt->service_user);
191a22ee13135fd752fcf03a43ef2f1b070937f5Timo Sirainen array_append(&client->state.rcpt_to, &rcpt, 1);
42abccd9b2a5a4190bd3c14ec2dcc10d51c0f491Timo Sirainen if (client->lmtp_set->lmtp_user_concurrency_limit > 0) {
42abccd9b2a5a4190bd3c14ec2dcc10d51c0f491Timo Sirainen rcpt->anvil_query = anvil_client_query(anvil, query,
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);
f24f2b5e17ea1ce029b5627a1dc50ba80f02c9c7Timo Sirainen if (client->lmtp_set->lmtp_user_concurrency_limit > 0 &&
f24f2b5e17ea1ce029b5627a1dc50ba80f02c9c7Timo Sirainen rcpt->parallel_count >= client->lmtp_set->lmtp_user_concurrency_limit) {
42abccd9b2a5a4190bd3c14ec2dcc10d51c0f491Timo Sirainen client_send_line(client, ERRSTR_TEMP_USERDB_FAIL_PREFIX
42abccd9b2a5a4190bd3c14ec2dcc10d51c0f491Timo Sirainen "Too many concurrent deliveries for 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. */
a566d9f8797dd66b4d5432a20ab2b9f7c8f76102Timo Sirainen line = t_strdup_printf("mail_max_lock_timeout=%u",
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,
18458233e89c8467e30d8b93b1823128f26bc174Timo Sirainen var_expand(str, client->state.dest_user->set->mail_log_prefix,
18458233e89c8467e30d8b93b1823128f26bc174Timo Sirainen mail_user_var_expand_table(client->state.dest_user));
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen sets = mail_storage_service_user_get_set(rcpt->service_user);
8c30a8e508dd05b63c9b1fa7ae9c637d132dac6fTimo Sirainen settings_var_expand(&lda_setting_parser_info, lda_set, client->pool,
ee77aba32f3d05ecd87cca445c77e1f1095035e2Timo Sirainen mail_user_var_expand_table(client->state.dest_user));
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 &&
42abccd9b2a5a4190bd3c14ec2dcc10d51c0f491Timo Sirainen if (client->lmtp_set->lmtp_user_concurrency_limit > 0) {
42abccd9b2a5a4190bd3c14ec2dcc10d51c0f491Timo Sirainen master_service_anvil_send(master_service, t_strconcat(
42abccd9b2a5a4190bd3c14ec2dcc10d51c0f491Timo Sirainen "CONNECT\t", my_pid, "\t", master_service_get_name(master_service),
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,
42abccd9b2a5a4190bd3c14ec2dcc10d51c0f491Timo Sirainen if (client->lmtp_set->lmtp_user_concurrency_limit > 0) {
42abccd9b2a5a4190bd3c14ec2dcc10d51c0f491Timo Sirainen master_service_anvil_send(master_service, t_strconcat(
42abccd9b2a5a4190bd3c14ec2dcc10d51c0f491Timo Sirainen "DISCONNECT\t", my_pid, "\t", master_service_get_name(master_service),
ee77aba32f3d05ecd87cca445c77e1f1095035e2Timo Sirainenstatic bool client_deliver_next(struct client *client, struct mail *src_mail,
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],
ee77aba32f3d05ecd87cca445c77e1f1095035e2Timo Sirainen /* failed. try the next one. */
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",
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)
ee77aba32f3d05ecd87cca445c77e1f1095035e2Timo Sirainen while (client_deliver_next(client, src_mail, session)) {
ee77aba32f3d05ecd87cca445c77e1f1095035e2Timo Sirainen /* use the first saved message to save it elsewhere too.
ee77aba32f3d05ecd87cca445c77e1f1095035e2Timo Sirainen this might allow hard linking the files. */
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 */
42abccd9b2a5a4190bd3c14ec2dcc10d51c0f491Timo Sirainen client->state.anvil_pending_data_write = TRUE;
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 */