Thanks to John Cummings.

This commit is contained in:
rsc 2005-10-29 16:26:44 +00:00
parent cd37451963
commit 5cdb17983a
94 changed files with 26853 additions and 0 deletions

221
src/cmd/upas/fs/dat.h Normal file
View file

@ -0,0 +1,221 @@
typedef struct Message Message;
struct Message
{
int id;
int refs;
int subname;
char name[Elemlen];
// pointers into message
char *start; // start of message
char *end; // end of message
char *header; // start of header
char *hend; // end of header
int hlen; // length of header minus ignored fields
char *mheader; // start of mime header
char *mhend; // end of mime header
char *body; // start of body
char *bend; // end of body
char *rbody; // raw (unprocessed) body
char *rbend; // end of raw (unprocessed) body
char *lim;
char deleted;
char inmbox;
char mallocd; // message is malloc'd
char ballocd; // body is malloc'd
char hallocd; // header is malloce'd
// mail info
String *unixheader;
String *unixfrom;
String *unixdate;
String *from822;
String *sender822;
String *to822;
String *bcc822;
String *cc822;
String *replyto822;
String *date822;
String *inreplyto822;
String *subject822;
String *messageid822;
String *addrs;
String *mimeversion;
String *sdigest;
// mime info
String *boundary;
String *type;
int encoding;
int disposition;
String *charset;
String *filename;
int converted;
int decoded;
char lines[10]; // number of lines in rawbody
Message *next; // same level
Message *part; // down a level
Message *whole; // up a level
uchar digest[SHA1dlen];
vlong imapuid; // used by imap4
char uidl[80]; // used by pop3
int mesgno;
};
enum
{
// encodings
Enone= 0,
Ebase64,
Equoted,
// disposition possibilities
Dnone= 0,
Dinline,
Dfile,
Dignore,
PAD64= '=',
};
typedef struct Mailbox Mailbox;
struct Mailbox
{
QLock ql; /* jpc named Qlock */
int refs;
Mailbox *next;
int id;
int dolock; // lock when syncing?
int std;
char name[Elemlen];
char path[Pathlen];
Dir *d;
Message *root;
int vers; // goes up each time mailbox is read
ulong waketime;
char *(*sync)(Mailbox*, int);
void (*close)(Mailbox*);
char *(*fetch)(Mailbox*, Message*);
char *(*ctl)(Mailbox*, int, char**);
void *aux; // private to Mailbox implementation
};
typedef char *Mailboxinit(Mailbox*, char*);
extern Message *root;
extern Mailboxinit plan9mbox;
extern Mailboxinit pop3mbox;
extern Mailboxinit imap4mbox;
char* syncmbox(Mailbox*, int);
char* geterrstr(void);
void* emalloc(ulong);
void* erealloc(void*, ulong);
Message* newmessage(Message*);
void delmessage(Mailbox*, Message*);
void delmessages(int, char**);
int newid(void);
void mailplumb(Mailbox*, Message*, int);
char* newmbox(char*, char*, int);
void freembox(char*);
void logmsg(char*, Message*);
void msgincref(Message*);
void msgdecref(Mailbox*, Message*);
void mboxincref(Mailbox*);
void mboxdecref(Mailbox*);
void convert(Message*);
void decode(Message*);
int cistrncmp(char*, char*, int);
int cistrcmp(char*, char*);
int latin1toutf(char*, char*, char*);
int windows1257toutf(char*, char*, char*);
int decquoted(char*, char*, char*);
int xtoutf(char*, char**, char*, char*);
void countlines(Message*);
int headerlen(Message*);
void parse(Message*, int, Mailbox*, int);
void parseheaders(Message*, int, Mailbox*, int);
void parsebody(Message*, Mailbox*);
void parseunix(Message*);
String* date822tounix(char*);
int fidmboxrefs(Mailbox*);
int hashmboxrefs(Mailbox*);
void checkmboxrefs(void);
extern int debug;
extern int fflag;
extern int logging;
extern char user[Elemlen];
extern char stdmbox[Pathlen];
extern QLock mbllock;
extern Mailbox *mbl;
extern char *mntpt;
extern int biffing;
extern int plumbing;
extern char* Enotme;
enum
{
/* mail subobjects */
Qbody,
Qbcc,
Qcc,
Qdate,
Qdigest,
Qdisposition,
Qfilename,
Qfrom,
Qheader,
Qinreplyto,
Qlines,
Qmimeheader,
Qmessageid,
Qraw,
Qrawbody,
Qrawheader,
Qrawunix,
Qreplyto,
Qsender,
Qsubject,
Qto,
Qtype,
Qunixheader,
Qinfo,
Qunixdate,
Qmax,
/* other files */
Qtop,
Qmbox,
Qdir,
Qctl,
Qmboxctl,
};
#define PATH(id, f) ((((id)&0xfffff)<<10) | (f))
#define FILE(p) ((p) & 0x3ff)
/* char *dirtab[]; jpc */
// hash table to aid in name lookup, all files have an entry
typedef struct Hash Hash;
struct Hash {
Hash *next;
char *name;
ulong ppath;
Qid qid;
Mailbox *mb;
Message *m;
};
Hash *hlook(ulong, char*);
void henter(ulong, char*, Qid, Message*, Mailbox*);
void hfree(ulong, char*);
ulong msgallocd, msgfreed;

1704
src/cmd/upas/fs/fs.c Normal file

File diff suppressed because it is too large Load diff

876
src/cmd/upas/fs/imap4.c Normal file
View file

