rev |
line source |
meillo@0
|
1 /* MasqMail
|
meillo@0
|
2 Copyright (C) 1999-2001 Oliver Kurth
|
meillo@0
|
3
|
meillo@0
|
4 This program is free software; you can redistribute it and/or modify
|
meillo@0
|
5 it under the terms of the GNU General Public License as published by
|
meillo@0
|
6 the Free Software Foundation; either version 2 of the License, or
|
meillo@0
|
7 (at your option) any later version.
|
meillo@0
|
8
|
meillo@0
|
9 This program is distributed in the hope that it will be useful,
|
meillo@0
|
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
meillo@0
|
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
meillo@0
|
12 GNU General Public License for more details.
|
meillo@0
|
13
|
meillo@0
|
14 You should have received a copy of the GNU General Public License
|
meillo@0
|
15 along with this program; if not, write to the Free Software
|
meillo@0
|
16 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
meillo@0
|
17 */
|
meillo@0
|
18
|
meillo@0
|
19 #include "masqmail.h"
|
meillo@0
|
20 #include "readsock.h"
|
meillo@0
|
21
|
meillo@0
|
22 /*
|
meillo@0
|
23 I always forget these rfc numbers:
|
meillo@0
|
24 RFC 821 (SMTP)
|
meillo@0
|
25 RFC 1869 (ESMTP)
|
meillo@0
|
26 RFC 1870 (ESMTP SIZE)
|
meillo@0
|
27 RFC 2197 (ESMTP PIPELINE)
|
meillo@0
|
28 RFC 2554 (ESMTP AUTH)
|
meillo@0
|
29 */
|
meillo@0
|
30
|
meillo@0
|
31 #ifdef ENABLE_SMTP_SERVER
|
meillo@0
|
32
|
meillo@10
|
33 smtp_cmd smtp_cmds[] = {
|
meillo@15
|
34 {SMTP_HELO, "HELO",},
|
meillo@15
|
35 {SMTP_EHLO, "EHLO",},
|
meillo@15
|
36 {SMTP_MAIL_FROM, "MAIL FROM:",},
|
meillo@15
|
37 {SMTP_RCPT_TO, "RCPT TO:",},
|
meillo@15
|
38 {SMTP_DATA, "DATA",},
|
meillo@15
|
39 {SMTP_QUIT, "QUIT",},
|
meillo@15
|
40 {SMTP_RSET, "RSET",},
|
meillo@15
|
41 {SMTP_NOOP, "NOOP",},
|
meillo@15
|
42 {SMTP_HELP, "HELP"},
|
meillo@0
|
43 };
|
meillo@0
|
44
|
meillo@10
|
45 static smtp_cmd_id
|
meillo@10
|
46 get_id(const gchar * line)
|
meillo@0
|
47 {
|
meillo@10
|
48 gint i;
|
meillo@10
|
49 for (i = 0; i < SMTP_NUM_IDS; i++) {
|
meillo@10
|
50 if (strncasecmp(smtp_cmds[i].cmd, line, strlen(smtp_cmds[i].cmd)) == 0)
|
meillo@10
|
51 return (smtp_cmd_id) i;
|
meillo@10
|
52 }
|
meillo@10
|
53 return SMTP_ERROR;
|
meillo@0
|
54 }
|
meillo@0
|
55
|
meillo@0
|
56 /* this is a quick hack: we expect the address to be syntactically correct
|
meillo@0
|
57 and containing the mailbox only:
|
meillo@0
|
58 */
|
meillo@10
|
59 static gboolean
|
meillo@10
|
60 get_address(gchar * line, gchar * addr)
|
meillo@0
|
61 {
|
meillo@10
|
62 gchar *p = line, *q = addr;
|
meillo@0
|
63
|
meillo@10
|
64 /* skip MAIL FROM: and RCPT TO: */
|
meillo@10
|
65 while (*p && (*p != ':'))
|
meillo@10
|
66 p++;
|
meillo@10
|
67 p++;
|
meillo@0
|
68
|
meillo@10
|
69 /* skip spaces: */
|
meillo@10
|
70 while (*p && isspace(*p))
|
meillo@10
|
71 p++;
|
meillo@0
|
72
|
meillo@10
|
73 /* get address: */
|
meillo@10
|
74 while (*p && !isspace(*p) && (q < addr + MAX_ADDRESS - 1))
|
meillo@10
|
75 *(q++) = *(p++);
|
meillo@10
|
76 *q = 0;
|
meillo@0
|
77
|
meillo@10
|
78 return TRUE;
|
meillo@0
|
79 }
|
meillo@0
|
80
|
meillo@10
|
81 static smtp_connection*
|
meillo@10
|
82 create_base(gchar * remote_host)
|
meillo@0
|
83 {
|
meillo@10
|
84 smtp_connection *base = g_malloc(sizeof(smtp_connection));
|
meillo@10
|
85 if (base) {
|
meillo@10
|
86 base->remote_host = g_strdup(remote_host);
|
meillo@0
|
87
|
meillo@10
|
88 base->prot = PROT_SMTP;
|
meillo@10
|
89 base->next_id = 0;
|
meillo@10
|
90 base->helo_seen = 0;
|
meillo@10
|
91 base->from_seen = 0;
|
meillo@10
|
92 base->rcpt_seen = 0;
|
meillo@10
|
93 base->msg = NULL;
|
meillo@0
|
94
|
meillo@10
|
95 return base;
|
meillo@10
|
96 }
|
meillo@10
|
97 return NULL;
|
meillo@0
|
98 }
|
meillo@0
|
99
|
meillo@10
|
100 static void
|
meillo@10
|
101 smtp_printf(FILE * out, gchar * fmt, ...)
|
meillo@0
|
102 {
|
meillo@10
|
103 va_list args;
|
meillo@10
|
104 va_start(args, fmt);
|
meillo@0
|
105
|
meillo@10
|
106 DEBUG(4) {
|
meillo@10
|
107 gchar buf[256];
|
meillo@10
|
108 va_list args_copy;
|
meillo@0
|
109
|
meillo@10
|
110 va_copy(args_copy, args);
|
meillo@10
|
111 vsnprintf(buf, 255, fmt, args_copy);
|
meillo@10
|
112 va_end(args_copy);
|
meillo@0
|
113
|
meillo@10
|
114 debugf(">>>%s", buf);
|
meillo@10
|
115 }
|
meillo@0
|
116
|
meillo@10
|
117 vfprintf(out, fmt, args);
|
meillo@10
|
118 fflush(out);
|
meillo@0
|
119
|
meillo@10
|
120 va_end(args);
|
meillo@0
|
121 }
|
meillo@0
|
122
|
meillo@10
|
123 void
|
meillo@10
|
124 smtp_in(FILE * in, FILE * out, gchar * remote_host, gchar * ident)
|
meillo@0
|
125 {
|
meillo@10
|
126 gchar *buffer;
|
meillo@10
|
127 smtp_cmd_id cmd_id;
|
meillo@10
|
128 message *msg = NULL;
|
meillo@10
|
129 smtp_connection *psc;
|
meillo@10
|
130 int len;
|
meillo@0
|
131
|
meillo@10
|
132 DEBUG(5) debugf("smtp_in entered, remote_host = %s\n", remote_host);
|
meillo@0
|
133
|
meillo@10
|
134 psc = create_base(remote_host);
|
meillo@10
|
135 psc->msg = msg;
|
meillo@0
|
136
|
meillo@10
|
137 buffer = (gchar *) g_malloc(BUF_LEN);
|
meillo@10
|
138 if (buffer) {
|
meillo@10
|
139 /* send greeting string, containing ESMTP: */
|
meillo@10
|
140 smtp_printf(out, "220 %s MasqMail %s ESMTP\r\n", conf.host_name, VERSION);
|
meillo@0
|
141
|
meillo@10
|
142 while ((len = read_sockline(in, buffer, BUF_LEN, 5 * 60, READSOCKL_CHUG)) >= 0) {
|
meillo@10
|
143 cmd_id = get_id(buffer);
|
meillo@10
|
144
|
meillo@10
|
145 switch (cmd_id) {
|
meillo@10
|
146 case SMTP_EHLO:
|
meillo@10
|
147 psc->prot = PROT_ESMTP;
|
meillo@10
|
148 /* fall through */
|
meillo@10
|
149 case SMTP_HELO:
|
meillo@10
|
150 psc->helo_seen = TRUE;
|
meillo@10
|
151
|
meillo@10
|
152 if (!conf.defer_all) { /* I need this to debug delivery failures */
|
meillo@10
|
153 if (psc->prot == PROT_ESMTP) {
|
meillo@10
|
154 smtp_printf(out, "250-%s Nice to meet you with ESMTP\r\n", conf.host_name);
|
meillo@10
|
155 /* not yet: fprintf(out, "250-SIZE\r\n"); */
|
meillo@10
|
156 smtp_printf(out, "250-PIPELINING\r\n" "250 HELP\r\n");
|
meillo@10
|
157 } else {
|
meillo@10
|
158 smtp_printf(out, "250 %s pretty old mailer, huh?\r\n", conf.host_name);
|
meillo@10
|
159 }
|
meillo@10
|
160 break;
|
meillo@10
|
161 } else {
|
meillo@10
|
162 smtp_printf(out, "421 %s service temporarily unavailable.\r\n", conf.host_name);
|
meillo@10
|
163 }
|
meillo@10
|
164
|
meillo@10
|
165 case SMTP_MAIL_FROM:
|
meillo@10
|
166 if (psc->helo_seen && !psc->from_seen) {
|
meillo@10
|
167 gchar buf[MAX_ADDRESS];
|
meillo@10
|
168 address *addr;
|
meillo@10
|
169
|
meillo@10
|
170 msg = create_message();
|
meillo@10
|
171 msg->received_host = remote_host ? g_strdup(remote_host) : NULL;
|
meillo@10
|
172 msg->received_prot = psc->prot;
|
meillo@10
|
173 msg->ident = ident ? g_strdup(ident) : NULL;
|
meillo@10
|
174 /* get transfer id and increment for next one */
|
meillo@10
|
175 msg->transfer_id = (psc->next_id)++;
|
meillo@10
|
176
|
meillo@10
|
177 get_address(buffer, buf);
|
meillo@10
|
178 if ((addr = remote_host
|
meillo@10
|
179 ? create_address(buf, TRUE)
|
meillo@10
|
180 : create_address_qualified(buf, TRUE, conf.host_name))) {
|
meillo@10
|
181 if (addr->domain != NULL) {
|
meillo@10
|
182 psc->from_seen = TRUE;
|
meillo@10
|
183 msg->return_path = addr;
|
meillo@10
|
184 smtp_printf(out, "250 OK %s is a nice guy.\r\n", addr->address);
|
meillo@10
|
185 } else {
|
meillo@10
|
186 smtp_printf(out, "501 return path must be qualified.\r\n", buf);
|
meillo@10
|
187 }
|
meillo@10
|
188 } else {
|
meillo@10
|
189 smtp_printf(out, "501 %s: syntax error.\r\n", buf);
|
meillo@10
|
190 }
|
meillo@10
|
191 } else {
|
meillo@10
|
192 if (!psc->helo_seen)
|
meillo@10
|
193 smtp_printf(out, "503 need HELO or EHLO\r\n");
|
meillo@10
|
194 else
|
meillo@10
|
195 smtp_printf(out, "503 MAIL FROM: already given.\r\n");
|
meillo@10
|
196 }
|
meillo@10
|
197 break;
|
meillo@10
|
198
|
meillo@10
|
199 case SMTP_RCPT_TO:
|
meillo@10
|
200
|
meillo@10
|
201 if (psc->helo_seen && psc->from_seen) {
|
meillo@10
|
202 char buf[MAX_ADDRESS];
|
meillo@10
|
203 address *addr;
|
meillo@10
|
204
|
meillo@10
|
205 get_address(buffer, buf);
|
meillo@10
|
206 if ((addr = remote_host
|
meillo@10
|
207 ? create_address(buf, TRUE)
|
meillo@10
|
208 : create_address_qualified(buf, TRUE, conf.host_name))) {
|
meillo@10
|
209 if (addr->local_part[0] != '|') {
|
meillo@10
|
210 if (addr->domain != NULL) {
|
meillo@10
|
211 gboolean do_relay = conf.do_relay;
|
meillo@10
|
212 if (!do_relay) {
|
meillo@10
|
213 if ((do_relay = addr_is_local(msg->return_path))) {
|
meillo@10
|
214 }
|
meillo@10
|
215 if (!do_relay) {
|
meillo@10
|
216 do_relay = addr_is_local(addr);
|
meillo@10
|
217 }
|
meillo@10
|
218 }
|
meillo@10
|
219 if (do_relay) {
|
meillo@10
|
220 psc->rcpt_seen = TRUE;
|
meillo@10
|
221 msg->rcpt_list = g_list_append(msg->rcpt_list, addr);
|
meillo@10
|
222 smtp_printf(out, "250 OK %s is our friend.\r\n", addr->address);
|
meillo@10
|
223 } else {
|
meillo@10
|
224 smtp_printf(out, "550 relaying to %s denied.\r\n", addr_string(addr));
|
meillo@10
|
225 }
|
meillo@10
|
226 } else {
|
meillo@10
|
227 smtp_printf(out, "501 recipient address must be qualified.\r\n", buf);
|
meillo@10
|
228 }
|
meillo@10
|
229 } else
|
meillo@10
|
230 smtp_printf(out, "501 %s: no pipe allowed for SMTP connections\r\n", buf);
|
meillo@10
|
231 } else {
|
meillo@10
|
232 smtp_printf(out, "501 %s: syntax error in address.\r\n", buf);
|
meillo@10
|
233 }
|
meillo@10
|
234 } else {
|
meillo@10
|
235
|
meillo@10
|
236 if (!psc->helo_seen)
|
meillo@10
|
237 smtp_printf(out, "503 need HELO or EHLO.\r\n");
|
meillo@10
|
238 else
|
meillo@10
|
239 smtp_printf(out, "503 need MAIL FROM: before RCPT TO:\r\n");
|
meillo@10
|
240 }
|
meillo@10
|
241 break;
|
meillo@10
|
242
|
meillo@10
|
243 case SMTP_DATA:
|
meillo@10
|
244 if (psc->helo_seen && psc->rcpt_seen) {
|
meillo@10
|
245 accept_error err;
|
meillo@10
|
246
|
meillo@10
|
247 smtp_printf(out, "354 okay, and do not forget the dot\r\n");
|
meillo@10
|
248
|
meillo@10
|
249 if ((err = accept_message(in, msg, conf.do_save_envelope_to ? ACC_SAVE_ENVELOPE_TO : 0)) == AERR_OK) {
|
meillo@10
|
250 if (spool_write(msg, TRUE)) {
|
meillo@10
|
251 pid_t pid;
|
meillo@10
|
252 smtp_printf(out, "250 OK id=%s\r\n", msg->uid);
|
meillo@10
|
253
|
meillo@10
|
254 if (remote_host != NULL)
|
meillo@10
|
255 logwrite(LOG_NOTICE, "%s <= <%s@%s> host=%s with %s\n", msg->uid, msg->return_path->local_part,
|
meillo@10
|
256 msg->return_path->domain, remote_host, prot_names[psc->prot]);
|
meillo@10
|
257 else
|
meillo@10
|
258 logwrite(LOG_NOTICE, "%s <= <%s@%s> with %s\n", msg->uid, msg->return_path->local_part,
|
meillo@10
|
259 msg->return_path->domain, prot_names[psc->prot]);
|
meillo@10
|
260
|
meillo@10
|
261 if (!conf.do_queue) {
|
meillo@10
|
262 if ((pid = fork()) == 0) {
|
meillo@10
|
263
|
meillo@10
|
264 if (deliver(msg))
|
meillo@10
|
265 _exit(EXIT_SUCCESS);
|
meillo@10
|
266 else
|
meillo@10
|
267 _exit(EXIT_FAILURE);
|
meillo@10
|
268
|
meillo@10
|
269 } else if (pid < 0) {
|
meillo@10
|
270 logwrite(LOG_ALERT, "could not fork for delivery, id = %s", msg->uid);
|
meillo@10
|
271 }
|
meillo@10
|
272 } else {
|
meillo@10
|
273 DEBUG(1) debugf("queuing forced by configuration or option.\n");
|
meillo@10
|
274 }
|
meillo@10
|
275 } else {
|
meillo@10
|
276 smtp_printf(out, "451 Could not write spool file\r\n");
|
meillo@10
|
277 return;
|
meillo@10
|
278 }
|
meillo@10
|
279 } else {
|
meillo@10
|
280 switch (err) {
|
meillo@10
|
281 case AERR_TIMEOUT:
|
meillo@10
|
282 return;
|
meillo@10
|
283 case AERR_EOF:
|
meillo@10
|
284 return;
|
meillo@10
|
285 default:
|
meillo@10
|
286 /* should never happen: */
|
meillo@10
|
287 smtp_printf(out, "451 Unknown error\r\n");
|
meillo@10
|
288 return;
|
meillo@10
|
289 }
|
meillo@10
|
290 }
|
meillo@10
|
291 psc->rcpt_seen = psc->from_seen = FALSE;
|
meillo@10
|
292 destroy_message(msg);
|
meillo@10
|
293 msg = NULL;
|
meillo@10
|
294 } else {
|
meillo@10
|
295 if (!psc->helo_seen)
|
meillo@10
|
296 smtp_printf(out, "503 need HELO or EHLO.\r\n");
|
meillo@10
|
297 else
|
meillo@10
|
298 smtp_printf(out, "503 need RCPT TO: before DATA\r\n");
|
meillo@10
|
299 }
|
meillo@10
|
300 break;
|
meillo@10
|
301 case SMTP_QUIT:
|
meillo@10
|
302 smtp_printf(out, "221 goodbye\r\n");
|
meillo@10
|
303 if (msg != NULL)
|
meillo@10
|
304 destroy_message(msg);
|
meillo@10
|
305 return;
|
meillo@10
|
306 case SMTP_RSET:
|
meillo@10
|
307 psc->from_seen = psc->rcpt_seen = FALSE;
|
meillo@10
|
308 if (msg != NULL)
|
meillo@10
|
309 destroy_message(msg);
|
meillo@10
|
310 msg = NULL;
|
meillo@10
|
311 smtp_printf(out, "250 OK\r\n");
|
meillo@10
|
312 break;
|
meillo@10
|
313 case SMTP_NOOP:
|
meillo@10
|
314 smtp_printf(out, "250 OK\r\n");
|
meillo@10
|
315 break;
|
meillo@10
|
316 case SMTP_HELP:
|
meillo@10
|
317 {
|
meillo@10
|
318 int i;
|
meillo@10
|
319
|
meillo@10
|
320 smtp_printf(out, "214-supported commands:\r\n");
|
meillo@10
|
321 for (i = 0; i < SMTP_NUM_IDS - 1; i++) {
|
meillo@10
|
322 smtp_printf(out, "214-%s\r\n", smtp_cmds[i].cmd);
|
meillo@10
|
323 }
|
meillo@10
|
324 smtp_printf(out, "214 %s\r\n", smtp_cmds[i].cmd);
|
meillo@10
|
325 }
|
meillo@10
|
326 break;
|
meillo@10
|
327 default:
|
meillo@10
|
328 smtp_printf(out, "501 command not recognized\r\n");
|
meillo@10
|
329 DEBUG(1) debugf("command not recognized, was '%s'\n", buffer);
|
meillo@10
|
330 break;
|
meillo@10
|
331 }
|
meillo@10
|
332 }
|
meillo@10
|
333 switch (len) {
|
meillo@10
|
334 case -3:
|
meillo@10
|
335 logwrite(LOG_NOTICE, "connection timed out\n");
|
meillo@10
|
336 break;
|
meillo@10
|
337 case -2:
|
meillo@10
|
338 logwrite(LOG_NOTICE, "line overflow\n");
|
meillo@10
|
339 break;
|
meillo@10
|
340 case -1:
|
meillo@10
|
341 logwrite(LOG_NOTICE, "received EOF\n");
|
meillo@10
|
342 break;
|
meillo@10
|
343 default:
|
meillo@10
|
344 break;
|
meillo@10
|
345 }
|
meillo@0
|
346 }
|
meillo@0
|
347 }
|
meillo@0
|
348 #endif
|