/*
* 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 <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#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;
}
syntax highlighted by Code2HTML, v. 0.9.1