masqmail

annotate src/accept.c @ 304:d5ce2ba71e7b

manual formating of Received: hdrs; changed hdr for local receival Now the Received: headers are much friendlier to read. About folding: We must fold any line at 998 chars before transfer. We should fold the lines we produce at 78 chars. That is what RFC 2821 requests. We should think about it, somewhen. The header for locally (i.e. non-SMTP) received mail is changed to the format postfix uses. This matches RFC 2821 better. The `from' clause should contain a domain or IP, not a user name. Also, the `with' clause should contain a registered standard protocol name, which ``local'' is not.
author markus schnalke <meillo@marmaro.de>
date Thu, 09 Dec 2010 18:28:11 -0300
parents 3e3c280ca5b2
children 29de6a1c4538
rev   line source
meillo@0 1 /* MasqMail
meillo@0 2 Copyright (C) 1999-2001 Oliver Kurth
meillo@224 3 Copyright (C) 2010 markus schnalke <meillo@marmaro.de>
meillo@0 4
meillo@0 5 This program is free software; you can redistribute it and/or modify
meillo@0 6 it under the terms of the GNU General Public License as published by
meillo@0 7 the Free Software Foundation; either version 2 of the License, or
meillo@0 8 (at your option) any later version.
meillo@0 9
meillo@0 10 This program is distributed in the hope that it will be useful,
meillo@0 11 but WITHOUT ANY WARRANTY; without even the implied warranty of
meillo@0 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
meillo@0 13 GNU General Public License for more details.
meillo@0 14
meillo@0 15 You should have received a copy of the GNU General Public License
meillo@0 16 along with this program; if not, write to the Free Software
meillo@0 17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
meillo@0 18 */
meillo@0 19
meillo@0 20 #include "masqmail.h"
meillo@0 21 #include "readsock.h"
meillo@0 22
meillo@299 23 /* must match PROT_* in masqmail.h */
meillo@10 24 gchar *prot_names[] = {
meillo@10 25 "local",
meillo@299 26 "SMTP",
meillo@299 27 "ESMTP",
meillo@10 28 "(unknown)" /* should not happen, but better than crashing. */
meillo@0 29 };
meillo@0 30
meillo@10 31 static gchar*
meillo@10 32 string_base62(gchar * res, guint value, gchar len)
meillo@0 33 {
meillo@10 34 static gchar base62_chars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
meillo@10 35 gchar *p = res + len;
meillo@14 36 *p = '\0';
meillo@10 37 while (p > res) {
meillo@10 38 *(--p) = base62_chars[value % 62];
meillo@10 39 value /= 62;
meillo@10 40 }
meillo@10 41 return res;
meillo@0 42 }
meillo@0 43
meillo@10 44 static gint
meillo@10 45 _g_list_addr_isequal(gconstpointer a, gconstpointer b)
meillo@0 46 {
meillo@10 47 address *addr1 = (address *) a;
meillo@10 48 address *addr2 = (address *) b;
meillo@10 49 int ret;
meillo@0 50
meillo@270 51 if ((ret = strcasecmp(addr1->domain, addr2->domain)) == 0) {
meillo@10 52 return strcmp(addr1->local_part, addr2->local_part);
meillo@270 53 }
meillo@270 54 return ret;
meillo@0 55 }
meillo@0 56
meillo@0 57 /* accept message from anywhere.
meillo@276 58 A message from local is indicated by msg->recieved_host == NULL
meillo@0 59
meillo@276 60 The -t option: With the ACC_RCPT_FROM_HEAD flag the addrs found found
meillo@276 61 in To/Cc/Bcc headers are added to the recipient list.
meillo@0 62 */
meillo@0 63
meillo@10 64 accept_error
meillo@10 65 accept_message_stream(FILE * in, message * msg, guint flags)
meillo@0 66 {
meillo@10 67 gchar *line, *line1;
meillo@10 68 int line_size = MAX_DATALINE;
meillo@10 69 gboolean in_headers = TRUE;
meillo@10 70 header *hdr = NULL;
meillo@10 71 gint line_cnt = 0, data_size = 0;
meillo@0 72
meillo@10 73 line = g_malloc(line_size);
meillo@14 74 line[0] = '\0';
meillo@0 75
meillo@10 76 while (TRUE) {
meillo@10 77 int len = read_sockline1(in, &line, &line_size, 5 * 60, READSOCKL_CVT_CRLF);
meillo@0 78
meillo@10 79 line1 = line;
meillo@0 80
meillo@110 81 if ((line[0] == '.') && (!(flags & ACC_DOT_IGNORE))) {
meillo@10 82 if (line[1] == '\n') {
meillo@10 83 g_free(line);
meillo@10 84 break;
meillo@10 85 }
meillo@10 86 line1++;
meillo@10 87 }
meillo@10 88
meillo@270 89 if ((len == -1) && (flags & (ACC_DOT_IGNORE | ACC_NODOT_RELAX))) {
meillo@270 90 /* we got an EOF, and the last line was not terminated by a CR */
meillo@270 91 gint len1 = strlen(line1);
meillo@270 92 if (len1 > 0) { /* == 0 is 'normal' (EOF after a CR) */
meillo@270 93 if (line1[len1 - 1] != '\n') { /* some mail clients allow unterminated lines */
meillo@270 94 line1[len1] = '\n';
meillo@270 95 line1[len1 + 1] = '\0';
meillo@270 96 msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1));
meillo@270 97 data_size += strlen(line1);
meillo@270 98 line_cnt++;
meillo@10 99 }
meillo@270 100 }
meillo@270 101 break;
meillo@270 102
meillo@270 103 } else if (len == -1) {
meillo@270 104 g_free(line);
meillo@270 105 return AERR_EOF;
meillo@270 106
meillo@270 107 } else if (len == -2) {
meillo@270 108 /* should not happen any more */
meillo@270 109 g_free(line);
meillo@270 110 return AERR_OVERFLOW;
meillo@270 111
meillo@270 112 } else if (len == -3) {
meillo@270 113 g_free(line);
meillo@270 114 return AERR_TIMEOUT;
meillo@270 115
meillo@270 116 } else if (len <= 0) {
meillo@270 117 /* does not happen */
meillo@270 118 g_free(line);
meillo@270 119 DEBUG(5) debugf("read_sockline returned %d\n", len);
meillo@270 120 return AERR_UNKNOWN;
meillo@270 121
meillo@270 122 }
meillo@270 123
meillo@270 124 if (in_headers) {
meillo@270 125
meillo@270 126 /* some pop servers send the 'From ' line, skip it: */
meillo@270 127 if (!msg->hdr_list && strncmp(line1, "From ", 5) == 0) {
meillo@270 128 continue;
meillo@270 129 }
meillo@270 130
meillo@270 131 if (line1[0] == ' ' || line1[0] == '\t') {
meillo@270 132 /* continuation of 'folded' header: */
meillo@270 133 if (hdr) {
meillo@270 134 hdr->header = g_strconcat(hdr->header, line1, NULL);
meillo@270 135 }
meillo@270 136
meillo@270 137 } else if (line1[0] == '\n') {
meillo@270 138 /* an empty line marks end of headers */
meillo@270 139 in_headers = FALSE;
meillo@10 140 } else {
meillo@270 141 /* in all other cases we expect another header */
meillo@270 142 if ((hdr = get_header(line1))) {
meillo@270 143 msg->hdr_list = g_list_append(msg->hdr_list, hdr);
meillo@10 144 } else {
meillo@270 145 /* if get_header() returns NULL, no header was recognized,
meillo@270 146 so this seems to be the first data line of a broken mailer
meillo@270 147 which does not send an empty line after the headers */
meillo@270 148 in_headers = FALSE;
meillo@270 149 msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1));
meillo@10 150 }
meillo@10 151 }
meillo@10 152 } else {
meillo@270 153 /* message body */
meillo@270 154 msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1));
meillo@270 155 data_size += strlen(line1);
meillo@270 156 line_cnt++;
meillo@270 157 }
meillo@10 158
meillo@120 159 if (conf.max_msg_size && (data_size > conf.max_msg_size)) {
meillo@117 160 DEBUG(4) debugf("accept_message_stream(): "
meillo@117 161 "received %d bytes (conf.max_msg_size=%d)\n",
meillo@117 162 data_size, conf.max_msg_size);
meillo@117 163 return AERR_SIZE;
meillo@117 164 }
meillo@117 165
meillo@0 166 }
meillo@0 167
meillo@270 168 DEBUG(4) debugf("received %d lines of data (%d bytes)\n", line_cnt, data_size);
meillo@270 169
meillo@270 170 if (!msg->data_list) {
meillo@10 171 /* make sure data list is not NULL: */
meillo@10 172 msg->data_list = g_list_append(NULL, g_strdup(""));
meillo@270 173 }
meillo@270 174 msg->data_list = g_list_reverse(msg->data_list);
meillo@0 175
meillo@10 176 /* we get here after we succesfully received the mail data */
meillo@0 177
meillo@10 178 msg->data_size = data_size;
meillo@10 179 msg->received_time = time(NULL);
meillo@0 180
meillo@10 181 return AERR_OK;
meillo@0 182 }
meillo@0 183
meillo@10 184 accept_error
meillo@10 185 accept_message_prepare(message * msg, guint flags)
meillo@0 186 {
meillo@10 187 struct passwd *passwd = NULL;
meillo@10 188 time_t rec_time = time(NULL);
meillo@0 189
meillo@10 190 DEBUG(5) debugf("accept_message_prepare()\n");
meillo@0 191
meillo@10 192 /* create unique message id */
meillo@10 193 msg->uid = g_malloc(14);
meillo@10 194 string_base62(msg->uid, rec_time, 6);
meillo@10 195 msg->uid[6] = '-';
meillo@298 196 string_base62(msg->uid + 7, getpid(), 3);
meillo@10 197 msg->uid[10] = '-';
meillo@298 198 string_base62(msg->uid + 11, msg->transfer_id, 2);
meillo@298 199 msg->uid[13] = '\0';
meillo@0 200
meillo@298 201 /* if local, get password entry and set return path if missing */
meillo@298 202 if (!msg->received_host) {
meillo@10 203 passwd = g_memdup(getpwuid(geteuid()), sizeof(struct passwd));
meillo@10 204 msg->ident = g_strdup(passwd->pw_name);
meillo@298 205 if (!msg->return_path) {
meillo@298 206 gchar *path = g_strdup_printf("<%s@%s>", passwd->pw_name, conf.host_name);
meillo@298 207 DEBUG(3) debugf("setting return_path for local accept: %s\n", path);
meillo@298 208 msg->return_path = create_address(path, TRUE);
meillo@298 209 g_free(path);
meillo@298 210 }
meillo@10 211 }
meillo@0 212
meillo@10 213 /* scan headers */
meillo@10 214 {
meillo@10 215 gboolean has_id = FALSE;
meillo@10 216 gboolean has_date = FALSE;
meillo@10 217 gboolean has_sender = FALSE;
meillo@10 218 gboolean has_from = FALSE;
meillo@10 219 gboolean has_to_or_cc = FALSE;
meillo@10 220 GList *hdr_node, *hdr_node_next;
meillo@10 221 header *hdr;
meillo@0 222
meillo@10 223 for (hdr_node = g_list_first(msg->hdr_list);
meillo@298 224 hdr_node;
meillo@298 225 hdr_node = hdr_node_next) {
meillo@10 226 hdr_node_next = g_list_next(hdr_node);
meillo@10 227 hdr = ((header *) (hdr_node->data));
meillo@10 228 DEBUG(5) debugf("scanning headers: %s", hdr->header);
meillo@10 229 switch (hdr->id) {
meillo@10 230 case HEAD_MESSAGE_ID:
meillo@10 231 has_id = TRUE;
meillo@10 232 break;
meillo@10 233 case HEAD_DATE:
meillo@10 234 has_date = TRUE;
meillo@10 235 break;
meillo@10 236 case HEAD_FROM:
meillo@10 237 has_from = TRUE;
meillo@10 238 break;
meillo@10 239 case HEAD_SENDER:
meillo@10 240 has_sender = TRUE;
meillo@10 241 break;
meillo@10 242 case HEAD_TO:
meillo@10 243 case HEAD_CC:
meillo@106 244 has_to_or_cc = TRUE;
meillo@106 245 /* fall through */
meillo@10 246 case HEAD_BCC:
meillo@10 247 if (flags & ACC_RCPT_FROM_HEAD) {
meillo@109 248 /* -t option (see comment above) */
meillo@10 249 DEBUG(5) debugf("hdr->value = %s\n", hdr->value);
meillo@10 250 if (hdr->value) {
meillo@10 251 msg->rcpt_list = addr_list_append_rfc822(msg->rcpt_list, hdr->value, conf.host_name);
meillo@10 252 }
meillo@10 253 }
meillo@106 254 if (hdr->id == HEAD_BCC) {
meillo@10 255 DEBUG(3) debugf("removing 'Bcc' header\n");
meillo@10 256 msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node);
meillo@10 257 g_list_free_1(hdr_node);
meillo@10 258 destroy_header(hdr);
meillo@106 259 }
meillo@10 260 break;
meillo@10 261 case HEAD_ENVELOPE_TO:
meillo@10 262 if (flags & ACC_SAVE_ENVELOPE_TO) {
meillo@10 263 DEBUG(3) debugf("creating 'X-Orig-Envelope-To' header\n");
meillo@22 264 msg->hdr_list = g_list_prepend(msg->hdr_list, create_header(HEAD_UNKNOWN,
meillo@276 265 "X-Orig-Envelope-To: %s", hdr->value));
meillo@10 266 }
meillo@10 267 DEBUG(3) debugf("removing 'Envelope-To' header\n");
meillo@10 268 msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node);
meillo@10 269 g_list_free_1(hdr_node);
meillo@10 270 destroy_header(hdr);
meillo@10 271 break;
meillo@10 272 case HEAD_RETURN_PATH:
meillo@10 273 if (flags & ACC_MAIL_FROM_HEAD) {
meillo@10 274 /* usually POP3 accept */
meillo@10 275 msg->return_path = create_address_qualified(hdr->value, TRUE, msg->received_host);
meillo@10 276 DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path));
meillo@10 277 }
meillo@10 278 DEBUG(3) debugf("removing 'Return-Path' header\n");
meillo@10 279 msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node);
meillo@10 280 g_list_free_1(hdr_node);
meillo@10 281 destroy_header(hdr);
meillo@10 282 break;
meillo@10 283 default:
meillo@10 284 break; /* make compiler happy */
meillo@10 285 }
meillo@10 286 }
meillo@10 287
meillo@298 288 if (!msg->return_path) {
meillo@298 289 /* TODO: do we still need this as we don't fetch
meillo@298 290 mail anymore? */
meillo@298 291 /* this can happen for pop3 accept only and if no
meillo@298 292 Return-Path: header was given */
meillo@10 293 GList *hdr_list;
meillo@10 294 header *hdr;
meillo@10 295
meillo@10 296 DEBUG(3) debugf("return_path == NULL\n");
meillo@10 297
meillo@10 298 hdr_list = find_header(msg->hdr_list, HEAD_SENDER, NULL);
meillo@298 299 if (!hdr_list) {
meillo@10 300 hdr_list = find_header(msg->hdr_list, HEAD_FROM, NULL);
meillo@298 301 }
meillo@10 302 if (hdr_list) {
meillo@10 303 gchar *addr;
meillo@10 304 hdr = (header *) (g_list_first(hdr_list)->data);
meillo@10 305
meillo@10 306 DEBUG(5) debugf("hdr->value = '%s'\n", hdr->value);
meillo@10 307
meillo@10 308 addr = g_strdup(hdr->value);
meillo@10 309 g_strchomp(addr);
meillo@10 310
meillo@299 311 msg->return_path = create_address_qualified(addr, FALSE, msg->received_host);
meillo@298 312 if (msg->return_path) {
meillo@10 313 DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path));
meillo@298 314 msg->hdr_list = g_list_append( msg->hdr_list, create_header(HEAD_UNKNOWN, "X-Warning: return path set from %s address\n", hdr->id == HEAD_SENDER ? "Sender:" : "From:"));
meillo@10 315 }
meillo@10 316 g_free(addr);
meillo@10 317 }
meillo@298 318 if (!msg->return_path) {
meillo@298 319 /* no Sender: or From: or
meillo@298 320 create_address_qualified failed */
meillo@10 321 msg->return_path = create_address_qualified("postmaster", TRUE, conf.host_name);
meillo@10 322 DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path));
meillo@298 323 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_UNKNOWN, "X-Warning: real return path is unknown\n"));
meillo@10 324 }
meillo@10 325 }
meillo@10 326
meillo@10 327 /* here we should have our recipients, fail if not: */
meillo@276 328 if (!msg->rcpt_list) {
meillo@10 329 logwrite(LOG_WARNING, "no recipients found in message\n");
meillo@10 330 return AERR_NORCPT;
meillo@10 331 }
meillo@10 332
meillo@299 333 if (!has_sender && !has_from) {
meillo@298 334 DEBUG(3) debugf("adding 'From:' header\n");
meillo@298 335 if (msg->full_sender_name) {
meillo@298 336 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_FROM, "From: \"%s\" <%s@%s>\n", msg->full_sender_name, msg->return_path->local_part, msg->return_path->domain));
meillo@298 337 } else {
meillo@298 338 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_FROM, "From: <%s@%s>\n", msg->return_path->local_part, msg->return_path->domain));
meillo@298 339 }
meillo@10 340 }
meillo@106 341 if (!has_to_or_cc) {
meillo@106 342 DEBUG(3) debugf("no To: or Cc: header, hence adding `To: undisclosed recipients:;'\n");
meillo@105 343 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_TO, "To: undisclosed-recipients:;\n"));
meillo@10 344 }
meillo@10 345 if (!has_date) {
meillo@10 346 DEBUG(3) debugf("adding 'Date:' header\n");
meillo@10 347 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_DATE, "Date: %s\n", rec_timestamp()));
meillo@10 348 }
meillo@10 349 if (!has_id) {
meillo@10 350 DEBUG(3) debugf("adding 'Message-ID:' header\n");
meillo@298 351 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_MESSAGE_ID, "Message-ID: <%s@%s>\n", msg->uid, conf.host_name));
meillo@10 352 }
meillo@0 353 }
meillo@10 354
meillo@10 355 /* Received header: */
meillo@10 356 /* At this point because we have to know the rcpts for the 'for' part */
meillo@298 357 /* The `for' part will only be used if exactly one rcpt is present. */
meillo@102 358 gchar *for_string = NULL;
meillo@102 359 header *hdr = NULL;
meillo@10 360
meillo@102 361 DEBUG(3) debugf("adding 'Received:' header\n");
meillo@10 362
meillo@102 363 if (g_list_length(msg->rcpt_list) == 1) {
meillo@102 364 address *addr = (address *) (g_list_first(msg->rcpt_list)->data);
meillo@304 365 for_string = g_strdup_printf("\n\tfor %s", addr_string(addr));
meillo@102 366 }
meillo@10 367
meillo@289 368 if (!msg->received_host) {
meillo@102 369 /* received locally */
meillo@289 370 hdr = create_header(HEAD_RECEIVED,
meillo@304 371 "Received: by %s (%s %s, from userid %d)\n\tid %s%s; %s\n",
meillo@304 372 conf.host_name, PACKAGE, VERSION, geteuid(),
meillo@289 373 msg->uid, for_string ? for_string : "", rec_timestamp());
meillo@102 374 } else {
meillo@102 375 /* received from remote */
meillo@10 376 #ifdef ENABLE_IDENT
meillo@102 377 DEBUG(5) debugf("adding 'Received:' header (5)\n");
meillo@289 378 hdr = create_header(HEAD_RECEIVED,
meillo@304 379 "Received: from %s (ident=%s)\n\tby %s with %s (%s %s)\n\tid %s%s; %s\n",
meillo@289 380 msg->received_host, msg->ident ? msg->ident : "unknown",
meillo@289 381 conf.host_name, prot_names[msg->received_prot], PACKAGE,
meillo@289 382 VERSION, msg->uid, for_string ? for_string : "",
meillo@289 383 rec_timestamp());
meillo@10 384 #else
meillo@289 385 hdr = create_header(HEAD_RECEIVED,
meillo@304 386 "Received: from %s\n\tby %s with %s (%s %s)\n\tid %s%s; %s\n",
meillo@289 387 msg->received_host, conf.host_name,
meillo@289 388 prot_names[msg->received_prot], PACKAGE, VERSION,
meillo@289 389 msg->uid, for_string ? for_string : "", rec_timestamp());
meillo@10 390 #endif
meillo@102 391 }
meillo@102 392 msg->hdr_list = g_list_prepend(msg->hdr_list, hdr);
meillo@10 393
meillo@102 394 if (for_string)
meillo@102 395 g_free(for_string);
meillo@0 396
meillo@10 397 return AERR_OK;
meillo@0 398 }
meillo@0 399
meillo@10 400 accept_error
meillo@10 401 accept_message(FILE * in, message * msg, guint flags)
meillo@0 402 {
meillo@10 403 accept_error err;
meillo@0 404
meillo@10 405 err = accept_message_stream(in, msg, flags);
meillo@270 406 if (err == AERR_OK) {
meillo@10 407 err = accept_message_prepare(msg, flags);
meillo@270 408 }
meillo@0 409
meillo@10 410 return err;
meillo@0 411 }