meillo@0: /* MasqMail meillo@0: Copyright (C) 1999-2001 Oliver Kurth meillo@0: meillo@0: This program is free software; you can redistribute it and/or modify meillo@0: it under the terms of the GNU General Public License as published by meillo@0: the Free Software Foundation; either version 2 of the License, or meillo@0: (at your option) any later version. meillo@0: meillo@0: This program is distributed in the hope that it will be useful, meillo@0: but WITHOUT ANY WARRANTY; without even the implied warranty of meillo@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the meillo@0: GNU General Public License for more details. meillo@0: meillo@0: You should have received a copy of the GNU General Public License meillo@0: along with this program; if not, write to the Free Software meillo@0: Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. meillo@0: */ meillo@0: meillo@0: #include "masqmail.h" meillo@0: #include "readsock.h" meillo@0: meillo@0: gchar *prot_names[] = meillo@0: { meillo@0: "local", meillo@0: "bsmtp", meillo@0: "smtp", meillo@0: "esmtp", meillo@0: "pop3", meillo@0: "apop", meillo@0: "(unknown)" /* should not happen, but better than crashing. */ meillo@0: }; meillo@0: meillo@0: static meillo@0: gchar *string_base62(gchar *res, guint value, gchar len) meillo@0: { meillo@0: static gchar base62_chars[] = meillo@0: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; meillo@0: gchar *p = res + len; meillo@0: *p = 0; meillo@0: while (p > res){ meillo@0: *(--p) = base62_chars[value % 62]; meillo@0: value /= 62; meillo@0: } meillo@0: return res; meillo@0: } meillo@0: meillo@0: static gint _g_list_addr_isequal(gconstpointer a, gconstpointer b) meillo@0: { meillo@0: address *addr1 = (address *)a; meillo@0: address *addr2 = (address *)b; meillo@0: int ret; meillo@0: meillo@0: if((ret = strcasecmp(addr1->domain, addr2->domain)) == 0) meillo@0: return strcmp(addr1->local_part, addr2->local_part); meillo@0: else meillo@0: return ret; meillo@0: } meillo@0: meillo@0: /* accept message from anywhere. meillo@0: A locally originating message is indicated by msg->recieved_host == NULL meillo@0: meillo@0: If the flags ACC_DEL_RCPTS is set, recipients in the msg->rcpt_list is meillo@0: copied and items occuring in it will be removed from the newly constructed meillo@0: (from To/Cc/Bcc headers if ACC_RCPT_TO is set) rcpt_list. meillo@0: */ meillo@0: meillo@0: accept_error accept_message_stream(FILE *in, message *msg, guint flags) meillo@0: { meillo@0: gchar *line, *line1; meillo@0: int line_size = MAX_DATALINE; meillo@0: gboolean in_headers = TRUE; meillo@0: header *hdr = NULL; meillo@0: gint line_cnt = 0, data_size = 0; meillo@0: meillo@0: line = g_malloc(line_size); meillo@0: line[0] = 0; meillo@0: meillo@0: while(TRUE){ meillo@0: int len = read_sockline1(in, &line, &line_size, 5*60, READSOCKL_CVT_CRLF); meillo@0: meillo@0: line1 = line; meillo@0: meillo@0: if((line[0] == '.') && (!(flags & ACC_NODOT_TERM))){ meillo@0: if(line[1] == '\n'){ meillo@0: g_free(line); meillo@0: break; meillo@0: } meillo@0: line1++; meillo@0: } meillo@0: meillo@0: if(len <= 0){ meillo@0: if((len == -1) && ((flags & ACC_NODOT_TERM) || (flags & ACC_NODOT_RELAX))){ meillo@0: /* we got an EOF, and the last line was not terminated by a CR */ meillo@0: gint len1 = strlen(line1); meillo@0: if(len1 > 0){ /* == 0 is 'normal' (EOF after a CR) */ meillo@0: if(line1[len1-1] != '\n'){ /* some mail clients allow unterminated lines */ meillo@0: line1[len1] = '\n'; meillo@0: line1[len1+1] = 0; meillo@0: msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1)); meillo@0: data_size += strlen(line1); meillo@0: line_cnt++; meillo@0: } meillo@0: } meillo@0: break; meillo@0: }else{ meillo@0: g_free(line); meillo@0: if(len == -1){ meillo@0: return AERR_EOF; meillo@0: }else if(len == -2){ meillo@0: /* should not happen any more */ meillo@0: return AERR_OVERFLOW; meillo@0: }else if(len == -3){ meillo@0: return AERR_TIMEOUT; meillo@0: }else{ meillo@0: /* does not happen */ meillo@0: DEBUG(5) debugf("read_sockline returned %d\n", len); meillo@0: return AERR_UNKNOWN; meillo@0: } meillo@0: } meillo@0: } meillo@0: else{ meillo@0: if(in_headers){ meillo@0: meillo@0: /* some pop servers send the 'From ' line, skip it: */ meillo@0: if(msg->hdr_list == NULL) meillo@0: if(strncmp(line1, "From ", 5) == 0) meillo@0: continue; meillo@0: meillo@0: if(line1[0] == ' ' || line1[0] == '\t'){ meillo@0: /* continuation of 'folded' header: */ meillo@0: if(hdr){ meillo@0: hdr->header = g_strconcat(hdr->header, line1, NULL); meillo@0: } meillo@0: meillo@0: }else if(line1[0] == '\n'){ meillo@0: /* an empty line marks end of headers */ meillo@0: in_headers = FALSE; meillo@0: }else{ meillo@0: /* in all other cases we expect another header */ meillo@0: if((hdr = get_header(line1))) meillo@0: msg->hdr_list = g_list_append(msg->hdr_list, hdr); meillo@0: else{ meillo@0: /* if get_header() returns NULL, no header was recognized, meillo@0: so this seems to be the first data line of a broken mailer meillo@0: which does not send an empty line after the headers */ meillo@0: in_headers = FALSE; meillo@0: msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1)); meillo@0: } meillo@0: } meillo@0: }else{ meillo@0: msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1)); meillo@0: data_size += strlen(line1); meillo@0: line_cnt++; meillo@0: } meillo@0: } meillo@0: } meillo@0: meillo@0: if(msg->data_list != NULL) meillo@0: msg->data_list = g_list_reverse(msg->data_list); meillo@0: else meillo@0: /* make sure data list is not NULL: */ meillo@0: msg->data_list = g_list_append(NULL, g_strdup("")); meillo@0: meillo@0: DEBUG(4) debugf("received %d lines of data (%d bytes)\n", meillo@0: line_cnt, data_size); meillo@0: /* we get here after we succesfully meillo@0: received the mail data */ meillo@0: meillo@0: msg->data_size = data_size; meillo@0: msg->received_time = time(NULL); meillo@0: meillo@0: return AERR_OK; meillo@0: } meillo@0: meillo@0: accept_error accept_message_prepare(message *msg, guint flags) meillo@0: { meillo@0: struct passwd *passwd = NULL; meillo@0: GList *non_rcpt_list = NULL; meillo@0: time_t rec_time = time(NULL); meillo@0: meillo@0: DEBUG(5) debugf("accept_message_prepare()\n"); meillo@0: meillo@0: /* create unique message id */ meillo@0: msg->uid = g_malloc(14); meillo@0: meillo@0: string_base62(msg->uid, rec_time, 6); meillo@0: msg->uid[6] = '-'; meillo@0: string_base62(&(msg->uid[7]), getpid(), 3); meillo@0: msg->uid[10] = '-'; meillo@0: string_base62(&(msg->uid[11]), msg->transfer_id, 2); meillo@0: msg->uid[13] = 0; meillo@0: meillo@0: /* if local, get password entry */ meillo@0: if(msg->received_host == NULL){ meillo@0: passwd = g_memdup(getpwuid(geteuid()), sizeof(struct passwd)); meillo@0: msg->ident = g_strdup(passwd->pw_name); meillo@0: } meillo@0: meillo@0: /* set return path if local */ meillo@0: if(msg->return_path == NULL){ meillo@0: meillo@0: if(msg->received_host == NULL){ meillo@0: gchar *path = g_strdup_printf("<%s@%s>", meillo@0: passwd->pw_name, conf.host_name); meillo@0: DEBUG(3) debugf("setting return_path for local accept: %s\n", path); meillo@0: msg->return_path = create_address(path, TRUE); meillo@0: g_free(path); meillo@0: } meillo@0: } meillo@0: meillo@0: /* -t option */ meillo@0: if(flags & ACC_DEL_RCPTS){ meillo@0: non_rcpt_list = msg->rcpt_list; meillo@0: msg->rcpt_list = NULL; meillo@0: } meillo@0: meillo@0: /* scan headers */ meillo@0: { meillo@0: gboolean has_id = FALSE; meillo@0: gboolean has_date = FALSE; meillo@0: gboolean has_sender = FALSE; meillo@0: gboolean has_from = FALSE; meillo@0: gboolean has_rcpt = FALSE; meillo@0: gboolean has_to_or_cc = FALSE; meillo@0: GList *hdr_node, *hdr_node_next; meillo@0: header *hdr; meillo@0: meillo@0: for(hdr_node = g_list_first(msg->hdr_list); meillo@0: hdr_node != NULL; meillo@0: hdr_node = hdr_node_next){ meillo@0: hdr_node_next = g_list_next(hdr_node); meillo@0: hdr = ((header *)(hdr_node->data)); meillo@0: DEBUG(5) debugf("scanning headers: %s", hdr->header); meillo@0: switch(hdr->id){ meillo@0: case HEAD_MESSAGE_ID: meillo@0: has_id = TRUE; break; meillo@0: case HEAD_DATE: meillo@0: has_date = TRUE; break; meillo@0: case HEAD_FROM: meillo@0: has_from = TRUE; meillo@0: break; meillo@0: case HEAD_SENDER: meillo@0: has_sender = TRUE; meillo@0: break; meillo@0: case HEAD_TO: meillo@0: case HEAD_CC: meillo@0: case HEAD_BCC: meillo@0: has_rcpt = TRUE; meillo@0: if(flags & ACC_RCPT_FROM_HEAD){ meillo@0: DEBUG(5) debugf("hdr->value = %s\n", hdr->value); meillo@0: if(hdr->value){ meillo@0: msg->rcpt_list = meillo@0: addr_list_append_rfc822(msg->rcpt_list, hdr->value, conf.host_name); meillo@0: } meillo@0: } meillo@0: if((flags & ACC_DEL_BCC) && (hdr->id == HEAD_BCC)){ meillo@0: DEBUG(3) debugf("removing 'Bcc' header\n"); meillo@0: msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node); meillo@0: g_list_free_1(hdr_node); meillo@0: destroy_header(hdr); meillo@0: }else meillo@0: has_to_or_cc = TRUE; meillo@0: break; meillo@0: case HEAD_ENVELOPE_TO: meillo@0: if(flags & ACC_SAVE_ENVELOPE_TO){ meillo@0: DEBUG(3) debugf("creating 'X-Orig-Envelope-To' header\n"); meillo@0: msg->hdr_list = meillo@0: g_list_prepend(msg->hdr_list, meillo@0: create_header(HEAD_UNKNOWN, meillo@0: "X-Orig-Envelope-to: %s", hdr->value)); meillo@0: } meillo@0: DEBUG(3) debugf("removing 'Envelope-To' header\n"); meillo@0: msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node); meillo@0: g_list_free_1(hdr_node); meillo@0: destroy_header(hdr); meillo@0: break; meillo@0: case HEAD_RETURN_PATH: meillo@0: if(flags & ACC_MAIL_FROM_HEAD){ meillo@0: /* usually POP3 accept */ meillo@0: msg->return_path = create_address_qualified(hdr->value, TRUE, msg->received_host); meillo@0: DEBUG(3) debugf("setting return_path to %s\n", meillo@0: addr_string(msg->return_path)); meillo@0: } meillo@0: DEBUG(3) debugf("removing 'Return-Path' header\n"); meillo@0: msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node); meillo@0: g_list_free_1(hdr_node); meillo@0: destroy_header(hdr); meillo@0: break; meillo@0: default: meillo@0: break; /* make compiler happy */ meillo@0: } meillo@0: } meillo@0: meillo@0: if(msg->return_path == NULL){ meillo@0: /* this can happen for pop3 accept only meillo@0: and if no Return-path: header was given */ meillo@0: GList *hdr_list; meillo@0: header *hdr; meillo@0: meillo@0: DEBUG(3) debugf("return_path == NULL\n"); meillo@0: meillo@0: hdr_list = find_header(msg->hdr_list, HEAD_SENDER, NULL); meillo@0: if(!hdr_list) hdr_list = find_header(msg->hdr_list, HEAD_FROM, NULL); meillo@0: if(hdr_list){ meillo@0: gchar *addr; meillo@0: hdr = (header *)(g_list_first(hdr_list)->data); meillo@0: meillo@0: DEBUG(5) debugf("hdr->value = '%s'\n", hdr->value); meillo@0: meillo@0: addr = g_strdup(hdr->value); meillo@0: g_strchomp(addr); meillo@0: meillo@0: if((msg->return_path = meillo@0: create_address_qualified(addr, FALSE, msg->received_host)) meillo@0: != NULL){ meillo@0: DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path)); meillo@0: msg->hdr_list = meillo@0: g_list_append(msg->hdr_list, meillo@0: create_header(HEAD_UNKNOWN, meillo@0: "X-Warning: return path set from %s address\n", meillo@0: hdr->id == HEAD_SENDER ? "Sender:" : "From:")); meillo@0: } meillo@0: g_free(addr); meillo@0: } meillo@0: if(msg->return_path == NULL){ /* no Sender: or From: or create_address_qualified failed */ meillo@0: msg->return_path = create_address_qualified("postmaster", TRUE, conf.host_name); meillo@0: DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path)); meillo@0: msg->hdr_list = meillo@0: g_list_append(msg->hdr_list, meillo@0: create_header(HEAD_UNKNOWN, meillo@0: "X-Warning: real return path is unkown\n")); meillo@0: } meillo@0: } meillo@0: meillo@0: if(flags & ACC_DEL_RCPTS){ meillo@0: GList *rcpt_node; meillo@0: foreach(non_rcpt_list, rcpt_node){ meillo@0: address *rcpt = (address *)(rcpt_node->data); meillo@0: GList *node; meillo@0: if((node = g_list_find_custom(msg->rcpt_list, rcpt, meillo@0: _g_list_addr_isequal))){ meillo@0: DEBUG(3) debugf("removing rcpt address %s\n", addr_string(node->data)); meillo@0: meillo@0: msg->rcpt_list = g_list_remove_link(msg->rcpt_list, node); meillo@0: destroy_address((address *)(node->data)); meillo@0: g_list_free_1(node); meillo@0: } meillo@0: } meillo@0: } meillo@0: meillo@0: /* here we should have our recipients, fail if not: */ meillo@0: if(msg->rcpt_list == NULL){ meillo@0: logwrite(LOG_WARNING, "no recipients found in message\n"); meillo@0: return AERR_NORCPT; meillo@0: } meillo@0: meillo@0: if(!(has_sender || has_from)){ meillo@0: DEBUG(3) debugf("adding 'From' header\n"); meillo@0: msg->hdr_list = meillo@0: g_list_append(msg->hdr_list, meillo@0: msg->full_sender_name ? meillo@0: create_header(HEAD_FROM, "From: \"%s\" <%s@%s>\n", meillo@0: msg->full_sender_name, meillo@0: msg->return_path->local_part, meillo@0: msg->return_path->domain) : meillo@0: create_header(HEAD_FROM, "From: <%s@%s>\n", meillo@0: msg->return_path->local_part, meillo@0: msg->return_path->domain) meillo@0: ); meillo@0: } meillo@0: if((flags & ACC_HEAD_FROM_RCPT) && !has_rcpt){ meillo@0: GList *node; meillo@0: DEBUG(3) debugf("adding 'To' header(s)\n"); meillo@0: for(node = g_list_first(msg->rcpt_list); meillo@0: node; meillo@0: node = g_list_next(node)){ meillo@0: msg->hdr_list = meillo@0: g_list_append(msg->hdr_list, meillo@0: create_header(HEAD_TO, "To: %s\n", addr_string(msg->return_path))); meillo@0: } meillo@0: } meillo@0: if((flags & ACC_DEL_BCC) && !has_to_or_cc){ meillo@0: /* Bcc headers have been removed, and there are no remaining rcpt headers */ meillo@0: DEBUG(3) debugf("adding empty 'Bcc:' header\n"); meillo@0: msg->hdr_list = meillo@0: g_list_append(msg->hdr_list, create_header(HEAD_BCC, "Bcc:\n")); meillo@0: } meillo@0: if(!has_date){ meillo@0: DEBUG(3) debugf("adding 'Date:' header\n"); meillo@0: msg->hdr_list = meillo@0: g_list_append(msg->hdr_list, meillo@0: create_header(HEAD_DATE, "Date: %s\n", rec_timestamp())); meillo@0: } meillo@0: if(!has_id){ meillo@0: DEBUG(3) debugf("adding 'Message-ID:' header\n"); meillo@0: msg->hdr_list = meillo@0: g_list_append(msg->hdr_list, meillo@0: create_header(HEAD_MESSAGE_ID, meillo@0: "Message-ID: <%s@%s>\n", meillo@0: msg->uid, conf.host_name)); meillo@0: } meillo@0: } meillo@0: meillo@0: /* Received header: */ meillo@0: /* At this point because we have to know the rcpts for the 'for' part */ meillo@0: if(!(flags & ACC_NO_RECVD_HDR)){ meillo@0: gchar *for_string = NULL; meillo@0: header *hdr = NULL; meillo@0: meillo@0: DEBUG(3) debugf("adding 'Received:' header\n"); meillo@0: meillo@0: if(g_list_length(msg->rcpt_list) == 1){ meillo@0: address *addr = (address *)(g_list_first(msg->rcpt_list)->data); meillo@0: for_string = g_strdup_printf(" for %s", addr_string(addr)); meillo@0: } meillo@0: meillo@0: if(msg->received_host == NULL){ meillo@0: hdr = create_header(HEAD_RECEIVED, meillo@0: "Received: from %s by %s" meillo@0: " with %s (%s %s) id %s%s;" meillo@0: " %s\n", meillo@0: passwd->pw_name, conf.host_name, meillo@0: prot_names[msg->received_prot], meillo@0: PACKAGE, VERSION, meillo@0: msg->uid, for_string ? for_string : "", meillo@0: rec_timestamp()); meillo@0: }else{ meillo@0: #ifdef ENABLE_IDENT meillo@0: DEBUG(5) debugf("adding 'Received:' header (5)\n"); meillo@0: hdr = create_header(HEAD_RECEIVED, meillo@0: "Received: from %s (ident=%s) by %s" meillo@0: " with %s (%s %s) id %s%s;" meillo@0: " %s\n", meillo@0: msg->received_host, meillo@0: msg->ident ? msg->ident : "unknown", meillo@0: conf.host_name, meillo@0: prot_names[msg->received_prot], meillo@0: PACKAGE, VERSION, meillo@0: msg->uid, for_string ? for_string : "", meillo@0: rec_timestamp()); meillo@0: #else meillo@0: hdr = create_header(HEAD_RECEIVED, meillo@0: "Received: from %s by %s" meillo@0: " with %s (%s %s) id %s%s;" meillo@0: " %s\n", meillo@0: msg->received_host, meillo@0: conf.host_name, meillo@0: prot_names[msg->received_prot], meillo@0: PACKAGE, VERSION, meillo@0: msg->uid, for_string ? for_string : "", meillo@0: rec_timestamp()); meillo@0: #endif meillo@0: } meillo@0: header_fold(hdr); meillo@0: msg->hdr_list = g_list_prepend(msg->hdr_list, hdr); meillo@0: meillo@0: if(for_string) g_free(for_string); meillo@0: } meillo@0: meillo@0: /* write message to spool: */ meillo@0: /* accept is no longer responsible for this meillo@0: if(!spool_write(msg, TRUE)) meillo@0: return AERR_NOSPOOL; meillo@0: */ meillo@0: return AERR_OK; meillo@0: } meillo@0: meillo@0: accept_error accept_message(FILE *in, message *msg, guint flags) meillo@0: { meillo@0: accept_error err; meillo@0: meillo@0: err = accept_message_stream(in, msg, flags); meillo@0: if(err == AERR_OK) meillo@0: err = accept_message_prepare(msg, flags); meillo@0: meillo@0: return err; meillo@0: } meillo@0: