blob: 6c455833a76b1779a8eb19e4e8353d9d6c00ef00 [file] [log] [blame]
#include "common.h"
#include "send.h"
/* globals to all files */
int rmail;
char *thissys, *altthissys;
int nflg;
int xflg;
int debug;
int rflg;
int iflg = 1;
int nosummary;
/* global to this file */
static String *errstring;
static message *mp;
static int interrupt;
static int savemail;
static Biobuf in;
static int forked;
static int add822headers = 1;
static String *arglist;
/* predeclared */
static int send(dest *, message *, int);
static void lesstedious(void);
static void save_mail(message *);
static int complain_mail(dest *, message *);
static int pipe_mail(dest *, message *);
static void appaddr(String *, dest *);
static void mkerrstring(String *, message *, dest *, dest *, char *, int);
static int replymsg(String *, message *, dest *);
static int catchint(void*, char*);
void
usage(void)
{
fprint(2, "usage: mail [-birtx] list-of-addresses\n");
exits("usage");
}
void
main(int argc, char *argv[])
{
dest *dp=0;
int checkforward;
char *base;
int rv;
/* process args */
ARGBEGIN{
case '#':
nflg = 1;
break;
case 'b':
add822headers = 0;
break;
case 'x':
nflg = 1;
xflg = 1;
break;
case 'd':
debug = 1;
break;
case 'i':
iflg = 0;
break;
case 'r':
rflg = 1;
break;
default:
usage();
}ARGEND
while(*argv){
if(shellchars(*argv)){
fprint(2, "illegal characters in destination\n");
exits("syntax");
}
d_insert(&dp, d_new(s_copy(*argv++)));
}
if (dp == 0)
usage();
arglist = d_to(dp);
/*
* get context:
* - whether we're rmail or mail
*/
base = basename(argv0);
checkforward = rmail = (strcmp(base, "rmail")==0) | rflg;
thissys = sysname_read();
altthissys = alt_sysname_read();
if(rmail)
add822headers = 0;
/*
* read the mail. If an interrupt occurs while reading, save in
* dead.letter
*/
if (!nflg) {
Binit(&in, 0, OREAD);
if(!rmail)
atnotify(catchint, 1);
mp = m_read(&in, rmail, !iflg);
if (mp == 0)
exit(0);
if (interrupt != 0) {
save_mail(mp);
exit(1);
}
} else {
mp = m_new();
if(default_from(mp) < 0){
fprint(2, "%s: can't determine login name\n", argv0);
exit(1);
}
}
errstring = s_new();
getrules();
/*
* If this is a gateway, translate the sender address into a local
* address. This only happens if mail to the local address is
* forwarded to the sender.
*/
gateway(mp);
/*
* Protect against shell characters in the sender name for
* security reasons.
*/
mp->sender = escapespecial(mp->sender);
if (shellchars(s_to_c(mp->sender)))
mp->replyaddr = s_copy("postmaster");
else
mp->replyaddr = s_clone(mp->sender);
/*
* reject messages that have been looping for too long
*/
if(mp->received > 32)
exit(refuse(dp, mp, "possible forward loop", 0, 0));
/*
* reject messages that are too long. We don't do it earlier
* in m_read since we haven't set up enough things yet.
*/
if(mp->size < 0)
exit(refuse(dp, mp, "message too long", 0, 0));
rv = send(dp, mp, checkforward);
if(savemail)
save_mail(mp);
if(mp)
m_free(mp);
exit(rv);
}
/* send a message to a list of sites */
static int
send(dest *destp, message *mp, int checkforward)
{
dest *dp; /* destination being acted upon */
dest *bound; /* bound destinations */
int errors=0;
/* bind the destinations to actions */
bound = up_bind(destp, mp, checkforward);
if(add822headers && mp->haveto == 0){
if(nosummary)
mp->to = d_to(bound);
else
mp->to = arglist;
}
/* loop through and execute commands */
for (dp = d_rm(&bound); dp != 0; dp = d_rm(&bound)) {
switch (dp->status) {
case d_cat:
errors += cat_mail(dp, mp);
break;
case d_pipeto:
case d_pipe:
if (!rmail && !nflg && !forked) {
forked = 1;
lesstedious();
}
errors += pipe_mail(dp, mp);
break;
default:
errors += complain_mail(dp, mp);
break;
}
}
return errors;
}
/* avoid user tedium (as Mike Lesk said in a previous version) */
static void
lesstedious(void)
{
int i;
if(debug)
return;
switch(fork()){
case -1:
break;
case 0:
sysdetach();
for(i=0; i<3; i++)
close(i);
savemail = 0;
break;
default:
exit(0);
}
}
/* save the mail */
static void
save_mail(message *mp)
{
Biobuf *fp;
String *file;
file = s_new();
deadletter(file);
fp = sysopen(s_to_c(file), "cAt", 0660);
if (fp == 0)
return;
m_bprint(mp, fp);
sysclose(fp);
fprint(2, "saved in %s\n", s_to_c(file));
s_free(file);
}
/* remember the interrupt happened */
static int
catchint(void *a, char *msg)
{
USED(a);
if(strstr(msg, "interrupt") || strstr(msg, "hangup")) {
interrupt = 1;
return 1;
}
return 0;
}
/* dispose of incorrect addresses */
static int
complain_mail(dest *dp, message *mp)
{
char *msg;
switch (dp->status) {
case d_undefined:
msg = "Invalid address"; /* a little different, for debugging */
break;
case d_syntax:
msg = "invalid address";
break;
case d_unknown:
msg = "unknown user";
break;
case d_eloop:
case d_loop:
msg = "forwarding loop";
break;
case d_noforward:
if(dp->pstat && *s_to_c(dp->repl2))
return refuse(dp, mp, s_to_c(dp->repl2), dp->pstat, 0);
else
msg = "destination unknown or forwarding disallowed";
break;
case d_pipe:
msg = "broken pipe";
break;
case d_cat:
msg = "broken cat";
break;
case d_translate:
if(dp->pstat && *s_to_c(dp->repl2))
return refuse(dp, mp, s_to_c(dp->repl2), dp->pstat, 0);
else
msg = "name translation failed";
break;
case d_alias:
msg = "broken alias";
break;
case d_badmbox:
msg = "corrupted mailbox";
break;
case d_resource:
return refuse(dp, mp, "out of some resource. Try again later.", 0, 1);
default:
msg = "unknown d_";
break;
}
if (nflg) {
print("%s: %s\n", msg, s_to_c(dp->addr));
return 0;
}
return refuse(dp, mp, msg, 0, 0);
}
/* dispose of remote addresses */
static int
pipe_mail(dest *dp, message *mp)
{
dest *next, *list=0;
String *cmd;
process *pp;
int status;
char *none;
String *errstring=s_new();
if (dp->status == d_pipeto)
none = "none";
else
none = 0;
/*
* collect the arguments
*/
next = d_rm_same(&dp);
if(xflg)
cmd = s_new();
else
cmd = s_clone(next->repl1);
for(; next != 0; next = d_rm_same(&dp)){
if(xflg){
s_append(cmd, s_to_c(next->addr));
s_append(cmd, "\n");
} else {
if (next->repl2 != 0) {
s_append(cmd, " ");
s_append(cmd, s_to_c(next->repl2));
}
}
d_insert(&list, next);
}
if (nflg) {
if(xflg)
print("%s", s_to_c(cmd));
else
print("%s\n", s_to_c(cmd));
s_free(cmd);
return 0;
}
/*
* run the process
*/
pp = proc_start(s_to_c(cmd), instream(), 0, outstream(), 1, none);
if(pp==0 || pp->std[0]==0 || pp->std[2]==0)
return refuse(list, mp, "out of processes, pipes, or memory", 0, 1);
pipesig(0);
m_print(mp, pp->std[0]->fp, thissys, 0);
pipesigoff();
stream_free(pp->std[0]);
pp->std[0] = 0;
while(s_read_line(pp->std[2]->fp, errstring))
;
status = proc_wait(pp);
proc_free(pp);
s_free(cmd);
/*
* return status
*/
if (status != 0)
return refuse(list, mp, s_to_c(errstring), status, 0);
loglist(list, mp, "remote");
return 0;
}
static void
appaddr(String *sp, dest *dp)
{
dest *parent;
String *s;
if (dp->parent != 0) {
for(parent=dp->parent; parent->parent!=0; parent=parent->parent)
;
s = unescapespecial(s_clone(parent->addr));
s_append(sp, s_to_c(s));
s_free(s);
s_append(sp, "' alias `");
}
s = unescapespecial(s_clone(dp->addr));
s_append(sp, s_to_c(s));
s_free(s);
}
/*
* reject delivery
*
* returns 0 - if mail has been disposed of
* other - if mail has not been disposed
*/
int
refuse(dest *list, message *mp, char *cp, int status, int outofresources)
{
String *errstring=s_new();
dest *dp;
int rv;
dp = d_rm(&list);
mkerrstring(errstring, mp, dp, list, cp, status);
/*
* log first in case we get into trouble
*/
logrefusal(dp, mp, s_to_c(errstring));
/*
* bulk mail is never replied to, if we're out of resources,
* let the sender try again
*/
if(rmail){
/* accept it or request a retry */
if(outofresources){
fprint(2, "Mail %s\n", s_to_c(errstring));
rv = 1; /* try again later */
} else if(mp->bulk)
rv = 0; /* silently discard bulk */
else
rv = replymsg(errstring, mp, dp); /* try later if we can't reply */
} else {
/* aysnchronous delivery only happens if !rmail */
if(forked){
/*
* if spun off for asynchronous delivery, we own the mail now.
* return it or dump it on the floor. rv really doesn't matter.
*/
rv = 0;
if(!outofresources && !mp->bulk)
replymsg(errstring, mp, dp);
} else {
fprint(2, "Mail %s\n", s_to_c(errstring));
savemail = 1;
rv = 1;
}
}
s_free(errstring);
return rv;
}
/* make the error message */
static void
mkerrstring(String *errstring, message *mp, dest *dp, dest *list, char *cp, int status)
{
dest *next;
char smsg[64];
String *sender;
sender = unescapespecial(s_clone(mp->sender));
/* list all aliases */
s_append(errstring, " from '");
s_append(errstring, s_to_c(sender));
s_append(errstring, "'\nto '");
appaddr(errstring, dp);
for(next = d_rm(&list); next != 0; next = d_rm(&list)) {
s_append(errstring, "'\nand '");
appaddr(errstring, next);
d_insert(&dp, next);
}
s_append(errstring, "'\nfailed with error '");
s_append(errstring, cp);
s_append(errstring, "'.\n");
/* >> and | deserve different flavored messages */
switch(dp->status) {
case d_pipe:
s_append(errstring, "The mailer `");
s_append(errstring, s_to_c(dp->repl1));
sprint(smsg, "' returned error status %x.\n\n", status);
s_append(errstring, smsg);
break;
}
s_free(sender);
}
/*
* create a new boundary
*/
static String*
mkboundary(void)
{
char buf[32];
int i;
static int already;
if(already == 0){
srand((time(0)<<16)|getpid());
already = 1;
}
strcpy(buf, "upas-");
for(i = 5; i < sizeof(buf)-1; i++)
buf[i] = 'a' + nrand(26);
buf[i] = 0;
return s_copy(buf);
}
/*
* reply with up to 1024 characters of the
* original message
*/
static int
replymsg(String *errstring, message *mp, dest *dp)
{
message *refp = m_new();
dest *ndp;
char *rcvr;
int rv;
String *boundary;
boundary = mkboundary();
refp->bulk = 1;
refp->rfc822headers = 1;
rcvr = dp->status==d_eloop ? "postmaster" : s_to_c(mp->replyaddr);
ndp = d_new(s_copy(rcvr));
s_append(refp->sender, "postmaster");
s_append(refp->replyaddr, "/dev/null");
s_append(refp->date, thedate());
refp->haveto = 1;
s_append(refp->body, "To: ");
s_append(refp->body, rcvr);
s_append(refp->body, "\n");
s_append(refp->body, "Subject: bounced mail\n");
s_append(refp->body, "MIME-Version: 1.0\n");
s_append(refp->body, "Content-Type: multipart/mixed;\n");
s_append(refp->body, "\tboundary=\"");
s_append(refp->body, s_to_c(boundary));
s_append(refp->body, "\"\n");
s_append(refp->body, "Content-Disposition: inline\n");
s_append(refp->body, "\n");
s_append(refp->body, "This is a multi-part message in MIME format.\n");
s_append(refp->body, "--");
s_append(refp->body, s_to_c(boundary));
s_append(refp->body, "\n");
s_append(refp->body, "Content-Disposition: inline\n");
s_append(refp->body, "Content-Type: text/plain; charset=\"US-ASCII\"\n");
s_append(refp->body, "Content-Transfer-Encoding: 7bit\n");
s_append(refp->body, "\n");
s_append(refp->body, "The attached mail");
s_append(refp->body, s_to_c(errstring));
s_append(refp->body, "--");
s_append(refp->body, s_to_c(boundary));
s_append(refp->body, "\n");
s_append(refp->body, "Content-Type: message/rfc822\n");
s_append(refp->body, "Content-Disposition: inline\n\n");
s_append(refp->body, s_to_c(mp->body));
s_append(refp->body, "--");
s_append(refp->body, s_to_c(boundary));
s_append(refp->body, "--\n");
refp->size = s_len(refp->body);
rv = send(ndp, refp, 0);
m_free(refp);
d_free(ndp);
return rv;
}