@ -0,0 +1,876 @@
#include "common.h"
#include <ctype.h>
#include <plumb.h>
#include <libsec.h>
#include <auth.h>
#include "dat.h"
#pragma varargck argpos imap4cmd 2
#pragma varargck type "Z" char*
int doublequote(Fmt*);
int pipeline = 1;
/* static char Eio[] = "i/o error"; jpc */
typedef struct Imap Imap;
struct Imap {
char *freep; // free this to free the strings below
char *host;
char *user;
char *mbox;
int mustssl;
int refreshtime;
int debug;
ulong tag;
ulong validity;
int nmsg;
int size;
char *base;
char *data;
vlong *uid;
int nuid;
int muid;
Thumbprint *thumb;
// open network connection
Biobuf bin;
Biobuf bout;
int fd;
};
static char*
removecr(char *s)
{
char *r, *w;
for(r=w=s; *r; r++)
if(*r != '\r')
*w++ = *r;
*w = '\0';
return s;
}
//
// send imap4 command
//
static void
imap4cmd(Imap *imap, char *fmt, ...)
{
char buf[128], *p;
va_list va;
va_start(va, fmt);
p = buf+sprint(buf, "9X%lud ", imap->tag);
vseprint(p, buf+sizeof(buf), fmt, va);
va_end(va);
p = buf+strlen(buf);
if(p > (buf+sizeof(buf)-3))
sysfatal("imap4 command too long");
if(imap->debug)
fprint(2, "-> %s\n", buf);
strcpy(p, "\r\n");
Bwrite(&imap->bout, buf, strlen(buf));
Bflush(&imap->bout);
}
enum {
OK,
NO,
BAD,
BYE,
EXISTS,
STATUS,
FETCH,
UNKNOWN,
};
static char *verblist[] = {
[OK] "OK",
[NO] "NO",
[BAD] "BAD",
[BYE] "BYE",
[EXISTS] "EXISTS",
[STATUS] "STATUS",
[FETCH] "FETCH",
};
static int
verbcode(char *verb)
{
int i;
char *q;
if(q = strchr(verb, ' '))
*q = '\0';
for(i=0; i<nelem(verblist); i++)
if(verblist[i] && strcmp(verblist[i], verb)==0){
if(q)
*q = ' ';
return i;
}
if(q)
*q = ' ';
return UNKNOWN;
}
static void
strupr(char *s)
{
for(; *s; s++)
if('a' <= *s && *s <= 'z')
*s += 'A'-'a';
}
static void
imapgrow(Imap *imap, int n)
{
int i;
if(imap->data == nil){
imap->base = emalloc(n+1);
imap->data = imap->base;
imap->size = n+1;
}
if(n >= imap->size){
// friggin microsoft - reallocate
i = imap->data - imap->base;
imap->base = erealloc(imap->base, i+n+1);
imap->data = imap->base + i;
imap->size = n+1;
}
}
//
// get imap4 response line. there might be various
// data or other informational lines mixed in.
//
static char*
imap4resp(Imap *imap)
{
char *line, *p, *ep, *op, *q, *r, *en, *verb;
int i, n;
static char error[256];
while(p = Brdline(&imap->bin, '\n')){
ep = p+Blinelen(&imap->bin);
while(ep > p && (ep[-1]=='\n' || ep[-1]=='\r'))
*--ep = '\0';
if(imap->debug)
fprint(2, "<- %s\n", p);
strupr(p);
switch(p[0]){
case '+':
if(imap->tag == 0)
fprint(2, "unexpected: %s\n", p);
break;
// ``unsolicited'' information; everything happens here.
case '*':
if(p[1]!=' ')
continue;
p += 2;
line = p;
n = strtol(p, &p, 10);
if(*p==' ')
p++;
verb = p;
if(p = strchr(verb, ' '))
p++;
else
p = verb+strlen(verb);
switch(verbcode(verb)){
case OK:
case NO:
case BAD:
// human readable text at p;
break;
case BYE:
// early disconnect
// human readable text at p;
break;
// * 32 EXISTS
case EXISTS:
imap->nmsg = n;
break;
// * STATUS Inbox (MESSAGES 2 UIDVALIDITY 960164964)
case STATUS:
if(q = strstr(p, "MESSAGES"))
imap->nmsg = atoi(q+8);
if(q = strstr(p, "UIDVALIDITY"))
imap->validity = strtoul(q+11, 0, 10);
break;
case FETCH:
// * 1 FETCH (uid 8889 RFC822.SIZE 3031 body[] {3031}
// <3031 bytes of data>
// )
if(strstr(p, "RFC822.SIZE") && strstr(p, "BODY[]")){
if((q = strchr(p, '{'))
&& (n=strtol(q+1, &en, 0), *en=='}')){
if(imap->data == nil || n >= imap->size)
imapgrow(imap, n);
if((i = Bread(&imap->bin, imap->data, n)) != n){
snprint(error, sizeof error,
"short read %d != %d: %r\n",
i, n);
return error;
}
if(imap->debug)
fprint(2, "<- read %d bytes\n", n);
imap->data[n] = '\0';
if(imap->debug)
fprint(2, "<- %s\n", imap->data);
imap->data += n;
imap->size -= n;
p = Brdline(&imap->bin, '\n');
if(imap->debug)
fprint(2, "<- ignoring %.*s\n",
Blinelen(&imap->bin), p);
}else if((q = strchr(p, '"')) && (r = strchr(q+1, '"'))){
*r = '\0';
q++;
n = r-q;
if(imap->data == nil || n >= imap->size)
imapgrow(imap, n);
memmove(imap->data, q, n);
imap->data[n] = '\0';
imap->data += n;
imap->size -= n;
}else
return "confused about FETCH response";
break;
}
// * 1 FETCH (UID 1 RFC822.SIZE 511)
if(q=strstr(p, "RFC822.SIZE")){
imap->size = atoi(q+11);
break;
}
// * 1 FETCH (UID 1 RFC822.HEADER {496}
// <496 bytes of data>
// )
// * 1 FETCH (UID 1 RFC822.HEADER "data")
if(strstr(p, "RFC822.HEADER") || strstr(p, "RFC822.TEXT")){
if((q = strchr(p, '{'))
&& (n=strtol(q+1, &en, 0), *en=='}')){
if(imap->data == nil || n >= imap->size)
imapgrow(imap, n);
if((i = Bread(&imap->bin, imap->data, n)) != n){
snprint(error, sizeof error,
"short read %d != %d: %r\n",
i, n);
return error;
}
if(imap->debug)
fprint(2, "<- read %d bytes\n", n);
imap->data[n] = '\0';
if(imap->debug)
fprint(2, "<- %s\n", imap->data);
imap->data += n;
imap->size -= n;
p = Brdline(&imap->bin, '\n');
if(imap->debug)
fprint(2, "<- ignoring %.*s\n",
Blinelen(&imap->bin), p);
}else if((q = strchr(p, '"')) && (r = strchr(q+1, '"'))){
*r = '\0';
q++;
n = r-q;
if(imap->data == nil || n >= imap->size)
imapgrow(imap, n);
memmove(imap->data, q, n);
imap->data[n] = '\0';
imap->data += n;
imap->size -= n;
}else
return "confused about FETCH response";
break;
}
// * 1 FETCH (UID 1)
// * 2 FETCH (UID 6)
if(q = strstr(p, "UID")){
if(imap->nuid < imap->muid)
imap->uid[imap->nuid++] = ((vlong)imap->validity<<32)|strtoul(q+3, nil, 10);
break;
}
}
if(imap->tag == 0)
return line;
break;
case '9': // response to our message
op = p;
if(p[1]=='X' && strtoul(p+2, &p, 10)==imap->tag){
while(*p==' ')
p++;
imap->tag++;
return p;
}
fprint(2, "expected %lud; got %s\n", imap->tag, op);
break;
default:
if(imap->debug || *p)
fprint(2, "unexpected line: %s\n", p);
}
}
snprint(error, sizeof error, "i/o error: %r\n");
return error;
}
static int
isokay(char *resp)
{
return strncmp(resp, "OK", 2)==0;
}
//
// log in to IMAP4 server, select mailbox, no SSL at the moment
//
static char*
imap4login(Imap *imap)
{
char *s;
UserPasswd *up;
imap->tag = 0;
s = imap4resp(imap);
if(!isokay(s))
return "error in initial IMAP handshake";
if(imap->user != nil)
up = auth_getuserpasswd(auth_getkey, "proto=pass service=imap server=%q user=%q", imap->host, imap->user);
else
up = auth_getuserpasswd(auth_getkey, "proto=pass service=imap server=%q", imap->host);
if(up == nil)
return "cannot find IMAP password";
imap->tag = 1;
imap4cmd(imap, "LOGIN %Z %Z", up->user, up->passwd);
free(up);
if(!isokay(s = imap4resp(imap)))
return s;
imap4cmd(imap, "SELECT %Z", imap->mbox);
if(!isokay(s = imap4resp(imap)))
return s;
return nil;
}
//
// push tls onto a connection
//
int
mypushtls(int fd)
{
int p[2];
char buf[10];
if(pipe(p) < 0)
return -1;
switch(fork()){
case -1:
close(p[0]);
close(p[1]);
return -1;
case 0:
close(p[1]);
dup(p[0], 0);
dup(p[0], 1);
sprint(buf, "/fd/%d", fd);
execl("/bin/tlsrelay", "tlsrelay", "-f", buf, nil);
_exits(nil);
default:
break;
}
close(fd);
close(p[0]);
return p[1];
}
//
// dial and handshake with the imap server
//
static char*
imap4dial(Imap *imap)
{
char *err, *port;
uchar digest[SHA1dlen];
int sfd;
TLSconn conn;
if(imap->fd >= 0){
imap4cmd(imap, "noop");
if(isokay(imap4resp(imap)))
return nil;
close(imap->fd);
imap->fd = -1;
}
if(imap->mustssl)
port = "imaps";
else
port = "imap4";
if((imap->fd = dial(netmkaddr(imap->host, "net", port), 0, 0, 0)) < 0)
return geterrstr();
if(imap->mustssl){
memset(&conn, 0, sizeof conn);
sfd = tlsClient(imap->fd, &conn);
if(sfd < 0)
sysfatal("tlsClient: %r");
if(conn.cert==nil || conn.certlen <= 0)
sysfatal("server did not provide TLS certificate");
sha1(conn.cert, conn.certlen, digest, nil);
if(!imap->thumb || !okThumbprint(digest, imap->thumb)){
fmtinstall('H', encodefmt);
sysfatal("server certificate %.*H not recognized", SHA1dlen, digest);
}
free(conn.cert);
close(imap->fd);
imap->fd = sfd;
if(imap->debug){
char fn[128];
int fd;
snprint(fn, sizeof fn, "%s/ctl", conn.dir);
fd = open(fn, ORDWR);
if(fd < 0)
fprint(2, "opening ctl: %r\n");
if(fprint(fd, "debug") < 0)
fprint(2, "writing ctl: %r\n");
close(fd);
}
}
Binit(&imap->bin, imap->fd, OREAD);
Binit(&imap->bout, imap->fd, OWRITE);
if(err = imap4login(imap)) {
close(imap->fd);
return err;
}
return nil;
}
//
// close connection
//
#if 0 /* jpc */
static void
imap4hangup(Imap *imap)
{
imap4cmd(imap, "LOGOUT");
imap4resp(imap);
close(imap->fd);
}
#endif
//
// download a single message
//
static char*
imap4fetch(Mailbox *mb, Message *m)
{
int i;
char *p, *s, sdigest[2*SHA1dlen+1];
Imap *imap;
imap = mb->aux;
imap->size = 0;
if(!isokay(s = imap4resp(imap)))
return s;
p = imap->base;
if(p == nil)
return "did not get message body";
removecr(p);
free(m->start);
m->start = p;
m->end = p+strlen(p);
m->bend = m->rbend = m->end;
m->header = m->start;
imap->base = nil;
imap->data = nil;
parse(m, 0, mb, 1);
// digest headers
sha1((uchar*)m->start, m->end - m->start, m->digest, nil);
for(i = 0; i < SHA1dlen; i++)
sprint(sdigest+2*i, "%2.2ux", m->digest[i]);
m->sdigest = s_copy(sdigest);
return nil;
}
//
// check for new messages on imap4 server
// download new messages, mark deleted messages
//
static char*
imap4read(Imap *imap, Mailbox *mb, int doplumb)
{
char *s;
int i, ignore, nnew, t;
Message *m, *next, **l;
imap4cmd(imap, "STATUS %Z (MESSAGES UIDVALIDITY)", imap->mbox);
if(!isokay(s = imap4resp(imap)))
return s;
imap->nuid = 0;
imap->uid = erealloc(imap->uid, imap->nmsg*sizeof(imap->uid[0]));
imap->muid = imap->nmsg;
if(imap->nmsg > 0){
imap4cmd(imap, "UID FETCH 1:* UID");
if(!isokay(s = imap4resp(imap)))
return s;
}
l = &mb->root->part;
for(i=0; i<imap->nuid; i++){
ignore = 0;
while(*l != nil){
if((*l)->imapuid == imap->uid[i]){
ignore = 1;
l = &(*l)->next;
break;
}else{
// old mail, we don't have it anymore
if(doplumb)
mailplumb(mb, *l, 1);
(*l)->inmbox = 0;
(*l)->deleted = 1;
l = &(*l)->next;
}
}
if(ignore)
continue;
// new message
m = newmessage(mb->root);
m->mallocd = 1;
m->inmbox = 1;
m->imapuid = imap->uid[i];
// add to chain, will download soon
*l = m;
l = &m->next;
}
// whatever is left at the end of the chain is gone
while(*l != nil){
if(doplumb)
mailplumb(mb, *l, 1);
(*l)->inmbox = 0;
(*l)->deleted = 1;
l = &(*l)->next;
}
// download new messages
t = imap->tag;
if(pipeline)
switch(rfork(RFPROC|RFMEM)){
case -1:
sysfatal("rfork: %r");
default:
break;
case 0:
for(m = mb->root->part; m != nil; m = m->next){
if(m->start != nil)
continue;
if(imap->debug)
fprint(2, "9X%d UID FETCH %lud (UID RFC822.SIZE BODY[])\r\n",
t, (ulong)m->imapuid);
Bprint(&imap->bout, "9X%d UID FETCH %lud (UID RFC822.SIZE BODY[])\r\n",
t++, (ulong)m->imapuid);
}
Bflush(&imap->bout);
_exits(nil);
}
nnew = 0;
for(m=mb->root->part; m!=nil; m=next){
next = m->next;
if(m->start != nil)
continue;
if(!pipeline){
Bprint(&imap->bout, "9X%lud UID FETCH %lud (UID RFC822.SIZE BODY[])\r\n",
(ulong)imap->tag, (ulong)m->imapuid);
Bflush(&imap->bout);
}
if(s = imap4fetch(mb, m)){
// message disappeared? unchain
fprint(2, "download %lud: %s\n", (ulong)m->imapuid, s);
delmessage(mb, m);
mb->root->subname--;
continue;
}
nnew++;
if(doplumb)
mailplumb(mb, m, 0);
}
if(pipeline)
waitpid();
if(nnew || mb->vers == 0){
mb->vers++;
henter(PATH(0, Qtop), mb->name,
(Qid){PATH(mb->id, Qmbox), mb->vers, QTDIR}, nil, mb);
}
return nil;
}
//
// sync mailbox
//
static void
imap4purge(Imap *imap, Mailbox *mb)
{
int ndel;
Message *m, *next;
ndel = 0;
for(m=mb->root->part; m!=nil; m=next){
next = m->next;
if(m->deleted && m->refs==0){
if(m->inmbox && (ulong)(m->imapuid>>32)==imap->validity){
imap4cmd(imap, "UID STORE %lud +FLAGS (\\Deleted)", (ulong)m->imapuid);
if(isokay(imap4resp(imap))){
ndel++;
delmessage(mb, m);
}
}else
delmessage(mb, m);
}
}
if(ndel){
imap4cmd(imap, "EXPUNGE");
imap4resp(imap);
}
}
//
// connect to imap4 server, sync mailbox
//
static char*
imap4sync(Mailbox *mb, int doplumb)
{
char *err;
Imap *imap;
imap = mb->aux;
if(err = imap4dial(imap)){
mb->waketime = time(0) + imap->refreshtime;
return err;
}
if((err = imap4read(imap, mb, doplumb)) == nil){
imap4purge(imap, mb);
mb->d->atime = mb->d->mtime = time(0);
}
/*
* don't hang up; leave connection open for next time.
*/
// imap4hangup(imap);
mb->waketime = time(0) + imap->refreshtime;
return err;
}
static char Eimap4ctl[] = "bad imap4 control message";
static char*
imap4ctl(Mailbox *mb, int argc, char **argv)
{
int n;
Imap *imap;
imap = mb->aux;
if(argc < 1)
return Eimap4ctl;
if(argc==1 && strcmp(argv[0], "debug")==0){
imap->debug = 1;
return nil;
}
if(argc==1 && strcmp(argv[0], "nodebug")==0){
imap->debug = 0;
return nil;
}
if(argc==1 && strcmp(argv[0], "thumbprint")==0){
if(imap->thumb)
freeThumbprints(imap->thumb);
imap->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude");
}
if(strcmp(argv[0], "refresh")==0){
if(argc==1){
imap->refreshtime = 60;
return nil;
}
if(argc==2){
n = atoi(argv[1]);
if(n < 15)
return Eimap4ctl;
imap->refreshtime = n;
return nil;
}
}
return Eimap4ctl;
}
//
// free extra memory associated with mb
//
static void
imap4close(Mailbox *mb)
{
Imap *imap;
imap = mb->aux;
free(imap->freep);
free(imap->base);
free(imap->uid);
if(imap->fd >= 0)
close(imap->fd);
free(imap);
}
//
// open mailboxes of the form /imap/host/user
//
char*
imap4mbox(Mailbox *mb, char *path)
{
char *f[10];
int mustssl, nf;
Imap *imap;
quotefmtinstall();
fmtinstall('Z', doublequote);
if(strncmp(path, "/imap/", 6) != 0 && strncmp(path, "/imaps/", 7) != 0)
return Enotme;
mustssl = (strncmp(path, "/imaps/", 7) == 0);
path = strdup(path);
if(path == nil)
return "out of memory";
nf = getfields(path, f, 5, 0, "/");
if(nf < 3){
free(path);
return "bad imap path syntax /imap[s]/system[/user[/mailbox]]";
}
imap = emalloc(sizeof(*imap));
imap->fd = -1;
imap->debug = debug;
imap->freep = path;
imap->mustssl = mustssl;
imap->host = f[2];
if(nf < 4)
imap->user = nil;
else
imap->user = f[3];
if(nf < 5)
imap->mbox = "Inbox";
else
imap->mbox = f[4];
imap->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude");
mb->aux = imap;
mb->sync = imap4sync;
mb->close = imap4close;
mb->ctl = imap4ctl;
mb->d = emalloc(sizeof(*mb->d));
//mb->fetch = imap4fetch;
return nil;
}
//
// Formatter for %"
// Use double quotes to protect white space, frogs, \ and "
//
enum
{
Qok = 0,
Qquote,
Qbackslash,
};
static int
needtoquote(Rune r)
{
if(r >= Runeself)
return Qquote;
if(r <= ' ')
return Qquote;
if(r=='\\' || r=='"')
return Qbackslash;
return Qok;
}
int
doublequote(Fmt *f)
{
char *s, *t;
int w, quotes;
Rune r;
s = va_arg(f->args, char*);
if(s == nil || *s == '\0')
return fmtstrcpy(f, "\"\"");
quotes = 0;
for(t=s; *t; t+=w){
w = chartorune(&r, t);
quotes |= needtoquote(r);
}
if(quotes == 0)
return fmtstrcpy(f, s);
fmtrune(f, '"');
for(t=s; *t; t+=w){
w = chartorune(&r, t);
if(needtoquote(r) == Qbackslash)
fmtrune(f, '\\');
fmtrune(f, r);
}
return fmtrune(f, '"');
}

