masqmail-0.2

annotate src/accept.c @ 3:8c55886cacd8

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