/*
* Copyright 1988 by Rayan S. Zachariassen, all rights reserved.
* This will be free software, but only when it is finished.
* Some functions Copyright 1991-2001 Matti Aarnio.
*/
/*
* The routines in this file implement various C-coded functions that
* are callable from the configuration file.
*/
#include "mailer.h"
#include <stdio.h>
#include <sys/types.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <ctype.h>
#include <fcntl.h>
#include <sys/file.h> /* O_RDONLY for run_praliases() */
#include <pwd.h> /* for run_homedir() */
#include <grp.h> /* for run_grpmems() */
#include <errno.h>
#include <sysexits.h>
#include "zmsignal.h"
#include "zsyslog.h"
#include "mail.h"
#include "interpret.h"
#include "io.h"
#include "libz.h"
#include "libc.h"
#include "prototypes.h"
#ifndef _IOFBF
#define _IOFBF 0
#endif /* !_IOFBF */
extern conscell *s_value;
/* The builtin functions are declared and initialized here. */
#define ARGCV __((int argc, const char *argv[]))
static int run_hostname ARGCV;
static int run_whataddress ARGCV;
static int run_iserrormsg ARGCV;
static int run_erraddrlog ARGCV;
static conscell *run_dblookup __((conscell *avl, conscell *il));
extern conscell *sh_car __((conscell *avl, conscell *il));
static conscell *run_cadr __((conscell *avl, conscell *il));
static conscell *run_caddr __((conscell *avl, conscell *il));
static conscell *run_cadddr __((conscell *avl, conscell *il));
static conscell *run_listexpand __((conscell *avl, conscell *il));
#if 0
static conscell *run_newattribute __((conscell *avl, conscell *il));
#endif
extern int run_doit ARGCV;
extern int run_stability ARGCV;
extern int run_process ARGCV;
static int run_grpmems ARGCV;
#if 0 /* dead code */
static int run_praliases ARGCV;
#endif
static int zap_DSN_notify ARGCV;
static int post_zap_DSN_notify ARGCV;
static int run_homedir ARGCV;
static int run_822date ARGCV;
static int run_filepriv ARGCV;
static int run_runas ARGCV;
static int run_cat ARGCV;
static int run_gensym ARGCV;
static int run_uid2login ARGCV;
static int run_login2uid ARGCV;
static int run_basename ARGCV;
static int run_recase ARGCV;
static int run_squirrel ARGCV;
static int run_822syntax ARGCV;
static int run_condquote ARGCV;
static int run_dequote ARGCV;
static int run_syslog ARGCV;
#if defined(XMEM) && defined(CSRIMALLOC)
static int run_malcontents ARGCV;
#endif /* CSRIMALLOC */
extern const char *traps[];
extern int nobody;
extern time_t time __((time_t *));
extern int routerdirloops;
#ifndef strchr
extern char *strchr(), *strrchr();
#endif
struct shCmd fnctns[] = {
{ "relation", run_relation, NULL, NULL, 0 },
{ DBLOOKUPNAME, NULL, run_dblookup, NULL, SH_ARGV },
/* The following are optional but are probably a good idea */
{ "db", run_db, NULL, NULL, 0 },
{ "trace", run_trace, NULL, NULL, 0 },
{ "untrace", run_trace, NULL, NULL, 0 },
{ "hostname", run_hostname, NULL, NULL, 0 },
{ "iserrormsg", run_iserrormsg, NULL, NULL, 0 },
{ "sender", run_whataddress,NULL, NULL, 0 },
{ "recipient", run_whataddress,NULL, NULL, 0 },
{ "erraddron", run_erraddrlog, NULL, NULL, 0 },
{ "channel", NULL, sh_car, NULL, SH_ARGV },
{ "host", NULL, run_cadr, NULL, SH_ARGV },
{ "user", NULL, run_caddr, NULL, SH_ARGV },
{ "attributes", NULL, run_cadddr, NULL, SH_ARGV },
{ "stability", run_stability, NULL, NULL, 0 },
{ "stableprocess", run_doit, NULL, NULL, 0 },
{ "daemon", run_daemon, NULL, NULL, 0 },
{ "process", run_process, NULL, NULL, 0 },
{ "rfc822", run_rfc822, NULL, NULL, 0 },
{ "groupmembers", run_grpmems, NULL, NULL, 0 },
#if 0
{ "printaliases", run_praliases, NULL, NULL, 0 },
#endif
{ "zapDSNnotify", zap_DSN_notify, NULL, NULL, SH_ARGV },
{ "postzapDSNnotify", post_zap_DSN_notify, NULL, NULL, SH_ARGV },
{ "listexpand", NULL, run_listexpand, NULL, SH_ARGV },
#if 0
{ "newattribute", NULL, run_newattribute, NULL, SH_ARGV },
#endif
{ "homedirectory",run_homedir, NULL, NULL, 0 },
{ "rfc822date", run_822date, NULL, NULL, 0 },
{ "filepriv", run_filepriv, NULL, NULL, SH_ARGV },
{ "runas", run_runas, NULL, NULL, SH_ARGV },
{ "cat", run_cat, NULL, NULL, SH_ARGV },
{ "gensym", run_gensym, NULL, NULL, 0 },
{ "uid2login", run_uid2login, NULL, NULL, 0 },
{ "login2uid", run_login2uid, NULL, NULL, 0 },
{ "basename", run_basename, NULL, NULL, 0 },
{ "recase", run_recase, NULL, NULL, 0 },
{ "squirrel", run_squirrel, NULL, NULL, 0 },
{ "rfc822syntax", run_822syntax, NULL, NULL, 0 },
{ "dequote", run_dequote, NULL, NULL, 0 },
{ "condquote", run_condquote, NULL, NULL, 0 },
{ "syslog", run_syslog, NULL, NULL, 0 },
{ "logger", run_syslog, NULL, NULL, 0 },
#if defined(XMEM) && defined(CSRIMALLOC)
{ "malcontents", run_malcontents,NULL, NULL, 0 },
#endif /* CSRIMALLOC */
/* The rest have been added locally */
{ NULL, NULL, NULL, NULL, 0 }
};
int funclevel = 0;
int D_sequencer = 0;
int D_hdr_rewrite = 0;
int D_router = 0;
int D_functions = 0;
int D_compare = 0;
int D_matched = 0;
int D_assign = 0;
int D_final = 0;
int D_db = 0;
int D_alias = 0;
int D_bind = 0;
int D_resolv = 0;
int D_alloc = 0;
int D_regnarrate = 0;
extern int D_rfc822;
static struct debugind {
const char *name;
int *indicator;
} buggers[] = {
{ "assign", &D_assign },
{ "bind", &D_bind },
{ "compare", &D_compare },
{ "db", &D_db },
{ "except", 0 },
{ "final", &D_final },
{ "functions", &D_functions },
{ "matched", &D_matched },
{ "memory", &D_alloc },
{ "on", &D_functions }, /* dup */
{ "regexp", &D_regnarrate },
{ "resolv", &D_resolv },
{ "rewrite", &D_hdr_rewrite },
{ "rfc822", &D_rfc822 },
{ "router", &D_router },
{ "sequencer", &D_sequencer },
{ NULL, 0 }
};
/* The builtin trace function. This is also used by command line debug specs */
int
run_trace(argc, argv)
int argc;
const char *argv[];
{
struct debugind *dbi;
int debug;
const char *prog;
int rc = 0;
if (argc == 1) {
fprintf(stderr, "Usage: %s all", argv[0]);
for (dbi = &buggers[0]; dbi->name != NULL; ++dbi)
fprintf(stderr, "|%s", dbi->name);
putc('\n', stderr);
return EX_USAGE;
}
prog = argv[0];
debug = (strncmp(*argv, "un", 2) != 0);
while (--argc > 0) {
++argv;
if (STREQ(*argv, "off") || STREQ(*argv, "all")) {
for (dbi = &buggers[0]; dbi->name != NULL; ++dbi)
if (dbi->indicator)
*(dbi->indicator) = (**argv == (debug?'a':'o'));
continue;
} else {
for (dbi = &buggers[0]; dbi->name != NULL; ++dbi) {
if (STREQ(*argv, dbi->name)) {
if (dbi->indicator == NULL)
debug = !debug; /* except */
else
*(dbi->indicator) = debug;
break;
}
}
}
if (dbi->name == NULL) {
fprintf(stderr, "%s: unknown attribute: %s\n",
prog, *argv);
rc = EX_USAGE;
}
}
return rc;
}
int gensym;
const char * const gs_name = "g%d";
static int
run_gensym(argc, argv)
int argc;
const char *argv[];
{
printf(gs_name, gensym++);
putchar('\n');
return 0;
}
extern void free_gensym __((void));
void
free_gensym()
{
int i;
char buf[30];
for (i=0; i<gensym; ++i) {
sprintf(buf, gs_name, i);
v_purge(buf);
}
}
/* hostname -- get system hostname or set my idea of the hostname */
static int
run_hostname(argc, argv)
int argc;
const char *argv[];
{
static char hostname[128];
if (argc > 1) {
memtypes oval = stickymem;
stickymem = MEM_MALLOC;
myhostname = strdup(argv[1]);
stickymem = oval;
} else {
/* can't fail... */
getmyhostname(hostname, sizeof hostname);
printf("%s\n", hostname);
}
return 0;
}
/*
* senderaddress/recipientaddress -- find out whether the current header
* address being rewritten is a sender or a recipient address
*/
static int
run_whataddress(argc, argv)
int argc;
const char *argv[];
{
static int toggle = 0;
if (isSenderAddr == isRecpntAddr /* == 0 */) {
fprintf(stderr,
"Should not call '%s' outside header address rewriting function!\n",
argv[0]);
toggle = !toggle;
return toggle; /* pseudorandom :-) */
} else if (argc > 1)
fprintf(stderr, "Usage: %s\n", argv[0]);
if (argv[0][0] == 's') /* called as 'senderaddress' */
return isSenderAddr ? 0 : 1;
return isRecpntAddr ? 0 : 1;
}
/*
* $(iserrormsg dummyargs) returns a flag
*/
static int
run_iserrormsg(argc, argv)
int argc;
const char *argv[];
{
return !isErrorMsg;
}
/*
* this is like accton(), but for logging errors in addresses.
*/
char *erraddrlog;
static int
run_erraddrlog(argc, argv)
int argc;
const char *argv[];
{
switch (argc) {
case 1:
if (erraddrlog)
free(erraddrlog);
erraddrlog = NULL;
break;
case 2:
erraddrlog = smalloc(MEM_PERM, strlen(argv[1])+1);
strcpy(erraddrlog, argv[1]);
break;
default:
fprintf(stderr, "Usage: %s [ /path ]\n", argv[0]);
return EX_USAGE;
}
return 0;
}
/*
* Interface to databases; the relation function arranges to attach this
* function to all database function definitions. It is called as
* database key
* and is expected to act like a normal function (i.e. print value on stdout).
*/
static conscell *
run_dblookup(avl, il)
conscell *avl, *il; /* Inputs gc protected */
{
conscell *l;
const char *argv30[30];
int i;
memset(argv30, 0, sizeof(argv30));
il = cdar(avl);
if (il == NULL || !STRING(il)) {
fprintf(stderr, "Usage: %s key [up_to_19_substitution_elements_or_options]\n", car(avl)->string);
return NULL;
}
i = 0;
for (; il && i < 30-1 && STRING(il); il = cdr(il))
argv30[i++] = il->string;
l = dblookup(car(avl)->string, i, argv30);
if (l == NULL)
return NULL;
return l;
}
static conscell *
run_cadr(avl, il)
conscell *avl, *il; /* Inputs gc protected */
{
il = cdar(avl);
if (il == NULL || STRING(il) || car(il) == NULL)
return NULL;
/* cdr */
car(il) = cdar(il);
/* car */
car(il) = copycell(car(il)); /* don't modify malloc'ed memory! */
cdar(il) = NULL;
return car(il);
}
static conscell *
run_caddr(avl, il)
conscell *avl, *il; /* Inputs gc protected */
{
il = cdar(avl);
if (il == NULL || STRING(il) || car(il) == NULL)
return NULL;
/* cdr */
car(il) = cdar(il);
/* cdr */
car(il) = cdar(il);
/* car */
/* setf preparation */
if (car(il) == NULL) {
return il;
}
car(il) = copycell(car(il)); /* don't modify malloc'ed memory! */
cdar(il) = NULL;
return car(il);
}
static conscell *
run_cadddr(avl, il)
conscell *avl, *il; /* Inputs gc protected */
{
il = cdar(avl);
if (il == NULL || STRING(il) || car(il) == NULL)
return NULL;
/* cdr */
car(il) = cdar(il);
/* cdr */
car(il) = cdar(il);
/* cdr */
car(il) = cdar(il);
/* car */
/* setf preparation */
if (car(il) == NULL) {
return il;
}
car(il) = copycell(car(il)); /* don't modify malloc'ed memory! */
cdar(il) = NULL;
return car(il);
}
/*
* Print a list of the members of a group.
*/
static int
run_grpmems(argc, argv)
int argc;
const char *argv[];
{
const char **cpp;
struct Zgroup *grp;
if (argc != 2) {
fprintf(stderr, "Usage: %s groupname\n", argv[0]);
return EX_USAGE;
}
grp = zgetgrnam(argv[1]);
if (grp == NULL) {
fprintf(stderr, "%s: no group '%s'\n", argv[0], argv[1]);
return 1;
}
for (cpp = grp->gr_mem; *cpp != NULL ; ++cpp)
printf("%s\n", *cpp);
endgrent();
return 0;
}
static struct headerinfo aliashdr = {
"aliases", AddressList, Recipient, normal
};
#if 0 /* dead code, 'zmailer newdb' does these differently... */
static int
run_praliases(argc, argv)
int argc;
const char *argv[];
{
register struct header *h;
struct envelope *e;
const char *cp;
int errflg, count, status;
long offset, size, prevsize, maxsize;
FILE *indexfp;
int c, verbose;
const char *indexfile;
char buf[8192], ibuf[BUFSIZ];
struct siobuf *osiop = NULL;
int tabsep = 0;
verbose = 0;
indexfile = NULL;
zoptind = 1;
errflg = 0;
while (1) {
c = zgetopt(argc, (char*const*)argv, "vo:t");
if (c == EOF)
break;
switch (c) {
case 'v':
++verbose;
break;
case 'o':
indexfile = zoptarg;
break;
case 't':
tabsep = 1;
break;
default:
++errflg;
break;
}
}
if (errflg || zoptind != argc - 1) {
fprintf(stderr,
"Usage: %s [ -v ] [ -o indexoutputfile ] aliasfile\n",
argv[0]);
return EX_USAGE;
}
e = (struct envelope *)tmalloc(sizeof (struct envelope));
if ((e->e_fp = fopen(argv[zoptind], "r")) == NULL) {
c = errno;
fprintf(stderr, "%s: open(\"%s\"): ", argv[0], argv[zoptind]);
errno = c;
perror("");
status = PERR_BADOPEN;
} else {
setvbuf(e->e_fp, buf, _IOFBF, sizeof buf);
osiop = siofds[FILENO(e->e_fp)];
siofds[FILENO(e->e_fp)] = NULL;
e->e_file = argv[zoptind];
status = makeLetter(e, 1); /* Parse the aliases database
as if all entries were of
same syntax as "To:" et.al.
on the normal email header*/
siofds[FILENO(e->e_fp)] = osiop;
fclose(e->e_fp);
e->e_fp = NULL;
}
if (status != 0) {
fprintf(stderr, "%s: format error!\n", argv[0]);
return 2;
}
for (h = e->e_headers; h != NULL; h = h->h_next) {
h->h_descriptor = &aliashdr;
h->h_contents = hdr_scanparse(e, h, 1, 0);
h->h_stamp = hdr_type(h);
if (h->h_stamp == BadHeader) {
hdr_errprint(e, h, stderr, "alias expansion");
++errflg;
} else if (h->h_contents.a == NULL) {
fprintf(stderr, "%s: null alias '%s'\n", argv[0],
h->h_pname);
++errflg;
}
}
if (errflg) {
fprintf(stderr,
"%s: aborted after detecting %d syntax error%s\n",
argv[0], errflg, errflg == 1 ? "" : "s");
return 3;
}
/* store all aliases in canonical lowercase */
for (h = e->e_headers, count = 0; h != NULL; h = h->h_next, ++count) {
strlower((char*)h->h_pname);
}
if (count <= 0) {
fprintf(stderr, "%s: no aliases found!\n", argv[0]);
return 4;
}
if (indexfile != NULL) {
if ((indexfp = fopen(indexfile, "w")) == NULL) {
c = errno;
fprintf(stderr, "%s: open(\"%s\"): ",
argv[0], indexfile);
errno = c;
perror("");
return 5;
}
setvbuf(indexfp, ibuf, _IOFBF, sizeof ibuf);
osiop = siofds[FILENO(indexfp)];
siofds[FILENO(indexfp)] = NULL;
} else
indexfp = NULL;
maxsize = size = 0;
cp = ":"; /* anything that can't be an alias */
for (h = e->e_headers ; h != NULL ; h = h->h_next) {
offset = 2 + strlen(h->h_pname);
/* offset += offset >= 7 ? 1 : 8 - offset%8; */
prevsize = size;
size = ftell(stdout);
if (size - prevsize > maxsize)
maxsize = size - prevsize;
if (*cp == *(h->h_pname) && STREQ(cp, h->h_pname)) {
fprintf(stderr, "%s: multiple definitions of '%s'.\n",
argv[0], h->h_pname);
cp = ":";
}
prevsize = size + offset; /* prevsize is convenient scratch */
if (indexfp != NULL) {
if (fprintf(indexfp, "%s\t%ld\n",
h->h_pname, prevsize) == EOF)
++errflg;
if (errflg)
break;
}
cp = h->h_pname;
hdr_print(h, stdout);
}
if (verbose) {
prevsize = size;
size = ftell(stdout);
if (size - prevsize > maxsize)
maxsize = size - prevsize;
fprintf(stderr,
"%d aliases, longest %ld bytes, %ld bytes total\n",
count, maxsize, size);
}
if (fflush(stdout) == EOF)
++errflg;
if (indexfp != NULL) {
siofds[FILENO(indexfp)] = osiop;
if (fclose(indexfp) == EOF)
++errflg;
}
if (errflg)
fprintf(stderr, "%s: I/O error while writing output!\n",
argv[0]);
return errflg;
}
#endif /* ... dead code */
#ifdef DEBUG_FOPEN
void
report_fds(fp)
FILE *fp;
{
int fdnro;
int lastfd = getdtablesize();
fd_set fdset;
struct timeval tv;
extern int errno;
fprintf(fp,"File handles: %d/",lastfd);
for (fdnro = 0; fdnro < lastfd; ++fdnro) {
char ss[4];
char *s = ss;
*s = 0;
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO(&fdset);
FD_SET(fdnro,&fdset);
errno = 0;
if (select(lastfd,&fdset,NULL,NULL,&tv) != -1)
*s++ = 'r';
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO(&fdset);
FD_SET(fdnro,&fdset);
if (select(lastfd,NULL,&fdset,NULL,&tv) != -1)
*s++ = 'w';
*s = 0;
if (ss[0] != 0)
fprintf(fp," %d%s",fdnro,ss);
}
fprintf(fp,"\n");
fflush(fp);
}
#endif
/*
* zap_DSN_notify()
*
* To be called immediately after a new nattr value has been generated,
* and thus a new storage variable for it exists!
*
* scrubbedset=$(zapDSNnotify attrlistvarname [diagname finalsuccessrcpt])
*
+
+ Damn... The issues here are rather complicated.
+ These routines will be suspended for a while pending further
+ analysis. [mea/10-sept-2000]
+
*/
static int zap_DSN_notify(argc, argv)
int argc;
const char *argv[];
{
#if 1
return 0;
#else
conscell *l, *lc, **pl;
conscell *l1, *tmp;
int notifysuccess = 0;
int notifytrace = 0;
int len;
char *s;
const char *onam = argv[1];
const char *diagname = argv[2];
const char *finalsuccessrcpt = argv[3];
const char *diagaddr = argv[4];
if (!diagname) finalsuccessrcpt = NULL;
if (!finalsuccessrcpt) diagaddr = NULL;
l1 = v_find(onam);
if (!l1) return 0;
l = cdr(l1);
lc = l1 = NULL;
pl = &car(l);
l = *pl;
for (lc = l; lc && cdr(lc); pl = &cddr(lc),lc = *pl) {
if (!STRING(lc))
return 0; /* ?? */
if (STREQ("DSN",lc->string)) {
lc = cdr(lc);
if (!lc || !STRING(lc))
return 0;
break;
}
}
if (!lc) return 0; /* No DSN data */
s = lc->string;
if (D_sequencer)
fprintf(stderr," zapDSNnotify('%s') DSN='%s' -> ", onam, s);
while (*s) {
if (CISTREQN(s,"NOTIFY=",7)) {
char *p = s+7;
/* While non-blank (parameter) string */
while (*p && *p != ' ' && *p != '\t') {
if (*p == ',') {
++p;
continue;
}
if (CISTREQN(p,"SUCCESS",7)) {
notifysuccess=1;
if (p[7] == ',')
strcpy(p,p+8); /* ZAP the "SUCCESS," status! */
else
strcpy(p,p+7); /* ZAP the "SUCCESS" status! */
continue;
}
if (CISTREQN(p,"TRACE",5)) {
notifytrace = 1;
if (p[5] == ',')
strcpy(p,p+6); /* ZAP the "TRACE," status! */
else
strcpy(p,p+5); /* ZAP the "TRACE" status! */
continue;
}
++p;
}
/* Ok, Now we may have e.g.:
i1: NOTIFY=NEVER
o1: NOTIFY=NEVER
i2: NOTIFY=SUCCESS,DELAY,FAILURE
o2: NOTIFY=DELAY,FAILURE
i3: NOTIFY=DELAY,SUCCESS,FAILURE
o3: NOTIFY=DELAY,FAILURE
i4: NOTIFY=DELAY,FAILURE,SUCCESS
o4: NOTIFY=DELAY,FAILURE,
i5: NOTIFY=SUCCESS
o5: NOTIFY=
of which the o4 needs trailing comma cleanup,
and o5 zapping of the entire NOTIFY parameter. */
if (p > s && p[-1] == ',') /* Case 4 */
p[-1] = ' '; /* An extra comma to zap */
if (p > s && p[-1] == '=') /* Case 5 */ {
while (*p == ' ' || *p == '\t') ++p;
strcpy(s, p); /* That the entire NOTIFY= parameter */
continue;
}
/* Skip over trailing whitespace */
while (*p == ' ' || *p == '\t') ++p;
s = p;
continue; /* This is done, restart */
} /* end of if ("NOTIFY=") */
/* Nothing recognized, scan over the non-blank string */
while (*s && *s != ' ' && *s != '\t') ++s;
/* And possible trailing whitespace */
while ( *s == ' ' || *s == '\t') ++s;
}
if (D_sequencer)
fprintf(stderr,"'%s' rc=%d\n", lc->string, notifysuccess);
if (!notifysuccess && !notifytrace) return 0; /* We are DONE! */
/* Now depending are we handling a mailing-list expansion, or
an alias expansion (RFC 1891, 6.2.7.*) we either report
"delivered" to the list input address, OR "expanded"
for aliases and .forwards (single recipient ones should
*not* report anything but rewrite the recipient address,
but we slip at that..) */
s = lc->string;
len = strlen(s);
if (diagname) len += strlen(diagname)+2;
if (finalsuccessrcpt) len += strlen(finalsuccessrcpt)+2;
if (diagaddr) len += strlen(diagaddr)+2;
s = (char *)realloc(s, len+23);
if (!s) return -1; /* Hardly happens, but... */
lc->string = s;
s += len;
if (notifysuccess && notifytrace)
strcpy(s, " NTRACE=SUCCESS,TRACE");
else if (notifysuccess)
strcpy(s, " NTRACE=SUCCESS");
else if (notifytrace)
strcpy(s, " NTRACE=TRACE");
if (notifysuccess) {
s += strlen(s);
if (diagname) sprintf(s,";%s", diagname);
s += strlen(s);
if (finalsuccessrcpt) sprintf(s,";%s", finalsuccessrcpt);
s += strlen(s);
if (diagaddr) sprintf(s,";%s", diagaddr);
}
return notifysuccess;
#endif
}
/*
* post_zap_DSN_notify()
*
* To be called immediately after a new nattr value has been generated,
* and thus a new storage variable for it exists!
*
* $(postzapDSNnotify chainvarname)
*
* If the variable called chainvarname has more than one address
* entry -- ( ((x x x x)) ((x x x x)) ) -- then we leave this NTRACE=
* data to be as is. If the variable has only ONE address ( ((x x x x)) )
* AND the ...............
+
+ Damn... The issues here are rather complicated.
+ These routines will be suspended for a while pending further
+ analysis. [mea/10-sept-2000]
+
*/
static int post_zap_DSN_notify(argc, argv)
int argc;
const char *argv[];
{
#if 1
return 0;
#else
conscell *l, *lc, **pl;
conscell *l1, *tmp, *l0;
int notifysuccess = 0;
int notifytrace = 0;
int len;
char *s;
const char *onam = argv[1];
l0 = v_find(onam);
if (!l0) return 0;
l = cdr(l1);
lc = l1 = NULL;
pl = &car(l);
l = *pl;
for (lc = l; lc && cdr(lc); pl = &cddr(lc),lc = *pl) {
if (!STRING(lc))
return 0; /* ?? */
if (STREQ("DSN",lc->string)) {
lc = cdr(lc);
if (!lc || !STRING(lc))
return 0;
break;
}
}
if (!lc) return 0; /* No DSN data */
s = lc->string;
if (D_sequencer)
fprintf(stderr," postzapDSNnotify('%s') DSN='%s' -> ", onam, s);
while (*s) {
if (CISTREQN(s,"NOTIFY=",7)) {
char *p = s+7;
/* While non-blank (parameter) string */
while (*p && *p != ' ' && *p != '\t') {
if (*p == ',') {
++p;
continue;
}
if (CISTREQN(p,"SUCCESS",7)) {
notifysuccess=1;
if (p[7] == ',')
strcpy(p,p+8); /* ZAP the "SUCCESS," status! */
else
strcpy(p,p+7); /* ZAP the "SUCCESS" status! */
continue;
}
if (CISTREQN(p,"TRACE",5)) {
notifytrace = 1;
if (p[5] == ',')
strcpy(p,p+6); /* ZAP the "TRACE," status! */
else
strcpy(p,p+5); /* ZAP the "TRACE" status! */
continue;
}
++p;
}
/* Ok, Now we may have e.g.:
i1: NOTIFY=NEVER
o1: NOTIFY=NEVER
i2: NOTIFY=SUCCESS,DELAY,FAILURE
o2: NOTIFY=DELAY,FAILURE
i3: NOTIFY=DELAY,SUCCESS,FAILURE
o3: NOTIFY=DELAY,FAILURE
i4: NOTIFY=DELAY,FAILURE,SUCCESS
o4: NOTIFY=DELAY,FAILURE,
i5: NOTIFY=SUCCESS
o5: NOTIFY=
of which the o4 needs trailing comma cleanup,
and o5 zapping of the entire NOTIFY parameter. */
if (p > s && p[-1] == ',') /* Case 4 */
p[-1] = ' '; /* An extra comma to zap */
if (p > s && p[-1] == '=') /* Case 5 */ {
while (*p == ' ' || *p == '\t') ++p;
strcpy(s, p); /* That the entire NOTIFY= parameter */
continue;
}
/* Skip over trailing whitespace */
while (*p == ' ' || *p == '\t') ++p;
s = p;
continue; /* This is done, restart */
} /* end of if ("NOTIFY=") */
/* Nothing recognized, scan over the non-blank string */
while (*s && *s != ' ' && *s != '\t') ++s;
/* And possible trailing whitespace */
while ( *s == ' ' || *s == '\t') ++s;
}
if (D_sequencer)
fprintf(stderr,"'%s' rc=%d\n", lc->string, notifysuccess);
if (!notifysuccess && !notifytrace) return 0; /* We are DONE! */
/* Now depending are we handling a mailing-list expansion, or
an alias expansion (RFC 1894, 6.2.7.*) we either report
"delivered" to the list input address, OR "expanded"
for aliases and .forwards (single recipient ones should
*not* report anything but rewrite the recipient address,
but we slip at that..) */
s = lc->string;
len = strlen(s);
s = (char *)realloc(s, len+23);
if (!s) return -1; /* Hardly happens, but... */
lc->string = s;
s += len;
if (notifysuccess && notifytrace)
strcpy(s, " NTRACE=SUCCESS,TRACE");
else if (notifysuccess)
strcpy(s, " NTRACE=SUCCESS");
else if (notifytrace)
strcpy(s, " NTRACE=TRACE");
return notifysuccess;
#endif
}
/* listexpand() -- do a bunch of tasks usually done with a group of
functions, starting with "listaddresses" ... */
static conscell *
run_listexpand(avl, il)
conscell *avl, *il;
{
struct header hs;
struct envelope *e;
struct address *ap, *aroot = NULL, **atail = &aroot;
token822 *t;
conscell *al = NULL, *alp = NULL, *tmp = NULL;
conscell *plustail = NULL, *domain = NULL;
conscell *l, *lrc;
char *localpart = NULL, *origaddr = NULL, *attributenam = NULL;
int c, n, errflag, stuff;
volatile int cnt;
const char *comment, *erroraddress;
char *s, *s_end;
int privilege = -1;
struct siobuf *osiop = NULL;
FILE *mfp = NULL, *fp;
char buf[4096];
char DSNbuf[4096+200];
int fd2;
char *olderrors = errors_to;
char *notary = NULL;
int no_dsn = 0;
int okaddresses = 0;
int errcount = 0;
int linecnt = 0;
int euid = geteuid();
GCVARS3;
il = cdar(avl); /* The CDR (next) of the arg list.. */
errflag = 0;
erroraddress = NULL;
comment = "list";
zoptind = 1;
while (il != NULL && STRING(il) && il->string[0] == '-' &&
cdr(il) != NULL && STRING(cdr(il))) {
switch( il->string[1] ) {
case 'c':
comment = (char*)cdr(il)->string;
if (strchr(comment,'\n') || strchr(comment,'\r'))
errflag = 1;
break;
case 'p':
privilege = atoi((char*)cdr(il)->string);
break;
case 'e':
erroraddress = (char*)cdr(il)->string;
if (strchr(erroraddress,'\n') || strchr(erroraddress,'\r'))
errflag = 1;
break;
case 'E':
if (errors_to != olderrors)
free(errors_to);
errors_to = strdup((char*)cdr(il)->string);
if (strchr(errors_to,'\n') || strchr(errors_to,'\r'))
errflag = 1;
break;
case 'N':
notary = (char *)cdr(il)->string;
if (strchr(notary,'\n') || strchr(notary,'\r'))
errflag = 1;
if (STREQ(notary,"-")) no_dsn = 1;
break;
default:
errflag = 1;
break;
}
il = cddr(il); /* Skip TWO entries at the time! */
}
if (privilege < 0)
privilege = nobody;
cnt = 0;
tmp = il;
for (; tmp != NULL; tmp = cdr(tmp)) ++cnt; /* Count arguments.. */
if (errflag || cnt < 3 || cnt > 5 ||
!STRING(il) || !STRING(cdr(il)) || !STRING(cddr(il)) ) {
fprintf(stderr,
"Usage: %s [ -e error-address ] [ -E errors-to-address ] [-p privilege] [ -c comment ] [ -N notarystring ] $attribute $localpart $origaddr [$plustail [$domain]] [ [<] /file/path ]\n",
car(avl)->string);
if (errors_to != olderrors)
free(errors_to);
errors_to = olderrors;
return NULL;
}
attributenam = (char*) (il)->string;
localpart = (char*) cdr(il)->string;
origaddr = (char*) cddr(il)->string;
if (cdr(cddr(il))) {
plustail = cdr(cddr(il));
if (cddr(cddr(il)))
domain = cddr(cddr(il));
}
/* We (memory-)leak this stuff for a moment.. (but it is tmalloc()ed)*/
e = (struct envelope *)tmalloc(sizeof (struct envelope));
e->e_nowtime = now;
e->e_file = (char*) car(avl)->string;
/* we only need sensible `e' if its a time stamp header,
which it ain't, so "listaddress" is just fine for e_file .. */
hs.h_descriptor = &aliashdr;
hs.h_pname = (char*) car(avl)->string;
initzline(4096);
/*
* These hoops are so we can do this for both real stdin and
* the fake I/O stuff in ../libsh.
*/
if ((fp = fdopen(0, "r")) == NULL) {
fprintf(stderr, "%s: fdopen failed\n", car(avl)->string);
#ifdef DEBUG_FOPEN
report_fds(stderr);
#endif
if (errors_to != olderrors)
free(errors_to);
errors_to = olderrors;
return NULL;
}
fd2 = dup(0); /* Copy the file handle for later returning
AFTER an fclose() has closed the primary
copy.. */
/* use a constant buffer to avoid memory leaks from stdio usage */
setvbuf(fp, buf, _IOFBF, sizeof buf);
c = 0;
while ((n = zgetline(fp)) > 0) {
++linecnt;
/*
* For sendmail compatibility, addresses may not cross line
* boundaries, and line boundary is just as good an address
* separator as comma is, sigh.
* Also lines beginning with '#' are comments, and blank
* lines are (sortof) comments too.
*/
if (zlinebuf[n-1] == '\n')
--n;
if (zlinebuf[0] == '#')
continue;
stuff = 0;
s_end = & zlinebuf[n];
for (s = zlinebuf; s < s_end; ++s) {
if (isascii(*s) && !isspace(*s))
c = stuff = 1;
if (*s == ',')
c = 0;
}
if (!stuff)
continue;
if (c) {
zlinebuf[n] = ',';
++n;
}
/* #ifdef SENDMAIL_COMPABILITY_KLUDGE */
/**
* Additional sendmail compability kludge ++haa
* If address starts with \, zap the \ character.
* This is just to not to force people to edit their
* .forwards :-( "\haa" works as expected :-)
**/
if (zlinebuf[0] == '\\') zlinebuf[0] = ' ';
for (s = zlinebuf+1; s < s_end; ++s) {
if (s[-1] == ',' && s[0]=='\\') s[0] = ' ';
}
/* end of sendmail compatibility kluge */
/* #endif */
/* create h->h_lines */
/*
* It is best to maintain a line at a time as tokens,
* so errors will print out nicely.
*/
hs.h_lines = t = makeToken(zlinebuf, n);
t->t_type = Line;
/* fix up any trailing comma (more sendmail
compatibility kluges) */
s = (char*)t->t_pname + TOKENLEN(t)-1;
if (c && (*s == ',' || *s == '\n'))
*s = '\0';
hs.h_contents = hdr_scanparse(e, &hs, 1, 1);
hs.h_stamp = hdr_type(&hs);
if (hs.h_stamp == BadHeader || hs.h_contents.a == NULL) {
++errcount;
if (hs.h_stamp == BadHeader) {
if (erroraddress != NULL) {
if (!isErrChannel && !isErrorMsg) {
if (mfp == NULL) {
/* We are likely running under 'runas ..'
Do reset now to ROOT, then to TRUSTED, ... */
runasrootuser();
runastrusteduser();
mfp = mail_open(MSG_RFC822);
runasrootuser();
if (mfp != NULL) {
osiop = siofds[FILENO(mfp)];
siofds[FILENO(mfp)] = NULL;
fprintf(mfp, "channel error\n");
fprintf(mfp, "errormsg\n");
fprintf(mfp, "to <%s>\n", erroraddress);
fprintf(mfp, "to <postoffice>\n");
fprintf(mfp, "env-end\n");
fprintf(mfp, "From: Error Channel <MAILER-DAEMON>\n");
fprintf(mfp, "To: %s\n", erroraddress);
fprintf(mfp, "Subject: Error in %s\n", comment);
fprintf(mfp, "Precedence: junk\n\n");
/* Print the report: */
fprintf(mfp,"Input file line number %d:\n",linecnt);
hdr_errprint(e, &hs, mfp, comment);
}
} else { /* mfp != NULL */
fprintf(mfp,"Input file line number %d:\n",linecnt);
hdr_errprint(e, &hs, mfp, comment);
}
}
#if 0
if (errcount == 1) /* At the first time only! */
printf("%s\n", erroraddress);
#endif
}
fprintf(stderr,"Input file line number %d:\n",linecnt);
hdr_errprint(e, &hs, stderr, comment);
} else { /* if (hs.h_contents.a == NULL) */
#if 0 /* Hmmm... We CAN have empty input lines! */
if (errcount == 1) {
/* Print only for the first intance.. */
if (erroraddress != NULL)
printf("%s\n", erroraddress);
fprintf(stderr, "listexpand: null input on line %d of STDIN\n", linecnt);
}
#endif
}
continue;
}
*atail = hs.h_contents.a;
while (*atail != NULL)
atail = &((*atail)->a_next);
++okaddresses;
}
fclose(fp); /* Now we discard the stdio buffers, but not the
fd number 0! Actually we use a copy of it.. */
dup2(fd2,0); /* Return the fd to descriptor 0.. */
close(fd2); /* .. and discard the backup copy.. */
if (okaddresses == 0 && !isErrChannel && !isErrorMsg) {
/* If the file is empty, use error address ...
Except when this is already an error mesage, say nothing! */
if (erroraddress == NULL)
erroraddress = "postmaster";
hs.h_lines = t = makeToken(erroraddress, strlen(erroraddress));
t->t_type = Line;
/* fix up any trailing comma (more sendmail
compatibility kluges) */
s = (char*)t->t_pname + TOKENLEN(t)-1;
if (c && (*s == ',' || *s == '\n'))
*s = '\0';
hs.h_contents = hdr_scanparse(e, &hs, 1, 1);
hs.h_stamp = hdr_type(&hs);
if (hs.h_stamp == BadHeader || hs.h_contents.a == NULL) {
/* OUTCH! Even the "erroraddress" parameter is illegal! */
fprintf(stderr,"listexpand: input parameters bad, empty input, even 'erroraddress' bad!\n");
} else {
*atail = hs.h_contents.a;
while (*atail != NULL)
atail = &((*atail)->a_next);
}
if (mfp == NULL) {
/* We are likely running under 'runas ..'
Do reset now to ROOT, then to TRUSTED, ... */
runasrootuser();
runastrusteduser();
mfp = mail_open(MSG_RFC822);
runasrootuser();
if (mfp != NULL) {
osiop = siofds[FILENO(mfp)];
siofds[FILENO(mfp)] = NULL;
fprintf(mfp, "channel error\n");
fprintf(mfp, "errormsg\n");
fprintf(mfp, "to <%s>\n", erroraddress);
fprintf(mfp, "to <postoffice>\n");
fprintf(mfp, "env-end\n");
fprintf(mfp, "From: Error Channel <MAILER-DAEMON>\n");
fprintf(mfp, "To: %s\n", erroraddress);
fprintf(mfp, "Subject: Error in %s\n", comment);
fprintf(mfp, "Precedence: junk\n\n");
/* Print the report: */
fprintf(mfp,"NO valid recipient addresses!\n");
fprintf(mfp,"Verify source file protection/ownership/access-path, and content.\n");
fprintf(mfp,"Current effective UID = %d\n", euid);
}
} else { /* mfp != NULL */
fprintf(mfp,"\nNO valid recipient addresses!\n");
fprintf(mfp,"Verify source file protection/ownership/access-path, and content.\n");
fprintf(mfp,"Current effective UID = %d\n", euid);
}
}
if (mfp != NULL) {
siofds[FILENO(mfp)] = osiop;
mail_close(mfp);
}
cnt = 0;
al = l = lrc = NULL;
GCPRO3(al, l, lrc);
for (ap = aroot; ap != NULL; ap = ap->a_next) {
int rc, slen;
memtypes omem;
char *s2, *se;
struct addr *pp;
for (pp = ap->a_tokens; pp != NULL; pp = pp->p_next)
if (pp->p_type == anAddress)
break;
if (pp == NULL)
continue;
buf[0] = 0;
pureAddressBuf(buf,sizeof(buf),pp);
if (buf[0] == 0) continue; /* Burp ??? */
#define cdddr(x) cdr(cddr(x))
#define use_lapply 1
#ifdef use_lapply
if (!no_dsn) {
*DSNbuf = 0;
if (notary != NULL)
strncpy(DSNbuf,notary,sizeof(DSNbuf)-30);
DSNbuf[sizeof(DSNbuf)-30] = 0; /* Be brutal, and chop
off the tail, if it is
too long.. */
s2 = strlen(DSNbuf)+DSNbuf;
if (s2 > DSNbuf)
strcpy(s2++," ");
strcpy(s2,"ORCPT=rfc822;");
s2 += strlen(s2);
se = DSNbuf + sizeof(DSNbuf)-1; /* BUF-end */
s = buf;
while (*s) {
c = *s;
if ('!' <= c && c <= '~' && c != '+' && c != '=') {
if (s2 < se)
*s2++ = c;
} else if (s2 < se) {
sprintf(s2,"+%02X",c);
s2 += 3;
}
++s;
}
*s2 = 0;
s = newattribute_2(attributenam,"DSN",DSNbuf);
} else {
s = attributenam;
}
omem = stickymem;
/* stickymem = MEM_MALLOC; */
/* The set of parameters for the rrouter() script
function are:
- address
- origaddress
- Attribute variable name
- plustail
- domain
(The last two were added in June-1998)
*/
slen = strlen(buf);
l = newstring(dupnstr(buf, slen), slen);
slen = strlen(origaddr);
cdr(l) = newstring(dupnstr(origaddr, slen), slen);
slen = strlen(s);
cddr(l) = newstring(dupnstr(s, slen), slen);
if (plustail != NULL) {
cdr(cddr(l)) = s_copy_chain(plustail);
if (domain != NULL)
cddr(cddr(l)) = s_copy_chain(domain);
}
l = ncons(l);
stickymem = omem;
deferit = 0;
v_set(DEFER, "");
rc = l_apply("rrouter", l);
lrc = s_value;
s_value = NULL;
omem = stickymem;
/* stickymem = MEM_MALLOC;
s_free_tree(l); */ /* We can clean up the input list */
stickymem = omem;
#else
lrc = router(ap, privilege, "recipient");
rc = (lrc == NULL);
#endif
#if 0 /* XX: This HOLD handling didn't work ?? */
if (deferit && (d = v_find(DEFER))) {
/* s_free_tree(lrc); */
lrc = NULL;
l = conststring("hold", 4);
cdr(l) = copycell(cdr(d));
slen = strlen(buf);
cddr(l) = newstring(dupnstr(buf, slen), slen);
cdddr(l) = car(attributes);
l = ncons(l);
l = ncons(l);
} else
;
#endif
if (rc != 0 || lrc == NULL || !LIST(lrc)) {
/* $(rrouter xx xx xx) returned something invalid.. */
#ifdef use_lapply
/* s_free_tree(lrc); */
#endif
lrc = NULL;
continue;
} else {
/*
* We expect router to either return
* (local - user attributes) or (((local - user attributes)))
* or ( ((local - user attr)) ((local - another attr)) )
*/
if (car(lrc) == NULL) {
/* duplicate removal trapped it. Empty list! */
continue;
}
if (LIST(car(lrc))) {
if (!LIST(caar(lrc)) || !STRING(caaar(lrc))) {
fprintf(stderr,
"%s: '%s' returned invalid 2-level list: ",
progname, ROUTER);
s_grind(lrc, stderr);
#ifdef use_lapply
/* s_free_tree(lrc); */
#endif
lrc = NULL;
if (errors_to != olderrors)
free(errors_to);
errors_to = olderrors;
UNGCPRO3;
return NULL;
}
l = s_copy_chain(lrc);
} else {
l = s_copy_chain(lrc);
l = ncons(l);
l = ncons(l);
}
#ifdef use_lapply
/* s_free_tree(lrc); */
#endif
lrc = NULL;
}
/* Now the "(conscell *) l" contains a route,
it is a time to put them together.. */
/* l -> ( ((chan param param2 attrs)) ) */
if (al == NULL) {
al = l; /* The head anchor */
alp = car(al); /* address list END node ptr */
while (cdr(alp) != NULL) {
alp = cdr(alp);
++cnt;
}
} else {
/*
(
((chan param param2 attrs))
((chan param param2 attrs))
)
*/
cdr(alp) = car(l); /* Attach the new list to the old */
while (cdr(alp) != NULL) {
alp = cdr(alp); /* .. and traverse to its end ... */
++cnt;
}
}
/* Debugging.. */
/* buf[len] = '\n';
buf[len+1] = 0;
fwrite(buf,1,len+1,stdout); */
}
#if 0
if (al == NULL) { /* ERROR! NO ADDRESSES! */
int slen;
al = conststring("error", 5);
cdr(al) = conststring("expansion", 9);
slen = strlen(localpart);
cddr(al) = newstring(dupnstr(localpart, slen), slen);
al = ncons(al);
al = ncons(al);
}
#endif
UNGCPRO3;
if (errors_to != olderrors)
free(errors_to);
errors_to = olderrors;
return al;
} /* end-of: run_listexpand() */
static int
run_homedir(argc, argv)
int argc;
const char *argv[];
{
struct Zpasswd *pw;
char *b;
int err;
if (argc != 2) {
fprintf(stderr, "Usage: %s name\n", argv[0]);
return EX_USAGE;
}
pw = zgetpwnam(argv[1]);
err = errno;
if (pw == NULL) {
strlower((char*)argv[1]);
pw = zgetpwnam(argv[1]);
err = errno;
if (pw == NULL) {
if (err == 0) return 2;
++deferit;
b = malloc(strlen(argv[1])+10);
sprintf(b, "HOME:%s", argv[1]);
v_set(DEFER, b);
free(b);
return 3;
}
}
printf("%s\n", pw->pw_dir);
return 0;
}
static int
run_822date(argc, argv)
int argc;
const char *argv[];
{
time_t dnow;
time(&dnow);
if (argc == 2 && STREQ(argv[1], "-s"))
printf("%ld\n", dnow);
else
printf("%s", rfc822date(&dnow));
return 0;
}
static int
run_filepriv(argc, argv)
int argc;
const char *argv[];
{
const char *argv0 = argv[0];
int id;
struct stat stbuf;
long fmode = -1;
long dmode = -1;
const char *file, *cp;
char *dir;
int maxperm = 0666 ^ filepriv_mask_reg; /* XOR.. */
int fd;
if (argc > 2 && argv[1][0] == '-' && argv[1][1] == 'M') {
++argv;
--argc;
maxperm = (int)strtol(argv[1],NULL,8); /* Octal! */
++argv;
--argc;
}
if (argc == 1 || argc > 3 || (maxperm & (~0664)) != 0) {
fprintf(stderr, "Usage: %s [-M maxperm] pathname [ uid ]\n", argv0);
if (maxperm & (~0664))
fprintf(stderr, " maxperm must be 664 or stricter!\n");
return EX_USAGE;
}
file = argv[1];
if (argc == 3 && isascii(argv[2][0]) && isdigit(argv[2][0])) {
fmode = S_IFREG|0400;
id = atoi(argv[2]);
} else {
fd = open(file, O_RDONLY, 0);
if (fd < 0) {
/* if we can't open it, don't trust it */
perror(file);
fprintf(stderr,
"%s: cannot open(\"%s\")!\n", argv0, file);
return 2;
}
if (fstat(fd, &stbuf) < 0) {
fprintf(stderr, "%s: cannot fstat(\"%s\")!\n",
argv0, file);
close(fd);
return 3;
}
fmode = stbuf.st_mode;
close(fd);
id = stbuf.st_uid;
}
cp = strrchr(file, '/');
if (cp == NULL) {
printf("%d\n", id);
return 0;
} else if (cp == file) /* root path */
++cp;
#ifdef USE_ALLOCA
dir = (char*)alloca(cp - file + 1);
#else
dir = (char*)emalloc(cp - file + 1);
#endif
memcpy(dir, file, cp - file);
dir[cp - file] = '\0';
/* Use stat(2), we fold symlinks to their final dirs(whatever) */
if (stat(dir, &stbuf) < 0 || !S_ISDIR(stbuf.st_mode)) {
fprintf(stderr, "%s: not a directory: \"%s\"!\n",
argv0, dir);
#ifndef USE_ALLOCA
free(dir);
#endif
return 4;
}
#ifndef USE_ALLOCA
free(dir);
#endif
dmode = stbuf.st_mode;
if (!S_ISDIR(dmode)) {
/* Directory is not directory ?? */
id = nobody;
}
if (id != nobody) {
if (!S_ISREG(fmode))
/* File is not a regular file ?? */
id = nobody;
}
/*
* If it is a special or directory or writable
* by non-owner (modulo permission), don't trust it!
* BUT ONLY if the directory where it is has X bits
* for group or others!
*/
/* Group and World accessibility of the residence directory
defines what bits to analyze.
*/
switch (dmode & 00011) {
case 0000: /* No Group access, no World access */
if ((fmode & 07700) & ~maxperm) /* Verify only User bits */
id = nobody;
break;
case 0010: /* Group access, no World access */
if ((fmode & 07770) & ~maxperm) /* Verify User and Group bits */
id = nobody;
break;
case 0001: /* No Group access, but yes World ?! */
case 0011: /* Group and World accesses */
if ((fmode & 07777) & ~maxperm) /* Verify all bits */
id = nobody;
break;
}
if ((dmode & filepriv_mask_dir) && /*If world/group writable*/
!(dmode & S_ISVTX)) /* and not sticky, OR */
id = nobody; /* Don't trust */
if (id != nobody &&
stbuf.st_uid != 0 && stbuf.st_uid != id)/*dir owned by root/user*/
id = nobody; /* Don't trust */
printf("%d\n", id);
return 0;
}
static int
run_runas(argc, argv)
int argc;
const char *argv[];
{
int uid, r;
const char *cp;
static int initeduid = 0;
static short myuid = -1;
if (argc < 3) {
fprintf(stderr, "Usage: %s user function [args...]\n", argv[0]);
return EX_USAGE;
}
cp = argv[1];
if (*cp == '-')
uid = -1, ++cp;
else
uid = 1;
if (isdigit(*cp)) /* what if user id is "3com" ? */
uid *= atoi(cp);
else /* look up login name and get uid */
uid = login_to_uid(cp);
if (!initeduid)
myuid = geteuid();
if (myuid == 0 && uid != 0) {
if (SETEUID(uid) < 0) {
fprintf(stderr, "%s: seteuid(%d): %s\n",
argv[0], uid, strerror(errno));
return 1;
}
}
/* must be builtin or defined function */
r = s_apply(argc - 2, argv + 2); /* within: run_runas() */
if (myuid == 0 && uid != 0) {
if (SETEUID(0) < 0)
abort(); /* user-identity change failed! */
}
return r;
}
static int
run_cat(argc, argv)
int argc;
const char *argv[];
{
FILE *fp;
char buf[8192];
int i;
struct stat stbuf;
if (argc < 2) {
fprintf(stderr, "Usage: %s [filenames ...]\n", argv[0]);
return EX_USAGE;
}
for ( ;argv[1] != NULL; ++argv) {
/* Must be a regular file (via a symlink, though!), no
pipes, sockets, devices... */
if (stat(argv[1], &stbuf) == 0 && S_ISREG(stbuf.st_mode) &&
(fp = fopen(argv[1], "r"))) {
for (;!ferror(fp) && !feof(fp);) {
i = fread(buf, 1, sizeof(buf), fp);
if (i > 0) {
int j = 0;
while (j < i)
j += fwrite(buf + j, 1, i - j, stdout);
} else
break;
}
fclose(fp);
}
}
return 0;
}
static int
run_uid2login(argc, argv)
int argc;
const char *argv[];
{
if (argc != 2 || !isdigit(argv[1][0])) {
fprintf(stderr, "Usage: %s uid\n", argv[0]);
return EX_USAGE;
}
printf("%s\n", uidpwnam(atoi(argv[1])));
return 0;
}
static int
run_login2uid(argc, argv)
int argc;
const char *argv[];
{
if (argc != 2) {
fprintf(stderr, "Usage: %s login\n", argv[0]);
return EX_USAGE;
}
printf("%d\n", login_to_uid(argv[1]));
return 0;
}
static int
run_basename(argc, argv)
int argc;
const char *argv[];
{
const char *cp;
int len;
if (argc == 1) {
fprintf(stderr, "Usage: %s pathname suffix-to-strip\n",
argv[0]);
return EX_USAGE;
}
cp = strrchr(argv[1], '/');
if (cp == NULL)
cp = argv[1];
else
++cp;
if (argc > 2 && (len = strlen(cp) - strlen(argv[2])) > 0) {
if (STREQ(cp + len, argv[2])) {
while (len-- > 0)
putchar(*cp++);
putchar('\n');
return 0;
}
}
printf("%s\n", cp);
return 0;
}
static int
run_syslog(argc, argv)
int argc;
const char *argv[];
{
int c;
int prio = LOG_INFO;
int errflg = 0;
zoptind = 1;
while ((c = zgetopt(argc, (char*const*)argv, "p:")) != EOF) {
switch (c) {
case 'p': /* priority */
if(STREQ(zoptarg, "debug")) {
prio = LOG_DEBUG;
} else if(STREQ(zoptarg, "info")) {
prio = LOG_INFO;
} else if(STREQ(zoptarg, "notice")) {
prio = LOG_NOTICE;
} else if(STREQ(zoptarg, "warning")) {
prio = LOG_WARNING;
} else if(STREQ(zoptarg, "err")) {
prio = LOG_ERR;
} else if(STREQ(zoptarg, "crit")) {
prio = LOG_CRIT;
} else if(STREQ(zoptarg, "alert")) {
prio = LOG_ALERT;
} else if(STREQ(zoptarg, "emerg")) {
prio = LOG_EMERG;
} else {
++errflg;
}
break;
default:
++errflg;
break;
}
}
if (errflg || zoptind != argc - 1) {
fprintf(stderr, "Usage: %s [-p prio] string\n", argv[0]);
return EX_USAGE;
}
zsyslog((prio, "%s", argv[zoptind]));
return 0;
}
static int
run_recase(argc, argv)
int argc;
const char *argv[];
{
char *cp;
int c, flag, errflg, action = 0;
zoptind = 1;
errflg = 0;
while (1) {
c = zgetopt(argc, (char*const*)argv, "ulp");
if (c == EOF)
break;
switch (c) {
case 'u': /* up-case */
case 'l': /* low-case */
case 'p': /* prettify */
action = c;
break;
default:
++errflg;
break;
}
}
if (errflg || zoptind != argc - 1) {
fprintf(stderr, "Usage: %s [ -u | -l | -p ] -- string\n",
argv[0]);
return EX_USAGE;
}
switch (action) {
case 'u':
strupper((char*)argv[zoptind]);
break;
case 'l':
strlower((char*)argv[zoptind]);
break;
case 'p':
flag = 1;
for (cp = (char*)argv[zoptind]; *cp != '\0'; ++cp) {
if (isascii(*cp) && isalnum(*cp)) {
if (flag && islower(*cp))
*cp = toupper(*cp);
else if (!flag && isupper(*cp))
*cp = tolower(*cp);
flag = 0;
} else
flag = 1;
}
break;
}
printf("%s\n", argv[zoptind]);
return 0;
}
#if defined(XMEM) && defined(CSRIMALLOC)
static int
run_malcontents(argc, argv)
int argc;
const char *argv[];
{
mal_contents(stdout);
}
#endif /* CSRIMALLOC */
static struct {
short fyitype;
short fyisave;
const char *fyiname;
const char *fyitext;
} fyitable[] = {
{ FYI_BREAKIN, 0, "breakin", "external message claims local origin!" },
{ FYI_BADHEADER, 0, "badheader","message header syntax error!" },
{ FYI_ILLHEADER, 0, "illheader","null header field name!" },
{ FYI_NOCHANNEL, 0, "nochannel","a null string input channel was specified" },
{ FYI_NOSENDER, 0, "nosender", "no sender could be determined!" },
};
void
optsave(type, e)
int type;
struct envelope *e;
{
int i;
for (i = 0; i < (sizeof fyitable / sizeof fyitable[0]); ++i) {
if (type == fyitable[i].fyitype) {
if (fyitable[i].fyisave) {
char name[20];
sprintf(name,"_%s", fyitable[i].fyiname);
squirrel(e, name, fyitable[i].fyitext);
}
fprintf(stderr, "*** %s\n", fyitable[i].fyitext);
return;
}
}
}
static int
run_squirrel(argc, argv)
int argc;
const char *argv[];
{
int i, j, errflag, flag;
errflag = 0;
for (i = 1; i < argc; ++i) {
if (argv[i][0] == '-') {
argv[i] = argv[i]+1;
flag = 0;
} else
flag = 1;
for (j = 0; j < (sizeof fyitable / sizeof fyitable[0]); ++j) {
if (CISTREQ(argv[i], fyitable[j].fyiname)) {
fyitable[j].fyisave = flag;
j = -1;
break;
}
}
if (j != -1)
++errflag;
}
if (errflag || argc == 1) {
fprintf(stderr, "Usage: %s [", argv[0]);
for (j = 0; j < (sizeof fyitable / sizeof fyitable[0]); ++j) {
if (j > 0)
fprintf(stderr, " |");
if (fyitable[j].fyisave)
fprintf(stderr, " -%s", fyitable[j].fyiname);
else
fprintf(stderr, " %s", fyitable[j].fyiname);
}
fprintf(stderr, " ]\n");
return EX_USAGE;
}
return 0;
}
static struct headerinfo addrhdr = {
"route-addr", RouteAddress, Recipient, normal
};
static int
run_822syntax(argc, argv)
int argc;
const char *argv[];
{
struct header hs;
struct envelope es;
/*char buf[4096];*/
if (argc != 2)
return EX_USAGE;
es.e_nowtime = now;
es.e_file = argv[0];
hs.h_descriptor = &addrhdr;
hs.h_pname = argv[1];
hs.h_lines = makeToken(argv[1], strlen(argv[1]));
hs.h_lines->t_type = Line;
hs.h_lines->t_next = NULL;
hs.h_contents = hdr_scanparse(&es, &hs, 1, 0);
hs.h_stamp = hdr_type(&hs);
if (hs.h_stamp == BadHeader) {
hdr_errprint(&es, &hs, stderr, "RFC822/976/2822");
return 1;
} else if (hs.h_contents.a == NULL)
return 1;
return 0;
}
static int
run_condquote_(argc, argv, condq)
int argc, condq;
const char *argv[];
{
const char *s;
int mustquote = 0;
int c, quoted;
int spc = 0;
int errflg = 0;
const char *appstr = NULL;
extern int rfc822_mustquote __((const char *, const int));
/* We remove quotes when they are not needed, and add them when
they really are needed! */
zoptind = 1;
while (1) {
c = zgetopt(argc, (char*const*)argv, "s:a:");
if (c == EOF) break;
switch (c) {
case 's':
spc = *zoptarg; /* First char only */
break;
case 'a':
appstr = zoptarg;
break;
default:
++errflg;
break;
}
}
if (errflg || zoptind != argc - 1) {
fprintf(stderr,
"Usage: %s [ -s SPCCHR ] [ -a APPENDSTR ] string\n",
argv[0]);
return EX_USAGE;
}
s = argv[zoptind];
mustquote = rfc822_mustquote(s, spc);
/* A bitset:
0001 Has Quotes
0002 Ended while inside a quote
0004 Has characters which must be quoted
*/
if (!condq || mustquote == 1) {
/* Well, we can actually DEQUOTE the thing just fine! */
quoted = 0;
for (; *s; ++s) {
c = *s;
if (c == ' ' && spc) {
putchar(spc);
quoted = 0;
continue;
}
if (quoted) {
putchar(c);
quoted = 0;
continue;
}
if (c == '\\') {
/* A quoted pair! */
quoted = 1;
putchar(c);
c = *++s;
} else if (c == '"') {
continue; /* Drop it! */
} else
putchar(c);
}
} else if (mustquote > 1 && !(mustquote & 1) && condq) {
/* Has things needing quotes, but no quotes in place! */
putchar('"');
for (; *s; ++s) {
c = *s;
if (c == ' ' && spc)
putchar(spc);
else if (c == '"') {
putchar('\\');
putchar(c);
} else
putchar(c);
}
putchar('"');
} else {
/* The original one.. */
for (; *s; ++s) {
c = *s;
if (c == ' ' && spc)
putchar(spc);
else if (c == '\\') {
putchar(c);
} else
putchar(c);
}
}
if (appstr)
puts(appstr);
return 0;
}
static int
run_condquote(argc, argv)
int argc;
const char *argv[];
{
return run_condquote_(argc,argv,1);
}
static int
run_dequote(argc, argv)
int argc;
const char *argv[];
{
return run_condquote_(argc,argv,0);
}
syntax highlighted by Code2HTML, v. 0.9.1