1601
src/cmd/upas/fs/mbox.c Normal file

File diff suppressed because it is too large Load diff

29
src/cmd/upas/fs/mkfile Normal file
View file

@ -0,0 +1,29 @@
<$PLAN9/src/mkhdr
TARG= fs\
OFILES=\
fs.$O\
imap4.$O\
mbox.$O\
plan9.$O\
pop3.$O\
strtotm.$O\
LIB=../common/libcommon.a\
# /usr/local/plan9/lib/libthread.a
HFILES= ../common/common.h\
dat.h
BIN=$PLAN9/bin/upas
UPDATE=\
mkfile\
$HFILES\
${TARG:%=%.c}\
${OFILES:%.$O=%.c}\
<$PLAN9/src/mkone
CFLAGS=$CFLAGS -I../common
# CFLAGS=$CFLAGS -I/sys/include -I../common

27
src/cmd/upas/fs/mkfile.9 Normal file
View file

@ -0,0 +1,27 @@
</$objtype/mkfile
TARG= fs\
OFILES=\
fs.$O\
imap4.$O\
mbox.$O\
plan9.$O\
pop3.$O\
strtotm.$O\
LIB=../common/libcommon.a$O\
HFILES= ../common/common.h\
dat.h
BIN=/$objtype/bin/upas
UPDATE=\
mkfile\
$HFILES\
${TARG:%=%.c}\
${OFILES:%.$O=%.c}\
</sys/src/cmd/mkone
CFLAGS=$CFLAGS -I/sys/include -I../common

