/* * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. */ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #ifdef DOSCCS static char sccsid[] = "@(#)sendout.c 2.95 (gritter) 6/16/07"; #endif #endif /* not lint */ #include "rcv.h" #include "extern.h" #include #include #include #include #include #include "md5.h" /* * Mail -- a mail program * * Mail to others. */ static char *send_boundary; static char *getencoding(enum conversion convert); static struct name *fixhead(struct header *hp, struct name *tolist); static int put_signature(FILE *fo, int convert); static int attach_file(struct attachment *ap, FILE *fo, int dosign); static int attach_message(struct attachment *ap, FILE *fo, int dosign); static int make_multipart(struct header *hp, int convert, FILE *fi, FILE *fo, const char *contenttype, const char *charset, int dosign); static FILE *infix(struct header *hp, FILE *fi, int dosign); static int savemail(char *name, FILE *fi); static int sendmail_internal(void *v, int recipient_record); static enum okay transfer(struct name *to, struct name *mailargs, FILE *input, struct header *hp); static enum okay start_mta(struct name *to, struct name *mailargs, FILE *input, struct header *hp); static void message_id(FILE *fo, struct header *hp); static int fmt(char *str, struct name *np, FILE *fo, int comma, int dropinvalid, int domime); static int infix_resend(FILE *fi, FILE *fo, struct message *mp, struct name *to, int add_resent); /* * Generate a boundary for MIME multipart messages. */ char * makeboundary(void) { static char bound[70]; time_t now; time(&now); snprintf(bound, sizeof bound, "=_%lx.%s", (long)now, getrandstring(48)); send_boundary = bound; return send_boundary; } /* * Get an encoding flag based on the given string. */ static char * getencoding(enum conversion convert) { switch (convert) { case CONV_7BIT: return "7bit"; case CONV_8BIT: return "8bit"; case CONV_TOQP: return "quoted-printable"; case CONV_TOB64: return "base64"; default: break; } /*NOTREACHED*/ return NULL; } /* * Fix the header by glopping all of the expanded names from * the distribution list into the appropriate fields. */ static struct name * fixhead(struct header *hp, struct name *tolist) { struct name *np; hp->h_to = NULL; hp->h_cc = NULL; hp->h_bcc = NULL; for (np = tolist; np != NULL; np = np->n_flink) if ((np->n_type & GMASK) == GTO) hp->h_to = cat(hp->h_to, nalloc(np->n_fullname, np->n_type|GFULL)); else if ((np->n_type & GMASK) == GCC) hp->h_cc = cat(hp->h_cc, nalloc(np->n_fullname, np->n_type|GFULL)); else if ((np->n_type & GMASK) == GBCC) hp->h_bcc = cat(hp->h_bcc, nalloc(np->n_fullname, np->n_type|GFULL)); return tolist; } /* * Do not change, you get incorrect base64 encodings else! */ #define INFIX_BUF 972 /* * Put the signature file at fo. */ static int put_signature(FILE *fo, int convert) { char *sig, buf[INFIX_BUF], c = '\n'; FILE *fsig; size_t sz; sig = value("signature"); if (sig == NULL || *sig == '\0') return 0; else sig = expand(sig); if ((fsig = Fopen(sig, "r")) == NULL) { perror(sig); return -1; } while ((sz = fread(buf, sizeof *buf, INFIX_BUF, fsig)) != 0) { c = buf[sz - 1]; if (mime_write(buf, sz, fo, convert, TD_NONE, NULL, (size_t)0, NULL, NULL) == 0) { perror(sig); Fclose(fsig); return -1; } } if (ferror(fsig)) { perror(sig); Fclose(fsig); return -1; } Fclose(fsig); if (c != '\n') putc('\n', fo); return 0; } /* * Write an attachment to the file buffer, converting to MIME. */ static int attach_file(struct attachment *ap, FILE *fo, int dosign) { FILE *fi; char *charset = NULL, *contenttype = NULL, *basename; enum conversion convert = CONV_TOB64; int err = 0; enum mimeclean isclean; size_t sz; char *buf; size_t bufsize, count; int lastc = EOF; #ifdef HAVE_ICONV char *tcs; #endif if ((fi = Fopen(ap->a_name, "r")) == NULL) { perror(ap->a_name); return -1; } if ((basename = strrchr(ap->a_name, '/')) == NULL) basename = ap->a_name; else basename++; if (ap->a_content_type) contenttype = ap->a_content_type; else contenttype = mime_filecontent(basename); if (ap->a_charset) charset = ap->a_charset; convert = get_mime_convert(fi, &contenttype, &charset, &isclean, dosign); fprintf(fo, "\n--%s\n" "Content-Type: %s", send_boundary, contenttype); if (charset == NULL) putc('\n', fo); else fprintf(fo, ";\n charset=%s\n", charset); if (ap->a_content_disposition == NULL) ap->a_content_disposition = "attachment"; fprintf(fo, "Content-Transfer-Encoding: %s\n" "Content-Disposition: %s;\n" " filename=\"", getencoding(convert), ap->a_content_disposition); mime_write(basename, strlen(basename), fo, CONV_TOHDR, TD_NONE, NULL, (size_t)0, NULL, NULL); fwrite("\"\n", sizeof (char), 2, fo); if (ap->a_content_id) fprintf(fo, "Content-ID: %s\n", ap->a_content_id); if (ap->a_content_description) fprintf(fo, "Content-Description: %s\n", ap->a_content_description); putc('\n', fo); #ifdef HAVE_ICONV if (iconvd != (iconv_t)-1) { iconv_close(iconvd); iconvd = (iconv_t)-1; } tcs = gettcharset(); if ((isclean & (MIME_HASNUL|MIME_CTRLCHAR)) == 0 && ascncasecmp(contenttype, "text/", 5) == 0 && isclean & MIME_HIGHBIT && charset != NULL && asccasecmp(charset, tcs)) { if ((iconvd = iconv_open_ft(charset, tcs)) == (iconv_t)-1 && errno != 0) { if (errno == EINVAL) fprintf(stderr, catgets(catd, CATSET, 179, "Cannot convert from %s to %s\n"), tcs, charset); else perror("iconv_open"); Fclose(fi); return -1; } } #endif /* HAVE_ICONV */ buf = smalloc(bufsize = INFIX_BUF); if (convert == CONV_TOQP #ifdef HAVE_ICONV || iconvd != (iconv_t)-1 #endif ) { fflush(fi); count = fsize(fi); } for (;;) { if (convert == CONV_TOQP #ifdef HAVE_ICONV || iconvd != (iconv_t)-1 #endif ) { if (fgetline(&buf, &bufsize, &count, &sz, fi, 0) == NULL) break; } else { if ((sz = fread(buf, sizeof *buf, bufsize, fi)) == 0) break; } lastc = buf[sz-1]; if (mime_write(buf, sz, fo, convert, TD_ICONV, NULL, (size_t)0, NULL, NULL) == 0) err = -1; } if (convert == CONV_TOQP && lastc != '\n') fwrite("=\n", 1, 2, fo); if (ferror(fi)) err = -1; Fclose(fi); free(buf); return err; } /* * Attach a message to the file buffer. */ static int attach_message(struct attachment *ap, FILE *fo, int dosign) { struct message *mp; fprintf(fo, "\n--%s\n" "Content-Type: message/rfc822\n" "Content-Disposition: inline\n\n", send_boundary); mp = &message[ap->a_msgno - 1]; touch(mp); if (send(mp, fo, 0, NULL, SEND_RFC822, NULL) < 0) return -1; return 0; } /* * Generate the body of a MIME multipart message. */ static int make_multipart(struct header *hp, int convert, FILE *fi, FILE *fo, const char *contenttype, const char *charset, int dosign) { struct attachment *att; fputs("This is a multi-part message in MIME format.\n", fo); if (fsize(fi) != 0) { char *buf, c = '\n'; size_t sz, bufsize, count; fprintf(fo, "\n--%s\n", send_boundary); fprintf(fo, "Content-Type: %s", contenttype); if (charset) fprintf(fo, "; charset=%s", charset); fprintf(fo, "\nContent-Transfer-Encoding: %s\n" "Content-Disposition: inline\n\n", getencoding(convert)); buf = smalloc(bufsize = INFIX_BUF); if (convert == CONV_TOQP #ifdef HAVE_ICONV || iconvd != (iconv_t)-1 #endif /* HAVE_ICONV */ ) { fflush(fi); count = fsize(fi); } for (;;) { if (convert == CONV_TOQP #ifdef HAVE_ICONV || iconvd != (iconv_t)-1 #endif /* HAVE_ICONV */ ) { if (fgetline(&buf, &bufsize, &count, &sz, fi, 0) == NULL) break; } else { sz = fread(buf, sizeof *buf, bufsize, fi); if (sz == 0) break; } c = buf[sz - 1]; if (mime_write(buf, sz, fo, convert, TD_ICONV, NULL, (size_t)0, NULL, NULL) == 0) { free(buf); return -1; } } free(buf); if (ferror(fi)) return -1; if (c != '\n') putc('\n', fo); if (charset != NULL) put_signature(fo, convert); } for (att = hp->h_attach; att != NULL; att = att->a_flink) { if (att->a_msgno) { if (attach_message(att, fo, dosign) != 0) return -1; } else { if (attach_file(att, fo, dosign) != 0) return -1; } } /* the final boundary with two attached dashes */ fprintf(fo, "\n--%s--\n", send_boundary); return 0; } /* * Prepend a header in front of the collected stuff * and return the new file. */ static FILE * infix(struct header *hp, FILE *fi, int dosign) { FILE *nfo, *nfi; char *tempMail; #ifdef HAVE_ICONV char *tcs, *convhdr = NULL; #endif enum mimeclean isclean; enum conversion convert; char *charset = NULL, *contenttype = NULL; int lastc = EOF; if ((nfo = Ftemp(&tempMail, "Rs", "w", 0600, 1)) == NULL) { perror(catgets(catd, CATSET, 178, "temporary mail file")); return(NULL); } if ((nfi = Fopen(tempMail, "r")) == NULL) { perror(tempMail); Fclose(nfo); return(NULL); } rm(tempMail); Ftfree(&tempMail); convert = get_mime_convert(fi, &contenttype, &charset, &isclean, dosign); #ifdef HAVE_ICONV tcs = gettcharset(); if ((convhdr = need_hdrconv(hp, GTO|GSUBJECT|GCC|GBCC|GIDENT)) != 0 && asccasecmp(convhdr, tcs)) { if (iconvd != (iconv_t)-1) iconv_close(iconvd); if ((iconvd = iconv_open_ft(convhdr, tcs)) == (iconv_t)-1 && errno != 0) { if (errno == EINVAL) fprintf(stderr, catgets(catd, CATSET, 179, "Cannot convert from %s to %s\n"), tcs, convhdr); else perror("iconv_open"); Fclose(nfo); return NULL; } } #endif /* HAVE_ICONV */ if (puthead(hp, nfo, GTO|GSUBJECT|GCC|GBCC|GNL|GCOMMA|GUA|GMIME |GMSGID|GIDENT|GREF|GDATE, SEND_MBOX, convert, contenttype, charset)) { Fclose(nfo); Fclose(nfi); #ifdef HAVE_ICONV if (iconvd != (iconv_t)-1) { iconv_close(iconvd); iconvd = (iconv_t)-1; } #endif return NULL; } #ifdef HAVE_ICONV if (convhdr && iconvd != (iconv_t)-1) { iconv_close(iconvd); iconvd = (iconv_t)-1; } if ((isclean & (MIME_HASNUL|MIME_CTRLCHAR)) == 0 && ascncasecmp(contenttype, "text/", 5) == 0 && isclean & MIME_HIGHBIT && charset != NULL && asccasecmp(charset, tcs)) { if (iconvd != (iconv_t)-1) iconv_close(iconvd); if ((iconvd = iconv_open_ft(charset, tcs)) == (iconv_t)-1 && errno != 0) { if (errno == EINVAL) fprintf(stderr, catgets(catd, CATSET, 179, "Cannot convert from %s to %s\n"), tcs, charset); else perror("iconv_open"); Fclose(nfo); return NULL; } } #endif if (hp->h_attach != NULL) { if (make_multipart(hp, convert, fi, nfo, contenttype, charset, dosign) != 0) { Fclose(nfo); Fclose(nfi); #ifdef HAVE_ICONV if (iconvd != (iconv_t)-1) { iconv_close(iconvd); iconvd = (iconv_t)-1; } #endif return NULL; } } else { size_t sz, bufsize, count; char *buf; if (convert == CONV_TOQP #ifdef HAVE_ICONV || iconvd != (iconv_t)-1 #endif /* HAVE_ICONV */ ) { fflush(fi); count = fsize(fi); } buf = smalloc(bufsize = INFIX_BUF); for (;;) { if (convert == CONV_TOQP #ifdef HAVE_ICONV || iconvd != (iconv_t)-1 #endif /* HAVE_ICONV */ ) { if (fgetline(&buf, &bufsize, &count, &sz, fi, 0) == NULL) break; } else { sz = fread(buf, sizeof *buf, bufsize, fi); if (sz == 0) break; } lastc = buf[sz - 1]; if (mime_write(buf, sz, nfo, convert, TD_ICONV, NULL, (size_t)0, NULL, NULL) == 0) { Fclose(nfo); Fclose(nfi); #ifdef HAVE_ICONV if (iconvd != (iconv_t)-1) { iconv_close(iconvd); iconvd = (iconv_t)-1; } #endif free(buf); return NULL; } } if (convert == CONV_TOQP && lastc != '\n') fwrite("=\n", 1, 2, nfo); free(buf); if (ferror(fi)) { Fclose(nfo); Fclose(nfi); #ifdef HAVE_ICONV if (iconvd != (iconv_t)-1) { iconv_close(iconvd); iconvd = (iconv_t)-1; } #endif return NULL; } if (charset != NULL) put_signature(nfo, convert); } #ifdef HAVE_ICONV if (iconvd != (iconv_t)-1) { iconv_close(iconvd); iconvd = (iconv_t)-1; } #endif fflush(nfo); if (ferror(nfo)) { perror(catgets(catd, CATSET, 180, "temporary mail file")); Fclose(nfo); Fclose(nfi); return NULL; } Fclose(nfo); Fclose(fi); fflush(nfi); rewind(nfi); return(nfi); } /* * Save the outgoing mail on the passed file. */ /*ARGSUSED*/ static int savemail(char *name, FILE *fi) { FILE *fo; char *buf; size_t bufsize, buflen, count; char *p; time_t now; int prependnl = 0; int error = 0; buf = smalloc(bufsize = LINESIZE); time(&now); if ((fo = Zopen(name, "a+", NULL)) == NULL) { if ((fo = Zopen(name, "wx", NULL)) == NULL) { perror(name); free(buf); return (-1); } } else { if (fseek(fo, -2L, SEEK_END) == 0) { switch (fread(buf, sizeof *buf, 2, fo)) { case 2: if (buf[1] != '\n') { prependnl = 1; break; } /*FALLTHRU*/ case 1: if (buf[0] != '\n') prependnl = 1; break; default: if (ferror(fo)) { perror(name); free(buf); return -1; } } fflush(fo); if (prependnl) { putc('\n', fo); fflush(fo); } } } fprintf(fo, "From %s %s", myname, ctime(&now)); buflen = 0; fflush(fi); rewind(fi); count = fsize(fi); while (fgetline(&buf, &bufsize, &count, &buflen, fi, 0) != NULL) { if (*buf == '>') { p = buf + 1; while (*p == '>') p++; if (strncmp(p, "From ", 5) == 0) /* we got a masked From line */ putc('>', fo); } else if (strncmp(buf, "From ", 5) == 0) putc('>', fo); fwrite(buf, sizeof *buf, buflen, fo); } if (buflen && *(buf + buflen - 1) != '\n') putc('\n', fo); putc('\n', fo); fflush(fo); if (ferror(fo)) { perror(name); error = -1; } if (Fclose(fo) != 0) error = -1; fflush(fi); rewind(fi); /* * OpenBSD 3.2 and NetBSD 1.5.2 were reported not to * reset the kernel file offset after the calls above, * a clear violation of IEEE Std 1003.1, 1996, 8.2.3.7. * So do it 'manually'. */ lseek(fileno(fi), 0, SEEK_SET); free(buf); return error; } /* * Interface between the argument list and the mail1 routine * which does all the dirty work. */ int mail(struct name *to, struct name *cc, struct name *bcc, struct name *smopts, char *subject, struct attachment *attach, char *quotefile, int recipient_record, int tflag) { struct header head; struct str in, out; memset(&head, 0, sizeof head); /* The given subject may be in RFC1522 format. */ if (subject != NULL) { in.s = subject; in.l = strlen(subject); mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV); head.h_subject = out.s; } if (tflag == 0) { head.h_to = to; head.h_cc = cc; head.h_bcc = bcc; } head.h_attach = attach; head.h_smopts = smopts; mail1(&head, 0, NULL, quotefile, recipient_record, 0, tflag); if (subject != NULL) free(out.s); return(0); } /* * Send mail to a bunch of user names. The interface is through * the mail routine below. */ static int sendmail_internal(void *v, int recipient_record) { char *str = v; struct header head; memset(&head, 0, sizeof head); head.h_to = extract(str, GTO|GFULL); mail1(&head, 0, NULL, NULL, recipient_record, 0, 0); return(0); } int sendmail(void *v) { return sendmail_internal(v, 0); } int Sendmail(void *v) { return sendmail_internal(v, 1); } static enum okay transfer(struct name *to, struct name *mailargs, FILE *input, struct header *hp) { char o[LINESIZE], *cp; struct name *np, *nt; int cnt = 0; FILE *ef; enum okay ok = OKAY; np = to; while (np) { snprintf(o, sizeof o, "smime-encrypt-%s", np->n_name); if ((cp = value(o)) != NULL) { if ((ef = smime_encrypt(input, cp, np->n_name)) != 0) { nt = nalloc(np->n_name, np->n_type & ~(GFULL|GSKIN)); if (start_mta(nt, mailargs, ef, hp) != OKAY) ok = STOP; Fclose(ef); } else { fprintf(stderr, "Message not sent to <%s>\n", np->n_name); senderr++; } rewind(input); if (np->n_flink) np->n_flink->n_blink = np->n_blink; if (np->n_blink) np->n_blink->n_flink = np->n_flink; if (np == to) to = np->n_flink; np = np->n_flink; } else { cnt++; np = np->n_flink; } } if (cnt) { if (value("smime-force-encryption") || start_mta(to, mailargs, input, hp) != OKAY) ok = STOP; } return ok; } /* * Start the Mail Transfer Agent * mailing to namelist and stdin redirected to input. */ static enum okay start_mta(struct name *to, struct name *mailargs, FILE *input, struct header *hp) { char **args = NULL, **t; pid_t pid; sigset_t nset; char *cp, *smtp; char *user = NULL, *password = NULL, *skinned = NULL; enum okay ok = STOP; #ifdef HAVE_SOCKETS struct termios otio; int reset_tio; #endif /* HAVE_SOCKETS */ if ((smtp = value("smtp")) == NULL) { args = unpack(cat(mailargs, to)); if (debug || value("debug")) { printf(catgets(catd, CATSET, 181, "Sendmail arguments:")); for (t = args; *t != NULL; t++) printf(" \"%s\"", *t); printf("\n"); return OKAY; } } #ifdef HAVE_SOCKETS if (smtp != NULL) { skinned = skin(myorigin(hp)); if ((user = smtp_auth_var("-user", skinned)) != NULL && (password = smtp_auth_var("-password", skinned)) == NULL) password = getpassword(&otio, &reset_tio, NULL); } #endif /* HAVE_SOCKETS */ /* * Fork, set up the temporary mail file as standard * input for "mail", and exec with the user list we generated * far above. */ if ((pid = fork()) == -1) { perror("fork"); savedeadletter(input); senderr++; return STOP; } if (pid == 0) { sigemptyset(&nset); sigaddset(&nset, SIGHUP); sigaddset(&nset, SIGINT); sigaddset(&nset, SIGQUIT); sigaddset(&nset, SIGTSTP); sigaddset(&nset, SIGTTIN); sigaddset(&nset, SIGTTOU); freopen("/dev/null", "r", stdin); if (smtp != NULL) { prepare_child(&nset, 0, 1); if (smtp_mta(smtp, to, input, hp, user, password, skinned) == 0) _exit(0); } else { prepare_child(&nset, fileno(input), -1); if ((cp = value("sendmail")) != NULL) cp = expand(cp); else cp = SENDMAIL; execv(cp, args); perror(cp); } savedeadletter(input); fputs(catgets(catd, CATSET, 182, ". . . message not sent.\n"), stderr); _exit(1); } if (value("verbose") != NULL || value("sendwait") || debug || value("debug")) { if (wait_child(pid) == 0) ok = OKAY; else senderr++; } else { ok = OKAY; free_child(pid); } return ok; } /* * Record outgoing mail if instructed to do so. */ static enum okay mightrecord(FILE *fp, struct name *to, int recipient_record) { char *cp, *cq, *ep; if (recipient_record) { cq = skin(to->n_name); cp = salloc(strlen(cq) + 1); strcpy(cp, cq); for (cq = cp; *cq && *cq != '@'; cq++); *cq = '\0'; } else cp = value("record"); if (cp != NULL) { ep = expand(cp); if (value("outfolder") && *ep != '/' && *ep != '+' && which_protocol(ep) == PROTO_FILE) { cq = salloc(strlen(cp) + 2); cq[0] = '+'; strcpy(&cq[1], cp); cp = cq; ep = expand(cp); } if (savemail(ep, fp) != 0) { fprintf(stderr, "Error while saving message to %s - " "message not sent\n", ep); rewind(fp); exit_status |= 1; savedeadletter(fp); return STOP; } } return OKAY; } /* * Mail a message on standard input to the people indicated * in the passed header. (Internal interface). */ enum okay mail1(struct header *hp, int printheaders, struct message *quote, char *quotefile, int recipient_record, int doprefix, int tflag) { struct name *to; FILE *mtf, *nmtf; enum okay ok = STOP; int dosign = -1; char *charsets, *ncs = NULL, *cp; #ifdef notdef if ((hp->h_to = checkaddrs(hp->h_to)) == NULL) { senderr++; return STOP; } #endif if ((cp = value("autocc")) != NULL && *cp) hp->h_cc = cat(hp->h_cc, checkaddrs(sextract(cp, GCC|GFULL))); if ((cp = value("autobcc")) != NULL && *cp) hp->h_bcc = cat(hp->h_bcc, checkaddrs(sextract(cp, GBCC|GFULL))); /* * Collect user's mail from standard input. * Get the result as mtf. */ if ((mtf = collect(hp, printheaders, quote, quotefile, doprefix, tflag)) == NULL) return STOP; if (value("interactive") != NULL) { if (((value("bsdcompat") || value("askatend")) && (value("askcc") != NULL || value("askbcc") != NULL)) || value("askattach") != NULL || value("asksign") != NULL) { if (value("askcc") != NULL) grabh(hp, GCC, 1); if (value("askbcc") != NULL) grabh(hp, GBCC, 1); if (value("askattach") != NULL) hp->h_attach = edit_attachments(hp->h_attach); if (value("asksign") != NULL) dosign = yorn("Sign this message (y/n)? "); } else { printf(catgets(catd, CATSET, 183, "EOT\n")); fflush(stdout); } } if (fsize(mtf) == 0) { if (hp->h_subject == NULL) printf(catgets(catd, CATSET, 184, "No message, no subject; hope that's ok\n")); else if (value("bsdcompat") || value("bsdmsgs")) printf(catgets(catd, CATSET, 185, "Null message body; hope that's ok\n")); } if (dosign < 0) { if (value("smime-sign") != NULL) dosign = 1; else dosign = 0; } /* * Now, take the user names from the combined * to and cc lists and do all the alias * processing. */ senderr = 0; if ((to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc)))) == NULL) { printf(catgets(catd, CATSET, 186, "No recipients specified\n")); senderr++; } to = fixhead(hp, to); if (hp->h_charset) { wantcharset = hp->h_charset; goto try; } hloop: wantcharset = NULL; if ((charsets = value("sendcharsets")) != NULL) { wantcharset = savestr(charsets); loop: if ((ncs = strchr(wantcharset, ',')) != NULL) *ncs++ = '\0'; } try: if ((nmtf = infix(hp, mtf, dosign)) == NULL) { if (hp->h_charset && (errno == EILSEQ || errno == EINVAL)) { rewind(mtf); hp->h_charset = NULL; goto hloop; } if (ncs && *ncs && (errno == EILSEQ || errno == EINVAL)) { rewind(mtf); wantcharset = ncs; goto loop; } /* fprintf(stderr, ". . . message lost, sorry.\n"); */ perror(""); fail: senderr++; rewind(mtf); savedeadletter(mtf); fputs(catgets(catd, CATSET, 187, ". . . message not sent.\n"), stderr); return STOP; } mtf = nmtf; if (dosign) { if ((nmtf = smime_sign(mtf, hp)) == NULL) goto fail; Fclose(mtf); mtf = nmtf; } /* * Look through the recipient list for names with /'s * in them which we write to as files directly. */ to = outof(to, mtf, hp); if (senderr) savedeadletter(mtf); to = elide(to); if (count(to) == 0) { if (senderr == 0) ok = OKAY; goto out; } if (mightrecord(mtf, to, recipient_record) != OKAY) goto out; ok = transfer(to, hp->h_smopts, mtf, hp); out: Fclose(mtf); return ok; } /* * Create a Message-Id: header field. * Use either the host name or the from address. */ static void message_id(FILE *fo, struct header *hp) { char *cp; time_t now; time(&now); if ((cp = value("hostname")) != NULL) fprintf(fo, "Message-ID: <%lx.%s@%s>\n", (long)now, getrandstring(24), cp); else if ((cp = skin(myorigin(hp))) != NULL && strchr(cp, '@') != NULL) fprintf(fo, "Message-ID: <%lx.%s%%%s>\n", (long)now, getrandstring(16), cp); } static const char *weekday_names[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; const char *month_names[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL }; /* * Create a Date: header field. * We compare the localtime() and gmtime() results to get the timezone, * because numeric timezones are easier to read and because $TZ is * not set on most GNU systems. */ int mkdate(FILE *fo, const char *field) { time_t t; struct tm *tmptr; int tzdiff, tzdiff_hour, tzdiff_min; time(&t); tzdiff = t - mktime(gmtime(&t)); tzdiff_hour = (int)(tzdiff / 60); tzdiff_min = tzdiff_hour % 60; tzdiff_hour /= 60; tmptr = localtime(&t); if (tmptr->tm_isdst > 0) tzdiff_hour++; return fprintf(fo, "%s: %s, %02d %s %04d %02d:%02d:%02d %+03d%02d\n", field, weekday_names[tmptr->tm_wday], tmptr->tm_mday, month_names[tmptr->tm_mon], tmptr->tm_year + 1900, tmptr->tm_hour, tmptr->tm_min, tmptr->tm_sec, tzdiff_hour, tzdiff_min); } static enum okay putname(char *line, enum gfield w, enum sendaction action, int *gotcha, char *prefix, FILE *fo, struct name **xp) { struct name *np; np = sextract(line, GEXTRA|GFULL); if (xp) *xp = np; if (np == NULL) return 0; if (fmt(prefix, np, fo, w&(GCOMMA|GFILES), 0, action != SEND_TODISP)) return 1; if (gotcha) (*gotcha)++; return 0; } #define FMT_CC_AND_BCC { \ if (hp->h_cc != NULL && w & GCC) { \ if (fmt("Cc:", hp->h_cc, fo, \ w&(GCOMMA|GFILES), 0, \ action!=SEND_TODISP)) \ return 1; \ gotcha++; \ } \ if (hp->h_bcc != NULL && w & GBCC) { \ if (fmt("Bcc:", hp->h_bcc, fo, \ w&(GCOMMA|GFILES), 0, \ action!=SEND_TODISP)) \ return 1; \ gotcha++; \ } \ } /* * Dump the to, subject, cc header on the * passed file buffer. */ int puthead(struct header *hp, FILE *fo, enum gfield w, enum sendaction action, enum conversion convert, char *contenttype, char *charset) { int gotcha; char *addr/*, *cp*/; int stealthmua; struct name *np, *fromfield = NULL, *senderfield = NULL; if (value("stealthmua")) stealthmua = 1; else stealthmua = 0; gotcha = 0; if (w & GDATE) { mkdate(fo, "Date"), gotcha++; } if (w & GIDENT) { if (hp->h_from != NULL) { if (fmt("From:", hp->h_from, fo, w&(GCOMMA|GFILES), 0, action!=SEND_TODISP)) return 1; gotcha++; fromfield = hp->h_from; } else if ((addr = myaddrs(hp)) != NULL) if (putname(addr, w, action, &gotcha, "From:", fo, &fromfield)) return 1; if ((addr = hp->h_organization) != NULL || (addr = value("ORGANIZATION")) != NULL) { fwrite("Organization: ", sizeof (char), 14, fo); if (mime_write(addr, strlen(addr), fo, action == SEND_TODISP ? CONV_NONE:CONV_TOHDR, action == SEND_TODISP ? TD_ISPR|TD_ICONV:TD_ICONV, NULL, (size_t)0, NULL, NULL) == 0) return 1; gotcha++; putc('\n', fo); } if (hp->h_replyto != NULL) { if (fmt("Reply-To:", hp->h_replyto, fo, w&(GCOMMA|GFILES), 0, action!=SEND_TODISP)) return 1; gotcha++; } else if ((addr = value("replyto")) != NULL) if (putname(addr, w, action, &gotcha, "Reply-To:", fo, NULL)) return 1; if (hp->h_sender != NULL) { if (fmt("Sender:", hp->h_sender, fo, w&(GCOMMA|GFILES), 0, action!=SEND_TODISP)) return 1; gotcha++; senderfield = hp->h_sender; } else if ((addr = value("sender")) != NULL) if (putname(addr, w, action, &gotcha, "Sender:", fo, &senderfield)) return 1; if (check_from_and_sender(fromfield, senderfield)) return 1; } if (hp->h_to != NULL && w & GTO) { if (fmt("To:", hp->h_to, fo, w&(GCOMMA|GFILES), 0, action!=SEND_TODISP)) return 1; gotcha++; } if (value("bsdcompat") == NULL && value("bsdorder") == NULL) FMT_CC_AND_BCC if (hp->h_subject != NULL && w & GSUBJECT) { fwrite("Subject: ", sizeof (char), 9, fo); if (ascncasecmp(hp->h_subject, "re: ", 4) == 0) { fwrite("Re: ", sizeof (char), 4, fo); if (mime_write(hp->h_subject + 4, strlen(hp->h_subject + 4), fo, action == SEND_TODISP ? CONV_NONE:CONV_TOHDR, action == SEND_TODISP ? TD_ISPR|TD_ICONV:TD_ICONV, NULL, (size_t)0, NULL, NULL) == 0) return 1; } else if (*hp->h_subject) { if (mime_write(hp->h_subject, strlen(hp->h_subject), fo, action == SEND_TODISP ? CONV_NONE:CONV_TOHDR, action == SEND_TODISP ? TD_ISPR|TD_ICONV:TD_ICONV, NULL, (size_t)0, NULL, NULL) == 0) return 1; } gotcha++; fwrite("\n", sizeof (char), 1, fo); } if (value("bsdcompat") || value("bsdorder")) FMT_CC_AND_BCC if (w & GMSGID && stealthmua == 0) message_id(fo, hp), gotcha++; if (hp->h_ref != NULL && w & GREF) { fmt("References:", hp->h_ref, fo, 0, 1, 0); if ((np = hp->h_ref) != NULL && np->n_name) { while (np->n_flink) np = np->n_flink; if (mime_name_invalid(np->n_name, 0) == 0) { fprintf(fo, "In-Reply-To: %s\n", np->n_name); gotcha++; } } } if (w & GUA && stealthmua == 0) fprintf(fo, "User-Agent: Heirloom mailx %s\n", version), gotcha++; if (w & GMIME) { fputs("MIME-Version: 1.0\n", fo), gotcha++; if (hp->h_attach != NULL) { makeboundary(); fprintf(fo, "Content-Type: multipart/mixed;\n" " boundary=\"%s\"\n", send_boundary); } else { fprintf(fo, "Content-Type: %s", contenttype); if (charset) fprintf(fo, "; charset=%s", charset); fprintf(fo, "\nContent-Transfer-Encoding: %s\n", getencoding(convert)); } } if (gotcha && w & GNL) putc('\n', fo); return(0); } /* * Format the given header line to not exceed 72 characters. */ static int fmt(char *str, struct name *np, FILE *fo, int flags, int dropinvalid, int domime) { int col, len, count = 0; int is_to = 0, comma; comma = flags&GCOMMA ? 1 : 0; col = strlen(str); if (col) { fwrite(str, sizeof *str, strlen(str), fo); if ((flags&GFILES) == 0 && col == 3 && asccasecmp(str, "to:") == 0 || col == 3 && asccasecmp(str, "cc:") == 0 || col == 4 && asccasecmp(str, "bcc:") == 0 || col == 10 && asccasecmp(str, "Resent-To:") == 0) is_to = 1; } for (; np != NULL; np = np->n_flink) { if (is_to && is_fileaddr(np->n_name)) continue; if (np->n_flink == NULL) comma = 0; if (mime_name_invalid(np->n_name, !dropinvalid)) { if (dropinvalid) continue; else return 1; } len = strlen(np->n_fullname); col++; /* for the space */ if (count && col + len + comma > 72 && col > 1) { fputs("\n ", fo); col = 1; } else putc(' ', fo); len = mime_write(np->n_fullname, len, fo, domime?CONV_TOHDR_A:CONV_NONE, TD_ICONV, NULL, (size_t)0, NULL, NULL); if (comma && !(is_to && is_fileaddr(np->n_flink->n_name))) putc(',', fo); col += len + comma; count++; } putc('\n', fo); return 0; } /* * Rewrite a message for resending, adding the Resent-Headers. */ static int infix_resend(FILE *fi, FILE *fo, struct message *mp, struct name *to, int add_resent) { size_t count; char *buf = NULL, *cp/*, *cp2*/; size_t c, bufsize = 0; struct name *fromfield = NULL, *senderfield = NULL; count = mp->m_size; /* * Write the Resent-Fields. */ if (add_resent) { fputs("Resent-", fo); mkdate(fo, "Date"); if ((cp = myaddrs(NULL)) != NULL) { if (putname(cp, GCOMMA, SEND_MBOX, NULL, "Resent-From:", fo, &fromfield)) return 1; } if ((cp = value("sender")) != NULL) { if (putname(cp, GCOMMA, SEND_MBOX, NULL, "Resent-Sender:", fo, &senderfield)) return 1; } #ifdef notdef /* * RFC 2822 disallows generation of this field. */ cp = value("replyto"); if (cp != NULL) { if (mime_name_invalid(cp, 1)) { if (buf) free(buf); return 1; } fwrite("Resent-Reply-To: ", sizeof (char), 17, fo); mime_write(cp, strlen(cp), fo, CONV_TOHDR_A, TD_ICONV, NULL, (size_t)0, NULL, NULL); putc('\n', fo); } #endif /* notdef */ if (fmt("Resent-To:", to, fo, 1, 1, 0)) { if (buf) free(buf); return 1; } if (value("stealthmua") == NULL) { fputs("Resent-", fo); message_id(fo, NULL); } } if (check_from_and_sender(fromfield, senderfield)) return 1; /* * Write the original headers. */ while (count > 0) { if ((cp = foldergets(&buf, &bufsize, &count, &c, fi)) == NULL) break; if (ascncasecmp("status: ", buf, 8) != 0 && strncmp("From ", buf, 5) != 0) { fwrite(buf, sizeof *buf, c, fo); } if (count > 0 && *buf == '\n') break; } /* * Write the message body. */ while (count > 0) { if (foldergets(&buf, &bufsize, &count, &c, fi) == NULL) break; if (count == 0 && *buf == '\n') break; fwrite(buf, sizeof *buf, c, fo); } if (buf) free(buf); if (ferror(fo)) { perror(catgets(catd, CATSET, 188, "temporary mail file")); return 1; } return 0; } enum okay resend_msg(struct message *mp, struct name *to, int add_resent) { FILE *ibuf, *nfo, *nfi; char *tempMail; struct header head; enum okay ok = STOP; memset(&head, 0, sizeof head); if ((to = checkaddrs(to)) == NULL) { senderr++; return STOP; } if ((nfo = Ftemp(&tempMail, "Rs", "w", 0600, 1)) == NULL) { senderr++; perror(catgets(catd, CATSET, 189, "temporary mail file")); return STOP; } if ((nfi = Fopen(tempMail, "r")) == NULL) { senderr++; perror(tempMail); return STOP; } rm(tempMail); Ftfree(&tempMail); if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL) return STOP; head.h_to = to; to = fixhead(&head, to); if (infix_resend(ibuf, nfo, mp, head.h_to, add_resent) != 0) { senderr++; rewind(nfo); savedeadletter(nfi); fputs(catgets(catd, CATSET, 190, ". . . message not sent.\n"), stderr); Fclose(nfo); Fclose(nfi); return STOP; } fflush(nfo); rewind(nfo); Fclose(nfo); to = outof(to, nfi, &head); if (senderr) savedeadletter(nfi); to = elide(to); if (count(to) != 0) { if (value("record-resent") == NULL || mightrecord(nfi, to, 0) == OKAY) ok = transfer(to, head.h_smopts, nfi, NULL); } else if (senderr == 0) ok = OKAY; Fclose(nfi); return ok; }