masqmail

annotate src/accept.c @ 209:10da50168dab

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