meillo@0: /* MasqMail meillo@0: Copyright (C) 1999-2001 Oliver Kurth meillo@174: 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: "pop3", meillo@10: "apop", 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@10: if ((ret = strcasecmp(addr1->domain, addr2->domain)) == 0) meillo@10: return strcmp(addr1->local_part, addr2->local_part); meillo@10: else meillo@10: 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@109: The -t option: meillo@109: The ACC_RCPT_FROM_HEAD flag adds the recipients found in To/Cc/Bcc meillo@109: headers to the recipients list. meillo@109: The ACC_DEL_RCPTS flag makes only sense if the ACC_RCPT_FROM_HEAD meillo@109: flag is given too. With ACC_DEL_RCPTS the recipients given on the meillo@109: command line are removed from the ones given in headers. 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@10: if (len <= 0) { meillo@111: if ((len == -1) && (flags & (ACC_DOT_IGNORE | ACC_NODOT_RELAX))) { meillo@10: /* we got an EOF, and the last line was not terminated by a CR */ meillo@10: gint len1 = strlen(line1); meillo@10: if (len1 > 0) { /* == 0 is 'normal' (EOF after a CR) */ meillo@10: if (line1[len1 - 1] != '\n') { /* some mail clients allow unterminated lines */ meillo@10: line1[len1] = '\n'; meillo@14: line1[len1 + 1] = '\0'; meillo@10: msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1)); meillo@10: data_size += strlen(line1); meillo@10: line_cnt++; meillo@10: } meillo@10: } meillo@10: break; meillo@10: } else { meillo@10: g_free(line); meillo@10: if (len == -1) { meillo@10: return AERR_EOF; meillo@10: } else if (len == -2) { meillo@10: /* should not happen any more */ meillo@10: return AERR_OVERFLOW; meillo@10: } else if (len == -3) { meillo@10: return AERR_TIMEOUT; meillo@10: } else { meillo@10: /* does not happen */ meillo@10: DEBUG(5) debugf("read_sockline returned %d\n", len); meillo@10: return AERR_UNKNOWN; meillo@10: } meillo@10: } meillo@10: } else { meillo@10: if (in_headers) { meillo@10: meillo@10: /* some pop servers send the 'From ' line, skip it: */ meillo@10: if (msg->hdr_list == NULL) meillo@10: if (strncmp(line1, "From ", 5) == 0) meillo@10: continue; meillo@10: meillo@10: if (line1[0] == ' ' || line1[0] == '\t') { meillo@10: /* continuation of 'folded' header: */ meillo@10: if (hdr) { meillo@179: char* cp; meillo@179: cp = g_strconcat(hdr->header, line1, NULL); meillo@179: hdr->value = cp + (hdr->value - hdr->header); meillo@179: free(hdr->header); meillo@179: hdr->header = cp; meillo@10: } meillo@10: meillo@10: } else if (line1[0] == '\n') { meillo@10: /* an empty line marks end of headers */ meillo@10: in_headers = FALSE; meillo@10: } else { meillo@10: /* in all other cases we expect another header */ meillo@10: if ((hdr = get_header(line1))) meillo@10: msg->hdr_list = g_list_append(msg->hdr_list, hdr); meillo@10: else { meillo@10: /* if get_header() returns NULL, no header was recognized, meillo@10: so this seems to be the first data line of a broken mailer meillo@10: which does not send an empty line after the headers */ meillo@10: in_headers = FALSE; meillo@10: msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1)); meillo@10: } meillo@10: } meillo@10: } else { meillo@10: msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1)); meillo@10: data_size += strlen(line1); meillo@10: line_cnt++; meillo@10: } 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@10: if (msg->data_list != NULL) meillo@10: msg->data_list = g_list_reverse(msg->data_list); meillo@10: else meillo@10: /* make sure data list is not NULL: */ meillo@10: msg->data_list = g_list_append(NULL, g_strdup("")); meillo@0: meillo@10: DEBUG(4) debugf("received %d lines of data (%d bytes)\n", line_cnt, data_size); 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: GList *non_rcpt_list = 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@0: meillo@10: string_base62(msg->uid, rec_time, 6); meillo@10: msg->uid[6] = '-'; meillo@10: string_base62(&(msg->uid[7]), getpid(), 3); meillo@10: msg->uid[10] = '-'; meillo@10: string_base62(&(msg->uid[11]), msg->transfer_id, 2); meillo@10: msg->uid[13] = 0; meillo@0: meillo@10: /* if local, get password entry */ meillo@10: if (msg->received_host == NULL) { meillo@10: passwd = g_memdup(getpwuid(geteuid()), sizeof(struct passwd)); meillo@10: msg->ident = g_strdup(passwd->pw_name); meillo@10: } meillo@0: meillo@10: /* set return path if local */ meillo@22: if (msg->return_path == NULL && msg->received_host == NULL) { meillo@22: gchar *path = g_strdup_printf("<%s@%s>", passwd->pw_name, conf.host_name); meillo@22: DEBUG(3) debugf("setting return_path for local accept: %s\n", path); meillo@22: msg->return_path = create_address(path, TRUE); meillo@22: g_free(path); meillo@10: } meillo@0: meillo@109: /* -t option (see comment above) */ meillo@10: if (flags & ACC_DEL_RCPTS) { meillo@10: non_rcpt_list = msg->rcpt_list; meillo@10: msg->rcpt_list = NULL; 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@10: hdr_node != NULL; 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@22: "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@10: if (msg->return_path == NULL) { meillo@10: /* this can happen for pop3 accept only and if no 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@10: if (!hdr_list) meillo@10: hdr_list = find_header(msg->hdr_list, HEAD_FROM, NULL); 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@10: if ((msg->return_path = create_address_qualified(addr, FALSE, msg->received_host)) != NULL) { meillo@10: DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path)); meillo@13: msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_UNKNOWN, meillo@13: "X-Warning: return path set from %s address\n", meillo@13: hdr->id == HEAD_SENDER ? "Sender:" : "From:")); meillo@10: } meillo@10: g_free(addr); meillo@10: } meillo@10: if (msg->return_path == NULL) { /* no Sender: or From: or 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@13: msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_UNKNOWN, meillo@25: "X-Warning: real return path is unknown\n")); meillo@10: } meillo@10: } meillo@10: meillo@10: if (flags & ACC_DEL_RCPTS) { meillo@109: /* remove the recipients given on the command line from the ones given in headers meillo@109: -t option (see comment above) */ meillo@10: GList *rcpt_node; meillo@10: foreach(non_rcpt_list, rcpt_node) { meillo@10: address *rcpt = (address *) (rcpt_node->data); meillo@10: GList *node; meillo@10: if ((node = g_list_find_custom(msg->rcpt_list, rcpt, _g_list_addr_isequal))) { meillo@10: DEBUG(3) debugf("removing rcpt address %s\n", addr_string(node->data)); meillo@10: msg->rcpt_list = g_list_remove_link(msg->rcpt_list, node); meillo@10: destroy_address((address *) (node->data)); meillo@10: g_list_free_1(node); meillo@10: } meillo@10: } meillo@10: } meillo@10: meillo@10: /* here we should have our recipients, fail if not: */ meillo@10: if (msg->rcpt_list == NULL) { meillo@10: logwrite(LOG_WARNING, "no recipients found in message\n"); meillo@10: return AERR_NORCPT; meillo@10: } meillo@10: meillo@10: if (!(has_sender || has_from)) { meillo@10: DEBUG(3) debugf("adding 'From' header\n"); meillo@10: msg->hdr_list = g_list_append(msg->hdr_list, meillo@10: msg->full_sender_name meillo@10: ? meillo@13: create_header(HEAD_FROM, "From: \"%s\" <%s@%s>\n", msg->full_sender_name, meillo@13: msg->return_path->local_part, msg->return_path->domain) meillo@10: : meillo@10: create_header(HEAD_FROM, "From: <%s@%s>\n", meillo@13: msg->return_path->local_part, msg->return_path->domain) meillo@10: ); 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@13: msg->hdr_list = g_list_append(msg->hdr_list, meillo@13: 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@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@102: for_string = g_strdup_printf(" for %s", addr_string(addr)); meillo@102: } meillo@10: meillo@102: if (msg->received_host == NULL) { meillo@102: /* received locally */ meillo@102: hdr = create_header(HEAD_RECEIVED, "Received: from %s by %s with %s (%s %s) id %s%s; %s\n", meillo@102: passwd->pw_name, conf.host_name, prot_names[msg->received_prot], meillo@102: PACKAGE, VERSION, 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@102: hdr = create_header(HEAD_RECEIVED, "Received: from %s (ident=%s) by %s with %s (%s %s) id %s%s; %s\n", meillo@102: msg->received_host, msg->ident ? msg->ident : "unknown", conf.host_name, meillo@102: prot_names[msg->received_prot], PACKAGE, VERSION, msg->uid, for_string ? for_string : "", meillo@102: rec_timestamp()); meillo@10: #else meillo@102: hdr = create_header(HEAD_RECEIVED, "Received: from %s by %s with %s (%s %s) id %s%s; %s\n", meillo@102: msg->received_host, conf.host_name, prot_names[msg->received_prot], meillo@102: PACKAGE, VERSION, 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: /* write message to spool: */ meillo@10: /* accept is no longer responsible for this meillo@13: if (!spool_write(msg, TRUE)) meillo@13: return AERR_NOSPOOL; meillo@10: */ 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@10: if (err == AERR_OK) meillo@10: err = accept_message_prepare(msg, flags); meillo@0: meillo@10: return err; meillo@0: }