405
src/cmd/upas/fs/plan9.c Normal file
View file

@ -0,0 +1,405 @@
#include "common.h"
#include <ctype.h>
#include <plumb.h>
#include <libsec.h>
#include "dat.h"
enum {
Buffersize = 64*1024,
};
typedef struct Inbuf Inbuf;
struct Inbuf
{
int fd;
uchar *lim;
uchar *rptr;
uchar *wptr;
uchar data[Buffersize+7];
};
static void
addtomessage(Message *m, uchar *p, int n, int done)
{
int i, len;
// add to message (+ 1 in malloc is for a trailing null)
if(m->lim - m->end < n){
if(m->start != nil){
i = m->end-m->start;
if(done)
len = i + n;
else
len = (4*(i+n))/3;
m->start = erealloc(m->start, len + 1);
m->end = m->start + i;
} else {
if(done)
len = n;
else
len = 2*n;
m->start = emalloc(len + 1);
m->end = m->start;
}
m->lim = m->start + len;
}
memmove(m->end, p, n);
m->end += n;
}
//
// read in a single message
//
static int
readmessage(Message *m, Inbuf *inb)
{
int i, n, done;
uchar *p, *np;
char sdigest[SHA1dlen*2+1];
char tmp[64];
for(done = 0; !done;){
n = inb->wptr - inb->rptr;
if(n < 6){
if(n)
memmove(inb->data, inb->rptr, n);
inb->rptr = inb->data;
inb->wptr = inb->rptr + n;
i = read(inb->fd, inb->wptr, Buffersize);
if(i < 0){
/* if(fd2path(inb->fd, tmp, sizeof tmp) < 0)
strcpy(tmp, "unknown mailbox"); jpc */
fprint(2, "error reading '%s': %r\n", tmp);
return -1;
}
if(i == 0){
if(n != 0)
addtomessage(m, inb->rptr, n, 1);
if(m->end == m->start)
return -1;
break;
}
inb->wptr += i;
}
// look for end of message
for(p = inb->rptr; p < inb->wptr; p = np+1){
// first part of search for '\nFrom '
np = memchr(p, '\n', inb->wptr - p);
if(np == nil){
p = inb->wptr;
break;
}
/*
* if we've found a \n but there's
* not enough room for '\nFrom ', don't do
* the comparison till we've read in more.
*/
if(inb->wptr - np < 6){
p = np;
break;
}
if(strncmp((char*)np, "\nFrom ", 6) == 0){
done = 1;
p = np+1;
break;
}
}
// add to message (+ 1 in malloc is for a trailing null)
n = p - inb->rptr;
addtomessage(m, inb->rptr, n, done);
inb->rptr += n;
}
// if it doesn't start with a 'From ', this ain't a mailbox
if(strncmp(m->start, "From ", 5) != 0)
return -1;
// dump trailing newline, make sure there's a trailing null
// (helps in body searches)
if(*(m->end-1) == '\n')
m->end--;
*m->end = 0;
m->bend = m->rbend = m->end;
// digest message
sha1((uchar*)m->start, m->end - m->start, m->digest, nil);
for(i = 0; i < SHA1dlen; i++)
sprint(sdigest+2*i, "%2.2ux", m->digest[i]);
m->sdigest = s_copy(sdigest);
return 0;
}
// throw out deleted messages. return number of freshly deleted messages
int
purgedeleted(Mailbox *mb)
{
Message *m, *next;
int newdels;
// forget about what's no longer in the mailbox
newdels = 0;
for(m = mb->root->part; m != nil; m = next){
next = m->next;
if(m->deleted && m->refs == 0){
if(m->inmbox)
newdels++;
delmessage(mb, m);
}
}
return newdels;
}
//
// read in the mailbox and parse into messages.
//
static char*
_readmbox(Mailbox *mb, int doplumb, Mlock *lk)
{
int fd;
String *tmp;
Dir *d;
static char err[128];
Message *m, **l;
Inbuf *inb;
char *x;
l = &mb->root->part;
/*
* open the mailbox. If it doesn't exist, try the temporary one.
*/
retry:
fd = open(mb->path, OREAD);
if(fd < 0){
errstr(err, sizeof(err));
if(strstr(err, "exist") != 0){
tmp = s_copy(mb->path);
s_append(tmp, ".tmp");
if(sysrename(s_to_c(tmp), mb->path) == 0){
s_free(tmp);
goto retry;
}
s_free(tmp);
}
return err;
}
/*
* a new qid.path means reread the mailbox, while
* a new qid.vers means read any new messages
*/
d = dirfstat(fd);
if(d == nil){
close(fd);
errstr(err, sizeof(err));
return err;
}
if(mb->d != nil){
if(d->qid.path == mb->d->qid.path && d->qid.vers == mb->d->qid.vers){
close(fd);
free(d);
return nil;
}
if(d->qid.path == mb->d->qid.path){
while(*l != nil)
l = &(*l)->next;
seek(fd, mb->d->length, 0);
}
free(mb->d);
}
mb->d = d;
mb->vers++;
henter(PATH(0, Qtop), mb->name,
(Qid){PATH(mb->id, Qmbox), mb->vers, QTDIR}, nil, mb);
inb = emalloc(sizeof(Inbuf));
inb->rptr = inb->wptr = inb->data;
inb->fd = fd;
// read new messages
snprint(err, sizeof err, "reading '%s'", mb->path);
logmsg(err, nil);
for(;;){
if(lk != nil)
syslockrefresh(lk);
m = newmessage(mb->root);
m->mallocd = 1;
m->inmbox = 1;
if(readmessage(m, inb) < 0){
delmessage(mb, m);
mb->root->subname--;
break;
}
// merge mailbox versions
while(*l != nil){
if(memcmp((*l)->digest, m->digest, SHA1dlen) == 0){
// matches mail we already read, discard
logmsg("duplicate", *l);
delmessage(mb, m);
mb->root->subname--;
m = nil;
l = &(*l)->next;
break;
} else {
// old mail no longer in box, mark deleted
logmsg("disappeared", *l);
if(doplumb)
mailplumb(mb, *l, 1);
(*l)->inmbox = 0;
(*l)->deleted = 1;
l = &(*l)->next;
}
}
if(m == nil)
continue;
x = strchr(m->start, '\n');
if(x == nil)
m->header = m->end;
else
m->header = x + 1;
m->mheader = m->mhend = m->header;
parseunix(m);
parse(m, 0, mb, 0);
logmsg("new", m);
/* chain in */
*l = m;
l = &m->next;
if(doplumb)
mailplumb(mb, m, 0);
}
logmsg("mbox read", nil);
// whatever is left has been removed from the mbox, mark deleted
while(*l != nil){
if(doplumb)
mailplumb(mb, *l, 1);
(*l)->inmbox = 0;
(*l)->deleted = 1;
l = &(*l)->next;
}
close(fd);
free(inb);
return nil;
}
static void
_writembox(Mailbox *mb, Mlock *lk)
{
Dir *d;
Message *m;
String *tmp;
int mode, errs;
Biobuf *b;
tmp = s_copy(mb->path);
s_append(tmp, ".tmp");
/*
* preserve old files permissions, if possible
*/
d = dirstat(mb->path);
if(d != nil){
mode = d->mode&0777;
free(d);
} else
mode = MBOXMODE;
sysremove(s_to_c(tmp));
b = sysopen(s_to_c(tmp), "alc", mode);
if(b == 0){
fprint(2, "can't write temporary mailbox %s: %r\n", s_to_c(tmp));
return;
}
logmsg("writing new mbox", nil);
errs = 0;
for(m = mb->root->part; m != nil; m = m->next){
if(lk != nil)
syslockrefresh(lk);
if(m->deleted)
continue;
logmsg("writing", m);
if(Bwrite(b, m->start, m->end - m->start) < 0)
errs = 1;
if(Bwrite(b, "\n", 1) < 0)
errs = 1;
}
logmsg("wrote new mbox", nil);
if(sysclose(b) < 0)
errs = 1;
if(errs){
fprint(2, "error writing temporary mail file\n");
s_free(tmp);
return;
}
sysremove(mb->path);
if(sysrename(s_to_c(tmp), mb->path) < 0)
fprint(2, "%s: can't rename %s to %s: %r\n", argv0,
s_to_c(tmp), mb->path);
s_free(tmp);
if(mb->d != nil)
free(mb->d);
mb->d = dirstat(mb->path);
}
char*
plan9syncmbox(Mailbox *mb, int doplumb)
{
Mlock *lk;
char *rv;
lk = nil;
if(mb->dolock){
lk = syslock(mb->path);
if(lk == nil)
return "can't lock mailbox";
}
rv = _readmbox(mb, doplumb, lk); /* interpolate */
if(purgedeleted(mb) > 0)
_writembox(mb, lk);
if(lk != nil)
sysunlock(lk);
return rv;
}
//
// look to see if we can open this mail box
//
char*
plan9mbox(Mailbox *mb, char *path)
{
static char err[64];
String *tmp;
if(access(path, AEXIST) < 0){
errstr(err, sizeof(err));
tmp = s_copy(path);
s_append(tmp, ".tmp");
if(access(s_to_c(tmp), AEXIST) < 0){
s_free(tmp);
return err;
}
s_free(tmp);
}
mb->sync = plan9syncmbox;
return nil;
}

