Thanks to John Cummings.
This commit is contained in:
1001
src/cmd/upas/common/libsys.c
Normal file
1001
src/cmd/upas/common/libsys.c
Normal file
File diff suppressed because it is too large
Load Diff
57
src/cmd/upas/common/mail.c
Normal file
57
src/cmd/upas/common/mail.c
Normal file
@@ -0,0 +1,57 @@
|
||||
#include "common.h"
|
||||
|
||||
/* format of REMOTE FROM lines */
|
||||
char *REMFROMRE =
|
||||
"^>?From[ \t]+((\".*\")?[^\" \t]+?(\".*\")?[^\" \t]+?)[ \t]+(.+)[ \t]+remote[ \t]+from[ \t]+(.*)\n$";
|
||||
int REMSENDERMATCH = 1;
|
||||
int REMDATEMATCH = 4;
|
||||
int REMSYSMATCH = 5;
|
||||
|
||||
/* format of LOCAL FROM lines */
|
||||
char *FROMRE =
|
||||
"^>?From[ \t]+((\".*\")?[^\" \t]+?(\".*\")?[^\" \t]+?)[ \t]+(.+)\n$";
|
||||
int SENDERMATCH = 1;
|
||||
int DATEMATCH = 4;
|
||||
|
||||
/* output a unix style local header */
|
||||
int
|
||||
print_header(Biobuf *fp, char *sender, char *date)
|
||||
{
|
||||
return Bprint(fp, "From %s %s\n", sender, date);
|
||||
}
|
||||
|
||||
/* output a unix style remote header */
|
||||
int
|
||||
print_remote_header(Biobuf *fp, char *sender, char *date, char *system)
|
||||
{
|
||||
return Bprint(fp, "From %s %s remote from %s\n", sender, date, system);
|
||||
}
|
||||
|
||||
/* parse a mailbox style header */
|
||||
int
|
||||
parse_header(char *line, String *sender, String *date)
|
||||
{
|
||||
if (!IS_HEADER(line))
|
||||
return -1;
|
||||
line += sizeof("From ") - 1;
|
||||
s_restart(sender);
|
||||
while(*line==' '||*line=='\t')
|
||||
line++;
|
||||
if(*line == '"'){
|
||||
s_putc(sender, *line++);
|
||||
while(*line && *line != '"')
|
||||
s_putc(sender, *line++);
|
||||
s_putc(sender, *line++);
|
||||
} else {
|
||||
while(*line && *line != ' ' && *line != '\t')
|
||||
s_putc(sender, *line++);
|
||||
}
|
||||
s_terminate(sender);
|
||||
s_restart(date);
|
||||
while(*line==' '||*line=='\t')
|
||||
line++;
|
||||
while(*line)
|
||||
s_putc(date, *line++);
|
||||
s_terminate(date);
|
||||
return 0;
|
||||
}
|
||||
18
src/cmd/upas/common/makefile
Normal file
18
src/cmd/upas/common/makefile
Normal file
@@ -0,0 +1,18 @@
|
||||
CFLAGS=${UNIX} -g -I. -I../libc -I../common -I/usr/include ${SCFLAGS}
|
||||
OBJS=mail.o aux.o string.o ${SYSOBJ}
|
||||
AR=ar
|
||||
.c.o: ; ${CC} -c ${CFLAGS} $*.c
|
||||
|
||||
common.a: ${OBJS}
|
||||
${AR} cr common.a ${OBJS}
|
||||
-ranlib common.a
|
||||
|
||||
aux.o: aux.h string.h mail.h
|
||||
string.o: string.h mail.h
|
||||
mail.o: mail.h
|
||||
syslog.o: sys.h
|
||||
mail.h: sys.h
|
||||
|
||||
clean:
|
||||
-rm -f *.[oO] core a.out *.a *.sL common.a
|
||||
|
||||
20
src/cmd/upas/common/mkfile
Normal file
20
src/cmd/upas/common/mkfile
Normal file
@@ -0,0 +1,20 @@
|
||||
<$PLAN9/src/mkhdr
|
||||
|
||||
LIB=libcommon.a
|
||||
|
||||
OFILES=aux.$O\
|
||||
become.$O\
|
||||
mail.$O\
|
||||
process.$O\
|
||||
libsys.$O\
|
||||
config.$O\
|
||||
appendfiletombox.$O\
|
||||
|
||||
HFILES=common.h\
|
||||
sys.h\
|
||||
|
||||
<$PLAN9/src/mklib
|
||||
|
||||
nuke:V:
|
||||
mk clean
|
||||
rm -f libcommon.a
|
||||
175
src/cmd/upas/common/process.c
Normal file
175
src/cmd/upas/common/process.c
Normal file
@@ -0,0 +1,175 @@
|
||||
#include "common.h"
|
||||
|
||||
/* make a stream to a child process */
|
||||
extern stream *
|
||||
instream(void)
|
||||
{
|
||||
stream *rv;
|
||||
int pfd[2];
|
||||
|
||||
if ((rv = (stream *)malloc(sizeof(stream))) == 0)
|
||||
return 0;
|
||||
memset(rv, 0, sizeof(stream));
|
||||
if (pipe(pfd) < 0)
|
||||
return 0;
|
||||
if(Binit(&rv->bb, pfd[1], OWRITE) < 0){
|
||||
close(pfd[0]);
|
||||
close(pfd[1]);
|
||||
return 0;
|
||||
}
|
||||
rv->fp = &rv->bb;
|
||||
rv->fd = pfd[0];
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* make a stream from a child process */
|
||||
extern stream *
|
||||
outstream(void)
|
||||
{
|
||||
stream *rv;
|
||||
int pfd[2];
|
||||
|
||||
if ((rv = (stream *)malloc(sizeof(stream))) == 0)
|
||||
return 0;
|
||||
memset(rv, 0, sizeof(stream));
|
||||
if (pipe(pfd) < 0)
|
||||
return 0;
|
||||
if (Binit(&rv->bb, pfd[0], OREAD) < 0){
|
||||
close(pfd[0]);
|
||||
close(pfd[1]);
|
||||
return 0;
|
||||
}
|
||||
rv->fp = &rv->bb;
|
||||
rv->fd = pfd[1];
|
||||
return rv;
|
||||
}
|
||||
|
||||
extern void
|
||||
stream_free(stream *sp)
|
||||
{
|
||||
int fd;
|
||||
|
||||
close(sp->fd);
|
||||
fd = Bfildes(sp->fp);
|
||||
Bterm(sp->fp);
|
||||
close(fd);
|
||||
free((char *)sp);
|
||||
}
|
||||
|
||||
/* start a new process */
|
||||
extern process *
|
||||
noshell_proc_start(char **av, stream *inp, stream *outp, stream *errp, int newpg, char *who)
|
||||
{
|
||||
process *pp;
|
||||
int i, n;
|
||||
|
||||
if ((pp = (process *)malloc(sizeof(process))) == 0) {
|
||||
if (inp != 0)
|
||||
stream_free(inp);
|
||||
if (outp != 0)
|
||||
stream_free(outp);
|
||||
if (errp != 0)
|
||||
stream_free(errp);
|
||||
return 0;
|
||||
}
|
||||
pp->std[0] = inp;
|
||||
pp->std[1] = outp;
|
||||
pp->std[2] = errp;
|
||||
switch (pp->pid = fork()) {
|
||||
case -1:
|
||||
proc_free(pp);
|
||||
return 0;
|
||||
case 0:
|
||||
if(newpg)
|
||||
sysdetach();
|
||||
for (i=0; i<3; i++)
|
||||
if (pp->std[i] != 0){
|
||||
close(Bfildes(pp->std[i]->fp));
|
||||
while(pp->std[i]->fd < 3)
|
||||
pp->std[i]->fd = dup(pp->std[i]->fd, -1);
|
||||
}
|
||||
for (i=0; i<3; i++)
|
||||
if (pp->std[i] != 0)
|
||||
dup(pp->std[i]->fd, i);
|
||||
for (n = sysfiles(); i < n; i++)
|
||||
close(i);
|
||||
if(who) {
|
||||
fprint(2,"process.c: trying to become(%s,%s)\n",av,who);
|
||||
// jpc become(av, who);
|
||||
}
|
||||
exec(av[0], av);
|
||||
perror("proc_start");
|
||||
exits("proc_start");
|
||||
default:
|
||||
for (i=0; i<3; i++)
|
||||
if (pp->std[i] != 0) {
|
||||
close(pp->std[i]->fd);
|
||||
pp->std[i]->fd = -1;
|
||||
}
|
||||
return pp;
|
||||
}
|
||||
}
|
||||
|
||||
/* start a new process under a shell */
|
||||
extern process *
|
||||
proc_start(char *cmd, stream *inp, stream *outp, stream *errp, int newpg, char *who)
|
||||
{
|
||||
char *av[4];
|
||||
|
||||
av[0] = unsharp(SHELL);
|
||||
av[1] = "-c";
|
||||
av[2] = cmd;
|
||||
av[3] = 0;
|
||||
return noshell_proc_start(av, inp, outp, errp, newpg, who);
|
||||
}
|
||||
|
||||
/* wait for a process to stop */
|
||||
extern int
|
||||
proc_wait(process *pp)
|
||||
{
|
||||
Waitmsg *status;
|
||||
char err[Errlen];
|
||||
|
||||
for(;;){
|
||||
status = wait();
|
||||
if(status == nil){
|
||||
errstr(err, sizeof(err));
|
||||
if(strstr(err, "interrupt") == 0)
|
||||
break;
|
||||
}
|
||||
if (status->pid==pp->pid)
|
||||
break;
|
||||
}
|
||||
pp->pid = -1;
|
||||
if(status == nil)
|
||||
pp->status = -1;
|
||||
else
|
||||
pp->status = status->msg[0];
|
||||
pp->waitmsg = status;
|
||||
return pp->status;
|
||||
}
|
||||
|
||||
/* free a process */
|
||||
extern int
|
||||
proc_free(process *pp)
|
||||
{
|
||||
int i;
|
||||
|
||||
if(pp->std[1] == pp->std[2])
|
||||
pp->std[2] = 0; /* avoid freeing it twice */
|
||||
for (i = 0; i < 3; i++)
|
||||
if (pp->std[i])
|
||||
stream_free(pp->std[i]);
|
||||
if (pp->pid >= 0)
|
||||
proc_wait(pp);
|
||||
free(pp->waitmsg);
|
||||
free((char *)pp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* kill a process */
|
||||
extern int
|
||||
proc_kill(process *pp)
|
||||
{
|
||||
return syskill(pp->pid);
|
||||
}
|
||||
85
src/cmd/upas/common/sys.h
Normal file
85
src/cmd/upas/common/sys.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* System dependent header files for research
|
||||
*/
|
||||
|
||||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <regexp.h>
|
||||
#include <bio.h>
|
||||
#include "libString.h" /* jpc String.h -> libString.h */
|
||||
|
||||
/*
|
||||
* for the lock routines in libsys.c
|
||||
*/
|
||||
typedef struct Mlock Mlock;
|
||||
struct Mlock {
|
||||
int fd;
|
||||
int pid;
|
||||
String *name;
|
||||
};
|
||||
|
||||
/*
|
||||
* from config.c
|
||||
*/
|
||||
extern char *MAILROOT; /* root of mail system */
|
||||
extern char *UPASLOG; /* log directory */
|
||||
extern char *UPASLIB; /* upas library directory */
|
||||
extern char *UPASBIN; /* upas binary directory */
|
||||
extern char *UPASTMP; /* temporary directory */
|
||||
extern char *SHELL; /* path name of shell */
|
||||
extern char *POST; /* path name of post server addresses */
|
||||
extern int MBOXMODE; /* default mailbox protection mode */
|
||||
|
||||
/*
|
||||
* files in libsys.c
|
||||
*/
|
||||
extern char *sysname_read(void);
|
||||
extern char *alt_sysname_read(void);
|
||||
extern char *domainname_read(void);
|
||||
extern char **sysnames_read(void);
|
||||
extern char *getlog(void);
|
||||
extern char *thedate(void);
|
||||
extern Biobuf *sysopen(char*, char*, ulong);
|
||||
extern int sysopentty(void);
|
||||
extern int sysclose(Biobuf*);
|
||||
extern int sysmkdir(char*, ulong);
|
||||
extern int syschgrp(char*, char*);
|
||||
extern Mlock *syslock(char *);
|
||||
extern void sysunlock(Mlock *);
|
||||
extern void syslockrefresh(Mlock *);
|
||||
extern int e_nonexistent(void);
|
||||
extern int e_locked(void);
|
||||
extern long sysfilelen(Biobuf*);
|
||||
extern int sysremove(char*);
|
||||
extern int sysrename(char*, char*);
|
||||
extern int sysexist(char*);
|
||||
extern int sysisdir(char*);
|
||||
extern int syskill(int);
|
||||
extern int syskillpg(int);
|
||||
extern int syscreate(char*, int, ulong);
|
||||
extern Mlock *trylock(char *);
|
||||
extern void exit9(int);
|
||||
extern void pipesig(int*);
|
||||
extern void pipesigoff(void);
|
||||
extern int holdon(void);
|
||||
extern void holdoff(int);
|
||||
extern int syscreatelocked(char*, int, int);
|
||||
extern int sysopenlocked(char*, int);
|
||||
extern int sysunlockfile(int);
|
||||
extern int sysfiles(void);
|
||||
extern int become(char**, char*);
|
||||
extern int sysdetach(void);
|
||||
extern int sysdirreadall(int, Dir**);
|
||||
extern String *username(String*);
|
||||
extern char* remoteaddr(int, char*);
|
||||
extern int creatembox(char*, char*);
|
||||
|
||||
extern String *readlock(String*);
|
||||
extern char *homedir(char*);
|
||||
extern String *mboxname(char*, String*);
|
||||
extern String *deadletter(String*);
|
||||
|
||||
/*
|
||||
* maximum size for a file path
|
||||
*/
|
||||
#define MAXPATHLEN 128
|
||||
8
src/cmd/upas/filterkit/dat.h
Normal file
8
src/cmd/upas/filterkit/dat.h
Normal file
@@ -0,0 +1,8 @@
|
||||
typedef struct Addr Addr;
|
||||
struct Addr
|
||||
{
|
||||
Addr *next;
|
||||
char *val;
|
||||
};
|
||||
|
||||
extern Addr* readaddrs(char*, Addr*);
|
||||
60
src/cmd/upas/filterkit/deliver.c
Normal file
60
src/cmd/upas/filterkit/deliver.c
Normal file
@@ -0,0 +1,60 @@
|
||||
#include "dat.h"
|
||||
#include "common.h"
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "usage: %s recipient fromaddr-file mbox\n", argv0);
|
||||
exits("usage");
|
||||
}
|
||||
|
||||
void
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int fd;
|
||||
char now[30];
|
||||
Addr *a;
|
||||
char *deliveredto;
|
||||
Mlock *l;
|
||||
int bytes;
|
||||
|
||||
ARGBEGIN{
|
||||
}ARGEND;
|
||||
|
||||
if(argc != 3)
|
||||
usage();
|
||||
|
||||
deliveredto = strrchr(argv[0], '!');
|
||||
if(deliveredto == nil)
|
||||
deliveredto = argv[0];
|
||||
else
|
||||
deliveredto++;
|
||||
a = readaddrs(argv[1], nil);
|
||||
if(a == nil)
|
||||
sysfatal("missing from address");
|
||||
|
||||
l = syslock(argv[2]);
|
||||
|
||||
/* append to mbox */
|
||||
fd = open(argv[2], OWRITE);
|
||||
if(fd < 0)
|
||||
sysfatal("opening mailbox: %r");
|
||||
seek(fd, 0, 2);
|
||||
strncpy(now, ctime(time(0)), sizeof(now));
|
||||
now[28] = 0;
|
||||
if(fprint(fd, "From %s %s\n", a->val, now) < 0)
|
||||
sysfatal("writing mailbox: %r");
|
||||
|
||||
/* copy message handles escapes and any needed new lines */
|
||||
bytes = appendfiletombox(0, fd);
|
||||
if(bytes < 0)
|
||||
sysfatal("writing mailbox: %r");
|
||||
|
||||
close(fd);
|
||||
sysunlock(l);
|
||||
|
||||
/* log it */
|
||||
syslog(0, "mail", "delivered %s From %s %s (%s) %d", deliveredto,
|
||||
a->val, now, argv[0], bytes);
|
||||
exits(0);
|
||||
}
|
||||
315
src/cmd/upas/filterkit/list.c
Normal file
315
src/cmd/upas/filterkit/list.c
Normal file
@@ -0,0 +1,315 @@
|
||||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <regexp.h>
|
||||
#include <libsec.h>
|
||||
#include <String.h>
|
||||
#include <bio.h>
|
||||
#include "dat.h"
|
||||
|
||||
int debug;
|
||||
|
||||
enum
|
||||
{
|
||||
Tregexp= (1<<0), /* ~ */
|
||||
Texact= (1<<1), /* = */
|
||||
};
|
||||
|
||||
typedef struct Pattern Pattern;
|
||||
struct Pattern
|
||||
{
|
||||
Pattern *next;
|
||||
int type;
|
||||
char *arg;
|
||||
int bang;
|
||||
};
|
||||
|
||||
String *patternpath;
|
||||
Pattern *patterns;
|
||||
String *mbox;
|
||||
|
||||
static void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "usage: %s 'check|add' patternfile addr [addr*]\n", argv0);
|
||||
exits("usage");
|
||||
}
|
||||
|
||||
/*
|
||||
* convert string to lower case
|
||||
*/
|
||||
static void
|
||||
mklower(char *p)
|
||||
{
|
||||
int c;
|
||||
|
||||
for(; *p; p++){
|
||||
c = *p;
|
||||
if(c <= 'Z' && c >= 'A')
|
||||
*p = c - 'A' + 'a';
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* simplify an address, reduce to a domain
|
||||
*/
|
||||
static String*
|
||||
simplify(char *addr)
|
||||
{
|
||||
int dots;
|
||||
char *p, *at;
|
||||
String *s;
|
||||
|
||||
mklower(addr);
|
||||
at = strchr(addr, '@');
|
||||
if(at == nil){
|
||||
/* local address, make it an exact match */
|
||||
s = s_copy("=");
|
||||
s_append(s, addr);
|
||||
return s;
|
||||
}
|
||||
|
||||
/* copy up to the '@' sign */
|
||||
at++;
|
||||
s = s_copy("~");
|
||||
for(p = addr; p < at; p++){
|
||||
if(strchr(".*+?(|)\\[]^$", *p))
|
||||
s_putc(s, '\\');
|
||||
s_putc(s, *p);
|
||||
}
|
||||
|
||||
/* just any address matching the two most significant domain elements */
|
||||
s_append(s, "(.*\\.)?");
|
||||
p = addr+strlen(addr);
|
||||
dots = 0;
|
||||
for(; p > at; p--){
|
||||
if(*p != '.')
|
||||
continue;
|
||||
if(dots++ > 0){
|
||||
p++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for(; *p; p++){
|
||||
if(strchr(".*+?(|)\\[]^$", *p) != 0)
|
||||
s_putc(s, '\\');
|
||||
s_putc(s, *p);
|
||||
}
|
||||
s_terminate(s);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/*
|
||||
* link patterns in order
|
||||
*/
|
||||
static int
|
||||
newpattern(int type, char *arg, int bang)
|
||||
{
|
||||
Pattern *p;
|
||||
static Pattern *last;
|
||||
|
||||
mklower(arg);
|
||||
|
||||
p = mallocz(sizeof *p, 1);
|
||||
if(p == nil)
|
||||
return -1;
|
||||
if(type == Tregexp){
|
||||
p->arg = malloc(strlen(arg)+3);
|
||||
if(p->arg == nil){
|
||||
free(p);
|
||||
return -1;
|
||||
}
|
||||
p->arg[0] = 0;
|
||||
strcat(p->arg, "^");
|
||||
strcat(p->arg, arg);
|
||||
strcat(p->arg, "$");
|
||||
} else {
|
||||
p->arg = strdup(arg);
|
||||
if(p->arg == nil){
|
||||
free(p);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
p->type = type;
|
||||
p->bang = bang;
|
||||
if(last == nil)
|
||||
patterns = p;
|
||||
else
|
||||
last->next = p;
|
||||
last = p;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* patterns are either
|
||||
* ~ regular expression
|
||||
* = exact match string
|
||||
*
|
||||
* all comparisons are case insensitive
|
||||
*/
|
||||
static int
|
||||
readpatterns(char *path)
|
||||
{
|
||||
Biobuf *b;
|
||||
char *p;
|
||||
char *token[2];
|
||||
int n;
|
||||
int bang;
|
||||
|
||||
b = Bopen(path, OREAD);
|
||||
if(b == nil)
|
||||
return -1;
|
||||
while((p = Brdline(b, '\n')) != nil){
|
||||
p[Blinelen(b)-1] = 0;
|
||||
n = tokenize(p, token, 2);
|
||||
if(n == 0)
|
||||
continue;
|
||||
|
||||
mklower(token[0]);
|
||||
p = token[0];
|
||||
if(*p == '!'){
|
||||
p++;
|
||||
bang = 1;
|
||||
} else
|
||||
bang = 0;
|
||||
|
||||
if(*p == '='){
|
||||
if(newpattern(Texact, p+1, bang) < 0)
|
||||
return -1;
|
||||
} else if(*p == '~'){
|
||||
if(newpattern(Tregexp, p+1, bang) < 0)
|
||||
return -1;
|
||||
} else if(strcmp(token[0], "#include") == 0 && n == 2)
|
||||
readpatterns(token[1]);
|
||||
}
|
||||
Bterm(b);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* fuck, shit, bugger, damn */
|
||||
void regerror(char*)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* check lower case version of address agains patterns
|
||||
*/
|
||||
static Pattern*
|
||||
checkaddr(char *arg)
|
||||
{
|
||||
Pattern *p;
|
||||
Reprog *rp;
|
||||
String *s;
|
||||
|
||||
s = s_copy(arg);
|
||||
mklower(s_to_c(s));
|
||||
|
||||
for(p = patterns; p != nil; p = p->next)
|
||||
switch(p->type){
|
||||
case Texact:
|
||||
if(strcmp(p->arg, s_to_c(s)) == 0){
|
||||
free(s);
|
||||
return p;
|
||||
}
|
||||
break;
|
||||
case Tregexp:
|
||||
rp = regcomp(p->arg);
|
||||
if(rp == nil)
|
||||
continue;
|
||||
if(regexec(rp, s_to_c(s), nil, 0)){
|
||||
free(rp);
|
||||
free(s);
|
||||
return p;
|
||||
}
|
||||
free(rp);
|
||||
break;
|
||||
}
|
||||
s_free(s);
|
||||
return 0;
|
||||
}
|
||||
static char*
|
||||
check(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
Addr *a;
|
||||
Pattern *p;
|
||||
int matchedbang;
|
||||
|
||||
matchedbang = 0;
|
||||
for(i = 0; i < argc; i++){
|
||||
a = readaddrs(argv[i], nil);
|
||||
for(; a != nil; a = a->next){
|
||||
p = checkaddr(a->val);
|
||||
if(p == nil)
|
||||
continue;
|
||||
if(p->bang)
|
||||
matchedbang = 1;
|
||||
else
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
if(matchedbang)
|
||||
return "!match";
|
||||
else
|
||||
return "no match";
|
||||
}
|
||||
|
||||
/*
|
||||
* add anything that isn't already matched, all matches are lower case
|
||||
*/
|
||||
static char*
|
||||
add(char *pp, int argc, char **argv)
|
||||
{
|
||||
int fd, i;
|
||||
String *s;
|
||||
char *cp;
|
||||
Addr *a;
|
||||
|
||||
a = nil;
|
||||
for(i = 0; i < argc; i++)
|
||||
a = readaddrs(argv[i], a);
|
||||
|
||||
fd = open(pp, OWRITE);
|
||||
seek(fd, 0, 2);
|
||||
for(; a != nil; a = a->next){
|
||||
if(checkaddr(a->val))
|
||||
continue;
|
||||
s = simplify(a->val);
|
||||
cp = s_to_c(s);
|
||||
fprint(fd, "%q\t%q\n", cp, a->val);
|
||||
if(*cp == '=')
|
||||
newpattern(Texact, cp+1, 0);
|
||||
else if(*cp == '~')
|
||||
newpattern(Tregexp, cp+1, 0);
|
||||
s_free(s);
|
||||
}
|
||||
close(fd);
|
||||
return nil;
|
||||
}
|
||||
|
||||
void
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
char *patternpath;
|
||||
|
||||
ARGBEGIN {
|
||||
case 'd':
|
||||
debug++;
|
||||
break;
|
||||
} ARGEND;
|
||||
|
||||
quotefmtinstall();
|
||||
|
||||
if(argc < 3)
|
||||
usage();
|
||||
|
||||
patternpath = argv[1];
|
||||
readpatterns(patternpath);
|
||||
if(strcmp(argv[0], "add") == 0)
|
||||
exits(add(patternpath, argc-2, argv+2));
|
||||
else if(strcmp(argv[0], "check") == 0)
|
||||
exits(check(argc-2, argv+2));
|
||||
else
|
||||
usage();
|
||||
}
|
||||
21
src/cmd/upas/filterkit/mkfile
Normal file
21
src/cmd/upas/filterkit/mkfile
Normal file
@@ -0,0 +1,21 @@
|
||||
</$objtype/mkfile
|
||||
|
||||
TARG=\
|
||||
token\
|
||||
list\
|
||||
deliver\
|
||||
|
||||
LIB=../common/libcommon.a$O\
|
||||
|
||||
BIN=/$objtype/bin/upas
|
||||
OFILES=readaddrs.$O
|
||||
UPDATE=\
|
||||
mkfile\
|
||||
${TARG:%=%.c}\
|
||||
pipeto.sample\
|
||||
pipefrom.sample\
|
||||
pipeto.sample-hold\
|
||||
|
||||
</sys/src/cmd/mkmany
|
||||
CFLAGS=$CFLAGS -I../common
|
||||
|
||||
24
src/cmd/upas/filterkit/pipefrom.sample
Executable file
24
src/cmd/upas/filterkit/pipefrom.sample
Executable file
@@ -0,0 +1,24 @@
|
||||
#!/bin/rc
|
||||
|
||||
rfork e
|
||||
TMP=/tmp/myupassend.$pid
|
||||
|
||||
# collect upas/send options
|
||||
options=()
|
||||
while (! ~ $#* 0 && ~ $1 -*) {
|
||||
options=($options $1);
|
||||
shift
|
||||
}
|
||||
|
||||
# collect addresses and add them to my patterns
|
||||
dests=()
|
||||
while (! ~ $#* 0) {
|
||||
dests=($dests $1);
|
||||
shift
|
||||
}
|
||||
echo $dests > $TMP
|
||||
upas/list add /mail/box/$user/_pattern $TMP >[2] /dev/null
|
||||
rm $TMP
|
||||
|
||||
# send mail
|
||||
upas/send $options $dests
|
||||
73
src/cmd/upas/filterkit/pipeto.sample
Normal file
73
src/cmd/upas/filterkit/pipeto.sample
Normal file
@@ -0,0 +1,73 @@
|
||||
#!/bin/rc
|
||||
|
||||
# create a /tmp for here documents
|
||||
rfork en
|
||||
bind -c /mail/tmp /tmp
|
||||
|
||||
KEY=whocares
|
||||
USER=ken
|
||||
|
||||
RECIP=$1
|
||||
MBOX=$2
|
||||
PF=/mail/box/$USER/_pattern
|
||||
TMP=/mail/tmp/mine.$pid
|
||||
BIN=/bin/upas
|
||||
D=/mail/fs/mbox/1
|
||||
|
||||
# save and parse the mail file
|
||||
{sed '/^$/,$ s/^From / From /'; echo} > $TMP
|
||||
upas/fs -f $TMP
|
||||
|
||||
# if we like the source
|
||||
# or if the subject contains a valid token
|
||||
# then deliver the mail and allow all the addresses
|
||||
if( $BIN/list check $PF $D/from $D/sender $D/replyto )
|
||||
{
|
||||
$BIN/deliver $RECIP $D/from $MBOX < $D/raw
|
||||
$BIN/list add $PF $D/from $D/to $D/cc $D/sender
|
||||
rm $TMP
|
||||
exit 0
|
||||
}
|
||||
switch($status){
|
||||
case *!match*
|
||||
echo `{date} dropped $RECIP From `{cat $D/replyto} >> /mail/box/$USER/_bounced >[2] /dev/null
|
||||
rm $TMP
|
||||
exit 0
|
||||
}
|
||||
if ( $BIN/token $KEY $D/subject )
|
||||
{
|
||||
$BIN/deliver $RECIP $D/from $MBOX < $D/raw
|
||||
$BIN/list add $PF $D/from $D/to $D/cc $D/sender
|
||||
rm $TMP
|
||||
echo `{date} added $RECIP From `{cat $D/replyto} \
|
||||
>> /mail/box/$USER/_bounced >[2] /dev/null
|
||||
exit 0
|
||||
}
|
||||
|
||||
# don't recognize the sender so
|
||||
# return the message with instructions
|
||||
TOKEN=`{upas/token $KEY}
|
||||
upasname=/dev/null
|
||||
{{cat; cat $D/raw} | upas/send `{cat $D/replyto}}<<EOF
|
||||
Subject: $USER's mail filter
|
||||
I've been getting so much junk mail that I'm resorting to
|
||||
a draconian mechanism to avoid the mail. In order
|
||||
to make sure that there's a real person sending mail, I'm
|
||||
asking you to explicitly enable access. To do that, send
|
||||
mail to $USER at this domain with the token:
|
||||
$TOKEN
|
||||
in the subject of your mail message. After that, you
|
||||
shouldn't get any bounces from me. Sorry if this is
|
||||
an inconvenience.
|
||||
|
||||
----------------
|
||||
Original message
|
||||
----------------
|
||||
EOF
|
||||
|
||||
echo `{date} bounced $RECIP From `{cat $D/replyto} \
|
||||
>> /mail/box/$USER/_bounced >[2] /dev/null
|
||||
|
||||
rv=$status
|
||||
rm $TMP
|
||||
exit $status
|
||||
43
src/cmd/upas/filterkit/pipeto.sample-hold
Normal file
43
src/cmd/upas/filterkit/pipeto.sample-hold
Normal file
@@ -0,0 +1,43 @@
|
||||
#!/bin/rc
|
||||
|
||||
# create a /tmp for here documents
|
||||
rfork en
|
||||
bind -c /mail/tmp /tmp
|
||||
|
||||
KEY=whocares
|
||||
USER=ken
|
||||
|
||||
RECIP=$1
|
||||
MBOX=$2
|
||||
PF=/mail/box/$USER/_pattern
|
||||
TMP=/mail/tmp/mine.$pid
|
||||
BIN=/bin/upas
|
||||
D=/mail/fs/mbox/1
|
||||
|
||||
# save and parse the mail file
|
||||
{sed '/^$/,$ s/^From / From /'; echo} > $TMP
|
||||
upas/fs -f $TMP
|
||||
|
||||
# if we like the source
|
||||
# or if the subject contains a valid token
|
||||
# then deliver the mail and allow all the addresses
|
||||
if( $BIN/list check $PF $D/from $D/sender $D/replyto )
|
||||
{
|
||||
$BIN/deliver $RECIP $D/from $MBOX < $D/raw
|
||||
$BIN/list add $PF $D/from $D/to $D/cc $D/sender
|
||||
rm $TMP
|
||||
exit 0
|
||||
}
|
||||
switch($status){
|
||||
case *!match*
|
||||
echo `{date} dropped $RECIP From `{cat $D/replyto} >> /mail/box/$USER/_bounced >[2] /dev/null
|
||||
rm $TMP
|
||||
exit 0
|
||||
}
|
||||
|
||||
# don't recognize the sender so hold the message
|
||||
$BIN/deliver $RECIP $D/from /mail/box/$USER/_held < $D/raw
|
||||
|
||||
rv=$status
|
||||
rm $TMP
|
||||
exit $status
|
||||
98
src/cmd/upas/filterkit/readaddrs.c
Normal file
98
src/cmd/upas/filterkit/readaddrs.c
Normal file
@@ -0,0 +1,98 @@
|
||||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include "dat.h"
|
||||
|
||||
void*
|
||||
emalloc(int size)
|
||||
{
|
||||
void *a;
|
||||
|
||||
a = mallocz(size, 1);
|
||||
if(a == nil)
|
||||
sysfatal("%r");
|
||||
return a;
|
||||
}
|
||||
|
||||
char*
|
||||
estrdup(char *s)
|
||||
{
|
||||
s = strdup(s);
|
||||
if(s == nil)
|
||||
sysfatal("%r");
|
||||
return s;
|
||||
}
|
||||
|
||||
/*
|
||||
* like tokenize but obey "" quoting
|
||||
*/
|
||||
int
|
||||
tokenize822(char *str, char **args, int max)
|
||||
{
|
||||
int na;
|
||||
int intok = 0, inquote = 0;
|
||||
|
||||
if(max <= 0)
|
||||
return 0;
|
||||
for(na=0; ;str++)
|
||||
switch(*str) {
|
||||
case ' ':
|
||||
case '\t':
|
||||
if(inquote)
|
||||
goto Default;
|
||||
/* fall through */
|
||||
case '\n':
|
||||
*str = 0;
|
||||
if(!intok)
|
||||
continue;
|
||||
intok = 0;
|
||||
if(na < max)
|
||||
continue;
|
||||
/* fall through */
|
||||
case 0:
|
||||
return na;
|
||||
case '"':
|
||||
inquote ^= 1;
|
||||
/* fall through */
|
||||
Default:
|
||||
default:
|
||||
if(intok)
|
||||
continue;
|
||||
args[na++] = str;
|
||||
intok = 1;
|
||||
}
|
||||
return 0; /* can't get here; silence compiler */
|
||||
}
|
||||
|
||||
Addr*
|
||||
readaddrs(char *file, Addr *a)
|
||||
{
|
||||
int fd;
|
||||
int i, n;
|
||||
char buf[8*1024];
|
||||
char *f[128];
|
||||
Addr **l;
|
||||
Addr *first;
|
||||
|
||||
/* add to end */
|
||||
first = a;
|
||||
for(l = &first; *l != nil; l = &(*l)->next)
|
||||
;
|
||||
|
||||
/* read in the addresses */
|
||||
fd = open(file, OREAD);
|
||||
if(fd < 0)
|
||||
return first;
|
||||
n = read(fd, buf, sizeof(buf)-1);
|
||||
close(fd);
|
||||
if(n <= 0)
|
||||
return first;
|
||||
buf[n] = 0;
|
||||
|
||||
n = tokenize822(buf, f, nelem(f));
|
||||
for(i = 0; i < n; i++){
|
||||
*l = a = emalloc(sizeof *a);
|
||||
l = &a->next;
|
||||
a->val = estrdup(f[i]);
|
||||
}
|
||||
return first;
|
||||
}
|
||||
89
src/cmd/upas/filterkit/token.c
Normal file
89
src/cmd/upas/filterkit/token.c
Normal file
@@ -0,0 +1,89 @@
|
||||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <libsec.h>
|
||||
#include <String.h>
|
||||
#include "dat.h"
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "usage: %s key [token]\n", argv0);
|
||||
exits("usage");
|
||||
}
|
||||
|
||||
static String*
|
||||
mktoken(char *key, long thetime)
|
||||
{
|
||||
char *now;
|
||||
uchar digest[SHA1dlen];
|
||||
char token[64];
|
||||
String *s;
|
||||
|
||||
now = ctime(thetime);
|
||||
memset(now+11, ':', 8);
|
||||
hmac_sha1((uchar*)now, strlen(now), (uchar*)key, strlen(key), digest, nil);
|
||||
enc64(token, sizeof token, digest, sizeof digest);
|
||||
s = s_new();
|
||||
s_nappend(s, token, 5);
|
||||
return s;
|
||||
}
|
||||
|
||||
static char*
|
||||
check_token(char *key, char *file)
|
||||
{
|
||||
String *s;
|
||||
long now;
|
||||
int i;
|
||||
char buf[1024];
|
||||
int fd;
|
||||
|
||||
fd = open(file, OREAD);
|
||||
if(fd < 0)
|
||||
return "no match";
|
||||
i = read(fd, buf, sizeof(buf)-1);
|
||||
close(fd);
|
||||
if(i < 0)
|
||||
return "no match";
|
||||
buf[i] = 0;
|
||||
|
||||
now = time(0);
|
||||
|
||||
for(i = 0; i < 14; i++){
|
||||
s = mktoken(key, now-24*60*60*i);
|
||||
if(strstr(buf, s_to_c(s)) != nil){
|
||||
s_free(s);
|
||||
return nil;
|
||||
}
|
||||
s_free(s);
|
||||
}
|
||||
return "no match";
|
||||
}
|
||||
|
||||
static char*
|
||||
create_token(char *key)
|
||||
{
|
||||
String *s;
|
||||
|
||||
s = mktoken(key, time(0));
|
||||
print("%s", s_to_c(s));
|
||||
return nil;
|
||||
}
|
||||
|
||||
void
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
ARGBEGIN {
|
||||
} ARGEND;
|
||||
|
||||
switch(argc){
|
||||
case 2:
|
||||
exits(check_token(argv[0], argv[1]));
|
||||
break;
|
||||
case 1:
|
||||
exits(create_token(argv[0]));
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
exits(0);
|
||||
}
|
||||
221
src/cmd/upas/fs/dat.h
Normal file
221
src/cmd/upas/fs/dat.h
Normal file
@@ -0,0 +1,221 @@
|
||||
typedef struct Message Message;
|
||||
struct Message
|
||||
{
|
||||
int id;
|
||||
int refs;
|
||||
int subname;
|
||||
char name[Elemlen];
|
||||
|
||||
// pointers into message
|
||||
char *start; // start of message
|
||||
char *end; // end of message
|
||||
char *header; // start of header
|
||||
char *hend; // end of header
|
||||
int hlen; // length of header minus ignored fields
|
||||
char *mheader; // start of mime header
|
||||
char *mhend; // end of mime header
|
||||
char *body; // start of body
|
||||
char *bend; // end of body
|
||||
char *rbody; // raw (unprocessed) body
|
||||
char *rbend; // end of raw (unprocessed) body
|
||||
char *lim;
|
||||
char deleted;
|
||||
char inmbox;
|
||||
char mallocd; // message is malloc'd
|
||||
char ballocd; // body is malloc'd
|
||||
char hallocd; // header is malloce'd
|
||||
|
||||
// mail info
|
||||
String *unixheader;
|
||||
String *unixfrom;
|
||||
String *unixdate;
|
||||
String *from822;
|
||||
String *sender822;
|
||||
String *to822;
|
||||
String *bcc822;
|
||||
String *cc822;
|
||||
String *replyto822;
|
||||
String *date822;
|
||||
String *inreplyto822;
|
||||
String *subject822;
|
||||
String *messageid822;
|
||||
String *addrs;
|
||||
String *mimeversion;
|
||||
String *sdigest;
|
||||
|
||||
// mime info
|
||||
String *boundary;
|
||||
String *type;
|
||||
int encoding;
|
||||
int disposition;
|
||||
String *charset;
|
||||
String *filename;
|
||||
int converted;
|
||||
int decoded;
|
||||
char lines[10]; // number of lines in rawbody
|
||||
|
||||
Message *next; // same level
|
||||
Message *part; // down a level
|
||||
Message *whole; // up a level
|
||||
|
||||
uchar digest[SHA1dlen];
|
||||
|
||||
vlong imapuid; // used by imap4
|
||||
|
||||
char uidl[80]; // used by pop3
|
||||
int mesgno;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
// encodings
|
||||
Enone= 0,
|
||||
Ebase64,
|
||||
Equoted,
|
||||
|
||||
// disposition possibilities
|
||||
Dnone= 0,
|
||||
Dinline,
|
||||
Dfile,
|
||||
Dignore,
|
||||
|
||||
PAD64= '=',
|
||||
};
|
||||
|
||||
typedef struct Mailbox Mailbox;
|
||||
struct Mailbox
|
||||
{
|
||||
QLock ql; /* jpc named Qlock */
|
||||
int refs;
|
||||
Mailbox *next;
|
||||
int id;
|
||||
int dolock; // lock when syncing?
|
||||
int std;
|
||||
char name[Elemlen];
|
||||
char path[Pathlen];
|
||||
Dir *d;
|
||||
Message *root;
|
||||
int vers; // goes up each time mailbox is read
|
||||
|
||||
ulong waketime;
|
||||
char *(*sync)(Mailbox*, int);
|
||||
void (*close)(Mailbox*);
|
||||
char *(*fetch)(Mailbox*, Message*);
|
||||
char *(*ctl)(Mailbox*, int, char**);
|
||||
void *aux; // private to Mailbox implementation
|
||||
};
|
||||
|
||||
typedef char *Mailboxinit(Mailbox*, char*);
|
||||
|
||||
extern Message *root;
|
||||
extern Mailboxinit plan9mbox;
|
||||
extern Mailboxinit pop3mbox;
|
||||
extern Mailboxinit imap4mbox;
|
||||
|
||||
char* syncmbox(Mailbox*, int);
|
||||
char* geterrstr(void);
|
||||
void* emalloc(ulong);
|
||||
void* erealloc(void*, ulong);
|
||||
Message* newmessage(Message*);
|
||||
void delmessage(Mailbox*, Message*);
|
||||
void delmessages(int, char**);
|
||||
int newid(void);
|
||||
void mailplumb(Mailbox*, Message*, int);
|
||||
char* newmbox(char*, char*, int);
|
||||
void freembox(char*);
|
||||
void logmsg(char*, Message*);
|
||||
void msgincref(Message*);
|
||||
void msgdecref(Mailbox*, Message*);
|
||||
void mboxincref(Mailbox*);
|
||||
void mboxdecref(Mailbox*);
|
||||
void convert(Message*);
|
||||
void decode(Message*);
|
||||
int cistrncmp(char*, char*, int);
|
||||
int cistrcmp(char*, char*);
|
||||
int latin1toutf(char*, char*, char*);
|
||||
int windows1257toutf(char*, char*, char*);
|
||||
int decquoted(char*, char*, char*);
|
||||
int xtoutf(char*, char**, char*, char*);
|
||||
void countlines(Message*);
|
||||
int headerlen(Message*);
|
||||
void parse(Message*, int, Mailbox*, int);
|
||||
void parseheaders(Message*, int, Mailbox*, int);
|
||||
void parsebody(Message*, Mailbox*);
|
||||
void parseunix(Message*);
|
||||
String* date822tounix(char*);
|
||||
int fidmboxrefs(Mailbox*);
|
||||
int hashmboxrefs(Mailbox*);
|
||||
void checkmboxrefs(void);
|
||||
|
||||
extern int debug;
|
||||
extern int fflag;
|
||||
extern int logging;
|
||||
extern char user[Elemlen];
|
||||
extern char stdmbox[Pathlen];
|
||||
extern QLock mbllock;
|
||||
extern Mailbox *mbl;
|
||||
extern char *mntpt;
|
||||
extern int biffing;
|
||||
extern int plumbing;
|
||||
extern char* Enotme;
|
||||
|
||||
enum
|
||||
{
|
||||
/* mail subobjects */
|
||||
Qbody,
|
||||
Qbcc,
|
||||
Qcc,
|
||||
Qdate,
|
||||
Qdigest,
|
||||
Qdisposition,
|
||||
Qfilename,
|
||||
Qfrom,
|
||||
Qheader,
|
||||
Qinreplyto,
|
||||
Qlines,
|
||||
Qmimeheader,
|
||||
Qmessageid,
|
||||
Qraw,
|
||||
Qrawbody,
|
||||
Qrawheader,
|
||||
Qrawunix,
|
||||
Qreplyto,
|
||||
Qsender,
|
||||
Qsubject,
|
||||
Qto,
|
||||
Qtype,
|
||||
Qunixheader,
|
||||
Qinfo,
|
||||
Qunixdate,
|
||||
Qmax,
|
||||
|
||||
/* other files */
|
||||
Qtop,
|
||||
Qmbox,
|
||||
Qdir,
|
||||
Qctl,
|
||||
Qmboxctl,
|
||||
};
|
||||
|
||||
#define PATH(id, f) ((((id)&0xfffff)<<10) | (f))
|
||||
#define FILE(p) ((p) & 0x3ff)
|
||||
|
||||
/* char *dirtab[]; jpc */
|
||||
|
||||
// hash table to aid in name lookup, all files have an entry
|
||||
typedef struct Hash Hash;
|
||||
struct Hash {
|
||||
Hash *next;
|
||||
char *name;
|
||||
ulong ppath;
|
||||
Qid qid;
|
||||
Mailbox *mb;
|
||||
Message *m;
|
||||
};
|
||||
|
||||
Hash *hlook(ulong, char*);
|
||||
void henter(ulong, char*, Qid, Message*, Mailbox*);
|
||||
void hfree(ulong, char*);
|
||||
|
||||
ulong msgallocd, msgfreed;
|
||||
|
||||
1704
src/cmd/upas/fs/fs.c
Normal file
1704
src/cmd/upas/fs/fs.c
Normal file
File diff suppressed because it is too large
Load Diff
876
src/cmd/upas/fs/imap4.c
Normal file
876
src/cmd/upas/fs/imap4.c
Normal file
@@ -0,0 +1,876 @@
|
||||
#include "common.h"
|
||||
#include <ctype.h>
|
||||
#include <plumb.h>
|
||||
#include <libsec.h>
|
||||
#include <auth.h>
|
||||
#include "dat.h"
|
||||
|
||||
#pragma varargck argpos imap4cmd 2
|
||||
#pragma varargck type "Z" char*
|
||||
|
||||
int doublequote(Fmt*);
|
||||
int pipeline = 1;
|
||||
|
||||
/* static char Eio[] = "i/o error"; jpc */
|
||||
|
||||
typedef struct Imap Imap;
|
||||
struct Imap {
|
||||
char *freep; // free this to free the strings below
|
||||
|
||||
char *host;
|
||||
char *user;
|
||||
char *mbox;
|
||||
|
||||
int mustssl;
|
||||
int refreshtime;
|
||||
int debug;
|
||||
|
||||
ulong tag;
|
||||
ulong validity;
|
||||
int nmsg;
|
||||
int size;
|
||||
char *base;
|
||||
char *data;
|
||||
|
||||
vlong *uid;
|
||||
int nuid;
|
||||
int muid;
|
||||
|
||||
Thumbprint *thumb;
|
||||
|
||||
// open network connection
|
||||
Biobuf bin;
|
||||
Biobuf bout;
|
||||
int fd;
|
||||
};
|
||||
|
||||
static char*
|
||||
removecr(char *s)
|
||||
{
|
||||
char *r, *w;
|
||||
|
||||
for(r=w=s; *r; r++)
|
||||
if(*r != '\r')
|
||||
*w++ = *r;
|
||||
*w = '\0';
|
||||
return s;
|
||||
}
|
||||
|
||||
//
|
||||
// send imap4 command
|
||||
//
|
||||
static void
|
||||
imap4cmd(Imap *imap, char *fmt, ...)
|
||||
{
|
||||
char buf[128], *p;
|
||||
va_list va;
|
||||
|
||||
va_start(va, fmt);
|
||||
p = buf+sprint(buf, "9X%lud ", imap->tag);
|
||||
vseprint(p, buf+sizeof(buf), fmt, va);
|
||||
va_end(va);
|
||||
|
||||
p = buf+strlen(buf);
|
||||
if(p > (buf+sizeof(buf)-3))
|
||||
sysfatal("imap4 command too long");
|
||||
|
||||
if(imap->debug)
|
||||
fprint(2, "-> %s\n", buf);
|
||||
strcpy(p, "\r\n");
|
||||
Bwrite(&imap->bout, buf, strlen(buf));
|
||||
Bflush(&imap->bout);
|
||||
}
|
||||
|
||||
enum {
|
||||
OK,
|
||||
NO,
|
||||
BAD,
|
||||
BYE,
|
||||
EXISTS,
|
||||
STATUS,
|
||||
FETCH,
|
||||
UNKNOWN,
|
||||
};
|
||||
|
||||
static char *verblist[] = {
|
||||
[OK] "OK",
|
||||
[NO] "NO",
|
||||
[BAD] "BAD",
|
||||
[BYE] "BYE",
|
||||
[EXISTS] "EXISTS",
|
||||
[STATUS] "STATUS",
|
||||
[FETCH] "FETCH",
|
||||
};
|
||||
|
||||
static int
|
||||
verbcode(char *verb)
|
||||
{
|
||||
int i;
|
||||
char *q;
|
||||
|
||||
if(q = strchr(verb, ' '))
|
||||
*q = '\0';
|
||||
|
||||
for(i=0; i<nelem(verblist); i++)
|
||||
if(verblist[i] && strcmp(verblist[i], verb)==0){
|
||||
if(q)
|
||||
*q = ' ';
|
||||
return i;
|
||||
}
|
||||
if(q)
|
||||
*q = ' ';
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
static void
|
||||
strupr(char *s)
|
||||
{
|
||||
for(; *s; s++)
|
||||
if('a' <= *s && *s <= 'z')
|
||||
*s += 'A'-'a';
|
||||
}
|
||||
|
||||
static void
|
||||
imapgrow(Imap *imap, int n)
|
||||
{
|
||||
int i;
|
||||
|
||||
if(imap->data == nil){
|
||||
imap->base = emalloc(n+1);
|
||||
imap->data = imap->base;
|
||||
imap->size = n+1;
|
||||
}
|
||||
if(n >= imap->size){
|
||||
// friggin microsoft - reallocate
|
||||
i = imap->data - imap->base;
|
||||
imap->base = erealloc(imap->base, i+n+1);
|
||||
imap->data = imap->base + i;
|
||||
imap->size = n+1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// get imap4 response line. there might be various
|
||||
// data or other informational lines mixed in.
|
||||
//
|
||||
static char*
|
||||
imap4resp(Imap *imap)
|
||||
{
|
||||
char *line, *p, *ep, *op, *q, *r, *en, *verb;
|
||||
int i, n;
|
||||
static char error[256];
|
||||
|
||||
while(p = Brdline(&imap->bin, '\n')){
|
||||
ep = p+Blinelen(&imap->bin);
|
||||
while(ep > p && (ep[-1]=='\n' || ep[-1]=='\r'))
|
||||
*--ep = '\0';
|
||||
|
||||
if(imap->debug)
|
||||
fprint(2, "<- %s\n", p);
|
||||
strupr(p);
|
||||
|
||||
switch(p[0]){
|
||||
case '+':
|
||||
if(imap->tag == 0)
|
||||
fprint(2, "unexpected: %s\n", p);
|
||||
break;
|
||||
|
||||
// ``unsolicited'' information; everything happens here.
|
||||
case '*':
|
||||
if(p[1]!=' ')
|
||||
continue;
|
||||
p += 2;
|
||||
line = p;
|
||||
n = strtol(p, &p, 10);
|
||||
if(*p==' ')
|
||||
p++;
|
||||
verb = p;
|
||||
|
||||
if(p = strchr(verb, ' '))
|
||||
p++;
|
||||
else
|
||||
p = verb+strlen(verb);
|
||||
|
||||
switch(verbcode(verb)){
|
||||
case OK:
|
||||
case NO:
|
||||
case BAD:
|
||||
// human readable text at p;
|
||||
break;
|
||||
case BYE:
|
||||
// early disconnect
|
||||
// human readable text at p;
|
||||
break;
|
||||
|
||||
// * 32 EXISTS
|
||||
case EXISTS:
|
||||
imap->nmsg = n;
|
||||
break;
|
||||
|
||||
// * STATUS Inbox (MESSAGES 2 UIDVALIDITY 960164964)
|
||||
case STATUS:
|
||||
if(q = strstr(p, "MESSAGES"))
|
||||
imap->nmsg = atoi(q+8);
|
||||
if(q = strstr(p, "UIDVALIDITY"))
|
||||
imap->validity = strtoul(q+11, 0, 10);
|
||||
break;
|
||||
|
||||
case FETCH:
|
||||
// * 1 FETCH (uid 8889 RFC822.SIZE 3031 body[] {3031}
|
||||
// <3031 bytes of data>
|
||||
// )
|
||||
if(strstr(p, "RFC822.SIZE") && strstr(p, "BODY[]")){
|
||||
if((q = strchr(p, '{'))
|
||||
&& (n=strtol(q+1, &en, 0), *en=='}')){
|
||||
if(imap->data == nil || n >= imap->size)
|
||||
imapgrow(imap, n);
|
||||
if((i = Bread(&imap->bin, imap->data, n)) != n){
|
||||
snprint(error, sizeof error,
|
||||
"short read %d != %d: %r\n",
|
||||
i, n);
|
||||
return error;
|
||||
}
|
||||
if(imap->debug)
|
||||
fprint(2, "<- read %d bytes\n", n);
|
||||
imap->data[n] = '\0';
|
||||
if(imap->debug)
|
||||
fprint(2, "<- %s\n", imap->data);
|
||||
imap->data += n;
|
||||
imap->size -= n;
|
||||
p = Brdline(&imap->bin, '\n');
|
||||
if(imap->debug)
|
||||
fprint(2, "<- ignoring %.*s\n",
|
||||
Blinelen(&imap->bin), p);
|
||||
}else if((q = strchr(p, '"')) && (r = strchr(q+1, '"'))){
|
||||
*r = '\0';
|
||||
q++;
|
||||
n = r-q;
|
||||
if(imap->data == nil || n >= imap->size)
|
||||
imapgrow(imap, n);
|
||||
memmove(imap->data, q, n);
|
||||
imap->data[n] = '\0';
|
||||
imap->data += n;
|
||||
imap->size -= n;
|
||||
}else
|
||||
return "confused about FETCH response";
|
||||
break;
|
||||
}
|
||||
|
||||
// * 1 FETCH (UID 1 RFC822.SIZE 511)
|
||||
if(q=strstr(p, "RFC822.SIZE")){
|
||||
imap->size = atoi(q+11);
|
||||
break;
|
||||
}
|
||||
|
||||
// * 1 FETCH (UID 1 RFC822.HEADER {496}
|
||||
// <496 bytes of data>
|
||||
// )
|
||||
// * 1 FETCH (UID 1 RFC822.HEADER "data")
|
||||
if(strstr(p, "RFC822.HEADER") || strstr(p, "RFC822.TEXT")){
|
||||
if((q = strchr(p, '{'))
|
||||
&& (n=strtol(q+1, &en, 0), *en=='}')){
|
||||
if(imap->data == nil || n >= imap->size)
|
||||
imapgrow(imap, n);
|
||||
if((i = Bread(&imap->bin, imap->data, n)) != n){
|
||||
snprint(error, sizeof error,
|
||||
"short read %d != %d: %r\n",
|
||||
i, n);
|
||||
return error;
|
||||
}
|
||||
if(imap->debug)
|
||||
fprint(2, "<- read %d bytes\n", n);
|
||||
imap->data[n] = '\0';
|
||||
if(imap->debug)
|
||||
fprint(2, "<- %s\n", imap->data);
|
||||
imap->data += n;
|
||||
imap->size -= n;
|
||||
p = Brdline(&imap->bin, '\n');
|
||||
if(imap->debug)
|
||||
fprint(2, "<- ignoring %.*s\n",
|
||||
Blinelen(&imap->bin), p);
|
||||
}else if((q = strchr(p, '"')) && (r = strchr(q+1, '"'))){
|
||||
*r = '\0';
|
||||
q++;
|
||||
n = r-q;
|
||||
if(imap->data == nil || n >= imap->size)
|
||||
imapgrow(imap, n);
|
||||
memmove(imap->data, q, n);
|
||||
imap->data[n] = '\0';
|
||||
imap->data += n;
|
||||
imap->size -= n;
|
||||
}else
|
||||
return "confused about FETCH response";
|
||||
break;
|
||||
}
|
||||
|
||||
// * 1 FETCH (UID 1)
|
||||
// * 2 FETCH (UID 6)
|
||||
if(q = strstr(p, "UID")){
|
||||
if(imap->nuid < imap->muid)
|
||||
imap->uid[imap->nuid++] = ((vlong)imap->validity<<32)|strtoul(q+3, nil, 10);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(imap->tag == 0)
|
||||
return line;
|
||||
break;
|
||||
|
||||
case '9': // response to our message
|
||||
op = p;
|
||||
if(p[1]=='X' && strtoul(p+2, &p, 10)==imap->tag){
|
||||
while(*p==' ')
|
||||
p++;
|
||||
imap->tag++;
|
||||
return p;
|
||||
}
|
||||
fprint(2, "expected %lud; got %s\n", imap->tag, op);
|
||||
break;
|
||||
|
||||
default:
|
||||
if(imap->debug || *p)
|
||||
fprint(2, "unexpected line: %s\n", p);
|
||||
}
|
||||
}
|
||||
snprint(error, sizeof error, "i/o error: %r\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
static int
|
||||
isokay(char *resp)
|
||||
{
|
||||
return strncmp(resp, "OK", 2)==0;
|
||||
}
|
||||
|
||||
//
|
||||
// log in to IMAP4 server, select mailbox, no SSL at the moment
|
||||
//
|
||||
static char*
|
||||
imap4login(Imap *imap)
|
||||
{
|
||||
char *s;
|
||||
UserPasswd *up;
|
||||
|
||||
imap->tag = 0;
|
||||
s = imap4resp(imap);
|
||||
if(!isokay(s))
|
||||
return "error in initial IMAP handshake";
|
||||
|
||||
if(imap->user != nil)
|
||||
up = auth_getuserpasswd(auth_getkey, "proto=pass service=imap server=%q user=%q", imap->host, imap->user);
|
||||
else
|
||||
up = auth_getuserpasswd(auth_getkey, "proto=pass service=imap server=%q", imap->host);
|
||||
if(up == nil)
|
||||
return "cannot find IMAP password";
|
||||
|
||||
imap->tag = 1;
|
||||
imap4cmd(imap, "LOGIN %Z %Z", up->user, up->passwd);
|
||||
free(up);
|
||||
if(!isokay(s = imap4resp(imap)))
|
||||
return s;
|
||||
|
||||
imap4cmd(imap, "SELECT %Z", imap->mbox);
|
||||
if(!isokay(s = imap4resp(imap)))
|
||||
return s;
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
//
|
||||
// push tls onto a connection
|
||||
//
|
||||
int
|
||||
mypushtls(int fd)
|
||||
{
|
||||
int p[2];
|
||||
char buf[10];
|
||||
|
||||
if(pipe(p) < 0)
|
||||
return -1;
|
||||
|
||||
switch(fork()){
|
||||
case -1:
|
||||
close(p[0]);
|
||||
close(p[1]);
|
||||
return -1;
|
||||
case 0:
|
||||
close(p[1]);
|
||||
dup(p[0], 0);
|
||||
dup(p[0], 1);
|
||||
sprint(buf, "/fd/%d", fd);
|
||||
execl("/bin/tlsrelay", "tlsrelay", "-f", buf, nil);
|
||||
_exits(nil);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
close(fd);
|
||||
close(p[0]);
|
||||
return p[1];
|
||||
}
|
||||
|
||||
//
|
||||
// dial and handshake with the imap server
|
||||
//
|
||||
static char*
|
||||
imap4dial(Imap *imap)
|
||||
{
|
||||
char *err, *port;
|
||||
uchar digest[SHA1dlen];
|
||||
int sfd;
|
||||
TLSconn conn;
|
||||
|
||||
if(imap->fd >= 0){
|
||||
imap4cmd(imap, "noop");
|
||||
if(isokay(imap4resp(imap)))
|
||||
return nil;
|
||||
close(imap->fd);
|
||||
imap->fd = -1;
|
||||
}
|
||||
|
||||
if(imap->mustssl)
|
||||
port = "imaps";
|
||||
else
|
||||
port = "imap4";
|
||||
|
||||
if((imap->fd = dial(netmkaddr(imap->host, "net", port), 0, 0, 0)) < 0)
|
||||
return geterrstr();
|
||||
|
||||
if(imap->mustssl){
|
||||
memset(&conn, 0, sizeof conn);
|
||||
sfd = tlsClient(imap->fd, &conn);
|
||||
if(sfd < 0)
|
||||
sysfatal("tlsClient: %r");
|
||||
if(conn.cert==nil || conn.certlen <= 0)
|
||||
sysfatal("server did not provide TLS certificate");
|
||||
sha1(conn.cert, conn.certlen, digest, nil);
|
||||
if(!imap->thumb || !okThumbprint(digest, imap->thumb)){
|
||||
fmtinstall('H', encodefmt);
|
||||
sysfatal("server certificate %.*H not recognized", SHA1dlen, digest);
|
||||
}
|
||||
free(conn.cert);
|
||||
close(imap->fd);
|
||||
imap->fd = sfd;
|
||||
|
||||
if(imap->debug){
|
||||
char fn[128];
|
||||
int fd;
|
||||
|
||||
snprint(fn, sizeof fn, "%s/ctl", conn.dir);
|
||||
fd = open(fn, ORDWR);
|
||||
if(fd < 0)
|
||||
fprint(2, "opening ctl: %r\n");
|
||||
if(fprint(fd, "debug") < 0)
|
||||
fprint(2, "writing ctl: %r\n");
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
Binit(&imap->bin, imap->fd, OREAD);
|
||||
Binit(&imap->bout, imap->fd, OWRITE);
|
||||
|
||||
if(err = imap4login(imap)) {
|
||||
close(imap->fd);
|
||||
return err;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
//
|
||||
// close connection
|
||||
//
|
||||
#if 0 /* jpc */
|
||||
static void
|
||||
imap4hangup(Imap *imap)
|
||||
{
|
||||
imap4cmd(imap, "LOGOUT");
|
||||
imap4resp(imap);
|
||||
close(imap->fd);
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// download a single message
|
||||
//
|
||||
static char*
|
||||
imap4fetch(Mailbox *mb, Message *m)
|
||||
{
|
||||
int i;
|
||||
char *p, *s, sdigest[2*SHA1dlen+1];
|
||||
Imap *imap;
|
||||
|
||||
imap = mb->aux;
|
||||
|
||||
imap->size = 0;
|
||||
|
||||
if(!isokay(s = imap4resp(imap)))
|
||||
return s;
|
||||
|
||||
p = imap->base;
|
||||
if(p == nil)
|
||||
return "did not get message body";
|
||||
|
||||
removecr(p);
|
||||
free(m->start);
|
||||
m->start = p;
|
||||
m->end = p+strlen(p);
|
||||
m->bend = m->rbend = m->end;
|
||||
m->header = m->start;
|
||||
|
||||
imap->base = nil;
|
||||
imap->data = nil;
|
||||
|
||||
parse(m, 0, mb, 1);
|
||||
|
||||
// digest headers
|
||||
sha1((uchar*)m->start, m->end - m->start, m->digest, nil);
|
||||
for(i = 0; i < SHA1dlen; i++)
|
||||
sprint(sdigest+2*i, "%2.2ux", m->digest[i]);
|
||||
m->sdigest = s_copy(sdigest);
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
//
|
||||
// check for new messages on imap4 server
|
||||
// download new messages, mark deleted messages
|
||||
//
|
||||
static char*
|
||||
imap4read(Imap *imap, Mailbox *mb, int doplumb)
|
||||
{
|
||||
char *s;
|
||||
int i, ignore, nnew, t;
|
||||
Message *m, *next, **l;
|
||||
|
||||
imap4cmd(imap, "STATUS %Z (MESSAGES UIDVALIDITY)", imap->mbox);
|
||||
if(!isokay(s = imap4resp(imap)))
|
||||
return s;
|
||||
|
||||
imap->nuid = 0;
|
||||
imap->uid = erealloc(imap->uid, imap->nmsg*sizeof(imap->uid[0]));
|
||||
imap->muid = imap->nmsg;
|
||||
|
||||
if(imap->nmsg > 0){
|
||||
imap4cmd(imap, "UID FETCH 1:* UID");
|
||||
if(!isokay(s = imap4resp(imap)))
|
||||
return s;
|
||||
}
|
||||
|
||||
l = &mb->root->part;
|
||||
for(i=0; i<imap->nuid; i++){
|
||||
ignore = 0;
|
||||
while(*l != nil){
|
||||
if((*l)->imapuid == imap->uid[i]){
|
||||
ignore = 1;
|
||||
l = &(*l)->next;
|
||||
break;
|
||||
}else{
|
||||
// old mail, we don't have it anymore
|
||||
if(doplumb)
|
||||
mailplumb(mb, *l, 1);
|
||||
(*l)->inmbox = 0;
|
||||
(*l)->deleted = 1;
|
||||
l = &(*l)->next;
|
||||
}
|
||||
}
|
||||
if(ignore)
|
||||
continue;
|
||||
|
||||
// new message
|
||||
m = newmessage(mb->root);
|
||||
m->mallocd = 1;
|
||||
m->inmbox = 1;
|
||||
m->imapuid = imap->uid[i];
|
||||
|
||||
// add to chain, will download soon
|
||||
*l = m;
|
||||
l = &m->next;
|
||||
}
|
||||
|
||||
// whatever is left at the end of the chain is gone
|
||||
while(*l != nil){
|
||||
if(doplumb)
|
||||
mailplumb(mb, *l, 1);
|
||||
(*l)->inmbox = 0;
|
||||
(*l)->deleted = 1;
|
||||
l = &(*l)->next;
|
||||
}
|
||||
|
||||
// download new messages
|
||||
t = imap->tag;
|
||||
if(pipeline)
|
||||
switch(rfork(RFPROC|RFMEM)){
|
||||
case -1:
|
||||
sysfatal("rfork: %r");
|
||||
default:
|
||||
break;
|
||||
case 0:
|
||||
for(m = mb->root->part; m != nil; m = m->next){
|
||||
if(m->start != nil)
|
||||
continue;
|
||||
if(imap->debug)
|
||||
fprint(2, "9X%d UID FETCH %lud (UID RFC822.SIZE BODY[])\r\n",
|
||||
t, (ulong)m->imapuid);
|
||||
Bprint(&imap->bout, "9X%d UID FETCH %lud (UID RFC822.SIZE BODY[])\r\n",
|
||||
t++, (ulong)m->imapuid);
|
||||
}
|
||||
Bflush(&imap->bout);
|
||||
_exits(nil);
|
||||
}
|
||||
|
||||
nnew = 0;
|
||||
for(m=mb->root->part; m!=nil; m=next){
|
||||
next = m->next;
|
||||
if(m->start != nil)
|
||||
continue;
|
||||
|
||||
if(!pipeline){
|
||||
Bprint(&imap->bout, "9X%lud UID FETCH %lud (UID RFC822.SIZE BODY[])\r\n",
|
||||
(ulong)imap->tag, (ulong)m->imapuid);
|
||||
Bflush(&imap->bout);
|
||||
}
|
||||
|
||||
if(s = imap4fetch(mb, m)){
|
||||
// message disappeared? unchain
|
||||
fprint(2, "download %lud: %s\n", (ulong)m->imapuid, s);
|
||||
delmessage(mb, m);
|
||||
mb->root->subname--;
|
||||
continue;
|
||||
}
|
||||
nnew++;
|
||||
if(doplumb)
|
||||
mailplumb(mb, m, 0);
|
||||
}
|
||||
if(pipeline)
|
||||
waitpid();
|
||||
|
||||
if(nnew || mb->vers == 0){
|
||||
mb->vers++;
|
||||
henter(PATH(0, Qtop), mb->name,
|
||||
(Qid){PATH(mb->id, Qmbox), mb->vers, QTDIR}, nil, mb);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
//
|
||||
// sync mailbox
|
||||
//
|
||||
static void
|
||||
imap4purge(Imap *imap, Mailbox *mb)
|
||||
{
|
||||
int ndel;
|
||||
Message *m, *next;
|
||||
|
||||
ndel = 0;
|
||||
for(m=mb->root->part; m!=nil; m=next){
|
||||
next = m->next;
|
||||
if(m->deleted && m->refs==0){
|
||||
if(m->inmbox && (ulong)(m->imapuid>>32)==imap->validity){
|
||||
imap4cmd(imap, "UID STORE %lud +FLAGS (\\Deleted)", (ulong)m->imapuid);
|
||||
if(isokay(imap4resp(imap))){
|
||||
ndel++;
|
||||
delmessage(mb, m);
|
||||
}
|
||||
}else
|
||||
delmessage(mb, m);
|
||||
}
|
||||
}
|
||||
|
||||
if(ndel){
|
||||
imap4cmd(imap, "EXPUNGE");
|
||||
imap4resp(imap);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// connect to imap4 server, sync mailbox
|
||||
//
|
||||
static char*
|
||||
imap4sync(Mailbox *mb, int doplumb)
|
||||
{
|
||||
char *err;
|
||||
Imap *imap;
|
||||
|
||||
imap = mb->aux;
|
||||
|
||||
if(err = imap4dial(imap)){
|
||||
mb->waketime = time(0) + imap->refreshtime;
|
||||
return err;
|
||||
}
|
||||
|
||||
if((err = imap4read(imap, mb, doplumb)) == nil){
|
||||
imap4purge(imap, mb);
|
||||
mb->d->atime = mb->d->mtime = time(0);
|
||||
}
|
||||
/*
|
||||
* don't hang up; leave connection open for next time.
|
||||
*/
|
||||
// imap4hangup(imap);
|
||||
mb->waketime = time(0) + imap->refreshtime;
|
||||
return err;
|
||||
}
|
||||
|
||||
static char Eimap4ctl[] = "bad imap4 control message";
|
||||
|
||||
static char*
|
||||
imap4ctl(Mailbox *mb, int argc, char **argv)
|
||||
{
|
||||
int n;
|
||||
Imap *imap;
|
||||
|
||||
imap = mb->aux;
|
||||
if(argc < 1)
|
||||
return Eimap4ctl;
|
||||
|
||||
if(argc==1 && strcmp(argv[0], "debug")==0){
|
||||
imap->debug = 1;
|
||||
return nil;
|
||||
}
|
||||
|
||||
if(argc==1 && strcmp(argv[0], "nodebug")==0){
|
||||
imap->debug = 0;
|
||||
return nil;
|
||||
}
|
||||
|
||||
if(argc==1 && strcmp(argv[0], "thumbprint")==0){
|
||||
if(imap->thumb)
|
||||
freeThumbprints(imap->thumb);
|
||||
imap->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude");
|
||||
}
|
||||
if(strcmp(argv[0], "refresh")==0){
|
||||
if(argc==1){
|
||||
imap->refreshtime = 60;
|
||||
return nil;
|
||||
}
|
||||
if(argc==2){
|
||||
n = atoi(argv[1]);
|
||||
if(n < 15)
|
||||
return Eimap4ctl;
|
||||
imap->refreshtime = n;
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
return Eimap4ctl;
|
||||
}
|
||||
|
||||
//
|
||||
// free extra memory associated with mb
|
||||
//
|
||||
static void
|
||||
imap4close(Mailbox *mb)
|
||||
{
|
||||
Imap *imap;
|
||||
|
||||
imap = mb->aux;
|
||||
free(imap->freep);
|
||||
free(imap->base);
|
||||
free(imap->uid);
|
||||
if(imap->fd >= 0)
|
||||
close(imap->fd);
|
||||
free(imap);
|
||||
}
|
||||
|
||||
//
|
||||
// open mailboxes of the form /imap/host/user
|
||||
//
|
||||
char*
|
||||
imap4mbox(Mailbox *mb, char *path)
|
||||
{
|
||||
char *f[10];
|
||||
int mustssl, nf;
|
||||
Imap *imap;
|
||||
|
||||
quotefmtinstall();
|
||||
fmtinstall('Z', doublequote);
|
||||
if(strncmp(path, "/imap/", 6) != 0 && strncmp(path, "/imaps/", 7) != 0)
|
||||
return Enotme;
|
||||
mustssl = (strncmp(path, "/imaps/", 7) == 0);
|
||||
|
||||
path = strdup(path);
|
||||
if(path == nil)
|
||||
return "out of memory";
|
||||
|
||||
nf = getfields(path, f, 5, 0, "/");
|
||||
if(nf < 3){
|
||||
free(path);
|
||||
return "bad imap path syntax /imap[s]/system[/user[/mailbox]]";
|
||||
}
|
||||
|
||||
imap = emalloc(sizeof(*imap));
|
||||
imap->fd = -1;
|
||||
imap->debug = debug;
|
||||
imap->freep = path;
|
||||
imap->mustssl = mustssl;
|
||||
imap->host = f[2];
|
||||
if(nf < 4)
|
||||
imap->user = nil;
|
||||
else
|
||||
imap->user = f[3];
|
||||
if(nf < 5)
|
||||
imap->mbox = "Inbox";
|
||||
else
|
||||
imap->mbox = f[4];
|
||||
imap->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude");
|
||||
|
||||
mb->aux = imap;
|
||||
mb->sync = imap4sync;
|
||||
mb->close = imap4close;
|
||||
mb->ctl = imap4ctl;
|
||||
mb->d = emalloc(sizeof(*mb->d));
|
||||
//mb->fetch = imap4fetch;
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
//
|
||||
// Formatter for %"
|
||||
// Use double quotes to protect white space, frogs, \ and "
|
||||
//
|
||||
enum
|
||||
{
|
||||
Qok = 0,
|
||||
Qquote,
|
||||
Qbackslash,
|
||||
};
|
||||
|
||||
static int
|
||||
needtoquote(Rune r)
|
||||
{
|
||||
if(r >= Runeself)
|
||||
return Qquote;
|
||||
if(r <= ' ')
|
||||
return Qquote;
|
||||
if(r=='\\' || r=='"')
|
||||
return Qbackslash;
|
||||
return Qok;
|
||||
}
|
||||
|
||||
int
|
||||
doublequote(Fmt *f)
|
||||
{
|
||||
char *s, *t;
|
||||
int w, quotes;
|
||||
Rune r;
|
||||
|
||||
s = va_arg(f->args, char*);
|
||||
if(s == nil || *s == '\0')
|
||||
return fmtstrcpy(f, "\"\"");
|
||||
|
||||
quotes = 0;
|
||||
for(t=s; *t; t+=w){
|
||||
w = chartorune(&r, t);
|
||||
quotes |= needtoquote(r);
|
||||
}
|
||||
if(quotes == 0)
|
||||
return fmtstrcpy(f, s);
|
||||
|
||||
fmtrune(f, '"');
|
||||
for(t=s; *t; t+=w){
|
||||
w = chartorune(&r, t);
|
||||
if(needtoquote(r) == Qbackslash)
|
||||
fmtrune(f, '\\');
|
||||
fmtrune(f, r);
|
||||
}
|
||||
return fmtrune(f, '"');
|
||||
}
|
||||
1601
src/cmd/upas/fs/mbox.c
Normal file
1601
src/cmd/upas/fs/mbox.c
Normal file
File diff suppressed because it is too large
Load Diff
29
src/cmd/upas/fs/mkfile
Normal file
29
src/cmd/upas/fs/mkfile
Normal file
@@ -0,0 +1,29 @@
|
||||
<$PLAN9/src/mkhdr
|
||||
|
||||
TARG= fs\
|
||||
|
||||
OFILES=\
|
||||
fs.$O\
|
||||
imap4.$O\
|
||||
mbox.$O\
|
||||
plan9.$O\
|
||||
pop3.$O\
|
||||
strtotm.$O\
|
||||
|
||||
LIB=../common/libcommon.a\
|
||||
# /usr/local/plan9/lib/libthread.a
|
||||
|
||||
HFILES= ../common/common.h\
|
||||
dat.h
|
||||
|
||||
BIN=$PLAN9/bin/upas
|
||||
|
||||
UPDATE=\
|
||||
mkfile\
|
||||
$HFILES\
|
||||
${TARG:%=%.c}\
|
||||
${OFILES:%.$O=%.c}\
|
||||
|
||||
<$PLAN9/src/mkone
|
||||
CFLAGS=$CFLAGS -I../common
|
||||
# CFLAGS=$CFLAGS -I/sys/include -I../common
|
||||
27
src/cmd/upas/fs/mkfile.9
Normal file
27
src/cmd/upas/fs/mkfile.9
Normal file
@@ -0,0 +1,27 @@
|
||||
</$objtype/mkfile
|
||||
|
||||
TARG= fs\
|
||||
|
||||
OFILES=\
|
||||
fs.$O\
|
||||
imap4.$O\
|
||||
mbox.$O\
|
||||
plan9.$O\
|
||||
pop3.$O\
|
||||
strtotm.$O\
|
||||
|
||||
LIB=../common/libcommon.a$O\
|
||||
|
||||
HFILES= ../common/common.h\
|
||||
dat.h
|
||||
|
||||
BIN=/$objtype/bin/upas
|
||||
|
||||
UPDATE=\
|
||||
mkfile\
|
||||
$HFILES\
|
||||
${TARG:%=%.c}\
|
||||
${OFILES:%.$O=%.c}\
|
||||
|
||||
</sys/src/cmd/mkone
|
||||
CFLAGS=$CFLAGS -I/sys/include -I../common
|
||||
405
src/cmd/upas/fs/plan9.c
Normal file
405
src/cmd/upas/fs/plan9.c
Normal file
@@ -0,0 +1,405 @@
|
||||
#include "common.h"
|
||||
#include <ctype.h>
|
||||
#include <plumb.h>
|
||||
#include <libsec.h>
|
||||
#include "dat.h"
|
||||
|
||||
enum {
|
||||
Buffersize = 64*1024,
|
||||
};
|
||||
|
||||
typedef struct Inbuf Inbuf;
|
||||
struct Inbuf
|
||||
{
|
||||
int fd;
|
||||
uchar *lim;
|
||||
uchar *rptr;
|
||||
uchar *wptr;
|
||||
uchar data[Buffersize+7];
|
||||
};
|
||||
|
||||
static void
|
||||
addtomessage(Message *m, uchar *p, int n, int done)
|
||||
{
|
||||
int i, len;
|
||||
|
||||
// add to message (+ 1 in malloc is for a trailing null)
|
||||
if(m->lim - m->end < n){
|
||||
if(m->start != nil){
|
||||
i = m->end-m->start;
|
||||
if(done)
|
||||
len = i + n;
|
||||
else
|
||||
len = (4*(i+n))/3;
|
||||
m->start = erealloc(m->start, len + 1);
|
||||
m->end = m->start + i;
|
||||
} else {
|
||||
if(done)
|
||||
len = n;
|
||||
else
|
||||
len = 2*n;
|
||||
m->start = emalloc(len + 1);
|
||||
m->end = m->start;
|
||||
}
|
||||
m->lim = m->start + len;
|
||||
}
|
||||
|
||||
memmove(m->end, p, n);
|
||||
m->end += n;
|
||||
}
|
||||
|
||||
//
|
||||
// read in a single message
|
||||
//
|
||||
static int
|
||||
readmessage(Message *m, Inbuf *inb)
|
||||
{
|
||||
int i, n, done;
|
||||
uchar *p, *np;
|
||||
char sdigest[SHA1dlen*2+1];
|
||||
char tmp[64];
|
||||
|
||||
for(done = 0; !done;){
|
||||
n = inb->wptr - inb->rptr;
|
||||
if(n < 6){
|
||||
if(n)
|
||||
memmove(inb->data, inb->rptr, n);
|
||||
inb->rptr = inb->data;
|
||||
inb->wptr = inb->rptr + n;
|
||||
i = read(inb->fd, inb->wptr, Buffersize);
|
||||
if(i < 0){
|
||||
/* if(fd2path(inb->fd, tmp, sizeof tmp) < 0)
|
||||
strcpy(tmp, "unknown mailbox"); jpc */
|
||||
fprint(2, "error reading '%s': %r\n", tmp);
|
||||
return -1;
|
||||
}
|
||||
if(i == 0){
|
||||
if(n != 0)
|
||||
addtomessage(m, inb->rptr, n, 1);
|
||||
if(m->end == m->start)
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
inb->wptr += i;
|
||||
}
|
||||
|
||||
// look for end of message
|
||||
for(p = inb->rptr; p < inb->wptr; p = np+1){
|
||||
// first part of search for '\nFrom '
|
||||
np = memchr(p, '\n', inb->wptr - p);
|
||||
if(np == nil){
|
||||
p = inb->wptr;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* if we've found a \n but there's
|
||||
* not enough room for '\nFrom ', don't do
|
||||
* the comparison till we've read in more.
|
||||
*/
|
||||
if(inb->wptr - np < 6){
|
||||
p = np;
|
||||
break;
|
||||
}
|
||||
|
||||
if(strncmp((char*)np, "\nFrom ", 6) == 0){
|
||||
done = 1;
|
||||
p = np+1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// add to message (+ 1 in malloc is for a trailing null)
|
||||
n = p - inb->rptr;
|
||||
addtomessage(m, inb->rptr, n, done);
|
||||
inb->rptr += n;
|
||||
}
|
||||
|
||||
// if it doesn't start with a 'From ', this ain't a mailbox
|
||||
if(strncmp(m->start, "From ", 5) != 0)
|
||||
return -1;
|
||||
|
||||
// dump trailing newline, make sure there's a trailing null
|
||||
// (helps in body searches)
|
||||
if(*(m->end-1) == '\n')
|
||||
m->end--;
|
||||
*m->end = 0;
|
||||
m->bend = m->rbend = m->end;
|
||||
|
||||
// digest message
|
||||
sha1((uchar*)m->start, m->end - m->start, m->digest, nil);
|
||||
for(i = 0; i < SHA1dlen; i++)
|
||||
sprint(sdigest+2*i, "%2.2ux", m->digest[i]);
|
||||
m->sdigest = s_copy(sdigest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// throw out deleted messages. return number of freshly deleted messages
|
||||
int
|
||||
purgedeleted(Mailbox *mb)
|
||||
{
|
||||
Message *m, *next;
|
||||
int newdels;
|
||||
|
||||
// forget about what's no longer in the mailbox
|
||||
newdels = 0;
|
||||
for(m = mb->root->part; m != nil; m = next){
|
||||
next = m->next;
|
||||
if(m->deleted && m->refs == 0){
|
||||
if(m->inmbox)
|
||||
newdels++;
|
||||
delmessage(mb, m);
|
||||
}
|
||||
}
|
||||
return newdels;
|
||||
}
|
||||
|
||||
//
|
||||
// read in the mailbox and parse into messages.
|
||||
//
|
||||
static char*
|
||||
_readmbox(Mailbox *mb, int doplumb, Mlock *lk)
|
||||
{
|
||||
int fd;
|
||||
String *tmp;
|
||||
Dir *d;
|
||||
static char err[128];
|
||||
Message *m, **l;
|
||||
Inbuf *inb;
|
||||
char *x;
|
||||
|
||||
l = &mb->root->part;
|
||||
|
||||
/*
|
||||
* open the mailbox. If it doesn't exist, try the temporary one.
|
||||
*/
|
||||
retry:
|
||||
fd = open(mb->path, OREAD);
|
||||
if(fd < 0){
|
||||
errstr(err, sizeof(err));
|
||||
if(strstr(err, "exist") != 0){
|
||||
tmp = s_copy(mb->path);
|
||||
s_append(tmp, ".tmp");
|
||||
if(sysrename(s_to_c(tmp), mb->path) == 0){
|
||||
s_free(tmp);
|
||||
goto retry;
|
||||
}
|
||||
s_free(tmp);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* a new qid.path means reread the mailbox, while
|
||||
* a new qid.vers means read any new messages
|
||||
*/
|
||||
d = dirfstat(fd);
|
||||
if(d == nil){
|
||||
close(fd);
|
||||
errstr(err, sizeof(err));
|
||||
return err;
|
||||
}
|
||||
if(mb->d != nil){
|
||||
if(d->qid.path == mb->d->qid.path && d->qid.vers == mb->d->qid.vers){
|
||||
close(fd);
|
||||
free(d);
|
||||
return nil;
|
||||
}
|
||||
if(d->qid.path == mb->d->qid.path){
|
||||
while(*l != nil)
|
||||
l = &(*l)->next;
|
||||
seek(fd, mb->d->length, 0);
|
||||
}
|
||||
free(mb->d);
|
||||
}
|
||||
mb->d = d;
|
||||
mb->vers++;
|
||||
henter(PATH(0, Qtop), mb->name,
|
||||
(Qid){PATH(mb->id, Qmbox), mb->vers, QTDIR}, nil, mb);
|
||||
|
||||
inb = emalloc(sizeof(Inbuf));
|
||||
inb->rptr = inb->wptr = inb->data;
|
||||
inb->fd = fd;
|
||||
|
||||
// read new messages
|
||||
snprint(err, sizeof err, "reading '%s'", mb->path);
|
||||
logmsg(err, nil);
|
||||
for(;;){
|
||||
if(lk != nil)
|
||||
syslockrefresh(lk);
|
||||
m = newmessage(mb->root);
|
||||
m->mallocd = 1;
|
||||
m->inmbox = 1;
|
||||
if(readmessage(m, inb) < 0){
|
||||
delmessage(mb, m);
|
||||
mb->root->subname--;
|
||||
break;
|
||||
}
|
||||
|
||||
// merge mailbox versions
|
||||
while(*l != nil){
|
||||
if(memcmp((*l)->digest, m->digest, SHA1dlen) == 0){
|
||||
// matches mail we already read, discard
|
||||
logmsg("duplicate", *l);
|
||||
delmessage(mb, m);
|
||||
mb->root->subname--;
|
||||
m = nil;
|
||||
l = &(*l)->next;
|
||||
break;
|
||||
} else {
|
||||
// old mail no longer in box, mark deleted
|
||||
logmsg("disappeared", *l);
|
||||
if(doplumb)
|
||||
mailplumb(mb, *l, 1);
|
||||
(*l)->inmbox = 0;
|
||||
(*l)->deleted = 1;
|
||||
l = &(*l)->next;
|
||||
}
|
||||
}
|
||||
if(m == nil)
|
||||
continue;
|
||||
|
||||
x = strchr(m->start, '\n');
|
||||
if(x == nil)
|
||||
m->header = m->end;
|
||||
else
|
||||
m->header = x + 1;
|
||||
m->mheader = m->mhend = m->header;
|
||||
parseunix(m);
|
||||
parse(m, 0, mb, 0);
|
||||
logmsg("new", m);
|
||||
|
||||
/* chain in */
|
||||
*l = m;
|
||||
l = &m->next;
|
||||
if(doplumb)
|
||||
mailplumb(mb, m, 0);
|
||||
|
||||
}
|
||||
logmsg("mbox read", nil);
|
||||
|
||||
// whatever is left has been removed from the mbox, mark deleted
|
||||
while(*l != nil){
|
||||
if(doplumb)
|
||||
mailplumb(mb, *l, 1);
|
||||
(*l)->inmbox = 0;
|
||||
(*l)->deleted = 1;
|
||||
l = &(*l)->next;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
free(inb);
|
||||
return nil;
|
||||
}
|
||||
|
||||
static void
|
||||
_writembox(Mailbox *mb, Mlock *lk)
|
||||
{
|
||||
Dir *d;
|
||||
Message *m;
|
||||
String *tmp;
|
||||
int mode, errs;
|
||||
Biobuf *b;
|
||||
|
||||
tmp = s_copy(mb->path);
|
||||
s_append(tmp, ".tmp");
|
||||
|
||||
/*
|
||||
* preserve old files permissions, if possible
|
||||
*/
|
||||
d = dirstat(mb->path);
|
||||
if(d != nil){
|
||||
mode = d->mode&0777;
|
||||
free(d);
|
||||
} else
|
||||
mode = MBOXMODE;
|
||||
|
||||
sysremove(s_to_c(tmp));
|
||||
b = sysopen(s_to_c(tmp), "alc", mode);
|
||||
if(b == 0){
|
||||
fprint(2, "can't write temporary mailbox %s: %r\n", s_to_c(tmp));
|
||||
return;
|
||||
}
|
||||
|
||||
logmsg("writing new mbox", nil);
|
||||
errs = 0;
|
||||
for(m = mb->root->part; m != nil; m = m->next){
|
||||
if(lk != nil)
|
||||
syslockrefresh(lk);
|
||||
if(m->deleted)
|
||||
continue;
|
||||
logmsg("writing", m);
|
||||
if(Bwrite(b, m->start, m->end - m->start) < 0)
|
||||
errs = 1;
|
||||
if(Bwrite(b, "\n", 1) < 0)
|
||||
errs = 1;
|
||||
}
|
||||
logmsg("wrote new mbox", nil);
|
||||
|
||||
if(sysclose(b) < 0)
|
||||
errs = 1;
|
||||
|
||||
if(errs){
|
||||
fprint(2, "error writing temporary mail file\n");
|
||||
s_free(tmp);
|
||||
return;
|
||||
}
|
||||
|
||||
sysremove(mb->path);
|
||||
if(sysrename(s_to_c(tmp), mb->path) < 0)
|
||||
fprint(2, "%s: can't rename %s to %s: %r\n", argv0,
|
||||
s_to_c(tmp), mb->path);
|
||||
s_free(tmp);
|
||||
if(mb->d != nil)
|
||||
free(mb->d);
|
||||
mb->d = dirstat(mb->path);
|
||||
}
|
||||
|
||||
char*
|
||||
plan9syncmbox(Mailbox *mb, int doplumb)
|
||||
{
|
||||
Mlock *lk;
|
||||
char *rv;
|
||||
|
||||
lk = nil;
|
||||
if(mb->dolock){
|
||||
lk = syslock(mb->path);
|
||||
if(lk == nil)
|
||||
return "can't lock mailbox";
|
||||
}
|
||||
|
||||
rv = _readmbox(mb, doplumb, lk); /* interpolate */
|
||||
if(purgedeleted(mb) > 0)
|
||||
_writembox(mb, lk);
|
||||
|
||||
if(lk != nil)
|
||||
sysunlock(lk);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
//
|
||||
// look to see if we can open this mail box
|
||||
//
|
||||
char*
|
||||
plan9mbox(Mailbox *mb, char *path)
|
||||
{
|
||||
static char err[64];
|
||||
String *tmp;
|
||||
|
||||
if(access(path, AEXIST) < 0){
|
||||
errstr(err, sizeof(err));
|
||||
tmp = s_copy(path);
|
||||
s_append(tmp, ".tmp");
|
||||
if(access(s_to_c(tmp), AEXIST) < 0){
|
||||
s_free(tmp);
|
||||
return err;
|
||||
}
|
||||
s_free(tmp);
|
||||
}
|
||||
|
||||
mb->sync = plan9syncmbox;
|
||||
return nil;
|
||||
}
|
||||
700
src/cmd/upas/fs/pop3.c
Normal file
700
src/cmd/upas/fs/pop3.c
Normal file
@@ -0,0 +1,700 @@
|
||||
#include "common.h"
|
||||
#include <ctype.h>
|
||||
#include <plumb.h>
|
||||
#include <libsec.h>
|
||||
#include <auth.h>
|
||||
#include <thread.h>
|
||||
#include "dat.h"
|
||||
|
||||
#pragma varargck type "M" uchar*
|
||||
#pragma varargck argpos pop3cmd 2
|
||||
|
||||
typedef struct Pop Pop;
|
||||
struct Pop {
|
||||
char *freep; // free this to free the strings below
|
||||
|
||||
char *host;
|
||||
char *user;
|
||||
char *port;
|
||||
|
||||
int ppop;
|
||||
int refreshtime;
|
||||
int debug;
|
||||
int pipeline;
|
||||
int encrypted;
|
||||
int needtls;
|
||||
int notls;
|
||||
int needssl;
|
||||
|
||||
// open network connection
|
||||
Biobuf bin;
|
||||
Biobuf bout;
|
||||
int fd;
|
||||
char *lastline; // from Brdstr
|
||||
|
||||
Thumbprint *thumb;
|
||||
};
|
||||
|
||||
char*
|
||||
geterrstr(void)
|
||||
{
|
||||
static char err[64];
|
||||
|
||||
err[0] = '\0';
|
||||
errstr(err, sizeof(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
//
|
||||
// get pop3 response line , without worrying
|
||||
// about multiline responses; the clients
|
||||
// will deal with that.
|
||||
//
|
||||
static int
|
||||
isokay(char *s)
|
||||
{
|
||||
return s!=nil && strncmp(s, "+OK", 3)==0;
|
||||
}
|
||||
|
||||
static void
|
||||
pop3cmd(Pop *pop, char *fmt, ...)
|
||||
{
|
||||
char buf[128], *p;
|
||||
va_list va;
|
||||
|
||||
va_start(va, fmt);
|
||||
vseprint(buf, buf+sizeof(buf), fmt, va);
|
||||
va_end(va);
|
||||
|
||||
p = buf+strlen(buf);
|
||||
if(p > (buf+sizeof(buf)-3))
|
||||
sysfatal("pop3 command too long");
|
||||
|
||||
if(pop->debug)
|
||||
fprint(2, "<- %s\n", buf);
|
||||
strcpy(p, "\r\n");
|
||||
Bwrite(&pop->bout, buf, strlen(buf));
|
||||
Bflush(&pop->bout);
|
||||
}
|
||||
|
||||
static char*
|
||||
pop3resp(Pop *pop)
|
||||
{
|
||||
char *s;
|
||||
char *p;
|
||||
|
||||
alarm(60*1000);
|
||||
if((s = Brdstr(&pop->bin, '\n', 0)) == nil){
|
||||
close(pop->fd);
|
||||
pop->fd = -1;
|
||||
alarm(0);
|
||||
return "unexpected eof";
|
||||
}
|
||||
alarm(0);
|
||||
|
||||
p = s+strlen(s)-1;
|
||||
while(p >= s && (*p == '\r' || *p == '\n'))
|
||||
*p-- = '\0';
|
||||
|
||||
if(pop->debug)
|
||||
fprint(2, "-> %s\n", s);
|
||||
free(pop->lastline);
|
||||
pop->lastline = s;
|
||||
return s;
|
||||
}
|
||||
|
||||
#if 0 /* jpc */
|
||||
static int
|
||||
pop3log(char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap,fmt);
|
||||
syslog(0, "/sys/log/pop3", fmt, ap);
|
||||
va_end(ap);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static char*
|
||||
pop3pushtls(Pop *pop)
|
||||
{
|
||||
int fd;
|
||||
uchar digest[SHA1dlen];
|
||||
TLSconn conn;
|
||||
|
||||
memset(&conn, 0, sizeof conn);
|
||||
// conn.trace = pop3log;
|
||||
fd = tlsClient(pop->fd, &conn);
|
||||
if(fd < 0)
|
||||
return "tls error";
|
||||
if(conn.cert==nil || conn.certlen <= 0){
|
||||
close(fd);
|
||||
return "server did not provide TLS certificate";
|
||||
}
|
||||
sha1(conn.cert, conn.certlen, digest, nil);
|
||||
if(!pop->thumb || !okThumbprint(digest, pop->thumb)){
|
||||
fmtinstall('H', encodefmt);
|
||||
close(fd);
|
||||
free(conn.cert);
|
||||
fprint(2, "upas/fs pop3: server certificate %.*H not recognized\n", SHA1dlen, digest);
|
||||
return "bad server certificate";
|
||||
}
|
||||
free(conn.cert);
|
||||
close(pop->fd);
|
||||
pop->fd = fd;
|
||||
pop->encrypted = 1;
|
||||
Binit(&pop->bin, pop->fd, OREAD);
|
||||
Binit(&pop->bout, pop->fd, OWRITE);
|
||||
return nil;
|
||||
}
|
||||
|
||||
//
|
||||
// get capability list, possibly start tls
|
||||
//
|
||||
static char*
|
||||
pop3capa(Pop *pop)
|
||||
{
|
||||
char *s;
|
||||
int hastls;
|
||||
|
||||
pop3cmd(pop, "CAPA");
|
||||
if(!isokay(pop3resp(pop)))
|
||||
return nil;
|
||||
|
||||
hastls = 0;
|
||||
for(;;){
|
||||
s = pop3resp(pop);
|
||||
if(strcmp(s, ".") == 0 || strcmp(s, "unexpected eof") == 0)
|
||||
break;
|
||||
if(strcmp(s, "STLS") == 0)
|
||||
hastls = 1;
|
||||
if(strcmp(s, "PIPELINING") == 0)
|
||||
pop->pipeline = 1;
|
||||
}
|
||||
|
||||
if(hastls && !pop->notls){
|
||||
pop3cmd(pop, "STLS");
|
||||
if(!isokay(s = pop3resp(pop)))
|
||||
return s;
|
||||
if((s = pop3pushtls(pop)) != nil)
|
||||
return s;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
//
|
||||
// log in using APOP if possible, password if allowed by user
|
||||
//
|
||||
static char*
|
||||
pop3login(Pop *pop)
|
||||
{
|
||||
int n;
|
||||
char *s, *p, *q;
|
||||
char ubuf[128], user[128];
|
||||
char buf[500];
|
||||
UserPasswd *up;
|
||||
|
||||
s = pop3resp(pop);
|
||||
if(!isokay(s))
|
||||
return "error in initial handshake";
|
||||
|
||||
if(pop->user)
|
||||
snprint(ubuf, sizeof ubuf, " user=%q", pop->user);
|
||||
else
|
||||
ubuf[0] = '\0';
|
||||
|
||||
// look for apop banner
|
||||
if(pop->ppop==0 && (p = strchr(s, '<')) && (q = strchr(p+1, '>'))) {
|
||||
*++q = '\0';
|
||||
if((n=auth_respond(p, q-p, user, sizeof user, buf, sizeof buf, auth_getkey, "proto=apop role=client server=%q%s",
|
||||
pop->host, ubuf)) < 0)
|
||||
return "factotum failed";
|
||||
if(user[0]=='\0')
|
||||
return "factotum did not return a user name";
|
||||
|
||||
if(s = pop3capa(pop))
|
||||
return s;
|
||||
|
||||
pop3cmd(pop, "APOP %s %.*s", user, n, buf);
|
||||
if(!isokay(s = pop3resp(pop)))
|
||||
return s;
|
||||
|
||||
return nil;
|
||||
} else {
|
||||
if(pop->ppop == 0)
|
||||
return "no APOP hdr from server";
|
||||
|
||||
if(s = pop3capa(pop))
|
||||
return s;
|
||||
|
||||
if(pop->needtls && !pop->encrypted)
|
||||
return "could not negotiate TLS";
|
||||
|
||||
up = auth_getuserpasswd(auth_getkey, "role=client proto=pass service=pop dom=%q%s",
|
||||
pop->host, ubuf);
|
||||
/* up = auth_getuserpasswd(auth_getkey, "proto=pass service=pop dom=%q%s",
|
||||
pop->host, ubuf); jpc */
|
||||
if(up == nil)
|
||||
return "no usable keys found";
|
||||
|
||||
pop3cmd(pop, "USER %s", up->user);
|
||||
if(!isokay(s = pop3resp(pop))){
|
||||
free(up);
|
||||
return s;
|
||||
}
|
||||
pop3cmd(pop, "PASS %s", up->passwd);
|
||||
free(up);
|
||||
if(!isokay(s = pop3resp(pop)))
|
||||
return s;
|
||||
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// dial and handshake with pop server
|
||||
//
|
||||
static char*
|
||||
pop3dial(Pop *pop)
|
||||
{
|
||||
char *err;
|
||||
|
||||
if((pop->fd = dial(netmkaddr(pop->host, "net", pop->needssl ? "pop3s" : "pop3"), 0, 0, 0)) < 0)
|
||||
return geterrstr();
|
||||
|
||||
if(pop->needssl){
|
||||
if((err = pop3pushtls(pop)) != nil)
|
||||
return err;
|
||||
}else{
|
||||
Binit(&pop->bin, pop->fd, OREAD);
|
||||
Binit(&pop->bout, pop->fd, OWRITE);
|
||||
}
|
||||
|
||||
if(err = pop3login(pop)) {
|
||||
close(pop->fd);
|
||||
return err;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
//
|
||||
// close connection
|
||||
//
|
||||
static void
|
||||
pop3hangup(Pop *pop)
|
||||
{
|
||||
pop3cmd(pop, "QUIT");
|
||||
pop3resp(pop);
|
||||
close(pop->fd);
|
||||
}
|
||||
|
||||
//
|
||||
// download a single message
|
||||
//
|
||||
static char*
|
||||
pop3download(Pop *pop, Message *m)
|
||||
{
|
||||
char *s, *f[3], *wp, *ep;
|
||||
char sdigest[SHA1dlen*2+1];
|
||||
int i, l, sz;
|
||||
|
||||
if(!pop->pipeline)
|
||||
pop3cmd(pop, "LIST %d", m->mesgno);
|
||||
if(!isokay(s = pop3resp(pop)))
|
||||
return s;
|
||||
|
||||
if(tokenize(s, f, 3) != 3)
|
||||
return "syntax error in LIST response";
|
||||
|
||||
if(atoi(f[1]) != m->mesgno)
|
||||
return "out of sync with pop3 server";
|
||||
|
||||
sz = atoi(f[2])+200; /* 200 because the plan9 pop3 server lies */
|
||||
if(sz == 0)
|
||||
return "invalid size in LIST response";
|
||||
|
||||
m->start = wp = emalloc(sz+1);
|
||||
ep = wp+sz;
|
||||
|
||||
if(!pop->pipeline)
|
||||
pop3cmd(pop, "RETR %d", m->mesgno);
|
||||
if(!isokay(s = pop3resp(pop))) {
|
||||
m->start = nil;
|
||||
free(wp);
|
||||
return s;
|
||||
}
|
||||
|
||||
s = nil;
|
||||
while(wp <= ep) {
|
||||
s = pop3resp(pop);
|
||||
if(strcmp(s, "unexpected eof") == 0) {
|
||||
free(m->start);
|
||||
m->start = nil;
|
||||
return "unexpected end of conversation";
|
||||
}
|
||||
if(strcmp(s, ".") == 0)
|
||||
break;
|
||||
|
||||
l = strlen(s)+1;
|
||||
if(s[0] == '.') {
|
||||
s++;
|
||||
l--;
|
||||
}
|
||||
/*
|
||||
* grow by 10%/200bytes - some servers
|
||||
* lie about message sizes
|
||||
*/
|
||||
if(wp+l > ep) {
|
||||
int pos = wp - m->start;
|
||||
sz += ((sz / 10) < 200)? 200: sz/10;
|
||||
m->start = erealloc(m->start, sz+1);
|
||||
wp = m->start+pos;
|
||||
ep = m->start+sz;
|
||||
}
|
||||
memmove(wp, s, l-1);
|
||||
wp[l-1] = '\n';
|
||||
wp += l;
|
||||
}
|
||||
|
||||
if(s == nil || strcmp(s, ".") != 0)
|
||||
return "out of sync with pop3 server";
|
||||
|
||||
m->end = wp;
|
||||
|
||||
// make sure there's a trailing null
|
||||
// (helps in body searches)
|
||||
*m->end = 0;
|
||||
m->bend = m->rbend = m->end;
|
||||
m->header = m->start;
|
||||
|
||||
// digest message
|
||||
sha1((uchar*)m->start, m->end - m->start, m->digest, nil);
|
||||
for(i = 0; i < SHA1dlen; i++)
|
||||
sprint(sdigest+2*i, "%2.2ux", m->digest[i]);
|
||||
m->sdigest = s_copy(sdigest);
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
//
|
||||
// check for new messages on pop server
|
||||
// UIDL is not required by RFC 1939, but
|
||||
// netscape requires it, so almost every server supports it.
|
||||
// we'll use it to make our lives easier.
|
||||
//
|
||||
static char*
|
||||
pop3read(Pop *pop, Mailbox *mb, int doplumb)
|
||||
{
|
||||
char *s, *p, *uidl, *f[2];
|
||||
int mesgno, ignore, nnew;
|
||||
Message *m, *next, **l;
|
||||
|
||||
// Some POP servers disallow UIDL if the maildrop is empty.
|
||||
pop3cmd(pop, "STAT");
|
||||
if(!isokay(s = pop3resp(pop)))
|
||||
return s;
|
||||
|
||||
// fetch message listing; note messages to grab
|
||||
l = &mb->root->part;
|
||||
if(strncmp(s, "+OK 0 ", 6) != 0) {
|
||||
pop3cmd(pop, "UIDL");
|
||||
if(!isokay(s = pop3resp(pop)))
|
||||
return s;
|
||||
|
||||
for(;;){
|
||||
p = pop3resp(pop);
|
||||
if(strcmp(p, ".") == 0 || strcmp(p, "unexpected eof") == 0)
|
||||
break;
|
||||
|
||||
if(tokenize(p, f, 2) != 2)
|
||||
continue;
|
||||
|
||||
mesgno = atoi(f[0]);
|
||||
uidl = f[1];
|
||||
if(strlen(uidl) > 75) // RFC 1939 says 70 characters max
|
||||
continue;
|
||||
|
||||
ignore = 0;
|
||||
while(*l != nil) {
|
||||
if(strcmp((*l)->uidl, uidl) == 0) {
|
||||
// matches mail we already have, note mesgno for deletion
|
||||
(*l)->mesgno = mesgno;
|
||||
ignore = 1;
|
||||
l = &(*l)->next;
|
||||
break;
|
||||
} else {
|
||||
// old mail no longer in box mark deleted
|
||||
if(doplumb)
|
||||
mailplumb(mb, *l, 1);
|
||||
(*l)->inmbox = 0;
|
||||
(*l)->deleted = 1;
|
||||
l = &(*l)->next;
|
||||
}
|
||||
}
|
||||
if(ignore)
|
||||
continue;
|
||||
|
||||
m = newmessage(mb->root);
|
||||
m->mallocd = 1;
|
||||
m->inmbox = 1;
|
||||
m->mesgno = mesgno;
|
||||
strcpy(m->uidl, uidl);
|
||||
|
||||
// chain in; will fill in message later
|
||||
*l = m;
|
||||
l = &m->next;
|
||||
}
|
||||
}
|
||||
|
||||
// whatever is left has been removed from the mbox, mark as deleted
|
||||
while(*l != nil) {
|
||||
if(doplumb)
|
||||
mailplumb(mb, *l, 1);
|
||||
(*l)->inmbox = 0;
|
||||
(*l)->deleted = 1;
|
||||
l = &(*l)->next;
|
||||
}
|
||||
|
||||
// download new messages
|
||||
nnew = 0;
|
||||
if(pop->pipeline){
|
||||
switch(rfork(RFPROC|RFMEM)){
|
||||
case -1:
|
||||
fprint(2, "rfork: %r\n");
|
||||
pop->pipeline = 0;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
case 0:
|
||||
for(m = mb->root->part; m != nil; m = m->next){
|
||||
if(m->start != nil)
|
||||
continue;
|
||||
Bprint(&pop->bout, "LIST %d\r\nRETR %d\r\n", m->mesgno, m->mesgno);
|
||||
}
|
||||
Bflush(&pop->bout);
|
||||
threadexits(nil);
|
||||
/* _exits(nil); jpc */
|
||||
}
|
||||
}
|
||||
|
||||
for(m = mb->root->part; m != nil; m = next) {
|
||||
next = m->next;
|
||||
|
||||
if(m->start != nil)
|
||||
continue;
|
||||
|
||||
if(s = pop3download(pop, m)) {
|
||||
// message disappeared? unchain
|
||||
fprint(2, "download %d: %s\n", m->mesgno, s);
|
||||
delmessage(mb, m);
|
||||
mb->root->subname--;
|
||||
continue;
|
||||
}
|
||||
nnew++;
|
||||
parse(m, 0, mb, 1);
|
||||
|
||||
if(doplumb)
|
||||
mailplumb(mb, m, 0);
|
||||
}
|
||||
if(pop->pipeline)
|
||||
waitpid();
|
||||
|
||||
if(nnew || mb->vers == 0) {
|
||||
mb->vers++;
|
||||
henter(PATH(0, Qtop), mb->name,
|
||||
(Qid){PATH(mb->id, Qmbox), mb->vers, QTDIR}, nil, mb);
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
//
|
||||
// delete marked messages
|
||||
//
|
||||
static void
|
||||
pop3purge(Pop *pop, Mailbox *mb)
|
||||
{
|
||||
Message *m, *next;
|
||||
|
||||
if(pop->pipeline){
|
||||
switch(rfork(RFPROC|RFMEM)){
|
||||
case -1:
|
||||
fprint(2, "rfork: %r\n");
|
||||
pop->pipeline = 0;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
case 0:
|
||||
for(m = mb->root->part; m != nil; m = next){
|
||||
next = m->next;
|
||||
if(m->deleted && m->refs == 0){
|
||||
if(m->inmbox)
|
||||
Bprint(&pop->bout, "DELE %d\r\n", m->mesgno);
|
||||
}
|
||||
}
|
||||
Bflush(&pop->bout);
|
||||
/* _exits(nil); jpc */
|
||||
threadexits(nil);
|
||||
}
|
||||
}
|
||||
for(m = mb->root->part; m != nil; m = next) {
|
||||
next = m->next;
|
||||
if(m->deleted && m->refs == 0) {
|
||||
if(m->inmbox) {
|
||||
if(!pop->pipeline)
|
||||
pop3cmd(pop, "DELE %d", m->mesgno);
|
||||
if(isokay(pop3resp(pop)))
|
||||
delmessage(mb, m);
|
||||
} else
|
||||
delmessage(mb, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// connect to pop3 server, sync mailbox
|
||||
static char*
|
||||
pop3sync(Mailbox *mb, int doplumb)
|
||||
{
|
||||
char *err;
|
||||
Pop *pop;
|
||||
|
||||
pop = mb->aux;
|
||||
|
||||
if(err = pop3dial(pop)) {
|
||||
mb->waketime = time(0) + pop->refreshtime;
|
||||
return err;
|
||||
}
|
||||
|
||||
if((err = pop3read(pop, mb, doplumb)) == nil){
|
||||
pop3purge(pop, mb);
|
||||
mb->d->atime = mb->d->mtime = time(0);
|
||||
}
|
||||
pop3hangup(pop);
|
||||
mb->waketime = time(0) + pop->refreshtime;
|
||||
return err;
|
||||
}
|
||||
|
||||
static char Epop3ctl[] = "bad pop3 control message";
|
||||
|
||||
static char*
|
||||
pop3ctl(Mailbox *mb, int argc, char **argv)
|
||||
{
|
||||
int n;
|
||||
Pop *pop;
|
||||
char *m, *me;
|
||||
|
||||
pop = mb->aux;
|
||||
if(argc < 1)
|
||||
return Epop3ctl;
|
||||
|
||||
if(argc==1 && strcmp(argv[0], "debug")==0){
|
||||
pop->debug = 1;
|
||||
return nil;
|
||||
}
|
||||
|
||||
if(argc==1 && strcmp(argv[0], "nodebug")==0){
|
||||
pop->debug = 0;
|
||||
return nil;
|
||||
}
|
||||
|
||||
if(argc==1 && strcmp(argv[0], "thumbprint")==0){
|
||||
if(pop->thumb)
|
||||
freeThumbprints(pop->thumb);
|
||||
/* pop->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude"); jpc */
|
||||
m = unsharp("#9/sys/lib/tls/mail");
|
||||
me = unsharp("#9/sys/lib/tls/mail.exclude");
|
||||
pop->thumb = initThumbprints(m, me);
|
||||
}
|
||||
if(strcmp(argv[0], "refresh")==0){
|
||||
if(argc==1){
|
||||
pop->refreshtime = 60;
|
||||
return nil;
|
||||
}
|
||||
if(argc==2){
|
||||
n = atoi(argv[1]);
|
||||
if(n < 15)
|
||||
return Epop3ctl;
|
||||
pop->refreshtime = n;
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
return Epop3ctl;
|
||||
}
|
||||
|
||||
// free extra memory associated with mb
|
||||
static void
|
||||
pop3close(Mailbox *mb)
|
||||
{
|
||||
Pop *pop;
|
||||
|
||||
pop = mb->aux;
|
||||
free(pop->freep);
|
||||
free(pop);
|
||||
}
|
||||
|
||||
//
|
||||
// open mailboxes of the form /pop/host/user or /apop/host/user
|
||||
//
|
||||
char*
|
||||
pop3mbox(Mailbox *mb, char *path)
|
||||
{
|
||||
char *f[10];
|
||||
int nf, apop, ppop, popssl, apopssl, apoptls, popnotls, apopnotls, poptls;
|
||||
Pop *pop;
|
||||
char *m, *me;
|
||||
|
||||
quotefmtinstall();
|
||||
popssl = strncmp(path, "/pops/", 6) == 0;
|
||||
apopssl = strncmp(path, "/apops/", 7) == 0;
|
||||
poptls = strncmp(path, "/poptls/", 8) == 0;
|
||||
popnotls = strncmp(path, "/popnotls/", 10) == 0;
|
||||
ppop = popssl || poptls || popnotls || strncmp(path, "/pop/", 5) == 0;
|
||||
apoptls = strncmp(path, "/apoptls/", 9) == 0;
|
||||
apopnotls = strncmp(path, "/apopnotls/", 11) == 0;
|
||||
apop = apopssl || apoptls || apopnotls || strncmp(path, "/apop/", 6) == 0;
|
||||
|
||||
if(!ppop && !apop)
|
||||
return Enotme;
|
||||
|
||||
path = strdup(path);
|
||||
if(path == nil)
|
||||
return "out of memory";
|
||||
|
||||
nf = getfields(path, f, nelem(f), 0, "/");
|
||||
if(nf != 3 && nf != 4) {
|
||||
free(path);
|
||||
return "bad pop3 path syntax /[a]pop[tls|ssl]/system[/user]";
|
||||
}
|
||||
|
||||
pop = emalloc(sizeof(*pop));
|
||||
pop->freep = path;
|
||||
pop->host = f[2];
|
||||
if(nf < 4)
|
||||
pop->user = nil;
|
||||
else
|
||||
pop->user = f[3];
|
||||
pop->ppop = ppop;
|
||||
pop->needssl = popssl || apopssl;
|
||||
pop->needtls = poptls || apoptls;
|
||||
pop->refreshtime = 60;
|
||||
pop->notls = popnotls || apopnotls;
|
||||
/* pop->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude"); jpc */
|
||||
m = unsharp("#9/sys/lib/tls/mail");
|
||||
me = unsharp("#9/sys/lib/tls/mail.exclude");
|
||||
pop->thumb = initThumbprints(m, me);
|
||||
|
||||
mb->aux = pop;
|
||||
mb->sync = pop3sync;
|
||||
mb->close = pop3close;
|
||||
mb->ctl = pop3ctl;
|
||||
mb->d = emalloc(sizeof(*mb->d));
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
15
src/cmd/upas/fs/readdir.c
Normal file
15
src/cmd/upas/fs/readdir.c
Normal file
@@ -0,0 +1,15 @@
|
||||
#include <u.h>
|
||||
#include <libc.h>
|
||||
|
||||
void
|
||||
main(void)
|
||||
{
|
||||
Dir d;
|
||||
int fd, n;
|
||||
|
||||
fd = open("/mail/fs", OREAD);
|
||||
while((n = dirread(fd, &d, sizeof(d))) > 0){
|
||||
print("%s\n", d.name);
|
||||
}
|
||||
print("n = %d\n", n);
|
||||
}
|
||||
113
src/cmd/upas/fs/strtotm.c
Normal file
113
src/cmd/upas/fs/strtotm.c
Normal file
@@ -0,0 +1,113 @@
|
||||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <ctype.h>
|
||||
|
||||
static char*
|
||||
skiptext(char *q)
|
||||
{
|
||||
while(*q!='\0' && *q!=' ' && *q!='\t' && *q!='\r' && *q!='\n')
|
||||
q++;
|
||||
return q;
|
||||
}
|
||||
|
||||
static char*
|
||||
skipwhite(char *q)
|
||||
{
|
||||
while(*q==' ' || *q=='\t' || *q=='\r' || *q=='\n')
|
||||
q++;
|
||||
return q;
|
||||
}
|
||||
|
||||
static char* months[] = {
|
||||
"jan", "feb", "mar", "apr",
|
||||
"may", "jun", "jul", "aug",
|
||||
"sep", "oct", "nov", "dec"
|
||||
};
|
||||
|
||||
static int
|
||||
strcmplwr(char *a, char *b, int n)
|
||||
{
|
||||
char *eb;
|
||||
|
||||
eb = b+n;
|
||||
while(*a && *b && b<eb){
|
||||
if(tolower(*a) != tolower(*b))
|
||||
return 1;
|
||||
a++;
|
||||
b++;
|
||||
}
|
||||
if(b==eb)
|
||||
return 0;
|
||||
return *a != *b;
|
||||
}
|
||||
|
||||
int
|
||||
strtotm(char *p, Tm *tmp)
|
||||
{
|
||||
char *q, *r;
|
||||
int j;
|
||||
Tm tm;
|
||||
int delta;
|
||||
|
||||
delta = 0;
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
tm.mon = -1;
|
||||
tm.hour = -1;
|
||||
tm.min = -1;
|
||||
tm.year = -1;
|
||||
tm.mday = -1;
|
||||
for(p=skipwhite(p); *p; p=skipwhite(q)){
|
||||
q = skiptext(p);
|
||||
|
||||
/* look for time in hh:mm[:ss] */
|
||||
if(r = memchr(p, ':', q-p)){
|
||||
tm.hour = strtol(p, 0, 10);
|
||||
tm.min = strtol(r+1, 0, 10);
|
||||
if(r = memchr(r+1, ':', q-(r+1)))
|
||||
tm.sec = strtol(r+1, 0, 10);
|
||||
else
|
||||
tm.sec = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* look for month */
|
||||
for(j=0; j<12; j++)
|
||||
if(strcmplwr(p, months[j], 3)==0){
|
||||
tm.mon = j;
|
||||
break;
|
||||
}
|
||||
|
||||
if(j!=12)
|
||||
continue;
|
||||
|
||||
/* look for time zone [A-Z][A-Z]T */
|
||||
if(q-p==3 && 'A' <= p[0] && p[0] <= 'Z'
|
||||
&& 'A' <= p[1] && p[1] <= 'Z' && p[2] == 'T'){
|
||||
strecpy(tm.zone, tm.zone+4, p);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(p[0]=='+'||p[0]=='-')
|
||||
if(q-p==5 && strspn(p+1, "0123456789") == 4){
|
||||
delta = (((p[1]-'0')*10+p[2]-'0')*60+(p[3]-'0')*10+p[4]-'0')*60;
|
||||
if(p[0] == '-')
|
||||
delta = -delta;
|
||||
continue;
|
||||
}
|
||||
if(strspn(p, "0123456789") == q-p){
|
||||
j = strtol(p, nil, 10);
|
||||
if(1 <= j && j <= 31)
|
||||
tm.mday = j;
|
||||
if(j >= 1900)
|
||||
tm.year = j-1900;
|
||||
}
|
||||
}
|
||||
|
||||
if(tm.mon<0 || tm.year<0
|
||||
|| tm.hour<0 || tm.min<0
|
||||
|| tm.mday<0)
|
||||
return -1;
|
||||
|
||||
*tmp = *localtime(tm2sec(&tm)-delta);
|
||||
return 0;
|
||||
}
|
||||
81
src/cmd/upas/fs/tester.c
Normal file
81
src/cmd/upas/fs/tester.c
Normal file
@@ -0,0 +1,81 @@
|
||||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <ctype.h>
|
||||
#include <String.h>
|
||||
#include "message.h"
|
||||
|
||||
Message *root;
|
||||
|
||||
void
|
||||
prindent(int i)
|
||||
{
|
||||
for(; i > 0; i--)
|
||||
print(" ");
|
||||
}
|
||||
|
||||
void
|
||||
prstring(int indent, char *tag, String *s)
|
||||
{
|
||||
if(s == nil)
|
||||
return;
|
||||
prindent(indent+1);
|
||||
print("%s %s\n", tag, s_to_c(s));
|
||||
}
|
||||
|
||||
void
|
||||
info(int indent, int mno, Message *m)
|
||||
{
|
||||
int i;
|
||||
Message *nm;
|
||||
|
||||
prindent(indent);
|
||||
print("%d%c %d ", mno, m->allocated?'*':' ', m->end - m->start);
|
||||
if(m->unixfrom != nil)
|
||||
print("uf %s ", s_to_c(m->unixfrom));
|
||||
if(m->unixdate != nil)
|
||||
print("ud %s ", s_to_c(m->unixdate));
|
||||
print("\n");
|
||||
prstring(indent, "from:", m->from822);
|
||||
prstring(indent, "sender:", m->sender822);
|
||||
prstring(indent, "to:", m->to822);
|
||||
prstring(indent, "cc:", m->cc822);
|
||||
prstring(indent, "reply-to:", m->replyto822);
|
||||
prstring(indent, "subject:", m->subject822);
|
||||
prstring(indent, "date:", m->date822);
|
||||
prstring(indent, "filename:", m->filename);
|
||||
prstring(indent, "type:", m->type);
|
||||
prstring(indent, "charset:", m->charset);
|
||||
|
||||
i = 1;
|
||||
for(nm = m->part; nm != nil; nm = nm->next){
|
||||
info(indent+1, i++, nm);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
char *err;
|
||||
char *mboxfile;
|
||||
|
||||
ARGBEGIN{
|
||||
}ARGEND;
|
||||
|
||||
if(argc > 0)
|
||||
mboxfile = argv[0];
|
||||
else
|
||||
mboxfile = "./mbox";
|
||||
|
||||
root = newmessage(nil);
|
||||
|
||||
err = readmbox(mboxfile, &root->part);
|
||||
if(err != nil){
|
||||
fprint(2, "boom: %s\n", err);
|
||||
exits(0);
|
||||
}
|
||||
|
||||
info(0, 1, root);
|
||||
|
||||
exits(0);
|
||||
}
|
||||
1857
src/cmd/upas/marshal/marshal.c
Normal file
1857
src/cmd/upas/marshal/marshal.c
Normal file
File diff suppressed because it is too large
Load Diff
20
src/cmd/upas/marshal/mkfile
Normal file
20
src/cmd/upas/marshal/mkfile
Normal file
@@ -0,0 +1,20 @@
|
||||
<$PLAN9/src/mkhdr
|
||||
|
||||
TARG=marshal
|
||||
|
||||
LIB=../common/libcommon.a\
|
||||
|
||||
HFILES= ../common/common.h\
|
||||
|
||||
OFILES= marshal.$O
|
||||
|
||||
BIN=$PLAN9/bin/upas
|
||||
|
||||
UPDATE=\
|
||||
mkfile\
|
||||
$HFILES\
|
||||
${OFILES:%.$O=%.c}\
|
||||
|
||||
<$PLAN9/src/mkone
|
||||
CFLAGS=$CFLAGS -I../common
|
||||
|
||||
9
src/cmd/upas/misc/gone.fishing
Normal file
9
src/cmd/upas/misc/gone.fishing
Normal file
@@ -0,0 +1,9 @@
|
||||
#!/bin/sh
|
||||
PATH=/bin:/usr/bin
|
||||
message=${1-/usr/lib/upas/gone.msg}
|
||||
return=`sed '2,$s/^From[ ]/>&/'|tee -a $HOME/gone.mail|sed -n '1s/^From[ ]\([^ ]*\)[ ].*$/\1/p'`
|
||||
echo '' >>$HOME/gone.mail
|
||||
grep "^$return" $HOME/gone.addrs >/dev/null 2>/dev/null || {
|
||||
echo $return >>$HOME/gone.addrs
|
||||
mail $return < $message
|
||||
}
|
||||
4
src/cmd/upas/misc/gone.msg
Normal file
4
src/cmd/upas/misc/gone.msg
Normal file
@@ -0,0 +1,4 @@
|
||||
This is a recorded message. I am currently out of contact with my
|
||||
computer system. Your message to me has been saved and will be
|
||||
read upon my return. This is the last time you will receive this
|
||||
message during my absence. Thank you.
|
||||
51
src/cmd/upas/misc/mail.c
Normal file
51
src/cmd/upas/misc/mail.c
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* #!/bin/sh
|
||||
* case $1 in
|
||||
* -n)
|
||||
* exit 0 ;;
|
||||
* -m*|-f*|-r*|-p*|-e*|"")
|
||||
* exec /usr/lib/upas/edmail $*
|
||||
* exit $? ;;
|
||||
* *)
|
||||
* exec /usr/lib/upas/send $*
|
||||
* exit $? ;;
|
||||
* esac
|
||||
*/
|
||||
|
||||
|
||||
extern *UPASROOT;
|
||||
|
||||
#define EDMAIL "edmail"
|
||||
#define SEND "send"
|
||||
|
||||
main (argc, argv)
|
||||
int argc;
|
||||
char **argv;
|
||||
{
|
||||
char *progname = SEND;
|
||||
char realprog[500];
|
||||
|
||||
if (argc > 1) {
|
||||
if (argv[1][0] == '-') {
|
||||
switch (argv[1][1]) {
|
||||
case 'n':
|
||||
exit (0);
|
||||
|
||||
case 'm':
|
||||
case 'f':
|
||||
case 'r':
|
||||
case 'p':
|
||||
case 'e':
|
||||
case '\0':
|
||||
progname = EDMAIL;
|
||||
}
|
||||
}
|
||||
} else
|
||||
progname = EDMAIL;
|
||||
|
||||
sprint(realprog, "%s/%s", UPASROOT, progname);
|
||||
execv (realprog, argv);
|
||||
perror (realprog);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
12
src/cmd/upas/misc/mail.rc
Executable file
12
src/cmd/upas/misc/mail.rc
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/bin/rc
|
||||
switch($#*){
|
||||
case 0
|
||||
exec upas/nedmail
|
||||
}
|
||||
|
||||
switch($1){
|
||||
case -f* -r* -c* -m*
|
||||
exec upas/nedmail $*
|
||||
case *
|
||||
exec upas/marshal $*
|
||||
}
|
||||
12
src/cmd/upas/misc/mail.sh
Normal file
12
src/cmd/upas/misc/mail.sh
Normal file
@@ -0,0 +1,12 @@
|
||||
#!/bin/sh
|
||||
case $1 in
|
||||
-n)
|
||||
exec LIBDIR/notify
|
||||
exit $? ;;
|
||||
-m*|-f*|-r*|-p*|-e*|"")
|
||||
exec LIBDIR/edmail $*
|
||||
exit $? ;;
|
||||
*)
|
||||
exec LIBDIR/send $*
|
||||
exit $? ;;
|
||||
esac
|
||||
44
src/cmd/upas/misc/makefile
Normal file
44
src/cmd/upas/misc/makefile
Normal file
@@ -0,0 +1,44 @@
|
||||
LIB=/usr/lib/upas
|
||||
CFLAGS=${UNIX} -g -I. -I../libc -I../common -I/usr/include -I/usr/include/sys
|
||||
LFLAGS=-g
|
||||
HOSTNAME=cat /etc/whoami
|
||||
|
||||
.c.o: ; $(CC) -c $(CFLAGS) $*.c
|
||||
all: mail
|
||||
|
||||
sedfile:
|
||||
echo 's+LIBDIR+$(LIB)+g' >sed.file
|
||||
echo 's+HOSTNAME+$(HOSTNAME)+g' >>sed.file
|
||||
|
||||
install: sedfile install.fish install.mail.sh
|
||||
|
||||
install.fish:
|
||||
cp gone.msg $(LIB)
|
||||
sed -f sed.file gone.fishing >$(LIB)/gone.fishing
|
||||
-chmod 775 $(LIB)/gone.fishing
|
||||
-chown bin $(LIB)/gone.fishing $(LIB)/gone.msg
|
||||
|
||||
install.mail.sh:
|
||||
sed -f sed.file mail.sh >/bin/mail
|
||||
-chown bin /bin/mail
|
||||
-chmod 775 /bin/mail
|
||||
|
||||
install.notify: notify
|
||||
cp notify $(LIB)/notify
|
||||
-chmod 775 $(LIB)/notify
|
||||
-chown bin $(LIB)/notify
|
||||
|
||||
install.mail: mail
|
||||
cp mail /bin
|
||||
strip /bin/mail
|
||||
|
||||
notify: notify.o
|
||||
cc $(LFLAGS) notify.o -o notify
|
||||
|
||||
mail: mail.o ../config/config.o
|
||||
cc $(LFLAGS) mail.o ../config/config.o -o mail
|
||||
|
||||
clean:
|
||||
-rm -f *.[oOa] core a.out *.sL notify
|
||||
-rm -f sed.file mail
|
||||
|
||||
39
src/cmd/upas/misc/mkfile
Normal file
39
src/cmd/upas/misc/mkfile
Normal file
@@ -0,0 +1,39 @@
|
||||
|
||||
RCFILES=mail.rc\
|
||||
|
||||
all:Q:
|
||||
;
|
||||
|
||||
installall:Q: install
|
||||
;
|
||||
|
||||
install:V:
|
||||
cp mail.rc /rc/bin/mail
|
||||
|
||||
safeinstall:V:
|
||||
cp mail.rc /rc/bin/mail
|
||||
|
||||
safeinstallall:V:
|
||||
cp mail.rc /rc/bin/mail
|
||||
|
||||
clean:Q:
|
||||
;
|
||||
nuke:V:
|
||||
rm /rc/bin/mail
|
||||
|
||||
UPDATE=\
|
||||
gone.fishing\
|
||||
gone.msg\
|
||||
mail.c\
|
||||
mail.rc\
|
||||
mail.sh\
|
||||
makefile\
|
||||
mkfile\
|
||||
namefiles\
|
||||
omail.rc\
|
||||
qmail\
|
||||
remotemail\
|
||||
rewrite\
|
||||
|
||||
update:V:
|
||||
update $UPDATEFLAGS $UPDATE
|
||||
2
src/cmd/upas/misc/namefiles
Normal file
2
src/cmd/upas/misc/namefiles
Normal file
@@ -0,0 +1,2 @@
|
||||
names.local
|
||||
names.global
|
||||
14
src/cmd/upas/misc/omail.rc
Executable file
14
src/cmd/upas/misc/omail.rc
Executable file
@@ -0,0 +1,14 @@
|
||||
#!/bin/rc
|
||||
switch($#*){
|
||||
case 0
|
||||
exec upas/edmail -m
|
||||
}
|
||||
|
||||
switch($1){
|
||||
case -F* -m* -f* -r* -p* -e* -c* -D*
|
||||
exec upas/edmail -m $*
|
||||
case '-#'* -a*
|
||||
exec upas/sendmail $*
|
||||
case *
|
||||
exec upas/sendmail $*
|
||||
}
|
||||
6
src/cmd/upas/misc/qmail
Executable file
6
src/cmd/upas/misc/qmail
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/rc
|
||||
sender=$1
|
||||
shift
|
||||
addr=$1
|
||||
shift
|
||||
qer /mail/queue mail $sender $addr $* && runq /mail/queue /mail/lib/remotemail
|
||||
7
src/cmd/upas/misc/remotemail
Executable file
7
src/cmd/upas/misc/remotemail
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/bin/rc
|
||||
shift
|
||||
sender=$1
|
||||
shift
|
||||
addr=$1
|
||||
shift
|
||||
/bin/upas/smtp -g research.research.bell-labs.com $addr $sender $*
|
||||
20
src/cmd/upas/misc/rewrite
Normal file
20
src/cmd/upas/misc/rewrite
Normal file
@@ -0,0 +1,20 @@
|
||||
# case conversion for postmaster
|
||||
pOsTmAsTeR alias postmaster
|
||||
|
||||
# local mail
|
||||
[^!@]+ translate "/bin/upas/aliasmail '&'"
|
||||
local!(.*) >> /mail/box/\1/mbox
|
||||
\l!(.*) alias \1
|
||||
(helix|helix.bell-labs.com)!(.*) alias \2
|
||||
|
||||
# we can be just as complicated as BSD sendmail...
|
||||
# convert source domain address to a chain a@b@c@d...
|
||||
@([^@!,]*):([^!@]*)@([^!]*) alias \2@\3@\1
|
||||
@([^@!]*),([^!@,]*):([^!@]*)@([^!]*) alias @\1:\3@\4@\2
|
||||
|
||||
# convert a chain a@b@c@d... to ...d!c!b!a
|
||||
([^@]+)@([^@]+)@(.+) alias \2!\1@\3
|
||||
([^@]+)@([^@]+) alias \2!\1
|
||||
|
||||
# /mail/lib/remotemail will take care of gating to systems we don't know
|
||||
([^!]*)!(.*) | "/mail/lib/qmail '\s' 'net!\1'" "'\2'"
|
||||
197
src/cmd/upas/ml/common.c
Normal file
197
src/cmd/upas/ml/common.c
Normal file
@@ -0,0 +1,197 @@
|
||||
#include "common.h"
|
||||
#include "dat.h"
|
||||
|
||||
String*
|
||||
getaddr(Node *p)
|
||||
{
|
||||
for(; p; p = p->next){
|
||||
if(p->s && p->addr)
|
||||
return p->s;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
/* send messae adding our own reply-to and precedence */
|
||||
void
|
||||
getaddrs(void)
|
||||
{
|
||||
Field *f;
|
||||
|
||||
for(f = firstfield; f; f = f->next){
|
||||
if(f->node->c == FROM && from == nil)
|
||||
from = getaddr(f->node);
|
||||
if(f->node->c == SENDER && sender == nil)
|
||||
sender = getaddr(f->node);
|
||||
}
|
||||
}
|
||||
|
||||
/* write address file, should be append only */
|
||||
void
|
||||
writeaddr(char *file, char *addr, int rem, char *listname)
|
||||
{
|
||||
int fd;
|
||||
Dir nd;
|
||||
|
||||
fd = open(file, OWRITE);
|
||||
if(fd < 0){
|
||||
fd = create(file, OWRITE, DMAPPEND|0666);
|
||||
if(fd < 0)
|
||||
sysfatal("creating address list %s: %r", file);
|
||||
nulldir(&nd);
|
||||
nd.mode = DMAPPEND|0666;
|
||||
dirwstat(file, &nd);
|
||||
} else
|
||||
seek(fd, 0, 2);
|
||||
if(rem)
|
||||
fprint(fd, "!%s\n", addr);
|
||||
else
|
||||
fprint(fd, "%s\n", addr);
|
||||
close(fd);
|
||||
|
||||
if(*addr != '#')
|
||||
sendnotification(addr, listname, rem);
|
||||
}
|
||||
|
||||
void
|
||||
remaddr(char *addr)
|
||||
{
|
||||
Addr **l;
|
||||
Addr *a;
|
||||
|
||||
for(l = &al; *l; l = &(*l)->next){
|
||||
a = *l;
|
||||
if(strcmp(addr, a->addr) == 0){
|
||||
(*l) = a->next;
|
||||
free(a);
|
||||
na--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
addaddr(char *addr)
|
||||
{
|
||||
Addr **l;
|
||||
Addr *a;
|
||||
|
||||
for(l = &al; *l; l = &(*l)->next){
|
||||
if(strcmp(addr, (*l)->addr) == 0)
|
||||
return 0;
|
||||
}
|
||||
na++;
|
||||
*l = a = malloc(sizeof(*a)+strlen(addr)+1);
|
||||
if(a == nil)
|
||||
sysfatal("allocating: %r");
|
||||
a->addr = (char*)&a[1];
|
||||
strcpy(a->addr, addr);
|
||||
a->next = nil;
|
||||
*l = a;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* read address file */
|
||||
void
|
||||
readaddrs(char *file)
|
||||
{
|
||||
Biobuf *b;
|
||||
char *p;
|
||||
|
||||
b = Bopen(file, OREAD);
|
||||
if(b == nil)
|
||||
return;
|
||||
|
||||
while((p = Brdline(b, '\n')) != nil){
|
||||
p[Blinelen(b)-1] = 0;
|
||||
if(*p == '#')
|
||||
continue;
|
||||
if(*p == '!')
|
||||
remaddr(p+1);
|
||||
else
|
||||
addaddr(p);
|
||||
}
|
||||
Bterm(b);
|
||||
}
|
||||
|
||||
/* start a mailer sending to all the receivers */
|
||||
int
|
||||
startmailer(char *name)
|
||||
{
|
||||
int pfd[2];
|
||||
char **av;
|
||||
int ac;
|
||||
Addr *a;
|
||||
|
||||
putenv("upasname", "/dev/null");
|
||||
if(pipe(pfd) < 0)
|
||||
sysfatal("creating pipe: %r");
|
||||
switch(fork()){
|
||||
case -1:
|
||||
sysfatal("starting mailer: %r");
|
||||
case 0:
|
||||
close(pfd[1]);
|
||||
break;
|
||||
default:
|
||||
close(pfd[0]);
|
||||
return pfd[1];
|
||||
}
|
||||
|
||||
dup(pfd[0], 0);
|
||||
close(pfd[0]);
|
||||
|
||||
av = malloc(sizeof(char*)*(na+2));
|
||||
if(av == nil)
|
||||
sysfatal("starting mailer: %r");
|
||||
ac = 0;
|
||||
av[ac++] = name;
|
||||
for(a = al; a != nil; a = a->next)
|
||||
av[ac++] = a->addr;
|
||||
av[ac] = 0;
|
||||
exec("/bin/upas/send", av);
|
||||
sysfatal("execing mailer: %r");
|
||||
|
||||
/* not reached */
|
||||
return -1;
|
||||
}
|
||||
|
||||
void
|
||||
sendnotification(char *addr, char *listname, int rem)
|
||||
{
|
||||
int pfd[2];
|
||||
Waitmsg *w;
|
||||
|
||||
putenv("upasname", "/dev/null");
|
||||
if(pipe(pfd) < 0)
|
||||
sysfatal("creating pipe: %r");
|
||||
switch(fork()){
|
||||
case -1:
|
||||
sysfatal("starting mailer: %r");
|
||||
case 0:
|
||||
close(pfd[1]);
|
||||
dup(pfd[0], 0);
|
||||
close(pfd[0]);
|
||||
execl("/bin/upas/send", "mlnotify", addr, nil);
|
||||
sysfatal("execing mailer: %r");
|
||||
break;
|
||||
default:
|
||||
close(pfd[0]);
|
||||
fprint(pfd[1], "From: %s-owner\n\n", listname);
|
||||
if(rem)
|
||||
fprint(pfd[1], "You have removed from the %s mailing list\n", listname);
|
||||
else{
|
||||
fprint(pfd[1], "You have been added to the %s mailing list\n", listname);
|
||||
fprint(pfd[1], "To be removed, send an email to %s-owner containing\n",
|
||||
listname);
|
||||
fprint(pfd[1], "the word 'remove' in the subject or body.\n");
|
||||
}
|
||||
close(pfd[1]);
|
||||
|
||||
/* wait for mailer to end */
|
||||
while(w = wait()){
|
||||
if(w->msg != nil && w->msg[0])
|
||||
sysfatal("%s", w->msg);
|
||||
free(w);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
25
src/cmd/upas/ml/dat.h
Normal file
25
src/cmd/upas/ml/dat.h
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
#include "../smtp/smtp.h"
|
||||
#include "../smtp/y.tab.h"
|
||||
|
||||
typedef struct Addr Addr;
|
||||
struct Addr
|
||||
{
|
||||
char *addr;
|
||||
Addr *next;
|
||||
};
|
||||
|
||||
String *from;
|
||||
String *sender;
|
||||
Field *firstfield;
|
||||
int na;
|
||||
Addr *al;
|
||||
|
||||
extern String* getaddr(Node *p);
|
||||
extern void getaddrs(void);
|
||||
extern void writeaddr(char *file, char *addr, int, char *);
|
||||
extern void remaddr(char *addr);
|
||||
extern int addaddr(char *addr);
|
||||
extern void readaddrs(char *file);
|
||||
extern int startmailer(char *name);
|
||||
extern void sendnotification(char *addr, char *listname, int rem);
|
||||
40
src/cmd/upas/ml/mkfile
Normal file
40
src/cmd/upas/ml/mkfile
Normal file
@@ -0,0 +1,40 @@
|
||||
</$objtype/mkfile
|
||||
|
||||
TARG=ml\
|
||||
mlowner\
|
||||
mlmgr\
|
||||
|
||||
OFILES=\
|
||||
common.$O\
|
||||
|
||||
LIB=../common/libcommon.av\
|
||||
|
||||
UHFILES= ../common/common.h\
|
||||
../common/sys.h\
|
||||
dat.h\
|
||||
|
||||
HFILES=$UHFILES\
|
||||
../smtp/y.tab.h\
|
||||
|
||||
LIB=../common/libcommon.a$O\
|
||||
|
||||
BIN=/$objtype/bin/upas
|
||||
|
||||
UPDATE=\
|
||||
mkfile\
|
||||
$UHFILES\
|
||||
${TARG:%=%.c}\
|
||||
${OFILES:%.$O=%.c}\
|
||||
../smtp/rfc822.y\
|
||||
|
||||
</sys/src/cmd/mkmany
|
||||
CFLAGS=$CFLAGS -I../common
|
||||
|
||||
$O.ml: ../smtp/rfc822.tab.$O
|
||||
$O.mlowner: ../smtp/rfc822.tab.$O
|
||||
|
||||
../smtp/y.tab.h ../smtp/rfc822.tab.$O:
|
||||
@{
|
||||
cd ../smtp
|
||||
mk rfc822.tab.$O
|
||||
}
|
||||
167
src/cmd/upas/ml/ml.c
Normal file
167
src/cmd/upas/ml/ml.c
Normal file
@@ -0,0 +1,167 @@
|
||||
#include "common.h"
|
||||
#include "dat.h"
|
||||
|
||||
Biobuf in;
|
||||
|
||||
Addr *al;
|
||||
int na;
|
||||
String *from;
|
||||
String *sender;
|
||||
|
||||
void printmsg(int fd, String *msg, char *replyto, char *listname);
|
||||
void appendtoarchive(char* listname, String *firstline, String *msg);
|
||||
void printsubject(int fd, Field *f, char *listname);
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "usage: %s address-list-file listname\n", argv0);
|
||||
exits("usage");
|
||||
}
|
||||
|
||||
void
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
String *msg;
|
||||
String *firstline;
|
||||
char *listname, *alfile;
|
||||
Waitmsg *w;
|
||||
int fd;
|
||||
char *replytoname = nil;
|
||||
|
||||
ARGBEGIN{
|
||||
case 'r':
|
||||
replytoname = ARGF();
|
||||
break;
|
||||
}ARGEND;
|
||||
|
||||
rfork(RFENVG|RFREND);
|
||||
|
||||
if(argc < 2)
|
||||
usage();
|
||||
alfile = argv[0];
|
||||
listname = argv[1];
|
||||
if(replytoname == nil)
|
||||
replytoname = listname;
|
||||
|
||||
readaddrs(alfile);
|
||||
|
||||
if(Binit(&in, 0, OREAD) < 0)
|
||||
sysfatal("opening input: %r");
|
||||
|
||||
msg = s_new();
|
||||
firstline = s_new();
|
||||
|
||||
/* discard the 'From ' line */
|
||||
if(s_read_line(&in, firstline) == nil)
|
||||
sysfatal("reading input: %r");
|
||||
|
||||
/* read up to the first 128k of the message. more is redculous.
|
||||
Not if word documents are distributed. Upped it to 2MB (pb) */
|
||||
if(s_read(&in, msg, 2*1024*1024) <= 0)
|
||||
sysfatal("reading input: %r");
|
||||
|
||||
/* parse the header */
|
||||
yyinit(s_to_c(msg), s_len(msg));
|
||||
yyparse();
|
||||
|
||||
/* get the sender */
|
||||
getaddrs();
|
||||
if(from == nil)
|
||||
from = sender;
|
||||
if(from == nil)
|
||||
sysfatal("message must contain From: or Sender:");
|
||||
if(strcmp(listname, s_to_c(from)) == 0)
|
||||
sysfatal("can't remail messages from myself");
|
||||
addaddr(s_to_c(from));
|
||||
|
||||
/* start the mailer up and return a pipe to it */
|
||||
fd = startmailer(listname);
|
||||
|
||||
/* send message adding our own reply-to and precedence */
|
||||
printmsg(fd, msg, replytoname, listname);
|
||||
close(fd);
|
||||
|
||||
/* wait for mailer to end */
|
||||
while(w = wait()){
|
||||
if(w->msg != nil && w->msg[0])
|
||||
sysfatal("%s", w->msg);
|
||||
free(w);
|
||||
}
|
||||
|
||||
/* if the mailbox exits, cat the mail to the end of it */
|
||||
appendtoarchive(listname, firstline, msg);
|
||||
exits(0);
|
||||
}
|
||||
|
||||
/* send message filtering Reply-to out of messages */
|
||||
void
|
||||
printmsg(int fd, String *msg, char *replyto, char *listname)
|
||||
{
|
||||
Field *f, *subject;
|
||||
Node *p;
|
||||
char *cp, *ocp;
|
||||
|
||||
subject = nil;
|
||||
cp = s_to_c(msg);
|
||||
for(f = firstfield; f; f = f->next){
|
||||
ocp = cp;
|
||||
for(p = f->node; p; p = p->next)
|
||||
cp = p->end+1;
|
||||
if(f->node->c == REPLY_TO)
|
||||
continue;
|
||||
if(f->node->c == PRECEDENCE)
|
||||
continue;
|
||||
if(f->node->c == SUBJECT){
|
||||
subject = f;
|
||||
continue;
|
||||
}
|
||||
write(fd, ocp, cp-ocp);
|
||||
}
|
||||
printsubject(fd, subject, listname);
|
||||
fprint(fd, "Reply-To: %s\nPrecedence: bulk\n", replyto);
|
||||
write(fd, cp, s_len(msg) - (cp - s_to_c(msg)));
|
||||
}
|
||||
|
||||
/* if the mailbox exits, cat the mail to the end of it */
|
||||
void
|
||||
appendtoarchive(char* listname, String *firstline, String *msg)
|
||||
{
|
||||
String *mbox;
|
||||
int fd;
|
||||
|
||||
mbox = s_new();
|
||||
mboxpath("mbox", listname, mbox, 0);
|
||||
if(access(s_to_c(mbox), 0) < 0)
|
||||
return;
|
||||
fd = open(s_to_c(mbox), OWRITE);
|
||||
if(fd < 0)
|
||||
return;
|
||||
s_append(msg, "\n");
|
||||
write(fd, s_to_c(firstline), s_len(firstline));
|
||||
write(fd, s_to_c(msg), s_len(msg));
|
||||
}
|
||||
|
||||
/* add the listname to the subject */
|
||||
void
|
||||
printsubject(int fd, Field *f, char *listname)
|
||||
{
|
||||
char *s, *e;
|
||||
Node *p;
|
||||
char *ln;
|
||||
|
||||
if(f == nil || f->node == nil){
|
||||
fprint(fd, "Subject: [%s]\n", listname);
|
||||
return;
|
||||
}
|
||||
s = e = f->node->end + 1;
|
||||
for(p = f->node; p; p = p->next)
|
||||
e = p->end;
|
||||
*e = 0;
|
||||
ln = smprint("[%s]", listname);
|
||||
if(ln != nil && strstr(s, ln) == nil)
|
||||
fprint(fd, "Subject: %s%s\n", ln, s);
|
||||
else
|
||||
fprint(fd, "Subject:%s\n", s);
|
||||
free(ln);
|
||||
}
|
||||
110
src/cmd/upas/ml/mlmgr.c
Normal file
110
src/cmd/upas/ml/mlmgr.c
Normal file
@@ -0,0 +1,110 @@
|
||||
#include "common.h"
|
||||
#include "dat.h"
|
||||
|
||||
int cflag;
|
||||
int aflag;
|
||||
int rflag;
|
||||
|
||||
int createpipeto(char *alfile, char *user, char *listname, int owner);
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "usage:\t%s -c listname\n", argv0);
|
||||
fprint(2, "\t%s -[ar] listname addr\n", argv0);
|
||||
exits("usage");
|
||||
}
|
||||
|
||||
void
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
char *listname, *addr;
|
||||
String *owner, *alfile;
|
||||
|
||||
rfork(RFENVG|RFREND);
|
||||
|
||||
ARGBEGIN{
|
||||
case 'c':
|
||||
cflag = 1;
|
||||
break;
|
||||
case 'r':
|
||||
rflag = 1;
|
||||
break;
|
||||
case 'a':
|
||||
aflag = 1;
|
||||
break;
|
||||
}ARGEND;
|
||||
|
||||
if(aflag + rflag + cflag > 1){
|
||||
fprint(2, "%s: -a, -r, and -c are mutually exclusive\n", argv0);
|
||||
exits("usage");
|
||||
}
|
||||
|
||||
if(argc < 1)
|
||||
usage();
|
||||
|
||||
listname = argv[0];
|
||||
alfile = s_new();
|
||||
mboxpath("address-list", listname, alfile, 0);
|
||||
|
||||
if(cflag){
|
||||
owner = s_copy(listname);
|
||||
s_append(owner, "-owner");
|
||||
if(creatembox(listname, nil) < 0)
|
||||
sysfatal("creating %s's mbox: %r", listname);
|
||||
if(creatembox(s_to_c(owner), nil) < 0)
|
||||
sysfatal("creating %s's mbox: %r", s_to_c(owner));
|
||||
if(createpipeto(s_to_c(alfile), listname, listname, 0) < 0)
|
||||
sysfatal("creating %s's pipeto: %r", s_to_c(owner));
|
||||
if(createpipeto(s_to_c(alfile), s_to_c(owner), listname, 1) < 0)
|
||||
sysfatal("creating %s's pipeto: %r", s_to_c(owner));
|
||||
writeaddr(s_to_c(alfile), "# mlmgr c flag", 0, listname);
|
||||
} else if(rflag){
|
||||
if(argc != 2)
|
||||
usage();
|
||||
addr = argv[1];
|
||||
writeaddr(s_to_c(alfile), "# mlmgr r flag", 0, listname);
|
||||
writeaddr(s_to_c(alfile), addr, 1, listname);
|
||||
} else if(aflag){
|
||||
if(argc != 2)
|
||||
usage();
|
||||
addr = argv[1];
|
||||
writeaddr(s_to_c(alfile), "# mlmgr a flag", 0, listname);
|
||||
writeaddr(s_to_c(alfile), addr, 0, listname);
|
||||
} else
|
||||
usage();
|
||||
exits(0);
|
||||
}
|
||||
|
||||
int
|
||||
createpipeto(char *alfile, char *user, char *listname, int owner)
|
||||
{
|
||||
String *f;
|
||||
int fd;
|
||||
Dir *d;
|
||||
|
||||
f = s_new();
|
||||
mboxpath("pipeto", user, f, 0);
|
||||
fprint(2, "creating new pipeto: %s\n", s_to_c(f));
|
||||
fd = create(s_to_c(f), OWRITE, 0775);
|
||||
if(fd < 0)
|
||||
return -1;
|
||||
d = dirfstat(fd);
|
||||
if(d == nil){
|
||||
fprint(fd, "Couldn't stat %s: %r\n", s_to_c(f));
|
||||
return -1;
|
||||
}
|
||||
d->mode |= 0775;
|
||||
if(dirfwstat(fd, d) < 0)
|
||||
fprint(fd, "Couldn't wstat %s: %r\n", s_to_c(f));
|
||||
free(d);
|
||||
|
||||
fprint(fd, "#!/bin/rc\n");
|
||||
if(owner)
|
||||
fprint(fd, "/bin/upas/mlowner %s %s\n", alfile, listname);
|
||||
else
|
||||
fprint(fd, "/bin/upas/ml %s %s\n", alfile, user);
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
64
src/cmd/upas/ml/mlowner.c
Normal file
64
src/cmd/upas/ml/mlowner.c
Normal file
@@ -0,0 +1,64 @@
|
||||
#include "common.h"
|
||||
#include "dat.h"
|
||||
|
||||
Biobuf in;
|
||||
|
||||
String *from;
|
||||
String *sender;
|
||||
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "usage: %s address-list-file listname\n", argv0);
|
||||
exits("usage");
|
||||
}
|
||||
|
||||
void
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
String *msg;
|
||||
char *alfile;
|
||||
char *listname;
|
||||
|
||||
ARGBEGIN{
|
||||
}ARGEND;
|
||||
|
||||
rfork(RFENVG|RFREND);
|
||||
|
||||
if(argc < 2)
|
||||
usage();
|
||||
alfile = argv[0];
|
||||
listname = argv[1];
|
||||
|
||||
if(Binit(&in, 0, OREAD) < 0)
|
||||
sysfatal("opening input: %r");
|
||||
|
||||
msg = s_new();
|
||||
|
||||
/* discard the 'From ' line */
|
||||
if(s_read_line(&in, msg) == nil)
|
||||
sysfatal("reading input: %r");
|
||||
|
||||
/* read up to the first 128k of the message. more is redculous */
|
||||
if(s_read(&in, s_restart(msg), 128*1024) <= 0)
|
||||
sysfatal("reading input: %r");
|
||||
|
||||
/* parse the header */
|
||||
yyinit(s_to_c(msg), s_len(msg));
|
||||
yyparse();
|
||||
|
||||
/* get the sender */
|
||||
getaddrs();
|
||||
if(from == nil)
|
||||
from = sender;
|
||||
if(from == nil)
|
||||
sysfatal("message must contain From: or Sender:");
|
||||
|
||||
if(strstr(s_to_c(msg), "remove")||strstr(s_to_c(msg), "unsubscribe"))
|
||||
writeaddr(alfile, s_to_c(from), 1, listname);
|
||||
else if(strstr(s_to_c(msg), "subscribe"))
|
||||
writeaddr(alfile, s_to_c(from), 0, listname);
|
||||
|
||||
exits(0);
|
||||
}
|
||||
20
src/cmd/upas/ned/mkfile
Normal file
20
src/cmd/upas/ned/mkfile
Normal file
@@ -0,0 +1,20 @@
|
||||
<$PLAN9/src/mkhdr
|
||||
|
||||
TARG=nedmail
|
||||
|
||||
LIB=../common/libcommon.a\
|
||||
|
||||
HFILES= ../common/common.h\
|
||||
|
||||
OFILES=nedmail.$O
|
||||
|
||||
BIN=$PLAN9/bin/upas
|
||||
|
||||
UPDATE=\
|
||||
mkfile\
|
||||
${OFILES:%.$O=%.c}\
|
||||
$HFILES\
|
||||
|
||||
<$PLAN9/src/mkone
|
||||
CFLAGS=$CFLAGS -I../common
|
||||
|
||||
2586
src/cmd/upas/ned/nedmail.c
Normal file
2586
src/cmd/upas/ned/nedmail.c
Normal file
File diff suppressed because it is too large
Load Diff
16
src/cmd/upas/pop3/mkfile
Normal file
16
src/cmd/upas/pop3/mkfile
Normal file
@@ -0,0 +1,16 @@
|
||||
</$objtype/mkfile
|
||||
|
||||
TARG=pop3
|
||||
|
||||
OFILES=pop3.$O
|
||||
|
||||
BIN=/$objtype/bin/upas
|
||||
LIB=../common/libcommon.a$O
|
||||
|
||||
UPDATE=\
|
||||
mkfile\
|
||||
${OFILES:%.$O=%.c}\
|
||||
|
||||
</sys/src/cmd/mkone
|
||||
|
||||
CFLAGS=$CFLAGS -I../common
|
||||
804
src/cmd/upas/pop3/pop3.c
Normal file
804
src/cmd/upas/pop3/pop3.c
Normal file
@@ -0,0 +1,804 @@
|
||||
#include "common.h"
|
||||
#include <ctype.h>
|
||||
#include <auth.h>
|
||||
#include <libsec.h>
|
||||
|
||||
typedef struct Cmd Cmd;
|
||||
struct Cmd
|
||||
{
|
||||
char *name;
|
||||
int needauth;
|
||||
int (*f)(char*);
|
||||
};
|
||||
|
||||
static void hello(void);
|
||||
static int apopcmd(char*);
|
||||
static int capacmd(char*);
|
||||
static int delecmd(char*);
|
||||
static int listcmd(char*);
|
||||
static int noopcmd(char*);
|
||||
static int passcmd(char*);
|
||||
static int quitcmd(char*);
|
||||
static int rsetcmd(char*);
|
||||
static int retrcmd(char*);
|
||||
static int statcmd(char*);
|
||||
static int stlscmd(char*);
|
||||
static int topcmd(char*);
|
||||
static int synccmd(char*);
|
||||
static int uidlcmd(char*);
|
||||
static int usercmd(char*);
|
||||
static char *nextarg(char*);
|
||||
static int getcrnl(char*, int);
|
||||
static int readmbox(char*);
|
||||
static void sendcrnl(char*, ...);
|
||||
static int senderr(char*, ...);
|
||||
static int sendok(char*, ...);
|
||||
#pragma varargck argpos sendcrnl 1
|
||||
#pragma varargck argpos senderr 1
|
||||
#pragma varargck argpos sendok 1
|
||||
|
||||
Cmd cmdtab[] =
|
||||
{
|
||||
"apop", 0, apopcmd,
|
||||
"capa", 0, capacmd,
|
||||
"dele", 1, delecmd,
|
||||
"list", 1, listcmd,
|
||||
"noop", 0, noopcmd,
|
||||
"pass", 0, passcmd,
|
||||
"quit", 0, quitcmd,
|
||||
"rset", 0, rsetcmd,
|
||||
"retr", 1, retrcmd,
|
||||
"stat", 1, statcmd,
|
||||
"stls", 0, stlscmd,
|
||||
"sync", 1, synccmd,
|
||||
"top", 1, topcmd,
|
||||
"uidl", 1, uidlcmd,
|
||||
"user", 0, usercmd,
|
||||
0, 0, 0,
|
||||
};
|
||||
|
||||
static Biobuf in;
|
||||
static Biobuf out;
|
||||
static int passwordinclear;
|
||||
static int didtls;
|
||||
|
||||
typedef struct Msg Msg;
|
||||
struct Msg
|
||||
{
|
||||
int upasnum;
|
||||
char digest[64];
|
||||
int bytes;
|
||||
int deleted;
|
||||
};
|
||||
|
||||
static int totalbytes;
|
||||
static int totalmsgs;
|
||||
static Msg *msg;
|
||||
static int nmsg;
|
||||
static int loggedin;
|
||||
static int debug;
|
||||
static uchar *tlscert;
|
||||
static int ntlscert;
|
||||
static char *peeraddr;
|
||||
static char tmpaddr[64];
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "usage: upas/pop3 [-a authmboxfile] [-d debugfile] [-p]\n");
|
||||
exits("usage");
|
||||
}
|
||||
|
||||
void
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int fd;
|
||||
char *arg, cmdbuf[1024];
|
||||
Cmd *c;
|
||||
|
||||
rfork(RFNAMEG);
|
||||
Binit(&in, 0, OREAD);
|
||||
Binit(&out, 1, OWRITE);
|
||||
|
||||
ARGBEGIN{
|
||||
case 'a':
|
||||
loggedin = 1;
|
||||
if(readmbox(EARGF(usage())) < 0)
|
||||
exits(nil);
|
||||
break;
|
||||
case 'd':
|
||||
debug++;
|
||||
if((fd = create(EARGF(usage()), OWRITE, 0666)) >= 0 && fd != 2){
|
||||
dup(fd, 2);
|
||||
close(fd);
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
strecpy(tmpaddr, tmpaddr+sizeof tmpaddr, EARGF(usage()));
|
||||
if(arg = strchr(tmpaddr, '!'))
|
||||
*arg = '\0';
|
||||
peeraddr = tmpaddr;
|
||||
break;
|
||||
case 't':
|
||||
tlscert = readcert(EARGF(usage()), &ntlscert);
|
||||
if(tlscert == nil){
|
||||
senderr("cannot read TLS certificate: %r");
|
||||
exits(nil);
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
passwordinclear = 1;
|
||||
break;
|
||||
}ARGEND
|
||||
|
||||
/* do before TLS */
|
||||
if(peeraddr == nil)
|
||||
peeraddr = remoteaddr(0,0);
|
||||
|
||||
hello();
|
||||
|
||||
while(Bflush(&out), getcrnl(cmdbuf, sizeof cmdbuf) > 0){
|
||||
arg = nextarg(cmdbuf);
|
||||
for(c=cmdtab; c->name; c++)
|
||||
if(cistrcmp(c->name, cmdbuf) == 0)
|
||||
break;
|
||||
if(c->name == 0){
|
||||
senderr("unknown command %s", cmdbuf);
|
||||
continue;
|
||||
}
|
||||
if(c->needauth && !loggedin){
|
||||
senderr("%s requires authentication", cmdbuf);
|
||||
continue;
|
||||
}
|
||||
(*c->f)(arg);
|
||||
}
|
||||
exits(nil);
|
||||
}
|
||||
|
||||
/* sort directories in increasing message number order */
|
||||
static int
|
||||
dircmp(void *a, void *b)
|
||||
{
|
||||
return atoi(((Dir*)a)->name) - atoi(((Dir*)b)->name);
|
||||
}
|
||||
|
||||
static int
|
||||
readmbox(char *box)
|
||||
{
|
||||
int fd, i, n, nd, lines, pid;
|
||||
char buf[100], err[ERRMAX];
|
||||
char *p;
|
||||
Biobuf *b;
|
||||
Dir *d, *draw;
|
||||
Msg *m;
|
||||
Waitmsg *w;
|
||||
|
||||
unmount(nil, "/mail/fs");
|
||||
switch(pid = fork()){
|
||||
case -1:
|
||||
return senderr("can't fork to start upas/fs");
|
||||
|
||||
case 0:
|
||||
close(0);
|
||||
close(1);
|
||||
open("/dev/null", OREAD);
|
||||
open("/dev/null", OWRITE);
|
||||
execl("/bin/upas/fs", "upas/fs", "-np", "-f", box, nil);
|
||||
snprint(err, sizeof err, "upas/fs: %r");
|
||||
_exits(err);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if((w = wait()) == nil || w->pid != pid || w->msg[0] != '\0'){
|
||||
if(w && w->pid==pid)
|
||||
return senderr("%s", w->msg);
|
||||
else
|
||||
return senderr("can't initialize upas/fs");
|
||||
}
|
||||
free(w);
|
||||
|
||||
if(chdir("/mail/fs/mbox") < 0)
|
||||
return senderr("can't initialize upas/fs: %r");
|
||||
|
||||
if((fd = open(".", OREAD)) < 0)
|
||||
return senderr("cannot open /mail/fs/mbox: %r");
|
||||
nd = dirreadall(fd, &d);
|
||||
close(fd);
|
||||
if(nd < 0)
|
||||
return senderr("cannot read from /mail/fs/mbox: %r");
|
||||
|
||||
msg = mallocz(sizeof(Msg)*nd, 1);
|
||||
if(msg == nil)
|
||||
return senderr("out of memory");
|
||||
|
||||
if(nd == 0)
|
||||
return 0;
|
||||
qsort(d, nd, sizeof(d[0]), dircmp);
|
||||
|
||||
for(i=0; i<nd; i++){
|
||||
m = &msg[nmsg];
|
||||
m->upasnum = atoi(d[i].name);
|
||||
sprint(buf, "%d/digest", m->upasnum);
|
||||
if((fd = open(buf, OREAD)) < 0)
|
||||
continue;
|
||||
n = readn(fd, m->digest, sizeof m->digest - 1);
|
||||
close(fd);
|
||||
if(n < 0)
|
||||
continue;
|
||||
m->digest[n] = '\0';
|
||||
|
||||
/*
|
||||
* We need the number of message lines so that we
|
||||
* can adjust the byte count to include \r's.
|
||||
* Upas/fs gives us the number of lines in the raw body
|
||||
* in the lines file, but we have to count rawheader ourselves.
|
||||
* There is one blank line between raw header and raw body.
|
||||
*/
|
||||
sprint(buf, "%d/rawheader", m->upasnum);
|
||||
if((b = Bopen(buf, OREAD)) == nil)
|
||||
continue;
|
||||
lines = 0;
|
||||
for(;;){
|
||||
p = Brdline(b, '\n');
|
||||
if(p == nil){
|
||||
if((n = Blinelen(b)) == 0)
|
||||
break;
|
||||
Bseek(b, n, 1);
|
||||
}else
|
||||
lines++;
|
||||
}
|
||||
Bterm(b);
|
||||
lines++;
|
||||
sprint(buf, "%d/lines", m->upasnum);
|
||||
if((fd = open(buf, OREAD)) < 0)
|
||||
continue;
|
||||
n = readn(fd, buf, sizeof buf - 1);
|
||||
close(fd);
|
||||
if(n < 0)
|
||||
continue;
|
||||
buf[n] = '\0';
|
||||
lines += atoi(buf);
|
||||
|
||||
sprint(buf, "%d/raw", m->upasnum);
|
||||
if((draw = dirstat(buf)) == nil)
|
||||
continue;
|
||||
m->bytes = lines+draw->length;
|
||||
free(draw);
|
||||
nmsg++;
|
||||
totalmsgs++;
|
||||
totalbytes += m->bytes;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* get a line that ends in crnl or cr, turn terminating crnl into a nl
|
||||
*
|
||||
* return 0 on EOF
|
||||
*/
|
||||
static int
|
||||
getcrnl(char *buf, int n)
|
||||
{
|
||||
int c;
|
||||
char *ep;
|
||||
char *bp;
|
||||
Biobuf *fp = ∈
|
||||
|
||||
Bflush(&out);
|
||||
|
||||
bp = buf;
|
||||
ep = bp + n - 1;
|
||||
while(bp != ep){
|
||||
c = Bgetc(fp);
|
||||
if(debug) {
|
||||
seek(2, 0, 2);
|
||||
fprint(2, "%c", c);
|
||||
}
|
||||
switch(c){
|
||||
case -1:
|
||||
*bp = 0;
|
||||
if(bp==buf)
|
||||
return 0;
|
||||
else
|
||||
return bp-buf;
|
||||
case '\r':
|
||||
c = Bgetc(fp);
|
||||
if(c == '\n'){
|
||||
if(debug) {
|
||||
seek(2, 0, 2);
|
||||
fprint(2, "%c", c);
|
||||
}
|
||||
*bp = 0;
|
||||
return bp-buf;
|
||||
}
|
||||
Bungetc(fp);
|
||||
c = '\r';
|
||||
break;
|
||||
case '\n':
|
||||
*bp = 0;
|
||||
return bp-buf;
|
||||
}
|
||||
*bp++ = c;
|
||||
}
|
||||
*bp = 0;
|
||||
return bp-buf;
|
||||
}
|
||||
|
||||
static void
|
||||
sendcrnl(char *fmt, ...)
|
||||
{
|
||||
char buf[1024];
|
||||
va_list arg;
|
||||
|
||||
va_start(arg, fmt);
|
||||
vseprint(buf, buf+sizeof(buf), fmt, arg);
|
||||
va_end(arg);
|
||||
if(debug)
|
||||
fprint(2, "-> %s\n", buf);
|
||||
Bprint(&out, "%s\r\n", buf);
|
||||
}
|
||||
|
||||
static int
|
||||
senderr(char *fmt, ...)
|
||||
{
|
||||
char buf[1024];
|
||||
va_list arg;
|
||||
|
||||
va_start(arg, fmt);
|
||||
vseprint(buf, buf+sizeof(buf), fmt, arg);
|
||||
va_end(arg);
|
||||
if(debug)
|
||||
fprint(2, "-> -ERR %s\n", buf);
|
||||
Bprint(&out, "-ERR %s\r\n", buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
sendok(char *fmt, ...)
|
||||
{
|
||||
char buf[1024];
|
||||
va_list arg;
|
||||
|
||||
va_start(arg, fmt);
|
||||
vseprint(buf, buf+sizeof(buf), fmt, arg);
|
||||
va_end(arg);
|
||||
if(*buf){
|
||||
if(debug)
|
||||
fprint(2, "-> +OK %s\n", buf);
|
||||
Bprint(&out, "+OK %s\r\n", buf);
|
||||
} else {
|
||||
if(debug)
|
||||
fprint(2, "-> +OK\n");
|
||||
Bprint(&out, "+OK\r\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
capacmd(char*)
|
||||
{
|
||||
sendok("");
|
||||
sendcrnl("TOP");
|
||||
if(passwordinclear || didtls)
|
||||
sendcrnl("USER");
|
||||
sendcrnl("PIPELINING");
|
||||
sendcrnl("UIDL");
|
||||
sendcrnl("STLS");
|
||||
sendcrnl(".");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
delecmd(char *arg)
|
||||
{
|
||||
int n;
|
||||
|
||||
if(*arg==0)
|
||||
return senderr("DELE requires a message number");
|
||||
|
||||
n = atoi(arg)-1;
|
||||
if(n < 0 || n >= nmsg || msg[n].deleted)
|
||||
return senderr("no such message");
|
||||
|
||||
msg[n].deleted = 1;
|
||||
totalmsgs--;
|
||||
totalbytes -= msg[n].bytes;
|
||||
sendok("message %d deleted", n+1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
listcmd(char *arg)
|
||||
{
|
||||
int i, n;
|
||||
|
||||
if(*arg == 0){
|
||||
sendok("+%d message%s (%d octets)", totalmsgs, totalmsgs==1 ? "":"s", totalbytes);
|
||||
for(i=0; i<nmsg; i++){
|
||||
if(msg[i].deleted)
|
||||
continue;
|
||||
sendcrnl("%d %d", i+1, msg[i].bytes);
|
||||
}
|
||||
sendcrnl(".");
|
||||
}else{
|
||||
n = atoi(arg)-1;
|
||||
if(n < 0 || n >= nmsg || msg[n].deleted)
|
||||
return senderr("no such message");
|
||||
sendok("%d %d", n+1, msg[n].bytes);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
noopcmd(char *arg)
|
||||
{
|
||||
USED(arg);
|
||||
sendok("");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
_synccmd(char*)
|
||||
{
|
||||
int i, fd;
|
||||
char *s;
|
||||
Fmt f;
|
||||
|
||||
if(!loggedin){
|
||||
sendok("");
|
||||
return;
|
||||
}
|
||||
|
||||
fmtstrinit(&f);
|
||||
fmtprint(&f, "delete mbox");
|
||||
for(i=0; i<nmsg; i++)
|
||||
if(msg[i].deleted)
|
||||
fmtprint(&f, " %d", msg[i].upasnum);
|
||||
s = fmtstrflush(&f);
|
||||
if(strcmp(s, "delete mbox") != 0){ /* must have something to delete */
|
||||
if((fd = open("../ctl", OWRITE)) < 0){
|
||||
senderr("open ctl to delete messages: %r");
|
||||
return;
|
||||
}
|
||||
if(write(fd, s, strlen(s)) < 0){
|
||||
senderr("error deleting messages: %r");
|
||||
return;
|
||||
}
|
||||
}
|
||||
sendok("");
|
||||
}
|
||||
|
||||
static int
|
||||
synccmd(char*)
|
||||
{
|
||||
_synccmd(nil);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
quitcmd(char*)
|
||||
{
|
||||
synccmd(nil);
|
||||
exits(nil);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
retrcmd(char *arg)
|
||||
{
|
||||
int n;
|
||||
Biobuf *b;
|
||||
char buf[40], *p;
|
||||
|
||||
if(*arg == 0)
|
||||
return senderr("RETR requires a message number");
|
||||
n = atoi(arg)-1;
|
||||
if(n < 0 || n >= nmsg || msg[n].deleted)
|
||||
return senderr("no such message");
|
||||
snprint(buf, sizeof buf, "%d/raw", msg[n].upasnum);
|
||||
if((b = Bopen(buf, OREAD)) == nil)
|
||||
return senderr("message disappeared");
|
||||
sendok("");
|
||||
while((p = Brdstr(b, '\n', 1)) != nil){
|
||||
if(p[0]=='.')
|
||||
Bwrite(&out, ".", 1);
|
||||
Bwrite(&out, p, strlen(p));
|
||||
Bwrite(&out, "\r\n", 2);
|
||||
free(p);
|
||||
}
|
||||
Bterm(b);
|
||||
sendcrnl(".");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
rsetcmd(char*)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0; i<nmsg; i++){
|
||||
if(msg[i].deleted){
|
||||
msg[i].deleted = 0;
|
||||
totalmsgs++;
|
||||
totalbytes += msg[i].bytes;
|
||||
}
|
||||
}
|
||||
return sendok("");
|
||||
}
|
||||
|
||||
static int
|
||||
statcmd(char*)
|
||||
{
|
||||
return sendok("%d %d", totalmsgs, totalbytes);
|
||||
}
|
||||
|
||||
static int
|
||||
trace(char *fmt, ...)
|
||||
{
|
||||
va_list arg;
|
||||
int n;
|
||||
|
||||
va_start(arg, fmt);
|
||||
n = vfprint(2, fmt, arg);
|
||||
va_end(arg);
|
||||
return n;
|
||||
}
|
||||
|
||||
static int
|
||||
stlscmd(char*)
|
||||
{
|
||||
int fd;
|
||||
TLSconn conn;
|
||||
|
||||
if(didtls)
|
||||
return senderr("tls already started");
|
||||
if(!tlscert)
|
||||
return senderr("don't have any tls credentials");
|
||||
sendok("");
|
||||
Bflush(&out);
|
||||
|
||||
memset(&conn, 0, sizeof conn);
|
||||
conn.cert = tlscert;
|
||||
conn.certlen = ntlscert;
|
||||
if(debug)
|
||||
conn.trace = trace;
|
||||
fd = tlsServer(0, &conn);
|
||||
if(fd < 0)
|
||||
sysfatal("tlsServer: %r");
|
||||
dup(fd, 0);
|
||||
dup(fd, 1);
|
||||
close(fd);
|
||||
Binit(&in, 0, OREAD);
|
||||
Binit(&out, 1, OWRITE);
|
||||
didtls = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
topcmd(char *arg)
|
||||
{
|
||||
int done, i, lines, n;
|
||||
char buf[40], *p;
|
||||
Biobuf *b;
|
||||
|
||||
if(*arg == 0)
|
||||
return senderr("TOP requires a message number");
|
||||
n = atoi(arg)-1;
|
||||
if(n < 0 || n >= nmsg || msg[n].deleted)
|
||||
return senderr("no such message");
|
||||
arg = nextarg(arg);
|
||||
if(*arg == 0)
|
||||
return senderr("TOP requires a line count");
|
||||
lines = atoi(arg);
|
||||
if(lines < 0)
|
||||
return senderr("bad args to TOP");
|
||||
snprint(buf, sizeof buf, "%d/raw", msg[n].upasnum);
|
||||
if((b = Bopen(buf, OREAD)) == nil)
|
||||
return senderr("message disappeared");
|
||||
sendok("");
|
||||
while(p = Brdstr(b, '\n', 1)){
|
||||
if(p[0]=='.')
|
||||
Bputc(&out, '.');
|
||||
Bwrite(&out, p, strlen(p));
|
||||
Bwrite(&out, "\r\n", 2);
|
||||
done = p[0]=='\0';
|
||||
free(p);
|
||||
if(done)
|
||||
break;
|
||||
}
|
||||
for(i=0; i<lines; i++){
|
||||
p = Brdstr(b, '\n', 1);
|
||||
if(p == nil)
|
||||
break;
|
||||
if(p[0]=='.')
|
||||
Bwrite(&out, ".", 1);
|
||||
Bwrite(&out, p, strlen(p));
|
||||
Bwrite(&out, "\r\n", 2);
|
||||
free(p);
|
||||
}
|
||||
sendcrnl(".");
|
||||
Bterm(b);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
uidlcmd(char *arg)
|
||||
{
|
||||
int n;
|
||||
|
||||
if(*arg==0){
|
||||
sendok("");
|
||||
for(n=0; n<nmsg; n++){
|
||||
if(msg[n].deleted)
|
||||
continue;
|
||||
sendcrnl("%d %s", n+1, msg[n].digest);
|
||||
}
|
||||
sendcrnl(".");
|
||||
}else{
|
||||
n = atoi(arg)-1;
|
||||
if(n < 0 || n >= nmsg || msg[n].deleted)
|
||||
return senderr("no such message");
|
||||
sendok("%d %s", n+1, msg[n].digest);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char*
|
||||
nextarg(char *p)
|
||||
{
|
||||
while(*p && *p != ' ' && *p != '\t')
|
||||
p++;
|
||||
while(*p == ' ' || *p == '\t')
|
||||
*p++ = 0;
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
* authentication
|
||||
*/
|
||||
Chalstate *chs;
|
||||
char user[256];
|
||||
char box[256];
|
||||
char cbox[256];
|
||||
|
||||
static void
|
||||
hello(void)
|
||||
{
|
||||
fmtinstall('H', encodefmt);
|
||||
if((chs = auth_challenge("proto=apop role=server")) == nil){
|
||||
senderr("auth server not responding, try later");
|
||||
exits(nil);
|
||||
}
|
||||
|
||||
sendok("POP3 server ready %s", chs->chal);
|
||||
}
|
||||
|
||||
static int
|
||||
setuser(char *arg)
|
||||
{
|
||||
char *p;
|
||||
|
||||
strcpy(box, "/mail/box/");
|
||||
strecpy(box+strlen(box), box+sizeof box-7, arg);
|
||||
strcpy(cbox, box);
|
||||
cleanname(cbox);
|
||||
if(strcmp(cbox, box) != 0)
|
||||
return senderr("bad mailbox name");
|
||||
strcat(box, "/mbox");
|
||||
|
||||
strecpy(user, user+sizeof user, arg);
|
||||
if(p = strchr(user, '/'))
|
||||
*p = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
usercmd(char *arg)
|
||||
{
|
||||
if(loggedin)
|
||||
return senderr("already authenticated");
|
||||
if(*arg == 0)
|
||||
return senderr("USER requires argument");
|
||||
if(setuser(arg) < 0)
|
||||
return -1;
|
||||
return sendok("");
|
||||
}
|
||||
|
||||
static void
|
||||
enableaddr(void)
|
||||
{
|
||||
int fd;
|
||||
char buf[64];
|
||||
|
||||
/* hide the peer IP address under a rock in the ratifier FS */
|
||||
if(peeraddr == 0 || *peeraddr == 0)
|
||||
return;
|
||||
|
||||
sprint(buf, "/mail/ratify/trusted/%s#32", peeraddr);
|
||||
|
||||
/*
|
||||
* if the address is already there and the user owns it,
|
||||
* remove it and recreate it to give him a new time quanta.
|
||||
*/
|
||||
if(access(buf, 0) >= 0 && remove(buf) < 0)
|
||||
return;
|
||||
|
||||
fd = create(buf, OREAD, 0666);
|
||||
if(fd >= 0){
|
||||
close(fd);
|
||||
// syslog(0, "pop3", "ratified %s", peeraddr);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
dologin(char *response)
|
||||
{
|
||||
AuthInfo *ai;
|
||||
static int tries;
|
||||
|
||||
chs->user = user;
|
||||
chs->resp = response;
|
||||
chs->nresp = strlen(response);
|
||||
if((ai = auth_response(chs)) == nil){
|
||||
if(tries++ >= 5){
|
||||
senderr("authentication failed: %r; server exiting");
|
||||
exits(nil);
|
||||
}
|
||||
return senderr("authentication failed");
|
||||
}
|
||||
|
||||
if(auth_chuid(ai, nil) < 0){
|
||||
senderr("chuid failed: %r; server exiting");
|
||||
exits(nil);
|
||||
}
|
||||
auth_freeAI(ai);
|
||||
auth_freechal(chs);
|
||||
chs = nil;
|
||||
|
||||
loggedin = 1;
|
||||
if(newns(user, 0) < 0){
|
||||
senderr("newns failed: %r; server exiting");
|
||||
exits(nil);
|
||||
}
|
||||
|
||||
enableaddr();
|
||||
if(readmbox(box) < 0)
|
||||
exits(nil);
|
||||
return sendok("mailbox is %s", box);
|
||||
}
|
||||
|
||||
static int
|
||||
passcmd(char *arg)
|
||||
{
|
||||
DigestState *s;
|
||||
uchar digest[MD5dlen];
|
||||
char response[2*MD5dlen+1];
|
||||
|
||||
if(passwordinclear==0 && didtls==0)
|
||||
return senderr("password in the clear disallowed");
|
||||
|
||||
/* use password to encode challenge */
|
||||
if((chs = auth_challenge("proto=apop role=server")) == nil)
|
||||
return senderr("couldn't get apop challenge");
|
||||
|
||||
// hash challenge with secret and convert to ascii
|
||||
s = md5((uchar*)chs->chal, chs->nchal, 0, 0);
|
||||
md5((uchar*)arg, strlen(arg), digest, s);
|
||||
snprint(response, sizeof response, "%.*H", MD5dlen, digest);
|
||||
return dologin(response);
|
||||
}
|
||||
|
||||
static int
|
||||
apopcmd(char *arg)
|
||||
{
|
||||
char *resp;
|
||||
|
||||
resp = nextarg(arg);
|
||||
if(setuser(arg) < 0)
|
||||
return -1;
|
||||
return dologin(resp);
|
||||
}
|
||||
|
||||
22
src/cmd/upas/q/mkfile
Normal file
22
src/cmd/upas/q/mkfile
Normal file
@@ -0,0 +1,22 @@
|
||||
<$PLAN9/src/mkhdr
|
||||
|
||||
TARG = qer\
|
||||
runq\
|
||||
|
||||
OFILES=
|
||||
|
||||
HFILES=../common/common.h\
|
||||
../common/sys.h\
|
||||
|
||||
LIB=../common/libcommon.a\
|
||||
|
||||
BIN=$PLAN9/bin/upas
|
||||
|
||||
UPDATE=\
|
||||
mkfile\
|
||||
$HFILES\
|
||||
${OFILES:%.$O=%.c}\
|
||||
${TARG:%=%.c}\
|
||||
|
||||
<$PLAN9/src/mkmany
|
||||
CFLAGS=$CFLAGS -I../common
|
||||
193
src/cmd/upas/q/qer.c
Normal file
193
src/cmd/upas/q/qer.c
Normal file
@@ -0,0 +1,193 @@
|
||||
#include "common.h"
|
||||
|
||||
typedef struct Qfile Qfile;
|
||||
struct Qfile
|
||||
{
|
||||
Qfile *next;
|
||||
char *name;
|
||||
char *tname;
|
||||
} *files;
|
||||
|
||||
char *user;
|
||||
int isnone;
|
||||
|
||||
int copy(Qfile*);
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "usage: qer [-f file] [-q dir] q-root description reply-to arg-list\n");
|
||||
exits("usage");
|
||||
}
|
||||
|
||||
void
|
||||
error(char *f, char *a)
|
||||
{
|
||||
char err[Errlen+1];
|
||||
char buf[256];
|
||||
|
||||
rerrstr(err, sizeof(err));
|
||||
snprint(buf, sizeof(buf), f, a);
|
||||
fprint(2, "qer: %s: %s\n", buf, err);
|
||||
exits(buf);
|
||||
}
|
||||
|
||||
void
|
||||
main(int argc, char**argv)
|
||||
{
|
||||
Dir *dir;
|
||||
String *f, *c;
|
||||
int fd;
|
||||
char file[1024];
|
||||
char buf[1024];
|
||||
long n;
|
||||
char *cp, *qdir;
|
||||
int i;
|
||||
Qfile *q, **l;
|
||||
|
||||
l = &files;
|
||||
qdir = 0;
|
||||
|
||||
ARGBEGIN {
|
||||
case 'f':
|
||||
q = malloc(sizeof(Qfile));
|
||||
q->name = ARGF();
|
||||
q->next = *l;
|
||||
*l = q;
|
||||
break;
|
||||
case 'q':
|
||||
qdir = ARGF();
|
||||
if(qdir == 0)
|
||||
usage();
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
} ARGEND;
|
||||
|
||||
if(argc < 3)
|
||||
usage();
|
||||
user = getuser();
|
||||
isnone = (qdir != 0) || (strcmp(user, "none") == 0);
|
||||
|
||||
if(qdir == 0) {
|
||||
qdir = user;
|
||||
if(qdir == 0)
|
||||
error("unknown user", 0);
|
||||
}
|
||||
snprint(file, sizeof(file), "%s/%s", argv[0], qdir);
|
||||
|
||||
/*
|
||||
* data file name
|
||||
*/
|
||||
f = s_copy(file);
|
||||
s_append(f, "/D.XXXXXX");
|
||||
mktemp(s_to_c(f));
|
||||
cp = utfrrune(s_to_c(f), '/');
|
||||
cp++;
|
||||
|
||||
/*
|
||||
* create directory and data file. once the data file
|
||||
* exists, runq won't remove the directory
|
||||
*/
|
||||
fd = -1;
|
||||
for(i = 0; i < 10; i++){
|
||||
int perm;
|
||||
|
||||
dir = dirstat(file);
|
||||
if(dir == nil){
|
||||
perm = isnone?0777:0775;
|
||||
if(sysmkdir(file, perm) < 0)
|
||||
continue;
|
||||
} else {
|
||||
if((dir->qid.type&QTDIR)==0)
|
||||
error("not a directory %s", file);
|
||||
}
|
||||
perm = isnone?0664:0660;
|
||||
fd = create(s_to_c(f), OWRITE, perm);
|
||||
if(fd >= 0)
|
||||
break;
|
||||
sleep(250);
|
||||
}
|
||||
if(fd < 0)
|
||||
error("creating data file %s", s_to_c(f));
|
||||
|
||||
/*
|
||||
* copy over associated files
|
||||
*/
|
||||
if(files){
|
||||
*cp = 'F';
|
||||
for(q = files; q; q = q->next){
|
||||
q->tname = strdup(s_to_c(f));
|
||||
if(copy(q) < 0)
|
||||
error("copying %s to queue", q->name);
|
||||
(*cp)++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* copy in the data file
|
||||
*/
|
||||
i = 0;
|
||||
while((n = read(0, buf, sizeof(buf)-1)) > 0){
|
||||
if(i++ == 0 && strncmp(buf, "From", 4) != 0){
|
||||
buf[n] = 0;
|
||||
syslog(0, "smtp", "qer usys data starts with %-40.40s\n", buf);
|
||||
}
|
||||
if(write(fd, buf, n) != n)
|
||||
error("writing data file %s", s_to_c(f));
|
||||
}
|
||||
/* if(n < 0)
|
||||
error("reading input"); */
|
||||
close(fd);
|
||||
|
||||
/*
|
||||
* create control file
|
||||
*/
|
||||
*cp = 'C';
|
||||
fd = syscreatelocked(s_to_c(f), OWRITE, 0664);
|
||||
if(fd < 0)
|
||||
error("creating control file %s", s_to_c(f));
|
||||
c = s_new();
|
||||
for(i = 1; i < argc; i++){
|
||||
s_append(c, argv[i]);
|
||||
s_append(c, " ");
|
||||
}
|
||||
for(q = files; q; q = q->next){
|
||||
s_append(c, q->tname);
|
||||
s_append(c, " ");
|
||||
}
|
||||
s_append(c, "\n");
|
||||
if(write(fd, s_to_c(c), strlen(s_to_c(c))) < 0) {
|
||||
sysunlockfile(fd);
|
||||
error("writing control file %s", s_to_c(f));
|
||||
}
|
||||
sysunlockfile(fd);
|
||||
exits(0);
|
||||
}
|
||||
|
||||
int
|
||||
copy(Qfile *q)
|
||||
{
|
||||
int from, to, n;
|
||||
char buf[4096];
|
||||
|
||||
from = open(q->name, OREAD);
|
||||
if(from < 0)
|
||||
return -1;
|
||||
to = create(q->tname, OWRITE, 0660);
|
||||
if(to < 0){
|
||||
close(from);
|
||||
return -1;
|
||||
}
|
||||
for(;;){
|
||||
n = read(from, buf, sizeof(buf));
|
||||
if(n <= 0)
|
||||
break;
|
||||
n = write(to, buf, n);
|
||||
if(n < 0)
|
||||
break;
|
||||
}
|
||||
close(to);
|
||||
close(from);
|
||||
return n;
|
||||
}
|
||||
766
src/cmd/upas/q/runq.c
Normal file
766
src/cmd/upas/q/runq.c
Normal file
@@ -0,0 +1,766 @@
|
||||
#include "common.h"
|
||||
#include <ctype.h>
|
||||
|
||||
void doalldirs(void);
|
||||
void dodir(char*);
|
||||
void dofile(Dir*);
|
||||
void rundir(char*);
|
||||
char* file(char*, char);
|
||||
void warning(char*, void*);
|
||||
void error(char*, void*);
|
||||
int returnmail(char**, char*, char*);
|
||||
void logit(char*, char*, char**);
|
||||
void doload(int);
|
||||
|
||||
#define HUNK 32
|
||||
char *cmd;
|
||||
char *root;
|
||||
int debug;
|
||||
int giveup = 2*24*60*60;
|
||||
int load;
|
||||
int limit;
|
||||
|
||||
/* the current directory */
|
||||
Dir *dirbuf;
|
||||
long ndirbuf = 0;
|
||||
int nfiles;
|
||||
char *curdir;
|
||||
|
||||
char *runqlog = "runq";
|
||||
|
||||
int *pidlist;
|
||||
char **badsys; /* array of recalcitrant systems */
|
||||
int nbad;
|
||||
int npid = 50;
|
||||
int sflag; /* single thread per directory */
|
||||
int aflag; /* all directories */
|
||||
int Eflag; /* ignore E.xxxxxx dates */
|
||||
int Rflag; /* no giving up, ever */
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "usage: runq [-adsE] [-q dir] [-l load] [-t time] [-r nfiles] [-n nprocs] q-root cmd\n");
|
||||
exits("");
|
||||
}
|
||||
|
||||
void
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
char *qdir, *x;
|
||||
|
||||
qdir = 0;
|
||||
|
||||
ARGBEGIN{
|
||||
case 'l':
|
||||
x = ARGF();
|
||||
if(x == 0)
|
||||
usage();
|
||||
load = atoi(x);
|
||||
if(load < 0)
|
||||
load = 0;
|
||||
break;
|
||||
case 'E':
|
||||
Eflag++;
|
||||
break;
|
||||
case 'R': /* no giving up -- just leave stuff in the queue */
|
||||
Rflag++;
|
||||
break;
|
||||
case 'a':
|
||||
aflag++;
|
||||
break;
|
||||
case 'd':
|
||||
debug++;
|
||||
break;
|
||||
case 'r':
|
||||
limit = atoi(ARGF());
|
||||
break;
|
||||
case 's':
|
||||
sflag++;
|
||||
break;
|
||||
case 't':
|
||||
giveup = 60*60*atoi(ARGF());
|
||||
break;
|
||||
case 'q':
|
||||
qdir = ARGF();
|
||||
if(qdir == 0)
|
||||
usage();
|
||||
break;
|
||||
case 'n':
|
||||
npid = atoi(ARGF());
|
||||
if(npid == 0)
|
||||
usage();
|
||||
break;
|
||||
}ARGEND;
|
||||
|
||||
if(argc != 2)
|
||||
usage();
|
||||
|
||||
pidlist = malloc(npid*sizeof(*pidlist));
|
||||
if(pidlist == 0)
|
||||
error("can't malloc", 0);
|
||||
|
||||
if(aflag == 0 && qdir == 0) {
|
||||
qdir = getuser();
|
||||
if(qdir == 0)
|
||||
error("unknown user", 0);
|
||||
}
|
||||
root = argv[0];
|
||||
cmd = argv[1];
|
||||
|
||||
if(chdir(root) < 0)
|
||||
error("can't cd to %s", root);
|
||||
|
||||
doload(1);
|
||||
if(aflag)
|
||||
doalldirs();
|
||||
else
|
||||
dodir(qdir);
|
||||
doload(0);
|
||||
exits(0);
|
||||
}
|
||||
|
||||
int
|
||||
emptydir(char *name)
|
||||
{
|
||||
int fd;
|
||||
long n;
|
||||
char buf[2048];
|
||||
|
||||
fd = open(name, OREAD);
|
||||
if(fd < 0)
|
||||
return 1;
|
||||
n = read(fd, buf, sizeof(buf));
|
||||
close(fd);
|
||||
if(n <= 0) {
|
||||
if(debug)
|
||||
fprint(2, "removing directory %s\n", name);
|
||||
syslog(0, runqlog, "rmdir %s", name);
|
||||
sysremove(name);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
forkltd(void)
|
||||
{
|
||||
int i;
|
||||
int pid;
|
||||
|
||||
for(i = 0; i < npid; i++){
|
||||
if(pidlist[i] <= 0)
|
||||
break;
|
||||
}
|
||||
|
||||
while(i >= npid){
|
||||
pid = waitpid();
|
||||
if(pid < 0){
|
||||
syslog(0, runqlog, "forkltd confused");
|
||||
exits(0);
|
||||
}
|
||||
|
||||
for(i = 0; i < npid; i++)
|
||||
if(pidlist[i] == pid)
|
||||
break;
|
||||
}
|
||||
pidlist[i] = fork();
|
||||
return pidlist[i];
|
||||
}
|
||||
|
||||
/*
|
||||
* run all user directories, must be bootes (or root on unix) to do this
|
||||
*/
|
||||
void
|
||||
doalldirs(void)
|
||||
{
|
||||
Dir *db;
|
||||
int fd;
|
||||
long i, n;
|
||||
|
||||
|
||||
fd = open(".", OREAD);
|
||||
if(fd == -1){
|
||||
warning("reading %s", root);
|
||||
return;
|
||||
}
|
||||
n = sysdirreadall(fd, &db);
|
||||
if(n > 0){
|
||||
for(i=0; i<n; i++){
|
||||
if(db[i].qid.type & QTDIR){
|
||||
if(emptydir(db[i].name))
|
||||
continue;
|
||||
switch(forkltd()){
|
||||
case -1:
|
||||
syslog(0, runqlog, "out of procs");
|
||||
doload(0);
|
||||
exits(0);
|
||||
case 0:
|
||||
if(sysdetach() < 0)
|
||||
error("%r", 0);
|
||||
dodir(db[i].name);
|
||||
exits(0);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
free(db);
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/*
|
||||
* cd to a user directory and run it
|
||||
*/
|
||||
void
|
||||
dodir(char *name)
|
||||
{
|
||||
curdir = name;
|
||||
|
||||
if(chdir(name) < 0){
|
||||
warning("cd to %s", name);
|
||||
return;
|
||||
}
|
||||
if(debug)
|
||||
fprint(2, "running %s\n", name);
|
||||
rundir(name);
|
||||
chdir("..");
|
||||
}
|
||||
|
||||
/*
|
||||
* run the current directory
|
||||
*/
|
||||
void
|
||||
rundir(char *name)
|
||||
{
|
||||
int fd;
|
||||
long i;
|
||||
|
||||
if(aflag && sflag)
|
||||
fd = sysopenlocked(".", OREAD);
|
||||
else
|
||||
fd = open(".", OREAD);
|
||||
if(fd == -1){
|
||||
warning("reading %s", name);
|
||||
return;
|
||||
}
|
||||
nfiles = sysdirreadall(fd, &dirbuf);
|
||||
if(nfiles > 0){
|
||||
for(i=0; i<nfiles; i++){
|
||||
if(dirbuf[i].name[0]!='C' || dirbuf[i].name[1]!='.')
|
||||
continue;
|
||||
dofile(&dirbuf[i]);
|
||||
}
|
||||
free(dirbuf);
|
||||
}
|
||||
if(aflag && sflag)
|
||||
sysunlockfile(fd);
|
||||
else
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/*
|
||||
* free files matching name in the current directory
|
||||
*/
|
||||
void
|
||||
remmatch(char *name)
|
||||
{
|
||||
long i;
|
||||
|
||||
syslog(0, runqlog, "removing %s/%s", curdir, name);
|
||||
|
||||
for(i=0; i<nfiles; i++){
|
||||
if(strcmp(&dirbuf[i].name[1], &name[1]) == 0)
|
||||
sysremove(dirbuf[i].name);
|
||||
}
|
||||
|
||||
/* error file (may have) appeared after we read the directory */
|
||||
/* stomp on data file in case of phase error */
|
||||
sysremove(file(name, 'D'));
|
||||
sysremove(file(name, 'E'));
|
||||
}
|
||||
|
||||
/*
|
||||
* like trylock, but we've already got the lock on fd,
|
||||
* and don't want an L. lock file.
|
||||
*/
|
||||
static Mlock *
|
||||
keeplockalive(char *path, int fd)
|
||||
{
|
||||
char buf[1];
|
||||
Mlock *l;
|
||||
|
||||
l = malloc(sizeof(Mlock));
|
||||
if(l == 0)
|
||||
return 0;
|
||||
l->fd = fd;
|
||||
l->name = s_new();
|
||||
s_append(l->name, path);
|
||||
|
||||
/* fork process to keep lock alive until sysunlock(l) */
|
||||
switch(l->pid = rfork(RFPROC)){
|
||||
default:
|
||||
break;
|
||||
case 0:
|
||||
fd = l->fd;
|
||||
for(;;){
|
||||
sleep(1000*60);
|
||||
if(pread(fd, buf, 1, 0) < 0)
|
||||
break;
|
||||
}
|
||||
_exits(0);
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
/*
|
||||
* try a message
|
||||
*/
|
||||
void
|
||||
dofile(Dir *dp)
|
||||
{
|
||||
Dir *d;
|
||||
int dfd, ac, dtime, efd, pid, i, etime;
|
||||
char *buf, *cp, **av;
|
||||
Waitmsg *wm;
|
||||
Biobuf *b;
|
||||
Mlock *l = nil;
|
||||
|
||||
if(debug)
|
||||
fprint(2, "dofile %s\n", dp->name);
|
||||
/*
|
||||
* if no data file or empty control or data file, just clean up
|
||||
* the empty control file must be 15 minutes old, to minimize the
|
||||
* chance of a race.
|
||||
*/
|
||||
d = dirstat(file(dp->name, 'D'));
|
||||
if(d == nil){
|
||||
syslog(0, runqlog, "no data file for %s", dp->name);
|
||||
remmatch(dp->name);
|
||||
return;
|
||||
}
|
||||
if(dp->length == 0){
|
||||
if(time(0)-dp->mtime > 15*60){
|
||||
syslog(0, runqlog, "empty ctl file for %s", dp->name);
|
||||
remmatch(dp->name);
|
||||
}
|
||||
return;
|
||||
}
|
||||
dtime = d->mtime;
|
||||
free(d);
|
||||
|
||||
/*
|
||||
* retry times depend on the age of the errors file
|
||||
*/
|
||||
if(!Eflag && (d = dirstat(file(dp->name, 'E'))) != nil){
|
||||
etime = d->mtime;
|
||||
free(d);
|
||||
if(etime - dtime < 60*60){
|
||||
/* up to the first hour, try every 15 minutes */
|
||||
if(time(0) - etime < 15*60)
|
||||
return;
|
||||
} else {
|
||||
/* after the first hour, try once an hour */
|
||||
if(time(0) - etime < 60*60)
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* open control and data
|
||||
*/
|
||||
b = sysopen(file(dp->name, 'C'), "rl", 0660);
|
||||
if(b == 0) {
|
||||
if(debug)
|
||||
fprint(2, "can't open %s: %r\n", file(dp->name, 'C'));
|
||||
return;
|
||||
}
|
||||
dfd = open(file(dp->name, 'D'), OREAD);
|
||||
if(dfd < 0){
|
||||
if(debug)
|
||||
fprint(2, "can't open %s: %r\n", file(dp->name, 'D'));
|
||||
Bterm(b);
|
||||
sysunlockfile(Bfildes(b));
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* make arg list
|
||||
* - read args into (malloc'd) buffer
|
||||
* - malloc a vector and copy pointers to args into it
|
||||
*/
|
||||
buf = malloc(dp->length+1);
|
||||
if(buf == 0){
|
||||
warning("buffer allocation", 0);
|
||||
Bterm(b);
|
||||
sysunlockfile(Bfildes(b));
|
||||
close(dfd);
|
||||
return;
|
||||
}
|
||||
if(Bread(b, buf, dp->length) != dp->length){
|
||||
warning("reading control file %s\n", dp->name);
|
||||
Bterm(b);
|
||||
sysunlockfile(Bfildes(b));
|
||||
close(dfd);
|
||||
free(buf);
|
||||
return;
|
||||
}
|
||||
buf[dp->length] = 0;
|
||||
av = malloc(2*sizeof(char*));
|
||||
if(av == 0){
|
||||
warning("argv allocation", 0);
|
||||
close(dfd);
|
||||
free(buf);
|
||||
Bterm(b);
|
||||
sysunlockfile(Bfildes(b));
|
||||
return;
|
||||
}
|
||||
for(ac = 1, cp = buf; *cp; ac++){
|
||||
while(isspace(*cp))
|
||||
*cp++ = 0;
|
||||
if(*cp == 0)
|
||||
break;
|
||||
|
||||
av = realloc(av, (ac+2)*sizeof(char*));
|
||||
if(av == 0){
|
||||
warning("argv allocation", 0);
|
||||
close(dfd);
|
||||
free(buf);
|
||||
Bterm(b);
|
||||
sysunlockfile(Bfildes(b));
|
||||
return;
|
||||
}
|
||||
av[ac] = cp;
|
||||
while(*cp && !isspace(*cp)){
|
||||
if(*cp++ == '"'){
|
||||
while(*cp && *cp != '"')
|
||||
cp++;
|
||||
if(*cp)
|
||||
cp++;
|
||||
}
|
||||
}
|
||||
}
|
||||
av[0] = cmd;
|
||||
av[ac] = 0;
|
||||
|
||||
if(!Eflag &&time(0) - dtime > giveup){
|
||||
if(returnmail(av, dp->name, "Giveup") != 0)
|
||||
logit("returnmail failed", dp->name, av);
|
||||
remmatch(dp->name);
|
||||
goto done;
|
||||
}
|
||||
|
||||
for(i = 0; i < nbad; i++){
|
||||
if(strcmp(av[3], badsys[i]) == 0)
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ken's fs, for example, gives us 5 minutes of inactivity before
|
||||
* the lock goes stale, so we have to keep reading it.
|
||||
*/
|
||||
l = keeplockalive(file(dp->name, 'C'), Bfildes(b));
|
||||
|
||||
/*
|
||||
* transfer
|
||||
*/
|
||||
pid = fork();
|
||||
switch(pid){
|
||||
case -1:
|
||||
sysunlock(l);
|
||||
sysunlockfile(Bfildes(b));
|
||||
syslog(0, runqlog, "out of procs");
|
||||
exits(0);
|
||||
case 0:
|
||||
if(debug) {
|
||||
fprint(2, "Starting %s", cmd);
|
||||
for(ac = 0; av[ac]; ac++)
|
||||
fprint(2, " %s", av[ac]);
|
||||
fprint(2, "\n");
|
||||
}
|
||||
logit("execing", dp->name, av);
|
||||
close(0);
|
||||
dup(dfd, 0);
|
||||
close(dfd);
|
||||
close(2);
|
||||
efd = open(file(dp->name, 'E'), OWRITE);
|
||||
if(efd < 0){
|
||||
if(debug) syslog(0, "runq", "open %s as %s: %r", file(dp->name,'E'), getuser());
|
||||
efd = create(file(dp->name, 'E'), OWRITE, 0666);
|
||||
if(efd < 0){
|
||||
if(debug) syslog(0, "runq", "create %s as %s: %r", file(dp->name, 'E'), getuser());
|
||||
exits("could not open error file - Retry");
|
||||
}
|
||||
}
|
||||
seek(efd, 0, 2);
|
||||
exec(cmd, av);
|
||||
error("can't exec %s", cmd);
|
||||
break;
|
||||
default:
|
||||
for(;;){
|
||||
wm = wait();
|
||||
if(wm == nil)
|
||||
error("wait failed: %r", "");
|
||||
if(wm->pid == pid)
|
||||
break;
|
||||
free(wm);
|
||||
}
|
||||
if(debug)
|
||||
fprint(2, "wm->pid %d wm->msg == %s\n", wm->pid, wm->msg);
|
||||
|
||||
if(wm->msg[0]){
|
||||
if(debug)
|
||||
fprint(2, "[%d] wm->msg == %s\n", getpid(), wm->msg);
|
||||
if(!Rflag && strstr(wm->msg, "Retry")==0){
|
||||
/* return the message and remove it */
|
||||
if(returnmail(av, dp->name, wm->msg) != 0)
|
||||
logit("returnmail failed", dp->name, av);
|
||||
remmatch(dp->name);
|
||||
} else {
|
||||
/* add sys to bad list and try again later */
|
||||
nbad++;
|
||||
badsys = realloc(badsys, nbad*sizeof(char*));
|
||||
badsys[nbad-1] = strdup(av[3]);
|
||||
}
|
||||
} else {
|
||||
/* it worked remove the message */
|
||||
remmatch(dp->name);
|
||||
}
|
||||
free(wm);
|
||||
|
||||
}
|
||||
done:
|
||||
if (l)
|
||||
sysunlock(l);
|
||||
Bterm(b);
|
||||
sysunlockfile(Bfildes(b));
|
||||
free(buf);
|
||||
free(av);
|
||||
close(dfd);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* return a name starting with the given character
|
||||
*/
|
||||
char*
|
||||
file(char *name, char type)
|
||||
{
|
||||
static char nname[Elemlen+1];
|
||||
|
||||
strncpy(nname, name, Elemlen);
|
||||
nname[Elemlen] = 0;
|
||||
nname[0] = type;
|
||||
return nname;
|
||||
}
|
||||
|
||||
/*
|
||||
* send back the mail with an error message
|
||||
*
|
||||
* return 0 if successful
|
||||
*/
|
||||
int
|
||||
returnmail(char **av, char *name, char *msg)
|
||||
{
|
||||
int pfd[2];
|
||||
Waitmsg *wm;
|
||||
int fd;
|
||||
char buf[256];
|
||||
char attachment[256];
|
||||
int i;
|
||||
long n;
|
||||
String *s;
|
||||
char *sender;
|
||||
|
||||
if(av[1] == 0 || av[2] == 0){
|
||||
logit("runq - dumping bad file", name, av);
|
||||
return 0;
|
||||
}
|
||||
|
||||
s = unescapespecial(s_copy(av[2]));
|
||||
sender = s_to_c(s);
|
||||
|
||||
if(!returnable(sender) || strcmp(sender, "postmaster") == 0) {
|
||||
logit("runq - dumping p to p mail", name, av);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(pipe(pfd) < 0){
|
||||
logit("runq - pipe failed", name, av);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch(rfork(RFFDG|RFPROC|RFENVG)){
|
||||
case -1:
|
||||
logit("runq - fork failed", name, av);
|
||||
return -1;
|
||||
case 0:
|
||||
logit("returning", name, av);
|
||||
close(pfd[1]);
|
||||
close(0);
|
||||
dup(pfd[0], 0);
|
||||
close(pfd[0]);
|
||||
putenv("upasname", "/dev/null");
|
||||
snprint(buf, sizeof(buf), "%s/marshal", UPASBIN);
|
||||
snprint(attachment, sizeof(attachment), "%s", file(name, 'D'));
|
||||
execl(buf, "send", "-A", attachment, "-s", "permanent failure", sender, nil);
|
||||
error("can't exec", 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
close(pfd[0]);
|
||||
fprint(pfd[1], "\n"); /* get out of headers */
|
||||
if(av[1]){
|
||||
fprint(pfd[1], "Your request ``%.20s ", av[1]);
|
||||
for(n = 3; av[n]; n++)
|
||||
fprint(pfd[1], "%s ", av[n]);
|
||||
}
|
||||
fprint(pfd[1], "'' failed (code %s).\nThe symptom was:\n\n", msg);
|
||||
fd = open(file(name, 'E'), OREAD);
|
||||
if(fd >= 0){
|
||||
for(;;){
|
||||
n = read(fd, buf, sizeof(buf));
|
||||
if(n <= 0)
|
||||
break;
|
||||
if(write(pfd[1], buf, n) != n){
|
||||
close(fd);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
close(pfd[1]);
|
||||
out:
|
||||
wm = wait();
|
||||
if(wm == nil){
|
||||
syslog(0, "runq", "wait: %r");
|
||||
logit("wait failed", name, av);
|
||||
return -1;
|
||||
}
|
||||
i = 0;
|
||||
if(wm->msg[0]){
|
||||
i = -1;
|
||||
syslog(0, "runq", "returnmail child: %s", wm->msg);
|
||||
logit("returnmail child failed", name, av);
|
||||
}
|
||||
free(wm);
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
* print a warning and continue
|
||||
*/
|
||||
void
|
||||
warning(char *f, void *a)
|
||||
{
|
||||
char err[65];
|
||||
char buf[256];
|
||||
|
||||
rerrstr(err, sizeof(err));
|
||||
snprint(buf, sizeof(buf), f, a);
|
||||
fprint(2, "runq: %s: %s\n", buf, err);
|
||||
}
|
||||
|
||||
/*
|
||||
* print an error and die
|
||||
*/
|
||||
void
|
||||
error(char *f, void *a)
|
||||
{
|
||||
char err[Errlen];
|
||||
char buf[256];
|
||||
|
||||
rerrstr(err, sizeof(err));
|
||||
snprint(buf, sizeof(buf), f, a);
|
||||
fprint(2, "runq: %s: %s\n", buf, err);
|
||||
exits(buf);
|
||||
}
|
||||
|
||||
void
|
||||
logit(char *msg, char *file, char **av)
|
||||
{
|
||||
int n, m;
|
||||
char buf[256];
|
||||
|
||||
n = snprint(buf, sizeof(buf), "%s/%s: %s", curdir, file, msg);
|
||||
for(; *av; av++){
|
||||
m = strlen(*av);
|
||||
if(n + m + 4 > sizeof(buf))
|
||||
break;
|
||||
sprint(buf + n, " '%s'", *av);
|
||||
n += m + 3;
|
||||
}
|
||||
syslog(0, runqlog, "%s", buf);
|
||||
}
|
||||
|
||||
char *loadfile = ".runqload";
|
||||
|
||||
/*
|
||||
* load balancing
|
||||
*/
|
||||
void
|
||||
doload(int start)
|
||||
{
|
||||
int fd;
|
||||
char buf[32];
|
||||
int i, n;
|
||||
Mlock *l;
|
||||
Dir *d;
|
||||
|
||||
if(load <= 0)
|
||||
return;
|
||||
|
||||
if(chdir(root) < 0){
|
||||
load = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
l = syslock(loadfile);
|
||||
fd = open(loadfile, ORDWR);
|
||||
if(fd < 0){
|
||||
fd = create(loadfile, 0666, ORDWR);
|
||||
if(fd < 0){
|
||||
load = 0;
|
||||
sysunlock(l);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* get current load */
|
||||
i = 0;
|
||||
n = read(fd, buf, sizeof(buf)-1);
|
||||
if(n >= 0){
|
||||
buf[n] = 0;
|
||||
i = atoi(buf);
|
||||
}
|
||||
if(i < 0)
|
||||
i = 0;
|
||||
|
||||
/* ignore load if file hasn't been changed in 30 minutes */
|
||||
d = dirfstat(fd);
|
||||
if(d != nil){
|
||||
if(d->mtime + 30*60 < time(0))
|
||||
i = 0;
|
||||
free(d);
|
||||
}
|
||||
|
||||
/* if load already too high, give up */
|
||||
if(start && i >= load){
|
||||
sysunlock(l);
|
||||
exits(0);
|
||||
}
|
||||
|
||||
/* increment/decrement load */
|
||||
if(start)
|
||||
i++;
|
||||
else
|
||||
i--;
|
||||
seek(fd, 0, 0);
|
||||
fprint(fd, "%d\n", i);
|
||||
sysunlock(l);
|
||||
close(fd);
|
||||
}
|
||||
667
src/cmd/upas/scanmail/common.c
Normal file
667
src/cmd/upas/scanmail/common.c
Normal file
@@ -0,0 +1,667 @@
|
||||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <bio.h>
|
||||
#include <regexp.h>
|
||||
#include "spam.h"
|
||||
|
||||
enum {
|
||||
Quanta = 8192,
|
||||
Minbody = 6000,
|
||||
HdrMax = 15,
|
||||
};
|
||||
|
||||
typedef struct keyword Keyword;
|
||||
typedef struct word Word;
|
||||
|
||||
struct word{
|
||||
char *string;
|
||||
int n;
|
||||
};
|
||||
|
||||
struct keyword{
|
||||
char *string;
|
||||
int value;
|
||||
};
|
||||
|
||||
Word htmlcmds[] =
|
||||
{
|
||||
"html", 4,
|
||||
"!doctype html", 13,
|
||||
0,
|
||||
|
||||
};
|
||||
|
||||
Word hrefs[] =
|
||||
{
|
||||
"a href=", 7,
|
||||
"a title=", 8,
|
||||
"a target=", 9,
|
||||
"base href=", 10,
|
||||
"img src=", 8,
|
||||
"img border=", 11,
|
||||
"form action=", 12,
|
||||
"!--", 3,
|
||||
0,
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* RFC822 header keywords to look for for fractured header.
|
||||
* all lengths must be less than HdrMax defined above.
|
||||
*/
|
||||
Word hdrwords[] =
|
||||
{
|
||||
"cc:", 3,
|
||||
"bcc:", 4,
|
||||
"to:", 3,
|
||||
0, 0,
|
||||
|
||||
};
|
||||
|
||||
Keyword keywords[] =
|
||||
{
|
||||
"header", HoldHeader,
|
||||
"line", SaveLine,
|
||||
"hold", Hold,
|
||||
"dump", Dump,
|
||||
"loff", Lineoff,
|
||||
0, Nactions,
|
||||
};
|
||||
|
||||
Patterns patterns[] = {
|
||||
[Dump] { "DUMP:", 0, 0 },
|
||||
[HoldHeader] { "HEADER:", 0, 0 },
|
||||
[Hold] { "HOLD:", 0, 0 },
|
||||
[SaveLine] { "LINE:", 0, 0 },
|
||||
[Lineoff] { "LINEOFF:", 0, 0 },
|
||||
[Nactions] { 0, 0, 0 },
|
||||
};
|
||||
|
||||
static char* endofhdr(char*, char*);
|
||||
static int escape(char**);
|
||||
static int extract(char*);
|
||||
static int findkey(char*);
|
||||
static int hash(int);
|
||||
static int isword(Word*, char*, int);
|
||||
static void parsealt(Biobuf*, char*, Spat**);
|
||||
|
||||
/*
|
||||
* The canonicalizer: convert input to canonical representation
|
||||
*/
|
||||
char*
|
||||
readmsg(Biobuf *bp, int *hsize, int *bufsize)
|
||||
{
|
||||
char *p, *buf;
|
||||
int n, offset, eoh, bsize, delta;
|
||||
|
||||
buf = 0;
|
||||
offset = 0;
|
||||
if(bufsize)
|
||||
*bufsize = 0;
|
||||
if(hsize)
|
||||
*hsize = 0;
|
||||
for(;;) {
|
||||
buf = Realloc(buf, offset+Quanta+1);
|
||||
n = Bread(bp, buf+offset, Quanta);
|
||||
if(n < 0){
|
||||
free(buf);
|
||||
return 0;
|
||||
}
|
||||
p = buf+offset; /* start of this chunk */
|
||||
offset += n; /* end of this chunk */
|
||||
buf[offset] = 0;
|
||||
if(n == 0){
|
||||
if(offset == 0)
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if(hsize == 0) /* don't process header */
|
||||
break;
|
||||
if(p != buf && p[-1] == '\n') /* check for EOH across buffer split */
|
||||
p--;
|
||||
p = endofhdr(p, buf+offset);
|
||||
if(p)
|
||||
break;
|
||||
if(offset >= Maxread) /* gargantuan header - just punt*/
|
||||
{
|
||||
if(hsize)
|
||||
*hsize = offset;
|
||||
if(bufsize)
|
||||
*bufsize = offset;
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
eoh = p-buf; /* End of header */
|
||||
bsize = offset - eoh; /* amount of body already read */
|
||||
|
||||
/* Read at least Minbody bytes of the body */
|
||||
if (bsize < Minbody){
|
||||
delta = Minbody-bsize;
|
||||
buf = Realloc(buf, offset+delta+1);
|
||||
n = Bread(bp, buf+offset, delta);
|
||||
if(n > 0) {
|
||||
offset += n;
|
||||
buf[offset] = 0;
|
||||
}
|
||||
}
|
||||
if(hsize)
|
||||
*hsize = eoh;
|
||||
if(bufsize)
|
||||
*bufsize = offset;
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int
|
||||
isword(Word *wp, char *text, int len)
|
||||
{
|
||||
for(;wp->string; wp++)
|
||||
if(len >= wp->n && strncmp(text, wp->string, wp->n) == 0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char*
|
||||
endofhdr(char *raw, char *end)
|
||||
{
|
||||
int i;
|
||||
char *p, *q;
|
||||
char buf[HdrMax];
|
||||
|
||||
/*
|
||||
* can't use strchr to search for newlines because
|
||||
* there may be embedded NULL's.
|
||||
*/
|
||||
for(p = raw; p < end; p++){
|
||||
if(*p != '\n' || p[1] != '\n')
|
||||
continue;
|
||||
p++;
|
||||
for(i = 0, q = p+1; i < sizeof(buf) && *q; q++){
|
||||
buf[i++] = tolower(*q);
|
||||
if(*q == ':' || *q == '\n')
|
||||
break;
|
||||
}
|
||||
if(!isword(hdrwords, buf, i))
|
||||
return p+1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
htmlmatch(Word *wp, char *text, char *end, int *n)
|
||||
{
|
||||
char *cp;
|
||||
int i, c, lastc;
|
||||
char buf[MaxHtml];
|
||||
|
||||
/*
|
||||
* extract a string up to '>'
|
||||
*/
|
||||
|
||||
i = lastc = 0;
|
||||
cp = text;
|
||||
while (cp < end && i < sizeof(buf)-1){
|
||||
c = *cp++;
|
||||
if(c == '=')
|
||||
c = escape(&cp);
|
||||
switch(c){
|
||||
case 0:
|
||||
case '\r':
|
||||
continue;
|
||||
case '>':
|
||||
goto out;
|
||||
case '\n':
|
||||
case ' ':
|
||||
case '\t':
|
||||
if(lastc == ' ')
|
||||
continue;
|
||||
c = ' ';
|
||||
break;
|
||||
default:
|
||||
c = tolower(c);
|
||||
break;
|
||||
}
|
||||
buf[i++] = lastc = c;
|
||||
}
|
||||
out:
|
||||
buf[i] = 0;
|
||||
if(n)
|
||||
*n = cp-text;
|
||||
return isword(wp, buf, i);
|
||||
}
|
||||
|
||||
static int
|
||||
escape(char **msg)
|
||||
{
|
||||
int c;
|
||||
char *p;
|
||||
|
||||
p = *msg;
|
||||
c = *p;
|
||||
if(c == '\n'){
|
||||
p++;
|
||||
c = *p++;
|
||||
} else
|
||||
if(c == '2'){
|
||||
c = tolower(p[1]);
|
||||
if(c == 'e'){
|
||||
p += 2;
|
||||
c = '.';
|
||||
}else
|
||||
if(c == 'f'){
|
||||
p += 2;
|
||||
c = '/';
|
||||
}else
|
||||
if(c == '0'){
|
||||
p += 2;
|
||||
c = ' ';
|
||||
}
|
||||
else c = '=';
|
||||
} else {
|
||||
if(c == '3' && tolower(p[1]) == 'd')
|
||||
p += 2;
|
||||
c = '=';
|
||||
}
|
||||
*msg = p;
|
||||
return c;
|
||||
}
|
||||
|
||||
static int
|
||||
htmlchk(char **msg, char *end)
|
||||
{
|
||||
int n;
|
||||
char *p;
|
||||
|
||||
static int ishtml;
|
||||
|
||||
p = *msg;
|
||||
if(ishtml == 0){
|
||||
ishtml = htmlmatch(htmlcmds, p, end, &n);
|
||||
|
||||
/* If not an HTML keyword, check if it's
|
||||
* an HTML comment (<!comment>). if so,
|
||||
* skip over it; otherwise copy it in.
|
||||
*/
|
||||
if(ishtml == 0 && *p != '!') /* not comment */
|
||||
return '<'; /* copy it */
|
||||
|
||||
} else if(htmlmatch(hrefs, p, end, &n)) /* if special HTML string */
|
||||
return '<'; /* copy it */
|
||||
|
||||
/*
|
||||
* this is an uninteresting HTML command; skip over it.
|
||||
*/
|
||||
p += n;
|
||||
*msg = p+1;
|
||||
return *p;
|
||||
}
|
||||
|
||||
/*
|
||||
* decode a base 64 encode body
|
||||
*/
|
||||
void
|
||||
conv64(char *msg, char *end, char *buf, int bufsize)
|
||||
{
|
||||
int len, i;
|
||||
char *cp;
|
||||
|
||||
len = end - msg;
|
||||
i = (len*3)/4+1; // room for max chars + null
|
||||
cp = Malloc(i);
|
||||
len = dec64((uchar*)cp, i, msg, len);
|
||||
convert(cp, cp+len, buf, bufsize, 1);
|
||||
free(cp);
|
||||
}
|
||||
|
||||
int
|
||||
convert(char *msg, char *end, char *buf, int bufsize, int isbody)
|
||||
{
|
||||
|
||||
char *p;
|
||||
int c, lastc, base64;
|
||||
|
||||
lastc = 0;
|
||||
base64 = 0;
|
||||
while(msg < end && bufsize > 0){
|
||||
c = *msg++;
|
||||
|
||||
/*
|
||||
* In the body only, try to strip most HTML and
|
||||
* replace certain MIME escape sequences with the character
|
||||
*/
|
||||
if(isbody) {
|
||||
do{
|
||||
p = msg;
|
||||
if(c == '<')
|
||||
c = htmlchk(&msg, end);
|
||||
if(c == '=')
|
||||
c = escape(&msg);
|
||||
} while(p != msg && p < end);
|
||||
}
|
||||
switch(c){
|
||||
case 0:
|
||||
case '\r':
|
||||
continue;
|
||||
case '\t':
|
||||
case ' ':
|
||||
case '\n':
|
||||
if(lastc == ' ')
|
||||
continue;
|
||||
c = ' ';
|
||||
break;
|
||||
case 'C': /* check for MIME base 64 encoding in header */
|
||||
case 'c':
|
||||
if(isbody == 0)
|
||||
if(msg < end-32 && *msg == 'o' && msg[1] == 'n')
|
||||
if(cistrncmp(msg+2, "tent-transfer-encoding: base64", 30) == 0)
|
||||
base64 = 1;
|
||||
c = 'c';
|
||||
break;
|
||||
default:
|
||||
c = tolower(c);
|
||||
break;
|
||||
}
|
||||
*buf++ = c;
|
||||
lastc = c;
|
||||
bufsize--;
|
||||
}
|
||||
*buf = 0;
|
||||
return base64;
|
||||
}
|
||||
|
||||
/*
|
||||
* The pattern parser: build data structures from the pattern file
|
||||
*/
|
||||
|
||||
static int
|
||||
hash(int c)
|
||||
{
|
||||
return c & 127;
|
||||
}
|
||||
|
||||
static int
|
||||
findkey(char *val)
|
||||
{
|
||||
Keyword *kp;
|
||||
|
||||
for(kp = keywords; kp->string; kp++)
|
||||
if(strcmp(val, kp->string) == 0)
|
||||
break;
|
||||
return kp->value;
|
||||
}
|
||||
|
||||
#define whitespace(c) ((c) == ' ' || (c) == '\t')
|
||||
|
||||
void
|
||||
parsepats(Biobuf *bp)
|
||||
{
|
||||
Pattern *p, *new;
|
||||
char *cp, *qp;
|
||||
int type, action, n, h;
|
||||
Spat *spat;
|
||||
|
||||
for(;;){
|
||||
cp = Brdline(bp, '\n');
|
||||
if(cp == 0)
|
||||
break;
|
||||
cp[Blinelen(bp)-1] = 0;
|
||||
while(*cp == ' ' || *cp == '\t')
|
||||
cp++;
|
||||
if(*cp == '#' || *cp == 0)
|
||||
continue;
|
||||
type = regexp;
|
||||
if(*cp == '*'){
|
||||
type = string;
|
||||
cp++;
|
||||
}
|
||||
qp = strchr(cp, ':');
|
||||
if(qp == 0)
|
||||
continue;
|
||||
*qp = 0;
|
||||
if(debug)
|
||||
fprint(2, "action = %s\n", cp);
|
||||
action = findkey(cp);
|
||||
if(action >= Nactions)
|
||||
continue;
|
||||
cp = qp+1;
|
||||
n = extract(cp);
|
||||
if(n <= 0 || *cp == 0)
|
||||
continue;
|
||||
|
||||
qp = strstr(cp, "~~");
|
||||
if(qp){
|
||||
*qp = 0;
|
||||
n = strlen(cp);
|
||||
}
|
||||
if(debug)
|
||||
fprint(2, " Pattern: `%s'\n", cp);
|
||||
|
||||
/* Hook regexps into a chain */
|
||||
if(type == regexp) {
|
||||
new = Malloc(sizeof(Pattern));
|
||||
new->action = action;
|
||||
new->pat = regcomp(cp);
|
||||
if(new->pat == 0){
|
||||
free(new);
|
||||
continue;
|
||||
}
|
||||
new->type = regexp;
|
||||
new->alt = 0;
|
||||
new->next = 0;
|
||||
|
||||
if(qp)
|
||||
parsealt(bp, qp+2, &new->alt);
|
||||
|
||||
new->next = patterns[action].regexps;
|
||||
patterns[action].regexps = new;
|
||||
continue;
|
||||
|
||||
}
|
||||
/* not a Regexp - hook strings into Pattern hash chain */
|
||||
spat = Malloc(sizeof(*spat));
|
||||
spat->next = 0;
|
||||
spat->alt = 0;
|
||||
spat->len = n;
|
||||
spat->string = Malloc(n+1);
|
||||
spat->c1 = cp[1];
|
||||
strcpy(spat->string, cp);
|
||||
|
||||
if(qp)
|
||||
parsealt(bp, qp+2, &spat->alt);
|
||||
|
||||
p = patterns[action].strings;
|
||||
if(p == 0) {
|
||||
p = Malloc(sizeof(Pattern));
|
||||
memset(p, 0, sizeof(*p));
|
||||
p->action = action;
|
||||
p->type = string;
|
||||
patterns[action].strings = p;
|
||||
}
|
||||
h = hash(*spat->string);
|
||||
spat->next = p->spat[h];
|
||||
p->spat[h] = spat;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
parsealt(Biobuf *bp, char *cp, Spat** head)
|
||||
{
|
||||
char *p;
|
||||
Spat *alt;
|
||||
|
||||
while(cp){
|
||||
if(*cp == 0){ /*escaped newline*/
|
||||
do{
|
||||
cp = Brdline(bp, '\n');
|
||||
if(cp == 0)
|
||||
return;
|
||||
cp[Blinelen(bp)-1] = 0;
|
||||
} while(extract(cp) <= 0 || *cp == 0);
|
||||
}
|
||||
|
||||
p = cp;
|
||||
cp = strstr(p, "~~");
|
||||
if(cp){
|
||||
*cp = 0;
|
||||
cp += 2;
|
||||
}
|
||||
if(strlen(p)){
|
||||
alt = Malloc(sizeof(*alt));
|
||||
alt->string = strdup(p);
|
||||
alt->next = *head;
|
||||
*head = alt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
extract(char *cp)
|
||||
{
|
||||
int c;
|
||||
char *p, *q, *r;
|
||||
|
||||
p = q = r = cp;
|
||||
while(whitespace(*p))
|
||||
p++;
|
||||
while(c = *p++){
|
||||
if (c == '#')
|
||||
break;
|
||||
if(c == '"'){
|
||||
while(*p && *p != '"'){
|
||||
if(*p == '\\' && p[1] == '"')
|
||||
p++;
|
||||
if('A' <= *p && *p <= 'Z')
|
||||
*q++ = *p++ + ('a'-'A');
|
||||
else
|
||||
*q++ = *p++;
|
||||
}
|
||||
if(*p)
|
||||
p++;
|
||||
r = q; /* never back up over a quoted string */
|
||||
} else {
|
||||
if('A' <= c && c <= 'Z')
|
||||
c += ('a'-'A');
|
||||
*q++ = c;
|
||||
}
|
||||
}
|
||||
while(q > r && whitespace(q[-1]))
|
||||
q--;
|
||||
*q = 0;
|
||||
return q-cp;
|
||||
}
|
||||
|
||||
/*
|
||||
* The matching engine: compare canonical input to pattern structures
|
||||
*/
|
||||
|
||||
static Spat*
|
||||
isalt(char *message, Spat *alt)
|
||||
{
|
||||
while(alt) {
|
||||
if(*cmd)
|
||||
if(message != cmd && strstr(cmd, alt->string))
|
||||
break;
|
||||
if(message != header+1 && strstr(header+1, alt->string))
|
||||
break;
|
||||
if(strstr(message, alt->string))
|
||||
break;
|
||||
alt = alt->next;
|
||||
}
|
||||
return alt;
|
||||
}
|
||||
|
||||
int
|
||||
matchpat(Pattern *p, char *message, Resub *m)
|
||||
{
|
||||
Spat *spat;
|
||||
char *s;
|
||||
int c, c1;
|
||||
|
||||
if(p->type == string){
|
||||
c1 = *message;
|
||||
for(s=message; c=c1; s++){
|
||||
c1 = s[1];
|
||||
for(spat=p->spat[hash(c)]; spat; spat=spat->next){
|
||||
if(c1 == spat->c1)
|
||||
if(memcmp(s, spat->string, spat->len) == 0)
|
||||
if(!isalt(message, spat->alt)){
|
||||
m->sp = s;
|
||||
m->ep = s + spat->len;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
m->sp = m->ep = 0;
|
||||
if(regexec(p->pat, message, m, 1) == 0)
|
||||
return 0;
|
||||
if(isalt(message, p->alt))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
xprint(int fd, char *type, Resub *m)
|
||||
{
|
||||
char *p, *q;
|
||||
int i;
|
||||
|
||||
if(m->sp == 0 || m->ep == 0)
|
||||
return;
|
||||
|
||||
/* back up approx 30 characters to whitespace */
|
||||
for(p = m->sp, i = 0; *p && i < 30; i++, p--)
|
||||
;
|
||||
while(*p && *p != ' ')
|
||||
p--;
|
||||
p++;
|
||||
|
||||
/* grab about 30 more chars beyond the end of the match */
|
||||
for(q = m->ep, i = 0; *q && i < 30; i++, q++)
|
||||
;
|
||||
while(*q && *q != ' ')
|
||||
q++;
|
||||
|
||||
fprint(fd, "%s %.*s~%.*s~%.*s\n", type, (int)(m->sp-p), p, (int)(m->ep-m->sp), m->sp, (int)(q-m->ep), m->ep);
|
||||
}
|
||||
|
||||
enum {
|
||||
INVAL= 255
|
||||
};
|
||||
|
||||
static uchar t64d[256] = {
|
||||
/*00 */ INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
|
||||
INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
|
||||
/*10*/ INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
|
||||
INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
|
||||
/*20*/ INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
|
||||
INVAL, INVAL, INVAL, 62, INVAL, INVAL, INVAL, 63,
|
||||
/*30*/ 52, 53, 54, 55, 56, 57, 58, 59,
|
||||
60, 61, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
|
||||
/*40*/ INVAL, 0, 1, 2, 3, 4, 5, 6,
|
||||
7, 8, 9, 10, 11, 12, 13, 14,
|
||||
/*50*/ 15, 16, 17, 18, 19, 20, 21, 22,
|
||||
23, 24, 25, INVAL, INVAL, INVAL, INVAL, INVAL,
|
||||
/*60*/ INVAL, 26, 27, 28, 29, 30, 31, 32,
|
||||
33, 34, 35, 36, 37, 38, 39, 40,
|
||||
/*70*/ 41, 42, 43, 44, 45, 46, 47, 48,
|
||||
49, 50, 51, INVAL, INVAL, INVAL, INVAL, INVAL,
|
||||
/*80*/ INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
|
||||
INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
|
||||
/*90*/ INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
|
||||
INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
|
||||
/*A0*/ INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
|
||||
INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
|
||||
/*B0*/ INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
|
||||
INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
|
||||
/*C0*/ INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
|
||||
INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
|
||||
/*D0*/ INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
|
||||
INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
|
||||
/*E0*/ INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
|
||||
INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
|
||||
/*F0*/ INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
|
||||
INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
|
||||
};
|
||||
24
src/cmd/upas/scanmail/mkfile
Normal file
24
src/cmd/upas/scanmail/mkfile
Normal file
@@ -0,0 +1,24 @@
|
||||
</$objtype/mkfile
|
||||
|
||||
TARG=scanmail\
|
||||
testscan
|
||||
|
||||
OFILES= common.$O
|
||||
|
||||
HFILES= spam.h\
|
||||
../common/sys.h\
|
||||
|
||||
LIB= ../common/libcommon.a$O\
|
||||
|
||||
BIN=/$objtype/bin/upas
|
||||
UPDATE=\
|
||||
mkfile\
|
||||
$HFILES\
|
||||
${OFILES:%.$O=%.c}\
|
||||
${TARG:%=%.c}\
|
||||
|
||||
</sys/src/cmd/mkmany
|
||||
CFLAGS=$CFLAGS -I../common
|
||||
|
||||
scanmail.$O: scanmail.c
|
||||
$CC $CFLAGS -D'SPOOL="/mail"' scanmail.c
|
||||
476
src/cmd/upas/scanmail/scanmail.c
Normal file
476
src/cmd/upas/scanmail/scanmail.c
Normal file
@@ -0,0 +1,476 @@
|
||||
#include "common.h"
|
||||
#include "spam.h"
|
||||
|
||||
int cflag;
|
||||
int debug;
|
||||
int hflag;
|
||||
int nflag;
|
||||
int sflag;
|
||||
int tflag;
|
||||
int vflag;
|
||||
Biobuf bin, bout, *cout;
|
||||
|
||||
/* file names */
|
||||
char patfile[128];
|
||||
char linefile[128];
|
||||
char holdqueue[128];
|
||||
char copydir[128];
|
||||
|
||||
char header[Hdrsize+2];
|
||||
char cmd[1024];
|
||||
char **qname;
|
||||
char **qdir;
|
||||
char *sender;
|
||||
String *recips;
|
||||
|
||||
char* canon(Biobuf*, char*, char*, int*);
|
||||
int matcher(char*, Pattern*, char*, Resub*);
|
||||
int matchaction(int, char*, Resub*);
|
||||
Biobuf *opencopy(char*);
|
||||
Biobuf *opendump(char*);
|
||||
char *qmail(char**, char*, int, Biobuf*);
|
||||
void saveline(char*, char*, Resub*);
|
||||
int optoutofspamfilter(char*);
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "missing or bad arguments to qer\n");
|
||||
exits("usage");
|
||||
}
|
||||
|
||||
void
|
||||
regerror(char *s)
|
||||
{
|
||||
fprint(2, "scanmail: %s\n", s);
|
||||
}
|
||||
|
||||
void *
|
||||
Malloc(long n)
|
||||
{
|
||||
void *p;
|
||||
|
||||
p = malloc(n);
|
||||
if(p == 0)
|
||||
exits("malloc");
|
||||
return p;
|
||||
}
|
||||
|
||||
void*
|
||||
Realloc(void *p, ulong n)
|
||||
{
|
||||
p = realloc(p, n);
|
||||
if(p == 0)
|
||||
exits("realloc");
|
||||
return p;
|
||||
}
|
||||
|
||||
void
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int i, n, nolines, optout;
|
||||
char **args, **a, *cp, *buf;
|
||||
char body[Bodysize+2];
|
||||
Resub match[1];
|
||||
Biobuf *bp;
|
||||
|
||||
optout = 1;
|
||||
a = args = Malloc((argc+1)*sizeof(char*));
|
||||
sprint(patfile, "%s/patterns", UPASLIB);
|
||||
sprint(linefile, "%s/lines", UPASLOG);
|
||||
sprint(holdqueue, "%s/queue.hold", SPOOL);
|
||||
sprint(copydir, "%s/copy", SPOOL);
|
||||
|
||||
*a++ = argv[0];
|
||||
for(argc--, argv++; argv[0] && argv[0][0] == '-'; argc--, argv++){
|
||||
switch(argv[0][1]){
|
||||
case 'c': /* save copy of message */
|
||||
cflag = 1;
|
||||
break;
|
||||
case 'd': /* debug */
|
||||
debug++;
|
||||
*a++ = argv[0];
|
||||
break;
|
||||
case 'h': /* queue held messages by sender domain */
|
||||
hflag = 1; /* -q flag must be set also */
|
||||
break;
|
||||
case 'n': /* NOHOLD mode */
|
||||
nflag = 1;
|
||||
break;
|
||||
case 'p': /* pattern file */
|
||||
if(argv[0][2] || argv[1] == 0)
|
||||
usage();
|
||||
argc--;
|
||||
argv++;
|
||||
strecpy(patfile, patfile+sizeof patfile, *argv);
|
||||
break;
|
||||
case 'q': /* queue name */
|
||||
if(argv[0][2] || argv[1] == 0)
|
||||
usage();
|
||||
*a++ = argv[0];
|
||||
argc--;
|
||||
argv++;
|
||||
qname = a;
|
||||
*a++ = argv[0];
|
||||
break;
|
||||
case 's': /* save copy of dumped message */
|
||||
sflag = 1;
|
||||
break;
|
||||
case 't': /* test mode - don't log match
|
||||
* and write message to /dev/null
|
||||
*/
|
||||
tflag = 1;
|
||||
break;
|
||||
case 'v': /* vebose - print matches */
|
||||
vflag = 1;
|
||||
break;
|
||||
default:
|
||||
*a++ = argv[0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(argc < 3)
|
||||
usage();
|
||||
|
||||
Binit(&bin, 0, OREAD);
|
||||
bp = Bopen(patfile, OREAD);
|
||||
if(bp){
|
||||
parsepats(bp);
|
||||
Bterm(bp);
|
||||
}
|
||||
qdir = a;
|
||||
sender = argv[2];
|
||||
|
||||
/* copy the rest of argv, acummulating the recipients as we go */
|
||||
for(i = 0; argv[i]; i++){
|
||||
*a++ = argv[i];
|
||||
if(i < 4) /* skip queue, 'mail', sender, dest sys */
|
||||
continue;
|
||||
/* recipients and smtp flags - skip the latter*/
|
||||
if(strcmp(argv[i], "-g") == 0){
|
||||
*a++ = argv[++i];
|
||||
continue;
|
||||
}
|
||||
if(recips)
|
||||
s_append(recips, ", ");
|
||||
else
|
||||
recips = s_new();
|
||||
s_append(recips, argv[i]);
|
||||
if(optout && !optoutofspamfilter(argv[i]))
|
||||
optout = 0;
|
||||
}
|
||||
*a = 0;
|
||||
/* construct a command string for matching */
|
||||
snprint(cmd, sizeof(cmd)-1, "%s %s", sender, s_to_c(recips));
|
||||
cmd[sizeof(cmd)-1] = 0;
|
||||
for(cp = cmd; *cp; cp++)
|
||||
*cp = tolower(*cp);
|
||||
|
||||
/* canonicalize a copy of the header and body.
|
||||
* buf points to orginal message and n contains
|
||||
* number of bytes of original message read during
|
||||
* canonicalization.
|
||||
*/
|
||||
*body = 0;
|
||||
*header = 0;
|
||||
buf = canon(&bin, header+1, body+1, &n);
|
||||
if (buf == 0)
|
||||
exits("read");
|
||||
|
||||
/* if all users opt out, don't try matches */
|
||||
if(optout){
|
||||
if(cflag)
|
||||
cout = opencopy(sender);
|
||||
exits(qmail(args, buf, n, cout));
|
||||
}
|
||||
|
||||
/* Turn off line logging, if command line matches */
|
||||
nolines = matchaction(Lineoff, cmd, match);
|
||||
|
||||
for(i = 0; patterns[i].action; i++){
|
||||
/* Lineoff patterns were already done above */
|
||||
if(i == Lineoff)
|
||||
continue;
|
||||
/* don't apply "Line" patterns if excluded above */
|
||||
if(nolines && i == SaveLine)
|
||||
continue;
|
||||
/* apply patterns to the sender/recips, header and body */
|
||||
if(matchaction(i, cmd, match))
|
||||
break;
|
||||
if(matchaction(i, header+1, match))
|
||||
break;
|
||||
if(i == HoldHeader)
|
||||
continue;
|
||||
if(matchaction(i, body+1, match))
|
||||
break;
|
||||
}
|
||||
if(cflag && patterns[i].action == 0) /* no match found - save msg */
|
||||
cout = opencopy(sender);
|
||||
|
||||
exits(qmail(args, buf, n, cout));
|
||||
}
|
||||
|
||||
char*
|
||||
qmail(char **argv, char *buf, int n, Biobuf *cout)
|
||||
{
|
||||
Waitmsg *status;
|
||||
int i, pid, pipefd[2];
|
||||
char path[512];
|
||||
Biobuf *bp;
|
||||
|
||||
pid = 0;
|
||||
if(tflag == 0){
|
||||
if(pipe(pipefd) < 0)
|
||||
exits("pipe");
|
||||
pid = fork();
|
||||
if(pid == 0){
|
||||
dup(pipefd[0], 0);
|
||||
for(i = sysfiles(); i >= 3; i--)
|
||||
close(i);
|
||||
snprint(path, sizeof(path), "%s/qer", UPASBIN);
|
||||
*argv=path;
|
||||
exec(path, argv);
|
||||
exits("exec");
|
||||
}
|
||||
Binit(&bout, pipefd[1], OWRITE);
|
||||
bp = &bout;
|
||||
} else
|
||||
bp = Bopen("/dev/null", OWRITE);
|
||||
|
||||
while(n > 0){
|
||||
Bwrite(bp, buf, n);
|
||||
if(cout)
|
||||
Bwrite(cout, buf, n);
|
||||
n = Bread(&bin, buf, sizeof(buf)-1);
|
||||
}
|
||||
Bterm(bp);
|
||||
if(cout)
|
||||
Bterm(cout);
|
||||
if(tflag)
|
||||
return 0;
|
||||
|
||||
close(pipefd[1]);
|
||||
close(pipefd[0]);
|
||||
for(;;){
|
||||
status = wait();
|
||||
if(status == nil || status->pid == pid)
|
||||
break;
|
||||
free(status);
|
||||
}
|
||||
if(status == nil)
|
||||
strcpy(buf, "wait failed");
|
||||
else{
|
||||
strcpy(buf, status->msg);
|
||||
free(status);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
char*
|
||||
canon(Biobuf *bp, char *header, char *body, int *n)
|
||||
{
|
||||
int hsize;
|
||||
char *raw;
|
||||
|
||||
hsize = 0;
|
||||
*header = 0;
|
||||
*body = 0;
|
||||
raw = readmsg(bp, &hsize, n);
|
||||
if(raw){
|
||||
if(convert(raw, raw+hsize, header, Hdrsize, 0))
|
||||
conv64(raw+hsize, raw+*n, body, Bodysize); /* base64 */
|
||||
else
|
||||
convert(raw+hsize, raw+*n, body, Bodysize, 1); /* text */
|
||||
}
|
||||
return raw;
|
||||
}
|
||||
|
||||
int
|
||||
matchaction(int action, char *message, Resub *m)
|
||||
{
|
||||
char *name;
|
||||
Pattern *p;
|
||||
|
||||
if(message == 0 || *message == 0)
|
||||
return 0;
|
||||
|
||||
name = patterns[action].action;
|
||||
p = patterns[action].strings;
|
||||
if(p)
|
||||
if(matcher(name, p, message, m))
|
||||
return 1;
|
||||
|
||||
for(p = patterns[action].regexps; p; p = p->next)
|
||||
if(matcher(name, p, message, m))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
matcher(char *action, Pattern *p, char *message, Resub *m)
|
||||
{
|
||||
char *cp;
|
||||
String *s;
|
||||
|
||||
for(cp = message; matchpat(p, cp, m); cp = m->ep){
|
||||
switch(p->action){
|
||||
case SaveLine:
|
||||
if(vflag)
|
||||
xprint(2, action, m);
|
||||
saveline(linefile, sender, m);
|
||||
break;
|
||||
case HoldHeader:
|
||||
case Hold:
|
||||
if(nflag)
|
||||
continue;
|
||||
if(vflag)
|
||||
xprint(2, action, m);
|
||||
*qdir = holdqueue;
|
||||
if(hflag && qname){
|
||||
cp = strchr(sender, '!');
|
||||
if(cp){
|
||||
*cp = 0;
|
||||
*qname = strdup(sender);
|
||||
*cp = '!';
|
||||
} else
|
||||
*qname = strdup(sender);
|
||||
}
|
||||
return 1;
|
||||
case Dump:
|
||||
if(vflag)
|
||||
xprint(2, action, m);
|
||||
*(m->ep) = 0;
|
||||
if(!tflag){
|
||||
s = s_new();
|
||||
s_append(s, sender);
|
||||
s = unescapespecial(s);
|
||||
syslog(0, "smtpd", "Dumped %s [%s] to %s", s_to_c(s), m->sp,
|
||||
s_to_c(s_restart(recips)));
|
||||
s_free(s);
|
||||
}
|
||||
tflag = 1;
|
||||
if(sflag)
|
||||
cout = opendump(sender);
|
||||
return 1;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
saveline(char *file, char *sender, Resub *rp)
|
||||
{
|
||||
char *p, *q;
|
||||
int i, c;
|
||||
Biobuf *bp;
|
||||
|
||||
if(rp->sp == 0 || rp->ep == 0)
|
||||
return;
|
||||
/* back up approx 20 characters to whitespace */
|
||||
for(p = rp->sp, i = 0; *p && i < 20; i++, p--)
|
||||
;
|
||||
while(*p && *p != ' ')
|
||||
p--;
|
||||
p++;
|
||||
|
||||
/* grab about 20 more chars beyond the end of the match */
|
||||
for(q = rp->ep, i = 0; *q && i < 20; i++, q++)
|
||||
;
|
||||
while(*q && *q != ' ')
|
||||
q++;
|
||||
|
||||
c = *q;
|
||||
*q = 0;
|
||||
bp = sysopen(file, "al", 0644);
|
||||
if(bp){
|
||||
Bprint(bp, "%s-> %s\n", sender, p);
|
||||
Bterm(bp);
|
||||
}
|
||||
else if(debug)
|
||||
fprint(2, "can't save line: (%s) %s\n", sender, p);
|
||||
*q = c;
|
||||
}
|
||||
|
||||
Biobuf*
|
||||
opendump(char *sender)
|
||||
{
|
||||
int i;
|
||||
ulong h;
|
||||
char buf[512];
|
||||
Biobuf *b;
|
||||
char *cp;
|
||||
|
||||
cp = ctime(time(0));
|
||||
cp[7] = 0;
|
||||
cp[10] = 0;
|
||||
if(cp[8] == ' ')
|
||||
sprint(buf, "%s/queue.dump/%s%c", SPOOL, cp+4, cp[9]);
|
||||
else
|
||||
sprint(buf, "%s/queue.dump/%s%c%c", SPOOL, cp+4, cp[8], cp[9]);
|
||||
cp = buf+strlen(buf);
|
||||
if(access(buf, 0) < 0 && sysmkdir(buf, 0777) < 0){
|
||||
syslog(0, "smtpd", "couldn't dump mail from %s: %r", sender);
|
||||
return 0;
|
||||
}
|
||||
|
||||
h = 0;
|
||||
while(*sender)
|
||||
h = h*257 + *sender++;
|
||||
for(i = 0; i < 50; i++){
|
||||
h += lrand();
|
||||
sprint(cp, "/%lud", h);
|
||||
b = sysopen(buf, "wlc", 0644);
|
||||
if(b){
|
||||
if(vflag)
|
||||
fprint(2, "saving in %s\n", buf);
|
||||
return b;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Biobuf*
|
||||
opencopy(char *sender)
|
||||
{
|
||||
int i;
|
||||
ulong h;
|
||||
char buf[512];
|
||||
Biobuf *b;
|
||||
|
||||
h = 0;
|
||||
while(*sender)
|
||||
h = h*257 + *sender++;
|
||||
for(i = 0; i < 50; i++){
|
||||
h += lrand();
|
||||
sprint(buf, "%s/%lud", copydir, h);
|
||||
b = sysopen(buf, "wlc", 0600);
|
||||
if(b)
|
||||
return b;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
optoutofspamfilter(char *addr)
|
||||
{
|
||||
char *p, *f;
|
||||
int rv;
|
||||
|
||||
p = strchr(addr, '!');
|
||||
if(p)
|
||||
p++;
|
||||
else
|
||||
p = addr;
|
||||
|
||||
rv = 0;
|
||||
f = smprint("/mail/box/%s/nospamfiltering", p);
|
||||
if(f != nil){
|
||||
rv = access(f, 0)==0;
|
||||
free(f);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
62
src/cmd/upas/scanmail/spam.h
Normal file
62
src/cmd/upas/scanmail/spam.h
Normal file
@@ -0,0 +1,62 @@
|
||||
|
||||
enum{
|
||||
Dump = 0, /* Actions must be in order of descending importance */
|
||||
HoldHeader,
|
||||
Hold,
|
||||
SaveLine,
|
||||
Lineoff, /* Lineoff must be the last action code */
|
||||
Nactions,
|
||||
|
||||
Nhash = 128,
|
||||
|
||||
regexp = 1, /* types: literal string or regular expression */
|
||||
string = 2,
|
||||
|
||||
MaxHtml = 256,
|
||||
Hdrsize = 4096,
|
||||
Bodysize = 8192,
|
||||
Maxread = 64*1024,
|
||||
};
|
||||
|
||||
typedef struct spat Spat;
|
||||
typedef struct pattern Pattern;
|
||||
typedef struct patterns Patterns;
|
||||
struct spat
|
||||
{
|
||||
char* string;
|
||||
int len;
|
||||
int c1;
|
||||
Spat* next;
|
||||
Spat* alt;
|
||||
};
|
||||
|
||||
struct pattern{
|
||||
struct pattern *next;
|
||||
int action;
|
||||
int type;
|
||||
Spat* alt;
|
||||
union{
|
||||
Reprog* pat;
|
||||
Spat* spat[Nhash];
|
||||
};
|
||||
};
|
||||
|
||||
struct patterns {
|
||||
char *action;
|
||||
Pattern *strings;
|
||||
Pattern *regexps;
|
||||
};
|
||||
|
||||
extern int debug;
|
||||
extern Patterns patterns[];
|
||||
extern char header[];
|
||||
extern char cmd[];
|
||||
|
||||
extern void conv64(char*, char*, char*, int);
|
||||
extern int convert(char*, char*, char*, int, int);
|
||||
extern void* Malloc(long n);
|
||||
extern int matchpat(Pattern*, char*, Resub*);
|
||||
extern char* readmsg(Biobuf*, int*, int*);
|
||||
extern void parsepats(Biobuf*);
|
||||
extern void* Realloc(void*, ulong);
|
||||
extern void xprint(int, char*, Resub*);
|
||||
212
src/cmd/upas/scanmail/testscan.c
Normal file
212
src/cmd/upas/scanmail/testscan.c
Normal file
@@ -0,0 +1,212 @@
|
||||
#include "sys.h"
|
||||
#include "spam.h"
|
||||
|
||||
int debug;
|
||||
Biobuf bin;
|
||||
char patfile[128], header[Hdrsize+2];
|
||||
char cmd[1024];
|
||||
|
||||
char* canon(Biobuf*, char*, char*, int*);
|
||||
int matcher(char *, Pattern*, char*, Resub*);
|
||||
int matchaction(Patterns*, char*);
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "missing or bad arguments to qer\n");
|
||||
exits("usage");
|
||||
}
|
||||
|
||||
void *
|
||||
Malloc(long n)
|
||||
{
|
||||
void *p;
|
||||
|
||||
p = malloc(n);
|
||||
if(p == 0){
|
||||
fprint(2, "malloc error");
|
||||
exits("malloc");
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
void*
|
||||
Realloc(void *p, ulong n)
|
||||
{
|
||||
p = realloc(p, n);
|
||||
if(p == 0){
|
||||
fprint(2, "realloc error");
|
||||
exits("realloc");
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
void
|
||||
dumppats(void)
|
||||
{
|
||||
int i, j;
|
||||
Pattern *p;
|
||||
Spat *s, *q;
|
||||
|
||||
for(i = 0; patterns[i].action; i++){
|
||||
for(p = patterns[i].regexps; p; p = p->next){
|
||||
print("%s <REGEXP>\n", patterns[i].action);
|
||||
if(p->alt)
|
||||
print("Alt:");
|
||||
for(s = p->alt; s; s = s->next)
|
||||
print("\t%s\n", s->string);
|
||||
}
|
||||
p = patterns[i].strings;
|
||||
if(p == 0)
|
||||
continue;
|
||||
|
||||
for(j = 0; j < Nhash; j++){
|
||||
for(s = p->spat[j]; s; s = s->next){
|
||||
print("%s %s\n", patterns[i].action, s->string);
|
||||
if(s->alt)
|
||||
print("Alt:");
|
||||
for(q = s->alt; q; q = q->next)
|
||||
print("\t%s\n", q->string);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int i, fd, n, aflag, vflag;
|
||||
char body[Bodysize+2], *raw, *ret;
|
||||
Biobuf *bp;
|
||||
|
||||
sprint(patfile, "%s/patterns", UPASLIB);
|
||||
aflag = -1;
|
||||
vflag = 0;
|
||||
ARGBEGIN {
|
||||
case 'a':
|
||||
aflag = 1;
|
||||
break;
|
||||
case 'v':
|
||||
vflag = 1;
|
||||
break;
|
||||
case 'd':
|
||||
debug++;
|
||||
break;
|
||||
case 'p':
|
||||
strcpy(patfile,ARGF());
|
||||
break;
|
||||
} ARGEND
|
||||
|
||||
bp = Bopen(patfile, OREAD);
|
||||
if(bp){
|
||||
parsepats(bp);
|
||||
Bterm(bp);
|
||||
}
|
||||
|
||||
if(argc >= 1){
|
||||
fd = open(*argv, OREAD);
|
||||
if(fd < 0){
|
||||
fprint(2, "can't open %s\n", *argv);
|
||||
exits("open");
|
||||
}
|
||||
Binit(&bin, fd, OREAD);
|
||||
} else
|
||||
Binit(&bin, 0, OREAD);
|
||||
|
||||
*body = 0;
|
||||
*header = 0;
|
||||
ret = 0;
|
||||
for(;;){
|
||||
raw = canon(&bin, header+1, body+1, &n);
|
||||
if(raw == 0)
|
||||
break;
|
||||
if(aflag == 0)
|
||||
continue;
|
||||
if(aflag < 0)
|
||||
aflag = 0;
|
||||
if(vflag){
|
||||
if(header[1]) {
|
||||
fprint(2, "\t**** Header ****\n\n");
|
||||
write(2, header+1, strlen(header+1));
|
||||
fprint(2, "\n");
|
||||
}
|
||||
fprint(2, "\t**** Body ****\n\n");
|
||||
if(body[1])
|
||||
write(2, body+1, strlen(body+1));
|
||||
fprint(2, "\n");
|
||||
}
|
||||
|
||||
for(i = 0; patterns[i].action; i++){
|
||||
if(matchaction(&patterns[i], header+1))
|
||||
ret = patterns[i].action;
|
||||
if(i == HoldHeader)
|
||||
continue;
|
||||
if(matchaction(&patterns[i], body+1))
|
||||
ret = patterns[i].action;
|
||||
}
|
||||
}
|
||||
exits(ret);
|
||||
}
|
||||
|
||||
char*
|
||||
canon(Biobuf *bp, char *header, char *body, int *n)
|
||||
{
|
||||
int hsize, base64;
|
||||
|
||||
static char *raw;
|
||||
|
||||
hsize = 0;
|
||||
base64 = 0;
|
||||
*header = 0;
|
||||
*body = 0;
|
||||
if(raw == 0){
|
||||
raw = readmsg(bp, &hsize, n);
|
||||
if(raw)
|
||||
base64 = convert(raw, raw+hsize, header, Hdrsize, 0);
|
||||
} else {
|
||||
free(raw);
|
||||
raw = readmsg(bp, 0, n);
|
||||
}
|
||||
if(raw){
|
||||
if(base64)
|
||||
conv64(raw+hsize, raw+*n, body, Bodysize);
|
||||
else
|
||||
convert(raw+hsize, raw+*n, body, Bodysize, 1);
|
||||
}
|
||||
return raw;
|
||||
}
|
||||
|
||||
int
|
||||
matchaction(Patterns *pp, char *message)
|
||||
{
|
||||
char *name, *cp;
|
||||
int ret;
|
||||
Pattern *p;
|
||||
Resub m[1];
|
||||
|
||||
if(message == 0 || *message == 0)
|
||||
return 0;
|
||||
|
||||
name = pp->action;
|
||||
p = pp->strings;
|
||||
ret = 0;
|
||||
if(p)
|
||||
for(cp = message; matcher(name, p, cp, m); cp = m[0].ep)
|
||||
ret++;
|
||||
|
||||
for(p = pp->regexps; p; p = p->next)
|
||||
for(cp = message; matcher(name, p, cp, m); cp = m[0].ep)
|
||||
ret++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
matcher(char *action, Pattern *p, char *message, Resub *m)
|
||||
{
|
||||
if(matchpat(p, message, m)){
|
||||
if(p->action != Lineoff)
|
||||
xprint(1, action, m);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
29
src/cmd/upas/send/authorize.c
Normal file
29
src/cmd/upas/send/authorize.c
Normal file
@@ -0,0 +1,29 @@
|
||||
#include "common.h"
|
||||
#include "send.h"
|
||||
|
||||
/*
|
||||
* Run a command to authorize or refuse entry. Return status 0 means
|
||||
* authorize, -1 means refuse.
|
||||
*/
|
||||
void
|
||||
authorize(dest *dp)
|
||||
{
|
||||
process *pp;
|
||||
String *errstr;
|
||||
|
||||
dp->authorized = 1;
|
||||
pp = proc_start(s_to_c(dp->repl1), (stream *)0, (stream *)0, outstream(), 1, 0);
|
||||
if (pp == 0){
|
||||
dp->status = d_noforward;
|
||||
return;
|
||||
}
|
||||
errstr = s_new();
|
||||
while(s_read_line(pp->std[2]->fp, errstr))
|
||||
;
|
||||
if ((dp->pstat = proc_wait(pp)) != 0) {
|
||||
dp->repl2 = errstr;
|
||||
dp->status = d_noforward;
|
||||
} else
|
||||
s_free(errstr);
|
||||
proc_free(pp);
|
||||
}
|
||||
133
src/cmd/upas/send/bind.c
Normal file
133
src/cmd/upas/send/bind.c
Normal file
@@ -0,0 +1,133 @@
|
||||
#include "common.h"
|
||||
#include "send.h"
|
||||
|
||||
static int forward_loop(char *, char *);
|
||||
|
||||
/* bind the destinations to the commands to be executed */
|
||||
extern dest *
|
||||
up_bind(dest *destp, message *mp, int checkforward)
|
||||
{
|
||||
dest *list[2]; /* lists of unbound destinations */
|
||||
int li; /* index into list[2] */
|
||||
dest *bound=0; /* bound destinations */
|
||||
dest *dp;
|
||||
int i;
|
||||
|
||||
list[0] = destp;
|
||||
list[1] = 0;
|
||||
|
||||
/*
|
||||
* loop once to check for:
|
||||
* - forwarding rights
|
||||
* - addressing loops
|
||||
* - illegal characters
|
||||
* - characters that need escaping
|
||||
*/
|
||||
for (dp = d_rm(&list[0]); dp != 0; dp = d_rm(&list[0])) {
|
||||
if (!checkforward)
|
||||
dp->authorized = 1;
|
||||
dp->addr = escapespecial(dp->addr);
|
||||
if (forward_loop(s_to_c(dp->addr), thissys)) {
|
||||
dp->status = d_eloop;
|
||||
d_same_insert(&bound, dp);
|
||||
} else if(forward_loop(s_to_c(mp->sender), thissys)) {
|
||||
dp->status = d_eloop;
|
||||
d_same_insert(&bound, dp);
|
||||
} else if(shellchars(s_to_c(dp->addr))) {
|
||||
dp->status = d_syntax;
|
||||
d_same_insert(&bound, dp);
|
||||
} else
|
||||
d_insert(&list[1], dp);
|
||||
}
|
||||
li = 1;
|
||||
|
||||
/* Loop until all addresses are bound or address loop detected */
|
||||
for (i=0; list[li]!=0 && i<32; ++i, li ^= 1) {
|
||||
/* Traverse the current list. Bound items are put on the
|
||||
* `bound' list. Unbound items are put on the next list to
|
||||
* traverse, `list[li^1]'.
|
||||
*/
|
||||
for (dp = d_rm(&list[li]); dp != 0; dp = d_rm(&list[li])){
|
||||
dest *newlist;
|
||||
|
||||
rewrite(dp, mp);
|
||||
if(debug)
|
||||
fprint(2, "%s -> %s\n", s_to_c(dp->addr),
|
||||
dp->repl1 ? s_to_c(dp->repl1):"");
|
||||
switch (dp->status) {
|
||||
case d_auth:
|
||||
/* authorize address if not already authorized */
|
||||
if(!dp->authorized){
|
||||
authorize(dp);
|
||||
if(dp->status==d_auth)
|
||||
d_insert(&list[li^1], dp);
|
||||
else
|
||||
d_insert(&bound, dp);
|
||||
}
|
||||
break;
|
||||
case d_cat:
|
||||
/* address -> local */
|
||||
newlist = expand_local(dp);
|
||||
if (newlist == 0) {
|
||||
/* append to mailbox (or error) */
|
||||
d_same_insert(&bound, dp);
|
||||
} else if (newlist->status == d_undefined) {
|
||||
/* Forward to ... */
|
||||
d_insert(&list[li^1], newlist);
|
||||
} else {
|
||||
/* Pipe to ... */
|
||||
d_same_insert(&bound, newlist);
|
||||
}
|
||||
break;
|
||||
case d_pipe:
|
||||
/* address -> command */
|
||||
d_same_insert(&bound, dp);
|
||||
break;
|
||||
case d_alias:
|
||||
/* address -> rewritten address */
|
||||
newlist = s_to_dest(dp->repl1, dp);
|
||||
if(newlist != 0)
|
||||
d_insert(&list[li^1], newlist);
|
||||
else
|
||||
d_same_insert(&bound, dp);
|
||||
break;
|
||||
case d_translate:
|
||||
/* pipe to a translator */
|
||||
newlist = translate(dp);
|
||||
if (newlist != 0)
|
||||
d_insert(&list[li^1], newlist);
|
||||
else
|
||||
d_same_insert(&bound, dp);
|
||||
break;
|
||||
default:
|
||||
/* error */
|
||||
d_same_insert(&bound, dp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* mark remaining comands as "forwarding loops" */
|
||||
for (dp = d_rm(&list[li]); dp != 0; dp = d_rm(&list[li])) {
|
||||
dp->status = d_loop;
|
||||
d_same_insert(&bound, dp);
|
||||
}
|
||||
|
||||
return bound;
|
||||
}
|
||||
|
||||
/* Return TRUE if a forwarding loop exists, i.e., the String `system'
|
||||
* is found more than 4 times in the return address.
|
||||
*/
|
||||
static int
|
||||
forward_loop(char *addr, char *system)
|
||||
{
|
||||
int len = strlen(system), found = 0;
|
||||
|
||||
while (addr = strchr(addr, '!'))
|
||||
if (!strncmp(++addr, system, len)
|
||||
&& addr[len] == '!' && ++found == 4)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
60
src/cmd/upas/send/cat_mail.c
Normal file
60
src/cmd/upas/send/cat_mail.c
Normal file
@@ -0,0 +1,60 @@
|
||||
#include "common.h"
|
||||
#include "send.h"
|
||||
|
||||
|
||||
/* dispose of local addresses */
|
||||
int
|
||||
cat_mail(dest *dp, message *mp)
|
||||
{
|
||||
Biobuf *fp;
|
||||
char *rcvr, *cp;
|
||||
Mlock *l;
|
||||
String *tmp, *s;
|
||||
int i, n;
|
||||
|
||||
s = unescapespecial(s_clone(dp->repl1));
|
||||
if (nflg) {
|
||||
if(!xflg)
|
||||
print("cat >> %s\n", s_to_c(s));
|
||||
else
|
||||
print("%s\n", s_to_c(dp->addr));
|
||||
s_free(s);
|
||||
return 0;
|
||||
}
|
||||
for(i = 0;; i++){
|
||||
l = syslock(s_to_c(s));
|
||||
if(l == 0)
|
||||
return refuse(dp, mp, "can't lock mail file", 0, 0);
|
||||
|
||||
fp = sysopen(s_to_c(s), "al", MBOXMODE);
|
||||
if(fp)
|
||||
break;
|
||||
tmp = s_append(0, s_to_c(s));
|
||||
s_append(tmp, ".tmp");
|
||||
fp = sysopen(s_to_c(tmp), "al", MBOXMODE);
|
||||
if(fp){
|
||||
syslog(0, "mail", "error: used %s", s_to_c(tmp));
|
||||
s_free(tmp);
|
||||
break;
|
||||
}
|
||||
s_free(tmp);
|
||||
sysunlock(l);
|
||||
if(i >= 5)
|
||||
return refuse(dp, mp, "mail file cannot be opened", 0, 0);
|
||||
sleep(1000);
|
||||
}
|
||||
s_free(s);
|
||||
n = m_print(mp, fp, (char *)0, 1);
|
||||
if (Bprint(fp, "\n") < 0 || Bflush(fp) < 0 || n < 0){
|
||||
sysclose(fp);
|
||||
sysunlock(l);
|
||||
return refuse(dp, mp, "error writing mail file", 0, 0);
|
||||
}
|
||||
sysclose(fp);
|
||||
sysunlock(l);
|
||||
rcvr = s_to_c(dp->addr);
|
||||
if(cp = strrchr(rcvr, '!'))
|
||||
rcvr = cp+1;
|
||||
logdelivery(dp, rcvr, mp);
|
||||
return 0;
|
||||
}
|
||||
260
src/cmd/upas/send/dest.c
Normal file
260
src/cmd/upas/send/dest.c
Normal file
@@ -0,0 +1,260 @@
|
||||
#include "common.h"
|
||||
#include "send.h"
|
||||
|
||||
static String* s_parseq(String*, String*);
|
||||
|
||||
/* exports */
|
||||
dest *dlist;
|
||||
|
||||
extern dest*
|
||||
d_new(String *addr)
|
||||
{
|
||||
dest *dp;
|
||||
|
||||
dp = (dest *)mallocz(sizeof(dest), 1);
|
||||
if (dp == 0) {
|
||||
perror("d_new");
|
||||
exit(1);
|
||||
}
|
||||
dp->same = dp;
|
||||
dp->nsame = 1;
|
||||
dp->nchar = 0;
|
||||
dp->next = dp;
|
||||
dp->addr = escapespecial(addr);
|
||||
dp->parent = 0;
|
||||
dp->repl1 = dp->repl2 = 0;
|
||||
dp->status = d_undefined;
|
||||
return dp;
|
||||
}
|
||||
|
||||
extern void
|
||||
d_free(dest *dp)
|
||||
{
|
||||
if (dp != 0) {
|
||||
s_free(dp->addr);
|
||||
s_free(dp->repl1);
|
||||
s_free(dp->repl2);
|
||||
free((char *)dp);
|
||||
}
|
||||
}
|
||||
|
||||
/* The following routines manipulate an ordered list of items. Insertions
|
||||
* are always to the end of the list. Deletions are from the beginning.
|
||||
*
|
||||
* The list are circular witht the `head' of the list being the last item
|
||||
* added.
|
||||
*/
|
||||
|
||||
/* Get first element from a circular list linked via 'next'. */
|
||||
extern dest *
|
||||
d_rm(dest **listp)
|
||||
{
|
||||
dest *dp;
|
||||
|
||||
if (*listp == 0)
|
||||
return 0;
|
||||
dp = (*listp)->next;
|
||||
if (dp == *listp)
|
||||
*listp = 0;
|
||||
else
|
||||
(*listp)->next = dp->next;
|
||||
dp->next = dp;
|
||||
return dp;
|
||||
}
|
||||
|
||||
/* Insert a new entry at the end of the list linked via 'next'. */
|
||||
extern void
|
||||
d_insert(dest **listp, dest *new)
|
||||
{
|
||||
dest *head;
|
||||
|
||||
if (*listp == 0) {
|
||||
*listp = new;
|
||||
return;
|
||||
}
|
||||
if (new == 0)
|
||||
return;
|
||||
head = new->next;
|
||||
new->next = (*listp)->next;
|
||||
(*listp)->next = head;
|
||||
*listp = new;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get first element from a circular list linked via 'same'. */
|
||||
extern dest *
|
||||
d_rm_same(dest **listp)
|
||||
{
|
||||
dest *dp;
|
||||
|
||||
if (*listp == 0)
|
||||
return 0;
|
||||
dp = (*listp)->same;
|
||||
if (dp == *listp)
|
||||
*listp = 0;
|
||||
else
|
||||
(*listp)->same = dp->same;
|
||||
dp->same = dp;
|
||||
return dp;
|
||||
}
|
||||
|
||||
/* Look for a duplicate on the same list */
|
||||
int
|
||||
d_same_dup(dest *dp, dest *new)
|
||||
{
|
||||
dest *first = dp;
|
||||
|
||||
if(new->repl2 == 0)
|
||||
return 1;
|
||||
do {
|
||||
if(strcmp(s_to_c(dp->repl2), s_to_c(new->repl2))==0)
|
||||
return 1;
|
||||
dp = dp->same;
|
||||
} while(dp != first);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Insert an entry into the corresponding list linked by 'same'. Note that
|
||||
* the basic structure is a list of lists.
|
||||
*/
|
||||
extern void
|
||||
d_same_insert(dest **listp, dest *new)
|
||||
{
|
||||
dest *dp;
|
||||
int len;
|
||||
|
||||
if(new->status == d_pipe || new->status == d_cat) {
|
||||
len = new->repl2 ? strlen(s_to_c(new->repl2)) : 0;
|
||||
if(*listp != 0){
|
||||
dp = (*listp)->next;
|
||||
do {
|
||||
if(dp->status == new->status
|
||||
&& strcmp(s_to_c(dp->repl1), s_to_c(new->repl1))==0){
|
||||
/* remove duplicates */
|
||||
if(d_same_dup(dp, new))
|
||||
return;
|
||||
/* add to chain if chain small enough */
|
||||
if(dp->nsame < MAXSAME
|
||||
&& dp->nchar + len < MAXSAMECHAR){
|
||||
new->same = dp->same;
|
||||
dp->same = new;
|
||||
dp->nchar += len + 1;
|
||||
dp->nsame++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
dp = dp->next;
|
||||
} while (dp != (*listp)->next);
|
||||
}
|
||||
new->nchar = strlen(s_to_c(new->repl1)) + len + 1;
|
||||
}
|
||||
new->next = new;
|
||||
d_insert(listp, new);
|
||||
}
|
||||
|
||||
/*
|
||||
* Form a To: if multiple destinations.
|
||||
* The local! and !local! checks are artificial intelligence,
|
||||
* there should be a better way.
|
||||
*/
|
||||
extern String*
|
||||
d_to(dest *list)
|
||||
{
|
||||
dest *np, *sp;
|
||||
String *s;
|
||||
int i, n;
|
||||
char *cp;
|
||||
|
||||
s = s_new();
|
||||
s_append(s, "To: ");
|
||||
np = list;
|
||||
i = n = 0;
|
||||
do {
|
||||
np = np->next;
|
||||
sp = np;
|
||||
do {
|
||||
sp = sp->same;
|
||||
cp = s_to_c(sp->addr);
|
||||
|
||||
/* hack to get local! out of the names */
|
||||
if(strncmp(cp, "local!", 6) == 0)
|
||||
cp += 6;
|
||||
|
||||
if(n > 20){ /* 20 to appease mailers complaining about long lines */
|
||||
s_append(s, "\n\t");
|
||||
n = 0;
|
||||
}
|
||||
if(i != 0){
|
||||
s_append(s, ", ");
|
||||
n += 2;
|
||||
}
|
||||
s_append(s, cp);
|
||||
n += strlen(cp);
|
||||
i++;
|
||||
} while(sp != np);
|
||||
} while(np != list);
|
||||
|
||||
return unescapespecial(s);
|
||||
}
|
||||
|
||||
/* expand a String of destinations into a linked list of destiniations */
|
||||
extern dest *
|
||||
s_to_dest(String *sp, dest *parent)
|
||||
{
|
||||
String *addr;
|
||||
dest *list=0;
|
||||
dest *new;
|
||||
|
||||
if (sp == 0)
|
||||
return 0;
|
||||
addr = s_new();
|
||||
while (s_parseq(sp, addr)!=0) {
|
||||
addr = escapespecial(addr);
|
||||
if(shellchars(s_to_c(addr))){
|
||||
while(new = d_rm(&list))
|
||||
d_free(new);
|
||||
break;
|
||||
}
|
||||
new = d_new(addr);
|
||||
new->parent = parent;
|
||||
new->authorized = parent->authorized;
|
||||
d_insert(&list, new);
|
||||
addr = s_new();
|
||||
}
|
||||
s_free(addr);
|
||||
return list;
|
||||
}
|
||||
|
||||
#undef isspace
|
||||
#define isspace(c) ((c)==' ' || (c)=='\t' || (c)=='\n')
|
||||
|
||||
/* Get the next field from a String. The field is delimited by white space.
|
||||
* Anything delimited by double quotes is included in the string.
|
||||
*/
|
||||
static String*
|
||||
s_parseq(String *from, String *to)
|
||||
{
|
||||
int c;
|
||||
|
||||
if (*from->ptr == '\0')
|
||||
return 0;
|
||||
if (to == 0)
|
||||
to = s_new();
|
||||
for (c = *from->ptr;!isspace(c) && c != 0; c = *(++from->ptr)){
|
||||
s_putc(to, c);
|
||||
if(c == '"'){
|
||||
for (c = *(++from->ptr); c && c != '"'; c = *(++from->ptr))
|
||||
s_putc(to, *from->ptr);
|
||||
s_putc(to, '"');
|
||||
if(c == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
s_terminate(to);
|
||||
|
||||
/* crunch trailing white */
|
||||
while(isspace(*from->ptr))
|
||||
from->ptr++;
|
||||
|
||||
return to;
|
||||
}
|
||||
128
src/cmd/upas/send/filter.c
Normal file
128
src/cmd/upas/send/filter.c
Normal file
@@ -0,0 +1,128 @@
|
||||
#include "common.h"
|
||||
#include "send.h"
|
||||
|
||||
Biobuf bin;
|
||||
int rmail, tflg;
|
||||
char *subjectarg;
|
||||
|
||||
char *findbody(char*);
|
||||
|
||||
void
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
message *mp;
|
||||
dest *dp;
|
||||
Reprog *p;
|
||||
Resub match[10];
|
||||
char file[MAXPATHLEN];
|
||||
Biobuf *fp;
|
||||
char *rcvr, *cp;
|
||||
Mlock *l;
|
||||
String *tmp;
|
||||
int i;
|
||||
int header, body;
|
||||
|
||||
header = body = 0;
|
||||
ARGBEGIN {
|
||||
case 'h':
|
||||
header = 1;
|
||||
break;
|
||||
case 'b':
|
||||
header = 1;
|
||||
body = 1;
|
||||
break;
|
||||
} ARGEND
|
||||
|
||||
Binit(&bin, 0, OREAD);
|
||||
if(argc < 2){
|
||||
fprint(2, "usage: filter rcvr mailfile [regexp mailfile ...]\n");
|
||||
exits("usage");
|
||||
}
|
||||
mp = m_read(&bin, 1, 0);
|
||||
|
||||
/* get rid of local system name */
|
||||
cp = strchr(s_to_c(mp->sender), '!');
|
||||
if(cp){
|
||||
cp++;
|
||||
mp->sender = s_copy(cp);
|
||||
}
|
||||
|
||||
dp = d_new(s_copy(argv[0]));
|
||||
strecpy(file, file+sizeof file, argv[1]);
|
||||
cp = findbody(s_to_c(mp->body));
|
||||
for(i = 2; i < argc; i += 2){
|
||||
p = regcomp(argv[i]);
|
||||
if(p == 0)
|
||||
continue;
|
||||
if(regexec(p, s_to_c(mp->sender), match, 10)){
|
||||
regsub(argv[i+1], file, sizeof(file), match, 10);
|
||||
break;
|
||||
}
|
||||
if(header == 0 && body == 0)
|
||||
continue;
|
||||
if(regexec(p, s_to_c(mp->body), match, 10)){
|
||||
if(body == 0 && match[0].s.sp >= cp)
|
||||
continue;
|
||||
regsub(argv[i+1], file, sizeof(file), match, 10);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* always lock the normal mail file to avoid too many lock files
|
||||
* lying about. This isn't right but it's what the majority prefers.
|
||||
*/
|
||||
l = syslock(argv[1]);
|
||||
if(l == 0){
|
||||
fprint(2, "can't lock mail file %s\n", argv[1]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* open the destination mail file
|
||||
*/
|
||||
fp = sysopen(file, "ca", MBOXMODE);
|
||||
if (fp == 0){
|
||||
tmp = s_append(0, file);
|
||||
s_append(tmp, ".tmp");
|
||||
fp = sysopen(s_to_c(tmp), "cal", MBOXMODE);
|
||||
if(fp == 0){
|
||||
sysunlock(l);
|
||||
fprint(2, "can't open mail file %s\n", file);
|
||||
exit(1);
|
||||
}
|
||||
syslog(0, "mail", "error: used %s", s_to_c(tmp));
|
||||
s_free(tmp);
|
||||
}
|
||||
Bseek(fp, 0, 2);
|
||||
if(m_print(mp, fp, (char *)0, 1) < 0
|
||||
|| Bprint(fp, "\n") < 0
|
||||
|| Bflush(fp) < 0){
|
||||
sysclose(fp);
|
||||
sysunlock(l);
|
||||
fprint(2, "can't write mail file %s\n", file);
|
||||
exit(1);
|
||||
}
|
||||
sysclose(fp);
|
||||
|
||||
sysunlock(l);
|
||||
rcvr = argv[0];
|
||||
if(cp = strrchr(rcvr, '!'))
|
||||
rcvr = cp+1;
|
||||
logdelivery(dp, rcvr, mp);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
char*
|
||||
findbody(char *p)
|
||||
{
|
||||
if(*p == '\n')
|
||||
return p;
|
||||
|
||||
while(*p){
|
||||
if(*p == '\n' && *(p+1) == '\n')
|
||||
return p+1;
|
||||
p++;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
24
src/cmd/upas/send/gateway.c
Normal file
24
src/cmd/upas/send/gateway.c
Normal file
@@ -0,0 +1,24 @@
|
||||
#include "common.h"
|
||||
#include "send.h"
|
||||
|
||||
#undef isspace
|
||||
#define isspace(c) ((c)==' ' || (c)=='\t' || (c)=='\n')
|
||||
|
||||
/*
|
||||
* Translate the last component of the sender address. If the translation
|
||||
* yields the same address, replace the sender with its last component.
|
||||
*/
|
||||
extern void
|
||||
gateway(message *mp)
|
||||
{
|
||||
char *base;
|
||||
String *s;
|
||||
|
||||
/* first remove all systems equivalent to us */
|
||||
base = skipequiv(s_to_c(mp->sender));
|
||||
if(base != s_to_c(mp->sender)){
|
||||
s = mp->sender;
|
||||
mp->sender = s_copy(base);
|
||||
s_free(s);
|
||||
}
|
||||
}
|
||||
129
src/cmd/upas/send/local.c
Normal file
129
src/cmd/upas/send/local.c
Normal file
@@ -0,0 +1,129 @@
|
||||
#include "common.h"
|
||||
#include "send.h"
|
||||
|
||||
static void
|
||||
mboxfile(dest *dp, String *user, String *path, char *file)
|
||||
{
|
||||
char *cp;
|
||||
|
||||
mboxpath(s_to_c(user), s_to_c(dp->addr), path, 0);
|
||||
cp = strrchr(s_to_c(path), '/');
|
||||
if(cp)
|
||||
path->ptr = cp+1;
|
||||
else
|
||||
path->ptr = path->base;
|
||||
s_append(path, file);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check forwarding requests
|
||||
*/
|
||||
extern dest*
|
||||
expand_local(dest *dp)
|
||||
{
|
||||
Biobuf *fp;
|
||||
String *file, *line, *s;
|
||||
dest *rv;
|
||||
int forwardok;
|
||||
char *user;
|
||||
|
||||
/* short circuit obvious security problems */
|
||||
if(strstr(s_to_c(dp->addr), "/../")){
|
||||
dp->status = d_unknown;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* isolate user's name if part of a path */
|
||||
user = strrchr(s_to_c(dp->addr), '!');
|
||||
if(user)
|
||||
user++;
|
||||
else
|
||||
user = s_to_c(dp->addr);
|
||||
|
||||
/* if no replacement string, plug in user's name */
|
||||
if(dp->repl1 == 0){
|
||||
dp->repl1 = s_new();
|
||||
mboxname(user, dp->repl1);
|
||||
}
|
||||
|
||||
s = unescapespecial(s_clone(dp->repl1));
|
||||
|
||||
/*
|
||||
* if this is the descendant of a `forward' file, don't
|
||||
* look for a forward.
|
||||
*/
|
||||
forwardok = 1;
|
||||
for(rv = dp->parent; rv; rv = rv->parent)
|
||||
if(rv->status == d_cat){
|
||||
forwardok = 0;
|
||||
break;
|
||||
}
|
||||
file = s_new();
|
||||
if(forwardok){
|
||||
/*
|
||||
* look for `forward' file for forwarding address(es)
|
||||
*/
|
||||
mboxfile(dp, s, file, "forward");
|
||||
fp = sysopen(s_to_c(file), "r", 0);
|
||||
if (fp != 0) {
|
||||
line = s_new();
|
||||
for(;;){
|
||||
if(s_read_line(fp, line) == nil)
|
||||
break;
|
||||
if(*(line->ptr - 1) != '\n')
|
||||
break;
|
||||
if(*(line->ptr - 2) == '\\')
|
||||
*(line->ptr-2) = ' ';
|
||||
*(line->ptr-1) = ' ';
|
||||
}
|
||||
sysclose(fp);
|
||||
if(debug)
|
||||
fprint(2, "forward = %s\n", s_to_c(line));
|
||||
rv = s_to_dest(s_restart(line), dp);
|
||||
s_free(line);
|
||||
if(rv){
|
||||
s_free(file);
|
||||
s_free(s);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* look for a 'pipe' file. This won't work if there are
|
||||
* special characters in the account name since the file
|
||||
* name passes through a shell. tdb.
|
||||
*/
|
||||
mboxfile(dp, dp->repl1, s_reset(file), "pipeto");
|
||||
if(sysexist(s_to_c(file))){
|
||||
if(debug)
|
||||
fprint(2, "found a pipeto file\n");
|
||||
dp->status = d_pipeto;
|
||||
line = s_new();
|
||||
s_append(line, "upasname='");
|
||||
s_append(line, user);
|
||||
s_append(line, "' ");
|
||||
s_append(line, s_to_c(file));
|
||||
s_append(line, " ");
|
||||
s_append(line, s_to_c(dp->addr));
|
||||
s_append(line, " ");
|
||||
s_append(line, s_to_c(dp->repl1));
|
||||
s_free(dp->repl1);
|
||||
dp->repl1 = line;
|
||||
s_free(file);
|
||||
s_free(s);
|
||||
return dp;
|
||||
}
|
||||
|
||||
/*
|
||||
* see if the mailbox directory exists
|
||||
*/
|
||||
mboxfile(dp, s, s_reset(file), ".");
|
||||
if(sysexist(s_to_c(file)))
|
||||
dp->status = d_cat;
|
||||
else
|
||||
dp->status = d_unknown;
|
||||
s_free(file);
|
||||
s_free(s);
|
||||
return 0;
|
||||
}
|
||||
85
src/cmd/upas/send/log.c
Normal file
85
src/cmd/upas/send/log.c
Normal file
@@ -0,0 +1,85 @@
|
||||
#include "common.h"
|
||||
#include "send.h"
|
||||
|
||||
/* configuration */
|
||||
#define LOGBiobuf "log/status"
|
||||
|
||||
/* log mail delivery */
|
||||
extern void
|
||||
logdelivery(dest *list, char *rcvr, message *mp)
|
||||
{
|
||||
dest *parent;
|
||||
String *srcvr, *sender;
|
||||
|
||||
srcvr = unescapespecial(s_copy(rcvr));
|
||||
sender = unescapespecial(s_clone(mp->sender));
|
||||
|
||||
for(parent=list; parent->parent!=0; parent=parent->parent)
|
||||
;
|
||||
if(parent!=list && strcmp(s_to_c(parent->addr), s_to_c(srcvr))!=0)
|
||||
syslog(0, "mail", "delivered %s From %.256s %.256s (%.256s) %d",
|
||||
rcvr,
|
||||
s_to_c(sender), s_to_c(mp->date),
|
||||
s_to_c(parent->addr), mp->size);
|
||||
else
|
||||
syslog(0, "mail", "delivered %s From %.256s %.256s %d", s_to_c(srcvr),
|
||||
s_to_c(sender), s_to_c(mp->date), mp->size);
|
||||
s_free(srcvr);
|
||||
s_free(sender);
|
||||
}
|
||||
|
||||
/* log mail forwarding */
|
||||
extern void
|
||||
loglist(dest *list, message *mp, char *tag)
|
||||
{
|
||||
dest *next;
|
||||
dest *parent;
|
||||
String *srcvr, *sender;
|
||||
|
||||
sender = unescapespecial(s_clone(mp->sender));
|
||||
|
||||
for(next=d_rm(&list); next != 0; next = d_rm(&list)) {
|
||||
for(parent=next; parent->parent!=0; parent=parent->parent)
|
||||
;
|
||||
srcvr = unescapespecial(s_clone(next->addr));
|
||||
if(parent!=next)
|
||||
syslog(0, "mail", "%s %.256s From %.256s %.256s (%.256s) %d",
|
||||
tag,
|
||||
s_to_c(srcvr), s_to_c(sender),
|
||||
s_to_c(mp->date), s_to_c(parent->addr), mp->size);
|
||||
else
|
||||
syslog(0, "mail", "%s %.256s From %.256s %.256s %d", tag,
|
||||
s_to_c(srcvr), s_to_c(sender),
|
||||
s_to_c(mp->date), mp->size);
|
||||
s_free(srcvr);
|
||||
}
|
||||
s_free(sender);
|
||||
}
|
||||
|
||||
/* log a mail refusal */
|
||||
extern void
|
||||
logrefusal(dest *dp, message *mp, char *msg)
|
||||
{
|
||||
char buf[2048];
|
||||
char *cp, *ep;
|
||||
String *sender, *srcvr;
|
||||
|
||||
srcvr = unescapespecial(s_clone(dp->addr));
|
||||
sender = unescapespecial(s_clone(mp->sender));
|
||||
|
||||
sprint(buf, "error %.256s From %.256s %.256s\nerror+ ", s_to_c(srcvr),
|
||||
s_to_c(sender), s_to_c(mp->date));
|
||||
s_free(srcvr);
|
||||
s_free(sender);
|
||||
cp = buf + strlen(buf);
|
||||
ep = buf + sizeof(buf) - sizeof("error + ");
|
||||
while(*msg && cp<ep) {
|
||||
*cp++ = *msg;
|
||||
if (*msg++ == '\n') {
|
||||
strcpy(cp, "error+ ");
|
||||
cp += sizeof("error+ ") - 1;
|
||||
}
|
||||
}
|
||||
*cp = 0;
|
||||
syslog(0, "mail", "%s", buf);
|
||||
}
|
||||
575
src/cmd/upas/send/main.c
Normal file
575
src/cmd/upas/send/main.c
Normal file
@@ -0,0 +1,575 @@
|
||||
#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;
|
||||
}
|
||||
46
src/cmd/upas/send/makefile
Normal file
46
src/cmd/upas/send/makefile
Normal file
@@ -0,0 +1,46 @@
|
||||
SSRC= message.c main.c bind.c rewrite.c local.c dest.c process.c translate.c\
|
||||
log.c chkfwd.c notify.c gateway.c authorize.o ../common/*.c
|
||||
SOBJ= message.o main.o bind.o rewrite.o local.o dest.o process.o translate.o\
|
||||
log.o chkfwd.o notify.o gateway.o authorize.o\
|
||||
../config/config.o ../common/common.a ../libc/libc.a
|
||||
SINC= ../common/mail.h ../common/string.h ../common/aux.h
|
||||
CFLAGS=${UNIX} -g -I. -I../libc -I../common -I/usr/include ${SCFLAGS}
|
||||
LFLAGS=-g
|
||||
.c.o: ; $(CC) -c $(CFLAGS) $*.c
|
||||
LIB=/usr/lib/upas
|
||||
|
||||
all: send
|
||||
|
||||
send: $(SOBJ)
|
||||
$(CC) $(SOBJ) $(LFLAGS) -o send
|
||||
|
||||
chkfwd.o: $(SINC) message.h dest.h
|
||||
dest.o: $(SINC) dest.h
|
||||
local.o: $(SINC) dest.h process.h
|
||||
log.o: $(SINC) message.h
|
||||
main.o: $(SINC) message.h dest.h process.h
|
||||
bind.o: $(SINC) dest.h message.h
|
||||
process.o: $(SINC) process.h
|
||||
rewrite.o: $(SINC) dest.h
|
||||
translate.o: $(SINC) dest.h process.h
|
||||
message.o: $(SINC) message.h
|
||||
notify.o: $(SINC) message.h
|
||||
gateway.o: $(SINC) dest.h message.h
|
||||
|
||||
prcan:
|
||||
prcan $(SSRC)
|
||||
|
||||
clean:
|
||||
-rm -f send *.[oO] a.out core *.sL rmail
|
||||
|
||||
cyntax:
|
||||
cyntax $(CFLAGS) $(SSRC)
|
||||
|
||||
install: send
|
||||
rm -f $(LIB)/send /bin/rmail
|
||||
cp send $(LIB)/send
|
||||
cp send /bin/rmail
|
||||
strip /bin/rmail
|
||||
strip $(LIB)/send
|
||||
chown root $(LIB)/send /bin/rmail
|
||||
chmod 4755 $(LIB)/send /bin/rmail
|
||||
573
src/cmd/upas/send/message.c
Normal file
573
src/cmd/upas/send/message.c
Normal file
@@ -0,0 +1,573 @@
|
||||
#include "common.h"
|
||||
#include "send.h"
|
||||
|
||||
#include "../smtp/smtp.h"
|
||||
#include "../smtp/y.tab.h"
|
||||
|
||||
/* global to this file */
|
||||
static Reprog *rfprog;
|
||||
static Reprog *fprog;
|
||||
|
||||
#define VMLIMIT (64*1024)
|
||||
#define MSGLIMIT (128*1024*1024)
|
||||
|
||||
int received; /* from rfc822.y */
|
||||
|
||||
static String* getstring(Node *p);
|
||||
static String* getaddr(Node *p);
|
||||
|
||||
extern int
|
||||
default_from(message *mp)
|
||||
{
|
||||
char *cp, *lp;
|
||||
|
||||
cp = getenv("upasname");
|
||||
lp = getlog();
|
||||
if(lp == nil)
|
||||
return -1;
|
||||
|
||||
if(cp && *cp)
|
||||
s_append(mp->sender, cp);
|
||||
else
|
||||
s_append(mp->sender, lp);
|
||||
s_append(mp->date, thedate());
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern message *
|
||||
m_new(void)
|
||||
{
|
||||
message *mp;
|
||||
|
||||
mp = (message *)mallocz(sizeof(message), 1);
|
||||
if (mp == 0) {
|
||||
perror("message:");
|
||||
exit(1);
|
||||
}
|
||||
mp->sender = s_new();
|
||||
mp->replyaddr = s_new();
|
||||
mp->date = s_new();
|
||||
mp->body = s_new();
|
||||
mp->size = 0;
|
||||
mp->fd = -1;
|
||||
return mp;
|
||||
}
|
||||
|
||||
extern void
|
||||
m_free(message *mp)
|
||||
{
|
||||
if(mp->fd >= 0){
|
||||
close(mp->fd);
|
||||
sysremove(s_to_c(mp->tmp));
|
||||
s_free(mp->tmp);
|
||||
}
|
||||
s_free(mp->sender);
|
||||
s_free(mp->date);
|
||||
s_free(mp->body);
|
||||
s_free(mp->havefrom);
|
||||
s_free(mp->havesender);
|
||||
s_free(mp->havereplyto);
|
||||
s_free(mp->havesubject);
|
||||
free((char *)mp);
|
||||
}
|
||||
|
||||
/* read a message into a temp file , return an open fd to it */
|
||||
static int
|
||||
m_read_to_file(Biobuf *fp, message *mp)
|
||||
{
|
||||
int fd;
|
||||
int n;
|
||||
String *file;
|
||||
char buf[4*1024];
|
||||
|
||||
file = s_new();
|
||||
/*
|
||||
* create temp file to be remove on close
|
||||
*/
|
||||
abspath("mtXXXXXX", UPASTMP, file);
|
||||
mktemp(s_to_c(file));
|
||||
if((fd = syscreate(s_to_c(file), ORDWR|ORCLOSE, 0600))<0){
|
||||
s_free(file);
|
||||
return -1;
|
||||
}
|
||||
mp->tmp = file;
|
||||
|
||||
/*
|
||||
* read the rest into the temp file
|
||||
*/
|
||||
while((n = Bread(fp, buf, sizeof(buf))) > 0){
|
||||
if(write(fd, buf, n) != n){
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
mp->size += n;
|
||||
if(mp->size > MSGLIMIT){
|
||||
mp->size = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mp->fd = fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* get the first address from a node */
|
||||
static String*
|
||||
getaddr(Node *p)
|
||||
{
|
||||
for(; p; p = p->next)
|
||||
if(p->s && p->addr)
|
||||
return s_copy(s_to_c(p->s));
|
||||
}
|
||||
|
||||
/* get the text of a header line minus the field name */
|
||||
static String*
|
||||
getstring(Node *p)
|
||||
{
|
||||
String *s;
|
||||
|
||||
s = s_new();
|
||||
if(p == nil)
|
||||
return s;
|
||||
|
||||
for(p = p->next; p; p = p->next){
|
||||
if(p->s){
|
||||
s_append(s, s_to_c(p->s));
|
||||
}else{
|
||||
s_putc(s, p->c);
|
||||
s_terminate(s);
|
||||
}
|
||||
if(p->white)
|
||||
s_append(s, s_to_c(p->white));
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
#if 0 /* jpc */
|
||||
static char *fieldname[] =
|
||||
{
|
||||
[WORD-WORD] "WORD",
|
||||
[DATE-WORD] "DATE",
|
||||
[RESENT_DATE-WORD] "RESENT_DATE",
|
||||
[RETURN_PATH-WORD] "RETURN_PATH",
|
||||
[FROM-WORD] "FROM",
|
||||
[SENDER-WORD] "SENDER",
|
||||
[REPLY_TO-WORD] "REPLY_TO",
|
||||
[RESENT_FROM-WORD] "RESENT_FROM",
|
||||
[RESENT_SENDER-WORD] "RESENT_SENDER",
|
||||
[RESENT_REPLY_TO-WORD] "RESENT_REPLY_TO",
|
||||
[SUBJECT-WORD] "SUBJECT",
|
||||
[TO-WORD] "TO",
|
||||
[CC-WORD] "CC",
|
||||
[BCC-WORD] "BCC",
|
||||
[RESENT_TO-WORD] "RESENT_TO",
|
||||
[RESENT_CC-WORD] "RESENT_CC",
|
||||
[RESENT_BCC-WORD] "RESENT_BCC",
|
||||
[REMOTE-WORD] "REMOTE",
|
||||
[PRECEDENCE-WORD] "PRECEDENCE",
|
||||
[MIMEVERSION-WORD] "MIMEVERSION",
|
||||
[CONTENTTYPE-WORD] "CONTENTTYPE",
|
||||
[MESSAGEID-WORD] "MESSAGEID",
|
||||
[RECEIVED-WORD] "RECEIVED",
|
||||
[MAILER-WORD] "MAILER",
|
||||
[BADTOKEN-WORD] "BADTOKEN",
|
||||
};
|
||||
#endif /* jpc */
|
||||
|
||||
/* fix 822 addresses */
|
||||
static void
|
||||
rfc822cruft(message *mp)
|
||||
{
|
||||
Field *f;
|
||||
Node *p;
|
||||
String *body, *s;
|
||||
char *cp;
|
||||
|
||||
/*
|
||||
* parse headers in in-core part
|
||||
*/
|
||||
yyinit(s_to_c(mp->body), s_len(mp->body));
|
||||
mp->rfc822headers = 0;
|
||||
yyparse();
|
||||
mp->rfc822headers = 1;
|
||||
mp->received = received;
|
||||
|
||||
/*
|
||||
* remove equivalent systems in all addresses
|
||||
*/
|
||||
body = s_new();
|
||||
cp = s_to_c(mp->body);
|
||||
for(f = firstfield; f; f = f->next){
|
||||
if(f->node->c == MIMEVERSION)
|
||||
mp->havemime = 1;
|
||||
if(f->node->c == FROM)
|
||||
mp->havefrom = getaddr(f->node);
|
||||
if(f->node->c == SENDER)
|
||||
mp->havesender = getaddr(f->node);
|
||||
if(f->node->c == REPLY_TO)
|
||||
mp->havereplyto = getaddr(f->node);
|
||||
if(f->node->c == TO)
|
||||
mp->haveto = 1;
|
||||
if(f->node->c == DATE)
|
||||
mp->havedate = 1;
|
||||
if(f->node->c == SUBJECT)
|
||||
mp->havesubject = getstring(f->node);
|
||||
if(f->node->c == PRECEDENCE && f->node->next && f->node->next->next){
|
||||
s = f->node->next->next->s;
|
||||
if(s && (strcmp(s_to_c(s), "bulk") == 0
|
||||
|| strcmp(s_to_c(s), "Bulk") == 0))
|
||||
mp->bulk = 1;
|
||||
}
|
||||
for(p = f->node; p; p = p->next){
|
||||
if(p->s){
|
||||
if(p->addr){
|
||||
cp = skipequiv(s_to_c(p->s));
|
||||
s_append(body, cp);
|
||||
} else
|
||||
s_append(body, s_to_c(p->s));
|
||||
}else{
|
||||
s_putc(body, p->c);
|
||||
s_terminate(body);
|
||||
}
|
||||
if(p->white)
|
||||
s_append(body, s_to_c(p->white));
|
||||
cp = p->end+1;
|
||||
}
|
||||
s_append(body, "\n");
|
||||
}
|
||||
|
||||
if(*s_to_c(body) == 0){
|
||||
s_free(body);
|
||||
return;
|
||||
}
|
||||
|
||||
if(*cp != '\n')
|
||||
s_append(body, "\n");
|
||||
s_memappend(body, cp, s_len(mp->body) - (cp - s_to_c(mp->body)));
|
||||
s_terminate(body);
|
||||
|
||||
firstfield = 0;
|
||||
mp->size += s_len(body) - s_len(mp->body);
|
||||
s_free(mp->body);
|
||||
mp->body = body;
|
||||
}
|
||||
|
||||
/* read in a message, interpret the 'From' header */
|
||||
extern message *
|
||||
m_read(Biobuf *fp, int rmail, int interactive)
|
||||
{
|
||||
message *mp;
|
||||
Resub subexp[10];
|
||||
char *line;
|
||||
int first;
|
||||
int n;
|
||||
|
||||
mp = m_new();
|
||||
|
||||
/* parse From lines if remote */
|
||||
if (rmail) {
|
||||
/* get remote address */
|
||||
String *sender=s_new();
|
||||
|
||||
if (rfprog == 0)
|
||||
rfprog = regcomp(REMFROMRE);
|
||||
first = 1;
|
||||
while(s_read_line(fp, s_restart(mp->body)) != 0) {
|
||||
memset(subexp, 0, sizeof(subexp));
|
||||
if (regexec(rfprog, s_to_c(mp->body), subexp, 10) == 0){
|
||||
if(first == 0)
|
||||
break;
|
||||
if (fprog == 0)
|
||||
fprog = regcomp(FROMRE);
|
||||
memset(subexp, 0, sizeof(subexp));
|
||||
if(regexec(fprog, s_to_c(mp->body), subexp,10) == 0)
|
||||
break;
|
||||
s_restart(mp->body);
|
||||
append_match(subexp, s_restart(sender), SENDERMATCH);
|
||||
append_match(subexp, s_restart(mp->date), DATEMATCH);
|
||||
break;
|
||||
}
|
||||
append_match(subexp, s_restart(sender), REMSENDERMATCH);
|
||||
append_match(subexp, s_restart(mp->date), REMDATEMATCH);
|
||||
if(subexp[REMSYSMATCH].s.sp!=subexp[REMSYSMATCH].e.ep){
|
||||
append_match(subexp, mp->sender, REMSYSMATCH);
|
||||
s_append(mp->sender, "!");
|
||||
}
|
||||
first = 0;
|
||||
}
|
||||
s_append(mp->sender, s_to_c(sender));
|
||||
|
||||
s_free(sender);
|
||||
}
|
||||
if(*s_to_c(mp->sender)=='\0')
|
||||
default_from(mp);
|
||||
|
||||
/* if sender address is unreturnable, treat message as bulk mail */
|
||||
if(!returnable(s_to_c(mp->sender)))
|
||||
mp->bulk = 1;
|
||||
|
||||
/* get body */
|
||||
if(interactive && !rmail){
|
||||
/* user typing on terminal: terminator == '.' or EOF */
|
||||
for(;;) {
|
||||
line = s_read_line(fp, mp->body);
|
||||
if (line == 0)
|
||||
break;
|
||||
if (strcmp(".\n", line)==0) {
|
||||
mp->body->ptr -= 2;
|
||||
*mp->body->ptr = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
mp->size = mp->body->ptr - mp->body->base;
|
||||
} else {
|
||||
/*
|
||||
* read up to VMLIMIT bytes (more or less) into main memory.
|
||||
* if message is longer put the rest in a tmp file.
|
||||
*/
|
||||
mp->size = mp->body->ptr - mp->body->base;
|
||||
n = s_read(fp, mp->body, VMLIMIT);
|
||||
if(n < 0){
|
||||
perror("m_read");
|
||||
exit(1);
|
||||
}
|
||||
mp->size += n;
|
||||
if(n == VMLIMIT){
|
||||
if(m_read_to_file(fp, mp) < 0){
|
||||
perror("m_read");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* ignore 0 length messages from a terminal
|
||||
*/
|
||||
if (!rmail && mp->size == 0)
|
||||
return 0;
|
||||
|
||||
rfc822cruft(mp);
|
||||
|
||||
return mp;
|
||||
}
|
||||
|
||||
/* return a piece of message starting at `offset' */
|
||||
extern int
|
||||
m_get(message *mp, long offset, char **pp)
|
||||
{
|
||||
static char buf[4*1024];
|
||||
|
||||
/*
|
||||
* are we past eof?
|
||||
*/
|
||||
if(offset >= mp->size)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* are we in the virtual memory portion?
|
||||
*/
|
||||
if(offset < s_len(mp->body)){
|
||||
*pp = mp->body->base + offset;
|
||||
return mp->body->ptr - mp->body->base - offset;
|
||||
}
|
||||
|
||||
/*
|
||||
* read it from the temp file
|
||||
*/
|
||||
offset -= s_len(mp->body);
|
||||
if(mp->fd < 0)
|
||||
return -1;
|
||||
if(seek(mp->fd, offset, 0)<0)
|
||||
return -1;
|
||||
*pp = buf;
|
||||
return read(mp->fd, buf, sizeof buf);
|
||||
}
|
||||
|
||||
/* output the message body without ^From escapes */
|
||||
static int
|
||||
m_noescape(message *mp, Biobuf *fp)
|
||||
{
|
||||
long offset;
|
||||
int n;
|
||||
char *p;
|
||||
|
||||
for(offset = 0; offset < mp->size; offset += n){
|
||||
n = m_get(mp, offset, &p);
|
||||
if(n <= 0){
|
||||
Bflush(fp);
|
||||
return -1;
|
||||
}
|
||||
if(Bwrite(fp, p, n) < 0)
|
||||
return -1;
|
||||
}
|
||||
return Bflush(fp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Output the message body with '^From ' escapes.
|
||||
* Ensures that any line starting with a 'From ' gets a ' ' stuck
|
||||
* in front of it.
|
||||
*/
|
||||
static int
|
||||
m_escape(message *mp, Biobuf *fp)
|
||||
{
|
||||
char *p, *np;
|
||||
char *end;
|
||||
long offset;
|
||||
int m, n;
|
||||
char *start;
|
||||
|
||||
for(offset = 0; offset < mp->size; offset += n){
|
||||
n = m_get(mp, offset, &start);
|
||||
if(n < 0){
|
||||
Bflush(fp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
p = start;
|
||||
for(end = p+n; p < end; p += m){
|
||||
np = memchr(p, '\n', end-p);
|
||||
if(np == 0){
|
||||
Bwrite(fp, p, end-p);
|
||||
break;
|
||||
}
|
||||
m = np - p + 1;
|
||||
if(m > 5 && strncmp(p, "From ", 5) == 0)
|
||||
Bputc(fp, ' ');
|
||||
Bwrite(fp, p, m);
|
||||
}
|
||||
}
|
||||
Bflush(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
printfrom(message *mp, Biobuf *fp)
|
||||
{
|
||||
String *s;
|
||||
int rv;
|
||||
|
||||
if(!returnable(s_to_c(mp->sender)))
|
||||
return Bprint(fp, "From: Postmaster\n");
|
||||
|
||||
s = username(mp->sender);
|
||||
if(s) {
|
||||
s_append(s, " <");
|
||||
s_append(s, s_to_c(mp->sender));
|
||||
s_append(s, ">");
|
||||
} else {
|
||||
s = s_copy(s_to_c(mp->sender));
|
||||
}
|
||||
s = unescapespecial(s);
|
||||
rv = Bprint(fp, "From: %s\n", s_to_c(s));
|
||||
s_free(s);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static char *
|
||||
rewritezone(char *z)
|
||||
{
|
||||
int mindiff;
|
||||
char s;
|
||||
Tm *tm;
|
||||
static char x[7];
|
||||
|
||||
tm = localtime(time(0));
|
||||
mindiff = tm->tzoff/60;
|
||||
|
||||
/* if not in my timezone, don't change anything */
|
||||
if(strcmp(tm->zone, z) != 0)
|
||||
return z;
|
||||
|
||||
if(mindiff < 0){
|
||||
s = '-';
|
||||
mindiff = -mindiff;
|
||||
} else
|
||||
s = '+';
|
||||
|
||||
sprint(x, "%c%.2d%.2d", s, mindiff/60, mindiff%60);
|
||||
return x;
|
||||
}
|
||||
|
||||
int
|
||||
isutf8(String *s)
|
||||
{
|
||||
char *p;
|
||||
|
||||
for(p = s_to_c(s); *p; p++)
|
||||
if(*p&0x80)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
printutf8mime(Biobuf *b)
|
||||
{
|
||||
Bprint(b, "MIME-Version: 1.0\n");
|
||||
Bprint(b, "Content-Type: text/plain; charset=\"UTF-8\"\n");
|
||||
Bprint(b, "Content-Transfer-Encoding: 8bit\n");
|
||||
}
|
||||
|
||||
/* output a message */
|
||||
extern int
|
||||
m_print(message *mp, Biobuf *fp, char *remote, int mbox)
|
||||
{
|
||||
String *date, *sender;
|
||||
char *f[6];
|
||||
int n;
|
||||
|
||||
sender = unescapespecial(s_clone(mp->sender));
|
||||
|
||||
if (remote != 0){
|
||||
if(print_remote_header(fp,s_to_c(sender),s_to_c(mp->date),remote) < 0){
|
||||
s_free(sender);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if(print_header(fp, s_to_c(sender), s_to_c(mp->date)) < 0){
|
||||
s_free(sender);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
s_free(sender);
|
||||
if(!rmail && !mp->havedate){
|
||||
/* add a date: line Date: Sun, 19 Apr 1998 12:27:52 -0400 */
|
||||
date = s_copy(s_to_c(mp->date));
|
||||
n = getfields(s_to_c(date), f, 6, 1, " \t");
|
||||
if(n == 6)
|
||||
Bprint(fp, "Date: %s, %s %s %s %s %s\n", f[0], f[2], f[1],
|
||||
f[5], f[3], rewritezone(f[4]));
|
||||
}
|
||||
if(!rmail && !mp->havemime && isutf8(mp->body))
|
||||
printutf8mime(fp);
|
||||
if(mp->to){
|
||||
/* add the to: line */
|
||||
if (Bprint(fp, "%s\n", s_to_c(mp->to)) < 0)
|
||||
return -1;
|
||||
/* add the from: line */
|
||||
if (!mp->havefrom && printfrom(mp, fp) < 0)
|
||||
return -1;
|
||||
if(!mp->rfc822headers && *s_to_c(mp->body) != '\n')
|
||||
if (Bprint(fp, "\n") < 0)
|
||||
return -1;
|
||||
} else if(!rmail){
|
||||
/* add the from: line */
|
||||
if (!mp->havefrom && printfrom(mp, fp) < 0)
|
||||
return -1;
|
||||
if(!mp->rfc822headers && *s_to_c(mp->body) != '\n')
|
||||
if (Bprint(fp, "\n") < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!mbox)
|
||||
return m_noescape(mp, fp);
|
||||
return m_escape(mp, fp);
|
||||
}
|
||||
|
||||
/* print just the message body */
|
||||
extern int
|
||||
m_bprint(message *mp, Biobuf *fp)
|
||||
{
|
||||
return m_noescape(mp, fp);
|
||||
}
|
||||
52
src/cmd/upas/send/mkfile
Normal file
52
src/cmd/upas/send/mkfile
Normal file
@@ -0,0 +1,52 @@
|
||||
<$PLAN9/src/mkhdr
|
||||
|
||||
TARG=send\
|
||||
filter
|
||||
|
||||
UOFILES=message.$O\
|
||||
dest.$O\
|
||||
log.$O\
|
||||
skipequiv.$O\
|
||||
|
||||
OFILES=\
|
||||
$UOFILES\
|
||||
../smtp/rfc822.tab.$O\
|
||||
|
||||
SMOBJ=main.$O\
|
||||
bind.$O\
|
||||
rewrite.$O\
|
||||
local.$O\
|
||||
translate.$O\
|
||||
authorize.$O\
|
||||
gateway.$O\
|
||||
cat_mail.$O\
|
||||
|
||||
LIB=../common/libcommon.av\
|
||||
|
||||
HFILES=send.h\
|
||||
../common/common.h\
|
||||
../common/sys.h\
|
||||
|
||||
LIB=../common/libcommon.a\
|
||||
|
||||
BIN=$PLAN9/bin/upas
|
||||
UPDATE=\
|
||||
mkfile\
|
||||
$HFILES\
|
||||
${UOFILES:%.$O=%.c}\
|
||||
${SMOBJ:%.$O=%.c}\
|
||||
${TARG:%=%.c}\
|
||||
|
||||
<$PLAN9/src/mkmany
|
||||
CFLAGS=$CFLAGS -I../common
|
||||
|
||||
$O.send: $SMOBJ $OFILES
|
||||
$LD $LDFLAGS -o $target $prereq $LIB
|
||||
|
||||
message.$O: ../smtp/y.tab.h
|
||||
|
||||
../smtp/y.tab.h ../smtp/rfc822.tab.$O: ../smtp/rfc822.y
|
||||
# @{
|
||||
cd ../smtp
|
||||
mk rfc822.tab.$O
|
||||
# }
|
||||
36
src/cmd/upas/send/regtest.c
Normal file
36
src/cmd/upas/send/regtest.c
Normal file
@@ -0,0 +1,36 @@
|
||||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <regexp.h>
|
||||
#include <bio.h>
|
||||
|
||||
main(void)
|
||||
{
|
||||
char *re;
|
||||
char *line;
|
||||
Reprog *prog;
|
||||
char *cp;
|
||||
Biobuf in;
|
||||
|
||||
Binit(&in, 0, OREAD);
|
||||
print("re> ");
|
||||
while(re = Brdline(&in, '\n')){
|
||||
re[Blinelen(&in)-1] = 0;
|
||||
if(*re == 0)
|
||||
break;
|
||||
prog = regcomp(re);
|
||||
print("> ");
|
||||
while(line = Brdline(&in, '\n')){
|
||||
line[Blinelen(&in)-1] = 0;
|
||||
if(cp = strchr(line, '\n'))
|
||||
*cp = 0;
|
||||
if(*line == 0)
|
||||
break;
|
||||
if(regexec(prog, line, 0))
|
||||
print("yes\n");
|
||||
else
|
||||
print("no\n");
|
||||
print("> ");
|
||||
}
|
||||
print("re> ");
|
||||
}
|
||||
}
|
||||
315
src/cmd/upas/send/rewrite.c
Normal file
315
src/cmd/upas/send/rewrite.c
Normal file
@@ -0,0 +1,315 @@
|
||||
#include "common.h"
|
||||
#include "send.h"
|
||||
|
||||
extern int debug;
|
||||
|
||||
/*
|
||||
* Routines for dealing with the rewrite rules.
|
||||
*/
|
||||
|
||||
/* globals */
|
||||
typedef struct rule rule;
|
||||
|
||||
#define NSUBEXP 10
|
||||
struct rule {
|
||||
String *matchre; /* address match */
|
||||
String *repl1; /* first replacement String */
|
||||
String *repl2; /* second replacement String */
|
||||
d_status type; /* type of rule */
|
||||
Reprog *program;
|
||||
Resub subexp[NSUBEXP];
|
||||
rule *next;
|
||||
};
|
||||
static rule *rulep;
|
||||
static rule *rlastp;
|
||||
|
||||
/* predeclared */
|
||||
static String *substitute(String *, Resub *, message *);
|
||||
static rule *findrule(String *, int);
|
||||
|
||||
|
||||
/*
|
||||
* Get the next token from `line'. The symbol `\l' is replaced by
|
||||
* the name of the local system.
|
||||
*/
|
||||
extern String *
|
||||
rule_parse(String *line, char *system, int *backl)
|
||||
{
|
||||
String *token;
|
||||
String *expanded;
|
||||
char *cp;
|
||||
|
||||
token = s_parse(line, 0);
|
||||
if(token == 0)
|
||||
return(token);
|
||||
if(strchr(s_to_c(token), '\\')==0)
|
||||
return(token);
|
||||
expanded = s_new();
|
||||
for(cp = s_to_c(token); *cp; cp++) {
|
||||
if(*cp == '\\') switch(*++cp) {
|
||||
case 'l':
|
||||
s_append(expanded, system);
|
||||
*backl = 1;
|
||||
break;
|
||||
case '\\':
|
||||
s_putc(expanded, '\\');
|
||||
break;
|
||||
default:
|
||||
s_putc(expanded, '\\');
|
||||
s_putc(expanded, *cp);
|
||||
break;
|
||||
} else
|
||||
s_putc(expanded, *cp);
|
||||
}
|
||||
s_free(token);
|
||||
s_terminate(expanded);
|
||||
return(expanded);
|
||||
}
|
||||
|
||||
static int
|
||||
getrule(String *line, String *type, char *system)
|
||||
{
|
||||
rule *rp;
|
||||
String *re;
|
||||
int backl;
|
||||
|
||||
backl = 0;
|
||||
|
||||
/* get a rule */
|
||||
re = rule_parse(s_restart(line), system, &backl);
|
||||
if(re == 0)
|
||||
return 0;
|
||||
rp = (rule *)malloc(sizeof(rule));
|
||||
if(rp == 0) {
|
||||
perror("getrules:");
|
||||
exit(1);
|
||||
}
|
||||
rp->next = 0;
|
||||
s_tolower(re);
|
||||
rp->matchre = s_new();
|
||||
s_append(rp->matchre, s_to_c(re));
|
||||
s_restart(rp->matchre);
|
||||
s_free(re);
|
||||
s_parse(line, s_restart(type));
|
||||
rp->repl1 = rule_parse(line, system, &backl);
|
||||
rp->repl2 = rule_parse(line, system, &backl);
|
||||
rp->program = 0;
|
||||
if(strcmp(s_to_c(type), "|") == 0)
|
||||
rp->type = d_pipe;
|
||||
else if(strcmp(s_to_c(type), ">>") == 0)
|
||||
rp->type = d_cat;
|
||||
else if(strcmp(s_to_c(type), "alias") == 0)
|
||||
rp->type = d_alias;
|
||||
else if(strcmp(s_to_c(type), "translate") == 0)
|
||||
rp->type = d_translate;
|
||||
else if(strcmp(s_to_c(type), "auth") == 0)
|
||||
rp->type = d_auth;
|
||||
else {
|
||||
s_free(rp->matchre);
|
||||
s_free(rp->repl1);
|
||||
s_free(rp->repl2);
|
||||
free((char *)rp);
|
||||
fprint(2,"illegal rewrite rule: %s\n", s_to_c(line));
|
||||
return 0;
|
||||
}
|
||||
if(rulep == 0)
|
||||
rulep = rlastp = rp;
|
||||
else
|
||||
rlastp = rlastp->next = rp;
|
||||
return backl;
|
||||
}
|
||||
|
||||
/*
|
||||
* rules are of the form:
|
||||
* <reg exp> <String> <repl exp> [<repl exp>]
|
||||
*/
|
||||
extern int
|
||||
getrules(void)
|
||||
{
|
||||
Biobuf *rfp;
|
||||
String *line;
|
||||
String *type;
|
||||
String *file;
|
||||
|
||||
file = abspath("rewrite", unsharp(UPASLIB), (String *)0);
|
||||
rfp = sysopen(s_to_c(file), "r", 0);
|
||||
if(rfp == 0) {
|
||||
rulep = 0;
|
||||
return -1;
|
||||
}
|
||||
rlastp = 0;
|
||||
line = s_new();
|
||||
type = s_new();
|
||||
while(s_getline(rfp, s_restart(line)))
|
||||
if(getrule(line, type, thissys) && altthissys)
|
||||
getrule(s_restart(line), type, altthissys);
|
||||
s_free(type);
|
||||
s_free(line);
|
||||
s_free(file);
|
||||
sysclose(rfp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* look up a matching rule */
|
||||
static rule *
|
||||
findrule(String *addrp, int authorized)
|
||||
{
|
||||
rule *rp;
|
||||
static rule defaultrule;
|
||||
|
||||
if(rulep == 0)
|
||||
return &defaultrule;
|
||||
for (rp = rulep; rp != 0; rp = rp->next) {
|
||||
if(rp->type==d_auth && authorized)
|
||||
continue;
|
||||
if(rp->program == 0)
|
||||
rp->program = regcomp(rp->matchre->base);
|
||||
if(rp->program == 0)
|
||||
continue;
|
||||
memset(rp->subexp, 0, sizeof(rp->subexp));
|
||||
if(debug)
|
||||
print("matching %s aginst %s\n", s_to_c(addrp), rp->matchre->base);
|
||||
if(regexec(rp->program, s_to_c(addrp), rp->subexp, NSUBEXP))
|
||||
if(s_to_c(addrp) == rp->subexp[0].s.sp)
|
||||
if((s_to_c(addrp) + strlen(s_to_c(addrp))) == rp->subexp[0].e.ep)
|
||||
return rp;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Transforms the address into a command.
|
||||
* Returns: -1 ifaddress not matched by reules
|
||||
* 0 ifaddress matched and ok to forward
|
||||
* 1 ifaddress matched and not ok to forward
|
||||
*/
|
||||
extern int
|
||||
rewrite(dest *dp, message *mp)
|
||||
{
|
||||
rule *rp; /* rewriting rule */
|
||||
String *lower; /* lower case version of destination */
|
||||
|
||||
/*
|
||||
* Rewrite the address. Matching is case insensitive.
|
||||
*/
|
||||
lower = s_clone(dp->addr);
|
||||
s_tolower(s_restart(lower));
|
||||
rp = findrule(lower, dp->authorized);
|
||||
if(rp == 0){
|
||||
s_free(lower);
|
||||
return -1;
|
||||
}
|
||||
strcpy(s_to_c(lower), s_to_c(dp->addr));
|
||||
dp->repl1 = substitute(rp->repl1, rp->subexp, mp);
|
||||
dp->repl2 = substitute(rp->repl2, rp->subexp, mp);
|
||||
dp->status = rp->type;
|
||||
if(debug){
|
||||
print("\t->");
|
||||
if(dp->repl1)
|
||||
print("%s", s_to_c(dp->repl1));
|
||||
if(dp->repl2)
|
||||
print("%s", s_to_c(dp->repl2));
|
||||
print("\n");
|
||||
}
|
||||
s_free(lower);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static String *
|
||||
substitute(String *source, Resub *subexp, message *mp)
|
||||
{
|
||||
int i;
|
||||
char *s;
|
||||
char *sp;
|
||||
String *stp;
|
||||
|
||||
if(source == 0)
|
||||
return 0;
|
||||
sp = s_to_c(source);
|
||||
|
||||
/* someplace to put it */
|
||||
stp = s_new();
|
||||
|
||||
/* do the substitution */
|
||||
while (*sp != '\0') {
|
||||
if(*sp == '\\') {
|
||||
switch (*++sp) {
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
i = *sp-'0';
|
||||
if(subexp[i].s.sp != 0)
|
||||
for (s = subexp[i].s.sp;
|
||||
s < subexp[i].e.ep;
|
||||
s++)
|
||||
s_putc(stp, *s);
|
||||
break;
|
||||
case '\\':
|
||||
s_putc(stp, '\\');
|
||||
break;
|
||||
case '\0':
|
||||
sp--;
|
||||
break;
|
||||
case 's':
|
||||
for(s = s_to_c(mp->replyaddr); *s; s++)
|
||||
s_putc(stp, *s);
|
||||
break;
|
||||
case 'p':
|
||||
if(mp->bulk)
|
||||
s = "bulk";
|
||||
else
|
||||
s = "normal";
|
||||
for(;*s; s++)
|
||||
s_putc(stp, *s);
|
||||
break;
|
||||
default:
|
||||
s_putc(stp, *sp);
|
||||
break;
|
||||
}
|
||||
} else if(*sp == '&') {
|
||||
if(subexp[0].s.sp != 0)
|
||||
for (s = subexp[0].s.sp;
|
||||
s < subexp[0].e.ep; s++)
|
||||
s_putc(stp, *s);
|
||||
} else
|
||||
s_putc(stp, *sp);
|
||||
sp++;
|
||||
}
|
||||
s_terminate(stp);
|
||||
|
||||
return s_restart(stp);
|
||||
}
|
||||
|
||||
extern void
|
||||
regerror(char* s)
|
||||
{
|
||||
fprint(2, "rewrite: %s\n", s);
|
||||
}
|
||||
|
||||
extern void
|
||||
dumprules(void)
|
||||
{
|
||||
rule *rp;
|
||||
|
||||
for (rp = rulep; rp != 0; rp = rp->next) {
|
||||
fprint(2, "'%s'", rp->matchre->base);
|
||||
switch (rp->type) {
|
||||
case d_pipe:
|
||||
fprint(2, " |");
|
||||
break;
|
||||
case d_cat:
|
||||
fprint(2, " >>");
|
||||
break;
|
||||
case d_alias:
|
||||
fprint(2, " alias");
|
||||
break;
|
||||
case d_translate:
|
||||
fprint(2, " translate");
|
||||
break;
|
||||
default:
|
||||
fprint(2, " UNKNOWN");
|
||||
break;
|
||||
}
|
||||
fprint(2, " '%s'", rp->repl1 ? rp->repl1->base:"...");
|
||||
fprint(2, " '%s'\n", rp->repl2 ? rp->repl2->base:"...");
|
||||
}
|
||||
}
|
||||
|
||||
108
src/cmd/upas/send/send.h
Normal file
108
src/cmd/upas/send/send.h
Normal file
@@ -0,0 +1,108 @@
|
||||
#define MAXSAME 16
|
||||
#define MAXSAMECHAR 1024
|
||||
|
||||
/* status of a destination*/
|
||||
typedef enum {
|
||||
d_undefined, /* address has not been matched*/
|
||||
d_pipe, /* repl1|repl2 == delivery command, rep*/
|
||||
d_cat, /* repl1 == mail file */
|
||||
d_translate, /* repl1 == translation command*/
|
||||
d_alias, /* repl1 == translation*/
|
||||
d_auth, /* repl1 == command to authorize*/
|
||||
d_syntax, /* addr contains illegal characters*/
|
||||
d_unknown, /* addr does not match a rewrite rule*/
|
||||
d_loop, /* addressing loop*/
|
||||
d_eloop, /* external addressing loop*/
|
||||
d_noforward, /* forwarding not allowed*/
|
||||
d_badmbox, /* mailbox badly formatted*/
|
||||
d_resource, /* ran out of something we needed*/
|
||||
d_pipeto, /* pipe to from a mailbox*/
|
||||
} d_status;
|
||||
|
||||
/* a destination*/
|
||||
typedef struct dest dest;
|
||||
struct dest {
|
||||
dest *next; /* for chaining*/
|
||||
dest *same; /* dests with same cmd*/
|
||||
dest *parent; /* destination we're a translation of*/
|
||||
String *addr; /* destination address*/
|
||||
String *repl1; /* substitution field 1*/
|
||||
String *repl2; /* substitution field 2*/
|
||||
int pstat; /* process status*/
|
||||
d_status status; /* delivery status*/
|
||||
int authorized; /* non-zero if we have been authorized*/
|
||||
int nsame; /* number of same dests chained to this entry*/
|
||||
int nchar; /* number of characters in the command*/
|
||||
};
|
||||
|
||||
typedef struct message message;
|
||||
struct message {
|
||||
String *sender;
|
||||
String *replyaddr;
|
||||
String *date;
|
||||
String *body;
|
||||
String *tmp; /* name of temp file */
|
||||
String *to;
|
||||
int size;
|
||||
int fd; /* if >= 0, the file the message is stored in*/
|
||||
char haveto;
|
||||
String *havefrom;
|
||||
String *havesender;
|
||||
String *havereplyto;
|
||||
char havedate;
|
||||
char havemime;
|
||||
String *havesubject;
|
||||
char bulk; /* if Precedence: Bulk in header */
|
||||
char rfc822headers;
|
||||
int received; /* number of received lines */
|
||||
char *boundary; /* bondary marker for attachments */
|
||||
};
|
||||
|
||||
/*
|
||||
* exported variables
|
||||
*/
|
||||
extern int rmail;
|
||||
extern int onatty;
|
||||
extern char *thissys, *altthissys;
|
||||
extern int xflg;
|
||||
extern int nflg;
|
||||
extern int tflg;
|
||||
extern int debug;
|
||||
extern int nosummary;
|
||||
|
||||
/*
|
||||
* exported procedures
|
||||
*/
|
||||
extern void authorize(dest*);
|
||||
extern int cat_mail(dest*, message*);
|
||||
extern dest *up_bind(dest*, message*, int);
|
||||
extern int ok_to_forward(char*);
|
||||
extern int lookup(char*, char*, Biobuf**, char*, Biobuf**);
|
||||
extern dest *d_new(String*);
|
||||
extern void d_free(dest*);
|
||||
extern dest *d_rm(dest**);
|
||||
extern void d_insert(dest**, dest*);
|
||||
extern dest *d_rm_same(dest**);
|
||||
extern void d_same_insert(dest**, dest*);
|
||||
extern String *d_to(dest*);
|
||||
extern dest *s_to_dest(String*, dest*);
|
||||
extern void gateway(message*);
|
||||
extern dest *expand_local(dest*);
|
||||
extern void logdelivery(dest*, char*, message*);
|
||||
extern void loglist(dest*, message*, char*);
|
||||
extern void logrefusal(dest*, message*, char*);
|
||||
extern int default_from(message*);
|
||||
extern message *m_new(void);
|
||||
extern void m_free(message*);
|
||||
extern message *m_read(Biobuf*, int, int);
|
||||
extern int m_get(message*, long, char**);
|
||||
extern int m_print(message*, Biobuf*, char*, int);
|
||||
extern int m_bprint(message*, Biobuf*);
|
||||
extern String *rule_parse(String*, char*, int*);
|
||||
extern int getrules(void);
|
||||
extern int rewrite(dest*, message*);
|
||||
extern void dumprules(void);
|
||||
extern void regerror(char*);
|
||||
extern dest *translate(dest*);
|
||||
extern char* skipequiv(char*);
|
||||
extern int refuse(dest*, message*, char*, int, int);
|
||||
93
src/cmd/upas/send/skipequiv.c
Normal file
93
src/cmd/upas/send/skipequiv.c
Normal file
@@ -0,0 +1,93 @@
|
||||
#include "common.h"
|
||||
#include "send.h"
|
||||
|
||||
#undef isspace
|
||||
#define isspace(c) ((c)==' ' || (c)=='\t' || (c)=='\n')
|
||||
|
||||
/*
|
||||
* skip past all systems in equivlist
|
||||
*/
|
||||
extern char*
|
||||
skipequiv(char *base)
|
||||
{
|
||||
char *sp;
|
||||
static Biobuf *fp;
|
||||
|
||||
while(*base){
|
||||
sp = strchr(base, '!');
|
||||
if(sp==0)
|
||||
break;
|
||||
*sp = '\0';
|
||||
if(lookup(base, "equivlist", &fp, 0, 0)==1){
|
||||
/* found or us, forget this system */
|
||||
*sp='!';
|
||||
base=sp+1;
|
||||
} else {
|
||||
/* no files or system is not found, and not us */
|
||||
*sp='!';
|
||||
break;
|
||||
}
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
static int
|
||||
okfile(char *cp, Biobuf *fp)
|
||||
{
|
||||
char *buf;
|
||||
int len;
|
||||
char *bp, *ep;
|
||||
int c;
|
||||
|
||||
len = strlen(cp);
|
||||
Bseek(fp, 0, 0);
|
||||
|
||||
/* one iteration per system name in the file */
|
||||
while(buf = Brdline(fp, '\n')) {
|
||||
ep = &buf[Blinelen(fp)];
|
||||
for(bp=buf; bp < ep;){
|
||||
while(isspace(*bp) || *bp==',')
|
||||
bp++;
|
||||
if(strncmp(bp, cp, len) == 0) {
|
||||
c = *(bp+len);
|
||||
if(isspace(c) || c==',')
|
||||
return 1;
|
||||
}
|
||||
while(bp < ep && (!isspace(*bp)) && *bp!=',')
|
||||
bp++;
|
||||
}
|
||||
}
|
||||
|
||||
/* didn't find it, prohibit forwarding */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* return 1 if name found in one of the files
|
||||
* 0 if name not found in one of the files
|
||||
* -1 if neither file exists
|
||||
*/
|
||||
extern int
|
||||
lookup(char *cp, char *local, Biobuf **lfpp, char *global, Biobuf **gfpp)
|
||||
{
|
||||
static String *file = 0;
|
||||
|
||||
if (local) {
|
||||
if (file == 0)
|
||||
file = s_new();
|
||||
abspath(local, UPASLIB, s_restart(file));
|
||||
if (*lfpp != 0 || (*lfpp = sysopen(s_to_c(file), "r", 0)) != 0) {
|
||||
if (okfile(cp, *lfpp))
|
||||
return 1;
|
||||
} else
|
||||
local = 0;
|
||||
}
|
||||
if (global) {
|
||||
abspath(global, UPASLIB, s_restart(file));
|
||||
if (*gfpp != 0 || (*gfpp = sysopen(s_to_c(file), "r", 0)) != 0) {
|
||||
if (okfile(cp, *gfpp))
|
||||
return 1;
|
||||
} else
|
||||
global = 0;
|
||||
}
|
||||
return (local || global)? 0 : -1;
|
||||
}
|
||||
43
src/cmd/upas/send/translate.c
Normal file
43
src/cmd/upas/send/translate.c
Normal file
@@ -0,0 +1,43 @@
|
||||
#include "common.h"
|
||||
#include "send.h"
|
||||
|
||||
/* pipe an address through a command to translate it */
|
||||
extern dest *
|
||||
translate(dest *dp)
|
||||
{
|
||||
process *pp;
|
||||
String *line;
|
||||
dest *rv;
|
||||
char *cp;
|
||||
int n;
|
||||
|
||||
pp = proc_start(s_to_c(dp->repl1), (stream *)0, outstream(), outstream(), 1, 0);
|
||||
if (pp == 0) {
|
||||
dp->status = d_resource;
|
||||
return 0;
|
||||
}
|
||||
line = s_new();
|
||||
for(;;) {
|
||||
cp = Brdline(pp->std[1]->fp, '\n');
|
||||
if(cp == 0)
|
||||
break;
|
||||
if(strncmp(cp, "_nosummary_", 11) == 0){
|
||||
nosummary = 1;
|
||||
continue;
|
||||
}
|
||||
n = Blinelen(pp->std[1]->fp);
|
||||
cp[n-1] = ' ';
|
||||
s_nappend(line, cp, n);
|
||||
}
|
||||
rv = s_to_dest(s_restart(line), dp);
|
||||
s_restart(line);
|
||||
while(s_read_line(pp->std[2]->fp, line))
|
||||
;
|
||||
if ((dp->pstat = proc_wait(pp)) != 0) {
|
||||
dp->repl2 = line;
|
||||
rv = 0;
|
||||
} else
|
||||
s_free(line);
|
||||
proc_free(pp);
|
||||
return rv;
|
||||
}
|
||||
29
src/cmd/upas/send/tryit
Normal file
29
src/cmd/upas/send/tryit
Normal file
@@ -0,0 +1,29 @@
|
||||
#!/bin/sh
|
||||
set -x
|
||||
|
||||
> /usr/spool/mail/test.local
|
||||
echo "Forward to test.local" > /usr/spool/mail/test.forward
|
||||
echo "Pipe to cat > /tmp/test.mail" > /usr/spool/mail/test.pipe
|
||||
chmod 644 /usr/spool/mail/test.pipe
|
||||
|
||||
mail test.local <<EOF
|
||||
mailed to test.local
|
||||
EOF
|
||||
mail test.forward <<EOF
|
||||
mailed to test.forward
|
||||
EOF
|
||||
mail test.pipe <<EOF
|
||||
mailed to test.pipe
|
||||
EOF
|
||||
mail dutoit!bowell!test.local <<EOF
|
||||
mailed to dutoit!bowell!test.local
|
||||
EOF
|
||||
|
||||
sleep 60
|
||||
|
||||
ls -l /usr/spool/mail/test.*
|
||||
ls -l /tmp/test.mail
|
||||
echo ">>>test.local<<<"
|
||||
cat /usr/spool/mail/test.local
|
||||
echo ">>>test.mail<<<"
|
||||
cat /tmp/test.mail
|
||||
274
src/cmd/upas/smtp/greylist.c
Normal file
274
src/cmd/upas/smtp/greylist.c
Normal file
@@ -0,0 +1,274 @@
|
||||
#include "common.h"
|
||||
#include "smtpd.h"
|
||||
#include "smtp.h"
|
||||
#include <ctype.h>
|
||||
#include <ip.h>
|
||||
#include <ndb.h>
|
||||
|
||||
typedef struct {
|
||||
int existed; /* these two are distinct to cope with errors */
|
||||
int created;
|
||||
int noperm;
|
||||
long mtime; /* mod time, iff it already existed */
|
||||
} Greysts;
|
||||
|
||||
/*
|
||||
* There's a bit of a problem with yahoo; they apparently have a vast
|
||||
* pool of machines that all run the same queue(s), so a 451 retry can
|
||||
* come from a different IP address for many, many retries, and it can
|
||||
* take ~5 hours for the same IP to call us back. Various other goofballs,
|
||||
* notably the IEEE, try to send mail just before 9 AM, then refuse to try
|
||||
* again until after 5 PM. Doh!
|
||||
*/
|
||||
enum {
|
||||
Nonspammax = 14*60*60, /* must call back within this time if real */
|
||||
};
|
||||
static char whitelist[] = "/mail/lib/whitelist";
|
||||
|
||||
/*
|
||||
* matches ip addresses or subnets in whitelist against nci->rsys.
|
||||
* ignores comments and blank lines in /mail/lib/whitelist.
|
||||
*/
|
||||
static int
|
||||
onwhitelist(void)
|
||||
{
|
||||
int lnlen;
|
||||
char *line, *parse;
|
||||
char input[128];
|
||||
uchar ip[IPaddrlen], ipmasked[IPaddrlen];
|
||||
uchar mask4[IPaddrlen], addr4[IPaddrlen];
|
||||
uchar mask[IPaddrlen], addr[IPaddrlen], addrmasked[IPaddrlen];
|
||||
Biobuf *wl;
|
||||
static int beenhere;
|
||||
static allzero[IPaddrlen];
|
||||
|
||||
if (!beenhere) {
|
||||
beenhere = 1;
|
||||
fmtinstall('I', eipfmt);
|
||||
}
|
||||
|
||||
parseip(ip, nci->rsys);
|
||||
wl = Bopen(whitelist, OREAD);
|
||||
if (wl == nil)
|
||||
return 1;
|
||||
while ((line = Brdline(wl, '\n')) != nil) {
|
||||
if (line[0] == '#' || line[0] == '\n')
|
||||
continue;
|
||||
lnlen = Blinelen(wl);
|
||||
line[lnlen-1] = '\0'; /* clobber newline */
|
||||
|
||||
/* default mask is /32 (v4) or /128 (v6) for bare IP */
|
||||
parse = line;
|
||||
if (strchr(line, '/') == nil) {
|
||||
strncpy(input, line, sizeof input - 5);
|
||||
if (strchr(line, '.') != nil)
|
||||
strcat(input, "/32");
|
||||
else
|
||||
strcat(input, "/128");
|
||||
parse = input;
|
||||
}
|
||||
/* sorry, dave; where's parsecidr for v4 or v6? */
|
||||
v4parsecidr(addr4, mask4, parse);
|
||||
v4tov6(addr, addr4);
|
||||
v4tov6(mask, mask4);
|
||||
|
||||
maskip(addr, mask, addrmasked);
|
||||
maskip(ip, mask, ipmasked);
|
||||
if (memcmp(ipmasked, addrmasked, IPaddrlen) == 0)
|
||||
break;
|
||||
}
|
||||
Bterm(wl);
|
||||
return line != nil;
|
||||
}
|
||||
|
||||
static int mkdirs(char *);
|
||||
|
||||
/*
|
||||
* if any directories leading up to path don't exist, create them.
|
||||
* modifies but restores path.
|
||||
*/
|
||||
static int
|
||||
mkpdirs(char *path)
|
||||
{
|
||||
int rv = 0;
|
||||
char *sl = strrchr(path, '/');
|
||||
|
||||
if (sl != nil) {
|
||||
*sl = '\0';
|
||||
rv = mkdirs(path);
|
||||
*sl = '/';
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*
|
||||
* if path or any directories leading up to it don't exist, create them.
|
||||
* modifies but restores path.
|
||||
*/
|
||||
static int
|
||||
mkdirs(char *path)
|
||||
{
|
||||
int fd;
|
||||
|
||||
if (access(path, AEXIST) >= 0)
|
||||
return 0;
|
||||
|
||||
/* make presumed-missing intermediate directories */
|
||||
if (mkpdirs(path) < 0)
|
||||
return -1;
|
||||
|
||||
/* make final directory */
|
||||
fd = create(path, OREAD, 0777|DMDIR);
|
||||
if (fd < 0)
|
||||
/*
|
||||
* we may have lost a race; if the directory now exists,
|
||||
* it's okay.
|
||||
*/
|
||||
return access(path, AEXIST) < 0? -1: 0;
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long
|
||||
getmtime(char *file)
|
||||
{
|
||||
long mtime = -1;
|
||||
Dir *ds = dirstat(file);
|
||||
|
||||
if (ds != nil) {
|
||||
mtime = ds->mtime;
|
||||
free(ds);
|
||||
}
|
||||
return mtime;
|
||||
}
|
||||
|
||||
static void
|
||||
tryaddgrey(char *file, Greysts *gsp)
|
||||
{
|
||||
int fd = create(file, OWRITE|OEXCL, 0444|DMEXCL);
|
||||
|
||||
gsp->created = (fd >= 0);
|
||||
if (fd >= 0) {
|
||||
close(fd);
|
||||
gsp->existed = 0; /* just created; couldn't have existed */
|
||||
} else {
|
||||
/*
|
||||
* why couldn't we create file? it must have existed
|
||||
* (or we were denied perm on parent dir.).
|
||||
* if it existed, fill in gsp->mtime; otherwise
|
||||
* make presumed-missing intermediate directories.
|
||||
*/
|
||||
gsp->existed = access(file, AEXIST) >= 0;
|
||||
if (gsp->existed)
|
||||
gsp->mtime = getmtime(file);
|
||||
else if (mkpdirs(file) < 0)
|
||||
gsp->noperm = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
addgreylist(char *file, Greysts *gsp)
|
||||
{
|
||||
tryaddgrey(file, gsp);
|
||||
if (!gsp->created && !gsp->existed && !gsp->noperm)
|
||||
/* retry the greylist entry with parent dirs created */
|
||||
tryaddgrey(file, gsp);
|
||||
}
|
||||
|
||||
static int
|
||||
recentcall(Greysts *gsp)
|
||||
{
|
||||
long delay = time(0) - gsp->mtime;
|
||||
|
||||
if (!gsp->existed)
|
||||
return 0;
|
||||
/* reject immediate call-back; spammers are doing that now */
|
||||
return delay >= 30 && delay <= Nonspammax;
|
||||
}
|
||||
|
||||
/*
|
||||
* policy: if (caller-IP, my-IP, rcpt) is not on the greylist,
|
||||
* reject this message as "451 temporary failure". if the caller is real,
|
||||
* he'll retry soon, otherwise he's a spammer.
|
||||
* at the first rejection, create a greylist entry for (my-ip, caller-ip,
|
||||
* rcpt, time), where time is the file's mtime. if they call back and there's
|
||||
* already a greylist entry, and it's within the allowed interval,
|
||||
* add their IP to the append-only whitelist.
|
||||
*
|
||||
* greylist files can be removed at will; at worst they'll cause a few
|
||||
* extra retries.
|
||||
*/
|
||||
|
||||
static int
|
||||
isrcptrecent(char *rcpt)
|
||||
{
|
||||
char *user;
|
||||
char file[256];
|
||||
Greysts gs;
|
||||
Greysts *gsp = &gs;
|
||||
|
||||
if (rcpt[0] == '\0' || strchr(rcpt, '/') != nil ||
|
||||
strcmp(rcpt, ".") == 0 || strcmp(rcpt, "..") == 0)
|
||||
return 0;
|
||||
|
||||
/* shorten names to fit pre-fossil or pre-9p2000 file servers */
|
||||
user = strrchr(rcpt, '!');
|
||||
if (user == nil)
|
||||
user = rcpt;
|
||||
else
|
||||
user++;
|
||||
|
||||
/* check & try to update the grey list entry */
|
||||
snprint(file, sizeof file, "/mail/grey/%s/%s/%s",
|
||||
nci->lsys, nci->rsys, user);
|
||||
memset(gsp, 0, sizeof *gsp);
|
||||
addgreylist(file, gsp);
|
||||
|
||||
/* if on greylist already and prior call was recent, add to whitelist */
|
||||
if (gsp->existed && recentcall(gsp)) {
|
||||
syslog(0, "smtpd",
|
||||
"%s/%s was grey; adding IP to white", nci->rsys, rcpt);
|
||||
return 1;
|
||||
} else if (gsp->existed)
|
||||
syslog(0, "smtpd", "call for %s/%s was seconds ago or long ago",
|
||||
nci->rsys, rcpt);
|
||||
else
|
||||
syslog(0, "smtpd", "no call registered for %s/%s; registering",
|
||||
nci->rsys, rcpt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
vfysenderhostok(void)
|
||||
{
|
||||
char *fqdn;
|
||||
int recent = 0;
|
||||
Link *l;
|
||||
|
||||
if (onwhitelist())
|
||||
return;
|
||||
|
||||
for (l = rcvers.first; l; l = l->next)
|
||||
if (isrcptrecent(s_to_c(l->p)))
|
||||
recent = 1;
|
||||
|
||||
/* if on greylist already and prior call was recent, add to whitelist */
|
||||
if (recent) {
|
||||
int fd = create(whitelist, OWRITE, 0666|DMAPPEND);
|
||||
|
||||
if (fd >= 0) {
|
||||
seek(fd, 0, 2); /* paranoia */
|
||||
if ((fqdn = csgetvalue(nil, "ip", nci->rsys, "dom", nil)) != nil)
|
||||
fprint(fd, "# %s\n%s\n\n", fqdn, nci->rsys);
|
||||
else
|
||||
fprint(fd, "# unknown\n%s\n\n", nci->rsys);
|
||||
close(fd);
|
||||
}
|
||||
} else {
|
||||
syslog(0, "smtpd",
|
||||
"no recent call from %s for a rcpt; rejecting with temporary failure",
|
||||
nci->rsys);
|
||||
reply("451 please try again soon from the same IP.\r\n");
|
||||
exits("no recent call for a rcpt");
|
||||
}
|
||||
}
|
||||
54
src/cmd/upas/smtp/mkfile
Normal file
54
src/cmd/upas/smtp/mkfile
Normal file
@@ -0,0 +1,54 @@
|
||||
<$PLAN9/src/mkhdr
|
||||
|
||||
TARG = # smtpd\
|
||||
smtp\
|
||||
|
||||
OFILES=
|
||||
|
||||
LIB=../common/libcommon.a\
|
||||
$PLAN9/lib/libthread.a # why do i have to explicitly put this?
|
||||
|
||||
HFILES=../common/common.h\
|
||||
../common/sys.h\
|
||||
smtpd.h\
|
||||
smtp.h\
|
||||
|
||||
BIN=$PLAN9/bin/upas
|
||||
UPDATE=\
|
||||
greylist.c\
|
||||
mkfile\
|
||||
mxdial.c\
|
||||
rfc822.y\
|
||||
rmtdns.c\
|
||||
smtpd.y\
|
||||
spam.c\
|
||||
$HFILES\
|
||||
${OFILES:%.$O=%.c}\
|
||||
${TARG:%=%.c}\
|
||||
|
||||
<$PLAN9/src/mkmany
|
||||
CFLAGS=$CFLAGS -I../common -D'SPOOL="/mail"'
|
||||
|
||||
$O.smtpd: smtpd.tab.$O rmtdns.$O spam.$O rfc822.tab.$O greylist.$O
|
||||
$O.smtp: rfc822.tab.$O mxdial.$O
|
||||
|
||||
smtpd.$O: smtpd.h
|
||||
|
||||
smtp.$O to.$O: smtp.h
|
||||
|
||||
smtpd.tab.c: smtpd.y smtpd.h
|
||||
yacc -o xxx smtpd.y
|
||||
sed 's/yy/zz/g' < xxx > $target
|
||||
rm xxx
|
||||
|
||||
rfc822.tab.c: rfc822.y smtp.h
|
||||
9 yacc -d -o $target rfc822.y
|
||||
|
||||
clean:V:
|
||||
rm -f *.[$OS] [$OS].$TARG smtpd.tab.c rfc822.tab.c y.tab.? y.debug $TARG
|
||||
|
||||
../common/libcommon.a$O:
|
||||
@{
|
||||
cd ../common
|
||||
mk
|
||||
}
|
||||
333
src/cmd/upas/smtp/mxdial.c
Normal file
333
src/cmd/upas/smtp/mxdial.c
Normal file
@@ -0,0 +1,333 @@
|
||||
#include "common.h"
|
||||
#include <ndb.h>
|
||||
#include "smtp.h" /* to publish dial_string_parse */
|
||||
|
||||
enum
|
||||
{
|
||||
Nmx= 16,
|
||||
Maxstring= 256,
|
||||
};
|
||||
|
||||
typedef struct Mx Mx;
|
||||
struct Mx
|
||||
{
|
||||
char host[256];
|
||||
char ip[24];
|
||||
int pref;
|
||||
};
|
||||
static Mx mx[Nmx];
|
||||
|
||||
Ndb *db;
|
||||
extern int debug;
|
||||
|
||||
static int mxlookup(DS*, char*);
|
||||
static int mxlookup1(DS*, char*);
|
||||
static int compar(void*, void*);
|
||||
static int callmx(DS*, char*, char*);
|
||||
static void expand_meta(DS *ds);
|
||||
extern int cistrcmp(char*, char*);
|
||||
|
||||
int
|
||||
mxdial(char *addr, char *ddomain, char *gdomain)
|
||||
{
|
||||
int fd;
|
||||
DS ds;
|
||||
char err[Errlen];
|
||||
|
||||
addr = netmkaddr(addr, 0, "smtp");
|
||||
dial_string_parse(addr, &ds);
|
||||
|
||||
/* try connecting to destination or any of it's mail routers */
|
||||
fd = callmx(&ds, addr, ddomain);
|
||||
|
||||
/* try our mail gateway */
|
||||
rerrstr(err, sizeof(err));
|
||||
if(fd < 0 && gdomain && strstr(err, "can't translate") != 0) {
|
||||
fprint(2,"dialing %s\n",gdomain);
|
||||
fd = dial(netmkaddr(gdomain, 0, "smtp"), 0, 0, 0);
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
/*
|
||||
* take an address and return all the mx entries for it,
|
||||
* most preferred first
|
||||
*/
|
||||
static int
|
||||
callmx(DS *ds, char *dest, char *domain)
|
||||
{
|
||||
int fd, i, nmx;
|
||||
char addr[Maxstring];
|
||||
|
||||
/* get a list of mx entries */
|
||||
nmx = mxlookup(ds, domain);
|
||||
if(nmx < 0){
|
||||
/* dns isn't working, don't just dial */
|
||||
return -1;
|
||||
}
|
||||
if(nmx == 0){
|
||||
if(debug)
|
||||
fprint(2, "mxlookup returns nothing\n");
|
||||
return dial(dest, 0, 0, 0);
|
||||
}
|
||||
|
||||
/* refuse to honor loopback addresses given by dns */
|
||||
for(i = 0; i < nmx; i++){
|
||||
if(strcmp(mx[i].ip, "127.0.0.1") == 0){
|
||||
if(debug)
|
||||
fprint(2, "mxlookup returns loopback\n");
|
||||
werrstr("illegal: domain lists 127.0.0.1 as mail server");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* sort by preference */
|
||||
if(nmx > 1)
|
||||
qsort(mx, nmx, sizeof(Mx), compar);
|
||||
|
||||
/* dial each one in turn */
|
||||
for(i = 0; i < nmx; i++){
|
||||
snprint(addr, sizeof(addr), "%s/%s!%s!%s", ds->netdir, ds->proto,
|
||||
mx[i].host, ds->service);
|
||||
if(debug)
|
||||
fprint(2, "mxdial trying %s\n", addr);
|
||||
fd = dial(addr, 0, 0, 0);
|
||||
if(fd >= 0)
|
||||
return fd;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* call the dns process and have it try to resolve the mx request
|
||||
*
|
||||
* this routine knows about the firewall and tries inside and outside
|
||||
* dns's seperately.
|
||||
*/
|
||||
static int
|
||||
mxlookup(DS *ds, char *domain)
|
||||
{
|
||||
int n;
|
||||
|
||||
/* just in case we find no domain name */
|
||||
strcpy(domain, ds->host);
|
||||
|
||||
if(ds->netdir){
|
||||
n = mxlookup1(ds, domain);
|
||||
} else {
|
||||
ds->netdir = "/net";
|
||||
n = mxlookup1(ds, domain);
|
||||
if(n == 0) {
|
||||
ds->netdir = "/net.alt";
|
||||
n = mxlookup1(ds, domain);
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static int
|
||||
mxlookup1(DS *ds, char *domain)
|
||||
{
|
||||
char buf[1024];
|
||||
char dnsname[Maxstring];
|
||||
char *fields[4];
|
||||
int i, n, fd, nmx;
|
||||
|
||||
snprint(dnsname, sizeof dnsname, "%s/dns", ds->netdir);
|
||||
|
||||
fd = open(dnsname, ORDWR);
|
||||
if(fd < 0)
|
||||
return 0;
|
||||
|
||||
nmx = 0;
|
||||
snprint(buf, sizeof(buf), "%s mx", ds->host);
|
||||
if(debug)
|
||||
fprint(2, "sending %s '%s'\n", dnsname, buf);
|
||||
n = write(fd, buf, strlen(buf));
|
||||
if(n < 0){
|
||||
rerrstr(buf, sizeof buf);
|
||||
if(debug)
|
||||
fprint(2, "dns: %s\n", buf);
|
||||
if(strstr(buf, "dns failure")){
|
||||
/* if dns fails for the mx lookup, we have to stop */
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* get any mx entries
|
||||
*/
|
||||
seek(fd, 0, 0);
|
||||
while(nmx < Nmx && (n = read(fd, buf, sizeof(buf)-1)) > 0){
|
||||
buf[n] = 0;
|
||||
if(debug)
|
||||
fprint(2, "dns mx: %s\n", buf);
|
||||
n = getfields(buf, fields, 4, 1, " \t");
|
||||
if(n < 4)
|
||||
continue;
|
||||
|
||||
if(strchr(domain, '.') == 0)
|
||||
strcpy(domain, fields[0]);
|
||||
|
||||
strncpy(mx[nmx].host, fields[3], sizeof(mx[n].host)-1);
|
||||
mx[nmx].pref = atoi(fields[2]);
|
||||
nmx++;
|
||||
}
|
||||
if(debug)
|
||||
fprint(2, "dns mx; got %d entries\n", nmx);
|
||||
}
|
||||
|
||||
/*
|
||||
* no mx record? try name itself.
|
||||
*/
|
||||
/*
|
||||
* BUG? If domain has no dots, then we used to look up ds->host
|
||||
* but return domain instead of ds->host in the list. Now we return
|
||||
* ds->host. What will this break?
|
||||
*/
|
||||
if(nmx == 0){
|
||||
mx[0].pref = 1;
|
||||
strncpy(mx[0].host, ds->host, sizeof(mx[0].host));
|
||||
nmx++;
|
||||
}
|
||||
|
||||
/*
|
||||
* look up all ip addresses
|
||||
*/
|
||||
for(i = 0; i < nmx; i++){
|
||||
seek(fd, 0, 0);
|
||||
snprint(buf, sizeof buf, "%s ip", mx[i].host);
|
||||
mx[i].ip[0] = 0;
|
||||
if(write(fd, buf, strlen(buf)) < 0)
|
||||
goto no;
|
||||
seek(fd, 0, 0);
|
||||
if((n = read(fd, buf, sizeof buf-1)) < 0)
|
||||
goto no;
|
||||
buf[n] = 0;
|
||||
if(getfields(buf, fields, 4, 1, " \t") < 3)
|
||||
goto no;
|
||||
strncpy(mx[i].ip, fields[2], sizeof(mx[i].ip)-1);
|
||||
continue;
|
||||
|
||||
no:
|
||||
/* remove mx[i] and go around again */
|
||||
nmx--;
|
||||
mx[i] = mx[nmx];
|
||||
i--;
|
||||
}
|
||||
return nmx;
|
||||
}
|
||||
|
||||
static int
|
||||
compar(void *a, void *b)
|
||||
{
|
||||
return ((Mx*)a)->pref - ((Mx*)b)->pref;
|
||||
}
|
||||
|
||||
/* break up an address to its component parts */
|
||||
void
|
||||
dial_string_parse(char *str, DS *ds)
|
||||
{
|
||||
char *p, *p2;
|
||||
|
||||
strncpy(ds->buf, str, sizeof(ds->buf));
|
||||
ds->buf[sizeof(ds->buf)-1] = 0;
|
||||
|
||||
p = strchr(ds->buf, '!');
|
||||
if(p == 0) {
|
||||
ds->netdir = 0;
|
||||
ds->proto = "net";
|
||||
ds->host = ds->buf;
|
||||
} else {
|
||||
if(*ds->buf != '/'){
|
||||
ds->netdir = 0;
|
||||
ds->proto = ds->buf;
|
||||
} else {
|
||||
for(p2 = p; *p2 != '/'; p2--)
|
||||
;
|
||||
*p2++ = 0;
|
||||
ds->netdir = ds->buf;
|
||||
ds->proto = p2;
|
||||
}
|
||||
*p = 0;
|
||||
ds->host = p + 1;
|
||||
}
|
||||
ds->service = strchr(ds->host, '!');
|
||||
if(ds->service)
|
||||
*ds->service++ = 0;
|
||||
if(*ds->host == '$')
|
||||
expand_meta(ds);
|
||||
}
|
||||
|
||||
#if 0 /* jpc */
|
||||
static void
|
||||
expand_meta(DS *ds)
|
||||
{
|
||||
char buf[128], cs[128], *net, *p;
|
||||
int fd, n;
|
||||
|
||||
net = ds->netdir;
|
||||
if(!net)
|
||||
net = "/net";
|
||||
|
||||
if(debug)
|
||||
fprint(2, "expanding %s!%s\n", net, ds->host);
|
||||
snprint(cs, sizeof(cs), "%s/cs", net);
|
||||
if((fd = open(cs, ORDWR)) == -1){
|
||||
if(debug)
|
||||
fprint(2, "open %s: %r\n", cs);
|
||||
syslog(0, "smtp", "cannot open %s: %r", cs);
|
||||
return;
|
||||
}
|
||||
|
||||
snprint(buf, sizeof(buf), "!ipinfo %s", ds->host+1); // +1 to skip $
|
||||
if(write(fd, buf, strlen(buf)) <= 0){
|
||||
if(debug)
|
||||
fprint(2, "write %s: %r\n", cs);
|
||||
syslog(0, "smtp", "%s to %s - write failed: %r", buf, cs);
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
seek(fd, 0, 0);
|
||||
if((n = read(fd, ds->expand, sizeof(ds->expand)-1)) < 0){
|
||||
if(debug)
|
||||
fprint(2, "read %s: %r\n", cs);
|
||||
syslog(0, "smtp", "%s - read failed: %r", cs);
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
ds->expand[n] = 0;
|
||||
if((p = strchr(ds->expand, '=')) == nil){
|
||||
if(debug)
|
||||
fprint(2, "response %s: %s\n", cs, ds->expand);
|
||||
syslog(0, "smtp", "%q from %s - bad response: %r", ds->expand, cs);
|
||||
return;
|
||||
}
|
||||
ds->host = p+1;
|
||||
|
||||
/* take only first one returned (quasi-bug) */
|
||||
if((p = strchr(ds->host, ' ')) != nil)
|
||||
*p = 0;
|
||||
}
|
||||
#endif /* jpc */
|
||||
|
||||
static void
|
||||
expand_meta(DS *ds)
|
||||
{
|
||||
Ndb *db;
|
||||
Ndbs s;
|
||||
char *sys, *smtpserver;
|
||||
|
||||
sys = sysname();
|
||||
db = ndbopen(unsharp("#9/ndb/local"));
|
||||
fprint(2,"%s",ds->host);
|
||||
smtpserver = ndbgetvalue(db, &s, "sys", sys, "smtp", nil);
|
||||
snprint(ds->host,128,"%s",smtpserver);
|
||||
fprint(2," exanded to %s\n",ds->host);
|
||||
|
||||
}
|
||||
1260
src/cmd/upas/smtp/rfc822.tab.c
Normal file
1260
src/cmd/upas/smtp/rfc822.tab.c
Normal file
File diff suppressed because it is too large
Load Diff
98
src/cmd/upas/smtp/rfc822.tab.h
Normal file
98
src/cmd/upas/smtp/rfc822.tab.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/* A Bison parser, made by GNU Bison 2.0. */
|
||||
|
||||
/* Skeleton parser for Yacc-like parsing with Bison,
|
||||
Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* As a special exception, when this file is copied by Bison into a
|
||||
Bison output file, you may use that output file without restriction.
|
||||
This special exception was added by the Free Software Foundation
|
||||
in version 1.24 of Bison. */
|
||||
|
||||
/* Tokens. */
|
||||
#ifndef YYTOKENTYPE
|
||||
# define YYTOKENTYPE
|
||||
/* Put the tokens into the symbol table, so that GDB and other debuggers
|
||||
know about them. */
|
||||
enum yytokentype {
|
||||
WORD = 258,
|
||||
DATE = 259,
|
||||
RESENT_DATE = 260,
|
||||
RETURN_PATH = 261,
|
||||
FROM = 262,
|
||||
SENDER = 263,
|
||||
REPLY_TO = 264,
|
||||
RESENT_FROM = 265,
|
||||
RESENT_SENDER = 266,
|
||||
RESENT_REPLY_TO = 267,
|
||||
SUBJECT = 268,
|
||||
TO = 269,
|
||||
CC = 270,
|
||||
BCC = 271,
|
||||
RESENT_TO = 272,
|
||||
RESENT_CC = 273,
|
||||
RESENT_BCC = 274,
|
||||
REMOTE = 275,
|
||||
PRECEDENCE = 276,
|
||||
MIMEVERSION = 277,
|
||||
CONTENTTYPE = 278,
|
||||
MESSAGEID = 279,
|
||||
RECEIVED = 280,
|
||||
MAILER = 281,
|
||||
BADTOKEN = 282
|
||||
};
|
||||
#endif
|
||||
#define WORD 258
|
||||
#define DATE 259
|
||||
#define RESENT_DATE 260
|
||||
#define RETURN_PATH 261
|
||||
#define FROM 262
|
||||
#define SENDER 263
|
||||
#define REPLY_TO 264
|
||||
#define RESENT_FROM 265
|
||||
#define RESENT_SENDER 266
|
||||
#define RESENT_REPLY_TO 267
|
||||
#define SUBJECT 268
|
||||
#define TO 269
|
||||
#define CC 270
|
||||
#define BCC 271
|
||||
#define RESENT_TO 272
|
||||
#define RESENT_CC 273
|
||||
#define RESENT_BCC 274
|
||||
#define REMOTE 275
|
||||
#define PRECEDENCE 276
|
||||
#define MIMEVERSION 277
|
||||
#define CONTENTTYPE 278
|
||||
#define MESSAGEID 279
|
||||
#define RECEIVED 280
|
||||
#define MAILER 281
|
||||
#define BADTOKEN 282
|
||||
|
||||
|
||||
|
||||
|
||||
#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED)
|
||||
typedef int YYSTYPE;
|
||||
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
|
||||
# define YYSTYPE_IS_DECLARED 1
|
||||
# define YYSTYPE_IS_TRIVIAL 1
|
||||
#endif
|
||||
|
||||
extern YYSTYPE yylval;
|
||||
|
||||
|
||||
|
||||
778
src/cmd/upas/smtp/rfc822.y
Normal file
778
src/cmd/upas/smtp/rfc822.y
Normal file
@@ -0,0 +1,778 @@
|
||||
%{
|
||||
#include "common.h"
|
||||
#include "smtp.h"
|
||||
#include <ctype.h>
|
||||
|
||||
char *yylp; /* next character to be lex'd */
|
||||
int yydone; /* tell yylex to give up */
|
||||
char *yybuffer; /* first parsed character */
|
||||
char *yyend; /* end of buffer to be parsed */
|
||||
Node *root;
|
||||
Field *firstfield;
|
||||
Field *lastfield;
|
||||
Node *usender;
|
||||
Node *usys;
|
||||
Node *udate;
|
||||
char *startfield, *endfield;
|
||||
int originator;
|
||||
int destination;
|
||||
int date;
|
||||
int received;
|
||||
int messageid;
|
||||
%}
|
||||
|
||||
%term WORD
|
||||
%term DATE
|
||||
%term RESENT_DATE
|
||||
%term RETURN_PATH
|
||||
%term FROM
|
||||
%term SENDER
|
||||
%term REPLY_TO
|
||||
%term RESENT_FROM
|
||||
%term RESENT_SENDER
|
||||
%term RESENT_REPLY_TO
|
||||
%term SUBJECT
|
||||
%term TO
|
||||
%term CC
|
||||
%term BCC
|
||||
%term RESENT_TO
|
||||
%term RESENT_CC
|
||||
%term RESENT_BCC
|
||||
%term REMOTE
|
||||
%term PRECEDENCE
|
||||
%term MIMEVERSION
|
||||
%term CONTENTTYPE
|
||||
%term MESSAGEID
|
||||
%term RECEIVED
|
||||
%term MAILER
|
||||
%term BADTOKEN
|
||||
%start msg
|
||||
%%
|
||||
|
||||
msg : fields
|
||||
| unixfrom '\n' fields
|
||||
;
|
||||
fields : '\n'
|
||||
{ yydone = 1; }
|
||||
| field '\n'
|
||||
| field '\n' fields
|
||||
;
|
||||
field : dates
|
||||
{ date = 1; }
|
||||
| originator
|
||||
{ originator = 1; }
|
||||
| destination
|
||||
{ destination = 1; }
|
||||
| subject
|
||||
| optional
|
||||
| ignored
|
||||
| received
|
||||
| precedence
|
||||
| error '\n' field
|
||||
;
|
||||
unixfrom : FROM route_addr unix_date_time REMOTE FROM word
|
||||
{ freenode($1); freenode($4); freenode($5);
|
||||
usender = $2; udate = $3; usys = $6;
|
||||
}
|
||||
;
|
||||
originator : REPLY_TO ':' address_list
|
||||
{ newfield(link3($1, $2, $3), 1); }
|
||||
| RETURN_PATH ':' route_addr
|
||||
{ newfield(link3($1, $2, $3), 1); }
|
||||
| FROM ':' mailbox_list
|
||||
{ newfield(link3($1, $2, $3), 1); }
|
||||
| SENDER ':' mailbox
|
||||
{ newfield(link3($1, $2, $3), 1); }
|
||||
| RESENT_REPLY_TO ':' address_list
|
||||
{ newfield(link3($1, $2, $3), 1); }
|
||||
| RESENT_SENDER ':' mailbox
|
||||
{ newfield(link3($1, $2, $3), 1); }
|
||||
| RESENT_FROM ':' mailbox
|
||||
{ newfield(link3($1, $2, $3), 1); }
|
||||
;
|
||||
dates : DATE ':' date_time
|
||||
{ newfield(link3($1, $2, $3), 0); }
|
||||
| RESENT_DATE ':' date_time
|
||||
{ newfield(link3($1, $2, $3), 0); }
|
||||
;
|
||||
destination : TO ':'
|
||||
{ newfield(link2($1, $2), 0); }
|
||||
| TO ':' address_list
|
||||
{ newfield(link3($1, $2, $3), 0); }
|
||||
| RESENT_TO ':'
|
||||
{ newfield(link2($1, $2), 0); }
|
||||
| RESENT_TO ':' address_list
|
||||
{ newfield(link3($1, $2, $3), 0); }
|
||||
| CC ':'
|
||||
{ newfield(link2($1, $2), 0); }
|
||||
| CC ':' address_list
|
||||
{ newfield(link3($1, $2, $3), 0); }
|
||||
| RESENT_CC ':'
|
||||
{ newfield(link2($1, $2), 0); }
|
||||
| RESENT_CC ':' address_list
|
||||
{ newfield(link3($1, $2, $3), 0); }
|
||||
| BCC ':'
|
||||
{ newfield(link2($1, $2), 0); }
|
||||
| BCC ':' address_list
|
||||
{ newfield(link3($1, $2, $3), 0); }
|
||||
| RESENT_BCC ':'
|
||||
{ newfield(link2($1, $2), 0); }
|
||||
| RESENT_BCC ':' address_list
|
||||
{ newfield(link3($1, $2, $3), 0); }
|
||||
;
|
||||
subject : SUBJECT ':' things
|
||||
{ newfield(link3($1, $2, $3), 0); }
|
||||
| SUBJECT ':'
|
||||
{ newfield(link2($1, $2), 0); }
|
||||
;
|
||||
received : RECEIVED ':' things
|
||||
{ newfield(link3($1, $2, $3), 0); received++; }
|
||||
| RECEIVED ':'
|
||||
{ newfield(link2($1, $2), 0); received++; }
|
||||
;
|
||||
precedence : PRECEDENCE ':' things
|
||||
{ newfield(link3($1, $2, $3), 0); }
|
||||
| PRECEDENCE ':'
|
||||
{ newfield(link2($1, $2), 0); }
|
||||
;
|
||||
ignored : ignoredhdr ':' things
|
||||
{ newfield(link3($1, $2, $3), 0); }
|
||||
| ignoredhdr ':'
|
||||
{ newfield(link2($1, $2), 0); }
|
||||
;
|
||||
ignoredhdr : MIMEVERSION | CONTENTTYPE | MESSAGEID { messageid = 1; } | MAILER
|
||||
;
|
||||
optional : fieldwords ':' things
|
||||
{ /* hack to allow same lex for field names and the rest */
|
||||
if(badfieldname($1)){
|
||||
freenode($1);
|
||||
freenode($2);
|
||||
freenode($3);
|
||||
return 1;
|
||||
}
|
||||
newfield(link3($1, $2, $3), 0);
|
||||
}
|
||||
| fieldwords ':'
|
||||
{ /* hack to allow same lex for field names and the rest */
|
||||
if(badfieldname($1)){
|
||||
freenode($1);
|
||||
freenode($2);
|
||||
return 1;
|
||||
}
|
||||
newfield(link2($1, $2), 0);
|
||||
}
|
||||
;
|
||||
address_list : address
|
||||
| address_list ',' address
|
||||
{ $$ = link3($1, $2, $3); }
|
||||
;
|
||||
address : mailbox
|
||||
| group
|
||||
;
|
||||
group : phrase ':' address_list ';'
|
||||
{ $$ = link2($1, link3($2, $3, $4)); }
|
||||
| phrase ':' ';'
|
||||
{ $$ = link3($1, $2, $3); }
|
||||
;
|
||||
mailbox_list : mailbox
|
||||
| mailbox_list ',' mailbox
|
||||
{ $$ = link3($1, $2, $3); }
|
||||
;
|
||||
mailbox : route_addr
|
||||
| phrase brak_addr
|
||||
{ $$ = link2($1, $2); }
|
||||
| brak_addr
|
||||
;
|
||||
brak_addr : '<' route_addr '>'
|
||||
{ $$ = link3($1, $2, $3); }
|
||||
| '<' '>'
|
||||
{ $$ = nobody($2); freenode($1); }
|
||||
;
|
||||
route_addr : route ':' at_addr
|
||||
{ $$ = address(concat($1, concat($2, $3))); }
|
||||
| addr_spec
|
||||
;
|
||||
route : '@' domain
|
||||
{ $$ = concat($1, $2); }
|
||||
| route ',' '@' domain
|
||||
{ $$ = concat($1, concat($2, concat($3, $4))); }
|
||||
;
|
||||
addr_spec : local_part
|
||||
{ $$ = address($1); }
|
||||
| at_addr
|
||||
;
|
||||
at_addr : local_part '@' domain
|
||||
{ $$ = address(concat($1, concat($2, $3)));}
|
||||
| at_addr '@' domain
|
||||
{ $$ = address(concat($1, concat($2, $3)));}
|
||||
;
|
||||
local_part : word
|
||||
;
|
||||
domain : word
|
||||
;
|
||||
phrase : word
|
||||
| phrase word
|
||||
{ $$ = link2($1, $2); }
|
||||
;
|
||||
things : thing
|
||||
| things thing
|
||||
{ $$ = link2($1, $2); }
|
||||
;
|
||||
thing : word | '<' | '>' | '@' | ':' | ';' | ','
|
||||
;
|
||||
date_time : things
|
||||
;
|
||||
unix_date_time : word word word unix_time word word
|
||||
{ $$ = link3($1, $3, link3($2, $6, link2($4, $5))); }
|
||||
;
|
||||
unix_time : word
|
||||
| unix_time ':' word
|
||||
{ $$ = link3($1, $2, $3); }
|
||||
;
|
||||
word : WORD | DATE | RESENT_DATE | RETURN_PATH | FROM | SENDER
|
||||
| REPLY_TO | RESENT_FROM | RESENT_SENDER | RESENT_REPLY_TO
|
||||
| TO | CC | BCC | RESENT_TO | RESENT_CC | RESENT_BCC | REMOTE | SUBJECT
|
||||
| PRECEDENCE | MIMEVERSION | CONTENTTYPE | MESSAGEID | RECEIVED | MAILER
|
||||
;
|
||||
fieldwords : fieldword
|
||||
| WORD
|
||||
| fieldwords fieldword
|
||||
{ $$ = link2($1, $2); }
|
||||
| fieldwords word
|
||||
{ $$ = link2($1, $2); }
|
||||
;
|
||||
fieldword : '<' | '>' | '@' | ';' | ','
|
||||
;
|
||||
%%
|
||||
|
||||
/*
|
||||
* Initialize the parsing. Done once for each header field.
|
||||
*/
|
||||
void
|
||||
yyinit(char *p, int len)
|
||||
{
|
||||
yybuffer = p;
|
||||
yylp = p;
|
||||
yyend = p + len;
|
||||
firstfield = lastfield = 0;
|
||||
received = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* keywords identifying header fields we care about
|
||||
*/
|
||||
typedef struct Keyword Keyword;
|
||||
struct Keyword {
|
||||
char *rep;
|
||||
int val;
|
||||
};
|
||||
|
||||
/* field names that we need to recognize */
|
||||
Keyword key[] = {
|
||||
{ "date", DATE },
|
||||
{ "resent-date", RESENT_DATE },
|
||||
{ "return_path", RETURN_PATH },
|
||||
{ "from", FROM },
|
||||
{ "sender", SENDER },
|
||||
{ "reply-to", REPLY_TO },
|
||||
{ "resent-from", RESENT_FROM },
|
||||
{ "resent-sender", RESENT_SENDER },
|
||||
{ "resent-reply-to", RESENT_REPLY_TO },
|
||||
{ "to", TO },
|
||||
{ "cc", CC },
|
||||
{ "bcc", BCC },
|
||||
{ "resent-to", RESENT_TO },
|
||||
{ "resent-cc", RESENT_CC },
|
||||
{ "resent-bcc", RESENT_BCC },
|
||||
{ "remote", REMOTE },
|
||||
{ "subject", SUBJECT },
|
||||
{ "precedence", PRECEDENCE },
|
||||
{ "mime-version", MIMEVERSION },
|
||||
{ "content-type", CONTENTTYPE },
|
||||
{ "message-id", MESSAGEID },
|
||||
{ "received", RECEIVED },
|
||||
{ "mailer", MAILER },
|
||||
{ "who-the-hell-cares", WORD }
|
||||
};
|
||||
|
||||
/*
|
||||
* Lexical analysis for an rfc822 header field. Continuation lines
|
||||
* are handled in yywhite() when skipping over white space.
|
||||
*
|
||||
*/
|
||||
int
|
||||
yylex(void)
|
||||
{
|
||||
String *t;
|
||||
int quoting;
|
||||
int escaping;
|
||||
char *start;
|
||||
Keyword *kp;
|
||||
int c, d;
|
||||
|
||||
/* print("lexing\n"); /**/
|
||||
if(yylp >= yyend)
|
||||
return 0;
|
||||
if(yydone)
|
||||
return 0;
|
||||
|
||||
quoting = escaping = 0;
|
||||
start = yylp;
|
||||
yylval = malloc(sizeof(Node));
|
||||
yylval->white = yylval->s = 0;
|
||||
yylval->next = 0;
|
||||
yylval->addr = 0;
|
||||
yylval->start = yylp;
|
||||
for(t = 0; yylp < yyend; yylp++){
|
||||
c = *yylp & 0xff;
|
||||
|
||||
/* dump nulls, they can't be in header */
|
||||
if(c == 0)
|
||||
continue;
|
||||
|
||||
if(escaping) {
|
||||
escaping = 0;
|
||||
} else if(quoting) {
|
||||
switch(c){
|
||||
case '\\':
|
||||
escaping = 1;
|
||||
break;
|
||||
case '\n':
|
||||
d = (*(yylp+1))&0xff;
|
||||
if(d != ' ' && d != '\t'){
|
||||
quoting = 0;
|
||||
yylp--;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case '"':
|
||||
quoting = 0;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch(c){
|
||||
case '\\':
|
||||
escaping = 1;
|
||||
break;
|
||||
case '(':
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\r':
|
||||
goto out;
|
||||
case '\n':
|
||||
if(yylp == start){
|
||||
yylp++;
|
||||
/* print("lex(c %c)\n", c); /**/
|
||||
yylval->end = yylp;
|
||||
return yylval->c = c;
|
||||
}
|
||||
goto out;
|
||||
case '@':
|
||||
case '>':
|
||||
case '<':
|
||||
case ':':
|
||||
case ',':
|
||||
case ';':
|
||||
if(yylp == start){
|
||||
yylp++;
|
||||
yylval->white = yywhite();
|
||||
/* print("lex(c %c)\n", c); /**/
|
||||
yylval->end = yylp;
|
||||
return yylval->c = c;
|
||||
}
|
||||
goto out;
|
||||
case '"':
|
||||
quoting = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(t == 0)
|
||||
t = s_new();
|
||||
s_putc(t, c);
|
||||
}
|
||||
out:
|
||||
yylval->white = yywhite();
|
||||
if(t) {
|
||||
s_terminate(t);
|
||||
} else /* message begins with white-space! */
|
||||
return yylval->c = '\n';
|
||||
yylval->s = t;
|
||||
for(kp = key; kp->val != WORD; kp++)
|
||||
if(cistrcmp(s_to_c(t), kp->rep)==0)
|
||||
break;
|
||||
/* print("lex(%d) %s\n", kp->val-WORD, s_to_c(t)); /**/
|
||||
yylval->end = yylp;
|
||||
return yylval->c = kp->val;
|
||||
}
|
||||
|
||||
void
|
||||
yyerror(char *x)
|
||||
{
|
||||
USED(x);
|
||||
|
||||
/*fprint(2, "parse err: %s\n", x);/**/
|
||||
}
|
||||
|
||||
/*
|
||||
* parse white space and comments
|
||||
*/
|
||||
String *
|
||||
yywhite(void)
|
||||
{
|
||||
String *w;
|
||||
int clevel;
|
||||
int c;
|
||||
int escaping;
|
||||
|
||||
escaping = clevel = 0;
|
||||
for(w = 0; yylp < yyend; yylp++){
|
||||
c = *yylp & 0xff;
|
||||
|
||||
/* dump nulls, they can't be in header */
|
||||
if(c == 0)
|
||||
continue;
|
||||
|
||||
if(escaping){
|
||||
escaping = 0;
|
||||
} else if(clevel) {
|
||||
switch(c){
|
||||
case '\n':
|
||||
/*
|
||||
* look for multiline fields
|
||||
*/
|
||||
if(*(yylp+1)==' ' || *(yylp+1)=='\t')
|
||||
break;
|
||||
else
|
||||
goto out;
|
||||
case '\\':
|
||||
escaping = 1;
|
||||
break;
|
||||
case '(':
|
||||
clevel++;
|
||||
break;
|
||||
case ')':
|
||||
clevel--;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch(c){
|
||||
case '\\':
|
||||
escaping = 1;
|
||||
break;
|
||||
case '(':
|
||||
clevel++;
|
||||
break;
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\r':
|
||||
break;
|
||||
case '\n':
|
||||
/*
|
||||
* look for multiline fields
|
||||
*/
|
||||
if(*(yylp+1)==' ' || *(yylp+1)=='\t')
|
||||
break;
|
||||
else
|
||||
goto out;
|
||||
default:
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if(w == 0)
|
||||
w = s_new();
|
||||
s_putc(w, c);
|
||||
}
|
||||
out:
|
||||
if(w)
|
||||
s_terminate(w);
|
||||
return w;
|
||||
}
|
||||
|
||||
/*
|
||||
* link two parsed entries together
|
||||
*/
|
||||
Node*
|
||||
link2(Node *p1, Node *p2)
|
||||
{
|
||||
Node *p;
|
||||
|
||||
for(p = p1; p->next; p = p->next)
|
||||
;
|
||||
p->next = p2;
|
||||
return p1;
|
||||
}
|
||||
|
||||
/*
|
||||
* link three parsed entries together
|
||||
*/
|
||||
Node*
|
||||
link3(Node *p1, Node *p2, Node *p3)
|
||||
{
|
||||
Node *p;
|
||||
|
||||
for(p = p2; p->next; p = p->next)
|
||||
;
|
||||
p->next = p3;
|
||||
|
||||
for(p = p1; p->next; p = p->next)
|
||||
;
|
||||
p->next = p2;
|
||||
|
||||
return p1;
|
||||
}
|
||||
|
||||
/*
|
||||
* make a:b, move all white space after both
|
||||
*/
|
||||
Node*
|
||||
colon(Node *p1, Node *p2)
|
||||
{
|
||||
if(p1->white){
|
||||
if(p2->white)
|
||||
s_append(p1->white, s_to_c(p2->white));
|
||||
} else {
|
||||
p1->white = p2->white;
|
||||
p2->white = 0;
|
||||
}
|
||||
|
||||
s_append(p1->s, ":");
|
||||
if(p2->s)
|
||||
s_append(p1->s, s_to_c(p2->s));
|
||||
|
||||
if(p1->end < p2->end)
|
||||
p1->end = p2->end;
|
||||
freenode(p2);
|
||||
return p1;
|
||||
}
|
||||
|
||||
/*
|
||||
* concatenate two fields, move all white space after both
|
||||
*/
|
||||
Node*
|
||||
concat(Node *p1, Node *p2)
|
||||
{
|
||||
char buf[2];
|
||||
|
||||
if(p1->white){
|
||||
if(p2->white)
|
||||
s_append(p1->white, s_to_c(p2->white));
|
||||
} else {
|
||||
p1->white = p2->white;
|
||||
p2->white = 0;
|
||||
}
|
||||
|
||||
if(p1->s == nil){
|
||||
buf[0] = p1->c;
|
||||
buf[1] = 0;
|
||||
p1->s = s_new();
|
||||
s_append(p1->s, buf);
|
||||
}
|
||||
|
||||
if(p2->s)
|
||||
s_append(p1->s, s_to_c(p2->s));
|
||||
else {
|
||||
buf[0] = p2->c;
|
||||
buf[1] = 0;
|
||||
s_append(p1->s, buf);
|
||||
}
|
||||
|
||||
if(p1->end < p2->end)
|
||||
p1->end = p2->end;
|
||||
freenode(p2);
|
||||
return p1;
|
||||
}
|
||||
|
||||
/*
|
||||
* look for disallowed chars in the field name
|
||||
*/
|
||||
int
|
||||
badfieldname(Node *p)
|
||||
{
|
||||
for(; p; p = p->next){
|
||||
/* field name can't contain white space */
|
||||
if(p->white && p->next)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* mark as an address
|
||||
*/
|
||||
Node *
|
||||
address(Node *p)
|
||||
{
|
||||
p->addr = 1;
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
* case independent string compare
|
||||
*/
|
||||
int
|
||||
cistrcmp(char *s1, char *s2)
|
||||
{
|
||||
int c1, c2;
|
||||
|
||||
for(; *s1; s1++, s2++){
|
||||
c1 = isupper(*s1) ? tolower(*s1) : *s1;
|
||||
c2 = isupper(*s2) ? tolower(*s2) : *s2;
|
||||
if (c1 != c2)
|
||||
return -1;
|
||||
}
|
||||
return *s2;
|
||||
}
|
||||
|
||||
/*
|
||||
* free a node
|
||||
*/
|
||||
void
|
||||
freenode(Node *p)
|
||||
{
|
||||
Node *tp;
|
||||
|
||||
while(p){
|
||||
tp = p->next;
|
||||
if(p->s)
|
||||
s_free(p->s);
|
||||
if(p->white)
|
||||
s_free(p->white);
|
||||
free(p);
|
||||
p = tp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* an anonymous user
|
||||
*/
|
||||
Node*
|
||||
nobody(Node *p)
|
||||
{
|
||||
if(p->s)
|
||||
s_free(p->s);
|
||||
p->s = s_copy("pOsTmAsTeR");
|
||||
p->addr = 1;
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
* add anything that was dropped because of a parse error
|
||||
*/
|
||||
void
|
||||
missing(Node *p)
|
||||
{
|
||||
Node *np;
|
||||
char *start, *end;
|
||||
Field *f;
|
||||
String *s;
|
||||
|
||||
start = yybuffer;
|
||||
if(lastfield != nil){
|
||||
for(np = lastfield->node; np; np = np->next)
|
||||
start = np->end+1;
|
||||
}
|
||||
|
||||
end = p->start-1;
|
||||
|
||||
if(end <= start)
|
||||
return;
|
||||
|
||||
if(strncmp(start, "From ", 5) == 0)
|
||||
return;
|
||||
|
||||
np = malloc(sizeof(Node));
|
||||
np->start = start;
|
||||
np->end = end;
|
||||
np->white = nil;
|
||||
s = s_copy("BadHeader: ");
|
||||
np->s = s_nappend(s, start, end-start);
|
||||
np->next = nil;
|
||||
|
||||
f = malloc(sizeof(Field));
|
||||
f->next = 0;
|
||||
f->node = np;
|
||||
f->source = 0;
|
||||
if(firstfield)
|
||||
lastfield->next = f;
|
||||
else
|
||||
firstfield = f;
|
||||
lastfield = f;
|
||||
}
|
||||
|
||||
/*
|
||||
* create a new field
|
||||
*/
|
||||
void
|
||||
newfield(Node *p, int source)
|
||||
{
|
||||
Field *f;
|
||||
|
||||
missing(p);
|
||||
|
||||
f = malloc(sizeof(Field));
|
||||
f->next = 0;
|
||||
f->node = p;
|
||||
f->source = source;
|
||||
if(firstfield)
|
||||
lastfield->next = f;
|
||||
else
|
||||
firstfield = f;
|
||||
lastfield = f;
|
||||
endfield = startfield;
|
||||
startfield = yylp;
|
||||
}
|
||||
|
||||
/*
|
||||
* fee a list of fields
|
||||
*/
|
||||
void
|
||||
freefield(Field *f)
|
||||
{
|
||||
Field *tf;
|
||||
|
||||
while(f){
|
||||
tf = f->next;
|
||||
freenode(f->node);
|
||||
free(f);
|
||||
f = tf;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* add some white space to a node
|
||||
*/
|
||||
Node*
|
||||
whiten(Node *p)
|
||||
{
|
||||
Node *tp;
|
||||
|
||||
for(tp = p; tp->next; tp = tp->next)
|
||||
;
|
||||
if(tp->white == 0)
|
||||
tp->white = s_copy(" ");
|
||||
return p;
|
||||
}
|
||||
|
||||
void
|
||||
yycleanup(void)
|
||||
{
|
||||
Field *f, *fnext;
|
||||
Node *np, *next;
|
||||
|
||||
for(f = firstfield; f; f = fnext){
|
||||
for(np = f->node; np; np = next){
|
||||
if(np->s)
|
||||
s_free(np->s);
|
||||
if(np->white)
|
||||
s_free(np->white);
|
||||
next = np->next;
|
||||
free(np);
|
||||
}
|
||||
fnext = f->next;
|
||||
free(f);
|
||||
}
|
||||
firstfield = lastfield = 0;
|
||||
}
|
||||
58
src/cmd/upas/smtp/rmtdns.c
Normal file
58
src/cmd/upas/smtp/rmtdns.c
Normal file
@@ -0,0 +1,58 @@
|
||||
#include "common.h"
|
||||
#include <ndb.h>
|
||||
|
||||
int
|
||||
rmtdns(char *net, char *path)
|
||||
{
|
||||
|
||||
int fd, n, r;
|
||||
char *domain, *cp, buf[1024];
|
||||
|
||||
if(net == 0 || path == 0)
|
||||
return 0;
|
||||
|
||||
domain = strdup(path);
|
||||
cp = strchr(domain, '!');
|
||||
if(cp){
|
||||
*cp = 0;
|
||||
n = cp-domain;
|
||||
} else
|
||||
n = strlen(domain);
|
||||
|
||||
if(*domain == '[' && domain[n-1] == ']'){ /* accept [nnn.nnn.nnn.nnn] */
|
||||
domain[n-1] = 0;
|
||||
r = strcmp(ipattr(domain+1), "ip");
|
||||
domain[n-1] = ']';
|
||||
} else
|
||||
r = strcmp(ipattr(domain), "ip"); /* accept nnn.nnn.nnn.nnn */
|
||||
|
||||
if(r == 0){
|
||||
free(domain);
|
||||
return 0;
|
||||
}
|
||||
|
||||
snprint(buf, sizeof(buf), "%s/dns", net);
|
||||
|
||||
fd = open(buf, ORDWR); /* look up all others */
|
||||
if(fd < 0){ /* dns screw up - can't check */
|
||||
free(domain);
|
||||
return 0;
|
||||
}
|
||||
|
||||
n = snprint(buf, sizeof(buf), "%s all", domain);
|
||||
free(domain);
|
||||
seek(fd, 0, 0);
|
||||
n = write(fd, buf, n);
|
||||
close(fd);
|
||||
if(n < 0){
|
||||
rerrstr(buf, sizeof(buf));
|
||||
if (strcmp(buf, "dns: name does not exist") == 0)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
void main(int, char *argv[]){ print("return = %d\n", rmtdns("/net.alt/tcp/109", argv[1]));}
|
||||
|
||||
*/
|
||||
1122
src/cmd/upas/smtp/smtp.c
Normal file
1122
src/cmd/upas/smtp/smtp.c
Normal file
File diff suppressed because it is too large
Load Diff
61
src/cmd/upas/smtp/smtp.h
Normal file
61
src/cmd/upas/smtp/smtp.h
Normal file
@@ -0,0 +1,61 @@
|
||||
typedef struct Node Node;
|
||||
typedef struct Field Field;
|
||||
typedef Node *Nodeptr;
|
||||
#define YYSTYPE Nodeptr
|
||||
|
||||
struct Node {
|
||||
Node *next;
|
||||
int c; /* token type */
|
||||
char addr; /* true if this is an address */
|
||||
String *s; /* string representing token */
|
||||
String *white; /* white space following token */
|
||||
char *start; /* first byte for this token */
|
||||
char *end; /* next byte in input */
|
||||
};
|
||||
|
||||
struct Field {
|
||||
Field *next;
|
||||
Node *node;
|
||||
int source;
|
||||
};
|
||||
|
||||
typedef struct DS DS;
|
||||
struct DS {
|
||||
/* dist string */
|
||||
char buf[128];
|
||||
char expand[128];
|
||||
char *netdir;
|
||||
char *proto;
|
||||
char *host;
|
||||
char *service;
|
||||
};
|
||||
|
||||
extern Field *firstfield;
|
||||
extern Field *lastfield;
|
||||
extern Node *usender;
|
||||
extern Node *usys;
|
||||
extern Node *udate;
|
||||
extern int originator;
|
||||
extern int destination;
|
||||
extern int date;
|
||||
extern int messageid;
|
||||
|
||||
Node* anonymous(Node*);
|
||||
Node* address(Node*);
|
||||
int badfieldname(Node*);
|
||||
Node* bang(Node*, Node*);
|
||||
Node* colon(Node*, Node*);
|
||||
int cistrcmp(char*, char*);
|
||||
Node* link2(Node*, Node*);
|
||||
Node* link3(Node*, Node*, Node*);
|
||||
void freenode(Node*);
|
||||
void newfield(Node*, int);
|
||||
void freefield(Field*);
|
||||
void yyinit(char*, int);
|
||||
int yyparse(void);
|
||||
int yylex(void);
|
||||
String* yywhite(void);
|
||||
Node* whiten(Node*);
|
||||
void yycleanup(void);
|
||||
int mxdial(char*, char*, char*);
|
||||
void dial_string_parse(char*, DS*);
|
||||
1494
src/cmd/upas/smtp/smtpd.c
Normal file
1494
src/cmd/upas/smtp/smtpd.c
Normal file
File diff suppressed because it is too large
Load Diff
68
src/cmd/upas/smtp/smtpd.h
Normal file
68
src/cmd/upas/smtp/smtpd.h
Normal file
@@ -0,0 +1,68 @@
|
||||
enum {
|
||||
ACCEPT = 0,
|
||||
REFUSED,
|
||||
DENIED,
|
||||
DIALUP,
|
||||
BLOCKED,
|
||||
DELAY,
|
||||
TRUSTED,
|
||||
NONE,
|
||||
|
||||
MAXREJECTS = 100,
|
||||
};
|
||||
|
||||
|
||||
typedef struct Link Link;
|
||||
typedef struct List List;
|
||||
|
||||
struct Link {
|
||||
Link *next;
|
||||
String *p;
|
||||
};
|
||||
|
||||
struct List {
|
||||
Link *first;
|
||||
Link *last;
|
||||
};
|
||||
|
||||
extern int fflag;
|
||||
extern int rflag;
|
||||
extern int sflag;
|
||||
|
||||
extern int debug;
|
||||
extern NetConnInfo *nci;
|
||||
extern char *dom;
|
||||
extern char* me;
|
||||
extern int trusted;
|
||||
extern List senders;
|
||||
extern List rcvers;
|
||||
|
||||
void addbadguy(char*);
|
||||
void auth(String *, String *);
|
||||
int blocked(String*);
|
||||
void data(void);
|
||||
char* dumpfile(char*);
|
||||
int forwarding(String*);
|
||||
void getconf(void);
|
||||
void hello(String*, int extended);
|
||||
void help(String *);
|
||||
int isbadguy(void);
|
||||
void listadd(List*, String*);
|
||||
void listfree(List*);
|
||||
int masquerade(String*, char*);
|
||||
void noop(void);
|
||||
int optoutofspamfilter(char*);
|
||||
void quit(void);
|
||||
void parseinit(void);
|
||||
void receiver(String*);
|
||||
int recipok(char*);
|
||||
int reply(char*, ...);
|
||||
void reset(void);
|
||||
int rmtdns(char*, char*);
|
||||
void sayhi(void);
|
||||
void sender(String*);
|
||||
void starttls(void);
|
||||
void turn(void);
|
||||
void verify(String*);
|
||||
void vfysenderhostok(void);
|
||||
int zzparse(void);
|
||||
317
src/cmd/upas/smtp/smtpd.y
Normal file
317
src/cmd/upas/smtp/smtpd.y
Normal file
@@ -0,0 +1,317 @@
|
||||
%{
|
||||
#include "common.h"
|
||||
#include <ctype.h>
|
||||
#include "smtpd.h"
|
||||
|
||||
#define YYSTYPE yystype
|
||||
typedef struct quux yystype;
|
||||
struct quux {
|
||||
String *s;
|
||||
int c;
|
||||
};
|
||||
Biobuf *yyfp;
|
||||
YYSTYPE *bang;
|
||||
extern Biobuf bin;
|
||||
extern int debug;
|
||||
|
||||
YYSTYPE cat(YYSTYPE*, YYSTYPE*, YYSTYPE*, YYSTYPE*, YYSTYPE*, YYSTYPE*, YYSTYPE*);
|
||||
int yyparse(void);
|
||||
int yylex(void);
|
||||
YYSTYPE anonymous(void);
|
||||
%}
|
||||
|
||||
%term SPACE
|
||||
%term CNTRL
|
||||
%term CRLF
|
||||
%start conversation
|
||||
%%
|
||||
|
||||
conversation : cmd
|
||||
| conversation cmd
|
||||
;
|
||||
cmd : error
|
||||
| 'h' 'e' 'l' 'o' spaces sdomain CRLF
|
||||
{ hello($6.s, 0); }
|
||||
| 'e' 'h' 'l' 'o' spaces sdomain CRLF
|
||||
{ hello($6.s, 1); }
|
||||
| 'm' 'a' 'i' 'l' spaces 'f' 'r' 'o' 'm' ':' spath CRLF
|
||||
{ sender($11.s); }
|
||||
| 'm' 'a' 'i' 'l' spaces 'f' 'r' 'o' 'm' ':' spath spaces 'a' 'u' 't' 'h' '=' sauth CRLF
|
||||
{ sender($11.s); }
|
||||
| 'r' 'c' 'p' 't' spaces 't' 'o' ':' spath CRLF
|
||||
{ receiver($9.s); }
|
||||
| 'd' 'a' 't' 'a' CRLF
|
||||
{ data(); }
|
||||
| 'r' 's' 'e' 't' CRLF
|
||||
{ reset(); }
|
||||
| 's' 'e' 'n' 'd' spaces 'f' 'r' 'o' 'm' ':' spath CRLF
|
||||
{ sender($11.s); }
|
||||
| 's' 'o' 'm' 'l' spaces 'f' 'r' 'o' 'm' ':' spath CRLF
|
||||
{ sender($11.s); }
|
||||
| 's' 'a' 'm' 'l' spaces 'f' 'r' 'o' 'm' ':' spath CRLF
|
||||
{ sender($11.s); }
|
||||
| 'v' 'r' 'f' 'y' spaces string CRLF
|
||||
{ verify($6.s); }
|
||||
| 'e' 'x' 'p' 'n' spaces string CRLF
|
||||
{ verify($6.s); }
|
||||
| 'h' 'e' 'l' 'p' CRLF
|
||||
{ help(0); }
|
||||
| 'h' 'e' 'l' 'p' spaces string CRLF
|
||||
{ help($6.s); }
|
||||
| 'n' 'o' 'o' 'p' CRLF
|
||||
{ noop(); }
|
||||
| 'q' 'u' 'i' 't' CRLF
|
||||
{ quit(); }
|
||||
| 't' 'u' 'r' 'n' CRLF
|
||||
{ turn(); }
|
||||
| 's' 't' 'a' 'r' 't' 't' 'l' 's' CRLF
|
||||
{ starttls(); }
|
||||
| 'a' 'u' 't' 'h' spaces name spaces string CRLF
|
||||
{ auth($6.s, $8.s); }
|
||||
| 'a' 'u' 't' 'h' spaces name CRLF
|
||||
{ auth($6.s, nil); }
|
||||
| CRLF
|
||||
{ reply("501 illegal command or bad syntax\r\n"); }
|
||||
;
|
||||
path : '<' '>' ={ $$ = anonymous(); }
|
||||
| '<' mailbox '>' ={ $$ = $2; }
|
||||
| '<' a_d_l ':' mailbox '>' ={ $$ = cat(&$2, bang, &$4, 0, 0 ,0, 0); }
|
||||
;
|
||||
spath : path ={ $$ = $1; }
|
||||
| spaces path ={ $$ = $2; }
|
||||
;
|
||||
auth : path ={ $$ = $1; }
|
||||
| mailbox ={ $$ = $1; }
|
||||
;
|
||||
sauth : auth ={ $$ = $1; }
|
||||
| spaces auth ={ $$ = $2; }
|
||||
;
|
||||
;
|
||||
a_d_l : at_domain ={ $$ = cat(&$1, 0, 0, 0, 0 ,0, 0); }
|
||||
| at_domain ',' a_d_l ={ $$ = cat(&$1, bang, &$3, 0, 0, 0, 0); }
|
||||
;
|
||||
at_domain : '@' domain ={ $$ = cat(&$2, 0, 0, 0, 0 ,0, 0); }
|
||||
;
|
||||
sdomain : domain ={ $$ = $1; }
|
||||
| domain spaces ={ $$ = $1; }
|
||||
;
|
||||
domain : element ={ $$ = cat(&$1, 0, 0, 0, 0 ,0, 0); }
|
||||
| element '.' ={ $$ = cat(&$1, 0, 0, 0, 0 ,0, 0); }
|
||||
| element '.' domain ={ $$ = cat(&$1, &$2, &$3, 0, 0 ,0, 0); }
|
||||
;
|
||||
element : name ={ $$ = cat(&$1, 0, 0, 0, 0 ,0, 0); }
|
||||
| '#' number ={ $$ = cat(&$1, &$2, 0, 0, 0 ,0, 0); }
|
||||
| '[' ']' ={ $$ = cat(&$1, &$2, 0, 0, 0 ,0, 0); }
|
||||
| '[' dotnum ']' ={ $$ = cat(&$1, &$2, &$3, 0, 0 ,0, 0); }
|
||||
;
|
||||
mailbox : local_part ={ $$ = cat(&$1, 0, 0, 0, 0 ,0, 0); }
|
||||
| local_part '@' domain ={ $$ = cat(&$3, bang, &$1, 0, 0 ,0, 0); }
|
||||
;
|
||||
local_part : dot_string ={ $$ = cat(&$1, 0, 0, 0, 0 ,0, 0); }
|
||||
| quoted_string ={ $$ = cat(&$1, 0, 0, 0, 0 ,0, 0); }
|
||||
;
|
||||
name : let_dig ={ $$ = cat(&$1, 0, 0, 0, 0 ,0, 0); }
|
||||
| let_dig ld_str ={ $$ = cat(&$1, &$2, 0, 0, 0 ,0, 0); }
|
||||
| let_dig ldh_str ld_str ={ $$ = cat(&$1, &$2, &$3, 0, 0 ,0, 0); }
|
||||
;
|
||||
ld_str : let_dig
|
||||
| let_dig ld_str ={ $$ = cat(&$1, &$2, 0, 0, 0 ,0, 0); }
|
||||
;
|
||||
ldh_str : hunder
|
||||
| ld_str hunder ={ $$ = cat(&$1, &$2, 0, 0, 0 ,0, 0); }
|
||||
| ldh_str ld_str hunder ={ $$ = cat(&$1, &$2, &$3, 0, 0 ,0, 0); }
|
||||
;
|
||||
let_dig : a
|
||||
| d
|
||||
;
|
||||
dot_string : string ={ $$ = cat(&$1, 0, 0, 0, 0 ,0, 0); }
|
||||
| string '.' dot_string ={ $$ = cat(&$1, &$2, &$3, 0, 0 ,0, 0); }
|
||||
;
|
||||
|
||||
string : char ={ $$ = cat(&$1, 0, 0, 0, 0 ,0, 0); }
|
||||
| string char ={ $$ = cat(&$1, &$2, 0, 0, 0 ,0, 0); }
|
||||
;
|
||||
|
||||
quoted_string : '"' qtext '"' ={ $$ = cat(&$1, &$2, &$3, 0, 0 ,0, 0); }
|
||||
;
|
||||
qtext : '\\' x ={ $$ = cat(&$2, 0, 0, 0, 0 ,0, 0); }
|
||||
| qtext '\\' x ={ $$ = cat(&$1, &$3, 0, 0, 0 ,0, 0); }
|
||||
| q
|
||||
| qtext q ={ $$ = cat(&$1, &$2, 0, 0, 0 ,0, 0); }
|
||||
;
|
||||
char : c
|
||||
| '\\' x ={ $$ = $2; }
|
||||
;
|
||||
dotnum : snum '.' snum '.' snum '.' snum ={ $$ = cat(&$1, &$2, &$3, &$4, &$5, &$6, &$7); }
|
||||
;
|
||||
number : d ={ $$ = cat(&$1, 0, 0, 0, 0 ,0, 0); }
|
||||
| number d ={ $$ = cat(&$1, &$2, 0, 0, 0 ,0, 0); }
|
||||
;
|
||||
snum : number ={ if(atoi(s_to_c($1.s)) > 255) print("bad snum\n"); }
|
||||
;
|
||||
spaces : SPACE ={ $$ = $1; }
|
||||
| SPACE spaces ={ $$ = $1; }
|
||||
;
|
||||
hunder : '-' | '_'
|
||||
;
|
||||
special1 : CNTRL
|
||||
| '(' | ')' | ',' | '.'
|
||||
| ':' | ';' | '<' | '>' | '@'
|
||||
;
|
||||
special : special1 | '\\' | '"'
|
||||
;
|
||||
notspecial : '!' | '#' | '$' | '%' | '&' | '\''
|
||||
| '*' | '+' | '-' | '/'
|
||||
| '=' | '?'
|
||||
| '[' | ']' | '^' | '_' | '`' | '{' | '|' | '}' | '~'
|
||||
;
|
||||
|
||||
a : 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i'
|
||||
| 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r'
|
||||
| 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z'
|
||||
;
|
||||
d : '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
|
||||
;
|
||||
c : a | d | notspecial
|
||||
;
|
||||
q : a | d | special1 | notspecial | SPACE
|
||||
;
|
||||
x : a | d | special | notspecial | SPACE
|
||||
;
|
||||
%%
|
||||
|
||||
void
|
||||
parseinit(void)
|
||||
{
|
||||
bang = (YYSTYPE*)malloc(sizeof(YYSTYPE));
|
||||
bang->c = '!';
|
||||
bang->s = 0;
|
||||
yyfp = &bin;
|
||||
}
|
||||
|
||||
yylex(void)
|
||||
{
|
||||
int c;
|
||||
|
||||
for(;;){
|
||||
c = Bgetc(yyfp);
|
||||
if(c == -1)
|
||||
return 0;
|
||||
if(debug)
|
||||
fprint(2, "%c", c);
|
||||
yylval.c = c = c & 0x7F;
|
||||
if(c == '\n'){
|
||||
return CRLF;
|
||||
}
|
||||
if(c == '\r'){
|
||||
c = Bgetc(yyfp);
|
||||
if(c != '\n'){
|
||||
Bungetc(yyfp);
|
||||
c = '\r';
|
||||
} else {
|
||||
if(debug)
|
||||
fprint(2, "%c", c);
|
||||
return CRLF;
|
||||
}
|
||||
}
|
||||
if(isalpha(c))
|
||||
return tolower(c);
|
||||
if(isspace(c))
|
||||
return SPACE;
|
||||
if(iscntrl(c))
|
||||
return CNTRL;
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
YYSTYPE
|
||||
cat(YYSTYPE *y1, YYSTYPE *y2, YYSTYPE *y3, YYSTYPE *y4, YYSTYPE *y5, YYSTYPE *y6, YYSTYPE *y7)
|
||||
{
|
||||
YYSTYPE rv;
|
||||
|
||||
if(y1->s)
|
||||
rv.s = y1->s;
|
||||
else {
|
||||
rv.s = s_new();
|
||||
s_putc(rv.s, y1->c);
|
||||
s_terminate(rv.s);
|
||||
}
|
||||
if(y2){
|
||||
if(y2->s){
|
||||
s_append(rv.s, s_to_c(y2->s));
|
||||
s_free(y2->s);
|
||||
} else {
|
||||
s_putc(rv.s, y2->c);
|
||||
s_terminate(rv.s);
|
||||
}
|
||||
} else
|
||||
return rv;
|
||||
if(y3){
|
||||
if(y3->s){
|
||||
s_append(rv.s, s_to_c(y3->s));
|
||||
s_free(y3->s);
|
||||
} else {
|
||||
s_putc(rv.s, y3->c);
|
||||
s_terminate(rv.s);
|
||||
}
|
||||
} else
|
||||
return rv;
|
||||
if(y4){
|
||||
if(y4->s){
|
||||
s_append(rv.s, s_to_c(y4->s));
|
||||
s_free(y4->s);
|
||||
} else {
|
||||
s_putc(rv.s, y4->c);
|
||||
s_terminate(rv.s);
|
||||
}
|
||||
} else
|
||||
return rv;
|
||||
if(y5){
|
||||
if(y5->s){
|
||||
s_append(rv.s, s_to_c(y5->s));
|
||||
s_free(y5->s);
|
||||
} else {
|
||||
s_putc(rv.s, y5->c);
|
||||
s_terminate(rv.s);
|
||||
}
|
||||
} else
|
||||
return rv;
|
||||
if(y6){
|
||||
if(y6->s){
|
||||
s_append(rv.s, s_to_c(y6->s));
|
||||
s_free(y6->s);
|
||||
} else {
|
||||
s_putc(rv.s, y6->c);
|
||||
s_terminate(rv.s);
|
||||
}
|
||||
} else
|
||||
return rv;
|
||||
if(y7){
|
||||
if(y7->s){
|
||||
s_append(rv.s, s_to_c(y7->s));
|
||||
s_free(y7->s);
|
||||
} else {
|
||||
s_putc(rv.s, y7->c);
|
||||
s_terminate(rv.s);
|
||||
}
|
||||
} else
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
yyerror(char *x)
|
||||
{
|
||||
USED(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* an anonymous user
|
||||
*/
|
||||
YYSTYPE
|
||||
anonymous(void)
|
||||
{
|
||||
YYSTYPE rv;
|
||||
|
||||
rv.s = s_copy("/dev/null");
|
||||
return rv;
|
||||
}
|
||||
591
src/cmd/upas/smtp/spam.c
Normal file
591
src/cmd/upas/smtp/spam.c
Normal file
@@ -0,0 +1,591 @@
|
||||
#include "common.h"
|
||||
#include "smtpd.h"
|
||||
#include <ip.h>
|
||||
|
||||
enum {
|
||||
NORELAY = 0,
|
||||
DNSVERIFY,
|
||||
SAVEBLOCK,
|
||||
DOMNAME,
|
||||
OURNETS,
|
||||
OURDOMS,
|
||||
|
||||
IP = 0,
|
||||
STRING,
|
||||
};
|
||||
|
||||
|
||||
typedef struct Keyword Keyword;
|
||||
|
||||
struct Keyword {
|
||||
char *name;
|
||||
int code;
|
||||
};
|
||||
|
||||
static Keyword options[] = {
|
||||
"norelay", NORELAY,
|
||||
"verifysenderdom", DNSVERIFY,
|
||||
"saveblockedmsg", SAVEBLOCK,
|
||||
"defaultdomain", DOMNAME,
|
||||
"ournets", OURNETS,
|
||||
"ourdomains", OURDOMS,
|
||||
0, NONE,
|
||||
};
|
||||
|
||||
static Keyword actions[] = {
|
||||
"allow", ACCEPT,
|
||||
"block", BLOCKED,
|
||||
"deny", DENIED,
|
||||
"dial", DIALUP,
|
||||
"delay", DELAY,
|
||||
0, NONE,
|
||||
};
|
||||
|
||||
static int hisaction;
|
||||
static List ourdoms;
|
||||
static List badguys;
|
||||
static ulong v4peerip;
|
||||
|
||||
static char* getline(Biobuf*);
|
||||
static int cidrcheck(char*);
|
||||
|
||||
static int
|
||||
findkey(char *val, Keyword *p)
|
||||
{
|
||||
|
||||
for(; p->name; p++)
|
||||
if(strcmp(val, p->name) == 0)
|
||||
break;
|
||||
return p->code;
|
||||
}
|
||||
|
||||
char*
|
||||
actstr(int a)
|
||||
{
|
||||
char buf[32];
|
||||
Keyword *p;
|
||||
|
||||
for(p=actions; p->name; p++)
|
||||
if(p->code == a)
|
||||
return p->name;
|
||||
if(a==NONE)
|
||||
return "none";
|
||||
sprint(buf, "%d", a);
|
||||
return buf;
|
||||
}
|
||||
|
||||
int
|
||||
getaction(char *s, char *type)
|
||||
{
|
||||
char buf[1024];
|
||||
Keyword *k;
|
||||
|
||||
if(s == nil || *s == 0)
|
||||
return ACCEPT;
|
||||
|
||||
for(k = actions; k->name != 0; k++){
|
||||
snprint(buf, sizeof buf, "/mail/ratify/%s/%s/%s", k->name, type, s);
|
||||
if(access(buf,0) >= 0)
|
||||
return k->code;
|
||||
}
|
||||
return ACCEPT;
|
||||
}
|
||||
|
||||
int
|
||||
istrusted(char *s)
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
if(s == nil || *s == 0)
|
||||
return 0;
|
||||
|
||||
snprint(buf, sizeof buf, "/mail/ratify/trusted/%s", s);
|
||||
return access(buf,0) >= 0;
|
||||
}
|
||||
|
||||
void
|
||||
getconf(void)
|
||||
{
|
||||
Biobuf *bp;
|
||||
char *cp, *p;
|
||||
String *s;
|
||||
char buf[512];
|
||||
uchar addr[4];
|
||||
|
||||
v4parseip(addr, nci->rsys);
|
||||
v4peerip = nhgetl(addr);
|
||||
|
||||
trusted = istrusted(nci->rsys);
|
||||
hisaction = getaction(nci->rsys, "ip");
|
||||
if(debug){
|
||||
fprint(2, "istrusted(%s)=%d\n", nci->rsys, trusted);
|
||||
fprint(2, "getaction(%s, ip)=%s\n", nci->rsys, actstr(hisaction));
|
||||
}
|
||||
snprint(buf, sizeof(buf), "%s/smtpd.conf", UPASLIB);
|
||||
bp = sysopen(buf, "r", 0);
|
||||
if(bp == 0)
|
||||
return;
|
||||
|
||||
for(;;){
|
||||
cp = getline(bp);
|
||||
if(cp == 0)
|
||||
break;
|
||||
p = cp+strlen(cp)+1;
|
||||
switch(findkey(cp, options)){
|
||||
case NORELAY:
|
||||
if(fflag == 0 && strcmp(p, "on") == 0)
|
||||
fflag++;
|
||||
break;
|
||||
case DNSVERIFY:
|
||||
if(rflag == 0 && strcmp(p, "on") == 0)
|
||||
rflag++;
|
||||
break;
|
||||
case SAVEBLOCK:
|
||||
if(sflag == 0 && strcmp(p, "on") == 0)
|
||||
sflag++;
|
||||
break;
|
||||
case DOMNAME:
|
||||
if(dom == 0)
|
||||
dom = strdup(p);
|
||||
break;
|
||||
case OURNETS:
|
||||
if (trusted == 0)
|
||||
trusted = cidrcheck(p);
|
||||
break;
|
||||
case OURDOMS:
|
||||
while(*p){
|
||||
s = s_new();
|
||||
s_append(s, p);
|
||||
listadd(&ourdoms, s);
|
||||
p += strlen(p)+1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
sysclose(bp);
|
||||
}
|
||||
|
||||
/*
|
||||
* match a user name. the only meta-char is '*' which matches all
|
||||
* characters. we only allow it as "*", which matches anything or
|
||||
* an * at the end of the name (e.g., "username*") which matches
|
||||
* trailing characters.
|
||||
*/
|
||||
static int
|
||||
usermatch(char *pathuser, char *specuser)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = strlen(specuser)-1;
|
||||
if(specuser[n] == '*'){
|
||||
if(n == 0) /* match everything */
|
||||
return 0;
|
||||
return strncmp(pathuser, specuser, n);
|
||||
}
|
||||
return strcmp(pathuser, specuser);
|
||||
}
|
||||
|
||||
static int
|
||||
dommatch(char *pathdom, char *specdom)
|
||||
{
|
||||
int n;
|
||||
|
||||
if (*specdom == '*'){
|
||||
if (specdom[1] == '.' && specdom[2]){
|
||||
specdom += 2;
|
||||
n = strlen(pathdom)-strlen(specdom);
|
||||
if(n == 0 || (n > 0 && pathdom[n-1] == '.'))
|
||||
return strcmp(pathdom+n, specdom);
|
||||
return n;
|
||||
}
|
||||
}
|
||||
return strcmp(pathdom, specdom);
|
||||
}
|
||||
|
||||
/*
|
||||
* figure out action for this sender
|
||||
*/
|
||||
int
|
||||
blocked(String *path)
|
||||
{
|
||||
String *lpath;
|
||||
int action;
|
||||
|
||||
if(debug)
|
||||
fprint(2, "blocked(%s)\n", s_to_c(path));
|
||||
|
||||
/* if the sender's IP address is blessed, ignore sender email address */
|
||||
if(trusted){
|
||||
if(debug)
|
||||
fprint(2, "\ttrusted => trusted\n");
|
||||
return TRUSTED;
|
||||
}
|
||||
|
||||
/* if sender's IP address is blocked, ignore sender email address */
|
||||
if(hisaction != ACCEPT){
|
||||
if(debug)
|
||||
fprint(2, "\thisaction=%s => %s\n", actstr(hisaction), actstr(hisaction));
|
||||
return hisaction;
|
||||
}
|
||||
|
||||
/* convert to lower case */
|
||||
lpath = s_copy(s_to_c(path));
|
||||
s_tolower(lpath);
|
||||
|
||||
/* classify */
|
||||
action = getaction(s_to_c(lpath), "account");
|
||||
if(debug)
|
||||
fprint(2, "\tgetaction account %s => %s\n", s_to_c(lpath), actstr(action));
|
||||
s_free(lpath);
|
||||
return action;
|
||||
}
|
||||
|
||||
/*
|
||||
* get a canonicalized line: a string of null-terminated lower-case
|
||||
* tokens with a two null bytes at the end.
|
||||
*/
|
||||
static char*
|
||||
getline(Biobuf *bp)
|
||||
{
|
||||
char c, *cp, *p, *q;
|
||||
int n;
|
||||
|
||||
static char *buf;
|
||||
static int bufsize;
|
||||
|
||||
for(;;){
|
||||
cp = Brdline(bp, '\n');
|
||||
if(cp == 0)
|
||||
return 0;
|
||||
n = Blinelen(bp);
|
||||
cp[n-1] = 0;
|
||||
if(buf == 0 || bufsize < n+1){
|
||||
bufsize += 512;
|
||||
if(bufsize < n+1)
|
||||
bufsize = n+1;
|
||||
buf = realloc(buf, bufsize);
|
||||
if(buf == 0)
|
||||
break;
|
||||
}
|
||||
q = buf;
|
||||
for (p = cp; *p; p++){
|
||||
c = *p;
|
||||
if(c == '\\' && p[1]) /* we don't allow \<newline> */
|
||||
c = *++p;
|
||||
else
|
||||
if(c == '#')
|
||||
break;
|
||||
else
|
||||
if(c == ' ' || c == '\t' || c == ',')
|
||||
if(q == buf || q[-1] == 0)
|
||||
continue;
|
||||
else
|
||||
c = 0;
|
||||
*q++ = tolower(c);
|
||||
}
|
||||
if(q != buf){
|
||||
if(q[-1])
|
||||
*q++ = 0;
|
||||
*q = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int
|
||||
isourdom(char *s)
|
||||
{
|
||||
Link *l;
|
||||
|
||||
if(strchr(s, '.') == nil)
|
||||
return 1;
|
||||
|
||||
for(l = ourdoms.first; l; l = l->next){
|
||||
if(dommatch(s, s_to_c(l->p)) == 0)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
forwarding(String *path)
|
||||
{
|
||||
char *cp, *s;
|
||||
String *lpath;
|
||||
|
||||
if(debug)
|
||||
fprint(2, "forwarding(%s)\n", s_to_c(path));
|
||||
|
||||
/* first check if they want loopback */
|
||||
lpath = s_copy(s_to_c(s_restart(path)));
|
||||
if(nci->rsys && *nci->rsys){
|
||||
cp = s_to_c(lpath);
|
||||
if(strncmp(cp, "[]!", 3) == 0){
|
||||
found:
|
||||
s_append(path, "[");
|
||||
s_append(path, nci->rsys);
|
||||
s_append(path, "]!");
|
||||
s_append(path, cp+3);
|
||||
s_terminate(path);
|
||||
s_free(lpath);
|
||||
return 0;
|
||||
}
|
||||
cp = strchr(cp,'!'); /* skip our domain and check next */
|
||||
if(cp++ && strncmp(cp, "[]!", 3) == 0)
|
||||
goto found;
|
||||
}
|
||||
|
||||
/* if mail is from a trusted IP addr, allow it to forward */
|
||||
if(trusted) {
|
||||
s_free(lpath);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* sender is untrusted; ensure receiver is in one of our domains */
|
||||
for(cp = s_to_c(lpath); *cp; cp++) /* convert receiver lc */
|
||||
*cp = tolower(*cp);
|
||||
|
||||
for(s = s_to_c(lpath); cp = strchr(s, '!'); s = cp+1){
|
||||
*cp = 0;
|
||||
if(!isourdom(s)){
|
||||
s_free(lpath);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
s_free(lpath);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
masquerade(String *path, char *him)
|
||||
{
|
||||
char *cp, *s;
|
||||
String *lpath;
|
||||
int rv = 0;
|
||||
|
||||
if(debug)
|
||||
fprint(2, "masquerade(%s)\n", s_to_c(path));
|
||||
|
||||
if(trusted)
|
||||
return 0;
|
||||
if(path == nil)
|
||||
return 0;
|
||||
|
||||
lpath = s_copy(s_to_c(path));
|
||||
|
||||
/* sender is untrusted; ensure receiver is in one of our domains */
|
||||
for(cp = s_to_c(lpath); *cp; cp++) /* convert receiver lc */
|
||||
*cp = tolower(*cp);
|
||||
s = s_to_c(lpath);
|
||||
|
||||
/* scan first element of ! or last element of @ paths */
|
||||
if((cp = strchr(s, '!')) != nil){
|
||||
*cp = 0;
|
||||
if(isourdom(s))
|
||||
rv = 1;
|
||||
} else if((cp = strrchr(s, '@')) != nil){
|
||||
if(isourdom(cp+1))
|
||||
rv = 1;
|
||||
} else {
|
||||
if(isourdom(him))
|
||||
rv = 1;
|
||||
}
|
||||
|
||||
s_free(lpath);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* this is a v4 only check */
|
||||
static int
|
||||
cidrcheck(char *cp)
|
||||
{
|
||||
char *p;
|
||||
ulong a, m;
|
||||
uchar addr[IPv4addrlen];
|
||||
uchar mask[IPv4addrlen];
|
||||
|
||||
if(v4peerip == 0)
|
||||
return 0;
|
||||
|
||||
/* parse a list of CIDR addresses comparing each to the peer IP addr */
|
||||
while(cp && *cp){
|
||||
v4parsecidr(addr, mask, cp);
|
||||
a = nhgetl(addr);
|
||||
m = nhgetl(mask);
|
||||
/*
|
||||
* if a mask isn't specified, we build a minimal mask
|
||||
* instead of using the default mask for that net. in this
|
||||
* case we never allow a class A mask (0xff000000).
|
||||
*/
|
||||
if(strchr(cp, '/') == 0){
|
||||
m = 0xff000000;
|
||||
p = cp;
|
||||
for(p = strchr(p, '.'); p && p[1]; p = strchr(p+1, '.'))
|
||||
m = (m>>8)|0xff000000;
|
||||
|
||||
/* force at least a class B */
|
||||
m |= 0xffff0000;
|
||||
}
|
||||
if((v4peerip&m) == a)
|
||||
return 1;
|
||||
cp += strlen(cp)+1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
isbadguy(void)
|
||||
{
|
||||
Link *l;
|
||||
|
||||
/* check if this IP address is banned */
|
||||
for(l = badguys.first; l; l = l->next)
|
||||
if(cidrcheck(s_to_c(l->p)))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
addbadguy(char *p)
|
||||
{
|
||||
listadd(&badguys, s_copy(p));
|
||||
};
|
||||
|
||||
char*
|
||||
dumpfile(char *sender)
|
||||
{
|
||||
int i, fd;
|
||||
ulong h;
|
||||
static char buf[512];
|
||||
char *cp;
|
||||
|
||||
if (sflag == 1){
|
||||
cp = ctime(time(0));
|
||||
cp[7] = 0;
|
||||
if(cp[8] == ' ')
|
||||
sprint(buf, "%s/queue.dump/%s%c", SPOOL, cp+4, cp[9]);
|
||||
else
|
||||
sprint(buf, "%s/queue.dump/%s%c%c", SPOOL, cp+4, cp[8], cp[9]);
|
||||
cp = buf+strlen(buf);
|
||||
if(access(buf, 0) < 0 && sysmkdir(buf, 0777) < 0)
|
||||
return "/dev/null";
|
||||
h = 0;
|
||||
while(*sender)
|
||||
h = h*257 + *sender++;
|
||||
for(i = 0; i < 50; i++){
|
||||
h += lrand();
|
||||
sprint(cp, "/%lud", h);
|
||||
if(access(buf, 0) >= 0)
|
||||
continue;
|
||||
fd = syscreate(buf, ORDWR, 0666);
|
||||
if(fd >= 0){
|
||||
if(debug)
|
||||
fprint(2, "saving in %s\n", buf);
|
||||
close(fd);
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
}
|
||||
return "/dev/null";
|
||||
}
|
||||
|
||||
char *validator = "/mail/lib/validateaddress";
|
||||
|
||||
int
|
||||
recipok(char *user)
|
||||
{
|
||||
char *cp, *p, c;
|
||||
char buf[512];
|
||||
int n;
|
||||
Biobuf *bp;
|
||||
int pid;
|
||||
Waitmsg *w;
|
||||
|
||||
if(shellchars(user)){
|
||||
syslog(0, "smtpd", "shellchars in user name");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(access(validator, AEXEC) == 0)
|
||||
switch(pid = fork()) {
|
||||
case -1:
|
||||
break;
|
||||
case 0:
|
||||
execl(validator, "validateaddress", user, nil);
|
||||
exits(0);
|
||||
default:
|
||||
while(w = wait()) {
|
||||
if(w->pid != pid)
|
||||
continue;
|
||||
if(w->msg[0] != 0){
|
||||
/*
|
||||
syslog(0, "smtpd", "validateaddress %s: %s", user, w->msg);
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
snprint(buf, sizeof(buf), "%s/names.blocked", UPASLIB);
|
||||
bp = sysopen(buf, "r", 0);
|
||||
if(bp == 0)
|
||||
return 1;
|
||||
for(;;){
|
||||
cp = Brdline(bp, '\n');
|
||||
if(cp == 0)
|
||||
break;
|
||||
n = Blinelen(bp);
|
||||
cp[n-1] = 0;
|
||||
|
||||
while(*cp == ' ' || *cp == '\t')
|
||||
cp++;
|
||||
for(p = cp; c = *p; p++){
|
||||
if(c == '#')
|
||||
break;
|
||||
if(c == ' ' || c == '\t')
|
||||
break;
|
||||
}
|
||||
if(p > cp){
|
||||
*p = 0;
|
||||
if(cistrcmp(user, cp) == 0){
|
||||
syslog(0, "smtpd", "names.blocked blocks %s", user);
|
||||
Bterm(bp);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
Bterm(bp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* a user can opt out of spam filtering by creating
|
||||
* a file in his mail directory named 'nospamfiltering'.
|
||||
*/
|
||||
int
|
||||
optoutofspamfilter(char *addr)
|
||||
{
|
||||
char *p, *f;
|
||||
int rv;
|
||||
|
||||
p = strchr(addr, '!');
|
||||
if(p)
|
||||
p++;
|
||||
else
|
||||
p = addr;
|
||||
|
||||
|
||||
rv = 0;
|
||||
f = smprint("/mail/box/%s/nospamfiltering", p);
|
||||
if(f != nil){
|
||||
rv = access(f, 0)==0;
|
||||
free(f);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
25
src/cmd/upas/smtp/y.tab.h
Normal file
25
src/cmd/upas/smtp/y.tab.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#define WORD 57346
|
||||
#define DATE 57347
|
||||
#define RESENT_DATE 57348
|
||||
#define RETURN_PATH 57349
|
||||
#define FROM 57350
|
||||
#define SENDER 57351
|
||||
#define REPLY_TO 57352
|
||||
#define RESENT_FROM 57353
|
||||
#define RESENT_SENDER 57354
|
||||
#define RESENT_REPLY_TO 57355
|
||||
#define SUBJECT 57356
|
||||
#define TO 57357
|
||||
#define CC 57358
|
||||
#define BCC 57359
|
||||
#define RESENT_TO 57360
|
||||
#define RESENT_CC 57361
|
||||
#define RESENT_BCC 57362
|
||||
#define REMOTE 57363
|
||||
#define PRECEDENCE 57364
|
||||
#define MIMEVERSION 57365
|
||||
#define CONTENTTYPE 57366
|
||||
#define MESSAGEID 57367
|
||||
#define RECEIVED 57368
|
||||
#define MAILER 57369
|
||||
#define BADTOKEN 57370
|
||||
17
src/cmd/upas/unesc/mkfile
Normal file
17
src/cmd/upas/unesc/mkfile
Normal file
@@ -0,0 +1,17 @@
|
||||
</$objtype/mkfile
|
||||
|
||||
TARG=unesc
|
||||
|
||||
OFILES=unesc.$O\
|
||||
|
||||
BIN=/$objtype/bin/upas
|
||||
|
||||
CC=pcc -c
|
||||
CFLAGS=-B
|
||||
|
||||
UPDATE=\
|
||||
mkfile\
|
||||
$HFILES\
|
||||
${OFILES:%.$O=%.c}\
|
||||
|
||||
</sys/src/cmd/mkone
|
||||
48
src/cmd/upas/unesc/unesc.c
Normal file
48
src/cmd/upas/unesc/unesc.c
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* upas/unesc - interpret =?foo?bar?=char?= escapes
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int
|
||||
hex(int c)
|
||||
{
|
||||
if('0' <= c && c <= '9')
|
||||
return c - '0';
|
||||
if('A' <= c && c <= 'F')
|
||||
return c - 'A' + 10;
|
||||
if('a' <= c && c <= 'f')
|
||||
return c - 'a' + 10;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int c;
|
||||
|
||||
while((c=getchar()) != EOF){
|
||||
if(c == '='){
|
||||
if((c=getchar()) == '?'){
|
||||
while((c=getchar()) != EOF && c != '?')
|
||||
continue;
|
||||
while((c=getchar()) != EOF && c != '?')
|
||||
continue;
|
||||
while((c=getchar()) != EOF && c != '?'){
|
||||
if(c == '='){
|
||||
c = hex(getchar()) << 4;
|
||||
c |= hex(getchar());
|
||||
}
|
||||
putchar(c);
|
||||
}
|
||||
(void) getchar(); /* consume '=' */
|
||||
}else{
|
||||
putchar('=');
|
||||
putchar(c);
|
||||
}
|
||||
}else
|
||||
putchar(c);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
20
src/cmd/upas/vf/mkfile
Normal file
20
src/cmd/upas/vf/mkfile
Normal file
@@ -0,0 +1,20 @@
|
||||
<$PLAN9/src/mkhdr
|
||||
|
||||
TARG=vf
|
||||
|
||||
OFILES=vf.$O\
|
||||
|
||||
LIB=../common/libcommon.a\
|
||||
|
||||
HFILES=../common/common.h\
|
||||
../common/sys.h\
|
||||
|
||||
|
||||
BIN=$PLAN9/bin/upas
|
||||
UPDATE=\
|
||||
mkfile\
|
||||
$HFILES\
|
||||
${OFILES:%.$O=%.c}\
|
||||
|
||||
<$PLAN9/src/mkone
|
||||
CFLAGS=$CFLAGS -I../common
|
||||
1110
src/cmd/upas/vf/vf.c
Normal file
1110
src/cmd/upas/vf/vf.c
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user