meillo@0: /* MasqMail meillo@0: Copyright (C) 1999-2001 Oliver Kurth meillo@224: Copyright (C) 2010 markus schnalke 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@10: gchar *prot_names[] = { meillo@10: "local", meillo@10: "bsmtp", meillo@10: "smtp", meillo@10: "esmtp", meillo@10: "(unknown)" /* should not happen, but better than crashing. */ meillo@0: }; meillo@0: meillo@10: static gchar* meillo@10: string_base62(gchar * res, guint value, gchar len) meillo@0: { meillo@10: static gchar base62_chars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; meillo@10: gchar *p = res + len; meillo@14: *p = '\0'; meillo@10: while (p > res) { meillo@10: *(--p) = base62_chars[value % 62]; meillo@10: value /= 62; meillo@10: } meillo@10: return res; meillo@0: } meillo@0: meillo@10: static gint meillo@10: _g_list_addr_isequal(gconstpointer a, gconstpointer b) meillo@0: { meillo@10: address *addr1 = (address *) a; meillo@10: address *addr2 = (address *) b; meillo@10: int ret; meillo@0: meillo@270: if ((ret = strcasecmp(addr1->domain, addr2->domain)) == 0) { meillo@10: return strcmp(addr1->local_part, addr2->local_part); meillo@270: } meillo@270: return ret; meillo@0: } meillo@0: meillo@0: /* accept message from anywhere. meillo@276: A message from local is indicated by msg->recieved_host == NULL meillo@0: meillo@276: The -t option: With the ACC_RCPT_FROM_HEAD flag the addrs found found meillo@276: in To/Cc/Bcc headers are added to the recipient list. meillo@0: */ meillo@0: meillo@10: accept_error meillo@10: accept_message_stream(FILE * in, message * msg, guint flags) meillo@0: { meillo@10: gchar *line, *line1; meillo@10: int line_size = MAX_DATALINE; meillo@10: gboolean in_headers = TRUE; meillo@10: header *hdr = NULL; meillo@10: gint line_cnt = 0, data_size = 0; meillo@0: meillo@10: line = g_malloc(line_size); meillo@14: line[0] = '\0'; meillo@0: meillo@10: while (TRUE) { meillo@10: int len = read_sockline1(in, &line, &line_size, 5 * 60, READSOCKL_CVT_CRLF); meillo@0: meillo@10: line1 = line; meillo@0: meillo@110: if ((line[0] == '.') && (!(flags & ACC_DOT_IGNORE))) { meillo@10: if (line[1] == '\n') { meillo@10: g_free(line); meillo@10: break; meillo@10: } meillo@10: line1++; meillo@10: } meillo@10: meillo@270: if ((len == -1) && (flags & (ACC_DOT_IGNORE | ACC_NODOT_RELAX))) { meillo@270: /* we got an EOF, and the last line was not terminated by a CR */ meillo@270: gint len1 = strlen(line1); meillo@270: if (len1 > 0) { /* == 0 is 'normal' (EOF after a CR) */ meillo@270: if (line1[len1 - 1] != '\n') { /* some mail clients allow unterminated lines */ meillo@270: line1[len1] = '\n'; meillo@270: line1[len1 + 1] = '\0'; meillo@270: msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1)); meillo@270: data_size += strlen(line1); meillo@270: line_cnt++; meillo@10: } meillo@270: } meillo@270: break; meillo@270: meillo@270: } else if (len == -1) { meillo@270: g_free(line); meillo@270: return AERR_EOF; meillo@270: meillo@270: } else if (len == -2) { meillo@270: /* should not happen any more */ meillo@270: g_free(line); meillo@270: return AERR_OVERFLOW; meillo@270: meillo@270: } else if (len == -3) { meillo@270: g_free(line); meillo@270: return AERR_TIMEOUT; meillo@270: meillo@270: } else if (len <= 0) { meillo@270: /* does not happen */ meillo@270: g_free(line); meillo@270: DEBUG(5) debugf("read_sockline returned %d\n", len); meillo@270: return AERR_UNKNOWN; meillo@270: meillo@270: } meillo@270: meillo@270: if (in_headers) { meillo@270: meillo@270: /* some pop servers send the 'From ' line, skip it: */ meillo@270: if (!msg->hdr_list && strncmp(line1, "From ", 5) == 0) { meillo@270: continue; meillo@270: } meillo@270: meillo@270: if (line1[0] == ' ' || line1[0] == '\t') { meillo@270: /* continuation of 'folded' header: */ meillo@270: if (hdr) { meillo@270: hdr->header = g_strconcat(hdr->header, line1, NULL); meillo@270: } meillo@270: meillo@270: } else if (line1[0] == '\n') { meillo@270: /* an empty line marks end of headers */ meillo@270: in_headers = FALSE; meillo@10: } else { meillo@270: /* in all other cases we expect another header */ meillo@270: if ((hdr = get_header(line1))) { meillo@270: msg->hdr_list = g_list_append(msg->hdr_list, hdr); meillo@10: } else { meillo@270: /* if get_header() returns NULL, no header was recognized, meillo@270: so this seems to be the first data line of a broken mailer meillo@270: which does not send an empty line after the headers */ meillo@270: in_headers = FALSE; meillo@270: msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1)); meillo@10: } meillo@10: } meillo@10: } else { meillo@270: /* message body */ meillo@270: msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1)); meillo@270: data_size += strlen(line1); meillo@270: line_cnt++; meillo@270: } meillo@10: meillo@120: if (conf.max_msg_size && (data_size > conf.max_msg_size)) { meillo@117: DEBUG(4) debugf("accept_message_stream(): " meillo@117: "received %d bytes (conf.max_msg_size=%d)\n", meillo@117: data_size, conf.max_msg_size); meillo@117: return AERR_SIZE; meillo@117: } meillo@117: meillo@0: } meillo@0: meillo@270: DEBUG(4) debugf("received %d lines of data (%d bytes)\n", line_cnt, data_size); meillo@270: meillo@270: if (!msg->data_list) { meillo@10: /* make sure data list is not NULL: */ meillo@10: msg->data_list = g_list_append(NULL, g_strdup("")); meillo@270: } meillo@270: msg->data_list = g_list_reverse(msg->data_list); meillo@0: meillo@10: /* we get here after we succesfully received the mail data */ meillo@0: meillo@10: msg->data_size = data_size; meillo@10: msg->received_time = time(NULL); meillo@0: meillo@10: return AERR_OK; meillo@0: } meillo@0: meillo@10: accept_error meillo@10: accept_message_prepare(message * msg, guint flags) meillo@0: { meillo@10: struct passwd *passwd = NULL; meillo@10: time_t rec_time = time(NULL); meillo@0: meillo@10: DEBUG(5) debugf("accept_message_prepare()\n"); meillo@0: meillo@10: /* create unique message id */ meillo@10: msg->uid = g_malloc(14); meillo@10: string_base62(msg->uid, rec_time, 6); meillo@10: msg->uid[6] = '-'; meillo@298: string_base62(msg->uid + 7, getpid(), 3); meillo@10: msg->uid[10] = '-'; meillo@298: string_base62(msg->uid + 11, msg->transfer_id, 2); meillo@298: msg->uid[13] = '\0'; meillo@0: meillo@298: /* if local, get password entry and set return path if missing */ meillo@298: if (!msg->received_host) { meillo@10: passwd = g_memdup(getpwuid(geteuid()), sizeof(struct passwd)); meillo@10: msg->ident = g_strdup(passwd->pw_name); meillo@298: if (!msg->return_path) { meillo@298: gchar *path = g_strdup_printf("<%s@%s>", passwd->pw_name, conf.host_name); meillo@298: DEBUG(3) debugf("setting return_path for local accept: %s\n", path); meillo@298: msg->return_path = create_address(path, TRUE); meillo@298: g_free(path); meillo@298: } meillo@10: } meillo@0: meillo@10: /* scan headers */ meillo@10: { meillo@10: gboolean has_id = FALSE; meillo@10: gboolean has_date = FALSE; meillo@10: gboolean has_sender = FALSE; meillo@10: gboolean has_from = FALSE; meillo@10: gboolean has_to_or_cc = FALSE; meillo@10: GList *hdr_node, *hdr_node_next; meillo@10: header *hdr; meillo@0: meillo@10: for (hdr_node = g_list_first(msg->hdr_list); meillo@298: hdr_node; meillo@298: hdr_node = hdr_node_next) { meillo@10: hdr_node_next = g_list_next(hdr_node); meillo@10: hdr = ((header *) (hdr_node->data)); meillo@10: DEBUG(5) debugf("scanning headers: %s", hdr->header); meillo@10: switch (hdr->id) { meillo@10: case HEAD_MESSAGE_ID: meillo@10: has_id = TRUE; meillo@10: break; meillo@10: case HEAD_DATE: meillo@10: has_date = TRUE; meillo@10: break; meillo@10: case HEAD_FROM: meillo@10: has_from = TRUE; meillo@10: break; meillo@10: case HEAD_SENDER: meillo@10: has_sender = TRUE; meillo@10: break; meillo@10: case HEAD_TO: meillo@10: case HEAD_CC: meillo@106: has_to_or_cc = TRUE; meillo@106: /* fall through */ meillo@10: case HEAD_BCC: meillo@10: if (flags & ACC_RCPT_FROM_HEAD) { meillo@109: /* -t option (see comment above) */ meillo@10: DEBUG(5) debugf("hdr->value = %s\n", hdr->value); meillo@10: if (hdr->value) { meillo@10: msg->rcpt_list = addr_list_append_rfc822(msg->rcpt_list, hdr->value, conf.host_name); meillo@10: } meillo@10: } meillo@106: if (hdr->id == HEAD_BCC) { meillo@10: DEBUG(3) debugf("removing 'Bcc' header\n"); meillo@10: msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node); meillo@10: g_list_free_1(hdr_node); meillo@10: destroy_header(hdr); meillo@106: } meillo@10: break; meillo@10: case HEAD_ENVELOPE_TO: meillo@10: if (flags & ACC_SAVE_ENVELOPE_TO) { meillo@10: DEBUG(3) debugf("creating 'X-Orig-Envelope-To' header\n"); meillo@22: msg->hdr_list = g_list_prepend(msg->hdr_list, create_header(HEAD_UNKNOWN, meillo@276: "X-Orig-Envelope-To: %s", hdr->value)); meillo@10: } meillo@10: DEBUG(3) debugf("removing 'Envelope-To' header\n"); meillo@10: msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node); meillo@10: g_list_free_1(hdr_node); meillo@10: destroy_header(hdr); meillo@10: break; meillo@10: case HEAD_RETURN_PATH: meillo@10: if (flags & ACC_MAIL_FROM_HEAD) { meillo@10: /* usually POP3 accept */ meillo@10: msg->return_path = create_address_qualified(hdr->value, TRUE, msg->received_host); meillo@10: DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path)); meillo@10: } meillo@10: DEBUG(3) debugf("removing 'Return-Path' header\n"); meillo@10: msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node); meillo@10: g_list_free_1(hdr_node); meillo@10: destroy_header(hdr); meillo@10: break; meillo@10: default: meillo@10: break; /* make compiler happy */ meillo@10: } meillo@10: } meillo@10: meillo@298: if (!msg->return_path) { meillo@298: /* TODO: do we still need this as we don't fetch meillo@298: mail anymore? */ meillo@298: /* this can happen for pop3 accept only and if no meillo@298: Return-Path: header was given */ meillo@10: GList *hdr_list; meillo@10: header *hdr; meillo@10: meillo@10: DEBUG(3) debugf("return_path == NULL\n"); meillo@10: meillo@10: hdr_list = find_header(msg->hdr_list, HEAD_SENDER, NULL); meillo@298: if (!hdr_list) { meillo@10: hdr_list = find_header(msg->hdr_list, HEAD_FROM, NULL); meillo@298: } meillo@10: if (hdr_list) { meillo@10: gchar *addr; meillo@10: hdr = (header *) (g_list_first(hdr_list)->data); meillo@10: meillo@10: DEBUG(5) debugf("hdr->value = '%s'\n", hdr->value); meillo@10: meillo@10: addr = g_strdup(hdr->value); meillo@10: g_strchomp(addr); meillo@10: meillo@298: msg->return_path = create_address_qualified(addr, FALSE, msg->received_host)); meillo@298: if (msg->return_path) { meillo@10: DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path)); meillo@298: 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: } meillo@10: g_free(addr); meillo@10: } meillo@298: if (!msg->return_path) { meillo@298: /* no Sender: or From: or meillo@298: create_address_qualified failed */ meillo@10: msg->return_path = create_address_qualified("postmaster", TRUE, conf.host_name); meillo@10: DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path)); meillo@298: msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_UNKNOWN, "X-Warning: real return path is unknown\n")); meillo@10: } meillo@10: } meillo@10: meillo@10: /* here we should have our recipients, fail if not: */ meillo@276: if (!msg->rcpt_list) { meillo@10: logwrite(LOG_WARNING, "no recipients found in message\n"); meillo@10: return AERR_NORCPT; meillo@10: } meillo@10: meillo@298: if (!has_sender && !has_from)) { meillo@298: DEBUG(3) debugf("adding 'From:' header\n"); meillo@298: if (msg->full_sender_name) { meillo@298: 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: } else { meillo@298: 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: } meillo@10: } meillo@106: if (!has_to_or_cc) { meillo@106: DEBUG(3) debugf("no To: or Cc: header, hence adding `To: undisclosed recipients:;'\n"); meillo@105: msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_TO, "To: undisclosed-recipients:;\n")); meillo@10: } meillo@10: if (!has_date) { meillo@10: DEBUG(3) debugf("adding 'Date:' header\n"); meillo@10: msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_DATE, "Date: %s\n", rec_timestamp())); meillo@10: } meillo@10: if (!has_id) { meillo@10: DEBUG(3) debugf("adding 'Message-ID:' header\n"); meillo@298: 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: } meillo@0: } meillo@10: meillo@10: /* Received header: */ meillo@10: /* At this point because we have to know the rcpts for the 'for' part */ meillo@298: /* The `for' part will only be used if exactly one rcpt is present. */ meillo@102: gchar *for_string = NULL; meillo@102: header *hdr = NULL; meillo@10: meillo@102: DEBUG(3) debugf("adding 'Received:' header\n"); meillo@10: meillo@102: if (g_list_length(msg->rcpt_list) == 1) { meillo@102: address *addr = (address *) (g_list_first(msg->rcpt_list)->data); meillo@298: for_string = g_strdup_printf("\n\tfor %s", addr_string(addr)); meillo@102: } meillo@10: meillo@289: if (!msg->received_host) { meillo@102: /* received locally */ meillo@289: hdr = create_header(HEAD_RECEIVED, meillo@289: "Received: from %s by %s with %s (%s %s) id %s%s; %s\n", meillo@289: passwd->pw_name, conf.host_name, meillo@289: prot_names[msg->received_prot], PACKAGE, VERSION, meillo@289: msg->uid, for_string ? for_string : "", rec_timestamp()); meillo@102: } else { meillo@102: /* received from remote */ meillo@10: #ifdef ENABLE_IDENT meillo@102: DEBUG(5) debugf("adding 'Received:' header (5)\n"); meillo@289: hdr = create_header(HEAD_RECEIVED, meillo@289: "Received: from %s (ident=%s) by %s with %s (%s %s) id %s%s; %s\n", meillo@289: msg->received_host, msg->ident ? msg->ident : "unknown", meillo@289: conf.host_name, prot_names[msg->received_prot], PACKAGE, meillo@289: VERSION, msg->uid, for_string ? for_string : "", meillo@289: rec_timestamp()); meillo@10: #else meillo@289: hdr = create_header(HEAD_RECEIVED, meillo@289: "Received: from %s by %s with %s (%s %s) id %s%s; %s\n", meillo@289: msg->received_host, conf.host_name, meillo@289: prot_names[msg->received_prot], PACKAGE, VERSION, meillo@289: msg->uid, for_string ? for_string : "", rec_timestamp()); meillo@10: #endif meillo@102: } meillo@102: header_fold(hdr); meillo@102: msg->hdr_list = g_list_prepend(msg->hdr_list, hdr); meillo@10: meillo@102: if (for_string) meillo@102: g_free(for_string); meillo@0: meillo@10: return AERR_OK; meillo@0: } meillo@0: meillo@10: accept_error meillo@10: accept_message(FILE * in, message * msg, guint flags) meillo@0: { meillo@10: accept_error err; meillo@0: meillo@10: err = accept_message_stream(in, msg, flags); meillo@270: if (err == AERR_OK) { meillo@10: err = accept_message_prepare(msg, flags); meillo@270: } meillo@0: meillo@10: return err; meillo@0: }