700
src/cmd/upas/fs/pop3.c Normal file
View file

@ -0,0 +1,700 @@
#include "common.h"
#include <ctype.h>
#include <plumb.h>
#include <libsec.h>
#include <auth.h>
#include <thread.h>
#include "dat.h"
#pragma varargck type "M" uchar*
#pragma varargck argpos pop3cmd 2
typedef struct Pop Pop;
struct Pop {
char *freep; // free this to free the strings below
char *host;
char *user;
char *port;
int ppop;
int refreshtime;
int debug;
int pipeline;
int encrypted;
int needtls;
int notls;
int needssl;
// open network connection
Biobuf bin;
Biobuf bout;
int fd;
char *lastline; // from Brdstr
Thumbprint *thumb;
};
char*
geterrstr(void)
{
static char err[64];
err[0] = '\0';
errstr(err, sizeof(err));
return err;
}
//
// get pop3 response line , without worrying
// about multiline responses; the clients
// will deal with that.
//
static int
isokay(char *s)
{
return s!=nil && strncmp(s, "+OK", 3)==0;
}
static void
pop3cmd(Pop *pop, char *fmt, ...)
{
char buf[128], *p;
va_list va;
va_start(va, fmt);
vseprint(buf, buf+sizeof(buf), fmt, va);
va_end(va);
p = buf+strlen(buf);
if(p > (buf+sizeof(buf)-3))
sysfatal("pop3 command too long");
if(pop->debug)
fprint(2, "<- %s\n", buf);
strcpy(p, "\r\n");
Bwrite(&pop->bout, buf, strlen(buf));
Bflush(&pop->bout);
}
static char*
pop3resp(Pop *pop)
{
char *s;
char *p;
alarm(60*1000);
if((s = Brdstr(&pop->bin, '\n', 0)) == nil){
close(pop->fd);
pop->fd = -1;
alarm(0);
return "unexpected eof";
}
alarm(0);
p = s+strlen(s)-1;
while(p >= s && (*p == '\r' || *p == '\n'))
*p-- = '\0';
if(pop->debug)
fprint(2, "-> %s\n", s);
free(pop->lastline);
pop->lastline = s;
return s;
}
#if 0 /* jpc */
static int
pop3log(char *fmt, ...)
{
va_list ap;
va_start(ap,fmt);
syslog(0, "/sys/log/pop3", fmt, ap);
va_end(ap);
return 0;
}
#endif
static char*
pop3pushtls(Pop *pop)
{
int fd;
uchar digest[SHA1dlen];
TLSconn conn;
memset(&conn, 0, sizeof conn);
// conn.trace = pop3log;
fd = tlsClient(pop->fd, &conn);
if(fd < 0)
return "tls error";
if(conn.cert==nil || conn.certlen <= 0){
close(fd);
return "server did not provide TLS certificate";
}
sha1(conn.cert, conn.certlen, digest, nil);
if(!pop->thumb || !okThumbprint(digest, pop->thumb)){
fmtinstall('H', encodefmt);
close(fd);
free(conn.cert);
fprint(2, "upas/fs pop3: server certificate %.*H not recognized\n", SHA1dlen, digest);
return "bad server certificate";
}
free(conn.cert);
close(pop->fd);
pop->fd = fd;
pop->encrypted = 1;
Binit(&pop->bin, pop->fd, OREAD);
Binit(&pop->bout, pop->fd, OWRITE);
return nil;
}
//
// get capability list, possibly start tls
//
static char*
pop3capa(Pop *pop)
{
char *s;
int hastls;
pop3cmd(pop, "CAPA");
if(!isokay(pop3resp(pop)))
return nil;
hastls = 0;
for(;;){
s = pop3resp(pop);
if(strcmp(s, ".") == 0 || strcmp(s, "unexpected eof") == 0)
break;
if(strcmp(s, "STLS") == 0)
hastls = 1;
if(strcmp(s, "PIPELINING") == 0)
pop->pipeline = 1;
}
if(hastls && !pop->notls){
pop3cmd(pop, "STLS");
if(!isokay(s = pop3resp(pop)))
return s;
if((s = pop3pushtls(pop)) != nil)
return s;
}
return nil;
}
//
// log in using APOP if possible, password if allowed by user
//
static char*
pop3login(Pop *pop)
{
int n;
char *s, *p, *q;
char ubuf[128], user[128];
char buf[500];
UserPasswd *up;
s = pop3resp(pop);
if(!isokay(s))
return "error in initial handshake";
if(pop->user)
snprint(ubuf, sizeof ubuf, " user=%q", pop->user);
else
ubuf[0] = '\0';
// look for apop banner
if(pop->ppop==0 && (p = strchr(s, '<')) && (q = strchr(p+1, '>'))) {
*++q = '\0';
if((n=auth_respond(p, q-p, user, sizeof user, buf, sizeof buf, auth_getkey, "proto=apop role=client server=%q%s",
pop->host, ubuf)) < 0)
return "factotum failed";
if(user[0]=='\0')
return "factotum did not return a user name";
if(s = pop3capa(pop))
return s;
pop3cmd(pop, "APOP %s %.*s", user, n, buf);
if(!isokay(s = pop3resp(pop)))
return s;
return nil;
} else {
if(pop->ppop == 0)
return "no APOP hdr from server";
if(s = pop3capa(pop))
return s;
if(pop->needtls && !pop->encrypted)
return "could not negotiate TLS";
up = auth_getuserpasswd(auth_getkey, "role=client proto=pass service=pop dom=%q%s",
pop->host, ubuf);
/* up = auth_getuserpasswd(auth_getkey, "proto=pass service=pop dom=%q%s",
pop->host, ubuf); jpc */
if(up == nil)
return "no usable keys found";
pop3cmd(pop, "USER %s", up->user);
if(!isokay(s = pop3resp(pop))){
free(up);
return s;
}
pop3cmd(pop, "PASS %s", up->passwd);
free(up);
if(!isokay(s = pop3resp(pop)))
return s;
return nil;
}
}
//
// dial and handshake with pop server
//
static char*
pop3dial(Pop *pop)
{
char *err;
if((pop->fd = dial(netmkaddr(pop->host, "net", pop->needssl ? "pop3s" : "pop3"), 0, 0, 0)) < 0)
return geterrstr();
if(pop->needssl){
if((err = pop3pushtls(pop)) != nil)
return err;
}else{
Binit(&pop->bin, pop->fd, OREAD);
Binit(&pop->bout, pop->fd, OWRITE);
}
if(err = pop3login(pop)) {
close(pop->fd);
return err;
}
return nil;
}
//
// close connection
//
static void
pop3hangup(Pop *pop)
{
pop3cmd(pop, "QUIT");
pop3resp(pop);
close(pop->fd);
}
//
// download a single message
//
static char*
pop3download(Pop *pop, Message *m)
{
char *s, *f[3], *wp, *ep;
char sdigest[SHA1dlen*2+1];
int i, l, sz;
if(!pop->pipeline)
pop3cmd(pop, "LIST %d", m->mesgno);
if(!isokay(s = pop3resp(pop)))
return s;
if(tokenize(s, f, 3) != 3)
return "syntax error in LIST response";
if(atoi(f[1]) != m->mesgno)
return "out of sync with pop3 server";
sz = atoi(f[2])+200; /* 200 because the plan9 pop3 server lies */
if(sz == 0)
return "invalid size in LIST response";
m->start = wp = emalloc(sz+1);
ep = wp+sz;
if(!pop->pipeline)
pop3cmd(pop, "RETR %d", m->mesgno);
if(!isokay(s = pop3resp(pop))) {
m->start = nil;
free(wp);
return s;
}
s = nil;
while(wp <= ep) {
s = pop3resp(pop);
if(strcmp(s, "unexpected eof") == 0) {
free(m->start);
m->start = nil;
return "unexpected end of conversation";
}
if(strcmp(s, ".") == 0)
break;
l = strlen(s)+1;
if(s[0] == '.') {
s++;
l--;
}
/*
* grow by 10%/200bytes - some servers
* lie about message sizes
*/
if(wp+l > ep) {
int pos = wp - m->start;
sz += ((sz / 10) < 200)? 200: sz/10;
m->start = erealloc(m->start, sz+1);
wp = m->start+pos;
ep = m->start+sz;
}
memmove(wp, s, l-1);
wp[l-1] = '\n';
wp += l;
}
if(s == nil || strcmp(s, ".") != 0)
return "out of sync with pop3 server";
m->end = wp;
// make sure there's a trailing null
// (helps in body searches)
*m->end = 0;
m->bend = m->rbend = m->end;
m->header = m->start;
// digest message
sha1((uchar*)m->start, m->end - m->start, m->digest, nil);
for(i = 0; i < SHA1dlen; i++)
sprint(sdigest+2*i, "%2.2ux", m->digest[i]);
m->sdigest = s_copy(sdigest);
return nil;
}
//
// check for new messages on pop server
// UIDL is not required by RFC 1939, but
// netscape requires it, so almost every server supports it.
// we'll use it to make our lives easier.
//
static char*
pop3read(Pop *pop, Mailbox *mb, int doplumb)
{
char *s, *p, *uidl, *f[2];
int mesgno, ignore, nnew;
Message *m, *next, **l;
// Some POP servers disallow UIDL if the maildrop is empty.
pop3cmd(pop, "STAT");
if(!isokay(s = pop3resp(pop)))
return s;
// fetch message listing; note messages to grab
l = &mb->root->part;
if(strncmp(s, "+OK 0 ", 6) != 0) {
pop3cmd(pop, "UIDL");
if(!isokay(s = pop3resp(pop)))
return s;
for(;;){
p = pop3resp(pop);
if(strcmp(p, ".") == 0 || strcmp(p, "unexpected eof") == 0)
break;
if(tokenize(p, f, 2) != 2)
continue;
mesgno = atoi(f[0]);
uidl = f[1];
if(strlen(uidl) > 75) // RFC 1939 says 70 characters max
continue;
ignore = 0;
while(*l != nil) {
if(strcmp((*l)->uidl, uidl) == 0) {
// matches mail we already have, note mesgno for deletion
(*l)->mesgno = mesgno;
ignore = 1;
l = &(*l)->next;
break;
} else {
// old mail no longer in box mark deleted
if(doplumb)
mailplumb(mb, *l, 1);
(*l)->inmbox = 0;
(*l)->deleted = 1;
l = &(*l)->next;
}
}
if(ignore)
continue;
m = newmessage(mb->root);
m->mallocd = 1;
m->inmbox = 1;
m->mesgno = mesgno;
strcpy(m->uidl, uidl);
// chain in; will fill in message later
*l = m;
l = &m->next;
}
}
// whatever is left has been removed from the mbox, mark as deleted
while(*l != nil) {
if(doplumb)
mailplumb(mb, *l, 1);
(*l)->inmbox = 0;
(*l)->deleted = 1;
l = &(*l)->next;
}
// download new messages
nnew = 0;
if(pop->pipeline){
switch(rfork(RFPROC|RFMEM)){
case -1:
fprint(2, "rfork: %r\n");
pop->pipeline = 0;
default:
break;
case 0:
for(m = mb->root->part; m != nil; m = m->next){
if(m->start != nil)
continue;
Bprint(&pop->bout, "LIST %d\r\nRETR %d\r\n", m->mesgno, m->mesgno);
}
Bflush(&pop->bout);
threadexits(nil);
/* _exits(nil); jpc */
}
}
for(m = mb->root->part; m != nil; m = next) {
next = m->next;
if(m->start != nil)
continue;
if(s = pop3download(pop, m)) {
// message disappeared? unchain
fprint(2, "download %d: %s\n", m->mesgno, s);
delmessage(mb, m);
mb->root->subname--;
continue;
}
nnew++;
parse(m, 0, mb, 1);
if(doplumb)
mailplumb(mb, m, 0);
}
if(pop->pipeline)
waitpid();
if(nnew || mb->vers == 0) {
mb->vers++;
henter(PATH(0, Qtop), mb->name,
(Qid){PATH(mb->id, Qmbox), mb->vers, QTDIR}, nil, mb);
}
return nil;
}
//
// delete marked messages
//
static void
pop3purge(Pop *pop, Mailbox *mb)
{
Message *m, *next;
if(pop->pipeline){
switch(rfork(RFPROC|RFMEM)){
case -1:
fprint(2, "rfork: %r\n");
pop->pipeline = 0;
default:
break;
case 0:
for(m = mb->root->part; m != nil; m = next){
next = m->next;
if(m->deleted && m->refs == 0){
if(m->inmbox)
Bprint(&pop->bout, "DELE %d\r\n", m->mesgno);
}
}
Bflush(&pop->bout);
/* _exits(nil); jpc */
threadexits(nil);
}
}
for(m = mb->root->part; m != nil; m = next) {
next = m->next;
if(m->deleted && m->refs == 0) {
if(m->inmbox) {
if(!pop->pipeline)
pop3cmd(pop, "DELE %d", m->mesgno);
if(isokay(pop3resp(pop)))
delmessage(mb, m);
} else
delmessage(mb, m);
}
}
}
// connect to pop3 server, sync mailbox
static char*
pop3sync(Mailbox *mb, int doplumb)
{
char *err;
Pop *pop;
pop = mb->aux;
if(err = pop3dial(pop)) {
mb->waketime = time(0) + pop->refreshtime;
return err;
}
if((err = pop3read(pop, mb, doplumb)) == nil){
pop3purge(pop, mb);
mb->d->atime = mb->d->mtime = time(0);
}
pop3hangup(pop);
mb->waketime = time(0) + pop->refreshtime;
return err;
}
static char Epop3ctl[] = "bad pop3 control message";
static char*
pop3ctl(Mailbox *mb, int argc, char **argv)
{
int n;
Pop *pop;
char *m, *me;
pop = mb->aux;
if(argc < 1)
return Epop3ctl;
if(argc==1 && strcmp(argv[0], "debug")==0){
pop->debug = 1;
return nil;
}
if(argc==1 && strcmp(argv[0], "nodebug")==0){
pop->debug = 0;
return nil;
}
if(argc==1 && strcmp(argv[0], "thumbprint")==0){
if(pop->thumb)
freeThumbprints(pop->thumb);
/* pop->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude"); jpc */
m = unsharp("#9/sys/lib/tls/mail");
me = unsharp("#9/sys/lib/tls/mail.exclude");
pop->thumb = initThumbprints(m, me);
}
if(strcmp(argv[0], "refresh")==0){
if(argc==1){
pop->refreshtime = 60;
return nil;
}
if(argc==2){
n = atoi(argv[1]);
if(n < 15)
return Epop3ctl;
pop->refreshtime = n;
return nil;
}
}
return Epop3ctl;
}
// free extra memory associated with mb
static void
pop3close(Mailbox *mb)
{
Pop *pop;
pop = mb->aux;
free(pop->freep);
free(pop);
}
//
// open mailboxes of the form /pop/host/user or /apop/host/user
//
char*
pop3mbox(Mailbox *mb, char *path)
{
char *f[10];
int nf, apop, ppop, popssl, apopssl, apoptls, popnotls, apopnotls, poptls;
Pop *pop;
char *m, *me;
quotefmtinstall();
popssl = strncmp(path, "/pops/", 6) == 0;
apopssl = strncmp(path, "/apops/", 7) == 0;
poptls = strncmp(path, "/poptls/", 8) == 0;
popnotls = strncmp(path, "/popnotls/", 10) == 0;
ppop = popssl || poptls || popnotls || strncmp(path, "/pop/", 5) == 0;
apoptls = strncmp(path, "/apoptls/", 9) == 0;
apopnotls = strncmp(path, "/apopnotls/", 11) == 0;
apop = apopssl || apoptls || apopnotls || strncmp(path, "/apop/", 6) == 0;
if(!ppop && !apop)
return Enotme;
path = strdup(path);
if(path == nil)
return "out of memory";
nf = getfields(path, f, nelem(f), 0, "/");
if(nf != 3 && nf != 4) {
free(path);
return "bad pop3 path syntax /[a]pop[tls|ssl]/system[/user]";
}
pop = emalloc(sizeof(*pop));
pop->freep = path;
pop->host = f[2];
if(nf < 4)
pop->user = nil;
else
pop->user = f[3];
pop->ppop = ppop;
pop->needssl = popssl || apopssl;
pop->needtls = poptls || apoptls;
pop->refreshtime = 60;
pop->notls = popnotls || apopnotls;
/* pop->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude"); jpc */
m = unsharp("#9/sys/lib/tls/mail");
me = unsharp("#9/sys/lib/tls/mail.exclude");
pop->thumb = initThumbprints(m, me);
mb->aux = pop;
mb->sync = pop3sync;
mb->close = pop3close;
mb->ctl = pop3ctl;
mb->d = emalloc(sizeof(*mb->d));
return nil;
}

