static char rcsid[] = "@(#)$Id: fastmail.c,v 1.34 2006/04/09 07:37:30 hurtta Exp $";
/******************************************************************************
* The Elm (ME+) Mail System - $Revision: 1.34 $ $State: Exp $
*
* Modified by: Kari Hurtta <hurtta+elm@posti.FMI.FI>
* (was hurtta+elm@ozone.FMI.FI)
******************************************************************************
* The Elm Mail System
*
* Copyright (c) 1988-1992 USENET Community Trust
* Copyright (c) 1986,1987 Dave Taylor
*****************************************************************************/
/** This program is specifically written for group mailing lists and
such batch type mail processing. It does NOT use aliases at all,
it does NOT read the /etc/password file to find the From: name
of the user and does NOT expand any addresses. It is meant
purely as a front-end for either /bin/mail or /usr/lib/sendmail
(according to what is available on the current system).
**** This program should be used with CAUTION *****
**/
/** The calling sequence for this program is:
fastmail {args} [ filename | - ] full-email-address
where args could be any (or all) of;
-b bcc-list (Blind carbon copies to)
-c cc-list (carbon copies to)
-C comment-line (Comments:)
-d ... (debug on)
-f from (from name)
-F from-addr (the actual address to be put in the From: line)
-i msg-id (In-Reply-To: msgid)
-r reply-to-address (Reply-To:)
-R references (References:)
-P precedence (Precedence:)
-s subject (subject of message)
**/
#include "elmutil.h"
#include "mailerlib.h"
#include "s_fastmail.h"
#if 0
DEBUG_VAR(Debug,__FILE__,"util");
#endif
#define temphome "/tmp/fastmail."
static void usage P_((void));
static unsigned char *s2us P_((char *str));
static unsigned char *s2us(str)
char *str;
{
return (unsigned char *)str;
}
static void mail_sent P_((union any_fd fd, char * title, struct run_state *rs,
int ret, int exit_stat));
static void mail_sent (fd,title,rs,ret,exit_code)
union any_fd fd;
char * title;
struct run_state *rs;
int ret;
int exit_code;
{
printf("%s\n",title);
fflush(stdout);
if (ret < 0)
lib_error(CATGETS(elm_msg_cat, FastmailSet, FastmailFailSignal,
"%.30s fail: Signal?"),
get_mailer_path(fd.mail_fd));
else if (ret > 0) {
if (rs->save_errno)
lib_error(CATGETS(elm_msg_cat, FastmailSet, FastmailFailErrno,
"Failed: %.30s: %.40s"),
get_mailer_path(fd.mail_fd),
error_description(rs->save_errno));
else if (exit_code) {
lib_error(
CATGETS(elm_msg_cat, FastmailSet,
FastmailMailerReturnedError,
"mailer returned error status %d"),
exit_code);
} else
lib_error(CATGETS(elm_msg_cat, FastmailSet, FastmailMailSent,
"Mail sent!"));
} else {
lib_error(CATGETS(elm_msg_cat, FastmailSet, FastmailLostErrno,
"%.30s lost: %.40s"),
get_mailer_path(fd.mail_fd),
error_description(rs->save_errno));
}
}
static void mailing_message P_((int background));
static void mailing_message(background)
int background;
{
if (!background)
lib_transient(CATGETS(elm_msg_cat, FastmailSet, FastmailSendingMail,
"Sending mail..."));
else
lib_error(CATGETS(elm_msg_cat, FastmailSet,
FastmailSendingMailBackground,
"Sending mail... in background"));
}
static void simple_add_addr P_((struct expanded_address *expanded,
struct mailer_info *mailer_info,
char * addr,
charset_t defcharset,
int demime));
static void simple_add_addr(expanded,mailer_info,addr,defcharset,demime)
struct expanded_address *expanded;
struct mailer_info *mailer_info;
char * addr;
charset_t defcharset;
int demime;
{
struct addr_item * TMP;
int j;
TMP = break_down_address(addr,demime,defcharset);
for (j = 0; TMP[j].addr && TMP[j].fullname; j++) {
int pos;
struct string *s;
if (qstrpbrk(TMP[j].addr,"!:@") == NULL &&
mailer_info &&
query_mailer_info(mailer_info,MI_USE_DOMAIN)) {
TMP[j].addr = strmcat(TMP[j].addr,"@");
TMP[j].addr = strmcat(TMP[j].addr, hostfullname);
}
pos = add_expanded_addr_(expanded,
TMP[j].addr,
TMP[j].fullname,
TMP[j].comment);
s = make_surface_addr(TMP[j]);
add_textual_addr_(expanded,s,pos,1);
free_string(&s);
}
free_addr_items(TMP);
}
int main P_((int argc, char *argv[]));
int main(argc, argv)
int argc;
char *argv[];
{
extern char *optarg;
extern int optind;
struct mailing_headers mailing_headers;
char *p;
int c;
struct mail_send_state * M = NULL;
out_state_t * mailer = NULL;
FILE * F =NULL;
char ** addrs3 = NULL;
struct mailer_info *mailer_info;
struct string * comments = NULL;
struct string * from_string = NULL;
char references[LONG_STRING];
char * filename = NULL;
char title[STRING];
references[0] = '\0';
#if DEBUG
init_debugfile("FASTMAIL");
#endif
locale_init();
#if DEBUG
/* So that is is possible to initialize debugging before all other stuff */
if (argc > 1 && 0 == strncmp("-d",argv[1],2) &&
argv[1][2]) {
set_debugging(argv[1]+2);
}
#endif
zero_mailing_headers(&mailing_headers);
user_init();
init_defaults();
init_mailerlib();
read_rc_file(0);
mailer_info = get_mailer_info();
if (!mailer_info)
exit(1);
/* free_mailing_headers will free this */
if (!mailing_headers.env_from)
mailing_headers.env_from = mailer_get_env_from(mailer_info);
/* Read .mailheaders for Return-Path (for envelope sender actually)
and user deifined headers
*/
import_mailheaders(&mailing_headers);
/* FIXME: what is correct character set? */
if ((p = getenv("REPLYTO")) != NULL)
simple_add_addr(&mailing_headers.reply_to,
mailer_info,
p,
display_charset,
!allow_no_hdrencoding);
/* auto bcc line */
if ((p = getenv("BCC")) != NULL)
simple_add_addr(&mailing_headers.bcc,
mailer_info,
p,
display_charset,
!allow_no_hdrencoding);
while ((c = getopt(argc, argv, "b:c:C:d:f:F:i:r:R:P:s:")) != EOF) {
switch (c) {
case 'b' :
simple_add_addr(&mailing_headers.bcc,mailer_info,
optarg,system_charset,
!allow_no_hdrencoding);
break;
case 'c' :
simple_add_addr(&mailing_headers.cc,mailer_info,
optarg,system_charset,
!allow_no_hdrencoding);
break;
case 'C' :
if ( comments )
usage();
comments = hdr_to_string(HDR_TEXT,optarg,system_charset,
!allow_no_hdrencoding);
break;
case 'd' :
#if DEBUG
set_debugging(optarg);
#else
lib_error(CATGETS(elm_msg_cat, FastmailSet,
FastmailIngoringDebug,
"Warning: system created without debugging enabled - request ignored\n"));
#endif
break;
case 'f' :
from_string = hdr_to_string(HDR_TEXT,optarg,system_charset,
!allow_no_hdrencoding);
break;
case 'F' :
simple_add_addr(&mailing_headers.from,mailer_info,
optarg,system_charset,
!allow_no_hdrencoding);
break;
case 'i' :
if (mailing_headers.in_reply_to.id ||
mailing_headers.in_reply_to.text)
usage();
{
char * X = safe_strdup(optarg);
char * p = X;
char * p1 = X;
while (whitespace(*p))
p++;
if ('<' == *p) {
char c;
p1 = qstrpbrk(p,">");
if (!p1) {
/* ERROR */
usage();
goto free_it;
}
p1++;
c = *p1;
*p1 = '\0';
mailing_headers.in_reply_to.id =
strmcpy(mailing_headers.in_reply_to.id,p);
*p1 = c;
}
if (p1 && p1[0]) {
if (!add_irt_phrase) {
/* TODO: Warning:
In-reply-to header should include only message-id
*/
}
mailing_headers.in_reply_to.text = hdr_to_string(HDR_TEXT,p1,
display_charset,
!allow_no_hdrencoding);
}
free_it:
free(X);
}
break;
case 'r' :
simple_add_addr(&mailing_headers.reply_to,mailer_info,
optarg,system_charset,
!allow_no_hdrencoding);
break;
case 'R' :
/* TODO: Fix this */
strfcpy(references, optarg,
sizeof references);
break;
case 'P' :
if (mailing_headers.precedence)
usage();
mailing_headers.precedence = safe_strdup(optarg);
break;
case 's' :
if (mailing_headers.subject)
usage();
mailing_headers.subject = hdr_to_string(HDR_TEXT,optarg,system_charset,
!allow_no_hdrencoding);
break;
case '?' :
usage();
}
}
if (from_string) {
if (mailing_headers.from.addrs_len > 0) {
int i;
for (i = 0; i < mailing_headers.from.addrs_len; i++) {
if (!mailing_headers.from.addrs[i].fullname)
mailing_headers.from.addrs[i].fullname = dup_string(from_string);
}
} else {
struct string * comment = new_string(display_charset);
struct string * s1 = NULL;
char *s = safe_strdup(username); /* Use username as surface addr */
char *x;
char * Q = from_addr_literal(mailer_info);
int pos = add_expanded_addr_(&mailing_headers.from,Q,from_string,comment);
free(Q); Q = NULL;
/* No fancy quotation stuff on textual form for editing */
for (x = s; *x; x++) {
switch(*x) {
case '"': case '\\': case '(': case ')': case ':':
case '<': case '>':
*x = '_';
break;
}
}
s1 = new_string2(display_charset,s2us(s));
add_textual_addr_(&mailing_headers.from,s1,pos,1);
free(s); s = NULL;
free_string(&s1);
free_string(&comment);
}
}
elm_sfprintf(version_buff, sizeof version_buff,
FRM("%s PL%s"), VERSION, PATCHLEVEL);
#ifdef DEBUG
{
int d = panic_dprint("\n\
======================================================\n\
Debug output of the FASTMAIL program (version %s).\n",
version_buff);
if (d >= 50) {
#if 0
panic_dprint("WARNING: Edit manually out sensitive information from that file!\n");
lower_prompt("WARNING: Debug file may include passwords -- edit it!");
sleep(5+sleepmsg);
#endif
}
}
#endif
if (optind >= argc) {
usage();
}
filename = argv[optind++];
if (optind >= argc) {
usage();
}
if (0 != strcmp(filename, "-")) {
if (access(filename, READ_ACCESS) == -1) {
lib_error(FRM("Error: can't find file %s!\n"),
filename);
exit(1);
}
}
while (optind < argc) {
simple_add_addr(&mailing_headers.to,mailer_info,
argv[optind++],system_charset,
!allow_no_hdrencoding);
}
addrs3 = argv_from_headers(&mailing_headers);
M = mailer_init(addrs3,0,0,mailer_info,
mailing_headers.env_from );
free(addrs3); addrs3 = NULL;
if (!M) {
exit(1);
}
mailer = get_mail_outfd(M);
/** Subject must appear even if "null" and must be first
at top of headers for mail because the
pure System V.3 mailer, in its infinite wisdom, now
assumes that anything the user sends is part of the
message body unless either:
1. the "-s" flag is used (although it doesn't seem
to be supported on all implementations?)
2. the first line is "Subject:". If so, then it'll
read until a blank line and assume all are meant
to be headers.
So the gory solution here is to move the Subject: line
up to the top. I assume it won't break anyone elses program
or anything anyway (besides, RFC-822 specifies that the *order*
of headers is irrelevant). Gahhhhh....
If we have been configured for a smart mailer then we don't want
to add a from line. If the user has specified one then we have
to honor their wishes. If they've just given a 'from name' then
we'll just put in the username and hope the mailer can add the
correct domain in.
**/
if (mailing_headers.subject)
write_string_header(mailer,"Subject",
mailing_headers.subject,
ENCODING_NONE /* TODO: FIX ME */,
!allow_no_hdrencoding,
display_charset /* What is correct charset? */
);
if (mailing_headers.from.addrs)
write_addr_header(mailer,"From",mailing_headers.from.addrs,
ENCODING_NONE /* TODO: FIX ME */,
!allow_no_hdrencoding,
display_charset /* What is correct charset? */
);
write_text_header(mailer,"Date",get_arpa_date(),
ENCODING_NONE /* TODO: FIX ME */
);
elm_sfprintf(title, sizeof title,
CATGETS(elm_msg_cat, FastmailSet, FastmailMailTo,
"Mail to %.50s..."),
mailing_headers.to.addrs ? mailing_headers.to.addrs[0].addr :
"(someone)");
if (mailing_headers.reply_to.addrs)
write_addr_header(mailer,"Reply-To",mailing_headers.reply_to.addrs,
ENCODING_NONE /* TODO: FIX ME */,
!allow_no_hdrencoding,
display_charset /* What is correct charset? */
);
if (mailing_headers.cc.addrs)
write_addr_header(mailer,"CC",mailing_headers.cc.addrs,
ENCODING_NONE /* TODO: FIX ME */,
!allow_no_hdrencoding,
display_charset /* What is correct charset? */
);
if (references[0])
write_text_header(mailer,"References",references,
ENCODING_NONE /* TODO: FIX ME */);
if (mailing_headers.precedence)
write_text_header(mailer,"Precedence",mailing_headers.precedence,
ENCODING_NONE /* TODO: FIX ME */);
if (mailing_headers.user_header_count > 0) {
int i;
for (i = 0; i < mailing_headers.user_header_count; i++) {
write_string_header(mailer,
give_header_name(mailing_headers.user_header[i].
name),
mailing_headers.user_header[i].value,
ENCODING_NONE /* TODO: FIX ME */,
!allow_no_hdrencoding,
display_charset /* What is correct charset? */
);
}
}
write_id_phrase_header(mailer,"In-Reply-To",
& (mailing_headers.in_reply_to),
ENCODING_NONE /* TODO: FIX ME */,
!allow_no_hdrencoding,
display_charset /* What is correct charset? */
);
if (comments)
write_string_header(mailer,"Comments",
comments,
ENCODING_NONE /* TODO: FIX ME */,
!allow_no_hdrencoding,
display_charset /* What is correct charset? */
);
#ifndef NO_XHEADER
state_printf(mailer, FRM("X-Mailer: fastmail [version %s PL%s]\n"),
VERSION, PATCHLEVEL);
#endif /* !NO_XHEADER */
print_EOLN(mailer,ENCODING_NONE /* TODO: FIX ME */);
if (0 == strcmp(filename, "-")) {
F = stdin;
} else
F = fopen(filename,"r");
if (F) {
char buffer[VERY_LONG_STRING];
int len;
while ((len = mail_gets (buffer, sizeof buffer, F)) > 0) {
/* FIXME: Should
state_convert_EOLN(buffer,&len,sizeof buffer,T);
called ?
*/
state_put(buffer, len, mailer);
}
} else {
lib_error(FRM("Error: can't find file %s!\n"),
filename);
}
if (F != stdin)
fclose(F);
mail_backend2(&M,mail_sent,
0,title,mailing_message);
free_mailer_info(&mailer_info);
free_mailing_headers(&mailing_headers);
/* FIXME */
exit(0);
/*NOTREACHED*/
}
static void usage()
{
lib_error(CATGETS(elm_msg_cat, FastmailSet, FastmailUsage,
"Usage: fastmail {args} [ filename | - ] address(es)\n\
where {args} can be;\n\
\t-b bcc-list\n\t-c cc-list\n\t-d ...\n\
\t-C comments\n\t-f from-name\n\t-F from-addr\n\
\t-i msg-id\n\t-r reply-to\n\t-R references\n\
\t-P precedence\n\t-s subject\n\n"));
exit(1);
}
/*
* Local Variables:
* mode:c
* c-basic-offset:4
* buffer-file-coding-system: iso-8859-1
* End:
*/
syntax highlighted by Code2HTML, v. 0.9.1