masqmail-0.2
view 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 |
line source
1 /* MasqMail
2 Copyright (C) 1999-2001 Oliver Kurth
3 Copyright (C) 2010 markus schnalke <meillo@marmaro.de>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
20 #include "masqmail.h"
21 #include "readsock.h"
23 /*
24 I always forget these rfc numbers:
25 RFC 821 (SMTP)
26 RFC 1869 (ESMTP)
27 RFC 1870 (ESMTP SIZE)
28 RFC 2197 (ESMTP PIPELINE)
29 RFC 2554 (ESMTP AUTH)
30 */
32 #ifdef ENABLE_SMTP_SERVER
34 smtp_cmd smtp_cmds[] = {
35 {SMTP_HELO, "HELO",},
36 {SMTP_EHLO, "EHLO",},
37 {SMTP_MAIL_FROM, "MAIL FROM:",},
38 {SMTP_RCPT_TO, "RCPT TO:",},
39 {SMTP_DATA, "DATA",},
40 {SMTP_QUIT, "QUIT",},
41 {SMTP_RSET, "RSET",},
42 {SMTP_NOOP, "NOOP",},
43 {SMTP_HELP, "HELP"},
44 };
46 static smtp_cmd_id
47 get_id(const gchar * line)
48 {
49 gint i;
50 for (i = 0; i < SMTP_NUM_IDS; i++) {
51 if (strncasecmp(smtp_cmds[i].cmd, line, strlen(smtp_cmds[i].cmd)) == 0) {
52 return (smtp_cmd_id) i;
53 }
54 }
55 return SMTP_ERROR;
56 }
58 /* this is a quick hack: we expect the address to be syntactically correct
59 and containing the mailbox only:
60 */
61 static gboolean
62 get_address(gchar * line, gchar * addr)
63 {
64 gchar *p = line;
65 gchar *q = addr;
67 /* skip MAIL FROM: and RCPT TO: */
68 while (*p && (*p != ':')) {
69 p++;
70 }
71 p++;
73 /* skip spaces: */
74 while (*p && isspace(*p)) {
75 p++;
76 }
78 /* get address: */
79 while (*p && !isspace(*p) && (q < addr + MAX_ADDRESS - 1)) {
80 *(q++) = *(p++);
81 }
82 *q = 0;
84 return TRUE;
85 }
87 static smtp_connection*
88 create_base(gchar * remote_host)
89 {
90 smtp_connection *base = g_malloc(sizeof(smtp_connection));
91 if (!base) {
92 return NULL;
93 }
95 base->remote_host = g_strdup(remote_host);
97 base->prot = PROT_SMTP;
98 base->next_id = 0;
99 base->helo_seen = 0;
100 base->from_seen = 0;
101 base->rcpt_seen = 0;
102 base->msg = NULL;
104 return base;
105 }
107 static void
108 smtp_printf(FILE * out, gchar * fmt, ...)
109 {
110 va_list args;
111 va_start(args, fmt);
113 DEBUG(4) {
114 gchar buf[256];
115 va_list args_copy;
117 va_copy(args_copy, args);
118 vsnprintf(buf, 255, fmt, args_copy);
119 va_end(args_copy);
121 debugf(">>>%s", buf);
122 }
124 vfprintf(out, fmt, args);
125 fflush(out);
127 va_end(args);
128 }
130 void
131 smtp_in(FILE * in, FILE * out, gchar * remote_host, gchar * ident)
132 {
133 gchar *buffer;
134 smtp_cmd_id cmd_id;
135 message *msg = NULL;
136 smtp_connection *psc;
137 int len;
139 DEBUG(5) debugf("smtp_in entered, remote_host = %s\n", remote_host);
141 psc = create_base(remote_host);
142 psc->msg = msg;
144 buffer = (gchar *) g_malloc(BUF_LEN);
145 if (!buffer) {
146 /* this check is actually unneccessary as g_malloc()
147 aborts on failure */
148 return;
149 }
151 /* send greeting string, containing ESMTP: */
152 smtp_printf(out, "220 %s MasqMail %s ESMTP\r\n", conf.host_name, VERSION);
154 while ((len = read_sockline(in, buffer, BUF_LEN, 5 * 60, READSOCKL_CHUG)) >= 0) {
155 cmd_id = get_id(buffer);
157 switch (cmd_id) {
158 case SMTP_EHLO:
159 psc->prot = PROT_ESMTP;
160 /* fall through */
161 case SMTP_HELO:
162 psc->helo_seen = TRUE;
164 if (conf.defer_all) { /* I need this to debug delivery failures */
165 smtp_printf(out, "421 %s service temporarily unavailable.\r\n", conf.host_name);
166 break;
167 }
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;
178 case SMTP_MAIL_FROM:
179 {
180 gchar buf[MAX_ADDRESS];
181 address *addr;
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 }
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)++;
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;
217 case SMTP_RCPT_TO:
218 {
219 char buf[MAX_ADDRESS];
220 address *addr;
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 }
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);
254 }
255 }
256 if (!do_relay) {
257 smtp_printf(out, "550 relaying to %s denied.\r\n", addr_string(addr));
258 break;
259 }
260 psc->rcpt_seen = TRUE;
261 msg->rcpt_list = g_list_append(msg->rcpt_list, addr);
262 smtp_printf(out, "250 OK %s is our friend.\r\n", addr->address);
263 }
264 break;
266 case SMTP_DATA:
267 if (!psc->helo_seen) {
268 smtp_printf(out, "503 need HELO or EHLO.\r\n");
269 break;
270 }
271 if (!psc->rcpt_seen) {
272 smtp_printf(out, "503 need RCPT TO: before DATA\r\n");
273 break;
274 }
275 accept_error err;
277 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);
280 if (err != AERR_OK) {
281 if (err == AERR_TIMEOUT || err == AERR_EOF) {
282 return;
283 }
284 /* should never happen: */
285 smtp_printf(out, "451 Unknown error\r\n");
286 return;
287 }
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);
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 }
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;
322 case SMTP_QUIT:
323 smtp_printf(out, "221 goodbye\r\n");
324 if (msg) {
325 destroy_message(msg);
326 }
327 return;
329 case SMTP_RSET:
330 psc->from_seen = psc->rcpt_seen = FALSE;
331 if (msg) {
332 destroy_message(msg);
333 msg = NULL;
334 }
335 smtp_printf(out, "250 OK\r\n");
336 break;
338 case SMTP_NOOP:
339 smtp_printf(out, "250 OK\r\n");
340 break;
342 case SMTP_HELP:
343 {
344 int i;
346 smtp_printf(out, "214-supported commands:\r\n");
347 for (i = 0; i < SMTP_NUM_IDS - 1; i++) {
348 smtp_printf(out, "214-%s\r\n", smtp_cmds[i].cmd);
349 }
350 smtp_printf(out, "214 %s\r\n", smtp_cmds[i].cmd);
351 }
352 break;
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;
358 }
359 }
360 switch (len) {
361 case -3:
362 logwrite(LOG_NOTICE, "connection timed out\n");
363 break;
364 case -2:
365 logwrite(LOG_NOTICE, "line overflow\n");
366 break;
367 case -1:
368 logwrite(LOG_NOTICE, "received EOF\n");
369 break;
370 default:
371 break;
372 }
373 }
374 #endif