masqmail-0.2
diff src/accept.c @ 0:08114f7dcc23
this is masqmail-0.2.21 from oliver kurth
author | meillo@marmaro.de |
---|---|
date | Fri, 26 Sep 2008 17:05:23 +0200 |
parents | |
children | 26e34ae9a3e3 |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/accept.c Fri Sep 26 17:05:23 2008 +0200 1.3 @@ -0,0 +1,478 @@ 1.4 +/* MasqMail 1.5 + Copyright (C) 1999-2001 Oliver Kurth 1.6 + 1.7 + This program is free software; you can redistribute it and/or modify 1.8 + it under the terms of the GNU General Public License as published by 1.9 + the Free Software Foundation; either version 2 of the License, or 1.10 + (at your option) any later version. 1.11 + 1.12 + This program is distributed in the hope that it will be useful, 1.13 + but WITHOUT ANY WARRANTY; without even the implied warranty of 1.14 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1.15 + GNU General Public License for more details. 1.16 + 1.17 + You should have received a copy of the GNU General Public License 1.18 + along with this program; if not, write to the Free Software 1.19 + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 1.20 +*/ 1.21 + 1.22 +#include "masqmail.h" 1.23 +#include "readsock.h" 1.24 + 1.25 +gchar *prot_names[] = 1.26 +{ 1.27 + "local", 1.28 + "bsmtp", 1.29 + "smtp", 1.30 + "esmtp", 1.31 + "pop3", 1.32 + "apop", 1.33 + "(unknown)" /* should not happen, but better than crashing. */ 1.34 +}; 1.35 + 1.36 +static 1.37 +gchar *string_base62(gchar *res, guint value, gchar len) 1.38 +{ 1.39 + static gchar base62_chars[] = 1.40 + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 1.41 + gchar *p = res + len; 1.42 + *p = 0; 1.43 + while (p > res){ 1.44 + *(--p) = base62_chars[value % 62]; 1.45 + value /= 62; 1.46 + } 1.47 + return res; 1.48 +} 1.49 + 1.50 +static gint _g_list_addr_isequal(gconstpointer a, gconstpointer b) 1.51 +{ 1.52 + address *addr1 = (address *)a; 1.53 + address *addr2 = (address *)b; 1.54 + int ret; 1.55 + 1.56 + if((ret = strcasecmp(addr1->domain, addr2->domain)) == 0) 1.57 + return strcmp(addr1->local_part, addr2->local_part); 1.58 + else 1.59 + return ret; 1.60 +} 1.61 + 1.62 +/* accept message from anywhere. 1.63 + A locally originating message is indicated by msg->recieved_host == NULL 1.64 + 1.65 + If the flags ACC_DEL_RCPTS is set, recipients in the msg->rcpt_list is 1.66 + copied and items occuring in it will be removed from the newly constructed 1.67 + (from To/Cc/Bcc headers if ACC_RCPT_TO is set) rcpt_list. 1.68 +*/ 1.69 + 1.70 +accept_error accept_message_stream(FILE *in, message *msg, guint flags) 1.71 +{ 1.72 + gchar *line, *line1; 1.73 + int line_size = MAX_DATALINE; 1.74 + gboolean in_headers = TRUE; 1.75 + header *hdr = NULL; 1.76 + gint line_cnt = 0, data_size = 0; 1.77 + 1.78 + line = g_malloc(line_size); 1.79 + line[0] = 0; 1.80 + 1.81 + while(TRUE){ 1.82 + int len = read_sockline1(in, &line, &line_size, 5*60, READSOCKL_CVT_CRLF); 1.83 + 1.84 + line1 = line; 1.85 + 1.86 + if((line[0] == '.') && (!(flags & ACC_NODOT_TERM))){ 1.87 + if(line[1] == '\n'){ 1.88 + g_free(line); 1.89 + break; 1.90 + } 1.91 + line1++; 1.92 + } 1.93 + 1.94 + if(len <= 0){ 1.95 + if((len == -1) && ((flags & ACC_NODOT_TERM) || (flags & ACC_NODOT_RELAX))){ 1.96 + /* we got an EOF, and the last line was not terminated by a CR */ 1.97 + gint len1 = strlen(line1); 1.98 + if(len1 > 0){ /* == 0 is 'normal' (EOF after a CR) */ 1.99 + if(line1[len1-1] != '\n'){ /* some mail clients allow unterminated lines */ 1.100 + line1[len1] = '\n'; 1.101 + line1[len1+1] = 0; 1.102 + msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1)); 1.103 + data_size += strlen(line1); 1.104 + line_cnt++; 1.105 + } 1.106 + } 1.107 + break; 1.108 + }else{ 1.109 + g_free(line); 1.110 + if(len == -1){ 1.111 + return AERR_EOF; 1.112 + }else if(len == -2){ 1.113 + /* should not happen any more */ 1.114 + return AERR_OVERFLOW; 1.115 + }else if(len == -3){ 1.116 + return AERR_TIMEOUT; 1.117 + }else{ 1.118 + /* does not happen */ 1.119 + DEBUG(5) debugf("read_sockline returned %d\n", len); 1.120 + return AERR_UNKNOWN; 1.121 + } 1.122 + } 1.123 + } 1.124 + else{ 1.125 + if(in_headers){ 1.126 + 1.127 + /* some pop servers send the 'From ' line, skip it: */ 1.128 + if(msg->hdr_list == NULL) 1.129 + if(strncmp(line1, "From ", 5) == 0) 1.130 + continue; 1.131 + 1.132 + if(line1[0] == ' ' || line1[0] == '\t'){ 1.133 + /* continuation of 'folded' header: */ 1.134 + if(hdr){ 1.135 + hdr->header = g_strconcat(hdr->header, line1, NULL); 1.136 + } 1.137 + 1.138 + }else if(line1[0] == '\n'){ 1.139 + /* an empty line marks end of headers */ 1.140 + in_headers = FALSE; 1.141 + }else{ 1.142 + /* in all other cases we expect another header */ 1.143 + if((hdr = get_header(line1))) 1.144 + msg->hdr_list = g_list_append(msg->hdr_list, hdr); 1.145 + else{ 1.146 + /* if get_header() returns NULL, no header was recognized, 1.147 + so this seems to be the first data line of a broken mailer 1.148 + which does not send an empty line after the headers */ 1.149 + in_headers = FALSE; 1.150 + msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1)); 1.151 + } 1.152 + } 1.153 + }else{ 1.154 + msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1)); 1.155 + data_size += strlen(line1); 1.156 + line_cnt++; 1.157 + } 1.158 + } 1.159 + } 1.160 + 1.161 + if(msg->data_list != NULL) 1.162 + msg->data_list = g_list_reverse(msg->data_list); 1.163 + else 1.164 + /* make sure data list is not NULL: */ 1.165 + msg->data_list = g_list_append(NULL, g_strdup("")); 1.166 + 1.167 + DEBUG(4) debugf("received %d lines of data (%d bytes)\n", 1.168 + line_cnt, data_size); 1.169 + /* we get here after we succesfully 1.170 + received the mail data */ 1.171 + 1.172 + msg->data_size = data_size; 1.173 + msg->received_time = time(NULL); 1.174 + 1.175 + return AERR_OK; 1.176 +} 1.177 + 1.178 +accept_error accept_message_prepare(message *msg, guint flags) 1.179 +{ 1.180 + struct passwd *passwd = NULL; 1.181 + GList *non_rcpt_list = NULL; 1.182 + time_t rec_time = time(NULL); 1.183 + 1.184 + DEBUG(5) debugf("accept_message_prepare()\n"); 1.185 + 1.186 + /* create unique message id */ 1.187 + msg->uid = g_malloc(14); 1.188 + 1.189 + string_base62(msg->uid, rec_time, 6); 1.190 + msg->uid[6] = '-'; 1.191 + string_base62(&(msg->uid[7]), getpid(), 3); 1.192 + msg->uid[10] = '-'; 1.193 + string_base62(&(msg->uid[11]), msg->transfer_id, 2); 1.194 + msg->uid[13] = 0; 1.195 + 1.196 + /* if local, get password entry */ 1.197 + if(msg->received_host == NULL){ 1.198 + passwd = g_memdup(getpwuid(geteuid()), sizeof(struct passwd)); 1.199 + msg->ident = g_strdup(passwd->pw_name); 1.200 + } 1.201 + 1.202 + /* set return path if local */ 1.203 + if(msg->return_path == NULL){ 1.204 + 1.205 + if(msg->received_host == NULL){ 1.206 + gchar *path = g_strdup_printf("<%s@%s>", 1.207 + passwd->pw_name, conf.host_name); 1.208 + DEBUG(3) debugf("setting return_path for local accept: %s\n", path); 1.209 + msg->return_path = create_address(path, TRUE); 1.210 + g_free(path); 1.211 + } 1.212 + } 1.213 + 1.214 + /* -t option */ 1.215 + if(flags & ACC_DEL_RCPTS){ 1.216 + non_rcpt_list = msg->rcpt_list; 1.217 + msg->rcpt_list = NULL; 1.218 + } 1.219 + 1.220 + /* scan headers */ 1.221 + { 1.222 + gboolean has_id = FALSE; 1.223 + gboolean has_date = FALSE; 1.224 + gboolean has_sender = FALSE; 1.225 + gboolean has_from = FALSE; 1.226 + gboolean has_rcpt = FALSE; 1.227 + gboolean has_to_or_cc = FALSE; 1.228 + GList *hdr_node, *hdr_node_next; 1.229 + header *hdr; 1.230 + 1.231 + for(hdr_node = g_list_first(msg->hdr_list); 1.232 + hdr_node != NULL; 1.233 + hdr_node = hdr_node_next){ 1.234 + hdr_node_next = g_list_next(hdr_node); 1.235 + hdr = ((header *)(hdr_node->data)); 1.236 + DEBUG(5) debugf("scanning headers: %s", hdr->header); 1.237 + switch(hdr->id){ 1.238 + case HEAD_MESSAGE_ID: 1.239 + has_id = TRUE; break; 1.240 + case HEAD_DATE: 1.241 + has_date = TRUE; break; 1.242 + case HEAD_FROM: 1.243 + has_from = TRUE; 1.244 + break; 1.245 + case HEAD_SENDER: 1.246 + has_sender = TRUE; 1.247 + break; 1.248 + case HEAD_TO: 1.249 + case HEAD_CC: 1.250 + case HEAD_BCC: 1.251 + has_rcpt = TRUE; 1.252 + if(flags & ACC_RCPT_FROM_HEAD){ 1.253 + DEBUG(5) debugf("hdr->value = %s\n", hdr->value); 1.254 + if(hdr->value){ 1.255 + msg->rcpt_list = 1.256 + addr_list_append_rfc822(msg->rcpt_list, hdr->value, conf.host_name); 1.257 + } 1.258 + } 1.259 + if((flags & ACC_DEL_BCC) && (hdr->id == HEAD_BCC)){ 1.260 + DEBUG(3) debugf("removing 'Bcc' header\n"); 1.261 + msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node); 1.262 + g_list_free_1(hdr_node); 1.263 + destroy_header(hdr); 1.264 + }else 1.265 + has_to_or_cc = TRUE; 1.266 + break; 1.267 + case HEAD_ENVELOPE_TO: 1.268 + if(flags & ACC_SAVE_ENVELOPE_TO){ 1.269 + DEBUG(3) debugf("creating 'X-Orig-Envelope-To' header\n"); 1.270 + msg->hdr_list = 1.271 + g_list_prepend(msg->hdr_list, 1.272 + create_header(HEAD_UNKNOWN, 1.273 + "X-Orig-Envelope-to: %s", hdr->value)); 1.274 + } 1.275 + DEBUG(3) debugf("removing 'Envelope-To' header\n"); 1.276 + msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node); 1.277 + g_list_free_1(hdr_node); 1.278 + destroy_header(hdr); 1.279 + break; 1.280 + case HEAD_RETURN_PATH: 1.281 + if(flags & ACC_MAIL_FROM_HEAD){ 1.282 + /* usually POP3 accept */ 1.283 + msg->return_path = create_address_qualified(hdr->value, TRUE, msg->received_host); 1.284 + DEBUG(3) debugf("setting return_path to %s\n", 1.285 + addr_string(msg->return_path)); 1.286 + } 1.287 + DEBUG(3) debugf("removing 'Return-Path' header\n"); 1.288 + msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node); 1.289 + g_list_free_1(hdr_node); 1.290 + destroy_header(hdr); 1.291 + break; 1.292 + default: 1.293 + break; /* make compiler happy */ 1.294 + } 1.295 + } 1.296 + 1.297 + if(msg->return_path == NULL){ 1.298 + /* this can happen for pop3 accept only 1.299 + and if no Return-path: header was given */ 1.300 + GList *hdr_list; 1.301 + header *hdr; 1.302 + 1.303 + DEBUG(3) debugf("return_path == NULL\n"); 1.304 + 1.305 + hdr_list = find_header(msg->hdr_list, HEAD_SENDER, NULL); 1.306 + if(!hdr_list) hdr_list = find_header(msg->hdr_list, HEAD_FROM, NULL); 1.307 + if(hdr_list){ 1.308 + gchar *addr; 1.309 + hdr = (header *)(g_list_first(hdr_list)->data); 1.310 + 1.311 + DEBUG(5) debugf("hdr->value = '%s'\n", hdr->value); 1.312 + 1.313 + addr = g_strdup(hdr->value); 1.314 + g_strchomp(addr); 1.315 + 1.316 + if((msg->return_path = 1.317 + create_address_qualified(addr, FALSE, msg->received_host)) 1.318 + != NULL){ 1.319 + DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path)); 1.320 + msg->hdr_list = 1.321 + g_list_append(msg->hdr_list, 1.322 + create_header(HEAD_UNKNOWN, 1.323 + "X-Warning: return path set from %s address\n", 1.324 + hdr->id == HEAD_SENDER ? "Sender:" : "From:")); 1.325 + } 1.326 + g_free(addr); 1.327 + } 1.328 + if(msg->return_path == NULL){ /* no Sender: or From: or create_address_qualified failed */ 1.329 + msg->return_path = create_address_qualified("postmaster", TRUE, conf.host_name); 1.330 + DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path)); 1.331 + msg->hdr_list = 1.332 + g_list_append(msg->hdr_list, 1.333 + create_header(HEAD_UNKNOWN, 1.334 + "X-Warning: real return path is unkown\n")); 1.335 + } 1.336 + } 1.337 + 1.338 + if(flags & ACC_DEL_RCPTS){ 1.339 + GList *rcpt_node; 1.340 + foreach(non_rcpt_list, rcpt_node){ 1.341 + address *rcpt = (address *)(rcpt_node->data); 1.342 + GList *node; 1.343 + if((node = g_list_find_custom(msg->rcpt_list, rcpt, 1.344 + _g_list_addr_isequal))){ 1.345 + DEBUG(3) debugf("removing rcpt address %s\n", addr_string(node->data)); 1.346 + 1.347 + msg->rcpt_list = g_list_remove_link(msg->rcpt_list, node); 1.348 + destroy_address((address *)(node->data)); 1.349 + g_list_free_1(node); 1.350 + } 1.351 + } 1.352 + } 1.353 + 1.354 + /* here we should have our recipients, fail if not: */ 1.355 + if(msg->rcpt_list == NULL){ 1.356 + logwrite(LOG_WARNING, "no recipients found in message\n"); 1.357 + return AERR_NORCPT; 1.358 + } 1.359 + 1.360 + if(!(has_sender || has_from)){ 1.361 + DEBUG(3) debugf("adding 'From' header\n"); 1.362 + msg->hdr_list = 1.363 + g_list_append(msg->hdr_list, 1.364 + msg->full_sender_name ? 1.365 + create_header(HEAD_FROM, "From: \"%s\" <%s@%s>\n", 1.366 + msg->full_sender_name, 1.367 + msg->return_path->local_part, 1.368 + msg->return_path->domain) : 1.369 + create_header(HEAD_FROM, "From: <%s@%s>\n", 1.370 + msg->return_path->local_part, 1.371 + msg->return_path->domain) 1.372 + ); 1.373 + } 1.374 + if((flags & ACC_HEAD_FROM_RCPT) && !has_rcpt){ 1.375 + GList *node; 1.376 + DEBUG(3) debugf("adding 'To' header(s)\n"); 1.377 + for(node = g_list_first(msg->rcpt_list); 1.378 + node; 1.379 + node = g_list_next(node)){ 1.380 + msg->hdr_list = 1.381 + g_list_append(msg->hdr_list, 1.382 + create_header(HEAD_TO, "To: %s\n", addr_string(msg->return_path))); 1.383 + } 1.384 + } 1.385 + if((flags & ACC_DEL_BCC) && !has_to_or_cc){ 1.386 + /* Bcc headers have been removed, and there are no remaining rcpt headers */ 1.387 + DEBUG(3) debugf("adding empty 'Bcc:' header\n"); 1.388 + msg->hdr_list = 1.389 + g_list_append(msg->hdr_list, create_header(HEAD_BCC, "Bcc:\n")); 1.390 + } 1.391 + if(!has_date){ 1.392 + DEBUG(3) debugf("adding 'Date:' header\n"); 1.393 + msg->hdr_list = 1.394 + g_list_append(msg->hdr_list, 1.395 + create_header(HEAD_DATE, "Date: %s\n", rec_timestamp())); 1.396 + } 1.397 + if(!has_id){ 1.398 + DEBUG(3) debugf("adding 'Message-ID:' header\n"); 1.399 + msg->hdr_list = 1.400 + g_list_append(msg->hdr_list, 1.401 + create_header(HEAD_MESSAGE_ID, 1.402 + "Message-ID: <%s@%s>\n", 1.403 + msg->uid, conf.host_name)); 1.404 + } 1.405 + } 1.406 + 1.407 + /* Received header: */ 1.408 + /* At this point because we have to know the rcpts for the 'for' part */ 1.409 + if(!(flags & ACC_NO_RECVD_HDR)){ 1.410 + gchar *for_string = NULL; 1.411 + header *hdr = NULL; 1.412 + 1.413 + DEBUG(3) debugf("adding 'Received:' header\n"); 1.414 + 1.415 + if(g_list_length(msg->rcpt_list) == 1){ 1.416 + address *addr = (address *)(g_list_first(msg->rcpt_list)->data); 1.417 + for_string = g_strdup_printf(" for %s", addr_string(addr)); 1.418 + } 1.419 + 1.420 + if(msg->received_host == NULL){ 1.421 + hdr = create_header(HEAD_RECEIVED, 1.422 + "Received: from %s by %s" 1.423 + " with %s (%s %s) id %s%s;" 1.424 + " %s\n", 1.425 + passwd->pw_name, conf.host_name, 1.426 + prot_names[msg->received_prot], 1.427 + PACKAGE, VERSION, 1.428 + msg->uid, for_string ? for_string : "", 1.429 + rec_timestamp()); 1.430 + }else{ 1.431 +#ifdef ENABLE_IDENT 1.432 + DEBUG(5) debugf("adding 'Received:' header (5)\n"); 1.433 + hdr = create_header(HEAD_RECEIVED, 1.434 + "Received: from %s (ident=%s) by %s" 1.435 + " with %s (%s %s) id %s%s;" 1.436 + " %s\n", 1.437 + msg->received_host, 1.438 + msg->ident ? msg->ident : "unknown", 1.439 + conf.host_name, 1.440 + prot_names[msg->received_prot], 1.441 + PACKAGE, VERSION, 1.442 + msg->uid, for_string ? for_string : "", 1.443 + rec_timestamp()); 1.444 +#else 1.445 + hdr = create_header(HEAD_RECEIVED, 1.446 + "Received: from %s by %s" 1.447 + " with %s (%s %s) id %s%s;" 1.448 + " %s\n", 1.449 + msg->received_host, 1.450 + conf.host_name, 1.451 + prot_names[msg->received_prot], 1.452 + PACKAGE, VERSION, 1.453 + msg->uid, for_string ? for_string : "", 1.454 + rec_timestamp()); 1.455 +#endif 1.456 + } 1.457 + header_fold(hdr); 1.458 + msg->hdr_list = g_list_prepend(msg->hdr_list, hdr); 1.459 + 1.460 + if(for_string) g_free(for_string); 1.461 + } 1.462 + 1.463 + /* write message to spool: */ 1.464 + /* accept is no longer responsible for this 1.465 + if(!spool_write(msg, TRUE)) 1.466 + return AERR_NOSPOOL; 1.467 + */ 1.468 + return AERR_OK; 1.469 +} 1.470 + 1.471 +accept_error accept_message(FILE *in, message *msg, guint flags) 1.472 +{ 1.473 + accept_error err; 1.474 + 1.475 + err = accept_message_stream(in, msg, flags); 1.476 + if(err == AERR_OK) 1.477 + err = accept_message_prepare(msg, flags); 1.478 + 1.479 + return err; 1.480 +} 1.481 +