15
src/cmd/upas/fs/readdir.c Normal file
View file

@ -0,0 +1,15 @@
#include <u.h>
#include <libc.h>
void
main(void)
{
Dir d;
int fd, n;
fd = open("/mail/fs", OREAD);
while((n = dirread(fd, &d, sizeof(d))) > 0){
print("%s\n", d.name);
}
print("n = %d\n", n);
}

113
src/cmd/upas/fs/strtotm.c Normal file
View file

@ -0,0 +1,113 @@
#include <u.h>
#include <libc.h>
#include <ctype.h>
static char*
skiptext(char *q)
{
while(*q!='\0' && *q!=' ' && *q!='\t' && *q!='\r' && *q!='\n')
q++;
return q;
}
static char*
skipwhite(char *q)
{
while(*q==' ' || *q=='\t' || *q=='\r' || *q=='\n')
q++;
return q;
}
static char* months[] = {
"jan", "feb", "mar", "apr",
"may", "jun", "jul", "aug",
"sep", "oct", "nov", "dec"
};
static int
strcmplwr(char *a, char *b, int n)
{
char *eb;
eb = b+n;
while(*a && *b && b<eb){
if(tolower(*a) != tolower(*b))
return 1;
a++;
b++;
}
if(b==eb)
return 0;
return *a != *b;
}
int
strtotm(char *p, Tm *tmp)
{
char *q, *r;
int j;
Tm tm;
int delta;
delta = 0;
memset(&tm, 0, sizeof(tm));
tm.mon = -1;
tm.hour = -1;
tm.min = -1;
tm.year = -1;
tm.mday = -1;
for(p=skipwhite(p); *p; p=skipwhite(q)){
q = skiptext(p);
/* look for time in hh:mm[:ss] */
if(r = memchr(p, ':', q-p)){
tm.hour = strtol(p, 0, 10);
tm.min = strtol(r+1, 0, 10);
if(r = memchr(r+1, ':', q-(r+1)))
tm.sec = strtol(r+1, 0, 10);
else
tm.sec = 0;
continue;
}
/* look for month */
for(j=0; j<12; j++)
if(strcmplwr(p, months[j], 3)==0){
tm.mon = j;
break;
}
if(j!=12)
continue;
/* look for time zone [A-Z][A-Z]T */
if(q-p==3 && 'A' <= p[0] && p[0] <= 'Z'
&& 'A' <= p[1] && p[1] <= 'Z' && p[2] == 'T'){
strecpy(tm.zone, tm.zone+4, p);
continue;
}
if(p[0]=='+'||p[0]=='-')
if(q-p==5 && strspn(p+1, "0123456789") == 4){
delta = (((p[1]-'0')*10+p[2]-'0')*60+(p[3]-'0')*10+p[4]-'0')*60;
if(p[0] == '-')
delta = -delta;
continue;
}
if(strspn(p, "0123456789") == q-p){
j = strtol(p, nil, 10);
if(1 <= j && j <= 31)
tm.mday = j;
if(j >= 1900)
tm.year = j-1900;
}
}
if(tm.mon<0 || tm.year<0
|| tm.hour<0 || tm.min<0
|| tm.mday<0)
return -1;
*tmp = *localtime(tm2sec(&tm)-delta);
return 0;
}

