/* * cddbd - CD Database Protocol Server * * Copyright (C) 1996-1997 Steve Scherf (steve@moonsoft.com) * Portions Copyright (C) 1999-2006 by various authors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef LINT static char *const _mail_c_ident_ = "@(#)$Id: mail.c,v 1.30.2.2 2006/04/16 14:55:43 joerg78 Exp $"; #endif #include #include #include #include #include #include #include #include "cddbd.h" /* Preprocessor definitions. */ #define SCAN_ADDR 0 #define SCAN_NAME 1 #define SCAN_MISC 2 /* Prototypes. */ int cddbd_submit(FILE *, email_hdr_t *, int, char *); int add_hdr(char *dir, unsigned int discid, email_hdr_t *eh, char *errstr); int cddbd_email_cmd(FILE *, email_hdr_t *, int, char *); int get_charset(char *, email_hdr_t *, char *); int get_encoding(char *, email_hdr_t *, char *); int parse_header(FILE *, email_hdr_t *, char *); /*int return_mail(FILE *, email_hdr_t *, int, char *);*/ int scan_from(char *, char *, int); void cpy_sender(char *, char *, char *, char *); static FILE *get_body(FILE *, email_hdr_t *, char *, char *); static FILE *build_returnmail(FILE *fp, FILE *tfp, char *tmail, char *errstr); #ifdef DB_WINDOWS_FORMAT int mergeUnix2WinDB(char *, char *, int); #endif /* Static variables. */ static char *x_cddbd_note = "X-Cddbd-Note"; static char *note = "Note: %s\n\n"; static char *sub_rej_subj = "Rejected freedb submission"; static char *sub_send_subj = "cddb %255s %08x"; static char *test_rej_subj = "Rejected freedb test submission"; static char *test_ok_subj = "Successful freedb test submission"; static char *cmd_send_subj = "cddb #command %16s"; static char *cmd_ok_subj = "cddb #response ok %s"; static char *cmd_rej_subj = "cddb #response failed %s"; /* static char *gen_rej_subj = "Rejected freedb email"; */ static char *gen_incl = "> %s\n"; static char *gen_incl_blank = ">\n"; static char *null_incl = "%s\n"; static char *null_incl_blank = "\n"; static char *test_ok_body[] = { "Your freedb test submission was accepted.\n", "\n", "The test submission follows:\n", 0 }; static char *test_ok_sig[] = { "\n", "Response generated by the CDDB daemon.\n", "\n", 0 }; static char *sub_rej_body[] = { "Your freedb submission was rejected for the following reason:\n", "\n", "%s\n", "\n", "Please fix the problem before you resubmit it. Only the first\n", "error in your submission was noted - there may be others. If you\n", "continue to have trouble, it may be due to a bug in your CD\n" "player software. If you suspect this, try acquiring a newer version.", "\n\n", "The rejected submission follows:\n", 0 }; static char *gen_rej_body[] = { "Your mail to the CDDB daemon failed for the following reason:\n", "\n", "%s\n", "\n", "Please fix the problem before you resend it. If you continue to\n", "have trouble, it may be due to a bug in the software used to\n", "send the mail.", "\n\n", "The rejected mail follows:\n", 0 }; static char *gen_rej_sig[] = { "\n", "If you need assistance, please take a look at the FAQ\n", "at http://www.freedb.org/modules.php?name=Sections&sop=viewarticle&artid=26\n", "If the FAQ doesn't help, send an e-mail to: %s\n", "\n", "Response generated by the CDDB daemon.\n", "\n", 0 }; static char *start_type[] = { "<", "(\"", "" /* huh? shouldn't that be <(\" ?? [+gg] */ }; static char *end_type[] = { ">", ")\"", "<(\"" }; /* truly global variables. mainly used in inet.c */ /* Header names */ char *content_encoding = "Content-Transfer-Encoding"; char *content_type = "Content-Type"; char *expires = "Expires"; char *rpath = "Return-Path"; char *content_len = "Content-Length"; char *mime_ver = "Mime-Version"; char *from = "From"; char *x_cddbd_from = "X-Cddbd-From"; char *x_cddbd_echo = "X-Cddbd-Echo"; char *x_cddbd_crc = "X-Cddbd-CRC"; char *to = "To"; char *subj = "Subject"; char *x_sender = "X-Sender"; /* text constants used in headers */ char *text_plain = "text/plain"; char *multi_alt = "multipart/alternative"; char *boundary = "boundary"; char *charset = "charset"; charset_t charsets[] = { { "us-ascii", 0 }, { "iso-8859-1", DF_ENC_LATIN1 }, { "utf-8", DF_ENC_UTF8 }, { 0, 0 } }; void cddbd_mail(int flags) { int ret; FILE *fp; FILE *tfp= (FILE *) 0; FILE *tfp2= (FILE *) 0; email_hdr_t eh; char *tmail; char *tmail2; char *tmail3; char buf[CDDBBUFSIZ]; char errstr[CDDBBUFSIZ]; errstr[0] = '\0'; /* Create a temporary file for the mail. */ tmail = cddbd_mktemp(); if((fp = fopen(tmail, "w+")) == NULL) { cddbd_snprintf(buf, sizeof(buf), "Can't open mail tmp file %s (%d)", tmail, errno); cddbd_log(LOG_ERR | LOG_MAIL, "%s", buf); /* Print to stderr so the bounced mail gets it. */ fprintf(stderr, "%s\n", buf); quit(QUIT_ERR); } /* Put the mail into the temp file. */ while(fgets(buf, sizeof(buf), stdin) != NULL) { if(fputs(buf, fp) == EOF) { cddbd_snprintf(buf, sizeof(buf), "Can't write mail tmp file %s (%d)", tmail, errno); cddbd_log(LOG_ERR | LOG_MAIL, "%s", buf); /* Print to stderr so the bounced mail gets it. */ fprintf(stderr, "%s\n", buf); quit(QUIT_ERR); } } /* Put the pointer back to the start of the file for reading. */ rewind(fp); /* parse the header, fill in "eh" structure */ ret = parse_header(fp, &eh, errstr); tmail2 = cddbd_mktemp(); if(ret == EE_OK && (tfp = get_body(fp, &eh, tmail2, errstr)) == NULL) ret = EE_ERROR; /* Put the pointer back to the start of the file for reading. */ rewind(fp); tmail3 = cddbd_mktemp(); if(ret == EE_OK && (tfp2=build_returnmail(fp, tfp, tmail3, errstr)) == NULL) ret = EE_ERROR; if(ret == EE_OK) { switch(eh.eh_class) { case EC_SUBMIT: ret = cddbd_submit(tfp, &eh, flags, errstr); /* Give back happy mail. */ if(ret == EE_OK && (flags & MF_TEST)) { ret = return_mail(tfp2, &eh, flags, errstr); if(ret != EE_OK) { cddbd_log(LOG_ERR | LOG_MAIL, "Couldn't send test response."); } } break; case EC_COMMAND: ret = cddbd_email_cmd(tfp, &eh, flags, errstr); break; case EC_NONE: default: ret = EE_ERROR; break; } } if(ret != EE_OK) { if(eh.eh_flags & EH_RCPT) { /* We have a sender, give him mail directly. */ if (tfp2 != NULL) ret = return_mail(tfp2, &eh, (flags | MF_FAIL), errstr); else ret = return_mail(fp, &eh, (flags | MF_FAIL), errstr); } if(ret != EE_OK) { /* We have no sender, give him mail indirectly. */ cddbd_snprintf(buf, sizeof(buf), "Unable to process email: %s", errstr); cddbd_log(LOG_ERR | LOG_MAIL, "%s", buf); /* Print to stderr so the bounced mail gets it. */ fprintf(stderr, "%s\n", buf); } } fclose(fp); if (tfp != (FILE *) 0) fclose(tfp); if (tfp2 != (FILE *) 0) fclose(tfp2); cddbd_freetemp(tmail); cddbd_freetemp(tmail2); cddbd_freetemp(tmail3); if(ret == EE_OK) quit(QUIT_OK); else quit(QUIT_ERR); } int cddbd_submit(FILE *fp, email_hdr_t *eh, int flags, char *errstr) { int i; int len; int ret; int dbflags; db_t *db; char buf[CDDBBUFSIZ]; char buf2[CDDBBUFSIZ]; struct stat sbuf; /* Set the interface type. */ interface = IF_SUBMIT; if(!(flags & MF_TEST)) hperm = *ck_host_perms(interface); if((ret = validate_email(fp, eh, errstr)) != EE_OK) return ret; if(!WRITE_OK(hperm) && !(flags & MF_TEST)) { cddbd_snprintf(errstr, CDDBBUFSIZ, "Email submissions disallowed"); return EE_ERROR; } if(categ_index(eh->eh_category) < 0) { cddbd_snprintf(errstr, CDDBBUFSIZ, "Invalid DB category: %s\n" "Valid categories are:", eh->eh_category); for(i = 0, len = strlen(errstr); categlist[i] != 0; i++) { cddbd_snprintf(&errstr[len], (CDDBBUFSIZ - len), " %s", categlist[i]); len += strlen(categlist[i]) + 1; } return EE_ERROR; } /* Specify acceptable charsets. */ dbflags = charsets[eh->eh_charset].df_flags; if((dbflags & DF_ENC_LATIN1) && utf_as_iso == UAI_CONVERT) dbflags |= DF_ENC_UTF8; if(!(flags & MF_TEST)) dbflags |= DF_CK_SUBMIT; rewind(fp); db = db_read(fp, buf, (DF_MAIL | DF_SUBMITTER | dbflags)); if(db == 0) { cddbd_snprintf(errstr, CDDBBUFSIZ, "Invalid DB submission: %s", buf); return EE_ERROR; } /* Check and disambiguate charset. */ if (db_disam_charset(db)) { cddbd_snprintf(errstr, CDDBBUFSIZ, "Entry rejected: looks like UTF-8."); return EE_ERROR; } if(!(flags & MF_TEST)) { /* Check for postdir, and create if it doesn't exist. */ if(stat(postdir, &sbuf)) { if(mkdir(postdir, (mode_t)db_dir_mode)) { cddbd_log(LOG_ERR, "Failed to create post dir %s.", postdir); cddbd_snprintf(errstr, CDDBBUFSIZ, "Internal server file error"); return EE_ERROR; } (void)cddbd_fix_file(postdir, db_dir_mode, db_uid, db_gid); } else if(!S_ISDIR(sbuf.st_mode)) { cddbd_log(LOG_ERR, "%s is not a directory.", postdir); cddbd_snprintf(errstr, CDDBBUFSIZ, "Internal server file error"); return EE_ERROR; } cddbd_snprintf(buf2, sizeof(buf), "%s/%s", postdir, eh->eh_category); /* zeke - add eh info to db struct for writing to post entry */ db->db_eh.eh_flags = eh->eh_flags; db->db_eh.eh_charset = eh->eh_charset; db->db_eh.eh_encoding = eh->eh_encoding; strcpy (db->db_eh.eh_to, eh->eh_to); strcpy (db->db_eh.eh_rcpt, eh->eh_rcpt); strcpy (db->db_eh.eh_host, eh->eh_host); /* end zeke */ if(!db_post(db, buf2, eh->eh_discid, buf)) { if(db_errno != DE_INVALID) { cddbd_snprintf(errstr, CDDBBUFSIZ, "Internal DB server error: %s", buf); } else cddbd_snprintf(errstr, CDDBBUFSIZ, "Invalid DB file: %s", buf); return EE_ERROR; } cddbd_log(LOG_INFO | LOG_WRITE, "Write (via SMTP - %s): %s %08x", eh->eh_to, eh->eh_category, eh->eh_discid); } else { cddbd_log(LOG_INFO, "Test email submission (from %s): %s %08x", eh->eh_to, eh->eh_category, eh->eh_discid); } return EE_OK; } int cddbd_email_cmd(FILE *fp, email_hdr_t *eh, int flags, char *errstr) { int ret; int found; pid_t f; char *tcmd; char buf[CDDBCMDLEN]; struct stat sbuf; /* Set the interface type. */ interface = IF_EMAIL; hperm = *ck_host_perms(interface); if(flags & MF_TEST) { cddbd_snprintf(errstr, CDDBBUFSIZ, "Email commands not accepted at this address"); return EE_ERROR; } if((ret = validate_email(fp, eh, errstr)) != EE_OK) return ret; found = 0; rewind(fp); while(fgets(buf, sizeof(buf), fp)) if(!strncmp(buf, asy_prefix[0], strlen(asy_prefix[0]))) { found++; break; } if(!found) { cddbd_snprintf(errstr, CDDBBUFSIZ, "No CDDBP command found in mail body"); return EE_ERROR; } /* Create a temporary file for the mail. */ tcmd = cddbd_mktemp(); if((fp = fopen(tcmd, "w+")) == NULL) { cddbd_snprintf(errstr, CDDBBUFSIZ, "Internal server error: can't open temp file %s", tcmd); return EE_ERROR; } f = cddbd_fork(); /* We're the child. Do the command. */ if(f == 0) { dup2(fileno(fp), 1); dup2(fileno(fp), 2); fclose(fp); close(0); _quit(cddbd_async_command(buf, 0), 0); } if(f < 0) { cddbd_snprintf(errstr, CDDBBUFSIZ, "Internal server error: can't fork"); fclose(fp); cddbd_freetemp(tcmd); return EE_ERROR; } if(wait(0) == -1 && errno != ECHILD) { cddbd_snprintf(errstr, CDDBBUFSIZ, "Internal server error: failed to wait on child (%d)", errno); fclose(fp); cddbd_freetemp(tcmd); return EE_ERROR; } if(stat(tcmd, &sbuf)) { cddbd_snprintf(errstr, CDDBBUFSIZ, "Internal server error: can't stat output file %s (%d)", tcmd, errno); fclose(fp); cddbd_freetemp(tcmd); return EE_ERROR; } /* Put something in the file if it's empty. */ if(sbuf.st_size == 0) { fprintf(fp, "403 Server error.\n"); fflush(fp); } ret = return_mail(fp, eh, (flags | MF_ENC), errstr); if(ret != EE_OK) { cddbd_snprintf(errstr, CDDBBUFSIZ, "Internal server error: can't generate response mail"); } fclose(fp); cddbd_freetemp(tcmd); /* Reset the interface type so we don't log a second hello. */ interface = IF_SUBMIT; return ret; } int validate_email(FILE *fp, email_hdr_t *eh, char *errstr) { uint32_t crc; uint32_t mcrc; ct_key_t *key; if(eh->eh_flags & EH_CRC) { if(PASSWD_REQ(hperm)) { if((key = getpasswd(hperm.hp_pwdlbl)) == NULL) { cddbd_snprintf(errstr, CDDBBUFSIZ, "Validation string lookup failed"); return EE_ERROR; } } else key = 0; if(crc_email(&crc, fp, eh->eh_rcpt, eh->eh_subj, errstr) != EE_OK) return EE_ERROR; mcrc = strtocrc(eh->eh_crc, 0, key); #if 1 if(crc != mcrc) { #else if(key && crc != mcrc) { #endif #if 1 cddbd_snprintf(errstr, CDDBBUFSIZ, "CRC in email (%08X) differs from expected CRC " "(%08X)", mcrc, crc); return EE_ERROR; #else cddbd_log(LOG_ERR | LOG_PASSWD, "CRC in email (%08X) differs from expected CRC " "(%08X)", mcrc, crc); #endif } hperm.hp_passwd = HP_PASSWD_OK; return EE_OK; } else if(hperm.hp_passwd == HP_PASSWD_REQ) { cddbd_snprintf(errstr, CDDBBUFSIZ, "Required CRC missing from email header"); return EE_ERROR; } return EE_OK; } int crc_email(uint32_t *crc, FILE *fp, char *rcpt, char *subj, char *errstr) { uint32_t len; uint32_t tcrc[3]; rewind(fp); if(crc32(CRC_FILE, fp, &tcrc[0], &len) == -1 || crc32(CRC_STRING, rcpt, &tcrc[1], &len) == -1 || crc32(CRC_STRING, subj, &tcrc[2], &len) == -1) { cddbd_snprintf(errstr, CDDBBUFSIZ, "Can't calculate email CRC (%d)", errno); return EE_ERROR; } *crc = tcrc[0] ^ tcrc[1] ^ tcrc[2]; return EE_OK; } static FILE * get_body(FILE *fp, email_hdr_t *eh, char *tmail, char *errstr) { int len; int found; FILE *tfp; char *p; char buf[CDDBBUFSIZ]; int (*efunc)(); if((tfp = fopen(tmail, "w+")) == NULL) { cddbd_snprintf(errstr, CDDBBUFSIZ, "Can't open mail tmp file %s (%d)", tmail, errno); return NULL; } efunc = encoding_types[eh->eh_encoding].en_decode; if(efunc != 0 && (efunc)(EO_START, 0, 0, 0) != EN_OK) { cddbd_snprintf(errstr, CDDBBUFSIZ, "Failed to initiate email decoding."); return NULL; } found = 0; /* Put the mail into the temp file. */ while(fgets(buf, sizeof(buf), fp) != NULL) { if((eh->eh_flags & EH_BOUNDARY) && strstr(buf, eh->eh_boundary) != NULL) { found++; break; } len = strlen(buf); if(efunc != 0) { if((efunc)(EO_DECODE, buf, &p, &len) != EN_OK) { cddbd_snprintf(errstr, CDDBBUFSIZ, "Failed to perform email decoding."); return NULL; } } else p = buf; if(len != 0 && fwrite(p, 1, len, tfp) != len) { cddbd_snprintf(errstr, CDDBBUFSIZ, "Can't write mail tmp file %s (%d)", tmail, errno); return NULL; } } if(efunc != 0 && (efunc)(EO_END, 0, 0, 0) != EN_OK) { cddbd_snprintf(errstr, CDDBBUFSIZ, "Failed to terminate email decoding."); return NULL; } if((eh->eh_flags & EH_BOUNDARY) && !found) { cddbd_snprintf(errstr, CDDBBUFSIZ, "End boundary not found in mail."); return NULL; } fflush(tfp); rewind(tfp); return tfp; } /* Read a header field, unfolding multiple lines if necessary. * Arguments and return value are just like fgets(). */ static char *get_hdr_line(char *buf, int size, FILE *fp) { int c, i = 0; while((c = fgetc(fp)) != EOF) { if(c == '\n') { c = fgetc(fp); if(c == ' ' || c == '\t') { if(i > 0 && buf[i - 1] == '\r') --i; } else { if(c != EOF) ungetc(c, fp); if(i + 1 < size) buf[i++] = '\n'; break; } } if(i + 1 < size) buf[i++] = c; } if(size) buf[i] = '\0'; return i ? buf : 0; } /* builds the info from the submission to be returned in a rejection notice */ /* or test submission response by taking the original mail header and the */ /* decoded mail */ static FILE *build_returnmail(FILE *fp, FILE *tfp, char *tmail, char *errstr) { int len; int blank; char buf[CDDBBUFSIZ]; char *p; FILE *tfp2; if((tfp2 = fopen(tmail, "w+")) == NULL) { cddbd_snprintf(errstr, CDDBBUFSIZ, "Can't open mail tmp file %s (%d)", tmail, errno); return NULL; } while(fgets(buf, sizeof(buf), fp) != NULL) { blank = (buf[0] == '\n' || (buf[0] == '\r' && buf[1] == '\n')); len = strlen(buf); p = buf; if(len != 0 && fwrite(p, 1, len, tfp2) != len) { cddbd_snprintf(errstr, CDDBBUFSIZ, "Can't write mail tmp file %s (%d)", tmail, errno); return NULL; } if (blank) break; } while(fgets(buf, sizeof(buf), tfp) != NULL) { len = strlen(buf); p = buf; if(len != 0 && fwrite(p, 1, len, tfp2) != len) { cddbd_snprintf(errstr, CDDBBUFSIZ, "Can't write mail tmp file %s (%d)", tmail, errno); return NULL; } } fflush(tfp2); rewind(tfp2); rewind(tfp); return tfp2; } /* This function parses the header of an email that * can be read from an open file handle. * Note, T2D: There are a few short cuts that should be fixed. */ int parse_header(FILE *fp, email_hdr_t *eh, char *errstr) { int len; int blank; /* various flags to indicate what header parts were already seen */ int header= 0; int gotsubj= 0; int gotenc= 0; int gotset= 0; int waitbound= 0; char *p; char *s; char buf[CDDBBUFSIZ]; char buf2[CDDBBUFSIZ]; char set[CDDBBUFSIZ]; char enc[CDDBBUFSIZ]; eh->eh_flags = 0; eh->eh_class = EC_NONE; eh->eh_charset = CC_US_ASCII; eh->eh_encoding = CE_7BIT; while(get_hdr_line(buf, sizeof(buf), fp)) { strcpy(buf2, buf); /* pointless copy */ blank = (buf[0] == '\n' || (buf[0] == '\r' && buf[1] == '\n')); if(!strncmp(buf, from, strlen(from)) || !strncmp(buf, x_cddbd_from, strlen(rpath)) || (!header && !strncmp(buf, rpath, strlen(rpath)))) { p = buf; while(*p != '\0' && !isspace(*p)) p++; while(*p != '\0' && isspace(*p)) p++; if(*p != '\0') { cpy_sender(p, eh->eh_rcpt, eh->eh_to, eh->eh_host); eh->eh_flags |= EH_RCPT; header++; } continue; } if(!header) continue; if(!strncmp(buf, subj, strlen(subj))) { p = buf; while(*p != '\0' && !isspace(*p)) p++; while(*p != '\0' && isspace(*p)) p++; if(sscanf(p, cmd_send_subj, eh->eh_serial)) eh->eh_class = EC_COMMAND; else if(sscanf(p, sub_send_subj, eh->eh_category, &eh->eh_discid) == 2) eh->eh_class = EC_SUBMIT; else { strcpy(errstr, "Malformed subject in header"); return EE_ERROR; } strip_crlf(p); strncpy(eh->eh_subj, p, sizeof(eh->eh_subj)); eh->eh_subj[sizeof(eh->eh_subj) - 1] = '\0'; gotsubj++; eh->eh_flags |= EH_SUBJECT; } else if(!cddbd_strncasecmp(buf, x_cddbd_crc, strlen(x_cddbd_crc))) { /* Skip junk. */ p = buf + strlen(x_cddbd_crc) + 1; while(*p != '\0' && !isxdigit(*p)) p++; /* Find the crc. */ if(sscanf(p, "%255[a-zA-Z0-9]", buf2) != 1 || strlen(buf2) != CDDBXCRCLEN) { cddbd_snprintf(errstr, CDDBBUFSIZ, "Malformed %s in header", x_cddbd_crc); return EE_ERROR; } strcpy(eh->eh_crc, buf2); eh->eh_flags |= EH_CRC; } else if(!cddbd_strncasecmp(buf, x_cddbd_echo, strlen(x_cddbd_echo))) { /* Skip junk. */ p = buf + strlen(x_cddbd_echo) + 1; while(*p != '\0' && isspace(*p)) p++; /* Find the echo string. */ if(sscanf(p, "%255[^\n\r]", buf2) != 1 || strlen(buf2) > CDDBXECHOLEN) { cddbd_snprintf(errstr, CDDBBUFSIZ, "Invalid %s in header", x_cddbd_echo); return EE_ERROR; } strcpy(eh->eh_echo, buf2); eh->eh_flags |= EH_ECHO; } else if(!cddbd_strncasecmp(buf, x_cddbd_note, strlen(x_cddbd_note))) { /* Skip junk. */ p = buf + strlen(x_cddbd_note) + 1; while(*p != '\0' && isspace(*p)) p++; /* Find the note string. */ if(sscanf(p, "%255[^\n\r]", buf2) != 1 || strlen(buf2) > CDDBXNOTELEN) { cddbd_snprintf(errstr, CDDBBUFSIZ, "Invalid %s in header", x_cddbd_note); return EE_ERROR; } strcpy(eh->eh_note, buf2); eh->eh_flags |= EH_NOTE; } else if(!cddbd_strncasecmp(buf, content_type, strlen(content_type))) { if((p = cddbd_strcasestr(buf, charset)) != 0) { /* Skip junk. */ p += strlen(charset); while(*p != '\0' && !isalpha(*p) && !isdigit(*p)) p++; /* Find the charset. */ if(sscanf(p, "%255[a-zA-Z0-9_-]", set) != 1) { cddbd_snprintf(errstr, CDDBBUFSIZ, "Malformed %s in header", content_type); return EE_ERROR; } gotset++; } if((p = cddbd_strcasestr(buf, boundary)) != 0) { /* Skip junk. */ p += strlen(boundary); while(*p != '\0') { if(*p == '=') { p++; break; } p++; } if(*p == '"') { p++; s = "%255[^\"]"; } else s = "%255s"; /* Find the boundary string. */ if(sscanf(p, s, eh->eh_boundary) != 1) { cddbd_snprintf(errstr, CDDBBUFSIZ, "Malformed %s in header", content_type); return EE_ERROR; } len = strlen(eh->eh_boundary); if(eh->eh_boundary[len - 1] == '"' || eh->eh_boundary[len - 1] == ';') eh->eh_boundary[len - 1] = '\0'; eh->eh_flags |= EH_BOUNDARY; waitbound++; } } else if(!cddbd_strncasecmp(buf, content_encoding, strlen(content_encoding))) { /* Skip junk. */ p = buf + strlen(content_encoding); while(*p != '\0' && !isalpha(*p) && !isdigit(*p)) p++; /* Find the encoding. */ if(sscanf(p, "%[a-zA-Z0-9_-]", enc) != 1) { cddbd_snprintf(errstr, CDDBBUFSIZ, "Malformed %s in header", content_encoding); return EE_ERROR; } gotenc++; } else if(waitbound && strstr(buf, eh->eh_boundary) != NULL) { waitbound = 0; } else if(blank && !waitbound) { get_rmt_hostname(-1, eh->eh_host, rhost); if(!gotsubj) { strcpy(errstr, "Missing subject in header"); return EE_ERROR; } if(gotenc && !get_encoding(enc, eh, errstr)) return EE_ERROR; if(gotset && !get_charset(set, eh, errstr)) return EE_ERROR; return EE_OK; } } if(waitbound) strcpy(errstr, "Missing boundary string"); else strcpy(errstr, "Malformed email header"); return EE_ERROR; } int get_encoding(char *encoding, email_hdr_t *eh, char *errstr) { int i; /* Look for the encoding in the supported list. */ for(i = 0; encoding_types[i].en_type != 0; i++) if(!cddbd_strcasecmp(encoding, encoding_types[i].en_type)) break; /* Unknown encoding. */ if(encoding_types[i].en_type == 0) { cddbd_snprintf(errstr, CDDBBUFSIZ, "Unsupported %s in header: %s", content_encoding, encoding); return 0; } eh->eh_encoding = i; return 1; } int get_charset(char *set, email_hdr_t *eh, char *errstr) { int i; /* Look for the charset in the supported list. */ for(i = 0; charsets[i].name != 0; i++) if(!cddbd_charcasecmp(set, charsets[i].name)) break; /* Unknown charset. */ if(charsets[i].name == 0) { cddbd_snprintf(errstr, CDDBBUFSIZ, "Unsupported %s in header: %s", charset, set); return 0; } eh->eh_charset = i; return 1; } void cpy_sender(char *str, char *rcpt, char *to, char *fhost) { int gotaddr= 0; int gotname= 0; int gotmisc= 0; char *p; char *pa= "unknown"; /* see NOTE below */ char *pn= "unknown"; /* see NOTE below */ char addr[CDDBBUFSIZ]; char name[CDDBBUFSIZ]; char misc[CDDBBUFSIZ]; /* Scan the string for the name and address. */ if(scan_from(str, addr, SCAN_ADDR)) gotaddr = 1; if(scan_from(str, name, SCAN_NAME)) gotname = 1; if((!gotaddr || !gotname) && scan_from(str, misc, SCAN_MISC)) gotmisc = 1; /* NOTE: there might be cases where neither SCAN_ADDR nor SCAN_NAME * nor SCAN_MISC return plausible values. Therefore pa and pn need * to be initialized to avoid picking-up garbage. Maybe it would * make sense not to process such cases at all. */ if(gotaddr) { pa = addr; } else if(gotmisc) { pa = misc; gotaddr = 1; gotmisc = 0; } if(gotname) { pn = name; } else if(gotmisc) { pn = misc; gotname = 1; } /* We couldn't parse the string, use the raw "from". */ if(!gotaddr) { strcpy(rcpt, str); pn = &rcpt[strlen(rcpt) - 1]; if(*pn == '\n') *pn = '\0'; strcpy(to, rcpt); strcpy(fhost, "unknown"); return; } /* Copy the relevant parts. */ cddbd_snprintf(rcpt, CDDBBUFSIZ, "%s", pa); if(gotname) cddbd_snprintf(to, CDDBBUFSIZ, "%s (%s)", pa, pn); else cddbd_snprintf(to, CDDBBUFSIZ, "%s", pa); /* * Get the hostname from the address. If it's a complicated * address, this may goof up. If there's no discernible host we * must assume it's because there isn't one, so call it "localhost". */ if((p = strrchr(pa, '@')) != NULL) cddbd_snprintf(fhost, CDDBBUFSIZ, "%s", (p + 1)); else if((p = strchr(pa, '!')) != NULL) { *p = '\0'; cddbd_snprintf(fhost, CDDBBUFSIZ, "%s", pa); } else if((p = strrchr(pa, '%')) != NULL) cddbd_snprintf(fhost, CDDBBUFSIZ, "%s", (p + 1)); else strcpy(fhost, LHOST); } int scan_from(char *str, char *buf, int type) { char *p; char *p1; /* Skip leading white space. */ p = str; while(*p != '\0' && isspace(*p)) p++; /* Scan for a beginning marker. */ while(*p != '\0' && !is_instr(*p, start_type[type])) p++; if(*p == '\0') return 0; /* Strip off marker, if there is one. */ if(*start_type[type] != '\0') p++; p1 = buf; /* Copy in relevant portion of the string. */ while(*p != '\0' && !is_instr(*p, end_type[type])) { *p1 = *p; p++; p1++; } /* Strip off trailing white space. */ p1--; while(isspace(*p1)) p1--; p1++; *p1 = '\0'; return 1; } int return_mail(FILE *fp, email_hdr_t *eh, int flags, char *errstr) { int i; int len; FILE *nfp; char *p; char *incl; char *bincl; char *subj; char **sig; char **body; char *copy_addr; char *rtn_addr; char *tret; char *echo; char buf[CDDBBUFSIZ]; char subj_buf[CDDBBUFSIZ]; struct stat sbuf; int charset = -1; /* Create a temporary file for the mail. */ tret = cddbd_mktemp(); if(flags & MF_FAIL) { strncpy(buf, errstr, sizeof(buf)); buf[sizeof(buf) - 1] = '\0'; if((p = (char *)strchr(buf, '\n')) != NULL) *p = '\0'; } /* Return address is currently always the admin. */ rtn_addr = admin_email; switch(eh->eh_class) { case EC_COMMAND: /* Command response. */ if(flags & MF_FAIL) { cddbd_log(LOG_ERR | LOG_MAIL, "Email command failed: %s", buf); cddbd_snprintf(subj_buf, sizeof(subj_buf), cmd_rej_subj, eh->eh_serial); sig = gen_rej_sig; body = gen_rej_body; incl = gen_incl; bincl = gen_incl_blank; copy_addr = bounce_email; } else { cddbd_snprintf(subj_buf, sizeof(subj_buf), cmd_ok_subj, eh->eh_serial); sig = 0; body = 0; incl = null_incl; bincl = null_incl_blank; copy_addr = 0; } subj = subj_buf; break; case EC_SUBMIT: /* We only send mail for rejections and test submissions. */ if(flags & MF_FAIL) { if(flags & MF_TEST) { cddbd_log(LOG_ERR | LOG_MAIL, "Test email submission failed: %s", buf); subj = test_rej_subj; } else { cddbd_log(LOG_ERR | LOG_MAIL, "Email submission failed: %s", buf); subj = sub_rej_subj; } sig = gen_rej_sig; body = sub_rej_body; copy_addr = bounce_email; } else { sig = test_ok_sig; body = test_ok_body; subj = test_ok_subj; copy_addr = test_email; } incl = gen_incl; bincl = gen_incl_blank; charset = eh->eh_charset; break; default: /* subj = gen_rej_subj; body = gen_rej_body; sig = gen_rej_sig; incl = gen_incl; bincl = gen_incl_blank; copy_addr = bounce_email; break; */ /* Because of the increasing spam problem, we shouldn't be sending people all this mail which responds to spam sent to the freedb submission address. */ cddbd_freetemp(tret); return EE_OK; } if(eh->eh_flags & EH_ECHO) echo = eh->eh_echo; else echo = 0; if((nfp = fopen(tret, "w+")) == NULL) { cddbd_log(LOG_ERR | LOG_MAIL, "Can't create mail tmp file %s (%d)", tret, errno); return EE_ERROR; } /* Write out the note string. */ if(eh->eh_flags & EH_NOTE) { if(fprintf(nfp, note, eh->eh_note) == EOF) { cddbd_log(LOG_ERR | LOG_MAIL, "Can't write mail tmp file %s (%d)", tret, errno); fclose(nfp); cddbd_freetemp(tret); return EE_ERROR; } } /* Write out the err string. */ for(i = 0; body && body[i]; i++) { if(fprintf(nfp, body[i], errstr) == EOF) { cddbd_log(LOG_ERR | LOG_MAIL, "Can't write mail tmp file %s (%d)", tret, errno); fclose(nfp); cddbd_freetemp(tret); return EE_ERROR; } } rewind(fp); /* Write out the mail body. */ while(fgets(buf, sizeof(buf), fp) != NULL) { strip_crlf(buf); /* remove blank lines, skip nfp write */ if(is_blank(buf, 0)) p = bincl; else p = incl; /* zeke - remove the ## comment lines, exit - we are done */ if(is_DblHash(buf)) break; if(fprintf(nfp, p, buf) == EOF) { cddbd_log(LOG_ERR | LOG_MAIL, "Can't write mail tmp file %s (%d)", tret, errno); fclose(nfp); cddbd_freetemp(tret); return EE_ERROR; } } /*!zeke - correct comment follows ???? */ /* Write out the err string. */ for(i = 0; sig && sig[i]; i++) { if(fprintf(nfp, sig[i], admin_email) == EOF) { cddbd_log(LOG_ERR | LOG_MAIL, "Can't write mail tmp file %s (%d)", tret, errno); fclose(nfp); cddbd_freetemp(tret); return EE_ERROR; } } fflush(nfp); if(fstat(fileno(nfp), &sbuf)) len = 0; else len = sbuf.st_size; rewind(nfp); /* Figure out if we need to encode the mail. */ if(!(flags & (MF_ENC | MF_MULTI))) { while(fgets(buf, sizeof(buf), nfp) != NULL) { if(is_rfc_1521_mappable((unsigned char *)buf, 1, 0)) { flags |= MF_ENC; break; } } } rewind(nfp); if(!smtp_open()) { fclose(nfp); cddbd_freetemp(tret); return EE_ERROR; } if(!smtp_transmit(nfp, charset, subj, eh->eh_rcpt, eh->eh_to, rtn_addr, echo, flags, len, 0)) { fclose(nfp); cddbd_freetemp(tret); return EE_ERROR; } if(copy_addr != 0 && copy_addr[0] != '\0') { rewind(nfp); cddbd_snprintf(buf, sizeof(buf), "%s (fwd)", subj); if(!smtp_transmit(nfp, charset, buf, copy_addr, copy_addr, rtn_addr, echo, flags, len, 0)) { fclose(nfp); cddbd_freetemp(tret); return EE_ERROR; } } smtp_close(); fclose(nfp); cddbd_freetemp(tret); return EE_OK; }