1 /* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
2
3 #include "lib.h"
4 #include "array.h"
5 #include "ioloop.h"
6 #include "smtp-parser.h"
7 #include "smtp-syntax.h"
8 #include "smtp-address.h"
9
10 #include "smtp-server-private.h"
11
12 /* MAIL command */
13
cmd_mail_check_state(struct smtp_server_cmd_ctx * cmd)14 static bool cmd_mail_check_state(struct smtp_server_cmd_ctx *cmd)
15 {
16 struct smtp_server_connection *conn = cmd->conn;
17 struct smtp_server_command *command = cmd->cmd;
18
19 if (smtp_server_command_is_replied(command) &&
20 !smtp_server_command_replied_success(command) &&
21 !smtp_server_command_reply_is_forwarded(command))
22 return FALSE;
23
24 if (conn->state.trans != NULL) {
25 smtp_server_reply(cmd, 503, "5.5.0", "MAIL already given");
26 return FALSE;
27 }
28 return TRUE;
29 }
30
31 static void
cmd_mail_completed(struct smtp_server_cmd_ctx * cmd,struct smtp_server_cmd_mail * data)32 cmd_mail_completed(struct smtp_server_cmd_ctx *cmd,
33 struct smtp_server_cmd_mail *data)
34 {
35 struct smtp_server_connection *conn = cmd->conn;
36 struct smtp_server_command *command = cmd->cmd;
37
38 i_assert(conn->state.pending_mail_cmds > 0);
39 conn->state.pending_mail_cmds--;
40
41 i_assert(smtp_server_command_is_replied(command));
42 i_assert(conn->state.state == SMTP_SERVER_STATE_MAIL_FROM ||
43 !smtp_server_command_replied_success(command));
44
45 if (!smtp_server_command_replied_success(command)) {
46 /* Failure */
47 return;
48 }
49
50 /* Success */
51 conn->state.trans = smtp_server_transaction_create(conn, data);
52 }
53
54 static void
cmd_mail_recheck(struct smtp_server_cmd_ctx * cmd,struct smtp_server_cmd_mail * data)55 cmd_mail_recheck(struct smtp_server_cmd_ctx *cmd,
56 struct smtp_server_cmd_mail *data)
57 {
58 struct smtp_server_connection *conn = cmd->conn;
59
60 /* All preceeding commands have finished and now the transaction state
61 is clear. This provides the opportunity to re-check the transaction
62 state */
63 if (!cmd_mail_check_state(cmd))
64 return;
65
66 /* Advance state */
67 smtp_server_connection_set_state(conn, SMTP_SERVER_STATE_MAIL_FROM,
68 smtp_address_encode(data->path));
69 }
70
smtp_server_cmd_mail(struct smtp_server_cmd_ctx * cmd,const char * params)71 void smtp_server_cmd_mail(struct smtp_server_cmd_ctx *cmd,
72 const char *params)
73 {
74 struct smtp_server_connection *conn = cmd->conn;
75 const struct smtp_server_settings *set = &conn->set;
76 enum smtp_capability caps = set->capabilities;
77 const struct smtp_server_callbacks *callbacks = conn->callbacks;
78 struct smtp_server_command *command = cmd->cmd;
79 struct smtp_server_cmd_mail *mail_data;
80 enum smtp_address_parse_flags path_parse_flags;
81 const char *const *param_extensions = NULL;
82 struct smtp_address *path = NULL;
83 enum smtp_param_parse_error pperror;
84 const char *error;
85 int ret;
86
87 /* mail = "MAIL FROM:" Reverse-path [SP Mail-parameters] CRLF
88 Reverse-path = Path / "<>"
89 */
90
91 /* Check transaction state as far as possible */
92 if (!cmd_mail_check_state(cmd))
93 return;
94
95 /* Reverse-path */
96 if (params == NULL || strncasecmp(params, "FROM:", 5) != 0) {
97 smtp_server_reply(cmd, 501, "5.5.4", "Invalid parameters");
98 return;
99 }
100 if (params[5] != ' ' && params[5] != '\t') {
101 params += 5;
102 } else if ((set->workarounds &
103 SMTP_SERVER_WORKAROUND_WHITESPACE_BEFORE_PATH) != 0) {
104 params += 5;
105 while (*params == ' ' || *params == '\t')
106 params++;
107 } else {
108 smtp_server_reply(cmd, 501, "5.5.4",
109 "Invalid FROM: "
110 "Unexpected whitespace before path");
111 return;
112 }
113 path_parse_flags =
114 SMTP_ADDRESS_PARSE_FLAG_ALLOW_EMPTY |
115 SMTP_ADDRESS_PARSE_FLAG_PRESERVE_RAW;
116 if (*params != '\0' &&
117 (set->workarounds & SMTP_SERVER_WORKAROUND_MAILBOX_FOR_PATH) != 0)
118 path_parse_flags |= SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL;
119 if (set->mail_path_allow_broken) {
120 path_parse_flags |=
121 SMTP_ADDRESS_PARSE_FLAG_ALLOW_BAD_LOCALPART |
122 SMTP_ADDRESS_PARSE_FLAG_IGNORE_BROKEN;
123 }
124 ret = smtp_address_parse_path_full(pool_datastack_create(), params,
125 path_parse_flags, &path, &error,
126 ¶ms);
127 if (ret < 0 && !smtp_address_is_broken(path)) {
128 smtp_server_reply(cmd, 501, "5.5.4",
129 "Invalid FROM: %s", error);
130 return;
131 }
132 if (*params == ' ')
133 params++;
134 else if (*params != '\0') {
135 smtp_server_reply(
136 cmd, 501, "5.5.4",
137 "Invalid FROM: Invalid character in path");
138 return;
139 }
140 if (ret < 0) {
141 i_assert(set->mail_path_allow_broken);
142 e_debug(conn->event, "Invalid FROM: %s "
143 "(proceeding with <> as sender)", error);
144 }
145
146 mail_data = p_new(cmd->pool, struct smtp_server_cmd_mail, 1);
147
148 if (conn->set.protocol == SMTP_PROTOCOL_LMTP)
149 mail_data->flags |= SMTP_SERVER_TRANSACTION_FLAG_REPLY_PER_RCPT;
150
151 /* [SP Mail-parameters] */
152 if (array_is_created(&conn->mail_param_extensions))
153 param_extensions = array_front(&conn->mail_param_extensions);
154 if (smtp_params_mail_parse(cmd->pool, params, caps, param_extensions,
155 NULL, &mail_data->params, &pperror,
156 &error) < 0) {
157 switch (pperror) {
158 case SMTP_PARAM_PARSE_ERROR_BAD_SYNTAX:
159 smtp_server_reply(cmd, 501, "5.5.4", "%s", error);
160 break;
161 case SMTP_PARAM_PARSE_ERROR_NOT_SUPPORTED:
162 smtp_server_reply(cmd, 555, "5.5.4", "%s", error);
163 break;
164 default:
165 i_unreached();
166 }
167 return;
168 }
169
170 if ((caps & SMTP_CAPABILITY_SIZE) != 0 && set->max_message_size > 0 &&
171 mail_data->params.size > set->max_message_size) {
172 smtp_server_reply(cmd, 552, "5.2.3",
173 "Message size exceeds administrative limit");
174 return;
175 }
176
177 mail_data->path = smtp_address_clone(cmd->pool, path);
178 mail_data->timestamp = ioloop_timeval;
179
180 smtp_server_command_add_hook(command, SMTP_SERVER_COMMAND_HOOK_NEXT,
181 cmd_mail_recheck, mail_data);
182 smtp_server_command_add_hook(command, SMTP_SERVER_COMMAND_HOOK_COMPLETED,
183 cmd_mail_completed, mail_data);
184
185 smtp_server_connection_set_state(conn, SMTP_SERVER_STATE_MAIL_FROM,
186 smtp_address_encode(mail_data->path));
187 conn->state.pending_mail_cmds++;
188
189 smtp_server_command_ref(command);
190 if (callbacks != NULL && callbacks->conn_cmd_mail != NULL) {
191 /* Specific implementation of MAIL command */
192 ret = callbacks->conn_cmd_mail(conn->context, cmd, mail_data);
193 if (ret <= 0) {
194 i_assert(ret == 0 ||
195 smtp_server_command_is_replied(command));
196 /* Command is waiting for external event or it failed */
197 smtp_server_command_unref(&command);
198 return;
199 }
200 }
201 if (!smtp_server_command_is_replied(command)) {
202 /* Set generic MAIL success reply if none is provided */
203 smtp_server_cmd_mail_reply_success(cmd);
204 }
205 smtp_server_command_unref(&command);
206 }
207
smtp_server_cmd_mail_reply_success(struct smtp_server_cmd_ctx * cmd)208 void smtp_server_cmd_mail_reply_success(struct smtp_server_cmd_ctx *cmd)
209 {
210 i_assert(cmd->cmd->reg->func == smtp_server_cmd_mail);
211
212 smtp_server_reply(cmd, 250, "2.1.0", "OK");
213 }
214