81
src/cmd/upas/fs/tester.c Normal file
View file

@ -0,0 +1,81 @@
#include <u.h>
#include <libc.h>
#include <ctype.h>
#include <String.h>
#include "message.h"
Message *root;
void
prindent(int i)
{
for(; i > 0; i--)
print(" ");
}
void
prstring(int indent, char *tag, String *s)
{
if(s == nil)
return;
prindent(indent+1);
print("%s %s\n", tag, s_to_c(s));
}
void
info(int indent, int mno, Message *m)
{
int i;
Message *nm;
prindent(indent);
print("%d%c %d ", mno, m->allocated?'*':' ', m->end - m->start);
if(m->unixfrom != nil)
print("uf %s ", s_to_c(m->unixfrom));
if(m->unixdate != nil)
print("ud %s ", s_to_c(m->unixdate));
print("\n");
prstring(indent, "from:", m->from822);
prstring(indent, "sender:", m->sender822);
prstring(indent, "to:", m->to822);
prstring(indent, "cc:", m->cc822);
prstring(indent, "reply-to:", m->replyto822);
prstring(indent, "subject:", m->subject822);
prstring(indent, "date:", m->date822);
prstring(indent, "filename:", m->filename);
prstring(indent, "type:", m->type);
prstring(indent, "charset:", m->charset);
i = 1;
for(nm = m->part; nm != nil; nm = nm->next){
info(indent+1, i++, nm);
}
}
void
main(int argc, char **argv)
{
char *err;
char *mboxfile;
ARGBEGIN{
}ARGEND;
if(argc > 0)
mboxfile = argv[0];
else
mboxfile = "./mbox";
root = newmessage(nil);
err = readmbox(mboxfile, &root->part);
if(err != nil){
fprint(2, "boom: %s\n", err);
exits(0);
}
info(0, 1, root);
exits(0);
}