Thanks to John Cummings.
This commit is contained in:
parent
cd37451963
commit
5cdb17983a
94 changed files with 26853 additions and 0 deletions
221
src/cmd/upas/fs/dat.h
Normal file
221
src/cmd/upas/fs/dat.h
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
typedef struct Message Message;
|
||||
struct Message
|
||||
{
|
||||
int id;
|
||||
int refs;
|
||||
int subname;
|
||||
char name[Elemlen];
|
||||
|
||||
// pointers into message
|
||||
char *start; // start of message
|
||||
char *end; // end of message
|
||||
char *header; // start of header
|
||||
char *hend; // end of header
|
||||
int hlen; // length of header minus ignored fields
|
||||
char *mheader; // start of mime header
|
||||
char *mhend; // end of mime header
|
||||
char *body; // start of body
|
||||
char *bend; // end of body
|
||||
char *rbody; // raw (unprocessed) body
|
||||
char *rbend; // end of raw (unprocessed) body
|
||||
char *lim;
|
||||
char deleted;
|
||||
char inmbox;
|
||||
char mallocd; // message is malloc'd
|
||||
char ballocd; // body is malloc'd
|
||||
char hallocd; // header is malloce'd
|
||||
|
||||
// mail info
|
||||
String *unixheader;
|
||||
String *unixfrom;
|
||||
String *unixdate;
|
||||
String *from822;
|
||||
String *sender822;
|
||||
String *to822;
|
||||
String *bcc822;
|
||||
String *cc822;
|
||||
String *replyto822;
|
||||
String *date822;
|
||||
String *inreplyto822;
|
||||
String *subject822;
|
||||
String *messageid822;
|
||||
String *addrs;
|
||||
String *mimeversion;
|
||||
String *sdigest;
|
||||
|
||||
// mime info
|
||||
String *boundary;
|
||||
String *type;
|
||||
int encoding;
|
||||
int disposition;
|
||||
String *charset;
|
||||
String *filename;
|
||||
int converted;
|
||||
int decoded;
|
||||
char lines[10]; // number of lines in rawbody
|
||||
|
||||
Message *next; // same level
|
||||
Message *part; // down a level
|
||||
Message *whole; // up a level
|
||||
|
||||
uchar digest[SHA1dlen];
|
||||
|
||||
vlong imapuid; // used by imap4
|
||||
|
||||
char uidl[80]; // used by pop3
|
||||
int mesgno;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
// encodings
|
||||
Enone= 0,
|
||||
Ebase64,
|
||||
Equoted,
|
||||
|
||||
// disposition possibilities
|
||||
Dnone= 0,
|
||||
Dinline,
|
||||
Dfile,
|
||||
Dignore,
|
||||
|
||||
PAD64= '=',
|
||||
};
|
||||
|
||||
typedef struct Mailbox Mailbox;
|
||||
struct Mailbox
|
||||
{
|
||||
QLock ql; /* jpc named Qlock */
|
||||
int refs;
|
||||
Mailbox *next;
|
||||
int id;
|
||||
int dolock; // lock when syncing?
|
||||
int std;
|
||||
char name[Elemlen];
|
||||
char path[Pathlen];
|
||||
Dir *d;
|
||||
Message *root;
|
||||
int vers; // goes up each time mailbox is read
|
||||
|
||||
ulong waketime;
|
||||
char *(*sync)(Mailbox*, int);
|
||||
void (*close)(Mailbox*);
|
||||
char *(*fetch)(Mailbox*, Message*);
|
||||
char *(*ctl)(Mailbox*, int, char**);
|
||||
void *aux; // private to Mailbox implementation
|
||||
};
|
||||
|
||||
typedef char *Mailboxinit(Mailbox*, char*);
|
||||
|
||||
extern Message *root;
|
||||
extern Mailboxinit plan9mbox;
|
||||
extern Mailboxinit pop3mbox;
|
||||
extern Mailboxinit imap4mbox;
|
||||
|
||||
char* syncmbox(Mailbox*, int);
|
||||
char* geterrstr(void);
|
||||
void* emalloc(ulong);
|
||||
void* erealloc(void*, ulong);
|
||||
Message* newmessage(Message*);
|
||||
void delmessage(Mailbox*, Message*);
|
||||
void delmessages(int, char**);
|
||||
int newid(void);
|
||||
void mailplumb(Mailbox*, Message*, int);
|
||||
char* newmbox(char*, char*, int);
|
||||
void freembox(char*);
|
||||
void logmsg(char*, Message*);
|
||||
void msgincref(Message*);
|
||||
void msgdecref(Mailbox*, Message*);
|
||||
void mboxincref(Mailbox*);
|
||||
void mboxdecref(Mailbox*);
|
||||
void convert(Message*);
|
||||
void decode(Message*);
|
||||
int cistrncmp(char*, char*, int);
|
||||
int cistrcmp(char*, char*);
|
||||
int latin1toutf(char*, char*, char*);
|
||||
int windows1257toutf(char*, char*, char*);
|
||||
int decquoted(char*, char*, char*);
|
||||
int xtoutf(char*, char**, char*, char*);
|
||||
void countlines(Message*);
|
||||
int headerlen(Message*);
|
||||
void parse(Message*, int, Mailbox*, int);
|
||||
void parseheaders(Message*, int, Mailbox*, int);
|
||||
void parsebody(Message*, Mailbox*);
|
||||
void parseunix(Message*);
|
||||
String* date822tounix(char*);
|
||||
int fidmboxrefs(Mailbox*);
|
||||
int hashmboxrefs(Mailbox*);
|
||||
void checkmboxrefs(void);
|
||||
|
||||
extern int debug;
|
||||
extern int fflag;
|
||||
extern int logging;
|
||||
extern char user[Elemlen];
|
||||
extern char stdmbox[Pathlen];
|
||||
extern QLock mbllock;
|
||||
extern Mailbox *mbl;
|
||||
extern char *mntpt;
|
||||
extern int biffing;
|
||||
extern int plumbing;
|
||||
extern char* Enotme;
|
||||
|
||||
enum
|
||||
{
|
||||
/* mail subobjects */
|
||||
Qbody,
|
||||
Qbcc,
|
||||
Qcc,
|
||||
Qdate,
|
||||
Qdigest,
|
||||
Qdisposition,
|
||||
Qfilename,
|
||||
Qfrom,
|
||||
Qheader,
|
||||
Qinreplyto,
|
||||
Qlines,
|
||||
Qmimeheader,
|
||||
Qmessageid,
|
||||
Qraw,
|
||||
Qrawbody,
|
||||
Qrawheader,
|
||||
Qrawunix,
|
||||
Qreplyto,
|
||||
Qsender,
|
||||
Qsubject,
|
||||
Qto,
|
||||
Qtype,
|
||||
Qunixheader,
|
||||
Qinfo,
|
||||
Qunixdate,
|
||||
Qmax,
|
||||
|
||||
/* other files */
|
||||
Qtop,
|
||||
Qmbox,
|
||||
Qdir,
|
||||
Qctl,
|
||||
Qmboxctl,
|
||||
};
|
||||
|
||||
#define PATH(id, f) ((((id)&0xfffff)<<10) | (f))
|
||||
#define FILE(p) ((p) & 0x3ff)
|
||||
|
||||
/* char *dirtab[]; jpc */
|
||||
|
||||
// hash table to aid in name lookup, all files have an entry
|
||||
typedef struct Hash Hash;
|
||||
struct Hash {
|
||||
Hash *next;
|
||||
char *name;
|
||||
ulong ppath;
|
||||
Qid qid;
|
||||
Mailbox *mb;
|
||||
Message *m;
|
||||
};
|
||||
|
||||
Hash *hlook(ulong, char*);
|
||||
void henter(ulong, char*, Qid, Message*, Mailbox*);
|
||||
void hfree(ulong, char*);
|
||||
|
||||
ulong msgallocd, msgfreed;
|
||||
|
||||
1704
src/cmd/upas/fs/fs.c
Normal file
1704
src/cmd/upas/fs/fs.c
Normal file
File diff suppressed because it is too large
Load diff
876
src/cmd/upas/fs/imap4.c
Normal file
876
src/cmd/upas/fs/imap4.c
Normal file
|
|
@ -0,0 +1,876 @@
|
|||
#include "common.h"
|
||||
#include <ctype.h>
|
||||
#include <plumb.h>
|
||||
#include <libsec.h>
|
||||
#include <auth.h>
|
||||
#include "dat.h"
|
||||
|
||||
#pragma varargck argpos imap4cmd 2
|
||||
#pragma varargck type "Z" char*
|
||||
|
||||
int doublequote(Fmt*);
|
||||
int pipeline = 1;
|
||||
|
||||
/* static char Eio[] = "i/o error"; jpc */
|
||||
|
||||
typedef struct Imap Imap;
|
||||
struct Imap {
|
||||
char *freep; // free this to free the strings below
|
||||
|
||||
char *host;
|
||||
char *user;
|
||||
char *mbox;
|
||||
|
||||
int mustssl;
|
||||
int refreshtime;
|
||||
int debug;
|
||||
|
||||
ulong tag;
|
||||
ulong validity;
|
||||
int nmsg;
|
||||
int size;
|
||||
char *base;
|
||||
char *data;
|
||||
|
||||
vlong *uid;
|
||||
int nuid;
|
||||
int muid;
|
||||
|
||||
Thumbprint *thumb;
|
||||
|
||||
// open network connection
|
||||
Biobuf bin;
|
||||
Biobuf bout;
|
||||
int fd;
|
||||
};
|
||||
|
||||
static char*
|
||||
removecr(char *s)
|
||||
{
|
||||
char *r, *w;
|
||||
|
||||
for(r=w=s; *r; r++)
|
||||
if(*r != '\r')
|
||||
*w++ = *r;
|
||||
*w = '\0';
|
||||
return s;
|
||||
}
|
||||
|
||||
//
|
||||
// send imap4 command
|
||||
//
|
||||
static void
|
||||
imap4cmd(Imap *imap, char *fmt, ...)
|
||||
{
|
||||
char buf[128], *p;
|
||||
va_list va;
|
||||
|
||||
va_start(va, fmt);
|
||||
p = buf+sprint(buf, "9X%lud ", imap->tag);
|
||||
vseprint(p, buf+sizeof(buf), fmt, va);
|
||||
va_end(va);
|
||||
|
||||
p = buf+strlen(buf);
|
||||
if(p > (buf+sizeof(buf)-3))
|
||||
sysfatal("imap4 command too long");
|
||||
|
||||
if(imap->debug)
|
||||
fprint(2, "-> %s\n", buf);
|
||||
strcpy(p, "\r\n");
|
||||
Bwrite(&imap->bout, buf, strlen(buf));
|
||||
Bflush(&imap->bout);
|
||||
}
|
||||
|
||||
enum {
|
||||
OK,
|
||||
NO,
|
||||
BAD,
|
||||
BYE,
|
||||
EXISTS,
|
||||
STATUS,
|
||||
FETCH,
|
||||
UNKNOWN,
|
||||
};
|
||||
|
||||
static char *verblist[] = {
|
||||
[OK] "OK",
|
||||
[NO] "NO",
|
||||
[BAD] "BAD",
|
||||
[BYE] "BYE",
|
||||
[EXISTS] "EXISTS",
|
||||
[STATUS] "STATUS",
|
||||
[FETCH] "FETCH",
|
||||
};
|
||||
|
||||
static int
|
||||
verbcode(char *verb)
|
||||
{
|
||||
int i;
|
||||
char *q;
|
||||
|
||||
if(q = strchr(verb, ' '))
|
||||
*q = '\0';
|
||||
|
||||
for(i=0; i<nelem(verblist); i++)
|
||||
if(verblist[i] && strcmp(verblist[i], verb)==0){
|
||||
if(q)
|
||||
*q = ' ';
|
||||
return i;
|
||||
}
|
||||
if(q)
|
||||
*q = ' ';
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
static void
|
||||
strupr(char *s)
|
||||
{
|
||||
for(; *s; s++)
|
||||
if('a' <= *s && *s <= 'z')
|
||||
*s += 'A'-'a';
|
||||
}
|
||||
|
||||
static void
|
||||
imapgrow(Imap *imap, int n)
|
||||
{
|
||||
int i;
|
||||
|
||||
if(imap->data == nil){
|
||||
imap->base = emalloc(n+1);
|
||||
imap->data = imap->base;
|
||||
imap->size = n+1;
|
||||
}
|
||||
if(n >= imap->size){
|
||||
// friggin microsoft - reallocate
|
||||
i = imap->data - imap->base;
|
||||
imap->base = erealloc(imap->base, i+n+1);
|
||||
imap->data = imap->base + i;
|
||||
imap->size = n+1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// get imap4 response line. there might be various
|
||||
// data or other informational lines mixed in.
|
||||
//
|
||||
static char*
|
||||
imap4resp(Imap *imap)
|
||||
{
|
||||
char *line, *p, *ep, *op, *q, *r, *en, *verb;
|
||||
int i, n;
|
||||
static char error[256];
|
||||
|
||||
while(p = Brdline(&imap->bin, '\n')){
|
||||
ep = p+Blinelen(&imap->bin);
|
||||
while(ep > p && (ep[-1]=='\n' || ep[-1]=='\r'))
|
||||
*--ep = '\0';
|
||||
|
||||
if(imap->debug)
|
||||
fprint(2, "<- %s\n", p);
|
||||
strupr(p);
|
||||
|
||||
switch(p[0]){
|
||||
case '+':
|
||||
if(imap->tag == 0)
|
||||
fprint(2, "unexpected: %s\n", p);
|
||||
break;
|
||||
|
||||
// ``unsolicited'' information; everything happens here.
|
||||
case '*':
|
||||
if(p[1]!=' ')
|
||||
continue;
|
||||
p += 2;
|
||||
line = p;
|
||||
n = strtol(p, &p, 10);
|
||||
if(*p==' ')
|
||||
p++;
|
||||
verb = p;
|
||||
|
||||
if(p = strchr(verb, ' '))
|
||||
p++;
|
||||
else
|
||||
p = verb+strlen(verb);
|
||||
|
||||
switch(verbcode(verb)){
|
||||
case OK:
|
||||
case NO:
|
||||
case BAD:
|
||||
// human readable text at p;
|
||||
break;
|
||||
case BYE:
|
||||
// early disconnect
|
||||
// human readable text at p;
|
||||
break;
|
||||
|
||||
// * 32 EXISTS
|
||||
case EXISTS:
|
||||
imap->nmsg = n;
|
||||
break;
|
||||
|
||||
// * STATUS Inbox (MESSAGES 2 UIDVALIDITY 960164964)
|
||||
case STATUS:
|
||||
if(q = strstr(p, "MESSAGES"))
|
||||
imap->nmsg = atoi(q+8);
|
||||
if(q = strstr(p, "UIDVALIDITY"))
|
||||
imap->validity = strtoul(q+11, 0, 10);
|
||||
break;
|
||||
|
||||
case FETCH:
|
||||
// * 1 FETCH (uid 8889 RFC822.SIZE 3031 body[] {3031}
|
||||
// <3031 bytes of data>
|
||||
// )
|
||||
if(strstr(p, "RFC822.SIZE") && strstr(p, "BODY[]")){
|
||||
if((q = strchr(p, '{'))
|
||||
&& (n=strtol(q+1, &en, 0), *en=='}')){
|
||||
if(imap->data == nil || n >= imap->size)
|
||||
imapgrow(imap, n);
|
||||
if((i = Bread(&imap->bin, imap->data, n)) != n){
|
||||
snprint(error, sizeof error,
|
||||
"short read %d != %d: %r\n",
|
||||
i, n);
|
||||
return error;
|
||||
}
|
||||
if(imap->debug)
|
||||
fprint(2, "<- read %d bytes\n", n);
|
||||
imap->data[n] = '\0';
|
||||
if(imap->debug)
|
||||
fprint(2, "<- %s\n", imap->data);
|
||||
imap->data += n;
|
||||
imap->size -= n;
|
||||
p = Brdline(&imap->bin, '\n');
|
||||
if(imap->debug)
|
||||
fprint(2, "<- ignoring %.*s\n",
|
||||
Blinelen(&imap->bin), p);
|
||||
}else if((q = strchr(p, '"')) && (r = strchr(q+1, '"'))){
|
||||
*r = '\0';
|
||||
q++;
|
||||
n = r-q;
|
||||
if(imap->data == nil || n >= imap->size)
|
||||
imapgrow(imap, n);
|
||||
memmove(imap->data, q, n);
|
||||
imap->data[n] = '\0';
|
||||
imap->data += n;
|
||||
imap->size -= n;
|
||||
}else
|
||||
return "confused about FETCH response";
|
||||
break;
|
||||
}
|
||||
|
||||
// * 1 FETCH (UID 1 RFC822.SIZE 511)
|
||||
if(q=strstr(p, "RFC822.SIZE")){
|
||||
imap->size = atoi(q+11);
|
||||
break;
|
||||
}
|
||||
|
||||
// * 1 FETCH (UID 1 RFC822.HEADER {496}
|
||||
// <496 bytes of data>
|
||||
// )
|
||||
// * 1 FETCH (UID 1 RFC822.HEADER "data")
|
||||
if(strstr(p, "RFC822.HEADER") || strstr(p, "RFC822.TEXT")){
|
||||
if((q = strchr(p, '{'))
|
||||
&& (n=strtol(q+1, &en, 0), *en=='}')){
|
||||
if(imap->data == nil || n >= imap->size)
|
||||
imapgrow(imap, n);
|
||||
if((i = Bread(&imap->bin, imap->data, n)) != n){
|
||||
snprint(error, sizeof error,
|
||||
"short read %d != %d: %r\n",
|
||||
i, n);
|
||||
return error;
|
||||
}
|
||||
if(imap->debug)
|
||||
fprint(2, "<- read %d bytes\n", n);
|
||||
imap->data[n] = '\0';
|
||||
if(imap->debug)
|
||||
fprint(2, "<- %s\n", imap->data);
|
||||
imap->data += n;
|
||||
imap->size -= n;
|
||||
p = Brdline(&imap->bin, '\n');
|
||||
if(imap->debug)
|
||||
fprint(2, "<- ignoring %.*s\n",
|
||||
Blinelen(&imap->bin), p);
|
||||
}else if((q = strchr(p, '"')) && (r = strchr(q+1, '"'))){
|
||||
*r = '\0';
|
||||
q++;
|
||||
n = r-q;
|
||||
if(imap->data == nil || n >= imap->size)
|
||||
imapgrow(imap, n);
|
||||
memmove(imap->data, q, n);
|
||||
imap->data[n] = '\0';
|
||||
imap->data += n;
|
||||
imap->size -= n;
|
||||
}else
|
||||
return "confused about FETCH response";
|
||||
break;
|
||||
}
|
||||
|
||||
// * 1 FETCH (UID 1)
|
||||
// * 2 FETCH (UID 6)
|
||||
if(q = strstr(p, "UID")){
|
||||
if(imap->nuid < imap->muid)
|
||||
imap->uid[imap->nuid++] = ((vlong)imap->validity<<32)|strtoul(q+3, nil, 10);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(imap->tag == 0)
|
||||
return line;
|
||||
break;
|
||||
|
||||
case '9': // response to our message
|
||||
op = p;
|
||||
if(p[1]=='X' && strtoul(p+2, &p, 10)==imap->tag){
|
||||
while(*p==' ')
|
||||
p++;
|
||||
imap->tag++;
|
||||
return p;
|
||||
}
|
||||
fprint(2, "expected %lud; got %s\n", imap->tag, op);
|
||||
break;
|
||||
|
||||
default:
|
||||
if(imap->debug || *p)
|
||||
fprint(2, "unexpected line: %s\n", p);
|
||||
}
|
||||
}
|
||||
snprint(error, sizeof error, "i/o error: %r\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
static int
|
||||
isokay(char *resp)
|
||||
{
|
||||
return strncmp(resp, "OK", 2)==0;
|
||||
}
|
||||
|
||||
//
|
||||
// log in to IMAP4 server, select mailbox, no SSL at the moment
|
||||
//
|
||||
static char*
|
||||
imap4login(Imap *imap)
|
||||
{
|
||||
char *s;
|
||||
UserPasswd *up;
|
||||
|
||||
imap->tag = 0;
|
||||
s = imap4resp(imap);
|
||||
if(!isokay(s))
|
||||
return "error in initial IMAP handshake";
|
||||
|
||||
if(imap->user != nil)
|
||||
up = auth_getuserpasswd(auth_getkey, "proto=pass service=imap server=%q user=%q", imap->host, imap->user);
|
||||
else
|
||||
up = auth_getuserpasswd(auth_getkey, "proto=pass service=imap server=%q", imap->host);
|
||||
if(up == nil)
|
||||
return "cannot find IMAP password";
|
||||
|
||||
imap->tag = 1;
|
||||
imap4cmd(imap, "LOGIN %Z %Z", up->user, up->passwd);
|
||||
free(up);
|
||||
if(!isokay(s = imap4resp(imap)))
|
||||
return s;
|
||||
|
||||
imap4cmd(imap, "SELECT %Z", imap->mbox);
|
||||
if(!isokay(s = imap4resp(imap)))
|
||||
return s;
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
//
|
||||
// push tls onto a connection
|
||||
//
|
||||
int
|
||||
mypushtls(int fd)
|
||||
{
|
||||
int p[2];
|
||||
char buf[10];
|
||||
|
||||
if(pipe(p) < 0)
|
||||
return -1;
|
||||
|
||||
switch(fork()){
|
||||
case -1:
|
||||
close(p[0]);
|
||||
close(p[1]);
|
||||
return -1;
|
||||
case 0:
|
||||
close(p[1]);
|
||||
dup(p[0], 0);
|
||||
dup(p[0], 1);
|
||||
sprint(buf, "/fd/%d", fd);
|
||||
execl("/bin/tlsrelay", "tlsrelay", "-f", buf, nil);
|
||||
_exits(nil);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
close(fd);
|
||||
close(p[0]);
|
||||
return p[1];
|
||||
}
|
||||
|
||||
//
|
||||
// dial and handshake with the imap server
|
||||
//
|
||||
static char*
|
||||
imap4dial(Imap *imap)
|
||||
{
|
||||
char *err, *port;
|
||||
uchar digest[SHA1dlen];
|
||||
int sfd;
|
||||
TLSconn conn;
|
||||
|
||||
if(imap->fd >= 0){
|
||||
imap4cmd(imap, "noop");
|
||||
if(isokay(imap4resp(imap)))
|
||||
return nil;
|
||||
close(imap->fd);
|
||||
imap->fd = -1;
|
||||
}
|
||||
|
||||
if(imap->mustssl)
|
||||
port = "imaps";
|
||||
else
|
||||
port = "imap4";
|
||||
|
||||
if((imap->fd = dial(netmkaddr(imap->host, "net", port), 0, 0, 0)) < 0)
|
||||
return geterrstr();
|
||||
|
||||
if(imap->mustssl){
|
||||
memset(&conn, 0, sizeof conn);
|
||||
sfd = tlsClient(imap->fd, &conn);
|
||||
if(sfd < 0)
|
||||
sysfatal("tlsClient: %r");
|
||||
if(conn.cert==nil || conn.certlen <= 0)
|
||||
sysfatal("server did not provide TLS certificate");
|
||||
sha1(conn.cert, conn.certlen, digest, nil);
|
||||
if(!imap->thumb || !okThumbprint(digest, imap->thumb)){
|
||||
fmtinstall('H', encodefmt);
|
||||
sysfatal("server certificate %.*H not recognized", SHA1dlen, digest);
|
||||
}
|
||||
free(conn.cert);
|
||||
close(imap->fd);
|
||||
imap->fd = sfd;
|
||||
|
||||
if(imap->debug){
|
||||
char fn[128];
|
||||
int fd;
|
||||
|
||||
snprint(fn, sizeof fn, "%s/ctl", conn.dir);
|
||||
fd = open(fn, ORDWR);
|
||||
if(fd < 0)
|
||||
fprint(2, "opening ctl: %r\n");
|
||||
if(fprint(fd, "debug") < 0)
|
||||
fprint(2, "writing ctl: %r\n");
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
Binit(&imap->bin, imap->fd, OREAD);
|
||||
Binit(&imap->bout, imap->fd, OWRITE);
|
||||
|
||||
if(err = imap4login(imap)) {
|
||||
close(imap->fd);
|
||||
return err;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
//
|
||||
// close connection
|
||||
//
|
||||
#if 0 /* jpc */
|
||||
static void
|
||||
imap4hangup(Imap *imap)
|
||||
{
|
||||
imap4cmd(imap, "LOGOUT");
|
||||
imap4resp(imap);
|
||||
close(imap->fd);
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// download a single message
|
||||
//
|
||||
static char*
|
||||
imap4fetch(Mailbox *mb, Message *m)
|
||||
{
|
||||
int i;
|
||||
char *p, *s, sdigest[2*SHA1dlen+1];
|
||||
Imap *imap;
|
||||
|
||||
imap = mb->aux;
|
||||
|
||||
imap->size = 0;
|
||||
|
||||
if(!isokay(s = imap4resp(imap)))
|
||||
return s;
|
||||
|
||||
p = imap->base;
|
||||
if(p == nil)
|
||||
return "did not get message body";
|
||||
|
||||
removecr(p);
|
||||
free(m->start);
|
||||
m->start = p;
|
||||
m->end = p+strlen(p);
|
||||
m->bend = m->rbend = m->end;
|
||||
m->header = m->start;
|
||||
|
||||
imap->base = nil;
|
||||
imap->data = nil;
|
||||
|
||||
parse(m, 0, mb, 1);
|
||||
|
||||
// digest headers
|
||||
sha1((uchar*)m->start, m->end - m->start, m->digest, nil);
|
||||
for(i = 0; i < SHA1dlen; i++)
|
||||
sprint(sdigest+2*i, "%2.2ux", m->digest[i]);
|
||||
m->sdigest = s_copy(sdigest);
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
//
|
||||
// check for new messages on imap4 server
|
||||
// download new messages, mark deleted messages
|
||||
//
|
||||
static char*
|
||||
imap4read(Imap *imap, Mailbox *mb, int doplumb)
|
||||
{
|
||||
char *s;
|
||||
int i, ignore, nnew, t;
|
||||
Message *m, *next, **l;
|
||||
|
||||
imap4cmd(imap, "STATUS %Z (MESSAGES UIDVALIDITY)", imap->mbox);
|
||||
if(!isokay(s = imap4resp(imap)))
|
||||
return s;
|
||||
|
||||
imap->nuid = 0;
|
||||
imap->uid = erealloc(imap->uid, imap->nmsg*sizeof(imap->uid[0]));
|
||||
imap->muid = imap->nmsg;
|
||||
|
||||
if(imap->nmsg > 0){
|
||||
imap4cmd(imap, "UID FETCH 1:* UID");
|
||||
if(!isokay(s = imap4resp(imap)))
|
||||
return s;
|
||||
}
|
||||
|
||||
l = &mb->root->part;
|
||||
for(i=0; i<imap->nuid; i++){
|
||||
ignore = 0;
|
||||
while(*l != nil){
|
||||
if((*l)->imapuid == imap->uid[i]){
|
||||
ignore = 1;
|
||||
l = &(*l)->next;
|
||||
break;
|
||||
}else{
|
||||
// old mail, we don't have it anymore
|
||||
if(doplumb)
|
||||
mailplumb(mb, *l, 1);
|
||||
(*l)->inmbox = 0;
|
||||
(*l)->deleted = 1;
|
||||
l = &(*l)->next;
|
||||
}
|
||||
}
|
||||
if(ignore)
|
||||
continue;
|
||||
|
||||
// new message
|
||||
m = newmessage(mb->root);
|
||||
m->mallocd = 1;
|
||||
m->inmbox = 1;
|
||||
m->imapuid = imap->uid[i];
|
||||
|
||||
// add to chain, will download soon
|
||||
*l = m;
|
||||
l = &m->next;
|
||||
}
|
||||
|
||||
// whatever is left at the end of the chain is gone
|
||||
while(*l != nil){
|
||||
if(doplumb)
|
||||
mailplumb(mb, *l, 1);
|
||||
(*l)->inmbox = 0;
|
||||
(*l)->deleted = 1;
|
||||
l = &(*l)->next;
|
||||
}
|
||||
|
||||
// download new messages
|
||||
t = imap->tag;
|
||||
if(pipeline)
|
||||
switch(rfork(RFPROC|RFMEM)){
|
||||
case -1:
|
||||
sysfatal("rfork: %r");
|
||||
default:
|
||||
break;
|
||||
case 0:
|
||||
for(m = mb->root->part; m != nil; m = m->next){
|
||||
if(m->start != nil)
|
||||
continue;
|
||||
if(imap->debug)
|
||||
fprint(2, "9X%d UID FETCH %lud (UID RFC822.SIZE BODY[])\r\n",
|
||||
t, (ulong)m->imapuid);
|
||||
Bprint(&imap->bout, "9X%d UID FETCH %lud (UID RFC822.SIZE BODY[])\r\n",
|
||||
t++, (ulong)m->imapuid);
|
||||
}
|
||||
Bflush(&imap->bout);
|
||||
_exits(nil);
|
||||
}
|
||||
|
||||
nnew = 0;
|
||||
for(m=mb->root->part; m!=nil; m=next){
|
||||
next = m->next;
|
||||
if(m->start != nil)
|
||||
continue;
|
||||
|
||||
if(!pipeline){
|
||||
Bprint(&imap->bout, "9X%lud UID FETCH %lud (UID RFC822.SIZE BODY[])\r\n",
|
||||
(ulong)imap->tag, (ulong)m->imapuid);
|
||||
Bflush(&imap->bout);
|
||||
}
|
||||
|
||||
if(s = imap4fetch(mb, m)){
|
||||
// message disappeared? unchain
|
||||
fprint(2, "download %lud: %s\n", (ulong)m->imapuid, s);
|
||||
delmessage(mb, m);
|
||||
mb->root->subname--;
|
||||
continue;
|
||||
}
|
||||
nnew++;
|
||||
if(doplumb)
|
||||
mailplumb(mb, m, 0);
|
||||
}
|
||||
if(pipeline)
|
||||
waitpid();
|
||||
|
||||
if(nnew || mb->vers == 0){
|
||||
mb->vers++;
|
||||
henter(PATH(0, Qtop), mb->name,
|
||||
(Qid){PATH(mb->id, Qmbox), mb->vers, QTDIR}, nil, mb);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
//
|
||||
// sync mailbox
|
||||
//
|
||||
static void
|
||||
imap4purge(Imap *imap, Mailbox *mb)
|
||||
{
|
||||
int ndel;
|
||||
Message *m, *next;
|
||||
|
||||
ndel = 0;
|
||||
for(m=mb->root->part; m!=nil; m=next){
|
||||
next = m->next;
|
||||
if(m->deleted && m->refs==0){
|
||||
if(m->inmbox && (ulong)(m->imapuid>>32)==imap->validity){
|
||||
imap4cmd(imap, "UID STORE %lud +FLAGS (\\Deleted)", (ulong)m->imapuid);
|
||||
if(isokay(imap4resp(imap))){
|
||||
ndel++;
|
||||
delmessage(mb, m);
|
||||
}
|
||||
}else
|
||||
delmessage(mb, m);
|
||||
}
|
||||
}
|
||||
|
||||
if(ndel){
|
||||
imap4cmd(imap, "EXPUNGE");
|
||||
imap4resp(imap);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// connect to imap4 server, sync mailbox
|
||||
//
|
||||
static char*
|
||||
imap4sync(Mailbox *mb, int doplumb)
|
||||
{
|
||||
char *err;
|
||||
Imap *imap;
|
||||
|
||||
imap = mb->aux;
|
||||
|
||||
if(err = imap4dial(imap)){
|
||||
mb->waketime = time(0) + imap->refreshtime;
|
||||
return err;
|
||||
}
|
||||
|
||||
if((err = imap4read(imap, mb, doplumb)) == nil){
|
||||
imap4purge(imap, mb);
|
||||
mb->d->atime = mb->d->mtime = time(0);
|
||||
}
|
||||
/*
|
||||
* don't hang up; leave connection open for next time.
|
||||
*/
|
||||
// imap4hangup(imap);
|
||||
mb->waketime = time(0) + imap->refreshtime;
|
||||
return err;
|
||||
}
|
||||
|
||||
static char Eimap4ctl[] = "bad imap4 control message";
|
||||
|
||||
static char*
|
||||
imap4ctl(Mailbox *mb, int argc, char **argv)
|
||||
{
|
||||
int n;
|
||||
Imap *imap;
|
||||
|
||||
imap = mb->aux;
|
||||
if(argc < 1)
|
||||
return Eimap4ctl;
|
||||
|
||||
if(argc==1 && strcmp(argv[0], "debug")==0){
|
||||
imap->debug = 1;
|
||||
return nil;
|
||||
}
|
||||
|
||||
if(argc==1 && strcmp(argv[0], "nodebug")==0){
|
||||
imap->debug = 0;
|
||||
return nil;
|
||||
}
|
||||
|
||||
if(argc==1 && strcmp(argv[0], "thumbprint")==0){
|
||||
if(imap->thumb)
|
||||
freeThumbprints(imap->thumb);
|
||||
imap->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude");
|
||||
}
|
||||
if(strcmp(argv[0], "refresh")==0){
|
||||
if(argc==1){
|
||||
imap->refreshtime = 60;
|
||||
return nil;
|
||||
}
|
||||
if(argc==2){
|
||||
n = atoi(argv[1]);
|
||||
if(n < 15)
|
||||
return Eimap4ctl;
|
||||
imap->refreshtime = n;
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
return Eimap4ctl;
|
||||
}
|
||||
|
||||
//
|
||||
// free extra memory associated with mb
|
||||
//
|
||||
static void
|
||||
imap4close(Mailbox *mb)
|
||||
{
|
||||
Imap *imap;
|
||||
|
||||
imap = mb->aux;
|
||||
free(imap->freep);
|
||||
free(imap->base);
|
||||
free(imap->uid);
|
||||
if(imap->fd >= 0)
|
||||
close(imap->fd);
|
||||
free(imap);
|
||||
}
|
||||
|
||||
//
|
||||
// open mailboxes of the form /imap/host/user
|
||||
//
|
||||
char*
|
||||
imap4mbox(Mailbox *mb, char *path)
|
||||
{
|
||||
char *f[10];
|
||||
int mustssl, nf;
|
||||
Imap *imap;
|
||||
|
||||
quotefmtinstall();
|
||||
fmtinstall('Z', doublequote);
|
||||
if(strncmp(path, "/imap/", 6) != 0 && strncmp(path, "/imaps/", 7) != 0)
|
||||
return Enotme;
|
||||
mustssl = (strncmp(path, "/imaps/", 7) == 0);
|
||||
|
||||
path = strdup(path);
|
||||
if(path == nil)
|
||||
return "out of memory";
|
||||
|
||||
nf = getfields(path, f, 5, 0, "/");
|
||||
if(nf < 3){
|
||||
free(path);
|
||||
return "bad imap path syntax /imap[s]/system[/user[/mailbox]]";
|
||||
}
|
||||
|
||||
imap = emalloc(sizeof(*imap));
|
||||
imap->fd = -1;
|
||||
imap->debug = debug;
|
||||
imap->freep = path;
|
||||
imap->mustssl = mustssl;
|
||||
imap->host = f[2];
|
||||
if(nf < 4)
|
||||
imap->user = nil;
|
||||
else
|
||||
imap->user = f[3];
|
||||
if(nf < 5)
|
||||
imap->mbox = "Inbox";
|
||||
else
|
||||
imap->mbox = f[4];
|
||||
imap->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude");
|
||||
|
||||
mb->aux = imap;
|
||||
mb->sync = imap4sync;
|
||||
mb->close = imap4close;
|
||||
mb->ctl = imap4ctl;
|
||||
mb->d = emalloc(sizeof(*mb->d));
|
||||
//mb->fetch = imap4fetch;
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
//
|
||||
// Formatter for %"
|
||||
// Use double quotes to protect white space, frogs, \ and "
|
||||
//
|
||||
enum
|
||||
{
|
||||
Qok = 0,
|
||||
Qquote,
|
||||
Qbackslash,
|
||||
};
|
||||
|
||||
static int
|
||||
needtoquote(Rune r)
|
||||
{
|
||||
if(r >= Runeself)
|
||||
return Qquote;
|
||||
if(r <= ' ')
|
||||
return Qquote;
|
||||
if(r=='\\' || r=='"')
|
||||
return Qbackslash;
|
||||
return Qok;
|
||||
}
|
||||
|
||||
int
|
||||
doublequote(Fmt *f)
|
||||
{
|
||||
char *s, *t;
|
||||
int w, quotes;
|
||||
Rune r;
|
||||
|
||||
s = va_arg(f->args, char*);
|
||||
if(s == nil || *s == '\0')
|
||||
return fmtstrcpy(f, "\"\"");
|
||||
|
||||
quotes = 0;
|
||||
for(t=s; *t; t+=w){
|
||||
w = chartorune(&r, t);
|
||||
quotes |= needtoquote(r);
|
||||
}
|
||||
if(quotes == 0)
|
||||
return fmtstrcpy(f, s);
|
||||
|
||||
fmtrune(f, '"');
|
||||
for(t=s; *t; t+=w){
|
||||
w = chartorune(&r, t);
|
||||
if(needtoquote(r) == Qbackslash)
|
||||
fmtrune(f, '\\');
|
||||
fmtrune(f, r);
|
||||
}
|
||||
return fmtrune(f, '"');
|
||||
}
|
||||
1601
src/cmd/upas/fs/mbox.c
Normal file
1601
src/cmd/upas/fs/mbox.c
Normal file
File diff suppressed because it is too large
Load diff
29
src/cmd/upas/fs/mkfile
Normal file
29
src/cmd/upas/fs/mkfile
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<$PLAN9/src/mkhdr
|
||||
|
||||
TARG= fs\
|
||||
|
||||
OFILES=\
|
||||
fs.$O\
|
||||
imap4.$O\
|
||||
mbox.$O\
|
||||
plan9.$O\
|
||||
pop3.$O\
|
||||
strtotm.$O\
|
||||
|
||||
LIB=../common/libcommon.a\
|
||||
# /usr/local/plan9/lib/libthread.a
|
||||
|
||||
HFILES= ../common/common.h\
|
||||
dat.h
|
||||
|
||||
BIN=$PLAN9/bin/upas
|
||||
|
||||
UPDATE=\
|
||||
mkfile\
|
||||
$HFILES\
|
||||
${TARG:%=%.c}\
|
||||
${OFILES:%.$O=%.c}\
|
||||
|
||||
<$PLAN9/src/mkone
|
||||
CFLAGS=$CFLAGS -I../common
|
||||
# CFLAGS=$CFLAGS -I/sys/include -I../common
|
||||
27
src/cmd/upas/fs/mkfile.9
Normal file
27
src/cmd/upas/fs/mkfile.9
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
</$objtype/mkfile
|
||||
|
||||
TARG= fs\
|
||||
|
||||
OFILES=\
|
||||
fs.$O\
|
||||
imap4.$O\
|
||||
mbox.$O\
|
||||
plan9.$O\
|
||||
pop3.$O\
|
||||
strtotm.$O\
|
||||
|
||||
LIB=../common/libcommon.a$O\
|
||||
|
||||
HFILES= ../common/common.h\
|
||||
dat.h
|
||||
|
||||
BIN=/$objtype/bin/upas
|
||||
|
||||
UPDATE=\
|
||||
mkfile\
|
||||
$HFILES\
|
||||
${TARG:%=%.c}\
|
||||
${OFILES:%.$O=%.c}\
|
||||
|
||||
</sys/src/cmd/mkone
|
||||
CFLAGS=$CFLAGS -I/sys/include -I../common
|
||||
405
src/cmd/upas/fs/plan9.c
Normal file
405
src/cmd/upas/fs/plan9.c
Normal file
|
|
@ -0,0 +1,405 @@
|
|||
#include "common.h"
|
||||
#include <ctype.h>
|
||||
#include <plumb.h>
|
||||
#include <libsec.h>
|
||||
#include "dat.h"
|
||||
|
||||
enum {
|
||||
Buffersize = 64*1024,
|
||||
};
|
||||
|
||||
typedef struct Inbuf Inbuf;
|
||||
struct Inbuf
|
||||
{
|
||||
int fd;
|
||||
uchar *lim;
|
||||
uchar *rptr;
|
||||
uchar *wptr;
|
||||
uchar data[Buffersize+7];
|
||||
};
|
||||
|
||||
static void
|
||||
addtomessage(Message *m, uchar *p, int n, int done)
|
||||
{
|
||||
int i, len;
|
||||
|
||||
// add to message (+ 1 in malloc is for a trailing null)
|
||||
if(m->lim - m->end < n){
|
||||
if(m->start != nil){
|
||||
i = m->end-m->start;
|
||||
if(done)
|
||||
len = i + n;
|
||||
else
|
||||
len = (4*(i+n))/3;
|
||||
m->start = erealloc(m->start, len + 1);
|
||||
m->end = m->start + i;
|
||||
} else {
|
||||
if(done)
|
||||
len = n;
|
||||
else
|
||||
len = 2*n;
|
||||
m->start = emalloc(len + 1);
|
||||
m->end = m->start;
|
||||
}
|
||||
m->lim = m->start + len;
|
||||
}
|
||||
|
||||
memmove(m->end, p, n);
|
||||
m->end += n;
|
||||
}
|
||||
|
||||
//
|
||||
// read in a single message
|
||||
//
|
||||
static int
|
||||
readmessage(Message *m, Inbuf *inb)
|
||||
{
|
||||
int i, n, done;
|
||||
uchar *p, *np;
|
||||
char sdigest[SHA1dlen*2+1];
|
||||
char tmp[64];
|
||||
|
||||
for(done = 0; !done;){
|
||||
n = inb->wptr - inb->rptr;
|
||||
if(n < 6){
|
||||
if(n)
|
||||
memmove(inb->data, inb->rptr, n);
|
||||
inb->rptr = inb->data;
|
||||
inb->wptr = inb->rptr + n;
|
||||
i = read(inb->fd, inb->wptr, Buffersize);
|
||||
if(i < 0){
|
||||
/* if(fd2path(inb->fd, tmp, sizeof tmp) < 0)
|
||||
strcpy(tmp, "unknown mailbox"); jpc */
|
||||
fprint(2, "error reading '%s': %r\n", tmp);
|
||||
return -1;
|
||||
}
|
||||
if(i == 0){
|
||||
if(n != 0)
|
||||
addtomessage(m, inb->rptr, n, 1);
|
||||
if(m->end == m->start)
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
inb->wptr += i;
|
||||
}
|
||||
|
||||
// look for end of message
|
||||
for(p = inb->rptr; p < inb->wptr; p = np+1){
|
||||
// first part of search for '\nFrom '
|
||||
np = memchr(p, '\n', inb->wptr - p);
|
||||
if(np == nil){
|
||||
p = inb->wptr;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* if we've found a \n but there's
|
||||
* not enough room for '\nFrom ', don't do
|
||||
* the comparison till we've read in more.
|
||||
*/
|
||||
if(inb->wptr - np < 6){
|
||||
p = np;
|
||||
break;
|
||||
}
|
||||
|
||||
if(strncmp((char*)np, "\nFrom ", 6) == 0){
|
||||
done = 1;
|
||||
p = np+1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// add to message (+ 1 in malloc is for a trailing null)
|
||||
n = p - inb->rptr;
|
||||
addtomessage(m, inb->rptr, n, done);
|
||||
inb->rptr += n;
|
||||
}
|
||||
|
||||
// if it doesn't start with a 'From ', this ain't a mailbox
|
||||
if(strncmp(m->start, "From ", 5) != 0)
|
||||
return -1;
|
||||
|
||||
// dump trailing newline, make sure there's a trailing null
|
||||
// (helps in body searches)
|
||||
if(*(m->end-1) == '\n')
|
||||
m->end--;
|
||||
*m->end = 0;
|
||||
m->bend = m->rbend = m->end;
|
||||
|
||||
// digest message
|
||||
sha1((uchar*)m->start, m->end - m->start, m->digest, nil);
|
||||
for(i = 0; i < SHA1dlen; i++)
|
||||
sprint(sdigest+2*i, "%2.2ux", m->digest[i]);
|
||||
m->sdigest = s_copy(sdigest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// throw out deleted messages. return number of freshly deleted messages
|
||||
int
|
||||
purgedeleted(Mailbox *mb)
|
||||
{
|
||||
Message *m, *next;
|
||||
int newdels;
|
||||
|
||||
// forget about what's no longer in the mailbox
|
||||
newdels = 0;
|
||||
for(m = mb->root->part; m != nil; m = next){
|
||||
next = m->next;
|
||||
if(m->deleted && m->refs == 0){
|
||||
if(m->inmbox)
|
||||
newdels++;
|
||||
delmessage(mb, m);
|
||||
}
|
||||
}
|
||||
return newdels;
|
||||
}
|
||||
|
||||
//
|
||||
// read in the mailbox and parse into messages.
|
||||
//
|
||||
static char*
|
||||
_readmbox(Mailbox *mb, int doplumb, Mlock *lk)
|
||||
{
|
||||
int fd;
|
||||
String *tmp;
|
||||
Dir *d;
|
||||
static char err[128];
|
||||
Message *m, **l;
|
||||
Inbuf *inb;
|
||||
char *x;
|
||||
|
||||
l = &mb->root->part;
|
||||
|
||||
/*
|
||||
* open the mailbox. If it doesn't exist, try the temporary one.
|
||||
*/
|
||||
retry:
|
||||
fd = open(mb->path, OREAD);
|
||||
if(fd < 0){
|
||||
errstr(err, sizeof(err));
|
||||
if(strstr(err, "exist") != 0){
|
||||
tmp = s_copy(mb->path);
|
||||
s_append(tmp, ".tmp");
|
||||
if(sysrename(s_to_c(tmp), mb->path) == 0){
|
||||
s_free(tmp);
|
||||
goto retry;
|
||||
}
|
||||
s_free(tmp);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* a new qid.path means reread the mailbox, while
|
||||
* a new qid.vers means read any new messages
|
||||
*/
|
||||
d = dirfstat(fd);
|
||||
if(d == nil){
|
||||
close(fd);
|
||||
errstr(err, sizeof(err));
|
||||
return err;
|
||||
}
|
||||
if(mb->d != nil){
|
||||
if(d->qid.path == mb->d->qid.path && d->qid.vers == mb->d->qid.vers){
|
||||
close(fd);
|
||||
free(d);
|
||||
return nil;
|
||||
}
|
||||
if(d->qid.path == mb->d->qid.path){
|
||||
while(*l != nil)
|
||||
l = &(*l)->next;
|
||||
seek(fd, mb->d->length, 0);
|
||||
}
|
||||
free(mb->d);
|
||||
}
|
||||
mb->d = d;
|
||||
mb->vers++;
|
||||
henter(PATH(0, Qtop), mb->name,
|
||||
(Qid){PATH(mb->id, Qmbox), mb->vers, QTDIR}, nil, mb);
|
||||
|
||||
inb = emalloc(sizeof(Inbuf));
|
||||
inb->rptr = inb->wptr = inb->data;
|
||||
inb->fd = fd;
|
||||
|
||||
// read new messages
|
||||
snprint(err, sizeof err, "reading '%s'", mb->path);
|
||||
logmsg(err, nil);
|
||||
for(;;){
|
||||
if(lk != nil)
|
||||
syslockrefresh(lk);
|
||||
m = newmessage(mb->root);
|
||||
m->mallocd = 1;
|
||||
m->inmbox = 1;
|
||||
if(readmessage(m, inb) < 0){
|
||||
delmessage(mb, m);
|
||||
mb->root->subname--;
|
||||
break;
|
||||
}
|
||||
|
||||
// merge mailbox versions
|
||||
while(*l != nil){
|
||||
if(memcmp((*l)->digest, m->digest, SHA1dlen) == 0){
|
||||
// matches mail we already read, discard
|
||||
logmsg("duplicate", *l);
|
||||
delmessage(mb, m);
|
||||
mb->root->subname--;
|
||||
m = nil;
|
||||
l = &(*l)->next;
|
||||
break;
|
||||
} else {
|
||||
// old mail no longer in box, mark deleted
|
||||
logmsg("disappeared", *l);
|
||||
if(doplumb)
|
||||
mailplumb(mb, *l, 1);
|
||||
(*l)->inmbox = 0;
|
||||
(*l)->deleted = 1;
|
||||
l = &(*l)->next;
|
||||
}
|
||||
}
|
||||
if(m == nil)
|
||||
continue;
|
||||
|
||||
x = strchr(m->start, '\n');
|
||||
if(x == nil)
|
||||
m->header = m->end;
|
||||
else
|
||||
m->header = x + 1;
|
||||
m->mheader = m->mhend = m->header;
|
||||
parseunix(m);
|
||||
parse(m, 0, mb, 0);
|
||||
logmsg("new", m);
|
||||
|
||||
/* chain in */
|
||||
*l = m;
|
||||
l = &m->next;
|
||||
if(doplumb)
|
||||
mailplumb(mb, m, 0);
|
||||
|
||||
}
|
||||
logmsg("mbox read", nil);
|
||||
|
||||
// whatever is left has been removed from the mbox, mark deleted
|
||||
while(*l != nil){
|
||||
if(doplumb)
|
||||
mailplumb(mb, *l, 1);
|
||||
(*l)->inmbox = 0;
|
||||
(*l)->deleted = 1;
|
||||
l = &(*l)->next;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
free(inb);
|
||||
return nil;
|
||||
}
|
||||
|
||||
static void
|
||||
_writembox(Mailbox *mb, Mlock *lk)
|
||||
{
|
||||
Dir *d;
|
||||
Message *m;
|
||||
String *tmp;
|
||||
int mode, errs;
|
||||
Biobuf *b;
|
||||
|
||||
tmp = s_copy(mb->path);
|
||||
s_append(tmp, ".tmp");
|
||||
|
||||
/*
|
||||
* preserve old files permissions, if possible
|
||||
*/
|
||||
d = dirstat(mb->path);
|
||||
if(d != nil){
|
||||
mode = d->mode&0777;
|
||||
free(d);
|
||||
} else
|
||||
mode = MBOXMODE;
|
||||
|
||||
sysremove(s_to_c(tmp));
|
||||
b = sysopen(s_to_c(tmp), "alc", mode);
|
||||
if(b == 0){
|
||||
fprint(2, "can't write temporary mailbox %s: %r\n", s_to_c(tmp));
|
||||
return;
|
||||
}
|
||||
|
||||
logmsg("writing new mbox", nil);
|
||||
errs = 0;
|
||||
for(m = mb->root->part; m != nil; m = m->next){
|
||||
if(lk != nil)
|
||||
syslockrefresh(lk);
|
||||
if(m->deleted)
|
||||
continue;
|
||||
logmsg("writing", m);
|
||||
if(Bwrite(b, m->start, m->end - m->start) < 0)
|
||||
errs = 1;
|
||||
if(Bwrite(b, "\n", 1) < 0)
|
||||
errs = 1;
|
||||
}
|
||||
logmsg("wrote new mbox", nil);
|
||||
|
||||
if(sysclose(b) < 0)
|
||||
errs = 1;
|
||||
|
||||
if(errs){
|
||||
fprint(2, "error writing temporary mail file\n");
|
||||
s_free(tmp);
|
||||
return;
|
||||
}
|
||||
|
||||
sysremove(mb->path);
|
||||
if(sysrename(s_to_c(tmp), mb->path) < 0)
|
||||
fprint(2, "%s: can't rename %s to %s: %r\n", argv0,
|
||||
s_to_c(tmp), mb->path);
|
||||
s_free(tmp);
|
||||
if(mb->d != nil)
|
||||
free(mb->d);
|
||||
mb->d = dirstat(mb->path);
|
||||
}
|
||||
|
||||
char*
|
||||
plan9syncmbox(Mailbox *mb, int doplumb)
|
||||
{
|
||||
Mlock *lk;
|
||||
char *rv;
|
||||
|
||||
lk = nil;
|
||||
if(mb->dolock){
|
||||
lk = syslock(mb->path);
|
||||
if(lk == nil)
|
||||
return "can't lock mailbox";
|
||||
}
|
||||
|
||||
rv = _readmbox(mb, doplumb, lk); /* interpolate */
|
||||
if(purgedeleted(mb) > 0)
|
||||
_writembox(mb, lk);
|
||||
|
||||
if(lk != nil)
|
||||
sysunlock(lk);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
//
|
||||
// look to see if we can open this mail box
|
||||
//
|
||||
char*
|
||||
plan9mbox(Mailbox *mb, char *path)
|
||||
{
|
||||
static char err[64];
|
||||
String *tmp;
|
||||
|
||||
if(access(path, AEXIST) < 0){
|
||||
errstr(err, sizeof(err));
|
||||
tmp = s_copy(path);
|
||||
s_append(tmp, ".tmp");
|
||||
if(access(s_to_c(tmp), AEXIST) < 0){
|
||||
s_free(tmp);
|
||||
return err;
|
||||
}
|
||||
s_free(tmp);
|
||||
}
|
||||
|
||||
mb->sync = plan9syncmbox;
|
||||
return nil;
|
||||
}
|
||||
700
src/cmd/upas/fs/pop3.c
Normal file
700
src/cmd/upas/fs/pop3.c
Normal file
|
|
@ -0,0 +1,700 @@
|
|||
#include "common.h"
|
||||
#include <ctype.h>
|
||||
#include <plumb.h>
|
||||
#include <libsec.h>
|
||||
#include <auth.h>
|
||||
#include <thread.h>
|
||||
#include "dat.h"
|
||||
|
||||
#pragma varargck type "M" uchar*
|
||||
#pragma varargck argpos pop3cmd 2
|
||||
|
||||
typedef struct Pop Pop;
|
||||
struct Pop {
|
||||
char *freep; // free this to free the strings below
|
||||
|
||||
char *host;
|
||||
char *user;
|
||||
char *port;
|
||||
|
||||
int ppop;
|
||||
int refreshtime;
|
||||
int debug;
|
||||
int pipeline;
|
||||
int encrypted;
|
||||
int needtls;
|
||||
int notls;
|
||||
int needssl;
|
||||
|
||||
// open network connection
|
||||
Biobuf bin;
|
||||
Biobuf bout;
|
||||
int fd;
|
||||
char *lastline; // from Brdstr
|
||||
|
||||
Thumbprint *thumb;
|
||||
};
|
||||
|
||||
char*
|
||||
geterrstr(void)
|
||||
{
|
||||
static char err[64];
|
||||
|
||||
err[0] = '\0';
|
||||
errstr(err, sizeof(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
//
|
||||
// get pop3 response line , without worrying
|
||||
// about multiline responses; the clients
|
||||
// will deal with that.
|
||||
//
|
||||
static int
|
||||
isokay(char *s)
|
||||
{
|
||||
return s!=nil && strncmp(s, "+OK", 3)==0;
|
||||
}
|
||||
|
||||
static void
|
||||
pop3cmd(Pop *pop, char *fmt, ...)
|
||||
{
|
||||
char buf[128], *p;
|
||||
va_list va;
|
||||
|
||||
va_start(va, fmt);
|
||||
vseprint(buf, buf+sizeof(buf), fmt, va);
|
||||
va_end(va);
|
||||
|
||||
p = buf+strlen(buf);
|
||||
if(p > (buf+sizeof(buf)-3))
|
||||
sysfatal("pop3 command too long");
|
||||
|
||||
if(pop->debug)
|
||||
fprint(2, "<- %s\n", buf);
|
||||
strcpy(p, "\r\n");
|
||||
Bwrite(&pop->bout, buf, strlen(buf));
|
||||
Bflush(&pop->bout);
|
||||
}
|
||||
|
||||
static char*
|
||||
pop3resp(Pop *pop)
|
||||
{
|
||||
char *s;
|
||||
char *p;
|
||||
|
||||
alarm(60*1000);
|
||||
if((s = Brdstr(&pop->bin, '\n', 0)) == nil){
|
||||
close(pop->fd);
|
||||
pop->fd = -1;
|
||||
alarm(0);
|
||||
return "unexpected eof";
|
||||
}
|
||||
alarm(0);
|
||||
|
||||
p = s+strlen(s)-1;
|
||||
while(p >= s && (*p == '\r' || *p == '\n'))
|
||||
*p-- = '\0';
|
||||
|
||||
if(pop->debug)
|
||||
fprint(2, "-> %s\n", s);
|
||||
free(pop->lastline);
|
||||
pop->lastline = s;
|
||||
return s;
|
||||
}
|
||||
|
||||
#if 0 /* jpc */
|
||||
static int
|
||||
pop3log(char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap,fmt);
|
||||
syslog(0, "/sys/log/pop3", fmt, ap);
|
||||
va_end(ap);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static char*
|
||||
pop3pushtls(Pop *pop)
|
||||
{
|
||||
int fd;
|
||||
uchar digest[SHA1dlen];
|
||||
TLSconn conn;
|
||||
|
||||
memset(&conn, 0, sizeof conn);
|
||||
// conn.trace = pop3log;
|
||||
fd = tlsClient(pop->fd, &conn);
|
||||
if(fd < 0)
|
||||
return "tls error";
|
||||
if(conn.cert==nil || conn.certlen <= 0){
|
||||
close(fd);
|
||||
return "server did not provide TLS certificate";
|
||||
}
|
||||
sha1(conn.cert, conn.certlen, digest, nil);
|
||||
if(!pop->thumb || !okThumbprint(digest, pop->thumb)){
|
||||
fmtinstall('H', encodefmt);
|
||||
close(fd);
|
||||
free(conn.cert);
|
||||
fprint(2, "upas/fs pop3: server certificate %.*H not recognized\n", SHA1dlen, digest);
|
||||
return "bad server certificate";
|
||||
}
|
||||
free(conn.cert);
|
||||
close(pop->fd);
|
||||
pop->fd = fd;
|
||||
pop->encrypted = 1;
|
||||
Binit(&pop->bin, pop->fd, OREAD);
|
||||
Binit(&pop->bout, pop->fd, OWRITE);
|
||||
return nil;
|
||||
}
|
||||
|
||||
//
|
||||
// get capability list, possibly start tls
|
||||
//
|
||||
static char*
|
||||
pop3capa(Pop *pop)
|
||||
{
|
||||
char *s;
|
||||
int hastls;
|
||||
|
||||
pop3cmd(pop, "CAPA");
|
||||
if(!isokay(pop3resp(pop)))
|
||||
return nil;
|
||||
|
||||
hastls = 0;
|
||||
for(;;){
|
||||
s = pop3resp(pop);
|
||||
if(strcmp(s, ".") == 0 || strcmp(s, "unexpected eof") == 0)
|
||||
break;
|
||||
if(strcmp(s, "STLS") == 0)
|
||||
hastls = 1;
|
||||
if(strcmp(s, "PIPELINING") == 0)
|
||||
pop->pipeline = 1;
|
||||
}
|
||||
|
||||
if(hastls && !pop->notls){
|
||||
pop3cmd(pop, "STLS");
|
||||
if(!isokay(s = pop3resp(pop)))
|
||||
return s;
|
||||
if((s = pop3pushtls(pop)) != nil)
|
||||
return s;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
//
|
||||
// log in using APOP if possible, password if allowed by user
|
||||
//
|
||||
static char*
|
||||
pop3login(Pop *pop)
|
||||
{
|
||||
int n;
|
||||
char *s, *p, *q;
|
||||
char ubuf[128], user[128];
|
||||
char buf[500];
|
||||
UserPasswd *up;
|
||||
|
||||
s = pop3resp(pop);
|
||||
if(!isokay(s))
|
||||
return "error in initial handshake";
|
||||
|
||||
if(pop->user)
|
||||
snprint(ubuf, sizeof ubuf, " user=%q", pop->user);
|
||||
else
|
||||
ubuf[0] = '\0';
|
||||
|
||||
// look for apop banner
|
||||
if(pop->ppop==0 && (p = strchr(s, '<')) && (q = strchr(p+1, '>'))) {
|
||||
*++q = '\0';
|
||||
if((n=auth_respond(p, q-p, user, sizeof user, buf, sizeof buf, auth_getkey, "proto=apop role=client server=%q%s",
|
||||
pop->host, ubuf)) < 0)
|
||||
return "factotum failed";
|
||||
if(user[0]=='\0')
|
||||
return "factotum did not return a user name";
|
||||
|
||||
if(s = pop3capa(pop))
|
||||
return s;
|
||||
|
||||
pop3cmd(pop, "APOP %s %.*s", user, n, buf);
|
||||
if(!isokay(s = pop3resp(pop)))
|
||||
return s;
|
||||
|
||||
return nil;
|
||||
} else {
|
||||
if(pop->ppop == 0)
|
||||
return "no APOP hdr from server";
|
||||
|
||||
if(s = pop3capa(pop))
|
||||
return s;
|
||||
|
||||
if(pop->needtls && !pop->encrypted)
|
||||
return "could not negotiate TLS";
|
||||
|
||||
up = auth_getuserpasswd(auth_getkey, "role=client proto=pass service=pop dom=%q%s",
|
||||
pop->host, ubuf);
|
||||
/* up = auth_getuserpasswd(auth_getkey, "proto=pass service=pop dom=%q%s",
|
||||
pop->host, ubuf); jpc */
|
||||
if(up == nil)
|
||||
return "no usable keys found";
|
||||
|
||||
pop3cmd(pop, "USER %s", up->user);
|
||||
if(!isokay(s = pop3resp(pop))){
|
||||
free(up);
|
||||
return s;
|
||||
}
|
||||
pop3cmd(pop, "PASS %s", up->passwd);
|
||||
free(up);
|
||||
if(!isokay(s = pop3resp(pop)))
|
||||
return s;
|
||||
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// dial and handshake with pop server
|
||||
//
|
||||
static char*
|
||||
pop3dial(Pop *pop)
|
||||
{
|
||||
char *err;
|
||||
|
||||
if((pop->fd = dial(netmkaddr(pop->host, "net", pop->needssl ? "pop3s" : "pop3"), 0, 0, 0)) < 0)
|
||||
return geterrstr();
|
||||
|
||||
if(pop->needssl){
|
||||
if((err = pop3pushtls(pop)) != nil)
|
||||
return err;
|
||||
}else{
|
||||
Binit(&pop->bin, pop->fd, OREAD);
|
||||
Binit(&pop->bout, pop->fd, OWRITE);
|
||||
}
|
||||
|
||||
if(err = pop3login(pop)) {
|
||||
close(pop->fd);
|
||||
return err;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
//
|
||||
// close connection
|
||||
//
|
||||
static void
|
||||
pop3hangup(Pop *pop)
|
||||
{
|
||||
pop3cmd(pop, "QUIT");
|
||||
pop3resp(pop);
|
||||
close(pop->fd);
|
||||
}
|
||||
|
||||
//
|
||||
// download a single message
|
||||
//
|
||||
static char*
|
||||
pop3download(Pop *pop, Message *m)
|
||||
{
|
||||
char *s, *f[3], *wp, *ep;
|
||||
char sdigest[SHA1dlen*2+1];
|
||||
int i, l, sz;
|
||||
|
||||
if(!pop->pipeline)
|
||||
pop3cmd(pop, "LIST %d", m->mesgno);
|
||||
if(!isokay(s = pop3resp(pop)))
|
||||
return s;
|
||||
|
||||
if(tokenize(s, f, 3) != 3)
|
||||
return "syntax error in LIST response";
|
||||
|
||||
if(atoi(f[1]) != m->mesgno)
|
||||
return "out of sync with pop3 server";
|
||||
|
||||
sz = atoi(f[2])+200; /* 200 because the plan9 pop3 server lies */
|
||||
if(sz == 0)
|
||||
return "invalid size in LIST response";
|
||||
|
||||
m->start = wp = emalloc(sz+1);
|
||||
ep = wp+sz;
|
||||
|
||||
if(!pop->pipeline)
|
||||
pop3cmd(pop, "RETR %d", m->mesgno);
|
||||
if(!isokay(s = pop3resp(pop))) {
|
||||
m->start = nil;
|
||||
free(wp);
|
||||
return s;
|
||||
}
|
||||
|
||||
s = nil;
|
||||
while(wp <= ep) {
|
||||
s = pop3resp(pop);
|
||||
if(strcmp(s, "unexpected eof") == 0) {
|
||||
free(m->start);
|
||||
m->start = nil;
|
||||
return "unexpected end of conversation";
|
||||
}
|
||||
if(strcmp(s, ".") == 0)
|
||||
break;
|
||||
|
||||
l = strlen(s)+1;
|
||||
if(s[0] == '.') {
|
||||
s++;
|
||||
l--;
|
||||
}
|
||||
/*
|
||||
* grow by 10%/200bytes - some servers
|
||||
* lie about message sizes
|
||||
*/
|
||||
if(wp+l > ep) {
|
||||
int pos = wp - m->start;
|
||||
sz += ((sz / 10) < 200)? 200: sz/10;
|
||||
m->start = erealloc(m->start, sz+1);
|
||||
wp = m->start+pos;
|
||||
ep = m->start+sz;
|
||||
}
|
||||
memmove(wp, s, l-1);
|
||||
wp[l-1] = '\n';
|
||||
wp += l;
|
||||
}
|
||||
|
||||
if(s == nil || strcmp(s, ".") != 0)
|
||||
return "out of sync with pop3 server";
|
||||
|
||||
m->end = wp;
|
||||
|
||||
// make sure there's a trailing null
|
||||
// (helps in body searches)
|
||||
*m->end = 0;
|
||||
m->bend = m->rbend = m->end;
|
||||
m->header = m->start;
|
||||
|
||||
// digest message
|
||||
sha1((uchar*)m->start, m->end - m->start, m->digest, nil);
|
||||
for(i = 0; i < SHA1dlen; i++)
|
||||
sprint(sdigest+2*i, "%2.2ux", m->digest[i]);
|
||||
m->sdigest = s_copy(sdigest);
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
//
|
||||
// check for new messages on pop server
|
||||
// UIDL is not required by RFC 1939, but
|
||||
// netscape requires it, so almost every server supports it.
|
||||
// we'll use it to make our lives easier.
|
||||
//
|
||||
static char*
|
||||
pop3read(Pop *pop, Mailbox *mb, int doplumb)
|
||||
{
|
||||
char *s, *p, *uidl, *f[2];
|
||||
int mesgno, ignore, nnew;
|
||||
Message *m, *next, **l;
|
||||
|
||||
// Some POP servers disallow UIDL if the maildrop is empty.
|
||||
pop3cmd(pop, "STAT");
|
||||
if(!isokay(s = pop3resp(pop)))
|
||||
return s;
|
||||
|
||||
// fetch message listing; note messages to grab
|
||||
l = &mb->root->part;
|
||||
if(strncmp(s, "+OK 0 ", 6) != 0) {
|
||||
pop3cmd(pop, "UIDL");
|
||||
if(!isokay(s = pop3resp(pop)))
|
||||
return s;
|
||||
|
||||
for(;;){
|
||||
p = pop3resp(pop);
|
||||
if(strcmp(p, ".") == 0 || strcmp(p, "unexpected eof") == 0)
|
||||
break;
|
||||
|
||||
if(tokenize(p, f, 2) != 2)
|
||||
continue;
|
||||
|
||||
mesgno = atoi(f[0]);
|
||||
uidl = f[1];
|
||||
if(strlen(uidl) > 75) // RFC 1939 says 70 characters max
|
||||
continue;
|
||||
|
||||
ignore = 0;
|
||||
while(*l != nil) {
|
||||
if(strcmp((*l)->uidl, uidl) == 0) {
|
||||
// matches mail we already have, note mesgno for deletion
|
||||
(*l)->mesgno = mesgno;
|
||||
ignore = 1;
|
||||
l = &(*l)->next;
|
||||
break;
|
||||
} else {
|
||||
// old mail no longer in box mark deleted
|
||||
if(doplumb)
|
||||
mailplumb(mb, *l, 1);
|
||||
(*l)->inmbox = 0;
|
||||
(*l)->deleted = 1;
|
||||
l = &(*l)->next;
|
||||
}
|
||||
}
|
||||
if(ignore)
|
||||
continue;
|
||||
|
||||
m = newmessage(mb->root);
|
||||
m->mallocd = 1;
|
||||
m->inmbox = 1;
|
||||
m->mesgno = mesgno;
|
||||
strcpy(m->uidl, uidl);
|
||||
|
||||
// chain in; will fill in message later
|
||||
*l = m;
|
||||
l = &m->next;
|
||||
}
|
||||
}
|
||||
|
||||
// whatever is left has been removed from the mbox, mark as deleted
|
||||
while(*l != nil) {
|
||||
if(doplumb)
|
||||
mailplumb(mb, *l, 1);
|
||||
(*l)->inmbox = 0;
|
||||
(*l)->deleted = 1;
|
||||
l = &(*l)->next;
|
||||
}
|
||||
|
||||
// download new messages
|
||||
nnew = 0;
|
||||
if(pop->pipeline){
|
||||
switch(rfork(RFPROC|RFMEM)){
|
||||
case -1:
|
||||
fprint(2, "rfork: %r\n");
|
||||
pop->pipeline = 0;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
case 0:
|
||||
for(m = mb->root->part; m != nil; m = m->next){
|
||||
if(m->start != nil)
|
||||
continue;
|
||||
Bprint(&pop->bout, "LIST %d\r\nRETR %d\r\n", m->mesgno, m->mesgno);
|
||||
}
|
||||
Bflush(&pop->bout);
|
||||
threadexits(nil);
|
||||
/* _exits(nil); jpc */
|
||||
}
|
||||
}
|
||||
|
||||
for(m = mb->root->part; m != nil; m = next) {
|
||||
next = m->next;
|
||||
|
||||
if(m->start != nil)
|
||||
continue;
|
||||
|
||||
if(s = pop3download(pop, m)) {
|
||||
// message disappeared? unchain
|
||||
fprint(2, "download %d: %s\n", m->mesgno, s);
|
||||
delmessage(mb, m);
|
||||
mb->root->subname--;
|
||||
continue;
|
||||
}
|
||||
nnew++;
|
||||
parse(m, 0, mb, 1);
|
||||
|
||||
if(doplumb)
|
||||
mailplumb(mb, m, 0);
|
||||
}
|
||||
if(pop->pipeline)
|
||||
waitpid();
|
||||
|
||||
if(nnew || mb->vers == 0) {
|
||||
mb->vers++;
|
||||
henter(PATH(0, Qtop), mb->name,
|
||||
(Qid){PATH(mb->id, Qmbox), mb->vers, QTDIR}, nil, mb);
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
//
|
||||
// delete marked messages
|
||||
//
|
||||
static void
|
||||
pop3purge(Pop *pop, Mailbox *mb)
|
||||
{
|
||||
Message *m, *next;
|
||||
|
||||
if(pop->pipeline){
|
||||
switch(rfork(RFPROC|RFMEM)){
|
||||
case -1:
|
||||
fprint(2, "rfork: %r\n");
|
||||
pop->pipeline = 0;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
case 0:
|
||||
for(m = mb->root->part; m != nil; m = next){
|
||||
next = m->next;
|
||||
if(m->deleted && m->refs == 0){
|
||||
if(m->inmbox)
|
||||
Bprint(&pop->bout, "DELE %d\r\n", m->mesgno);
|
||||
}
|
||||
}
|
||||
Bflush(&pop->bout);
|
||||
/* _exits(nil); jpc */
|
||||
threadexits(nil);
|
||||
}
|
||||
}
|
||||
for(m = mb->root->part; m != nil; m = next) {
|
||||
next = m->next;
|
||||
if(m->deleted && m->refs == 0) {
|
||||
if(m->inmbox) {
|
||||
if(!pop->pipeline)
|
||||
pop3cmd(pop, "DELE %d", m->mesgno);
|
||||
if(isokay(pop3resp(pop)))
|
||||
delmessage(mb, m);
|
||||
} else
|
||||
delmessage(mb, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// connect to pop3 server, sync mailbox
|
||||
static char*
|
||||
pop3sync(Mailbox *mb, int doplumb)
|
||||
{
|
||||
char *err;
|
||||
Pop *pop;
|
||||
|
||||
pop = mb->aux;
|
||||
|
||||
if(err = pop3dial(pop)) {
|
||||
mb->waketime = time(0) + pop->refreshtime;
|
||||
return err;
|
||||
}
|
||||
|
||||
if((err = pop3read(pop, mb, doplumb)) == nil){
|
||||
pop3purge(pop, mb);
|
||||
mb->d->atime = mb->d->mtime = time(0);
|
||||
}
|
||||
pop3hangup(pop);
|
||||
mb->waketime = time(0) + pop->refreshtime;
|
||||
return err;
|
||||
}
|
||||
|
||||
static char Epop3ctl[] = "bad pop3 control message";
|
||||
|
||||
static char*
|
||||
pop3ctl(Mailbox *mb, int argc, char **argv)
|
||||
{
|
||||
int n;
|
||||
Pop *pop;
|
||||
char *m, *me;
|
||||
|
||||
pop = mb->aux;
|
||||
if(argc < 1)
|
||||
return Epop3ctl;
|
||||
|
||||
if(argc==1 && strcmp(argv[0], "debug")==0){
|
||||
pop->debug = 1;
|
||||
return nil;
|
||||
}
|
||||
|
||||
if(argc==1 && strcmp(argv[0], "nodebug")==0){
|
||||
pop->debug = 0;
|
||||
return nil;
|
||||
}
|
||||
|
||||
if(argc==1 && strcmp(argv[0], "thumbprint")==0){
|
||||
if(pop->thumb)
|
||||
freeThumbprints(pop->thumb);
|
||||
/* pop->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude"); jpc */
|
||||
m = unsharp("#9/sys/lib/tls/mail");
|
||||
me = unsharp("#9/sys/lib/tls/mail.exclude");
|
||||
pop->thumb = initThumbprints(m, me);
|
||||
}
|
||||
if(strcmp(argv[0], "refresh")==0){
|
||||
if(argc==1){
|
||||
pop->refreshtime = 60;
|
||||
return nil;
|
||||
}
|
||||
if(argc==2){
|
||||
n = atoi(argv[1]);
|
||||
if(n < 15)
|
||||
return Epop3ctl;
|
||||
pop->refreshtime = n;
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
return Epop3ctl;
|
||||
}
|
||||
|
||||
// free extra memory associated with mb
|
||||
static void
|
||||
pop3close(Mailbox *mb)
|
||||
{
|
||||
Pop *pop;
|
||||
|
||||
pop = mb->aux;
|
||||
free(pop->freep);
|
||||
free(pop);
|
||||
}
|
||||
|
||||
//
|
||||
// open mailboxes of the form /pop/host/user or /apop/host/user
|
||||
//
|
||||
char*
|
||||
pop3mbox(Mailbox *mb, char *path)
|
||||
{
|
||||
char *f[10];
|
||||
int nf, apop, ppop, popssl, apopssl, apoptls, popnotls, apopnotls, poptls;
|
||||
Pop *pop;
|
||||
char *m, *me;
|
||||
|
||||
quotefmtinstall();
|
||||
popssl = strncmp(path, "/pops/", 6) == 0;
|
||||
apopssl = strncmp(path, "/apops/", 7) == 0;
|
||||
poptls = strncmp(path, "/poptls/", 8) == 0;
|
||||
popnotls = strncmp(path, "/popnotls/", 10) == 0;
|
||||
ppop = popssl || poptls || popnotls || strncmp(path, "/pop/", 5) == 0;
|
||||
apoptls = strncmp(path, "/apoptls/", 9) == 0;
|
||||
apopnotls = strncmp(path, "/apopnotls/", 11) == 0;
|
||||
apop = apopssl || apoptls || apopnotls || strncmp(path, "/apop/", 6) == 0;
|
||||
|
||||
if(!ppop && !apop)
|
||||
return Enotme;
|
||||
|
||||
path = strdup(path);
|
||||
if(path == nil)
|
||||
return "out of memory";
|
||||
|
||||
nf = getfields(path, f, nelem(f), 0, "/");
|
||||
if(nf != 3 && nf != 4) {
|
||||
free(path);
|
||||
return "bad pop3 path syntax /[a]pop[tls|ssl]/system[/user]";
|
||||
}
|
||||
|
||||
pop = emalloc(sizeof(*pop));
|
||||
pop->freep = path;
|
||||
pop->host = f[2];
|
||||
if(nf < 4)
|
||||
pop->user = nil;
|
||||
else
|
||||
pop->user = f[3];
|
||||
pop->ppop = ppop;
|
||||
pop->needssl = popssl || apopssl;
|
||||
pop->needtls = poptls || apoptls;
|
||||
pop->refreshtime = 60;
|
||||
pop->notls = popnotls || apopnotls;
|
||||
/* pop->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude"); jpc */
|
||||
m = unsharp("#9/sys/lib/tls/mail");
|
||||
me = unsharp("#9/sys/lib/tls/mail.exclude");
|
||||
pop->thumb = initThumbprints(m, me);
|
||||
|
||||
mb->aux = pop;
|
||||
mb->sync = pop3sync;
|
||||
mb->close = pop3close;
|
||||
mb->ctl = pop3ctl;
|
||||
mb->d = emalloc(sizeof(*mb->d));
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
15
src/cmd/upas/fs/readdir.c
Normal file
15
src/cmd/upas/fs/readdir.c
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
|
||||
void
|
||||
main(void)
|
||||
{
|
||||
Dir d;
|
||||
int fd, n;
|
||||
|
||||
fd = open("/mail/fs", OREAD);
|
||||
while((n = dirread(fd, &d, sizeof(d))) > 0){
|
||||
print("%s\n", d.name);
|
||||
}
|
||||
print("n = %d\n", n);
|
||||
}
|
||||
113
src/cmd/upas/fs/strtotm.c
Normal file
113
src/cmd/upas/fs/strtotm.c
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <ctype.h>
|
||||
|
||||
static char*
|
||||
skiptext(char *q)
|
||||
{
|
||||
while(*q!='\0' && *q!=' ' && *q!='\t' && *q!='\r' && *q!='\n')
|
||||
q++;
|
||||
return q;
|
||||
}
|
||||
|
||||
static char*
|
||||
skipwhite(char *q)
|
||||
{
|
||||
while(*q==' ' || *q=='\t' || *q=='\r' || *q=='\n')
|
||||
q++;
|
||||
return q;
|
||||
}
|
||||
|
||||
static char* months[] = {
|
||||
"jan", "feb", "mar", "apr",
|
||||
"may", "jun", "jul", "aug",
|
||||
"sep", "oct", "nov", "dec"
|
||||
};
|
||||
|
||||
static int
|
||||
strcmplwr(char *a, char *b, int n)
|
||||
{
|
||||
char *eb;
|
||||
|
||||
eb = b+n;
|
||||
while(*a && *b && b<eb){
|
||||
if(tolower(*a) != tolower(*b))
|
||||
return 1;
|
||||
a++;
|
||||
b++;
|
||||
}
|
||||
if(b==eb)
|
||||
return 0;
|
||||
return *a != *b;
|
||||
}
|
||||
|
||||
int
|
||||
strtotm(char *p, Tm *tmp)
|
||||
{
|
||||
char *q, *r;
|
||||
int j;
|
||||
Tm tm;
|
||||
int delta;
|
||||
|
||||
delta = 0;
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
tm.mon = -1;
|
||||
tm.hour = -1;
|
||||
tm.min = -1;
|
||||
tm.year = -1;
|
||||
tm.mday = -1;
|
||||
for(p=skipwhite(p); *p; p=skipwhite(q)){
|
||||
q = skiptext(p);
|
||||
|
||||
/* look for time in hh:mm[:ss] */
|
||||
if(r = memchr(p, ':', q-p)){
|
||||
tm.hour = strtol(p, 0, 10);
|
||||
tm.min = strtol(r+1, 0, 10);
|
||||
if(r = memchr(r+1, ':', q-(r+1)))
|
||||
tm.sec = strtol(r+1, 0, 10);
|
||||
else
|
||||
tm.sec = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* look for month */
|
||||
for(j=0; j<12; j++)
|
||||
if(strcmplwr(p, months[j], 3)==0){
|
||||
tm.mon = j;
|
||||
break;
|
||||
}
|
||||
|
||||
if(j!=12)
|
||||
continue;
|
||||
|
||||
/* look for time zone [A-Z][A-Z]T */
|
||||
if(q-p==3 && 'A' <= p[0] && p[0] <= 'Z'
|
||||
&& 'A' <= p[1] && p[1] <= 'Z' && p[2] == 'T'){
|
||||
strecpy(tm.zone, tm.zone+4, p);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(p[0]=='+'||p[0]=='-')
|
||||
if(q-p==5 && strspn(p+1, "0123456789") == 4){
|
||||
delta = (((p[1]-'0')*10+p[2]-'0')*60+(p[3]-'0')*10+p[4]-'0')*60;
|
||||
if(p[0] == '-')
|
||||
delta = -delta;
|
||||
continue;
|
||||
}
|
||||
if(strspn(p, "0123456789") == q-p){
|
||||
j = strtol(p, nil, 10);
|
||||
if(1 <= j && j <= 31)
|
||||
tm.mday = j;
|
||||
if(j >= 1900)
|
||||
tm.year = j-1900;
|
||||
}
|
||||
}
|
||||
|
||||
if(tm.mon<0 || tm.year<0
|
||||
|| tm.hour<0 || tm.min<0
|
||||
|| tm.mday<0)
|
||||
return -1;
|
||||
|
||||
*tmp = *localtime(tm2sec(&tm)-delta);
|
||||
return 0;
|
||||
}
|
||||
81
src/cmd/upas/fs/tester.c
Normal file
81
src/cmd/upas/fs/tester.c
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <ctype.h>
|
||||
#include <String.h>
|
||||
#include "message.h"
|
||||
|
||||
Message *root;
|
||||
|
||||
void
|
||||
prindent(int i)
|
||||
{
|
||||
for(; i > 0; i--)
|
||||
print(" ");
|
||||
}
|
||||
|
||||
void
|
||||
prstring(int indent, char *tag, String *s)
|
||||
{
|
||||
if(s == nil)
|
||||
return;
|
||||
prindent(indent+1);
|
||||
print("%s %s\n", tag, s_to_c(s));
|
||||
}
|
||||
|
||||
void
|
||||
info(int indent, int mno, Message *m)
|
||||
{
|
||||
int i;
|
||||
Message *nm;
|
||||
|
||||
prindent(indent);
|
||||
print("%d%c %d ", mno, m->allocated?'*':' ', m->end - m->start);
|
||||
if(m->unixfrom != nil)
|
||||
print("uf %s ", s_to_c(m->unixfrom));
|
||||
if(m->unixdate != nil)
|
||||
print("ud %s ", s_to_c(m->unixdate));
|
||||
print("\n");
|
||||
prstring(indent, "from:", m->from822);
|
||||
prstring(indent, "sender:", m->sender822);
|
||||
prstring(indent, "to:", m->to822);
|
||||
prstring(indent, "cc:", m->cc822);
|
||||
prstring(indent, "reply-to:", m->replyto822);
|
||||
prstring(indent, "subject:", m->subject822);
|
||||
prstring(indent, "date:", m->date822);
|
||||
prstring(indent, "filename:", m->filename);
|
||||
prstring(indent, "type:", m->type);
|
||||
prstring(indent, "charset:", m->charset);
|
||||
|
||||
i = 1;
|
||||
for(nm = m->part; nm != nil; nm = nm->next){
|
||||
info(indent+1, i++, nm);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
char *err;
|
||||
char *mboxfile;
|
||||
|
||||
ARGBEGIN{
|
||||
}ARGEND;
|
||||
|
||||
if(argc > 0)
|
||||
mboxfile = argv[0];
|
||||
else
|
||||
mboxfile = "./mbox";
|
||||
|
||||
root = newmessage(nil);
|
||||
|
||||
err = readmbox(mboxfile, &root->part);
|
||||
if(err != nil){
|
||||
fprint(2, "boom: %s\n", err);
|
||||
exits(0);
|
||||
}
|
||||
|
||||
info(0, 1, root);
|
||||
|
||||
exits(0);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue