masqmail
view src/accept.c @ 246:4cff8638dd9b
SMTP client: tries EHLO now always first
Changed the behavior of the SMTP client. Now always an EHLO greeting
is sent, no matter what kind of greeting text the server had sent. If
the EHLO failed, an HELO greeting is tried as fall back. This is the
behavior RFC 2821 requires (section 3.2).
This change will fix setups that were not possible to sent to a
server because that requires AUTH but hadn't said ``ESMTP'' in its
greeting message.
See also: Debian bug #349211
Thanks to Steffen (inne)
author | markus schnalke <meillo@marmaro.de> |
---|---|
date | Thu, 28 Oct 2010 16:40:02 -0300 |
parents | 89f951be358f |
children | 409552c5647f |
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 gchar *prot_names[] = {
24 "local",
25 "bsmtp",
26 "smtp",
27 "esmtp",
28 "(unknown)" /* should not happen, but better than crashing. */
29 };
31 static gchar*
32 string_base62(gchar * res, guint value, gchar len)
33 {
34 static gchar base62_chars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
35 gchar *p = res + len;
36 *p = '\0';
37 while (p > res) {
38 *(--p) = base62_chars[value % 62];
39 value /= 62;
40 }
41 return res;
42 }
44 static gint
45 _g_list_addr_isequal(gconstpointer a, gconstpointer b)
46 {
47 address *addr1 = (address *) a;
48 address *addr2 = (address *) b;
49 int ret;
51 if ((ret = strcasecmp(addr1->domain, addr2->domain)) == 0)
52 return strcmp(addr1->local_part, addr2->local_part);
53 else
54 return ret;
55 }
57 /* accept message from anywhere.
58 A locally originating message is indicated by msg->recieved_host == NULL
60 The -t option:
61 The ACC_RCPT_FROM_HEAD flag adds the recipients found in To/Cc/Bcc
62 headers to the recipients list.
63 The ACC_DEL_RCPTS flag makes only sense if the ACC_RCPT_FROM_HEAD
64 flag is given too. With ACC_DEL_RCPTS the recipients given on the
65 command line are removed from the ones given in headers.
66 */
68 accept_error
69 accept_message_stream(FILE * in, message * msg, guint flags)
70 {
71 gchar *line, *line1;
72 int line_size = MAX_DATALINE;
73 gboolean in_headers = TRUE;
74 header *hdr = NULL;
75 gint line_cnt = 0, data_size = 0;
77 line = g_malloc(line_size);
78 line[0] = '\0';
80 while (TRUE) {
81 int len = read_sockline1(in, &line, &line_size, 5 * 60, READSOCKL_CVT_CRLF);
83 line1 = line;
85 if ((line[0] == '.') && (!(flags & ACC_DOT_IGNORE))) {
86 if (line[1] == '\n') {
87 g_free(line);
88 break;
89 }
90 line1++;
91 }
93 if (len <= 0) {
94 if ((len == -1) && (flags & (ACC_DOT_IGNORE | ACC_NODOT_RELAX))) {
95 /* we got an EOF, and the last line was not terminated by a CR */
96 gint len1 = strlen(line1);
97 if (len1 > 0) { /* == 0 is 'normal' (EOF after a CR) */
98 if (line1[len1 - 1] != '\n') { /* some mail clients allow unterminated lines */
99 line1[len1] = '\n';
100 line1[len1 + 1] = '\0';
101 msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1));
102 data_size += strlen(line1);
103 line_cnt++;
104 }
105 }
106 break;
107 } else {
108 g_free(line);
109 if (len == -1) {
110 return AERR_EOF;
111 } else if (len == -2) {
112 /* should not happen any more */
113 return AERR_OVERFLOW;
114 } else if (len == -3) {
115 return AERR_TIMEOUT;
116 } else {
117 /* does not happen */
118 DEBUG(5) debugf("read_sockline returned %d\n", len);
119 return AERR_UNKNOWN;
120 }
121 }
122 } else {
123 if (in_headers) {
125 /* some pop servers send the 'From ' line, skip it: */
126 if (msg->hdr_list == NULL)
127 if (strncmp(line1, "From ", 5) == 0)
128 continue;
130 if (line1[0] == ' ' || line1[0] == '\t') {
131 /* continuation of 'folded' header: */
132 if (hdr) {
133 hdr->header = g_strconcat(hdr->header, line1, NULL);
134 }
136 } else if (line1[0] == '\n') {
137 /* an empty line marks end of headers */
138 in_headers = FALSE;
139 } else {
140 /* in all other cases we expect another header */
141 if ((hdr = get_header(line1)))
142 msg->hdr_list = g_list_append(msg->hdr_list, hdr);
143 else {
144 /* if get_header() returns NULL, no header was recognized,
145 so this seems to be the first data line of a broken mailer
146 which does not send an empty line after the headers */
147 in_headers = FALSE;
148 msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1));
149 }
150 }
151 } else {
152 msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1));
153 data_size += strlen(line1);
154 line_cnt++;
155 }
156 }
157 if (conf.max_msg_size && (data_size > conf.max_msg_size)) {
158 DEBUG(4) debugf("accept_message_stream(): "
159 "received %d bytes (conf.max_msg_size=%d)\n",
160 data_size, conf.max_msg_size);
161 return AERR_SIZE;
162 }
164 }
166 if (msg->data_list != NULL)
167 msg->data_list = g_list_reverse(msg->data_list);
168 else
169 /* make sure data list is not NULL: */
170 msg->data_list = g_list_append(NULL, g_strdup(""));
172 DEBUG(4) debugf("received %d lines of data (%d bytes)\n", line_cnt, data_size);
173 /* we get here after we succesfully received the mail data */
175 msg->data_size = data_size;
176 msg->received_time = time(NULL);
178 return AERR_OK;
179 }
181 accept_error
182 accept_message_prepare(message * msg, guint flags)
183 {
184 struct passwd *passwd = NULL;
185 GList *non_rcpt_list = NULL;
186 time_t rec_time = time(NULL);
188 DEBUG(5) debugf("accept_message_prepare()\n");
190 /* create unique message id */
191 msg->uid = g_malloc(14);
193 string_base62(msg->uid, rec_time, 6);
194 msg->uid[6] = '-';
195 string_base62(&(msg->uid[7]), getpid(), 3);
196 msg->uid[10] = '-';
197 string_base62(&(msg->uid[11]), msg->transfer_id, 2);
198 msg->uid[13] = 0;
200 /* if local, get password entry */
201 if (msg->received_host == NULL) {
202 passwd = g_memdup(getpwuid(geteuid()), sizeof(struct passwd));
203 msg->ident = g_strdup(passwd->pw_name);
204 }
206 /* set return path if local */
207 if (msg->return_path == NULL && msg->received_host == NULL) {
208 gchar *path = g_strdup_printf("<%s@%s>", passwd->pw_name, conf.host_name);
209 DEBUG(3) debugf("setting return_path for local accept: %s\n", path);
210 msg->return_path = create_address(path, TRUE);
211 g_free(path);
212 }
214 /* -t option (see comment above) */
215 if (flags & ACC_DEL_RCPTS) {
216 non_rcpt_list = msg->rcpt_list;
217 msg->rcpt_list = NULL;
218 }
220 /* scan headers */
221 {
222 gboolean has_id = FALSE;
223 gboolean has_date = FALSE;
224 gboolean has_sender = FALSE;
225 gboolean has_from = FALSE;
226 gboolean has_to_or_cc = FALSE;
227 GList *hdr_node, *hdr_node_next;
228 header *hdr;
230 for (hdr_node = g_list_first(msg->hdr_list);
231 hdr_node != NULL; hdr_node = hdr_node_next) {
232 hdr_node_next = g_list_next(hdr_node);
233 hdr = ((header *) (hdr_node->data));
234 DEBUG(5) debugf("scanning headers: %s", hdr->header);
235 switch (hdr->id) {
236 case HEAD_MESSAGE_ID:
237 has_id = TRUE;
238 break;
239 case HEAD_DATE:
240 has_date = TRUE;
241 break;
242 case HEAD_FROM:
243 has_from = TRUE;
244 break;
245 case HEAD_SENDER:
246 has_sender = TRUE;
247 break;
248 case HEAD_TO:
249 case HEAD_CC:
250 has_to_or_cc = TRUE;
251 /* fall through */
252 case HEAD_BCC:
253 if (flags & ACC_RCPT_FROM_HEAD) {
254 /* -t option (see comment above) */
255 DEBUG(5) debugf("hdr->value = %s\n", hdr->value);
256 if (hdr->value) {
257 msg->rcpt_list = addr_list_append_rfc822(msg->rcpt_list, hdr->value, conf.host_name);
258 }
259 }
260 if (hdr->id == HEAD_BCC) {
261 DEBUG(3) debugf("removing 'Bcc' header\n");
262 msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node);
263 g_list_free_1(hdr_node);
264 destroy_header(hdr);
265 }
266 break;
267 case HEAD_ENVELOPE_TO:
268 if (flags & ACC_SAVE_ENVELOPE_TO) {
269 DEBUG(3) debugf("creating 'X-Orig-Envelope-To' header\n");
270 msg->hdr_list = g_list_prepend(msg->hdr_list, create_header(HEAD_UNKNOWN,
271 "X-Orig-Envelope-to: %s", hdr->value));
272 }
273 DEBUG(3) debugf("removing 'Envelope-To' header\n");
274 msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node);
275 g_list_free_1(hdr_node);
276 destroy_header(hdr);
277 break;
278 case HEAD_RETURN_PATH:
279 if (flags & ACC_MAIL_FROM_HEAD) {
280 /* usually POP3 accept */
281 msg->return_path = create_address_qualified(hdr->value, TRUE, msg->received_host);
282 DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path));
283 }
284 DEBUG(3) debugf("removing 'Return-Path' header\n");
285 msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node);
286 g_list_free_1(hdr_node);
287 destroy_header(hdr);
288 break;
289 default:
290 break; /* make compiler happy */
291 }
292 }
294 if (msg->return_path == NULL) {
295 /* this can happen for pop3 accept only and if no Return-path: header was given */
296 GList *hdr_list;
297 header *hdr;
299 DEBUG(3) debugf("return_path == NULL\n");
301 hdr_list = find_header(msg->hdr_list, HEAD_SENDER, NULL);
302 if (!hdr_list)
303 hdr_list = find_header(msg->hdr_list, HEAD_FROM, NULL);
304 if (hdr_list) {
305 gchar *addr;
306 hdr = (header *) (g_list_first(hdr_list)->data);
308 DEBUG(5) debugf("hdr->value = '%s'\n", hdr->value);
310 addr = g_strdup(hdr->value);
311 g_strchomp(addr);
313 if ((msg->return_path = create_address_qualified(addr, FALSE, msg->received_host)) != NULL) {
314 DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path));
315 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_UNKNOWN,
316 "X-Warning: return path set from %s address\n",
317 hdr->id == HEAD_SENDER ? "Sender:" : "From:"));
318 }
319 g_free(addr);
320 }
321 if (msg->return_path == NULL) { /* no Sender: or From: or create_address_qualified failed */
322 msg->return_path = create_address_qualified("postmaster", TRUE, conf.host_name);
323 DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path));
324 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_UNKNOWN,
325 "X-Warning: real return path is unknown\n"));
326 }
327 }
329 if (flags & ACC_DEL_RCPTS) {
330 /* remove the recipients given on the command line from the ones given in headers
331 -t option (see comment above) */
332 GList *rcpt_node;
333 foreach(non_rcpt_list, rcpt_node) {
334 address *rcpt = (address *) (rcpt_node->data);
335 GList *node;
336 if ((node = g_list_find_custom(msg->rcpt_list, rcpt, _g_list_addr_isequal))) {
337 DEBUG(3) debugf("removing rcpt address %s\n", addr_string(node->data));
338 msg->rcpt_list = g_list_remove_link(msg->rcpt_list, node);
339 destroy_address((address *) (node->data));
340 g_list_free_1(node);
341 }
342 }
343 }
345 /* here we should have our recipients, fail if not: */
346 if (msg->rcpt_list == NULL) {
347 logwrite(LOG_WARNING, "no recipients found in message\n");
348 return AERR_NORCPT;
349 }
351 if (!(has_sender || has_from)) {
352 DEBUG(3) debugf("adding 'From' header\n");
353 msg->hdr_list = g_list_append(msg->hdr_list,
354 msg->full_sender_name
355 ?
356 create_header(HEAD_FROM, "From: \"%s\" <%s@%s>\n", msg->full_sender_name,
357 msg->return_path->local_part, msg->return_path->domain)
358 :
359 create_header(HEAD_FROM, "From: <%s@%s>\n",
360 msg->return_path->local_part, msg->return_path->domain)
361 );
362 }
363 if (!has_to_or_cc) {
364 DEBUG(3) debugf("no To: or Cc: header, hence adding `To: undisclosed recipients:;'\n");
365 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_TO, "To: undisclosed-recipients:;\n"));
366 }
367 if (!has_date) {
368 DEBUG(3) debugf("adding 'Date:' header\n");
369 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_DATE, "Date: %s\n", rec_timestamp()));
370 }
371 if (!has_id) {
372 DEBUG(3) debugf("adding 'Message-ID:' header\n");
373 msg->hdr_list = g_list_append(msg->hdr_list,
374 create_header(HEAD_MESSAGE_ID, "Message-ID: <%s@%s>\n", msg->uid, conf.host_name));
375 }
376 }
378 /* Received header: */
379 /* At this point because we have to know the rcpts for the 'for' part */
380 gchar *for_string = NULL;
381 header *hdr = NULL;
383 DEBUG(3) debugf("adding 'Received:' header\n");
385 if (g_list_length(msg->rcpt_list) == 1) {
386 address *addr = (address *) (g_list_first(msg->rcpt_list)->data);
387 for_string = g_strdup_printf(" for %s", addr_string(addr));
388 }
390 if (msg->received_host == NULL) {
391 /* received locally */
392 hdr = create_header(HEAD_RECEIVED, "Received: from %s by %s with %s (%s %s) id %s%s; %s\n",
393 passwd->pw_name, conf.host_name, prot_names[msg->received_prot],
394 PACKAGE, VERSION, msg->uid, for_string ? for_string : "", rec_timestamp());
395 } else {
396 /* received from remote */
397 #ifdef ENABLE_IDENT
398 DEBUG(5) debugf("adding 'Received:' header (5)\n");
399 hdr = create_header(HEAD_RECEIVED, "Received: from %s (ident=%s) by %s with %s (%s %s) id %s%s; %s\n",
400 msg->received_host, msg->ident ? msg->ident : "unknown", conf.host_name,
401 prot_names[msg->received_prot], PACKAGE, VERSION, msg->uid, for_string ? for_string : "",
402 rec_timestamp());
403 #else
404 hdr = create_header(HEAD_RECEIVED, "Received: from %s by %s with %s (%s %s) id %s%s; %s\n",
405 msg->received_host, conf.host_name, prot_names[msg->received_prot],
406 PACKAGE, VERSION, msg->uid, for_string ? for_string : "", rec_timestamp());
407 #endif
408 }
409 header_fold(hdr);
410 msg->hdr_list = g_list_prepend(msg->hdr_list, hdr);
412 if (for_string)
413 g_free(for_string);
415 /* write message to spool: */
416 /* accept is no longer responsible for this
417 if (!spool_write(msg, TRUE))
418 return AERR_NOSPOOL;
419 */
420 return AERR_OK;
421 }
423 accept_error
424 accept_message(FILE * in, message * msg, guint flags)
425 {
426 accept_error err;
428 err = accept_message_stream(in, msg, flags);
429 if (err == AERR_OK)
430 err = accept_message_prepare(msg, flags);
432 return err;
433 }