masqmail

annotate src/accept.c @ 378:5781ba87df95

Removed ident. This had been discussed on the mailing list in Oct 2011. Ident is hardly useful in typical setups for masqmail. Probably Oliver had used it in his setup; that would make sense. Now, I know of nobody who needs it.
author markus schnalke <meillo@marmaro.de>
date Sat, 14 Jan 2012 21:36:58 +0100
parents 9bc3e47b0222
children aa40710f09fe
rev   line source
meillo@367 1 /*
meillo@367 2 ** MasqMail
meillo@367 3 ** Copyright (C) 1999-2001 Oliver Kurth
meillo@367 4 ** Copyright (C) 2010 markus schnalke <meillo@marmaro.de>
meillo@367 5 **
meillo@367 6 ** This program is free software; you can redistribute it and/or modify
meillo@367 7 ** it under the terms of the GNU General Public License as published by
meillo@367 8 ** the Free Software Foundation; either version 2 of the License, or
meillo@367 9 ** (at your option) any later version.
meillo@367 10 **
meillo@367 11 ** This program is distributed in the hope that it will be useful,
meillo@367 12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
meillo@367 13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
meillo@367 14 ** GNU General Public License for more details.
meillo@367 15 **
meillo@367 16 ** You should have received a copy of the GNU General Public License
meillo@367 17 ** along with this program; if not, write to the Free Software
meillo@367 18 ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
meillo@0 19 */
meillo@0 20
meillo@0 21 #include "masqmail.h"
meillo@0 22 #include "readsock.h"
meillo@0 23
meillo@299 24 /* must match PROT_* in masqmail.h */
meillo@10 25 gchar *prot_names[] = {
meillo@10 26 "local",
meillo@299 27 "SMTP",
meillo@299 28 "ESMTP",
meillo@10 29 "(unknown)" /* should not happen, but better than crashing. */
meillo@0 30 };
meillo@0 31
meillo@10 32 static gchar*
meillo@366 33 string_base62(gchar *res, guint value, gchar len)
meillo@0 34 {
meillo@10 35 static gchar base62_chars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
meillo@10 36 gchar *p = res + len;
meillo@14 37 *p = '\0';
meillo@10 38 while (p > res) {
meillo@10 39 *(--p) = base62_chars[value % 62];
meillo@10 40 value /= 62;
meillo@10 41 }
meillo@10 42 return res;
meillo@0 43 }
meillo@0 44
meillo@367 45 /*
meillo@367 46 ** accept message from anywhere.
meillo@367 47 ** A message from local is indicated by msg->recieved_host == NULL
meillo@367 48 **
meillo@367 49 ** The -t option: With the ACC_RCPT_FROM_HEAD flag the addrs found found
meillo@367 50 ** in To/Cc/Bcc headers are added to the recipient list.
meillo@0 51 */
meillo@0 52
meillo@10 53 accept_error
meillo@366 54 accept_message_stream(FILE *in, message *msg, guint flags)
meillo@0 55 {
meillo@10 56 gchar *line, *line1;
meillo@10 57 int line_size = MAX_DATALINE;
meillo@10 58 gboolean in_headers = TRUE;
meillo@10 59 header *hdr = NULL;
meillo@10 60 gint line_cnt = 0, data_size = 0;
meillo@0 61
meillo@10 62 line = g_malloc(line_size);
meillo@14 63 line[0] = '\0';
meillo@0 64
meillo@10 65 while (TRUE) {
meillo@10 66 int len = read_sockline1(in, &line, &line_size, 5 * 60, READSOCKL_CVT_CRLF);
meillo@0 67
meillo@10 68 line1 = line;
meillo@0 69
meillo@110 70 if ((line[0] == '.') && (!(flags & ACC_DOT_IGNORE))) {
meillo@10 71 if (line[1] == '\n') {
meillo@10 72 g_free(line);
meillo@10 73 break;
meillo@10 74 }
meillo@10 75 line1++;
meillo@10 76 }
meillo@10 77
meillo@270 78 if ((len == -1) && (flags & (ACC_DOT_IGNORE | ACC_NODOT_RELAX))) {
meillo@270 79 /* we got an EOF, and the last line was not terminated by a CR */
meillo@270 80 gint len1 = strlen(line1);
meillo@270 81 if (len1 > 0) { /* == 0 is 'normal' (EOF after a CR) */
meillo@270 82 if (line1[len1 - 1] != '\n') { /* some mail clients allow unterminated lines */
meillo@270 83 line1[len1] = '\n';
meillo@270 84 line1[len1 + 1] = '\0';
meillo@270 85 msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1));
meillo@270 86 data_size += strlen(line1);
meillo@270 87 line_cnt++;
meillo@10 88 }
meillo@270 89 }
meillo@270 90 break;
meillo@270 91
meillo@270 92 } else if (len == -1) {
meillo@270 93 g_free(line);
meillo@270 94 return AERR_EOF;
meillo@270 95
meillo@270 96 } else if (len == -2) {
meillo@270 97 /* should not happen any more */
meillo@270 98 g_free(line);
meillo@270 99 return AERR_OVERFLOW;
meillo@270 100
meillo@270 101 } else if (len == -3) {
meillo@270 102 g_free(line);
meillo@270 103 return AERR_TIMEOUT;
meillo@270 104
meillo@270 105 } else if (len <= 0) {
meillo@270 106 /* does not happen */
meillo@270 107 g_free(line);
meillo@270 108 DEBUG(5) debugf("read_sockline returned %d\n", len);
meillo@270 109 return AERR_UNKNOWN;
meillo@270 110
meillo@270 111 }
meillo@270 112
meillo@270 113 if (in_headers) {
meillo@270 114
meillo@270 115 /* some pop servers send the 'From ' line, skip it: */
meillo@270 116 if (!msg->hdr_list && strncmp(line1, "From ", 5) == 0) {
meillo@270 117 continue;
meillo@270 118 }
meillo@270 119
meillo@270 120 if (line1[0] == ' ' || line1[0] == '\t') {
meillo@270 121 /* continuation of 'folded' header: */
meillo@270 122 if (hdr) {
meillo@366 123 char *cp;
meillo@323 124 cp = g_strconcat(hdr->header, line1, NULL);
meillo@323 125 hdr->value = cp + (hdr->value - hdr->header);
meillo@323 126 free(hdr->header);
meillo@323 127 hdr->header = cp;
meillo@270 128 }
meillo@270 129
meillo@270 130 } else if (line1[0] == '\n') {
meillo@270 131 /* an empty line marks end of headers */
meillo@270 132 in_headers = FALSE;
meillo@10 133 } else {
meillo@270 134 /* in all other cases we expect another header */
meillo@270 135 if ((hdr = get_header(line1))) {
meillo@270 136 msg->hdr_list = g_list_append(msg->hdr_list, hdr);
meillo@10 137 } else {
meillo@367 138 /*
meillo@367 139 ** if get_header() returns NULL,
meillo@367 140 ** no header was recognized,
meillo@367 141 ** so this seems to be the first
meillo@367 142 ** data line of a broken mailer
meillo@367 143 ** which does not send an empty
meillo@367 144 ** line after the headers
meillo@367 145 */
meillo@270 146 in_headers = FALSE;
meillo@270 147 msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1));
meillo@10 148 }
meillo@10 149 }
meillo@10 150 } else {
meillo@270 151 /* message body */
meillo@270 152 msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1));
meillo@270 153 data_size += strlen(line1);
meillo@270 154 line_cnt++;
meillo@270 155 }
meillo@10 156
meillo@120 157 if (conf.max_msg_size && (data_size > conf.max_msg_size)) {
meillo@117 158 DEBUG(4) debugf("accept_message_stream(): "
meillo@117 159 "received %d bytes (conf.max_msg_size=%d)\n",
meillo@117 160 data_size, conf.max_msg_size);
meillo@117 161 return AERR_SIZE;
meillo@117 162 }
meillo@117 163
meillo@0 164 }
meillo@0 165
meillo@270 166 DEBUG(4) debugf("received %d lines of data (%d bytes)\n", line_cnt, data_size);
meillo@270 167
meillo@270 168 if (!msg->data_list) {
meillo@10 169 /* make sure data list is not NULL: */
meillo@10 170 msg->data_list = g_list_append(NULL, g_strdup(""));
meillo@270 171 }
meillo@270 172 msg->data_list = g_list_reverse(msg->data_list);
meillo@0 173
meillo@10 174 /* we get here after we succesfully received the mail data */
meillo@0 175
meillo@10 176 msg->data_size = data_size;
meillo@10 177 msg->received_time = time(NULL);
meillo@0 178
meillo@10 179 return AERR_OK;
meillo@0 180 }
meillo@0 181
meillo@10 182 accept_error
meillo@366 183 accept_message_prepare(message *msg, guint flags)
meillo@0 184 {
meillo@10 185 struct passwd *passwd = NULL;
meillo@10 186 time_t rec_time = time(NULL);
meillo@0 187
meillo@10 188 DEBUG(5) debugf("accept_message_prepare()\n");
meillo@0 189
meillo@10 190 /* create unique message id */
meillo@10 191 msg->uid = g_malloc(14);
meillo@10 192 string_base62(msg->uid, rec_time, 6);
meillo@10 193 msg->uid[6] = '-';
meillo@298 194 string_base62(msg->uid + 7, getpid(), 3);
meillo@10 195 msg->uid[10] = '-';
meillo@298 196 string_base62(msg->uid + 11, msg->transfer_id, 2);
meillo@298 197 msg->uid[13] = '\0';
meillo@0 198
meillo@298 199 /* if local, get password entry and set return path if missing */
meillo@298 200 if (!msg->received_host) {
meillo@10 201 passwd = g_memdup(getpwuid(geteuid()), sizeof(struct passwd));
meillo@10 202 msg->ident = g_strdup(passwd->pw_name);
meillo@298 203 if (!msg->return_path) {
meillo@298 204 gchar *path = g_strdup_printf("<%s@%s>", passwd->pw_name, conf.host_name);
meillo@298 205 DEBUG(3) debugf("setting return_path for local accept: %s\n", path);
meillo@298 206 msg->return_path = create_address(path, TRUE);
meillo@298 207 g_free(path);
meillo@298 208 }
meillo@10 209 }
meillo@0 210
meillo@10 211 /* scan headers */
meillo@10 212 {
meillo@10 213 gboolean has_id = FALSE;
meillo@10 214 gboolean has_date = FALSE;
meillo@10 215 gboolean has_sender = FALSE;
meillo@10 216 gboolean has_from = FALSE;
meillo@10 217 gboolean has_to_or_cc = FALSE;
meillo@10 218 GList *hdr_node, *hdr_node_next;
meillo@10 219 header *hdr;
meillo@0 220
meillo@10 221 for (hdr_node = g_list_first(msg->hdr_list);
meillo@298 222 hdr_node;
meillo@298 223 hdr_node = hdr_node_next) {
meillo@10 224 hdr_node_next = g_list_next(hdr_node);
meillo@10 225 hdr = ((header *) (hdr_node->data));
meillo@10 226 DEBUG(5) debugf("scanning headers: %s", hdr->header);
meillo@10 227 switch (hdr->id) {
meillo@10 228 case HEAD_MESSAGE_ID:
meillo@10 229 has_id = TRUE;
meillo@10 230 break;
meillo@10 231 case HEAD_DATE:
meillo@10 232 has_date = TRUE;
meillo@10 233 break;
meillo@10 234 case HEAD_FROM:
meillo@10 235 has_from = TRUE;
meillo@10 236 break;
meillo@10 237 case HEAD_SENDER:
meillo@10 238 has_sender = TRUE;
meillo@10 239 break;
meillo@10 240 case HEAD_TO:
meillo@10 241 case HEAD_CC:
meillo@106 242 has_to_or_cc = TRUE;
meillo@106 243 /* fall through */
meillo@10 244 case HEAD_BCC:
meillo@10 245 if (flags & ACC_RCPT_FROM_HEAD) {
meillo@109 246 /* -t option (see comment above) */
meillo@10 247 DEBUG(5) debugf("hdr->value = %s\n", hdr->value);
meillo@10 248 if (hdr->value) {
meillo@10 249 msg->rcpt_list = addr_list_append_rfc822(msg->rcpt_list, hdr->value, conf.host_name);
meillo@10 250 }
meillo@10 251 }
meillo@106 252 if (hdr->id == HEAD_BCC) {
meillo@10 253 DEBUG(3) debugf("removing 'Bcc' header\n");
meillo@10 254 msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node);
meillo@10 255 g_list_free_1(hdr_node);
meillo@10 256 destroy_header(hdr);
meillo@106 257 }
meillo@10 258 break;
meillo@10 259 case HEAD_ENVELOPE_TO:
meillo@10 260 if (flags & ACC_SAVE_ENVELOPE_TO) {
meillo@10 261 DEBUG(3) debugf("creating 'X-Orig-Envelope-To' header\n");
meillo@22 262 msg->hdr_list = g_list_prepend(msg->hdr_list, create_header(HEAD_UNKNOWN,
meillo@276 263 "X-Orig-Envelope-To: %s", hdr->value));
meillo@10 264 }
meillo@10 265 DEBUG(3) debugf("removing 'Envelope-To' header\n");
meillo@10 266 msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node);
meillo@10 267 g_list_free_1(hdr_node);
meillo@10 268 destroy_header(hdr);
meillo@10 269 break;
meillo@10 270 case HEAD_RETURN_PATH:
meillo@10 271 if (flags & ACC_MAIL_FROM_HEAD) {
meillo@10 272 /* usually POP3 accept */
meillo@10 273 msg->return_path = create_address_qualified(hdr->value, TRUE, msg->received_host);
meillo@10 274 DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path));
meillo@10 275 }
meillo@10 276 DEBUG(3) debugf("removing 'Return-Path' header\n");
meillo@10 277 msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node);
meillo@10 278 g_list_free_1(hdr_node);
meillo@10 279 destroy_header(hdr);
meillo@10 280 break;
meillo@10 281 default:
meillo@10 282 break; /* make compiler happy */
meillo@10 283 }
meillo@10 284 }
meillo@10 285
meillo@298 286 if (!msg->return_path) {
meillo@367 287 /*
meillo@367 288 ** TODO: do we still need this as we don't fetch
meillo@367 289 ** mail anymore?
meillo@367 290 ** This can happen for pop3 accept only and if no
meillo@367 291 ** Return-Path: header was given
meillo@367 292 */
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@102 376 DEBUG(5) debugf("adding 'Received:' header (5)\n");
meillo@289 377 hdr = create_header(HEAD_RECEIVED,
meillo@362 378 "Received: from %s\n\tby %s with %s (%s %s)\n\tid %s%s; %s\n",
meillo@362 379 msg->received_host,
meillo@289 380 conf.host_name, prot_names[msg->received_prot], PACKAGE,
meillo@289 381 VERSION, msg->uid, for_string ? for_string : "",
meillo@289 382 rec_timestamp());
meillo@102 383 }
meillo@102 384 msg->hdr_list = g_list_prepend(msg->hdr_list, hdr);
meillo@10 385
meillo@102 386 if (for_string)
meillo@102 387 g_free(for_string);
meillo@0 388
meillo@10 389 return AERR_OK;
meillo@0 390 }
meillo@0 391
meillo@10 392 accept_error
meillo@366 393 accept_message(FILE *in, message *msg, guint flags)
meillo@0 394 {
meillo@10 395 accept_error err;
meillo@0 396
meillo@10 397 err = accept_message_stream(in, msg, flags);
meillo@270 398 if (err == AERR_OK) {
meillo@10 399 err = accept_message_prepare(msg, flags);
meillo@270 400 }
meillo@0 401
meillo@10 402 return err;
meillo@0 403 }