ref: 937634c293c7101505746f537bd165b6a57bebdd
parent: 738be8c36f9f2765894a526f69ec043e483002b3
author: qwx <[email protected]>
date: Sat Dec 28 23:00:45 EST 2024
remove upas: revert nocert patch
--- a/sys/src/cmd/upas/fs/dat.h
+++ /dev/null
@@ -1,360 +1,0 @@
-#include <avl.h>
-
-enum {
- /* cache states */
- Cidx = 1<<0,
- Cidxstale = 1<<1,
- Cheader = 1<<2,
- Cbody = 1<<3,
- Cnew = 1<<4,
- Cmod = 1<<5,
-
- /* encodings */
- Enone= 0,
- Ebase64,
- Equoted,
-
- /* dispositions */
- Dnone= 0,
- Dinline,
- Dfile,
- Dignore,
-
- /* mb create flags */
- DMcreate = 0x02000000,
-
- /* rm flags */
- Rrecur = 1<<0,
- Rtrunc = 1<<1,
-
- /* m->deleted flags */
- Deleted = 1<<0,
- Dup = 1<<1,
- Dead = 1<<2,
- Disappear = 1<<3,
- Dmark = 1<<4, /* temporary mark for idx scan */
-
- Maxmsg = 75*1024*1024, /* maxmessage size; debugging */
- Maxcache = 512*1024, /* target cache size; set low for debugging */
- Nctab = 15, /* max # of cached messages >10 */
- Nref = 10,
-};
-
-typedef struct {
- Avl;
- uchar *digest;
-} Mtree;
-
-typedef struct Idx Idx;
-struct Idx {
- Mtree;
-
- char *str; /* as read from idx file */
- uchar flags;
- uvlong fileid;
- ulong lines;
- ulong size;
- ulong rawbsize; /* nasty imap4d */
- ulong ibadchars;
-
- char *ffrom;
- char *from;
- char *to;
- char *cc;
- char *bcc;
- char *replyto;
- char *messageid;
- char *subject;
- char *sender;
- char *inreplyto;
- char *date822;
- char *idxaux; /* mailbox specific */
-
- char *type; /* mime info */
- char *filename;
- char disposition;
-
- int nparts;
-};
-
-typedef struct Message Message;
-struct Message {
- ulong id;
- int refs;
- int subname;
- char name[12];
-
- /* top-level indexed information */
- Idx;
-
- /* caching help */
- uchar cstate;
- ulong infolen;
- ulong csize;
-
- /*
- * a plethoria of pointers into message
- * and some status. not valid unless cached
- */
- 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 mallocd; /* message is malloc'd */
- char ballocd; /* body is malloc'd */
- char hallocd; /* header is malloc'd */
- int badchars; /* running count of bad chars. */
-
- char deleted;
- char inmbox;
-
- /* mail info */
- char *unixheader;
- char *unixfrom;
- char *unixdate;
- char *references[Nref]; /* nil terminated unless full */
-
- /* mime info */
- char *charset;
- char *boundary;
- char converted;
- char encoding;
- char decoded;
-
- Message *next;
- Message *part;
- Message *whole;
- Message *lru; /* least recently use chain */
-
- union{
- char *lim; /* used by plan9; not compatable with cache */
- vlong imapuid; /* used by imap4 */
- void *aux;
- };
-};
-
-typedef struct Mcache Mcache;
-struct Mcache {
- uvlong cached;
- int nlru;
- Message *lru;
-};
-
-typedef struct Mailbox Mailbox;
-struct Mailbox {
- int refs;
- Mailbox *next;
- ulong id;
- int flags;
- char rmflags;
- char dolock; /* lock when syncing? */
- char addfrom;
- char name[Elemlen];
- char path[Pathlen];
- Dir *d;
- Message *root;
- Avltree *mtree;
- ulong vers; /* goes up each time mailbox is changed */
-
- /* cache tracking */
- Mcache;
-
- /* index tracking */
- Qid qid;
-
- ulong waketime;
- void (*close)(Mailbox*);
- void (*decache)(Mailbox*, Message*);
- char *(*move)(Mailbox*, Message*, char*);
- int (*fetch)(Mailbox*, Message*, uvlong, ulong);
- void (*delete)(Mailbox*, Message*);
- char *(*ctl)(Mailbox*, int, char**);
- char *(*remove)(Mailbox *, int);
- char *(*rename)(Mailbox*, char*, int);
- char *(*sync)(Mailbox*);
- void (*modflags)(Mailbox*, Message*, int);
- void (*idxwrite)(Biobuf*, Mailbox*);
- int (*idxread)(char*, Mailbox*);
- void (*idxinvalid)(Mailbox*);
- void *aux; /* private to Mailbox implementation */
-
- int syncing; /* currently syncing? */
-};
-
-typedef char *Mailboxinit(Mailbox*, char*);
-
-Mailboxinit plan9mbox;
-Mailboxinit pop3mbox;
-Mailboxinit imap4mbox;
-Mailboxinit mdirmbox;
-
-void genericidxwrite(Biobuf*, Mailbox*);
-int genericidxread(char*, Mailbox*);
-void genericidxinvalid(Mailbox*);
-
-void cachehash(Mailbox*, Message*);
-int cacheheaders(Mailbox*, Message*); /* "getcache" */
-int cachebody(Mailbox*, Message*);
-int cacheidx(Mailbox*, Message*);
-int ensurecache(Mailbox*, Message*);
-
-/**/
-void putcache(Mailbox*, Message*); /* asymmetricial */
-void cachefree(Mailbox*, Message*);
-
-char* syncmbox(Mailbox*, int);
-void* emalloc(ulong);
-void* erealloc(void*, ulong);
-Message* newmessage(Message*);
-void unnewmessage(Mailbox*, Message*, Message*);
-char* delmessages(int, char**);
-char *flagmessages(int, char**);
-char* movemessages(int, char**);
-void digestmessage(Mailbox*, Message*);
-
-int wraptls(int, char*);
-
-void eprint(char*, ...);
-void iprint(char *, ...);
-char* newmbox(char*, char*, int, Mailbox**);
-void freembox(char*);
-char* removembox(char*, int);
-void syncallmboxes(void);
-void logmsg(Message*, char*, ...);
-void msgincref(Mailbox*, Message*);
-void msgdecref(Mailbox*, Message*);
-void mboxincref(Mailbox*);
-void mboxdecref(Mailbox*);
-char *mboxrename(char*, char*, int);
-void convert(Message*);
-void decode(Message*);
-int decquoted(char*, char*, char*, int);
-int xtoutf(char*, char**, char*, char*);
-ulong countlines(Message*);
-void parse(Mailbox*, Message*, int, int);
-void parseheaders(Mailbox*, Message*, int, int);
-void parsebody(Message*, Mailbox*);
-int strtotm(char*, Tm*);
-char* lowercase(char*);
-
-char* sputc(char*, char*, int);
-char* seappend(char*, char*, char*, int);
-
-int hdrlen(char*, char*);
-char* rfc2047(char*, char*, char*, int, int);
-
-char* localremove(Mailbox*, int);
-char* localrename(Mailbox*, char*, int);
-void rmidx(char*, int);
-int vremove(char*);
-int rename(char *, char*, int);
-
-void mtreeinit(Mailbox *);
-void mtreefree(Mailbox *);
-Message* mtreefind(Mailbox*, uchar*);
-Message* mtreeadd(Mailbox*, Message*);
-void mtreedelete(Mailbox*, Message*);
-
-enum {
- /* mail sub-objects; must be sorted */
- Qbcc,
- Qbody,
- Qcc,
- Qdate,
- Qdigest,
- Qdisposition,
- Qffrom,
- Qfileid,
- Qfilename,
- Qflags,
- Qfrom,
- Qheader,
- Qinfo,
- Qinreplyto,
- Qlines,
- Qmessageid,
- Qmimeheader,
- Qraw,
- Qrawbody,
- Qrawheader,
- Qrawunix,
- Qreferences,
- Qreplyto,
- Qsender,
- Qsize,
- Qsubject,
- Qto,
- Qtype,
- Qunixdate,
- Qunixheader,
- Qmax,
-
- /* other files */
- Qtop,
- Qmbox,
- Qdir,
- Qctl,
- Qmboxctl,
-};
-
-#define PATH(id, f) (((uvlong)(id)<<10) | (f))
-#define FILE(p) ((int) (p) & 0x3ff)
-
-/* hash table to aid in name lookup, all files have an entry */
-typedef struct Hash Hash;
-struct Hash {
- Hash *next;
- char *name;
- uvlong ppath;
- Qid qid;
- Mailbox *mb;
- Message *m;
-};
-
-ulong hash(char*);
-Hash *hlook(uvlong, char*);
-void henter(uvlong, char*, Qid, Message*, Mailbox*);
-void hfree(uvlong, char*);
-
-char *intern(char*);
-void idxfree(Idx*);
-int rdidxfile(Mailbox*);
-int wridxfile(Mailbox*);
-
-char *modflags(Mailbox*, Message*, char*);
-int getmtokens(char *, char**, int, int);
-
-extern char Enotme[];
-extern char *mntpt;
-extern char user[Elemlen];
-extern char *dirtab[];
-extern int Sflag;
-extern int iflag;
-extern int biffing;
-extern ulong cachetarg;
-extern int debug;
-extern int lflag;
-extern int plumbing;
-extern ulong msgallocd;
-extern ulong msgfreed;
-extern int nocertcheck;
-extern Mailbox *mbl;
-extern Message *root;
-extern char *logf;
-
-#define dprint(...) if(debug) fprint(2, __VA_ARGS__); else {}
-#define Topmsg(mb, m) (m->whole == mb->root)
-#pragma varargck type "A" uchar*
-#pragma varargck type "D" uvlong
-#pragma varargck type "Δ" uvlong
-#pragma varargck argpos eprint 1
-#pragma varargck argpos iprint 1
-#pragma varargck argpos logmsg 2
-
--- a/sys/src/cmd/upas/fs/fs.c
+++ /dev/null
@@ -1,1692 +1,0 @@
-#include "common.h"
-#include <fcall.h>
-#include <libsec.h>
-#include <pool.h>
-#include "dat.h"
-
-typedef struct Fid Fid;
-struct Fid
-{
- Qid qid;
- short busy;
- short open;
- int fid;
- Fid *next;
- Mailbox *mb;
- Message *m;
-
- long foff; /* offset/DIRLEN of finger */
- Message *fptr; /* pointer to message at off */
- int fvers; /* mailbox version when finger was saved */
-};
-
-Fid *newfid(int);
-void error(char*);
-void io(void);
-void *erealloc(void*, ulong);
-void *emalloc(ulong);
-void usage(void);
-void reader(void);
-int readheader(Message*, char*, int, int);
-void post(char*, char*, int);
-
-char *rflush(Fid*), *rauth(Fid*),
- *rattach(Fid*), *rwalk(Fid*),
- *ropen(Fid*), *rcreate(Fid*),
- *rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
- *rremove(Fid*), *rstat(Fid*), *rwstat(Fid*),
- *rversion(Fid*);
-
-char *(*fcalls[])(Fid*) = {
- [Tflush] rflush,
- [Tversion] rversion,
- [Tauth] rauth,
- [Tattach] rattach,
- [Twalk] rwalk,
- [Topen] ropen,
- [Tcreate] rcreate,
- [Tread] rread,
- [Twrite] rwrite,
- [Tclunk] rclunk,
- [Tremove] rremove,
- [Tstat] rstat,
- [Twstat] rwstat,
-};
-
-char Eperm[] = "permission denied";
-char Enotdir[] = "not a directory";
-char Enoauth[] = "upas/fs: authentication not required";
-char Enotexist[] = "file does not exist";
-char Einuse[] = "file in use";
-char Eexist[] = "file exists";
-char Enotowner[] = "not owner";
-char Eisopen[] = "file already open for I/O";
-char Excl[] = "exclusive use file already open";
-char Ename[] = "illegal name";
-char Ebadctl[] = "unknown control message";
-char Ebadargs[] = "invalid arguments";
-char Enotme[] = "path not served by this file server";
-char Eio[] = "I/O error";
-
-char *dirtab[] = {
-[Qdir] ".",
-[Qbcc] "bcc",
-[Qbody] "body",
-[Qcc] "cc",
-[Qdate] "date",
-[Qdigest] "digest",
-[Qdisposition] "disposition",
-[Qffrom] "ffrom",
-[Qfileid] "fileid",
-[Qfilename] "filename",
-[Qflags] "flags",
-[Qfrom] "from",
-[Qheader] "header",
-[Qinfo] "info",
-[Qinreplyto] "inreplyto",
-[Qlines] "lines",
-[Qmessageid] "messageid",
-[Qmimeheader] "mimeheader",
-[Qraw] "raw",
-[Qrawbody] "rawbody",
-[Qrawheader] "rawheader",
-[Qrawunix] "rawunix",
-[Qreferences] "references",
-[Qreplyto] "replyto",
-[Qsender] "sender",
-[Qsize] "size",
-[Qsubject] "subject",
-[Qto] "to",
-[Qtype] "type",
-[Qunixdate] "unixdate",
-[Qunixheader] "unixheader",
-[Qctl] "ctl",
-[Qmboxctl] "ctl",
-};
-
-char *mntpt;
-char user[Elemlen];
-int Dflag;
-int Sflag;
-int iflag;
-int lflag;
-int biffing;
-int debug;
-int plumbing = 1;
-ulong cachetarg = Maxcache;
-int nocertcheck; /* ignore unrecognized certs. Still logged */
-Mailbox *mbl;
-
-static int messagesize = IOUNIT + IOHDRSZ;
-static int mfd[2];
-static char hbuf[32*1024];
-static uchar mbuf[IOUNIT];
-static uchar mdata[IOUNIT + IOHDRSZ];
-static ulong path; /* incremented for each new file */
-static Hash *htab[2053];
-static Fcall rhdr;
-static Fcall thdr;
-static Fid *fids;
-static QLock synclock;
-
-void
-sanemsg(Message *m)
-{
- if(m->end < m->start)
- abort();
- if(m->ballocd && (m->start <= m->body && m->end >= m->body))
- abort();
- if(m->end - m->start > Maxmsg)
- abort();
- if(m->size > Maxmsg)
- abort();
- if(m->fileid != 0 && m->fileid <= 1000000ull<<8)
- abort();
-}
-
-void
-sanembmsg(Mailbox *mb, Message *m)
-{
- sanemsg(m);
- if(Topmsg(mb, m)){
- if(m->start > end && m->size == 0)
- abort();
- if(m->fileid <= 1000000ull<<8)
- abort();
- }
-}
-
-static int
-Afmt(Fmt *f)
-{
- char buf[SHA1dlen*2 + 1];
- uchar *u, i;
-
- u = va_arg(f->args, uchar*);
- if(u == 0 && f->flags & FmtSharp)
- return fmtstrcpy(f, "-");
- if(u == 0)
- return fmtstrcpy(f, "<nildigest>");
- for(i = 0; i < SHA1dlen; i++)
- sprint(buf + 2*i, "%2.2ux", u[i]);
- return fmtstrcpy(f, buf);
-}
-
-static int
-Δfmt(Fmt *f)
-{
- uvlong v;
- Tm tm;
-
- v = va_arg(f->args, uvlong);
- if(f->flags & FmtSharp)
- if((v>>8) == 0)
- return fmtstrcpy(f, "");
- tmtime(&tm, v>>8, tzload("local"));
- return fmtprint(f, "%τ", tmfmt(&tm, "WW MMM _D hh:mm:ss Z YYYY"));
-}
-
-static int
-Dfmt(Fmt *f)
-{
- char buf[32];
- int seq;
- uvlong v;
-
- v = va_arg(f->args, uvlong);
- seq = v & 0xff;
- if(seq > 99)
- seq = 99;
- snprint(buf, sizeof buf, "%llud.%.2d", v>>8, seq);
- return fmtstrcpy(f, buf);
-}
-
-void
-usage(void)
-{
- fprint(2, "usage: upas/fs [-CDSbdlmnps] [-c cachetarg] [-f mboxfile] [-m mountpoint]\n");
- exits("usage");
-}
-
-void
-notifyf(void *, char *s)
-{
- if(strncmp(s, "interrupt", 9) == 0)
- noted(NCONT);
- if(strncmp(s, "die: yankee pig dog", 19) != 0)
- /* don't want to call syslog from notify handler */
- fprint(2, "upas/fs: user: %s; note: %s\n", getuser(), s);
- noted(NDFLT);
-}
-
-void
-setname(char **v)
-{
- char buf[128], *p, *e;
- int fd, i;
-
- snprint(buf, sizeof buf, "/proc/%d/args", getpid());
- if((fd = open(buf, OWRITE)) < 0)
- return;
- e = buf + sizeof buf;
- p = seprint(buf, e, "%s", v[0]);
- for(i = 0; v[++i]; )
- p = seprint(p, e, " %s", v[i]);
- write(fd, buf, p - buf);
- close(fd);
-}
-
-ulong
-ntoi(char *s)
-{
- ulong n;
-
- n = strtoul(s, &s, 0);
- for(;;)
- switch(*s++){
- default:
- usage();
- case 'g':
- n *= 1024;
- case 'm':
- n *= 1024;
- case 'k':
- n *= 1024;
- break;
- case 0:
- return n;
- }
-}
-
-void
-main(int argc, char *argv[])
-{
- char maildir[Pathlen], mbox[Pathlen], srvfile[64], **v;
- char *mboxfile, *err;
- int p[2], nodflt, srvpost;
-
- rfork(RFNOTEG);
- mboxfile = nil;
- nodflt = 0;
- srvpost = 0;
- v = argv;
-
- ARGBEGIN{
- case 'C':
- nocertcheck = 1;
- break;
- case 'D':
- Dflag = 1;
- break;
- case 'S':
- Sflag = 1;
- break;
- case 'b':
- biffing = 1;
- break;
- case 'c':
- cachetarg = ntoi(EARGF(usage()));
- break;
- case 'd':
- if(++debug > 1)
- mainmem->flags |= POOL_PARANOIA;
- break;
- case 'f':
- mboxfile = EARGF(usage());
- break;
- case 'i':
- iflag++;
- break;
- case 'l':
- lflag = 1;
- break;
- case 'm':
- mntpt = EARGF(usage());
- break;
- case 'n':
- nodflt = 1;
- break;
- case 'p':
- plumbing = 0;
- break;
- case 's':
- srvpost = 1;
- break;
- default:
- usage();
- }ARGEND
-
- if(argc)
- usage();
- fmtinstall('A', Afmt);
- fmtinstall('D', Dfmt);
- fmtinstall(L'Δ', Δfmt);
- fmtinstall('F', fcallfmt);
- fmtinstall('H', encodefmt); /* forces tls stuff */
- tmfmtinstall();
- quotefmtinstall();
- if(pipe(p) < 0)
- error("pipe failed");
- mfd[0] = p[0];
- mfd[1] = p[0];
-
- notify(notifyf);
- strcpy(user, getuser());
- if(mntpt == nil){
- snprint(maildir, sizeof(maildir), "/mail/fs");
- mntpt = maildir;
- }
- if(mboxfile == nil && !nodflt){
- snprint(mbox, sizeof mbox, "/mail/box/%s/mbox", user);
- mboxfile = mbox;
- }
-
- if(mboxfile != nil)
- if(err = newmbox(mboxfile, "mbox", 0, nil))
- sysfatal("opening %s: %s", mboxfile, err);
-
- switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG|RFREND)){
- case -1:
- error("fork");
- case 0:
- henter(PATH(0, Qtop), dirtab[Qctl],
- (Qid){PATH(0, Qctl), 0, QTFILE}, nil, nil);
- close(p[1]);
- setname(v);
- io();
- syncallmboxes();
- syskillpg(getpid());
- break;
- default:
- close(p[0]); /* don't deadlock if child fails */
- if(srvpost){
- snprint(srvfile, sizeof srvfile, "/srv/upasfs.%s", user);
- post(srvfile, "upasfs", p[1]);
- }else
- if(mount(p[1], -1, mntpt, MREPL, "") == -1)
- error("mount failed");
- }
- exits("");
-}
-
-char*
-sputc(char *p, char *e, int c)
-{
- if(p < e - 1)
- *p++ = c;
- return p;
-}
-
-char*
-seappend(char *s, char *e, char *a, int n)
-{
- int l;
-
- l = e - s - 1;
- if(l < n)
- n = l;
- memcpy(s, a, n);
- s += n;
- *s = 0;
- return s;
-}
-
-static int
-fileinfo(Mailbox *mb, Message *m, int t, char **pp)
-{
- char *s, *e, *p;
- int len, i;
- static char buf[64 + 512];
-
- if(cacheidx(mb, m) < 0)
- return -1;
- sanembmsg(mb, m);
- p = nil;
- len = -1;
- switch(t){
- case Qbody:
- if(cachebody(mb, m) < 0)
- return -1;
- p = m->body;
- len = m->bend - p;
- break;
- case Qbcc:
- p = m->bcc;
- break;
- case Qcc:
- p = m->cc;
- break;
- case Qdisposition:
- switch(m->disposition){
- case Dinline:
- p = "inline";
- break;
- case Dfile:
- p = "file";
- break;
- }
- break;
- case Qdate:
- if((p = m->date822) != nil)
- break;
- /* wet floor */
- case Qunixdate:
- p = buf;
- len = snprint(buf, sizeof buf, "%#Δ", m->fileid);
- break;
- case Qfilename:
- p = m->filename;
- break;
- case Qflags:
- p = flagbuf(buf, m->flags);
- break;
- case Qinreplyto:
- p = m->inreplyto;
- break;
- case Qmessageid:
- p = m->messageid;
- break;
- case Qfrom:
- p = m->from;
- break;
- case Qffrom:
- p = m->ffrom;
- break;
- case Qlines:
- len = snprint(buf, sizeof buf, "%lud", m->lines);
- p = buf;
- break;
- case Qraw:
- if(cachebody(mb, m) < 0)
- return -1;
- p = m->start;
- if(p != nil)
- if(strncmp(p, "From ", 5) == 0)
- if(e = strchr(p, '\n'))
- p = e + 1;
- len = m->rbend - p;
- break;
- case Qrawunix:
- if(cachebody(mb, m) < 0)
- return -1;
- p = m->start;
- len = m->end - p;
- break;
- case Qrawbody:
- if(cachebody(mb, m) < 0)
- return -1;
- p = m->rbody;
- len = m->rbend - p;
- break;
- case Qrawheader:
- if(cacheheaders(mb, m) < 0)
- return -1;
- p = m->header;
- len = m->hend - p;
- break;
- case Qmimeheader:
- if(cacheheaders(mb, m) < 0)
- return -1;
- p = m->mheader;
- len = m->mhend - p;
- break;
- case Qreferences:
- if(cacheheaders(mb, m) < 0)
- return -1;
- e = buf + sizeof buf;
- s = buf;
- for(i = 0; i < nelem(m->references); i++){
- if(m->references[i] == nil)
- break;
- s = seprint(s, e, "%s\n", m->references[i]);
- }
- *s = 0;
- p = buf;
- len = s - buf;
- break;
- case Qreplyto:
- p = m->replyto;
- break;
- case Qsender:
- p = m->sender;
- break;
- case Qsubject:
- p = m->subject;
- break;
- case Qsize:
- len = snprint(buf, sizeof buf, "%lud", m->size);
- p = buf;
- break;
- case Qto:
- p = m->to;
- break;
- case Qtype:
- p = m->type;
- break;
- case Qfileid:
- p = buf;
- len = snprint(buf, sizeof buf, "%D", m->fileid);
- break;
- case Qunixheader:
- if(cacheheaders(mb, m) < 0)
- return -1;
- p = m->unixheader;
- break;
- case Qdigest:
- p = buf;
- len = snprint(buf, sizeof buf, "%A", m->digest);
- break;
- }
- if(p == nil)
- p = "";
- if(len == -1)
- len = strlen(p);
- *pp = p;
- putcache(mb, m);
- return len;
-}
-
-int infofields[] = {
- Qfrom,
- Qto,
- Qcc,
- Qreplyto,
- Qunixdate,
- Qsubject,
- Qtype,
- Qdisposition,
- Qfilename,
- Qdigest,
- Qbcc,
- Qinreplyto,
- Qdate,
- Qsender,
- Qmessageid,
- Qlines,
- Qsize,
- Qflags,
- Qfileid,
- Qffrom,
-};
-
-int
-readinfo(Mailbox *mb, Message *m, char *buf, long off, int count)
-{
- char *s, *p, *e;
- int i, n;
- long off0;
-
- if(m->infolen > 0 && off >= m->infolen)
- return 0;
- off0 = off;
- s = buf;
- e = s + count;
- for(i = 0; s < e; i++){
- if(i == nelem(infofields)){
- m->infolen = s - buf + off0;
- break;
- }
- if((n = fileinfo(mb, m, infofields[i], &p)) < 0)
- return -1;
- if(off > n){
- off -= n + 1;
- continue;
- }
- if(off){
- n -= off;
- p += off;
- off = 0;
- }
- if(s + n > e)
- n = e - s;
- memcpy(s, p, n);
- s += n;
- if(s < e)
- *s++ = '\n';
- }
- return s - buf;
-}
-
-static int
-mkstat(Dir *d, Mailbox *mb, Message *m, int t)
-{
- char *p, *e;
- int n;
-
- d->uid = user;
- d->gid = user;
- d->muid = user;
- d->mode = 0444;
- d->qid.vers = 0;
- d->qid.type = QTFILE;
- d->type = 0;
- d->dev = 0;
- if(m && m->fileid > 1000000ull)
- d->atime = m->fileid >> 8;
- else if(mb && mb->d)
- d->atime = mb->d->mtime;
- else
- d->atime = time(0);
- d->mtime = d->atime;
-
- switch(t){
- case Qtop:
- d->name = ".";
- d->mode = DMDIR|0555;
- d->atime = d->mtime = time(0);
- d->length = 0;
- d->qid.path = PATH(0, Qtop);
- d->qid.type = QTDIR;
- break;
- case Qmbox:
- d->name = mb->name;
- d->mode = DMDIR|0555;
- d->length = 0;
- d->qid.path = PATH(mb->id, Qmbox);
- d->qid.type = QTDIR;
- d->qid.vers = mb->vers;
- break;
- case Qdir:
- d->name = m->name;
- d->mode = DMDIR|0555;
- d->length = 0;
- d->qid.path = PATH(m->id, Qdir);
- d->qid.type = QTDIR;
- break;
- case Qctl:
- d->name = dirtab[t];
- d->mode = 0666;
- d->atime = d->mtime = time(0);
- d->length = 0;
- d->qid.path = PATH(0, Qctl);
- break;
- case Qheader:
- if(cacheheaders(mb, m) < 0)
- return -1;
- d->name = dirtab[t];
- d->length = readheader(m, hbuf, 0, sizeof hbuf);
- putcache(mb, m);
- break;
- case Qmboxctl:
- d->name = dirtab[t];
- d->mode = 0222;
- d->atime = d->mtime = time(0);
- d->length = 0;
- d->qid.path = PATH(mb->id, Qmboxctl);
- break;
- case Qinfo:
- if((n = readinfo(mb, m, hbuf, 0, sizeof hbuf)) < 0)
- return -1;
- d->name = dirtab[t];
- d->length = n;
- d->qid.path = PATH(m->id, t);
- break;
- case Qraw:
- if(cacheheaders(mb, m) < 0)
- return -1;
- d->name = dirtab[t];
- d->length = m->size;
- p = m->start;
- if(p != nil)
- if(strncmp(p, "From ", 5) == 0)
- if(e = strchr(p, '\n'))
- d->length -= ++e - p;
- putcache(mb, m);
- break;
- case Qrawbody:
- d->name = dirtab[t];
- d->length = m->rawbsize;
- break;
- case Qrawunix:
- d->name = dirtab[t];
- d->length = m->size;
- if(mb->addfrom && Topmsg(mb, m)){
- if(cacheheaders(mb, m) < 0)
- return -1;
- d->length += strlen(m->unixheader);
- putcache(mb, m);
- }
- break;
- case Qflags:
- d->mode = 0666;
- default:
- if((n = fileinfo(mb, m, t, &p)) < 0)
- return -1;
- d->name = dirtab[t];
- d->length = n;
- d->qid.path = PATH(m->id, t);
- break;
- }
- return 0;
-}
-
-char*
-rversion(Fid*)
-{
- Fid *f;
-
- if(thdr.msize < 256)
- return "max messagesize too small";
- if(thdr.msize < messagesize)
- messagesize = thdr.msize;
- rhdr.msize = messagesize;
- rhdr.version = "9P2000";
- if(strncmp(thdr.version, "9P", 2) != 0)
- rhdr.version = "unknown";
-
- for(f = fids; f; f = f->next)
- if(f->busy)
- rclunk(f);
- return nil;
-}
-
-char*
-rauth(Fid*)
-{
- return Enoauth;
-}
-
-char*
-rflush(Fid*)
-{
- return 0;
-}
-
-char*
-rattach(Fid *f)
-{
- f->busy = 1;
- f->m = nil;
- f->mb = nil;
- f->qid.path = PATH(0, Qtop);
- f->qid.type = QTDIR;
- f->qid.vers = 0;
- rhdr.qid = f->qid;
- if(strcmp(thdr.uname, user) != 0)
- return Eperm;
- return 0;
-}
-
-static Fid*
-doclone(Fid *f, int nfid)
-{
- Fid *nf;
-
- nf = newfid(nfid);
- if(nf->busy)
- return nil;
- nf->busy = 1;
- nf->open = 0;
- if(nf->mb = f->mb)
- mboxincref(nf->mb);
- if(nf->m = f->m)
- msgincref(nf->mb, nf->m);
- nf->qid = f->qid;
- return nf;
-}
-
-/* slow? binary search? */
-static int
-dindex(char *name)
-{
- int i;
-
- for(i = 0; i < Qmax; i++)
- if(dirtab[i] != nil)
- if(strcmp(dirtab[i], name) == 0)
- return i;
- return -1;
-}
-
-char*
-dowalk(Fid *f, char *name)
-{
- char *p;
- Hash *h;
- int t;
-
- if(f->qid.type != QTDIR)
- return Enotdir;
- t = FILE(f->qid.path);
- if(strcmp(name, ".") == 0)
- return nil;
- if(strcmp(name, "..") == 0){
- switch(t){
- case Qtop:
- f->qid.path = PATH(0, Qtop);
- f->qid.type = QTDIR;
- f->qid.vers = 0;
- break;
- case Qmbox:
- f->qid.path = PATH(0, Qtop);
- f->qid.type = QTDIR;
- f->qid.vers = 0;
- mboxdecref(f->mb);
- f->mb = nil;
- break;
- case Qdir:
- if(Topmsg(f->mb, f->m)){
- f->qid.path = PATH(f->mb->id, Qmbox);
- f->qid.type = QTDIR;
- f->qid.vers = f->mb->vers;
- msgdecref(f->mb, f->m);
- f->m = nil;
- } else {
- msgincref(f->mb, f->m->whole);
- msgdecref(f->mb, f->m);
- f->m = f->m->whole;
- f->qid.path = PATH(f->m->id, Qdir);
- f->qid.type = QTDIR;
- }
- break;
- }
- return nil;
- }
-
- /* this must catch everything except . and .. */
- if(t == Qdir && *name >= 'a' && *name <= 'z'){
- for(;;){
- t = dindex(name);
- if(t == -1){
- if((p = strchr(name, '.')) != nil && *name != '.'){
- *p = 0;
- continue;
- }
- return Enotexist;
- }
- break;
- }
- h = hlook(f->qid.path, "xxx"); /* sleezy speedup */
- } else {
- h = hlook(f->qid.path, name);
- }
-
- if(h == nil)
- return Enotexist;
-
- if(h->mb)
- mboxincref(h->mb);
- if(h->m)
- msgincref(h->mb, h->m);
- if(f->m)
- msgdecref(f->mb, f->m);
- if(f->mb)
- mboxdecref(f->mb);
- f->m = h->m;
- f->mb = h->mb;
- f->qid = h->qid;
- if(t < Qmax)
- f->qid.path = PATH(f->m->id, t); /* sleezy speedup */
- return nil;
-}
-
-char*
-rwalk(Fid *f)
-{
- Fid *nf;
- char *rv;
- int i;
-
- if(f->open)
- return Eisopen;
-
- rhdr.nwqid = 0;
- nf = nil;
-
- /* clone if requested */
- if(thdr.newfid != thdr.fid){
- nf = doclone(f, thdr.newfid);
- if(nf == nil)
- return "new fid in use";
- f = nf;
- }
-
- /* if it's just a clone, return */
- if(thdr.nwname == 0 && nf != nil)
- return nil;
-
- /* walk each element */
- rv = nil;
- for(i = 0; i < thdr.nwname; i++){
- rv = dowalk(f, thdr.wname[i]);
- if(rv != nil){
- if(nf != nil)
- rclunk(nf);
- break;
- }
- rhdr.wqid[i] = f->qid;
- }
- rhdr.nwqid = i;
-
- /* we only error out if no walk */
- if(i > 0)
- rv = nil;
- return rv;
-}
-
-char*
-ropen(Fid *f)
-{
- int file;
-
- if(f->open)
- return Eisopen;
- file = FILE(f->qid.path);
- if(thdr.mode != OREAD)
- if(file != Qctl && file != Qmboxctl && file != Qflags)
- return Eperm;
-
- /* make sure we've decoded */
- if(file == Qbody){
- if(cachebody(f->mb, f->m) < 0)
- return Eio;
- decode(f->m);
- convert(f->m);
- putcache(f->mb, f->m);
- }
-
- rhdr.iounit = 0;
- rhdr.qid = f->qid;
- f->open = 1;
- return 0;
-}
-
-char*
-rcreate(Fid*)
-{
- return Eperm;
-}
-
-int
-readtopdir(Fid*, uchar *buf, long off, int cnt, int blen)
-{
- Dir d;
- int m, n;
- long pos;
- Mailbox *mb;
-
- n = 0;
- pos = 0;
- mkstat(&d, nil, nil, Qctl);
- m = convD2M(&d, &buf[n], blen);
- if(off <= pos){
- if(m <= BIT16SZ || m > cnt)
- return n;
- n += m;
- cnt -= m;
- }
- pos += m;
-
- for(mb = mbl; mb != nil; mb = mb->next){
- assert(mb->refs > 0);
-
- mkstat(&d, mb, nil, Qmbox);
- m = convD2M(&d, &buf[n], blen - n);
- if(off <= pos){
- if(m <= BIT16SZ || m > cnt)
- break;
- n += m;
- cnt -= m;
- }
- pos += m;
- }
- return n;
-}
-
-int
-readmboxdir(Fid *f, uchar *buf, long off, int cnt, int blen)
-{
- Dir d;
- int n, m;
- long pos;
- Message *msg;
-
- assert(f->mb->refs > 0);
-
- if(off == 0)
- syncmbox(f->mb, 1);
-
- n = 0;
- if(f->mb->ctl){
- mkstat(&d, f->mb, nil, Qmboxctl);
- m = convD2M(&d, &buf[n], blen);
- if(off == 0){
- if(m <= BIT16SZ || m > cnt){
- f->fptr = nil;
- return n;
- }
- n += m;
- cnt -= m;
- } else
- off -= m;
- }
-
- /* to avoid n**2 reads of the directory, use a saved finger pointer */
- if(f->mb->vers == f->fvers && off >= f->foff && f->fptr != nil){
- msg = f->fptr;
- pos = f->foff;
- } else {
- msg = f->mb->root->part;
- pos = 0;
- }
-
- for(; cnt > 0 && msg != nil; msg = msg->next){
- /* act like deleted files aren't there */
- if(msg->deleted)
- continue;
- if(mkstat(&d, f->mb, msg, Qdir) < 0)
- continue;
- m = convD2M(&d, &buf[n], blen - n);
- if(off <= pos){
- if(m <= BIT16SZ || m > cnt)
- break;
- n += m;
- cnt -= m;
- }
- pos += m;
- }
-
- /* save a finger pointer for next read of the mbox directory */
- f->foff = pos;
- f->fptr = msg;
- f->fvers = f->mb->vers;
- return n;
-}
-
-int
-readmsgdir(Fid *f, uchar *buf, long off, int cnt, int blen)
-{
- Dir d;
- int i, n, m;
- long pos;
- Message *msg;
-
- n = 0;
- pos = 0;
- for(i = 0; i < Qmax; i++){
- if(mkstat(&d, f->mb, f->m, i) < 0)
- continue;
- m = convD2M(&d, &buf[n], blen - n);
- if(off <= pos){
- if(m <= BIT16SZ || m > cnt)
- return n;
- n += m;
- cnt -= m;
- }
- pos += m;
- }
- for(msg = f->m->part; msg != nil; msg = msg->next){
- if(mkstat(&d, f->mb, msg, Qdir) < 0)
- continue;
- m = convD2M(&d, &buf[n], blen - n);
- if(off <= pos){
- if(m <= BIT16SZ || m > cnt)
- break;
- n += m;
- cnt -= m;
- }
- pos += m;
- }
- return n;
-}
-
-static int
-mboxctlread(Mailbox *mb, char **p)
-{
- static char buf[128];
-
- *p = buf;
- return snprint(*p, sizeof buf, "%s\n%ld\n", mb->path, mb->vers);
-}
-
-char*
-rread(Fid *f)
-{
- char *p;
- int t, i, n, cnt;
- long off;
-
- rhdr.count = 0;
- off = thdr.offset;
- cnt = thdr.count;
- if(cnt > messagesize - IOHDRSZ)
- cnt = messagesize - IOHDRSZ;
- rhdr.data = (char*)mbuf;
-
- t = FILE(f->qid.path);
- if(f->qid.type & QTDIR){
- if(t == Qtop)
- n = readtopdir(f, mbuf, off, cnt, messagesize - IOHDRSZ);
- else if(t == Qmbox)
- n = readmboxdir(f, mbuf, off, cnt, messagesize - IOHDRSZ);
- else
- n = readmsgdir(f, mbuf, off, cnt, messagesize - IOHDRSZ);
- rhdr.count = n;
- return nil;
- }
-
- switch(t){
- case Qctl:
- break;
- case Qmboxctl:
- i = mboxctlread(f->mb, &p);
- goto output;
- case Qheader:
- if(cacheheaders(f->mb, f->m) < 0)
- return Eio;
- rhdr.count = readheader(f->m, (char*)mbuf, off, cnt);
- putcache(f->mb, f->m);
- break;
- case Qinfo:
- if(cnt > sizeof mbuf)
- cnt = sizeof mbuf;
- if((i = readinfo(f->mb, f->m, (char*)mbuf, off, cnt)) < 0)
- return Eio;
- rhdr.count = i;
- break;
- case Qrawunix:
- if(f->mb->addfrom && Topmsg(f->mb, f->m)){
- if(cacheheaders(f->mb, f->m) < 0)
- return Eio;
- p = f->m->unixheader;
- if(off < strlen(p)){
- rhdr.count = strlen(p + off);
- memmove(mbuf, p + off, rhdr.count);
- break;
- }
- off -= strlen(p);
- }
- default:
- i = fileinfo(f->mb, f->m, t, &p);
- output:
- if(i < 0)
- return Eio;
- if(off < i){
- if(off + cnt > i)
- cnt = i - off;
- if(cnt > sizeof mbuf)
- cnt = sizeof mbuf;
- memmove(mbuf, p + off, cnt);
- rhdr.count = cnt;
- }
- break;
- }
- return nil;
-}
-
-char*
-modflags(Mailbox *mb, Message *m, char *p)
-{
- char *err;
- uchar f;
-
- f = m->flags;
- if(err = txflags(p, &f))
- return err;
- if(f != m->flags){
- m->flags = f;
- m->cstate |= Cidxstale;
- m->cstate |= Cmod;
- if(mb->modflags != nil)
- mb->modflags(mb, m, f);
- }
- return nil;
-}
-
-char*
-rwrite(Fid *f)
-{
- char *argvbuf[1024], **argv, file[Pathlen], *err, *v0;
- int i, t, argc, flags;
-
- t = FILE(f->qid.path);
- rhdr.count = thdr.count;
- if(thdr.count == 0)
- return Ebadctl;
- if(thdr.data[thdr.count - 1] == '\n')
- thdr.data[thdr.count - 1] = 0;
- else
- thdr.data[thdr.count] = 0;
- argv = argvbuf;
- switch(t){
- case Qctl:
- memset(argvbuf, 0, sizeof argvbuf);
- argc = tokenize(thdr.data, argv, nelem(argvbuf) - 1);
- if(argc == 0)
- return Ebadctl;
- if(strcmp(argv[0], "open") == 0 || strcmp(argv[0], "create") == 0){
- if(argc == 1 || argc > 3)
- return Ebadargs;
- mboxpathbuf(file, sizeof file, getlog(), argv[1]);
- if(argc == 3){
- if(strchr(argv[2], '/') != nil)
- return "/ not allowed in mailbox name";
- }else
- argv[2] = nil;
- flags = 0;
- if(strcmp(argv[0], "create") == 0)
- flags |= DMcreate;
- return newmbox(file, argv[2], flags, nil);
- }
- if(strcmp(argv[0], "close") == 0){
- if(argc < 2)
- return nil;
- for(i = 1; i < argc; i++)
- freembox(argv[i]);
- return nil;
- }
- if(strcmp(argv[0], "delete") == 0){
- if(argc < 3)
- return nil;
- delmessages(argc - 1, argv + 1);
- return nil;
- }
- if(strcmp(argv[0], "flag") == 0){
- if(argc < 3)
- return nil;
- return flagmessages(argc - 1, argv + 1);
- }
- if(strcmp(argv[0], "move") == 0){
- if(argc < 4)
- return nil;
- return movemessages(argc - 1, argv + 1);
- }
- if(strcmp(argv[0], "remove") == 0){
- v0 = argv0;
- flags = 0;
- ARGBEGIN{
- default:
- argv0 = v0;
- return Ebadargs;
- case 'r':
- flags |= Rrecur;
- break;
- case 't':
- flags |= Rtrunc;
- break;
- }ARGEND
- argv0 = v0;
- if(argc == 0)
- return Ebadargs;
- for(; *argv; argv++){
- mboxpathbuf(file, sizeof file, getlog(), *argv);
- if(err = newmbox(file, nil, 0, nil))
- return err;
- if(err = removembox(file, flags))
- return err;
- }
- return 0;
- }
- if(strcmp(argv[0], "rename") == 0){
- v0 = argv0;
- flags = 0;
- ARGBEGIN{
- case 't':
- flags |= Rtrunc;
- break;
- }ARGEND
- argv0 = v0;
- if(argc != 2)
- return Ebadargs;
- return mboxrename(argv[0], argv[1], flags);
- }
- return Ebadctl;
- case Qmboxctl:
- if(f->mb->ctl == nil)
- break;
- argc = tokenize(thdr.data, argv, nelem(argvbuf));
- if(argc == 0)
- return Ebadctl;
- return f->mb->ctl(f->mb, argc, argv);
- case Qflags:
- /*
- * modifying flags on subparts is a little strange.
- */
- if(!Topmsg(f->mb, f->m))
- break;
- return modflags(f->mb, f->m, thdr.data);
- }
- return Eperm;
-}
-
-char*
-rclunk(Fid *f)
-{
- f->busy = 1;
- /* coherence(); */
- f->fid = -1;
- f->open = 0;
- if(f->m != nil){
- msgdecref(f->mb, f->m);
- f->m = nil;
- }
- if(f->mb != nil){
- mboxdecref(f->mb);
- f->mb = nil;
- }
- f->busy = 0;
- return 0;
-}
-
-char *
-rremove(Fid *f)
-{
- if(f->mb != nil && f->m != nil && Topmsg(f->mb, f->m) && f->m->deleted == 0)
- f->m->deleted = Deleted;
- return rclunk(f);
-}
-
-char *
-rstat(Fid *f)
-{
- Dir d;
-
- if(FILE(f->qid.path) == Qmbox)
- syncmbox(f->mb, 1);
- if(mkstat(&d, f->mb, f->m, FILE(f->qid.path)) < 0)
- return Eio;
- rhdr.nstat = convD2M(&d, mbuf, messagesize - IOHDRSZ);
- rhdr.stat = mbuf;
- return 0;
-}
-
-char*
-rwstat(Fid*)
-{
- return Eperm;
-}
-
-static Fid*
-checkfid(Fid *f)
-{
- if(f->busy)
- switch(FILE(f->qid.path)){
- case Qtop:
- case Qctl:
- assert(f->mb == nil);
- assert(f->m == nil);
- break;
- case Qmbox:
- case Qmboxctl:
- assert(f->mb != nil && f->mb->refs > 0);
- assert(f->m == nil);
- break;
- default:
- assert(f->mb != nil && f->mb->refs > 0);
- assert(f->m != nil && f->m->refs > 0);
- break;
- }
- return f;
-}
-
-Fid*
-newfid(int fid)
-{
- Fid *f, *ff;
-
- ff = 0;
- for(f = fids; f; f = f->next)
- if(f->fid == fid)
- return checkfid(f);
- else if(!ff && !f->busy)
- ff = f;
- if(ff){
- ff->fid = fid;
- ff->fptr = nil;
- return ff;
- }
- f = emalloc(sizeof *f);
- f->fid = fid;
- f->fptr = nil;
- f->next = fids;
- fids = f;
- return f;
-}
-
-void
-io(void)
-{
- char *err;
- int n;
-
- /* start a process to watch the mailboxes*/
- if(plumbing || biffing)
- switch(rfork(RFPROC|RFMEM)){
- case -1:
- /* oh well */
- break;
- case 0:
- reader();
- exits("");
- default:
- break;
- }
-
- for(;;){
- n = read9pmsg(mfd[0], mdata, messagesize);
- if(n <= 0)
- return;
- if(convM2S(mdata, n, &thdr) == 0)
- continue;
-
- if(Dflag)
- fprint(2, "%s:<-%F\n", argv0, &thdr);
-
- qlock(&synclock);
- rhdr.data = (char*)mdata + messagesize;
- if(!fcalls[thdr.type])
- err = "bad fcall type";
- else
- err = fcalls[thdr.type](newfid(thdr.fid));
- if(err){
- rhdr.type = Rerror;
- rhdr.ename = err;
- }else{
- rhdr.type = thdr.type + 1;
- rhdr.fid = thdr.fid;
- }
- rhdr.tag = thdr.tag;
- qunlock(&synclock);
-
- if(Dflag)
- fprint(2, "%s:->%F\n", argv0, &rhdr);
- n = convS2M(&rhdr, mdata, messagesize);
- if(write(mfd[1], mdata, n) != n)
- error("mount write");
- }
-}
-
-static char *readerargv[] = {"upas/fs", "plumbing", 0};
-
-void
-reader(void)
-{
- ulong t;
- Dir *d;
- Mailbox *mb;
-
- setname(readerargv);
- sleep(15*1000);
- for(;;){
- qlock(&synclock);
- t = time(0);
- for(mb = mbl; mb != nil; mb = mb->next){
- if(mb->waketime != 0 && t >= mb->waketime){
- mb->waketime = 0;
- break;
- }
- if(mb->d != nil){
- d = dirstat(mb->path);
- if(d != nil){
- if(d->qid.path != mb->d->qid.path
- || d->qid.vers != mb->d->qid.vers){
- free(d);
- break;
- }
- free(d);
- }
- }
- }
- if(mb != nil) {
- syncmbox(mb, 1);
- qunlock(&synclock);
- } else {
- qunlock(&synclock);
- sleep(15*1000);
- }
- }
-}
-
-void
-error(char *s)
-{
- syskillpg(getpid());
- eprint("upas/fs: fatal error: %s: %r\n", s);
- exits(s);
-}
-
-
-typedef struct Ignorance Ignorance;
-struct Ignorance
-{
- Ignorance *next;
- char *str;
- int len;
-};
-static Ignorance *ignorance;
-
-/*
- * read the file of headers to ignore
- */
-static void
-readignore(void)
-{
- char *p;
- Ignorance *i;
- Biobuf *b;
-
- if(ignorance != nil)
- return;
-
- b = Bopen("/mail/lib/ignore", OREAD);
- if(b == 0)
- return;
- while(p = Brdline(b, '\n')){
- p[Blinelen(b) - 1] = 0;
- while(*p && (*p == ' ' || *p == '\t'))
- p++;
- if(*p == '#')
- continue;
- i = emalloc(sizeof *i);
- i->len = strlen(p);
- i->str = strdup(p);
- if(i->str == 0){
- free(i);
- break;
- }
- i->next = ignorance;
- ignorance = i;
- }
- Bterm(b);
-}
-
-static int
-ignore(char *p, int n)
-{
- Ignorance *i;
-
- readignore();
- for(i = ignorance; i != nil; i = i->next)
- if(i->len <= n && cistrncmp(i->str, p, i->len) == 0)
- return 1;
- return 0;
-}
-
-int
-readheader(Message *m, char *buf, int off, int cnt)
-{
- char *s, *end, *se, *p, *e, *to;
- int n, ns, salloc;
-
- to = buf;
- p = m->header;
- e = m->hend;
- s = emalloc(salloc = 2048);
- end = s + salloc;
-
- /* copy in good headers */
- while(cnt > 0 && p < e){
- if((n = hdrlen(p, e)) <= 0)
- break;
- if(ignore(p, n)){
- p += n;
- continue;
- }
- if(n + 1 > salloc){
- s = erealloc(s, salloc = n + 1);
- end = s + salloc;
- }
- se = rfc2047(s, end, p, n, 0);
- ns = se - s;
- if(off > 0){
- if(ns <= off){
- off -= ns;
- p += n;
- continue;
- }
- ns -= off;
- }
- if(ns > cnt)
- ns = cnt;
- memmove(to, s + off, ns);
- to += ns;
- p += n;
- cnt -= ns;
- off = 0;
- }
- free(s);
- return to - buf;
-}
-
-ulong
-hash(char *s)
-{
- ulong c, h;
-
- h = 0;
- while(c = *s++)
- h = h*131 + c;
-
- return h;
-}
-
-Hash*
-hlook(uvlong ppath, char *name)
-{
- ulong h;
- Hash *hp;
-
- h = (hash(name)+ppath) % nelem(htab);
- for(hp = htab[h]; hp != nil; hp = hp->next)
- if(ppath == hp->ppath && strcmp(name, hp->name) == 0)
- return hp;
- return nil;
-}
-
-void
-henter(uvlong ppath, char *name, Qid qid, Message *m, Mailbox *mb)
-{
- ulong h;
- Hash *hp, **l;
-
- h = (hash(name)+ppath) % nelem(htab);
- for(l = &htab[h]; *l != nil; l = &(*l)->next){
- hp = *l;
- if(ppath == hp->ppath && strcmp(name, hp->name) == 0){
- hp->m = m;
- hp->mb = mb;
- hp->qid = qid;
- return;
- }
- }
- *l = hp = emalloc(sizeof(*hp));
- hp->m = m;
- hp->mb = mb;
- hp->qid = qid;
- hp->name = name;
- hp->ppath = ppath;
-}
-
-void
-hfree(uvlong ppath, char *name)
-{
- ulong h;
- Hash *hp, **l;
-
- h = (hash(name)+ppath) % nelem(htab);
- for(l = &htab[h]; *l != nil; l = &(*l)->next){
- hp = *l;
- if(ppath == hp->ppath && strcmp(name, hp->name) == 0){
- hp->mb = nil;
- *l = hp->next;
- free(hp);
- break;
- }
- }
-}
-
-void
-post(char *name, char *envname, int srvfd)
-{
- char buf[32];
- int fd;
-
- fd = create(name, OWRITE, 0600);
- if(fd < 0)
- error("post failed");
- snprint(buf, sizeof buf, "%d", srvfd);
- if(write(fd, buf, strlen(buf)) != strlen(buf))
- error("srv write");
- close(fd);
- putenv(envname, name);
-}
--- a/sys/src/cmd/upas/fs/mbox.c
+++ /dev/null
@@ -1,1725 +1,0 @@
-#include "common.h"
-#include <ctype.h>
-#include <plumb.h>
-#include <libsec.h>
-#include "dat.h"
-
-typedef struct Header Header;
-struct Header {
- char *type;
- uintptr offset;
- char *(*f)(Message*, Header*, char*, char*);
- int len;
- int str;
-};
-
-/* headers */
-static char *ctype(Message*, Header*, char*, char*);
-static char *cencoding(Message*, Header*, char*, char*);
-static char *cdisposition(Message*, Header*, char*, char*);
-static char *from822(Message*, Header*, char*, char*);
-static char *replace822(Message*, Header*, char*, char*);
-static char *concat822(Message*, Header*, char*, char*);
-static char *copy822(Message*, Header*, char*, char*);
-static char *ref822(Message*, Header*, char*, char*);
-
-enum
-{
- Mhead = 11, /* offset of first mime header */
-};
-
-#define O(x) offsetof(Message, x)
-static Header head[] =
-{
- "date:", O(date822), copy822, 0, 0,
- "from:", O(from), from822, 0, 1,
- "to:", O(to), concat822, 0, 1,
- "sender:", O(sender), replace822, 0, 1,
- "reply-to:", O(replyto), replace822, 0, 1,
- "subject:", O(subject), copy822, 0, 1,
- "cc:", O(cc), concat822, 0, 1,
- "bcc:", O(bcc), concat822, 0, 1,
- "in-reply-to:", O(inreplyto), replace822, 0, 1,
- "message-id:", O(messageid), replace822, 0, 1,
- "references:", ~0, ref822, 0, 0,
-
-[Mhead] "content-type:", ~0, ctype, 0, 0,
- "content-transfer-encoding:", ~0, cencoding, 0, 0,
- "content-disposition:", ~0, cdisposition, 0, 0,
-};
-
-static Mailboxinit *boxinit[] = {
- imap4mbox,
- pop3mbox,
- mdirmbox,
- plan9mbox,
-};
-
-static void delmessage(Mailbox*, Message*);
-static void mailplumb(Mailbox*, Message*);
-
-char*
-syncmbox(Mailbox *mb, int doplumb)
-{
- char *s;
- int n, d, y, a;
- Message *m, *next;
-
- if(mb->syncing)
- return nil;
-
- mb->syncing = 1;
-
- a = mb->root->subname;
- if(rdidxfile(mb) == -2)
- wridxfile(mb);
- if(s = mb->sync(mb)){
- mb->syncing = 0;
- return s;
- }
- n = 0;
- d = 0;
- y = 0;
- for(m = mb->root->part; m; m = next){
- next = m->next;
- if(m->deleted == 0){
- if((m->cstate & Cidx) == 0){
- cachehash(mb, m);
- m->cstate |= Cnew;
- n++;
- }
- if((doplumb && m->cstate & (Cnew|Cmod)) && ensurecache(mb, m) == 0){
- mailplumb(mb, m);
- msgdecref(mb, m);
- }
- m->cstate &= ~(Cnew|Cmod);
- }
- if(m->cstate & Cidxstale)
- y++;
- if(m->deleted == 0 || m->refs > 0)
- continue;
- if(mb->delete && m->inmbox && m->deleted & Deleted)
- mb->delete(mb, m);
- if(!m->inmbox){
- if(doplumb)
- mailplumb(mb, m);
- delmessage(mb, m);
- d++;
- }
- }
- a = mb->root->subname - a;
- assert(a >= 0);
- if(n + d + y + a){
- Hash *h;
-
- iprint("deleted: %d; new %d; stale %d\n", d, n, y);
- logmsg(nil, "deleted: %d; new %d; stale %d", d, n, y);
- wridxfile(mb);
-
- mb->vers++;
- if(mb->refs > 0 && (h = hlook(PATH(0, Qtop), mb->name)) != nil && h->mb == mb)
- h->qid.vers = mb->vers;
- }
-
- mb->syncing = 0;
-
- return nil;
-}
-
-/*
- * not entirely clear where the locking should take place, if
- * it is required.
- */
-char*
-mboxrename(char *a, char *b, int flags)
-{
- char f0[Pathlen + 4], f1[Pathlen + 4], *err, *p0, *p1;
- Mailbox *mb;
-
- snprint(f0, sizeof f0, "%s", a);
- snprint(f1, sizeof f1, "%s", b);
- err = newmbox(f0, nil, 0, &mb);
- dprint("mboxrename %s %s -> %s\n", f0, f1, err);
- if(err == nil && mb->rename == nil)
- err = "rename not supported";
- if(err)
- goto done;
- err = mb->rename(mb, f1, flags);
- if(err)
- goto done;
- if(flags & Rtrunc)
- /* we're comitted, so forget bailing */
- err = newmbox(f0, nil, DMcreate, 0);
- p0 = f0 + strlen(f0);
- p1 = f1 + strlen(f1);
-
- strcat(f0, ".idx");
- strcat(f1, ".idx");
- rename(f0, f1, 0);
-
- *p0 = *p1 = 0;
- strcat(f0, ".imp");
- strcat(f1, ".imp");
- rename(f0, f1, 0);
-
- hfree(PATH(0, Qtop), mb->name);
- snprint(mb->path, sizeof mb->path, "%s", b);
- p0 = strrchr(mb->path, '/') + 1;
- if(p0 == (char*)1)
- p0 = mb->path;
- snprint(mb->name, sizeof mb->name, "%s", p0);
- henter(PATH(0, Qtop), mb->name,
- (Qid){PATH(mb->id, Qmbox), mb->vers, QTDIR}, nil, mb);
-done:
- return err;
-}
-
-static void
-initheaders(void)
-{
- int i;
- static int already;
-
- if(already)
- return;
- already = 1;
- for(i = 0; i < nelem(head); i++)
- head[i].len = strlen(head[i].type);
-}
-
-static ulong
-newid(void)
-{
- ulong rv;
- static ulong id;
- static Lock idlock;
-
- lock(&idlock);
- rv = ++id;
- unlock(&idlock);
-
- return rv;
-}
-
-char*
-newmbox(char *path, char *name, int flags, Mailbox **r)
-{
- char *p, *rv;
- int i;
- Mailbox *mb, **l;
-
- if(r)
- *r = nil;
- initheaders();
- mb = emalloc(sizeof *mb);
- mb->flags = flags;
- strncpy(mb->path, path, sizeof mb->path - 1);
- p = name;
- if(p == nil){
- p = strrchr(path, '/');
- if(p == nil)
- p = path;
- else
- p++;
- if(*p == 0){
- free(mb);
- return "bad mbox name";
- }
- }
- strncpy(mb->name, p, sizeof mb->name - 1);
- mb->idxread = genericidxread;
- mb->idxwrite = genericidxwrite;
- mb->idxinvalid = genericidxinvalid;
-
- /* check for a mailbox type */
- rv = Enotme; /* can't happen; shut compiler up */
- for(i = 0; i < nelem(boxinit); i++)
- if((rv = boxinit[i](mb, path)) != Enotme)
- break;
- if(rv){
- free(mb);
- return rv;
- }
-
- /* make sure name isn't taken */
- for(l = &mbl; *l != nil; l = &(*l)->next)
- if(strcmp((*l)->name, mb->name) == 0){
- if(strcmp(path, (*l)->path) == 0)
- rv = nil;
- else
- rv = "mbox name in use";
- if(mb->close)
- mb->close(mb);
- free(mb);
- return rv;
- }
-
- /* always try locking */
- mb->dolock = 1;
- mb->refs = 1;
- mb->next = nil;
- mb->id = newid();
- mb->root = newmessage(nil);
- mtreeinit(mb);
-
- *l = mb;
-
- henter(PATH(0, Qtop), mb->name,
- (Qid){PATH(mb->id, Qmbox), mb->vers, QTDIR}, nil, mb);
- if(mb->ctl)
- henter(PATH(mb->id, Qmbox), "ctl",
- (Qid){PATH(mb->id, Qmboxctl), 0, QTFILE}, nil, mb);
- if(r)
- *r = mb;
-
- return syncmbox(mb, 0);
-}
-
-/* close the named mailbox */
-void
-freembox(char *name)
-{
- Mailbox **l, *mb;
-
- for(l = &mbl; (mb = *l) != nil; l = &mb->next)
- if(strcmp(name, mb->name) == 0){
- *l = mb->next;
- mb->next = nil;
- hfree(PATH(0, Qtop), mb->name);
- if(mb->ctl)
- hfree(PATH(mb->id, Qmbox), "ctl");
- mboxdecref(mb);
- break;
- }
-}
-
-char*
-removembox(char *name, int flags)
-{
- Mailbox *mb;
-
- for(mb = mbl; mb != nil; mb = mb->next)
- if(strcmp(name, mb->path) == 0){
- mb->flags |= ORCLOSE;
- mb->rmflags = flags;
- freembox(mb->name);
- return 0;
- }
- return "maibox not found";
-}
-
-void
-syncallmboxes(void)
-{
- Mailbox *mb;
- char *err;
-
- for(mb = mbl; mb != nil; mb = mb->next)
- if(err = syncmbox(mb, 0))
- eprint("syncmbox: %s\n", err);
-}
-
-/*
- * look for the date in the first Received: line.
- * it's likely to be the right time zone (it's
- * the local system) and in a convenient format.
- */
-static int
-rxtotm(Message *m, Tm *tm)
-{
- char *p, *q;
- int r;
-
- if(cistrncmp(m->header, "received:", 9))
- return -1;
- q = strchr(m->header, ';');
- if(!q)
- return -1;
- p = q;
- while((p = strchr(p, '\n')) != nil){
- if(p[1] != ' ' && p[1] != '\t' && p[1] != '\n')
- break;
- p++;
- }
- if(!p)
- return -1;
- *p = '\0';
- r = strtotm(q + 1, tm);
- *p = '\n';
- return r;
-}
-
-static Message*
-gettopmsg(Mailbox *mb, Message *m)
-{
- while(!Topmsg(mb, m))
- m = m->whole;
- return m;
-}
-
-static void
-datesec(Mailbox *mb, Message *m)
-{
- vlong v;
- Tm tm;
-
- if(m->fileid > 1000000ull<<8)
- return;
- if(m->unixdate && strtotm(m->unixdate, &tm) >= 0)
- v = tmnorm(&tm);
- else if(m->date822 && strtotm(m->date822, &tm) >= 0)
- v = tmnorm(&tm);
- else if(rxtotm(m, &tm) >= 0)
- v = tmnorm(&tm);
- else{
- logmsg(gettopmsg(mb, m), "%s:%s: datasec %s %s\n", mb->path,
- m->whole? m->whole->name: "?",
- m->name, m->type);
- if(Topmsg(mb, m) || strcmp(m->type, "message/rfc822") == 0)
- abort();
- v = 0;
- }
- m->fileid = v<<8;
-}
-
-/*
- * parse a message
- */
-extern void sanemsg(Message*);
-extern void sanembmsg(Mailbox*, Message*);
-
-static Message*
-haschild(Message *m, int i)
-{
- for(m = m->part; m && i; i--)
- m = m->next;
- return m;
-}
-
-static void
-parseattachments(Message *m, Mailbox *mb)
-{
- char *p, *x;
- int i;
- Message *nm, **l;
-
- /* if there's a boundary, recurse... */
- dprint("parseattachments %p %ld boundary %s\n", m->start, (ulong)(m->end - m->start), m->boundary);
- if(m->boundary != nil){
- p = m->body;
- nm = nil;
- l = &m->part;
- for(i = 0;;){
- x = strstr(p, m->boundary);
- /* two sequential boundaries; ignore nil message */
- if(nm && x == p){
- p = strchr(x, '\n');
- if(p == nil){
- nm->rbend = nm->bend = nm->end = x;
- sanemsg(nm);
- break;
- }
- p = p + 1;
- continue;
- }
- /* no boundary, we're done */
- if(x == nil){
- if(nm != nil)
- nm->rbend = nm->bend = nm->end = m->bend;
- break;
- }
- /* boundary must be at the start of a line */
- if(x != m->body && x[-1] != '\n'){
- p = x + 1;
- continue;
- }
-
- if(nm != nil)
- nm->rbend = nm->bend = nm->end = x;
- x += strlen(m->boundary);
-
- /* is this the last part? ignore anything after it */
- if(strncmp(x, "--", 2) == 0)
- break;
- p = strchr(x, '\n');
- if(p == nil)
- break;
- if((nm = haschild(m, i++)) == nil){
- nm = newmessage(m);
- *l = nm;
- l = &nm->next;
- }
- nm->start = ++p;
- assert(nm->ballocd == 0);
- nm->mheader = nm->header = nm->body = nm->rbody = nm->start;
- }
- for(nm = m->part; nm != nil; nm = nm->next){
- nm->size = nm->end - nm->start;
- parse(mb, nm, 0, 1);
- cachehash(mb, nm); /* botchy place for this */
- }
- return;
- }
-
- /* if we've got an rfc822 message, recurse... */
- if(strcmp(m->type, "message/rfc822") == 0){
- if((nm = haschild(m, 0)) == nil){
- nm = newmessage(m);
- m->part = nm;
- }
- assert(nm->ballocd == 0);
- nm->start = nm->header = nm->body = nm->rbody = m->body;
- nm->end = nm->bend = nm->rbend = m->bend;
- nm->size = nm->end - nm->start;
- parse(mb, nm, 0, 0);
- cachehash(mb, nm); /* botchy place for this */
- }
-}
-
-static void
-parseunix(Message *m)
-{
- char *s, *p;
-
- m->unixheader = smprint("%.*s", utfnlen(m->start, m->header - m->start), m->start);
- s = m->unixheader + 5;
- if((p = strchr(s, ' ')) == nil)
- return;
- *p = 0;
- free(m->unixfrom);
- m->unixfrom = strdup(s);
- *p = ' ';
- m->unixdate = ++p;
-}
-
-void
-parseheaders(Mailbox *mb, Message *m, int addfrom, int justmime)
-{
- char *p, *e, *o, *t, *s;
- int i, i0, n;
- uintptr a;
-
- if(m->header == nil)
- m->header = m->start;
-
- /* parse unix header */
- if(!justmime && !addfrom && m->unixheader == nil){
- if(strncmp(m->start, "From ", 5) == 0)
- if((e = memchr(m->start, '\n', m->end - m->start)) != nil){
- m->header = e + 1;
- parseunix(m);
- }
- }
-
- /* parse mime headers */
- p = m->mheader = m->mhend = m->header;
- i0 = 0;
- if(justmime)
- i0 = Mhead;
- s = emalloc(2048);
- e = s + 2048 - 1;
- while((n = hdrlen(p, m->end)) > 0){
- if(n > e - s){
- s = erealloc(s, n);
- e = s + n - 1;
- }
- rfc2047(s, e, p, n, 1);
- p += n;
-
- for(i = i0; i < nelem(head); i++)
- if(!cistrncmp(s, head[i].type, head[i].len)){
- a = head[i].offset;
- if(a != ~0){
- if(o = *(char**)((char*)m + a))
- continue;
- t = head[i].f(m, head + i, o, s);
- *(char**)((char*)m + a) = t;
- }else
- head[i].f(m, head + i, 0, s);
- break;
- }
- }
- free(s);
- /* the blank line isn't really part of the body or header */
- if(justmime){
- m->mhend = p;
- m->hend = m->header;
- } else{
- m->hend = p;
- m->mhend = m->header;
- }
- if(*p == '\n')
- p++;
- m->rbody = m->body = p;
-
- if(!justmime)
- datesec(mb, m);
-
- /*
- * only fake header for top-level messages for pop3 and imap4
- * clients (those protocols don't include the unix header).
- * adding the unix header all the time screws up mime-attached
- * rfc822 messages.
- */
- if(!addfrom && m->unixfrom == nil) {
- free(m->unixheader);
- m->unixheader = nil;
- } else if(m->unixheader == nil){
- if(m->unixfrom != nil && strcmp(m->unixfrom, "???") != 0)
- p = m->unixfrom;
- else if(m->from != nil)
- p = m->from;
- else
- p = "???";
- m->unixheader = smprint("From %s %Δ\n", p, m->fileid);
- }
- m->unixdate = nil;
-
- m->cstate |= Cheader;
-sanembmsg(mb, m);
-}
-
-char*
-promote(char *s)
-{
- return s? strdup(s): nil;
-}
-
-void
-parsebody(Message *m, Mailbox *mb)
-{
- Message *nm;
-
- /* recurse */
- if(strncmp(m->type, "multipart/", 10) == 0)
- parseattachments(m, mb);
- else if(strcmp(m->type, "message/rfc822") == 0){
- decode(m);
- parseattachments(m, mb);
- nm = m->part;
-
- /* promote headers */
- if(m->replyto == nil && m->from == nil && m->sender == nil){
- m->from = promote(nm->from);
- m->to = promote(nm->to);
- m->date822 = promote(nm->date822);
- m->sender = promote(nm->sender);
- m->replyto = promote(nm->replyto);
- m->subject = promote(nm->subject);
- }
- }else if(strncmp(m->type, "text/", 5) == 0)
- sanemsg(m);
-
- free(m->boundary);
- m->boundary = nil;
-
- if(m->replyto == nil){
- if(m->from != nil)
- m->replyto = strdup(m->from);
- else if(m->sender != nil)
- m->replyto = strdup(m->sender);
- else if(m->unixfrom != nil)
- m->replyto = strdup(m->unixfrom);
- }
- if(m->from == nil && m->unixfrom != nil)
- m->from = strdup(m->unixfrom);
-
- free(m->unixfrom);
- m->unixfrom = nil;
-
- m->rawbsize = m->rbend - m->rbody;
- m->cstate |= Cbody;
-}
-
-void
-parse(Mailbox *mb, Message *m, int addfrom, int justmime)
-{
- sanemsg(m);
- if((m->cstate & Cheader) == 0)
- parseheaders(mb, m, addfrom, justmime);
- parsebody(m, mb);
- sanemsg(m);
-}
-
-static char*
-skipwhite(char *p)
-{
- while(isascii(*p) && isspace(*p))
- p++;
- return p;
-}
-
-static char*
-skiptosemi(char *p)
-{
- while(*p && *p != ';')
- p++;
- while(*p == ';' || (isascii(*p) && isspace(*p)))
- p++;
- return p;
-}
-
-static char*
-getstring(char *p, char *s, char *e, int dolower)
-{
- int c;
-
- p = skipwhite(p);
- if(*p == '"'){
- for(p++; (c = *p) != '"'; p++){
- if(c == '\\')
- c = *++p;
- /*
- * 821 says <x> after \ can be anything at all.
- * we just don't care.
- */
- if(c == 0)
- break;
- if(c < ' ')
- continue;
- if(dolower && c >= 'A' && c <= 'Z')
- c += 0x20;
- s = sputc(s, e, c);
- }
- if(*p == '"')
- p++;
- }else{
- for(; (c = *p) && !isspace(c) && c != ';'; p++){
- if(c == '\\')
- c = *++p;
- /*
- * 821 says <x> after \ can be anything at all.
- * we just don't care.
- */
- if(c == 0)
- break;
- if(c < ' ')
- continue;
- if(dolower && c >= 'A' && c <= 'Z')
- c += 0x20;
- s = sputc(s, e, c);
- }
- }
- *s = 0;
- return p;
-}
-
-static void
-setfilename(Message *m, char *p)
-{
- char buf[Pathlen];
-
- dprint("setfilename %p %s -> %s\n", m, m->filename, p);
- if(m->filename != nil)
- return;
- getstring(p, buf, buf + sizeof buf - 1, 0);
- m->filename = smprint("%s", buf);
- for(p = m->filename; *p; p++)
- if(*p == ' ' || *p == '\t' || *p == ';')
- *p = '_';
-}
-
-static char*
-rtrim(char *p)
-{
- char *e;
-
- if(p == 0)
- return p;
- e = p + strlen(p) - 1;
- while(e > p && isascii(*e) && isspace(*e))
- *e-- = 0;
- return p;
-}
-
-static char*
-unfold(char *s)
-{
- char *p, *q;
-
- q = s;
- for(p = q; *p != '\0'; p++)
- if(*p != '\r' && *p != '\n')
- *q++ = *p;
- *q = '\0';
- return s;
-}
-
-static char*
-addr822(char *p, char **ac)
-{
- int n, c, space, incomment, addrdone, inanticomment, quoted;
- char s[128+1], *ps, *e, *x, *list;
-
- list = 0;
- s[0] = 0;
- ps = s;
- e = s + sizeof s;
- space = quoted = incomment = addrdone = inanticomment = 0;
- n = 0;
- for(; c = *p; p++){
- if(!inanticomment && !quoted && !space && ps != s && c == ' '){
- ps = sputc(ps, e, c);
- space = 1;
- continue;
- }
- space = 0;
- if(!quoted && isspace(c) || c == '\r')
- continue;
- /* strings are always treated as atoms */
- if(!quoted && c == '"'){
- if(!addrdone && !incomment && !ac)
- ps = sputc(ps, e, c);
- for(p++; c = *p; p++){
- if(ac && c == '"')
- break;
- if(!addrdone && !incomment && c != '\r' && c != '\n')
- ps = sputc(ps, e, c);
- if(!quoted && *p == '"')
- break;
- if(*p == '\\')
- quoted = 1;
- else
- quoted = 0;
- }
- if(c == 0)
- break;
- quoted = 0;
- continue;
- }
-
- /* ignore everything in an expicit comment */
- if(!quoted && c == '('){
- incomment = 1;
- continue;
- }
- if(incomment){
- if(!quoted && c == ')')
- incomment = 0;
- quoted = 0;
- continue;
- }
-
- /* anticomments makes everything outside of them comments */
- if(!quoted && c == '<' && !inanticomment){
- if(ac){
- *ps-- = 0;
- if(ps > s && *ps == ' ')
- *ps = 0;
- if(*ac){
- *ac = smprint("%s, %s", x=*ac, s);
- free(x);
- }else
- *ac = smprint("%s", s);
- }
-
- inanticomment = 1;
- ps = s;
- continue;
- }
- if(!quoted && c == '>' && inanticomment){
- addrdone = 1;
- inanticomment = 0;
- continue;
- }
-
- /* commas separate addresses */
- if(!quoted && c == ',' && !inanticomment){
- *ps = 0;
- addrdone = 0;
- if(n++ != 0){
- list = smprint("%s %s", x=list, s);
- free(x);
- }else
- list = smprint("%s", s);
- ps = s;
- continue;
- }
-
- /* what's left is part of the address */
- ps = sputc(ps, e, c);
-
- /* quoted characters are recognized only as characters */
- if(c == '\\')
- quoted = 1;
- else
- quoted = 0;
-
- }
-
- if(ps > s){
- *ps = 0;
- if(n != 0){
- list = smprint("%s %s", x=list, s);
- free(x);
- }else
- list = smprint("%s", s);
- }
- return rtrim(list);
-}
-
-/*
- * per rfc2822 §4.5.3, permit multiple to, cc and bcc headers by
- * concatenating their values.
- */
-
-static char*
-concat822(Message*, Header *h, char *o, char *p)
-{
- char *s, *n;
-
- p += strlen(h->type);
- s = addr822(p, 0);
- if(o){
- n = smprint("%s %s", o, s);
- free(s);
- }else
- n = s;
- return n;
-}
-
-static char*
-from822(Message *m, Header *h, char*, char *p)
-{
- if(m->ffrom)
- free(m->ffrom);
- m->from = 0;
- return addr822(p + h->len, &m->ffrom);
-}
-
-static char*
-replace822(Message *, Header *h, char*, char *p)
-{
- return addr822(p + h->len, 0);
-}
-
-static char*
-copy822(Message*, Header *h, char*, char *p)
-{
- return rtrim(unfold(strdup(skipwhite(p + h->len))));
-}
-
-static char*
-ref822(Message *m, Header *h, char*, char *p)
-{
- char **a, *s, *f[Nref + 1];
- int i, j, n;
-
- s = strdup(skipwhite(p + h->len));
- n = getfields(s, f, nelem(f), 1, "<> \n\t\r,");
- if(n > Nref)
- n = Nref;
- /*
- * if there are too many references, drop from the beginning
- * of the list. If someone else has a duplicate, we keep the
- * old duplicate.
- */
- a = m->references;
- for(i = 0; i < n; i++){
- for(j = 0; j < Nref; j++)
- if(a[j] == nil || strcmp(a[j], f[i]) == 0)
- break;
- if(j == Nref){
- free(a[0]);
- memmove(&a[0], &a[1], (Nref - 1) * sizeof(a[0]));
- j--;
- } else if(a[j] != nil)
- continue;
- a[j] = strdup(f[i]);
- }
- free(s);
- return (char*)~0;
-}
-
-static int
-isattribute(char **pp, char *attr)
-{
- char *p;
- int n;
-
- n = strlen(attr);
- p = *pp;
- if(cistrncmp(p, attr, n) != 0)
- return 0;
- p += n;
- while(*p == ' ')
- p++;
- if(*p++ != '=')
- return 0;
- while(*p == ' ')
- p++;
- *pp = p;
- return 1;
-}
-
-static char*
-ctype(Message *m, Header *h, char*, char *p)
-{
- char buf[128], *e;
-
- e = buf + sizeof buf - 1;
- p = getstring(skipwhite(p + h->len), buf, e, 1);
- m->type = intern(buf);
-
- for(; *p; p = skiptosemi(p))
- if(isattribute(&p, "boundary")){
- p = getstring(p, buf, e, 0);
- free(m->boundary);
- m->boundary = smprint("--%s", buf);
- } else if(cistrncmp(p, "multipart", 9) == 0){
- /*
- * the first unbounded part of a multipart message,
- * the preamble, is not displayed or saved
- */
- } else if(isattribute(&p, "name")){
- setfilename(m, p);
- } else if(isattribute(&p, "charset")){
- p = getstring(p, buf, e, 1);
- m->charset = intern(buf);
- }
- return (char*)~0;
-}
-
-static char*
-cencoding(Message *m, Header *h, char*, char *p)
-{
- p = skipwhite(p + h->len);
- if(cistrncmp(p, "base64", 6) == 0)
- m->encoding = Ebase64;
- else if(cistrncmp(p, "quoted-printable", 16) == 0)
- m->encoding = Equoted;
- return (char*)~0;
-}
-
-static char*
-cdisposition(Message *m, Header *h, char*, char *p)
-{
- for(p = skipwhite(p + h->len); *p; p = skiptosemi(p))
- if(cistrncmp(p, "inline", 6) == 0)
- m->disposition = Dinline;
- else if(cistrncmp(p, "attachment", 10) == 0)
- m->disposition = Dfile;
- else if(cistrncmp(p, "filename=", 9) == 0){
- p += 9;
- setfilename(m, p);
- }
- return (char*)~0;
-}
-
-ulong msgallocd;
-ulong msgfreed;
-
-Message*
-newmessage(Message *parent)
-{
- static int id;
- Message *m;
-
- msgallocd++;
-
- m = emalloc(sizeof *m);
- dprint("newmessage %ld %p %p\n", msgallocd, parent, m);
- m->type = intern("text/plain");
- m->charset = intern("iso-8859-1");
- m->cstate = Cidxstale;
- m->flags = Frecent;
- m->id = newid();
- if(parent)
- snprint(m->name, sizeof m->name, "%d", ++(parent->subname));
- if(parent == nil)
- parent = m;
- m->whole = parent;
- m->hlen = -1;
- return m;
-}
-
-/* delete a message from a mailbox */
-static void
-delmessage(Mailbox *mb, Message *m)
-{
- Message **l;
-
- assert(m->refs == 0);
- while(m->part)
- delmessage(mb, m->part);
-
- mb->vers++;
- msgfreed++;
-
- if(m != m->whole){
- /* unchain from parent */
- for(l = &m->whole->part; *l && *l != m; l = &(*l)->next)
- ;
- if(*l != nil)
- *l = m->next;
- m->next = nil;
- /* clear out of name lookup hash table */
- if(m->whole->whole == m->whole)
- hfree(PATH(mb->id, Qmbox), m->name);
- else
- hfree(PATH(m->whole->id, Qdir), m->name);
- hfree(PATH(m->id, Qdir), "xxx"); /* sleezy speedup */
-
- if(Topmsg(mb, m))
- mtreedelete(mb, m);
- cachefree(mb, m);
- idxfree(m);
- }
- free(m);
-}
-
-void
-unnewmessage(Mailbox *mb, Message *parent, Message *m)
-{
- assert(parent->subname > 0);
- m->deleted = Dup;
- delmessage(mb, m);
- parent->subname -= 1;
-}
-
-/* mark messages (identified by path) for deletion */
-char*
-delmessages(int ac, char **av)
-{
- int i, needwrite;
- Mailbox *mb;
- Message *m;
-
- for(mb = mbl; mb != nil; mb = mb->next)
- if(strcmp(av[0], mb->name) == 0)
- break;
- if(mb == nil)
- return "no such mailbox";
-
- needwrite = 0;
- for(i = 1; i < ac; i++)
- for(m = mb->root->part; m != nil; m = m->next)
- if(strcmp(m->name, av[i]) == 0){
- if(!m->deleted){
- needwrite = 1;
- m->deleted = Deleted;
- logmsg(m, "deleting");
- }
- break;
- }
- if(needwrite)
- syncmbox(mb, 1);
- return 0;
-}
-
-char*
-flagmessages(int argc, char **argv)
-{
- char *err, *rerr;
- int i, needwrite;
- Mailbox *mb;
- Message *m;
-
- if(argc%2)
- return "bad flags";
- for(mb = mbl; mb != nil; mb = mb->next)
- if(strcmp(*argv, mb->name) == 0)
- break;
- if(mb == nil)
- return "no such mailbox";
- needwrite = 0;
- rerr = 0;
- for(i = 1; i < argc; i += 2)
- for(m = mb->root->part; m; m = m->next)
- if(strcmp(m->name, argv[i]) == 0){
- if(err = modflags(mb, m, argv[i + 1]))
- rerr = err;
- else
- needwrite = 1;
- }
- if(needwrite)
- syncmbox(mb, 1);
- return rerr;
-}
-
-char*
-movemessages(int argc, char **argv)
-{
- char *err, *dest, *rerr;
- int i, needwrite;
- Mailbox *mb;
- Message *m;
-
- rerr = 0;
- for(mb = mbl; mb != nil; mb = mb->next)
- if(strcmp(*argv, mb->name) == 0)
- break;
- if(mb == nil)
- return "no such mailbox";
- if(mb->move == nil)
- return "move not supported";
- dest = argv[argc - 1];
- needwrite = 0;
- for(i = 1; i < argc - 1; i++)
- for(m = mb->root->part; m; m = m->next)
- if(strcmp(m->name, argv[i]) == 0){
- if(err = mb->move(mb, m, dest))
- rerr = err;
- else
- needwrite = 1;
- }
- if(needwrite)
- syncmbox(mb, 1);
- return rerr;
-}
-
-void
-msgincref(Mailbox *mb, Message *m)
-{
- assert(mb->refs >= 0);
- for(;; m = m->whole){
- assert(m->refs >= 0);
- m->refs++;
- if(Topmsg(mb, m))
- break;
- }
-}
-
-void
-msgdecref(Mailbox *mb, Message *m)
-{
- assert(mb->refs >= 0);
- for(;; m = m->whole){
- assert(m->refs > 0);
- m->refs--;
- if(Topmsg(mb, m)){
- if(m->refs == 0){
- if(m->deleted)
- syncmbox(mb, 1);
- else
- putcache(mb, m);
- }
- break;
- }
- }
-}
-
-void
-mboxincref(Mailbox *mb)
-{
- assert(mb->refs > 0);
- mb->refs++;
-}
-
-static void
-mbrmidx(char *path, int flags)
-{
- char buf[Pathlen];
-
- snprint(buf, sizeof buf, "%s.idx", path);
- vremove(buf);
- if((flags & Rtrunc) == 0){
- snprint(buf, sizeof buf, "%s.imp", path);
- vremove(buf);
- }
-}
-
-void
-mboxdecref(Mailbox *mb)
-{
- assert(mb->refs > 0);
- if(--mb->refs)
- return;
- syncmbox(mb, 1);
- delmessage(mb, mb->root);
- if(mb->close)
- mb->close(mb);
- if(mb->flags & ORCLOSE && mb->remove)
- if(mb->remove(mb, mb->rmflags))
- rmidx(mb->path, mb->rmflags);
- mtreefree(mb);
- free(mb->d);
- free(mb);
-}
-
-
-/* just space over \r. sleezy but necessary for ms email. */
-int
-deccr(char *x, int len)
-{
- char *e;
-
- e = x + len;
- for(;;){
- x = memchr(x, '\r', e - x);
- if(x == nil)
- break;
- *x = ' ';
- }
- return len;
-}
-
-/*
- * undecode message body
- */
-void
-decode(Message *m)
-{
- int i, len;
- char *x;
-
- if(m->decoded)
- return;
- dprint("decode %d %p\n", m->encoding, m);
- switch(m->encoding){
- case Ebase64:
- len = m->bend - m->body;
- i = (len*3)/4 + 1; /* room for max chars + null */
- x = emalloc(i);
- len = dec64((uchar*)x, i, m->body, len);
- if(len == -1){
- free(x);
- break;
- }
- if(strncmp(m->type, "text/", 5) == 0)
- len = deccr(x, len);
- if(m->ballocd)
- free(m->body);
- m->body = x;
- m->bend = x + len;
- m->ballocd = 1;
- break;
- case Equoted:
- len = m->bend - m->body;
- x = emalloc(len + 2); /* room for null and possible extra nl */
- len = decquoted(x, m->body, m->bend, 0);
- if(m->ballocd)
- free(m->body);
- m->body = x;
- m->bend = x + len;
- m->ballocd = 1;
- break;
- default:
- break;
- }
- m->decoded = 1;
-}
-
-/* convert x to utf8 */
-void
-convert(Message *m)
-{
- int len;
- char *x;
-
- /* don't convert if we're not a leaf, not text, or already converted */
- if(m->converted)
- return;
- dprint("convert type=%q charset=%q %p\n", m->type, m->charset, m);
- m->converted = 1;
- if(m->part != nil || strncmp(m->type, "text", 4) != 0 || *m->charset == 0)
- return;
- len = xtoutf(m->charset, &x, m->body, m->bend);
- if(len > 0){
- if(m->ballocd)
- free(m->body);
- m->body = x;
- m->bend = x + len;
- m->ballocd = 1;
- }
-}
-
-static int
-hex2int(int x)
-{
- if(x >= '0' && x <= '9')
- return x - '0';
- if(x >= 'A' && x <= 'F')
- return x - 'A' + 10;
- if(x >= 'a' && x <= 'f')
- return x - 'a' + 10;
- return -1;
-}
-
-/*
- * underscores are translated in 2047 headers (uscores=1)
- * but not in the body (uscores=0)
- */
-static char*
-decquotedline(char *out, char *in, char *e, int uscores)
-{
- int c, soft;
-
- /* dump trailing white space */
- while(e >= in && (*e == ' ' || *e == '\t' || *e == '\r' || *e == '\n'))
- e--;
-
- /* trailing '=' means no newline */
- if(*e == '='){
- soft = 1;
- e--;
- } else
- soft = 0;
-
- while(in <= e){
- c = (*in++) & 0xff;
- switch(c){
- case '_':
- if(uscores){
- *out++ = ' ';
- break;
- }
- default:
- *out++ = c;
- break;
- case '=':
- c = hex2int(*in++)<<4;
- c |= hex2int(*in++);
- if(c != -1)
- *out++ = c;
- else{
- *out++ = '=';
- in -= 2;
- }
- break;
- }
- }
- if(!soft)
- *out++ = '\n';
- *out = 0;
-
- return out;
-}
-
-int
-decquoted(char *out, char *in, char *e, int uscores)
-{
- char *p, *nl;
-
- p = out;
- while((nl = strchr(in, '\n')) != nil && nl < e){
- p = decquotedline(p, in, nl, uscores);
- in = nl + 1;
- }
- if(in < e)
- p = decquotedline(p, in, e - 1, uscores);
-
- /* make sure we end with a new line */
- if(*(p - 1) != '\n'){
- *p++ = '\n';
- *p = 0;
- }
-
- return p - out;
-}
-
-char*
-lowercase(char *p)
-{
- char *op;
- int c;
-
- for(op = p; c = *p; p++)
- if(isupper(c))
- *p = tolower(c);
- return op;
-}
-
-/* translate latin1 directly since it fits neatly in utf */
-static int
-latin1toutf(char **out, char *in, char *e)
-{
- int n;
- char *p;
- Rune r;
-
- n = 0;
- for(p = in; p < e; p++)
- if(*p & 0x80)
- n++;
- if(n == 0)
- return 0;
-
- n += e - in;
- *out = p = malloc(n + 1);
- if(p == nil)
- return 0;
-
- for(; in < e; in++){
- r = (uchar)*in;
- p += runetochar(p, &r);
- }
- *p = 0;
- return p - *out;
-}
-
-/* translate any thing using the tcs program */
-int
-xtoutf(char *charset, char **out, char *in, char *e)
-{
- char *av[4], *p;
- int totcs[2], fromtcs[2], n, len, sofar;
-
- /* might not need to convert */
- if(strcmp(charset, "us-ascii") == 0 || strcmp(charset, "utf-8") == 0)
- return 0;
- if(strcmp(charset, "iso-8859-1") == 0)
- return latin1toutf(out, in, e);
-
- len = e - in + 1;
- sofar = 0;
- *out = p = malloc(len + 1);
- if(p == nil)
- return 0;
-
- av[0] = charset;
- av[1] = "-f";
- av[2] = charset;
- av[3] = 0;
- if(pipe(totcs) < 0)
- goto error;
- if(pipe(fromtcs) < 0){
- close(totcs[0]); close(totcs[1]);
- goto error;
- }
- switch(rfork(RFPROC|RFFDG|RFNOWAIT)){
- case -1:
- close(fromtcs[0]); close(fromtcs[1]);
- close(totcs[0]); close(totcs[1]);
- goto error;
- case 0:
- close(fromtcs[0]); close(totcs[1]);
- dup(fromtcs[1], 1);
- dup(totcs[0], 0);
- close(fromtcs[1]); close(totcs[0]);
- dup(open("/dev/null", OWRITE), 2);
- exec("/bin/tcs", av);
- _exits("");
- default:
- close(fromtcs[1]); close(totcs[0]);
- switch(rfork(RFPROC|RFFDG|RFNOWAIT)){
- case -1:
- close(fromtcs[0]); close(totcs[1]);
- goto error;
- case 0:
- close(fromtcs[0]);
- while(in < e){
- n = write(totcs[1], in, e - in);
- if(n <= 0)
- break;
- in += n;
- }
- close(totcs[1]);
- _exits("");
- default:
- close(totcs[1]);
- for(;;){
- n = read(fromtcs[0], &p[sofar], len - sofar);
- if(n <= 0)
- break;
- sofar += n;
- p[sofar] = 0;
- if(sofar == len){
- len += 1024;
- p = realloc(p, len + 1);
- if(p == nil)
- goto error;
- *out = p;
- }
- }
- close(fromtcs[0]);
- break;
- }
- break;
- }
- if(sofar == 0)
- goto error;
- return sofar;
-
-error:
- free(*out);
- *out = nil;
- return 0;
-}
-
-void *
-emalloc(ulong n)
-{
- void *p;
-
- p = mallocz(n, 1);
- if(!p)
- sysfatal("malloc %lud: %r", n);
- setmalloctag(p, getcallerpc(&n));
- return p;
-}
-
-void *
-erealloc(void *p, ulong n)
-{
- if(n == 0)
- n = 1;
- p = realloc(p, n);
- if(!p)
- sysfatal("realloc %lud: %r", n);
- setrealloctag(p, getcallerpc(&p));
- return p;
-}
-
-int
-myplumbsend(int fd, Plumbmsg *m)
-{
- char *buf;
- int n;
-
- buf = plumbpack(m, &n);
- if(buf == nil)
- return -1;
- n = write(fd, buf, n);
- free(buf);
- return n;
-}
-
-static void
-mailplumb(Mailbox *mb, Message *m)
-{
- char buf[256], dbuf[SHA1dlen*2 + 1], len[10], date[32], *from, *subject;
- int ai;
- Plumbmsg p;
- Plumbattr a[7];
- static int fd = -1;
-
- subject = m->subject;
- if(subject == nil)
- subject = "";
-
- from = m->from;
- if(from == nil)
- from = "";
-
- sprint(len, "%lud", m->size);
- if(biffing && m->inmbox)
- fprint(2, "[ %s / %s / %s ]\n", from, subject, len);
- if(!plumbing)
- return;
-
- if(fd < 0)
- fd = plumbopen("send", OWRITE);
- if(fd < 0)
- return;
-
- p.src = "mailfs";
- p.dst = "seemail";
- p.wdir = "/mail/fs";
- p.type = "text";
-
- ai = 0;
- a[ai].name = "filetype";
- a[ai].value = "mail";
-
- a[++ai].name = "sender";
- a[ai].value = from;
- a[ai-1].next = &a[ai];
-
- a[++ai].name = "length";
- a[ai].value = len;
- a[ai-1].next = &a[ai];
-
- a[++ai].name = "mailtype";
- if(!m->inmbox)
- a[ai].value = "delete";
- else if(m->cstate & Cmod)
- a[ai].value = "modify";
- else
- a[ai].value = "new";
- a[ai-1].next = &a[ai];
-
- snprint(date, sizeof date, "%Δ", m->fileid);
- a[++ai].name = "date";
- a[ai].value = date;
- a[ai-1].next = &a[ai];
-
- if(m->digest){
- snprint(dbuf, sizeof dbuf, "%A", m->digest);
- a[++ai].name = "digest";
- a[ai].value = dbuf;
- a[ai-1].next = &a[ai];
- }
- a[ai].next = nil;
- p.attr = a;
- snprint(buf, sizeof buf, "%s/%s/%s",
- mntpt, mb->name, m->name);
- p.ndata = strlen(buf);
- p.data = buf;
-
- myplumbsend(fd, &p);
-}
-
-/*
- * count the number of lines in the body (for imap4)
- */
-ulong
-countlines(Message *m)
-{
- char *p;
- ulong i;
-
- i = 0;
- for(p = strchr(m->rbody, '\n'); p != nil && p < m->rbend; p = strchr(p + 1, '\n'))
- i++;
- return i;
-}
-
-char *logf = "fs";
-
-void
-logmsg(Message *m, char *fmt, ...)
-{
- char buf[256], *p, *e;
- va_list args;
-
- if(!lflag)
- return;
- e = buf + sizeof buf;
- p = seprint(buf, e, "%s.%d: ", user, getpid());
- if(m)
- p = seprint(p, e, "from %s digest %A ",
- m->from, m->digest);
- va_start(args, fmt);
- vseprint(p, e, fmt, args);
- va_end(args);
-
- if(Sflag)
- fprint(2, "%s\n", buf);
- syslog(Sflag, logf, "%s", buf);
-}
-
-void
-iprint(char *fmt, ...)
-{
- char buf[256], *p, *e;
- va_list args;
-
- if(!iflag)
- return;
- e = buf + sizeof buf;
- p = seprint(buf, e, "%s.%d: ", user, getpid());
- va_start(args, fmt);
- vseprint(p, e, fmt, args);
- vfprint(2, fmt, args);
- va_end(args);
- syslog(Sflag, logf, "%s", buf);
-}
-
-void
-eprint(char *fmt, ...)
-{
- char buf[256], buf2[256], *p, *e;
- va_list args;
-
- e = buf + sizeof buf;
- p = seprint(buf, e, "%s.%d: ", user, getpid());
- va_start(args, fmt);
- vseprint(p, e, fmt, args);
- e = buf2 + sizeof buf2;
- p = seprint(buf2, e, "upas/fs: ");
- vseprint(p, e, fmt, args);
- va_end(args);
- syslog(Sflag, logf, "%s", buf);
- fprint(2, "%s", buf2);
-}
--- a/sys/src/cmd/upas/fs/tls.c
+++ /dev/null
@@ -1,37 +1,0 @@
-#include "common.h"
-#include <libsec.h>
-#include <auth.h>
-#include "dat.h"
-
-int
-wraptls(int ofd, char *host)
-{
- Thumbprint *thumb;
- TLSconn conn;
- int fd;
-
- memset(&conn, 0, sizeof conn);
- conn.serverName = host;
- fd = tlsClient(ofd, &conn);
- if(fd < 0){
- close(ofd);
- return -1;
- }
- if(nocertcheck){
- syslog(Sflag, logf, "ignoring cert for %s", host);
- goto skip;
- }
- thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude", "x509");
- if(thumb != nil){
- if(!okCertificate(conn.cert, conn.certlen, thumb)){
- werrstr("cert for %s not recognized: %r", host);
- close(fd);
- fd = -1;
- }
- freeThumbprints(thumb);
- }
-skip:
- free(conn.cert);
- free(conn.sessionID);
- return fd;
-}
--- a/sys/src/cmd/upas/smtp/smtp.c
+++ /dev/null
@@ -1,1209 +1,0 @@
-#include "common.h"
-#include "smtp.h"
-#include <ctype.h>
-#include <mp.h>
-#include <libsec.h>
-#include <auth.h>
-
-static char* connect(char*, Mx*);
-static char* wraptls(void);
-static char* dotls(char*);
-static char* doauth(char*);
-
-void addhostdom(String*, char*);
-String* bangtoat(char*);
-String* convertheader(String*);
-int dBprint(char*, ...);
-#pragma varargck argpos dBprint 1
-int dBputc(int);
-char* data(String*, Biobuf*, Mx*);
-char* domainify(char*, char*);
-String* fixrouteaddr(String*, Node*, Node*);
-char* getcrnl(String*);
-int getreply(void);
-char* hello(char*, int);
-char* mailfrom(char*);
-int printdate(Node*);
-int printheader(void);
-void putcrnl(char*, int);
-void quit(char*);
-char* rcptto(char*);
-char *rewritezone(char *);
-
-char Retry[] = "Retry, Temporary Failure";
-char Giveup[] = "Permanent Failure";
-
-String *reply; /* last reply */
-String *toline;
-
-int alarmscale;
-int autistic;
-int debug; /* true if we're debugging */
-int filter;
-int insecure;
-int last = 'n'; /* last character sent by putcrnl() */
-int ping;
-int quitting; /* when error occurs in quit */
-int tryauth; /* Try to authenticate, if supported */
-int trysecure; /* Try to use TLS if the other side supports it */
-int nocertcheck; /* ignore unrecognized certs. Still logged */
-
-char *quitrv; /* deferred return value when in quit */
-char ddomain[1024]; /* domain name of destination machine */
-char *gdomain; /* domain name of gateway */
-char *uneaten; /* first character after rfc822 headers */
-char *farend; /* system we are trying to send to */
-char *user; /* user we are authenticating as, if authenticating */
-char hostdomain[256];
-Mx *tmmx; /* global for timeout */
-
-Biobuf bin;
-Biobuf bout;
-Biobuf berr;
-Biobuf bfile;
-
-int
-Dfmt(Fmt *fmt)
-{
- Mx *mx;
-
- mx = va_arg(fmt->args, Mx*);
- if(mx == nil || mx->host[0] == 0)
- return fmtstrcpy(fmt, "");
- else
- return fmtprint(fmt, "(%s:%s)", mx->host, mx->ip);
-}
-#pragma varargck type "D" Mx*
-
-char*
-deliverytype(void)
-{
- if(ping)
- return "ping";
- return "delivery";
-}
-
-void
-usage(void)
-{
- fprint(2, "usage: smtp [-aACdfipst] [-b busted-mx] [-g gw] [-h host] "
- "[-u user] [.domain] net!host[!service] sender rcpt-list\n");
- exits(Giveup);
-}
-
-int
-timeout(void *, char *msg)
-{
- syslog(0, "smtp.fail", "%s interrupt: %s: %s %D", deliverytype(), farend, msg, tmmx);
- if(strstr(msg, "alarm")){
- fprint(2, "smtp timeout: connection to %s timed out\n", farend);
- if(quitting)
- exits(quitrv);
- exits(Retry);
- }
- if(strstr(msg, "closed pipe")){
- fprint(2, "smtp timeout: connection closed to %s\n", farend);
- if(quitting){
- syslog(0, "smtp.fail", "%s closed pipe to %s %D", deliverytype(), farend, tmmx);
- _exits(quitrv);
- }
- /* call _exits() to prevent Bio from trying to flush closed pipe */
- _exits(Retry);
- }
- return 0;
-}
-
-void
-removenewline(char *p)
-{
- int n = strlen(p) - 1;
-
- if(n < 0)
- return;
- if(p[n] == '\n')
- p[n] = 0;
-}
-
-void
-main(int argc, char **argv)
-{
- char *phase, *addr, *rv, *trv, *host, *domain;
- char **errs, *p, *e, hellodomain[256], allrx[512];
- int i, ok, rcvrs, bustedmx;
- String *from, *fromm, *sender;
- Mx mx;
- Tm tm;
-
- alarmscale = 60*1000; /* minutes */
- tmfmtinstall();
- quotefmtinstall();
- mailfmtinstall(); /* 2047 encoding */
- fmtinstall('D', Dfmt);
- fmtinstall('[', encodefmt);
- fmtinstall('H', encodefmt);
- errs = malloc(argc*sizeof(char*));
- reply = s_new();
- host = 0;
- bustedmx = 0;
- ARGBEGIN{
- case 'a':
- tryauth = 1;
- if(trysecure == 0)
- trysecure = 1;
- break;
- case 'A': /* autistic: won't talk to us until we talk (Verizon) */
- autistic = 1;
- break;
- case 'b':
- if(bustedmx >= Maxbustedmx)
- sysfatal("more than %d busted mxs given", Maxbustedmx);
- bustedmxs[bustedmx++] = EARGF(usage());
- break;
- case 'd':
- debug = 1;
- break;
- case 'f':
- filter = 1;
- break;
- case 'g':
- gdomain = EARGF(usage());
- break;
- case 'h':
- host = EARGF(usage());
- break;
- case 'i':
- insecure = 1;
- break;
- case 'p':
- alarmscale = 10*1000; /* tens of seconds */
- ping = 1;
- break;
- case 's':
- if(trysecure == 0)
- trysecure = 1;
- break;
- case 't':
- trysecure = 2;
- break;
- case 'u':
- user = EARGF(usage());
- break;
- case 'C':
- nocertcheck = 1;
- break;
- default:
- usage();
- break;
- }ARGEND;
-
- Binit(&berr, 2, OWRITE);
- Binit(&bfile, 0, OREAD);
-
- /*
- * get domain and add to host name
- */
- if(*argv && **argv=='.'){
- domain = *argv;
- argv++; argc--;
- } else
- domain = domainname_read();
- if(host == 0)
- host = sysname_read();
- if(user == nil)
- user = getuser();
- strcpy(hostdomain, domainify(host, domain));
- strcpy(hellodomain, domainify(sysname_read(), domain));
-
- /*
- * get destination address
- */
- if(*argv == 0)
- usage();
- addr = *argv++; argc--;
- farend = addr;
- if((rv = strrchr(addr, '!')) && rv[1] == '['){
- syslog(0, "smtp.fail", "%s to %s failed: illegal address",
- deliverytype(), addr);
- exits(Giveup);
- }
-
- /*
- * get sender's machine.
- * get sender in internet style. domainify if necessary.
- */
- if(*argv == 0)
- usage();
- sender = unescapespecial(s_copy(*argv++));
- argc--;
- fromm = s_clone(sender);
- rv = strrchr(s_to_c(fromm), '!');
- if(rv)
- *rv = 0;
- else
- *s_to_c(fromm) = 0;
- from = bangtoat(s_to_c(sender));
-
- /*
- * send the mail
- */
- rcvrs = 0;
- phase = "";
- USED(phase); /* just in case */
- if(filter){
- Binit(&bout, 1, OWRITE);
- rv = data(from, &bfile, nil);
- if(rv != 0){
- phase = "filter";
- goto error;
- }
- exits(0);
- }
-
- /* mxdial uses its own timeout handler */
- if((rv = connect(addr, &mx)) != 0)
- exits(rv);
-
- tmmx = &mx;
- /* 10 minutes to get through the initial handshake */
- atnotify(timeout, 1);
- alarm(10*alarmscale);
- if((rv = hello(hellodomain, 0)) != 0){
- phase = "hello";
- goto error;
- }
- alarm(10*alarmscale);
- if((rv = mailfrom(s_to_c(from))) != 0){
- phase = "mailfrom";
- goto error;
- }
-
- ok = 0;
- /* if any rcvrs are ok, we try to send the message */
- phase = "rcptto";
- for(i = 0; i < argc; i++){
- if((trv = rcptto(argv[i])) != 0){
- /* remember worst error */
- if(rv != Giveup)
- rv = trv;
- errs[rcvrs] = strdup(s_to_c(reply));
- removenewline(errs[rcvrs]);
- } else {
- ok++;
- errs[rcvrs] = 0;
- }
- rcvrs++;
- }
-
- /* if no ok rcvrs or worst error is retry, give up */
- if(ok == 0 && rcvrs == 0)
- phase = "rcptto; no addresses";
- if(ok == 0 || rv == Retry)
- goto error;
-
- if(ping){
- quit(0);
- exits(0);
- }
-
- rv = data(from, &bfile, &mx);
- if(rv != 0)
- goto error;
- quit(0);
- if(rcvrs == ok)
- exits(0);
-
- /*
- * here when some but not all rcvrs failed
- */
- fprint(2, "%τ connect to %s: %D %s:\n", thedate(&tm), addr, &mx, phase);
- for(i = 0; i < rcvrs; i++){
- if(errs[i]){
- syslog(0, "smtp.fail", "delivery to %s at %s %D %s, failed: %s",
- argv[i], addr, &mx, phase, errs[i]);
- fprint(2, " mail to %s failed: %s", argv[i], errs[i]);
- }
- }
- exits(Giveup);
-
- /*
- * here when all rcvrs failed
- */
-error:
- alarm(0);
- removenewline(s_to_c(reply));
- if(rcvrs > 0){
- p = allrx;
- e = allrx + sizeof allrx;
- seprint(p, e, "to ");
- for(i = 0; i < rcvrs - 1; i++)
- p = seprint(p, e, "%s,", argv[i]);
- seprint(p, e, "%s ", argv[i]);
- }
- syslog(0, "smtp.fail", "%s %s at %s %D %s failed: %s",
- deliverytype(), allrx, addr, &mx, phase, s_to_c(reply));
- fprint(2, "%τ connect to %s %D %s:\n%s\n", thedate(&tm), addr, &mx, phase, s_to_c(reply));
- if(!filter)
- quit(rv);
- exits(rv);
-}
-
-/*
- * connect to the remote host
- */
-static char *
-connect(char* net, Mx *mx)
-{
- char buf[ERRMAX];
- int fd;
-
- fd = mxdial(net, ddomain, gdomain, mx);
-
- if(fd < 0){
- rerrstr(buf, sizeof buf);
- Bprint(&berr, "smtp: %s (%s) %D\n", buf, net, mx);
- syslog(0, "smtp.fail", "%s %s (%s) %D", deliverytype(), buf, net, mx);
- if(strstr(buf, "illegal")
- || strstr(buf, "unknown")
- || strstr(buf, "can't translate"))
- return Giveup;
- else
- return Retry;
- }
- Binit(&bin, fd, OREAD);
- fd = dup(fd, -1);
- Binit(&bout, fd, OWRITE);
- return 0;
-}
-
-static char smtpthumbs[] = "/sys/lib/tls/smtp";
-static char smtpexclthumbs[] = "/sys/lib/tls/smtp.exclude";
-
-static int
-tracetls(char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- Bvprint(&berr, fmt, ap);
- Bprint(&berr, "\n");
- Bflush(&berr);
- va_end(ap);
- return 0;
-}
-
-static char*
-wraptls(void)
-{
- TLSconn *c;
- Thumbprint *goodcerts;
- char *err;
- int fd;
-
- goodcerts = nil;
- err = Giveup;
- c = mallocz(sizeof(*c), 1);
- if (c == nil)
- return err;
-
- if (debug)
- c->trace = tracetls;
-
- fd = tlsClient(Bfildes(&bout), c);
- if (fd < 0) {
- syslog(0, "smtp", "tlsClient to %q: %r", ddomain);
- goto Out;
- }
- Bterm(&bout);
- Binit(&bout, fd, OWRITE);
- fd = dup(fd, Bfildes(&bin));
- Bterm(&bin);
- Binit(&bin, fd, OREAD);
-
- if (nocertcheck) {
- syslog(0, "smtp", "ignoring cert for %s", ddomain);
- err = nil;
- goto Out;
- }
- goodcerts = initThumbprints(smtpthumbs, smtpexclthumbs, "x509");
- if (goodcerts == nil) {
- syslog(0, "smtp", "bad thumbprints in %s", smtpthumbs);
- goto Out;
- }
- if (!okCertificate(c->cert, c->certlen, goodcerts)) {
- syslog(0, "smtp", "cert for %s not recognized: %r", ddomain);
- goto Out;
- }
- syslog(0, "smtp", "started TLS to %q", ddomain);
- err = nil;
-Out:
- freeThumbprints(goodcerts);
- free(c->cert);
- free(c->sessionID);
- free(c);
- return err;
-}
-
-/*
- * exchange names with remote host, attempt to
- * enable encryption and optionally authenticate.
- * not fatal if we can't.
- */
-static char *
-dotls(char *me)
-{
- char *err;
-
- dBprint("STARTTLS\r\n");
- if (getreply() != 2)
- return Giveup;
-
- err = wraptls();
- if (err != nil)
- return err;
-
- return(hello(me, 1));
-}
-
-static char*
-smtpcram(DS *ds)
-{
- char *p, ch[128], usr[64], rbuf[128], ubuf[128], ebuf[192];
- int i, n, l;
-
- dBprint("AUTH CRAM-MD5\r\n");
- if(getreply() != 3)
- return Retry;
- p = s_to_c(reply) + 4;
- l = dec64((uchar*)ch, sizeof ch, p, strlen(p));
- ch[l] = 0;
- n = auth_respond(ch, l, usr, sizeof usr, rbuf, sizeof rbuf, auth_getkey,
- "proto=cram role=client server=%q user=%q",
- ds->host, user);
- if(n == -1){
- if(temperror())
- return Retry;
- syslog(0, "smtp.fail", "failed to get challenge response: %r");
- return Giveup;
- }
- if(usr[0] == 0)
- return "cannot find user name";
- for(i = 0; i < n; i++)
- rbuf[i] = tolower(rbuf[i]);
- l = snprint(ubuf, sizeof ubuf, "%s %.*s", usr, utfnlen(rbuf, n), rbuf);
- snprint(ebuf, sizeof ebuf, "%.*[", l, ubuf);
-
- dBprint("%s\r\n", ebuf);
- if(getreply() != 2)
- return Retry;
- return nil;
-}
-
-static char *
-doauth(char *methods)
-{
- char buf[1024], *err;
- UserPasswd *p;
- DS ds;
- int n;
-
- dialstringparse(farend, &ds);
- if(strstr(methods, "CRAM-MD5"))
- return smtpcram(&ds);
- p = auth_getuserpasswd(nil,
- "proto=pass service=smtp server=%q user=%q",
- ds.host, user);
- if (p == nil) {
- if(temperror())
- return Retry;
- syslog(0, "smtp.fail", "failed to get userpasswd: %r");
- return Giveup;
- }
- err = Retry;
- if (strstr(methods, "LOGIN")){
- dBprint("AUTH LOGIN\r\n");
- if (getreply() != 3)
- goto out;
-
- dBprint("%.*[\r\n", (int)strlen(p->user), p->user);
- if (getreply() != 3)
- goto out;
-
- dBprint("%.*[\r\n", (int)strlen(p->passwd), p->passwd);
- if (getreply() != 2)
- goto out;
-
- err = nil;
- }
- else if (strstr(methods, "PLAIN")){
- n = snprint(buf, sizeof(buf), "%c%s%c%s", 0, p->user, 0, p->passwd);
- dBprint("AUTH PLAIN %.*[\r\n", n, buf);
- memset(buf, 0, sizeof(buf));
- if (getreply() != 2)
- goto out;
- err = nil;
- } else
- err = "No supported AUTH method";
-out:
- memset(p->user, 0, strlen(p->user));
- memset(p->passwd, 0, strlen(p->passwd));
- free(p);
- return err;
-}
-
-char*
-hello(char *me, int encrypted)
-{
- char *ret, *s, *t;
- int ehlo;
- String *r;
-
- if(!encrypted){
- if(trysecure > 1){
- if((ret = wraptls()) != nil)
- return ret;
- encrypted = 1;
- }
-
- /*
- * Verizon fails to print the smtp greeting banner when it
- * answers a call. Send a no-op in the hope of making it
- * talk.
- */
- if(autistic){
- dBprint("NOOP\r\n");
- getreply(); /* consume the smtp greeting */
- /* next reply will be response to noop */
- }
- switch(getreply()){
- case 2:
- break;
- case 5:
- return Giveup;
- default:
- return Retry;
- }
- }
-
- ehlo = 1;
- Again:
- if(ehlo)
- dBprint("EHLO %s\r\n", me);
- else
- dBprint("HELO %s\r\n", me);
- switch(getreply()){
- case 2:
- break;
- case 5:
- if(ehlo){
- ehlo = 0;
- goto Again;
- }
- return Giveup;
- default:
- return Retry;
- }
- r = s_clone(reply);
- if(r == nil)
- return Retry; /* Out of memory or couldn't get string */
-
- /* Invariant: every line has a newline, a result of getcrlf() */
- for(s = s_to_c(r); (t = strchr(s, '\n')) != nil; s = t + 1){
- *t = '\0';
- if(!encrypted && trysecure &&
- (cistrcmp(s, "250-STARTTLS") == 0 ||
- cistrcmp(s, "250 STARTTLS") == 0)){
- s_free(r);
- return dotls(me);
- }
- if(tryauth && (encrypted || insecure) &&
- (cistrncmp(s, "250 AUTH", strlen("250 AUTH")) == 0 ||
- cistrncmp(s, "250-AUTH", strlen("250 AUTH")) == 0)){
- ret = doauth(s + strlen("250 AUTH "));
- s_free(r);
- return ret;
- }
- }
- s_free(r);
- return 0;
-}
-
-/*
- * report sender to remote
- */
-char *
-mailfrom(char *from)
-{
- if(!returnable(from))
- dBprint("MAIL FROM:<>\r\n");
- else if(strchr(from, '@'))
- dBprint("MAIL FROM:<%s>\r\n", from);
- else
- dBprint("MAIL FROM:<%s@%s>\r\n", from, hostdomain);
- switch(getreply()){
- case 2:
- return 0;
- case 5:
- return Giveup;
- default:
- return Retry;
- }
-}
-
-/*
- * report a recipient to remote
- */
-char *
-rcptto(char *to)
-{
- String *s;
-
- s = unescapespecial(bangtoat(to));
- if(toline == 0)
- toline = s_new();
- else
- s_append(toline, ", ");
- s_append(toline, s_to_c(s));
- if(strchr(s_to_c(s), '@'))
- dBprint("RCPT TO:<%s>\r\n", s_to_c(s));
- else {
- s_append(toline, "@");
- s_append(toline, ddomain);
- dBprint("RCPT TO:<%s@%s>\r\n", s_to_c(s), ddomain);
- }
- alarm(10*alarmscale);
- switch(getreply()){
- case 2:
- break;
- case 5:
- return Giveup;
- default:
- return Retry;
- }
- return 0;
-}
-
-/*
- * send the damn thing
- */
-char *
-data(String *from, Biobuf *b, Mx *mx)
-{
- char *buf, *cp, errmsg[ERRMAX];
- int n, nbytes, bufsize, eof;
- String *fromline;
-
- /*
- * input the header.
- */
-
- buf = malloc(1);
- if(buf == 0){
- s_append(s_restart(reply), "out of memory");
- return Retry;
- }
- n = 0;
- eof = 0;
- for(;;){
- cp = Brdline(b, '\n');
- if(cp == nil){
- eof = 1;
- break;
- }
- nbytes = Blinelen(b);
- buf = realloc(buf, n + nbytes + 1);
- if(buf == 0){
- s_append(s_restart(reply), "out of memory");
- return Retry;
- }
- strncpy(buf + n, cp, nbytes);
- n += nbytes;
- if(nbytes == 1) /* end of header */
- break;
- }
- buf[n] = 0;
- bufsize = n;
-
- /*
- * parse the header, turn all addresses into @ format
- */
- yyinit(buf, n);
- yyparse();
-
- /*
- * print message observing '.' escapes and using \r\n for \n
- */
- alarm(20*alarmscale);
- if(!filter){
- dBprint("DATA\r\n");
- switch(getreply()){
- case 3:
- break;
- case 5:
- free(buf);
- return Giveup;
- default:
- free(buf);
- return Retry;
- }
- }
- /*
- * send header. add a message-id, a sender, and a date if there
- * isn't one
- */
- nbytes = 0;
- fromline = convertheader(from);
- uneaten = buf;
-
- if(messageid == 0){
- uchar id[16];
-
- genrandom(id, sizeof(id));
- nbytes += dBprint("Message-ID: <%.*H@%s>\r\n",
- sizeof(id), id, hostdomain);
- }
-
- if(originator == 0)
- nbytes += dBprint("From: %s\r\n", s_to_c(fromline));
- s_free(fromline);
-
- if(destination == 0 && toline){
- if(*s_to_c(toline) == '@') /* route addr */
- nbytes += dBprint("To: <%s>\r\n", s_to_c(toline));
- else
- nbytes += dBprint("To: %s\r\n", s_to_c(toline));
- }
-
- if(date == 0 && udate)
- nbytes += printdate(udate);
- if(usys)
- uneaten = usys->end + 1;
- nbytes += printheader();
- if(*uneaten != '\n')
- putcrnl("\n", 1);
-
- /*
- * send body
- */
-
- putcrnl(uneaten, buf + n - uneaten);
- nbytes += buf + n - uneaten;
- if(eof == 0){
- for(;;){
- n = Bread(b, buf, bufsize);
- if(n < 0){
- rerrstr(errmsg, sizeof(errmsg));
- s_append(s_restart(reply), errmsg);
- free(buf);
- return Retry;
- }
- if(n == 0)
- break;
- alarm(10*alarmscale);
- putcrnl(buf, n);
- nbytes += n;
- }
- }
- free(buf);
- if(!filter){
- if(last != '\n')
- dBprint("\r\n.\r\n");
- else
- dBprint(".\r\n");
- alarm(10*alarmscale);
- switch(getreply()){
- case 2:
- break;
- case 5:
- return Giveup;
- default:
- return Retry;
- }
- syslog(0, "smtp", "%s sent %d bytes to %s %D", s_to_c(from),
- nbytes, s_to_c(toline), mx);
- }
- return 0;
-}
-
-/*
- * we're leaving
- */
-void
-quit(char *rv)
-{
- /* 60 minutes to quit */
- quitting = 1;
- quitrv = rv;
- alarm(60*alarmscale);
- dBprint("QUIT\r\n");
- getreply();
- Bterm(&bout);
- Bterm(&bfile);
-}
-
-/*
- * read a reply into a string, return the reply code
- */
-int
-getreply(void)
-{
- char *line;
- int rv;
-
- reply = s_reset(reply);
- for(;;){
- line = getcrnl(reply);
- if(debug)
- Bflush(&berr);
- if(line == 0)
- return -1;
- if(!isdigit(line[0]) || !isdigit(line[1]) || !isdigit(line[2]))
- return -1;
- if(line[3] != '-')
- break;
- }
- if(debug)
- Bflush(&berr);
- rv = atoi(line)/100;
- return rv;
-}
-void
-addhostdom(String *buf, char *host)
-{
- s_append(buf, "@");
- s_append(buf, host);
-}
-
-/*
- * Convert from `bang' to `source routing' format.
- *
- * a.x.y!b.p.o!c!d -> @a.x.y:[email protected]
- */
-String *
-bangtoat(char *addr)
-{
- char *field[128];
- int i, j, d;
- String *buf;
-
- /* parse the '!' format address */
- buf = s_new();
- for(i = 0; addr; i++){
- field[i] = addr;
- addr = strchr(addr, '!');
- if(addr)
- *addr++ = 0;
- }
- if(i == 1){
- s_append(buf, field[0]);
- return buf;
- }
-
- /*
- * count leading domain fields (non-domains don't count)
- */
- for(d = 0; d < i - 1; d++)
- if(strchr(field[d], '.') == 0)
- break;
- /*
- * if there are more than 1 leading domain elements,
- * put them in as source routing
- */
- if(d > 1){
- addhostdom(buf, field[0]);
- for(j = 1; j< d - 1; j++){
- s_append(buf, ",");
- s_append(buf, "@");
- s_append(buf, field[j]);
- }
- s_append(buf, ":");
- }
-
- /*
- * throw in the non-domain elements separated by '!'s
- */
- s_append(buf, field[d]);
- for(j = d + 1; j <= i - 1; j++){
- s_append(buf, "!");
- s_append(buf, field[j]);
- }
- if(d)
- addhostdom(buf, field[d-1]);
- return buf;
-}
-
-/*
- * convert header addresses to @ format.
- * if the address is a source address, and a domain is specified,
- * make sure it falls in the domain.
- */
-String*
-convertheader(String *from)
-{
- char *s, buf[64];
- Field *f;
- Node *p, *lastp;
- String *a;
-
- if(!returnable(s_to_c(from))){
- from = s_new();
- s_append(from, "Postmaster");
- addhostdom(from, hostdomain);
- } else
- if(strchr(s_to_c(from), '@') == 0){
- if(s = username(s_to_c(from))){
- /* this has always been here, but username() was broken */
- snprint(buf, sizeof buf, "%U", s);
- s_append(a = s_new(), buf);
- s_append(a, " <");
- s_append(a, s_to_c(from));
- addhostdom(a, hostdomain);
- s_append(a, ">");
- from = a;
- } else {
- from = s_copy(s_to_c(from));
- addhostdom(from, hostdomain);
- }
- } else
- from = s_copy(s_to_c(from));
- for(f = firstfield; f; f = f->next){
- lastp = 0;
- for(p = f->node; p; lastp = p, p = p->next){
- if(!p->addr)
- continue;
- a = bangtoat(s_to_c(p->s));
- s_free(p->s);
- if(strchr(s_to_c(a), '@') == 0)
- addhostdom(a, hostdomain);
- else if(*s_to_c(a) == '@')
- a = fixrouteaddr(a, p->next, lastp);
- p->s = a;
- }
- }
- return from;
-}
-/*
- * ensure route addr has brackets around it
- */
-String*
-fixrouteaddr(String *raddr, Node *next, Node *last)
-{
- String *a;
-
- if(last && last->c == '<' && next && next->c == '>')
- return raddr; /* properly formed already */
-
- a = s_new();
- s_append(a, "<");
- s_append(a, s_to_c(raddr));
- s_append(a, ">");
- s_free(raddr);
- return a;
-}
-
-/*
- * print out the parsed header
- */
-int
-printheader(void)
-{
- char *cp, c[1];
- int n, len;
- Field *f;
- Node *p;
-
- n = 0;
- for(f = firstfield; f; f = f->next){
- for(p = f->node; p; p = p->next){
- if(p->s)
- n += dBprint("%s", s_to_c(p->s));
- else {
- c[0] = p->c;
- putcrnl(c, 1);
- n++;
- }
- if(p->white){
- cp = s_to_c(p->white);
- len = strlen(cp);
- putcrnl(cp, len);
- n += len;
- }
- uneaten = p->end;
- }
- putcrnl("\n", 1);
- n++;
- uneaten++; /* skip newline */
- }
- return n;
-}
-
-/*
- * add a domain onto an name, return the new name
- */
-char *
-domainify(char *name, char *domain)
-{
- char *p;
- static String *s;
-
- if(domain == 0 || strchr(name, '.') != 0)
- return name;
-
- s = s_reset(s);
- s_append(s, name);
- p = strchr(domain, '.');
- if(p == 0){
- s_append(s, ".");
- p = domain;
- }
- s_append(s, p);
- return s_to_c(s);
-}
-
-/*
- * print message observing '.' escapes and using \r\n for \n
- */
-void
-putcrnl(char *cp, int n)
-{
- int c;
-
- for(; n; n--, cp++){
- c = *cp;
- if(c == '\n')
- dBputc('\r');
- else if(c == '.' && last=='\n')
- dBputc('.');
- dBputc(c);
- last = c;
- }
-}
-
-/*
- * Get a line including a crnl into a string. Convert crnl into nl.
- */
-char *
-getcrnl(String *s)
-{
- int c, count;
-
- count = 0;
- for(;;){
- c = Bgetc(&bin);
- if(debug)
- Bputc(&berr, c);
- switch(c){
- case -1:
- s_append(s, "connection closed unexpectedly by remote system");
- s_terminate(s);
- return 0;
- case '\r':
- c = Bgetc(&bin);
- if(c == '\n'){
- case '\n':
- s_putc(s, c);
- if(debug)
- Bputc(&berr, c);
- count++;
- s_terminate(s);
- return s->ptr - count;
- }
- Bungetc(&bin);
- s_putc(s, '\r');
- if(debug)
- Bputc(&berr, '\r');
- count++;
- break;
- default:
- s_putc(s, c);
- count++;
- break;
- }
- }
-}
-
-/*
- * print out a parsed date
- */
-int
-printdate(Node *p)
-{
- int n, sep;
-
- n = dBprint("Date: %s,", s_to_c(p->s));
- sep = 0;
- for(p = p->next; p; p = p->next){
- if(p->s){
- if(sep == 0){
- dBputc(' ');
- n++;
- }
- if(p->next)
- n += dBprint("%s", s_to_c(p->s));
- else
- n += dBprint("%s", rewritezone(s_to_c(p->s)));
- sep = 0;
- } else {
- dBputc(p->c);
- n++;
- sep = 1;
- }
- }
- n += dBprint("\r\n");
- return n;
-}
-
-char *
-rewritezone(char *z)
-{
- char s;
- int mindiff;
- 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;
-}
-
-/*
- * stolen from libc/port/print.c
- */
-
-int
-dBprint(char *fmt, ...)
-{
- char buf[4096], *out;
- int n;
- va_list arg;
-
- va_start(arg, fmt);
- out = vseprint(buf, buf + sizeof buf, fmt, arg);
- va_end(arg);
- if(debug){
- Bwrite(&berr, buf, out - buf);
- Bflush(&berr);
- }
- n = Bwrite(&bout, buf,out - buf);
- Bflush(&bout);
- return n;
-}
-
-int
-dBputc(int x)
-{
- if(debug)
- Bputc(&berr, x);
- return Bputc(&bout, x);
-}