comparison src/smtp_in.c @ 80:e5090ac234cf

refactoring plus one small bugfix replaced deep nested conditionals with early exits fixed a small bug in the same go (Note: it is bad to fix bugs during refactoring): The SMTP_HELO case did not break in case of error. Now it does.
author meillo@marmaro.de
date Sat, 19 Jun 2010 11:11:28 +0200
parents f671821d8222
children 71ce3a1568e9
comparison
equal deleted inserted replaced
79:d2bee9f4625c 80:e5090ac234cf
1 /* MasqMail 1 /* MasqMail
2 Copyright (C) 1999-2001 Oliver Kurth 2 Copyright (C) 1999-2001 Oliver Kurth
3 Copyright (C) 2010 markus schnalke <meillo@marmaro.de>
3 4
4 This program is free software; you can redistribute it and/or modify 5 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by 6 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or 7 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version. 8 (at your option) any later version.
45 static smtp_cmd_id 46 static smtp_cmd_id
46 get_id(const gchar * line) 47 get_id(const gchar * line)
47 { 48 {
48 gint i; 49 gint i;
49 for (i = 0; i < SMTP_NUM_IDS; i++) { 50 for (i = 0; i < SMTP_NUM_IDS; i++) {
50 if (strncasecmp(smtp_cmds[i].cmd, line, strlen(smtp_cmds[i].cmd)) == 0) 51 if (strncasecmp(smtp_cmds[i].cmd, line, strlen(smtp_cmds[i].cmd)) == 0) {
51 return (smtp_cmd_id) i; 52 return (smtp_cmd_id) i;
53 }
52 } 54 }
53 return SMTP_ERROR; 55 return SMTP_ERROR;
54 } 56 }
55 57
56 /* this is a quick hack: we expect the address to be syntactically correct 58 /* this is a quick hack: we expect the address to be syntactically correct
57 and containing the mailbox only: 59 and containing the mailbox only:
58 */ 60 */
59 static gboolean 61 static gboolean
60 get_address(gchar * line, gchar * addr) 62 get_address(gchar * line, gchar * addr)
61 { 63 {
62 gchar *p = line, *q = addr; 64 gchar *p = line;
65 gchar *q = addr;
63 66
64 /* skip MAIL FROM: and RCPT TO: */ 67 /* skip MAIL FROM: and RCPT TO: */
65 while (*p && (*p != ':')) 68 while (*p && (*p != ':')) {
66 p++; 69 p++;
70 }
67 p++; 71 p++;
68 72
69 /* skip spaces: */ 73 /* skip spaces: */
70 while (*p && isspace(*p)) 74 while (*p && isspace(*p)) {
71 p++; 75 p++;
76 }
72 77
73 /* get address: */ 78 /* get address: */
74 while (*p && !isspace(*p) && (q < addr + MAX_ADDRESS - 1)) 79 while (*p && !isspace(*p) && (q < addr + MAX_ADDRESS - 1)) {
75 *(q++) = *(p++); 80 *(q++) = *(p++);
81 }
76 *q = 0; 82 *q = 0;
77 83
78 return TRUE; 84 return TRUE;
79 } 85 }
80 86
81 static smtp_connection* 87 static smtp_connection*
82 create_base(gchar * remote_host) 88 create_base(gchar * remote_host)
83 { 89 {
84 smtp_connection *base = g_malloc(sizeof(smtp_connection)); 90 smtp_connection *base = g_malloc(sizeof(smtp_connection));
85 if (base) { 91 if (!base) {
86 base->remote_host = g_strdup(remote_host); 92 return NULL;
87 93 }
88 base->prot = PROT_SMTP; 94
89 base->next_id = 0; 95 base->remote_host = g_strdup(remote_host);
90 base->helo_seen = 0; 96
91 base->from_seen = 0; 97 base->prot = PROT_SMTP;
92 base->rcpt_seen = 0; 98 base->next_id = 0;
93 base->msg = NULL; 99 base->helo_seen = 0;
94 100 base->from_seen = 0;
95 return base; 101 base->rcpt_seen = 0;
96 } 102 base->msg = NULL;
97 return NULL; 103
104 return base;
98 } 105 }
99 106
100 static void 107 static void
101 smtp_printf(FILE * out, gchar * fmt, ...) 108 smtp_printf(FILE * out, gchar * fmt, ...)
102 { 109 {
133 140
134 psc = create_base(remote_host); 141 psc = create_base(remote_host);
135 psc->msg = msg; 142 psc->msg = msg;
136 143
137 buffer = (gchar *) g_malloc(BUF_LEN); 144 buffer = (gchar *) g_malloc(BUF_LEN);
138 if (buffer) { 145 if (!buffer) {
139 /* send greeting string, containing ESMTP: */ 146 /* this check is actually unneccessary as g_malloc()
140 smtp_printf(out, "220 %s MasqMail %s ESMTP\r\n", conf.host_name, VERSION); 147 aborts on failure */
141 148 return;
142 while ((len = read_sockline(in, buffer, BUF_LEN, 5 * 60, READSOCKL_CHUG)) >= 0) { 149 }
143 cmd_id = get_id(buffer); 150
144 151 /* send greeting string, containing ESMTP: */
145 switch (cmd_id) { 152 smtp_printf(out, "220 %s MasqMail %s ESMTP\r\n", conf.host_name, VERSION);
146 case SMTP_EHLO: 153
147 psc->prot = PROT_ESMTP; 154 while ((len = read_sockline(in, buffer, BUF_LEN, 5 * 60, READSOCKL_CHUG)) >= 0) {
148 /* fall through */ 155 cmd_id = get_id(buffer);
149 case SMTP_HELO: 156
150 psc->helo_seen = TRUE; 157 switch (cmd_id) {
151 158 case SMTP_EHLO:
152 if (!conf.defer_all) { /* I need this to debug delivery failures */ 159 psc->prot = PROT_ESMTP;
153 if (psc->prot == PROT_ESMTP) { 160 /* fall through */
154 smtp_printf(out, "250-%s Nice to meet you with ESMTP\r\n", conf.host_name); 161 case SMTP_HELO:
155 /* not yet: fprintf(out, "250-SIZE\r\n"); */ 162 psc->helo_seen = TRUE;
156 smtp_printf(out, "250-PIPELINING\r\n" "250 HELP\r\n"); 163
157 } else { 164 if (conf.defer_all) { /* I need this to debug delivery failures */
158 smtp_printf(out, "250 %s pretty old mailer, huh?\r\n", conf.host_name); 165 smtp_printf(out, "421 %s service temporarily unavailable.\r\n", conf.host_name);
166 break;
167 }
168
169 if (psc->prot == PROT_ESMTP) {
170 smtp_printf(out, "250-%s Nice to meet you with ESMTP\r\n", conf.host_name);
171 /* not yet: fprintf(out, "250-SIZE\r\n"); */
172 smtp_printf(out, "250-PIPELINING\r\n" "250 HELP\r\n");
173 } else {
174 smtp_printf(out, "250 %s pretty old mailer, huh?\r\n", conf.host_name);
175 }
176 break;
177
178 case SMTP_MAIL_FROM:
179 {
180 gchar buf[MAX_ADDRESS];
181 address *addr;
182
183 if (!psc->helo_seen) {
184 smtp_printf(out, "503 need HELO or EHLO\r\n");
185 break;
186 }
187 if (psc->from_seen) {
188 smtp_printf(out, "503 MAIL FROM: already given.\r\n");
189 break;
190 }
191
192 msg = create_message();
193 msg->received_host = remote_host ? g_strdup(remote_host) : NULL;
194 msg->received_prot = psc->prot;
195 msg->ident = ident ? g_strdup(ident) : NULL;
196 /* get transfer id and increment for next one */
197 msg->transfer_id = (psc->next_id)++;
198
199 get_address(buffer, buf);
200 if (remote_host) {
201 addr = create_address(buf, TRUE);
202 } else {
203 addr = create_address_qualified(buf, TRUE, conf.host_name);
204 }
205 if (!addr) {
206 smtp_printf(out, "501 %s: syntax error.\r\n", buf);
207 } else if (!addr->domain) {
208 smtp_printf(out, "501 return path must be qualified.\r\n", buf);
209 } else {
210 psc->from_seen = TRUE;
211 msg->return_path = addr;
212 smtp_printf(out, "250 OK %s is a nice guy.\r\n", addr->address);
213 }
214 }
215 break;
216
217 case SMTP_RCPT_TO:
218 {
219 char buf[MAX_ADDRESS];
220 address *addr;
221
222 if (!psc->helo_seen) {
223 smtp_printf(out, "503 need HELO or EHLO.\r\n");
224 break;
225 }
226 if (!psc->from_seen) {
227 smtp_printf(out, "503 need MAIL FROM: before RCPT TO:\r\n");
228 break;
229 }
230
231 get_address(buffer, buf);
232 if (remote_host) {
233 addr = create_address(buf, TRUE);
234 } else {
235 addr = create_address_qualified(buf, TRUE, conf.host_name);
236 }
237 if (!addr) {
238 smtp_printf(out, "501 %s: syntax error in address.\r\n", buf);
239 break;
240 }
241 if (addr->local_part[0] == '|') {
242 smtp_printf(out, "501 %s: no pipe allowed for SMTP connections\r\n", buf);
243 break;
244 }
245 if (!addr->domain) {
246 smtp_printf(out, "501 recipient address must be qualified.\r\n", buf);
247 break;
248 }
249 gboolean do_relay = conf.do_relay;
250 if (!do_relay) {
251 do_relay = addr_is_local(msg->return_path);
252 if (!do_relay) {
253 do_relay = addr_is_local(addr);
159 } 254 }
160 break; 255 }
161 } else { 256 if (!do_relay) {
162 smtp_printf(out, "421 %s service temporarily unavailable.\r\n", conf.host_name); 257 smtp_printf(out, "550 relaying to %s denied.\r\n", addr_string(addr));
163 } 258 break;
164 259 }
165 case SMTP_MAIL_FROM: 260 psc->rcpt_seen = TRUE;
166 if (psc->helo_seen && !psc->from_seen) { 261 msg->rcpt_list = g_list_append(msg->rcpt_list, addr);
167 gchar buf[MAX_ADDRESS]; 262 smtp_printf(out, "250 OK %s is our friend.\r\n", addr->address);
168 address *addr; 263 }
169 264 break;
170 msg = create_message(); 265
171 msg->received_host = remote_host ? g_strdup(remote_host) : NULL; 266 case SMTP_DATA:
172 msg->received_prot = psc->prot; 267 if (!psc->helo_seen) {
173 msg->ident = ident ? g_strdup(ident) : NULL; 268 smtp_printf(out, "503 need HELO or EHLO.\r\n");
174 /* get transfer id and increment for next one */
175 msg->transfer_id = (psc->next_id)++;
176
177 get_address(buffer, buf);
178 if ((addr = remote_host
179 ? create_address(buf, TRUE)
180 : create_address_qualified(buf, TRUE, conf.host_name))) {
181 if (addr->domain != NULL) {
182 psc->from_seen = TRUE;
183 msg->return_path = addr;
184 smtp_printf(out, "250 OK %s is a nice guy.\r\n", addr->address);
185 } else {
186 smtp_printf(out, "501 return path must be qualified.\r\n", buf);
187 }
188 } else {
189 smtp_printf(out, "501 %s: syntax error.\r\n", buf);
190 }
191 } else {
192 if (!psc->helo_seen)
193 smtp_printf(out, "503 need HELO or EHLO\r\n");
194 else
195 smtp_printf(out, "503 MAIL FROM: already given.\r\n");
196 }
197 break; 269 break;
198 270 }
199 case SMTP_RCPT_TO: 271 if (!psc->rcpt_seen) {
200 272 smtp_printf(out, "503 need RCPT TO: before DATA\r\n");
201 if (psc->helo_seen && psc->from_seen) {
202 char buf[MAX_ADDRESS];
203 address *addr;
204
205 get_address(buffer, buf);
206 if ((addr = remote_host
207 ? create_address(buf, TRUE)
208 : create_address_qualified(buf, TRUE, conf.host_name))) {
209 if (addr->local_part[0] != '|') {
210 if (addr->domain != NULL) {
211 gboolean do_relay = conf.do_relay;
212 if (!do_relay) {
213 if ((do_relay = addr_is_local(msg->return_path))) {
214 }
215 if (!do_relay) {
216 do_relay = addr_is_local(addr);
217 }
218 }
219 if (do_relay) {
220 psc->rcpt_seen = TRUE;
221 msg->rcpt_list = g_list_append(msg->rcpt_list, addr);
222 smtp_printf(out, "250 OK %s is our friend.\r\n", addr->address);
223 } else {
224 smtp_printf(out, "550 relaying to %s denied.\r\n", addr_string(addr));
225 }
226 } else {
227 smtp_printf(out, "501 recipient address must be qualified.\r\n", buf);
228 }
229 } else
230 smtp_printf(out, "501 %s: no pipe allowed for SMTP connections\r\n", buf);
231 } else {
232 smtp_printf(out, "501 %s: syntax error in address.\r\n", buf);
233 }
234 } else {
235
236 if (!psc->helo_seen)
237 smtp_printf(out, "503 need HELO or EHLO.\r\n");
238 else
239 smtp_printf(out, "503 need MAIL FROM: before RCPT TO:\r\n");
240 }
241 break; 273 break;
242 274 }
243 case SMTP_DATA: 275 accept_error err;
244 if (psc->helo_seen && psc->rcpt_seen) { 276
245 accept_error err; 277 smtp_printf(out, "354 okay, and do not forget the dot\r\n");
246 278
247 smtp_printf(out, "354 okay, and do not forget the dot\r\n"); 279 err = accept_message(in, msg, conf.do_save_envelope_to ? ACC_SAVE_ENVELOPE_TO : 0);
248 280 if (err != AERR_OK) {
249 if ((err = accept_message(in, msg, conf.do_save_envelope_to ? ACC_SAVE_ENVELOPE_TO : 0)) == AERR_OK) { 281 if (err == AERR_TIMEOUT || err == AERR_EOF) {
250 if (spool_write(msg, TRUE)) { 282 return;
251 pid_t pid; 283 }
252 smtp_printf(out, "250 OK id=%s\r\n", msg->uid); 284 /* should never happen: */
253 285 smtp_printf(out, "451 Unknown error\r\n");
254 if (remote_host != NULL)
255 logwrite(LOG_NOTICE, "%s <= <%s@%s> host=%s with %s\n", msg->uid, msg->return_path->local_part,
256 msg->return_path->domain, remote_host, prot_names[psc->prot]);
257 else
258 logwrite(LOG_NOTICE, "%s <= <%s@%s> with %s\n", msg->uid, msg->return_path->local_part,
259 msg->return_path->domain, prot_names[psc->prot]);
260
261 if (!conf.do_queue) {
262 if ((pid = fork()) == 0) {
263
264 if (deliver(msg))
265 _exit(EXIT_SUCCESS);
266 else
267 _exit(EXIT_FAILURE);
268
269 } else if (pid < 0) {
270 logwrite(LOG_ALERT, "could not fork for delivery, id = %s", msg->uid);
271 }
272 } else {
273 DEBUG(1) debugf("queuing forced by configuration or option.\n");
274 }
275 } else {
276 smtp_printf(out, "451 Could not write spool file\r\n");
277 return;
278 }
279 } else {
280 switch (err) {
281 case AERR_TIMEOUT:
282 return;
283 case AERR_EOF:
284 return;
285 default:
286 /* should never happen: */
287 smtp_printf(out, "451 Unknown error\r\n");
288 return;
289 }
290 }
291 psc->rcpt_seen = psc->from_seen = FALSE;
292 destroy_message(msg);
293 msg = NULL;
294 } else {
295 if (!psc->helo_seen)
296 smtp_printf(out, "503 need HELO or EHLO.\r\n");
297 else
298 smtp_printf(out, "503 need RCPT TO: before DATA\r\n");
299 }
300 break;
301 case SMTP_QUIT:
302 smtp_printf(out, "221 goodbye\r\n");
303 if (msg != NULL)
304 destroy_message(msg);
305 return; 286 return;
306 case SMTP_RSET: 287 }
307 psc->from_seen = psc->rcpt_seen = FALSE; 288
308 if (msg != NULL) 289
309 destroy_message(msg); 290 if (!spool_write(msg, TRUE)) {
291 smtp_printf(out, "451 Could not write spool file\r\n");
292 return;
293 }
294 pid_t pid;
295 smtp_printf(out, "250 OK id=%s\r\n", msg->uid);
296
297 if (remote_host != NULL) {
298 logwrite(LOG_NOTICE, "%s <= <%s@%s> host=%s with %s\n", msg->uid,
299 msg->return_path->local_part, msg->return_path->domain,
300 remote_host, prot_names[psc->prot]);
301 } else {
302 logwrite(LOG_NOTICE, "%s <= <%s@%s> with %s\n", msg->uid,
303 msg->return_path->local_part, msg->return_path->domain,
304 prot_names[psc->prot]);
305 }
306
307 if (conf.do_queue) {
308 DEBUG(1) debugf("queuing forced by configuration or option.\n");
309 } else {
310 pid = fork();
311 if (pid == 0) {
312 _exit(deliver(msg));
313 } else if (pid < 0) {
314 logwrite(LOG_ALERT, "could not fork for delivery, id = %s", msg->uid);
315 }
316 }
317 psc->rcpt_seen = psc->from_seen = FALSE;
318 destroy_message(msg);
319 msg = NULL;
320 break;
321
322 case SMTP_QUIT:
323 smtp_printf(out, "221 goodbye\r\n");
324 if (msg) {
325 destroy_message(msg);
326 }
327 return;
328
329 case SMTP_RSET:
330 psc->from_seen = psc->rcpt_seen = FALSE;
331 if (msg) {
332 destroy_message(msg);
310 msg = NULL; 333 msg = NULL;
311 smtp_printf(out, "250 OK\r\n"); 334 }
312 break; 335 smtp_printf(out, "250 OK\r\n");
313 case SMTP_NOOP: 336 break;
314 smtp_printf(out, "250 OK\r\n"); 337
315 break; 338 case SMTP_NOOP:
316 case SMTP_HELP: 339 smtp_printf(out, "250 OK\r\n");
317 { 340 break;
318 int i; 341
319 342 case SMTP_HELP:
320 smtp_printf(out, "214-supported commands:\r\n"); 343 {
321 for (i = 0; i < SMTP_NUM_IDS - 1; i++) { 344 int i;
322 smtp_printf(out, "214-%s\r\n", smtp_cmds[i].cmd); 345
323 } 346 smtp_printf(out, "214-supported commands:\r\n");
324 smtp_printf(out, "214 %s\r\n", smtp_cmds[i].cmd); 347 for (i = 0; i < SMTP_NUM_IDS - 1; i++) {
325 } 348 smtp_printf(out, "214-%s\r\n", smtp_cmds[i].cmd);
326 break; 349 }
327 default: 350 smtp_printf(out, "214 %s\r\n", smtp_cmds[i].cmd);
328 smtp_printf(out, "501 command not recognized\r\n"); 351 }
329 DEBUG(1) debugf("command not recognized, was '%s'\n", buffer); 352 break;
330 break; 353
331 } 354 default:
355 smtp_printf(out, "501 command not recognized\r\n");
356 DEBUG(1) debugf("command not recognized, was '%s'\n", buffer);
357 break;
332 } 358 }
333 switch (len) { 359 }
334 case -3: 360 switch (len) {
335 logwrite(LOG_NOTICE, "connection timed out\n"); 361 case -3:
336 break; 362 logwrite(LOG_NOTICE, "connection timed out\n");
337 case -2: 363 break;
338 logwrite(LOG_NOTICE, "line overflow\n"); 364 case -2:
339 break; 365 logwrite(LOG_NOTICE, "line overflow\n");
340 case -1: 366 break;
341 logwrite(LOG_NOTICE, "received EOF\n"); 367 case -1:
342 break; 368 logwrite(LOG_NOTICE, "received EOF\n");
343 default: 369 break;
344 break; 370 default:
345 } 371 break;
346 } 372 }
347 } 373 }
348 #endif 374 #endif