imap-based new upas/fs
This commit is contained in:
parent
1ea614ffaf
commit
941e17134e
15 changed files with 4168 additions and 0 deletions
318
src/cmd/upas/nfs/box.c
Normal file
318
src/cmd/upas/nfs/box.c
Normal file
|
|
@ -0,0 +1,318 @@
|
||||||
|
#include "a.h"
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
BoxSubChunk = 16,
|
||||||
|
BoxChunk = 64,
|
||||||
|
MsgChunk = 256,
|
||||||
|
PartChunk = 4,
|
||||||
|
PartSubChunk = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
Box **boxes;
|
||||||
|
uint nboxes;
|
||||||
|
Box *rootbox;
|
||||||
|
int boxid;
|
||||||
|
|
||||||
|
Box*
|
||||||
|
boxbyname(char *name)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* LATER: replace with hash table */
|
||||||
|
for(i=0; i<nboxes; i++)
|
||||||
|
if(boxes[i] && strcmp(boxes[i]->name, name) == 0)
|
||||||
|
return boxes[i];
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
Box*
|
||||||
|
subbox(Box *b, char *elem)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i=0; i<b->nsub; i++)
|
||||||
|
if(b->sub[i] && strcmp(b->sub[i]->elem, elem) == 0)
|
||||||
|
return b->sub[i];
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
Box*
|
||||||
|
boxbyid(uint id)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* LATER: replace with binary search */
|
||||||
|
for(i=0; i<nboxes; i++)
|
||||||
|
if(boxes[i] && boxes[i]->id == id)
|
||||||
|
return boxes[i];
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
Box*
|
||||||
|
boxcreate(char *name)
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
Box *b, *bb;
|
||||||
|
|
||||||
|
if((b = boxbyname(name)) != nil)
|
||||||
|
return b;
|
||||||
|
|
||||||
|
b = emalloc(sizeof *b);
|
||||||
|
b->id = ++boxid;
|
||||||
|
b->time = time(0);
|
||||||
|
b->name = estrdup(name);
|
||||||
|
b->uidnext = 1;
|
||||||
|
p = strrchr(b->name, '/');
|
||||||
|
if(p){
|
||||||
|
*p = 0;
|
||||||
|
bb = boxcreate(b->name);
|
||||||
|
*p = '/';
|
||||||
|
b->elem = p+1;
|
||||||
|
}else{
|
||||||
|
bb = rootbox;
|
||||||
|
b->elem = b->name;
|
||||||
|
}
|
||||||
|
if(nboxes%BoxChunk == 0)
|
||||||
|
boxes = erealloc(boxes, (nboxes+BoxChunk)*sizeof boxes[0]);
|
||||||
|
boxes[nboxes++] = b;
|
||||||
|
if(bb->nsub%BoxSubChunk == 0)
|
||||||
|
bb->sub = erealloc(bb->sub, (bb->nsub+BoxSubChunk)*sizeof bb->sub[0]);
|
||||||
|
bb->sub[bb->nsub++] = b;
|
||||||
|
b->parent = bb;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
boxfree(Box *b)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(b == nil)
|
||||||
|
return;
|
||||||
|
for(i=0; i<b->nmsg; i++)
|
||||||
|
msgfree(b->msg[i]);
|
||||||
|
free(b->msg);
|
||||||
|
free(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
Part*
|
||||||
|
partcreate(Msg *m, Part *pp)
|
||||||
|
{
|
||||||
|
Part *p;
|
||||||
|
|
||||||
|
if(m->npart%PartChunk == 0)
|
||||||
|
m->part = erealloc(m->part, (m->npart+PartChunk)*sizeof m->part[0]);
|
||||||
|
p = emalloc(sizeof *p);
|
||||||
|
p->msg = m;
|
||||||
|
p->ix = m->npart;
|
||||||
|
m->part[m->npart++] = p;
|
||||||
|
if(pp){
|
||||||
|
if(pp->nsub%PartSubChunk == 0)
|
||||||
|
pp->sub = erealloc(pp->sub, (pp->nsub+PartSubChunk)*sizeof pp->sub[0]);
|
||||||
|
p->pix = pp->nsub;
|
||||||
|
p->parent = pp;
|
||||||
|
pp->sub[pp->nsub++] = p;
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
partfree(Part *p)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(p == nil)
|
||||||
|
return;
|
||||||
|
for(i=0; i<p->nsub; i++)
|
||||||
|
partfree(p->sub[i]);
|
||||||
|
free(p->sub);
|
||||||
|
hdrfree(p->hdr);
|
||||||
|
free(p->type);
|
||||||
|
free(p->idstr);
|
||||||
|
free(p->desc);
|
||||||
|
free(p->encoding);
|
||||||
|
free(p->charset);
|
||||||
|
free(p->raw);
|
||||||
|
free(p->rawheader);
|
||||||
|
free(p->rawbody);
|
||||||
|
free(p->mimeheader);
|
||||||
|
free(p->body);
|
||||||
|
free(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
msgfree(Msg *m)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(m == nil)
|
||||||
|
return;
|
||||||
|
for(i=0; i<m->npart; i++)
|
||||||
|
free(m->part[i]);
|
||||||
|
free(m->part);
|
||||||
|
free(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
msgplumb(Msg *m, int delete)
|
||||||
|
{
|
||||||
|
static int fd = -1;
|
||||||
|
Plumbmsg p;
|
||||||
|
Plumbattr a[10];
|
||||||
|
char buf[256], date[40];
|
||||||
|
int ai;
|
||||||
|
|
||||||
|
if(m == nil || m->npart < 1 || m->part[0]->hdr == nil)
|
||||||
|
return;
|
||||||
|
if(m->box && strcmp(m->box->name, "mbox") != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
p.src = "mailfs";
|
||||||
|
p.dst = "seemail";
|
||||||
|
p.wdir = "/";
|
||||||
|
p.type = "text";
|
||||||
|
|
||||||
|
ai = 0;
|
||||||
|
a[ai].name = "filetype";
|
||||||
|
a[ai].value = "mail";
|
||||||
|
|
||||||
|
a[++ai].name = "mailtype";
|
||||||
|
a[ai].value = delete?"delete":"new";
|
||||||
|
a[ai-1].next = &a[ai];
|
||||||
|
|
||||||
|
if(m->part[0]->hdr->from){
|
||||||
|
a[++ai].name = "sender";
|
||||||
|
a[ai].value = m->part[0]->hdr->from;
|
||||||
|
a[ai-1].next = &a[ai];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(m->part[0]->hdr->subject){
|
||||||
|
a[++ai].name = "subject";
|
||||||
|
a[ai].value = m->part[0]->hdr->subject;
|
||||||
|
a[ai-1].next = &a[ai];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(m->part[0]->hdr->digest){
|
||||||
|
a[++ai].name = "digest";
|
||||||
|
a[ai].value = m->part[0]->hdr->digest;
|
||||||
|
a[ai-1].next = &a[ai];
|
||||||
|
}
|
||||||
|
|
||||||
|
strcpy(date, ctime(m->date));
|
||||||
|
date[strlen(date)-1] = 0; /* newline */
|
||||||
|
a[++ai].name = "date";
|
||||||
|
a[ai].value = date;
|
||||||
|
a[ai-1].next = &a[ai];
|
||||||
|
|
||||||
|
a[ai].next = nil;
|
||||||
|
|
||||||
|
p.attr = a;
|
||||||
|
snprint(buf, sizeof buf, "Mail/%s/%ud", m->box->name, m->id);
|
||||||
|
p.ndata = strlen(buf);
|
||||||
|
p.data = buf;
|
||||||
|
|
||||||
|
if(fd < 0)
|
||||||
|
fd = plumbopen("send", OWRITE);
|
||||||
|
if(fd < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
plumbsend(fd, &p);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Msg*
|
||||||
|
msgcreate(Box *box)
|
||||||
|
{
|
||||||
|
Msg *m;
|
||||||
|
|
||||||
|
m = emalloc(sizeof *m);
|
||||||
|
m->box = box;
|
||||||
|
partcreate(m, nil);
|
||||||
|
m->part[0]->type = estrdup("message/rfc822");
|
||||||
|
if(box->nmsg%MsgChunk == 0)
|
||||||
|
box->msg = erealloc(box->msg, (box->nmsg+MsgChunk)*sizeof box->msg[0]);
|
||||||
|
m->ix = box->nmsg++;
|
||||||
|
box->msg[m->ix] = m;
|
||||||
|
m->id = ++box->msgid;
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
Msg*
|
||||||
|
msgbyimapuid(Box *box, uint uid, int docreate)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
Msg *msg;
|
||||||
|
|
||||||
|
if(box == nil)
|
||||||
|
return nil;
|
||||||
|
/* LATER: binary search or something */
|
||||||
|
for(i=0; i<box->nmsg; i++)
|
||||||
|
if(box->msg[i]->imapuid == uid)
|
||||||
|
return box->msg[i];
|
||||||
|
if(!docreate)
|
||||||
|
return nil;
|
||||||
|
msg = msgcreate(box);
|
||||||
|
msg->imapuid = uid;
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
Msg*
|
||||||
|
msgbyid(Box *box, uint id)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(box == nil)
|
||||||
|
return nil;
|
||||||
|
/* LATER: binary search or something */
|
||||||
|
for(i=0; i<box->nmsg; i++)
|
||||||
|
if(box->msg[i]->id == id)
|
||||||
|
return box->msg[i];
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
Part*
|
||||||
|
partbyid(Msg *m, uint id)
|
||||||
|
{
|
||||||
|
if(m == nil)
|
||||||
|
return nil;
|
||||||
|
if(id >= m->npart)
|
||||||
|
return nil;
|
||||||
|
return m->part[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
Part*
|
||||||
|
subpart(Part *p, uint a)
|
||||||
|
{
|
||||||
|
if(p == nil || a >= p->nsub)
|
||||||
|
return nil;
|
||||||
|
return p->sub[a];
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
hdrfree(Hdr *h)
|
||||||
|
{
|
||||||
|
if(h == nil)
|
||||||
|
return;
|
||||||
|
free(h->date);
|
||||||
|
free(h->subject);
|
||||||
|
free(h->from);
|
||||||
|
free(h->sender);
|
||||||
|
free(h->replyto);
|
||||||
|
free(h->to);
|
||||||
|
free(h->cc);
|
||||||
|
free(h->bcc);
|
||||||
|
free(h->inreplyto);
|
||||||
|
free(h->messageid);
|
||||||
|
free(h->digest);
|
||||||
|
free(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
boxinit(void)
|
||||||
|
{
|
||||||
|
rootbox = emalloc(sizeof *rootbox);
|
||||||
|
rootbox->name = estrdup("");
|
||||||
|
rootbox->time = time(0);
|
||||||
|
}
|
||||||
|
|
||||||
133
src/cmd/upas/nfs/box.h
Normal file
133
src/cmd/upas/nfs/box.h
Normal file
|
|
@ -0,0 +1,133 @@
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
FlagJunk = 1<<0,
|
||||||
|
FlagNonJunk = 1<<1,
|
||||||
|
FlagReplied = 1<<2,
|
||||||
|
FlagFlagged = 1<<3,
|
||||||
|
FlagDeleted = 1<<4,
|
||||||
|
FlagDraft = 1<<5,
|
||||||
|
FlagSeen = 1<<6,
|
||||||
|
FlagNoInferiors = 1<<7,
|
||||||
|
FlagMarked = 1<<8,
|
||||||
|
FlagNoSelect = 1<<9,
|
||||||
|
FlagUnMarked = 1<<10,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct Box Box;
|
||||||
|
typedef struct Hdr Hdr;
|
||||||
|
typedef struct Msg Msg;
|
||||||
|
typedef struct Part Part;
|
||||||
|
|
||||||
|
struct Box
|
||||||
|
{
|
||||||
|
char* name; /* name of mailbox */
|
||||||
|
char* elem; /* last element in name */
|
||||||
|
uint ix; /* index in box[] array */
|
||||||
|
uint id; /* id shown in file system */
|
||||||
|
uint flags; /* FlagNoInferiors, etc. */
|
||||||
|
uint time; /* last update time */
|
||||||
|
uint msgid; /* last message id used */
|
||||||
|
|
||||||
|
Msg** msg; /* array of messages (can have nils) */
|
||||||
|
uint nmsg;
|
||||||
|
|
||||||
|
char* imapname; /* name on IMAP server */
|
||||||
|
u32int validity; /* IMAP validity number */
|
||||||
|
uint uidnext; /* IMAP expected next uid */
|
||||||
|
uint recent; /* IMAP first recent message */
|
||||||
|
uint exists; /* IMAP last message in box */
|
||||||
|
uint maxseen; /* maximum IMAP uid seen */
|
||||||
|
int mark;
|
||||||
|
uint imapinit; /* up-to-date w.r.t. IMAP */
|
||||||
|
|
||||||
|
Box* parent; /* in tree */
|
||||||
|
Box** sub;
|
||||||
|
uint nsub;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Hdr
|
||||||
|
{
|
||||||
|
/* LATER: store date as int, reformat for programs */
|
||||||
|
/* order known by fs.c */
|
||||||
|
char* date;
|
||||||
|
char* subject;
|
||||||
|
char* from;
|
||||||
|
char* sender;
|
||||||
|
char* replyto;
|
||||||
|
char* to;
|
||||||
|
char* cc;
|
||||||
|
char* bcc;
|
||||||
|
char* inreplyto;
|
||||||
|
char* messageid;
|
||||||
|
char* digest;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Msg
|
||||||
|
{
|
||||||
|
Box* box; /* mailbox containing msg */
|
||||||
|
uint ix; /* index in box->msg[] array */
|
||||||
|
uint id; /* id shown in file system */
|
||||||
|
uint imapuid; /* IMAP uid */
|
||||||
|
uint imapid; /* IMAP id */
|
||||||
|
uint flags; /* FlagDeleted etc. */
|
||||||
|
uint date; /* smtp envelope date */
|
||||||
|
uint size;
|
||||||
|
|
||||||
|
Part** part; /* message subparts - part[0] is root */
|
||||||
|
uint npart;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Part
|
||||||
|
{
|
||||||
|
Msg* msg; /* msg containing part */
|
||||||
|
uint ix; /* index in msg->part[] */
|
||||||
|
uint pix; /* id in parent->sub[] */
|
||||||
|
Part* parent; /* parent in structure */
|
||||||
|
Part** sub; /* children in structure */
|
||||||
|
uint nsub;
|
||||||
|
|
||||||
|
/* order known by fs.c */
|
||||||
|
char* type; /* e.g., "text/plain" */
|
||||||
|
char* idstr;
|
||||||
|
char* desc;
|
||||||
|
char* encoding;
|
||||||
|
char* charset;
|
||||||
|
char* raw;
|
||||||
|
char* rawheader;
|
||||||
|
char* rawbody;
|
||||||
|
char* mimeheader;
|
||||||
|
|
||||||
|
/* order known by fs.c */
|
||||||
|
uint size;
|
||||||
|
uint lines;
|
||||||
|
|
||||||
|
char* body;
|
||||||
|
uint nbody;
|
||||||
|
Hdr* hdr; /* RFC822 envelope for message/rfc822 */
|
||||||
|
};
|
||||||
|
|
||||||
|
void boxinit(void);
|
||||||
|
Box* boxbyname(char*);
|
||||||
|
Box* boxbyid(uint);
|
||||||
|
Box* boxcreate(char*);
|
||||||
|
void boxfree(Box*);
|
||||||
|
Box* subbox(Box*, char*);
|
||||||
|
Msg* msgcreate(Box*);
|
||||||
|
Part* partcreate(Msg*, Part*);
|
||||||
|
|
||||||
|
void hdrfree(Hdr*);
|
||||||
|
|
||||||
|
Msg* msgbyid(Box*, uint);
|
||||||
|
Msg* msgbyimapuid(Box*, uint, int);
|
||||||
|
void msgfree(Msg*);
|
||||||
|
void msgplumb(Msg*, int);
|
||||||
|
|
||||||
|
Part* partbyid(Msg*, uint);
|
||||||
|
Part* subpart(Part*, uint);
|
||||||
|
void partfree(Part*);
|
||||||
|
|
||||||
|
extern Box** boxes;
|
||||||
|
extern uint nboxes;
|
||||||
|
|
||||||
|
extern Box* rootbox;
|
||||||
|
|
||||||
257
src/cmd/upas/nfs/decode.c
Normal file
257
src/cmd/upas/nfs/decode.c
Normal file
|
|
@ -0,0 +1,257 @@
|
||||||
|
/* Quick and dirty RFC 2047 */
|
||||||
|
|
||||||
|
#include "a.h"
|
||||||
|
|
||||||
|
static int
|
||||||
|
unhex1(char c)
|
||||||
|
{
|
||||||
|
if('0' <= c && c <= '9')
|
||||||
|
return c-'0';
|
||||||
|
if('a' <= c && c <= 'f')
|
||||||
|
return c-'a'+10;
|
||||||
|
if('A' <= c && c <= 'F')
|
||||||
|
return c-'A'+10;
|
||||||
|
return 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
unhex(char *s)
|
||||||
|
{
|
||||||
|
return unhex1(s[0])*16+unhex1(s[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
decqp(uchar *out, int lim, char *in, int n)
|
||||||
|
{
|
||||||
|
char *p, *ep;
|
||||||
|
uchar *eout, *out0;
|
||||||
|
|
||||||
|
out0 = out;
|
||||||
|
eout = out+lim;
|
||||||
|
for(p=in, ep=in+n; p<ep && out<eout; ){
|
||||||
|
if(*p == '_'){
|
||||||
|
*out++ = ' ';
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
else if(*p == '='){
|
||||||
|
if(p+1 >= ep)
|
||||||
|
break;
|
||||||
|
if(*(p+1) == '\n'){
|
||||||
|
p += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(p+3 > ep)
|
||||||
|
break;
|
||||||
|
*out++ = unhex(p+1);
|
||||||
|
p += 3;
|
||||||
|
}else
|
||||||
|
*out++ = *p++;
|
||||||
|
}
|
||||||
|
return out-out0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char*
|
||||||
|
decode(int kind, char *s, int *len)
|
||||||
|
{
|
||||||
|
char *t;
|
||||||
|
int l;
|
||||||
|
|
||||||
|
if(s == nil)
|
||||||
|
return s;
|
||||||
|
switch(kind){
|
||||||
|
case QuotedPrintable:
|
||||||
|
l = strlen(s)+1;
|
||||||
|
t = emalloc(l);
|
||||||
|
l = decqp((uchar*)t, l, s, l-1);
|
||||||
|
*len = l;
|
||||||
|
t[l] = 0;
|
||||||
|
return t;
|
||||||
|
|
||||||
|
case Base64:
|
||||||
|
l = strlen(s)+1;
|
||||||
|
t = emalloc(l);
|
||||||
|
l = dec64((uchar*)t, l, s, l-1);
|
||||||
|
*len = l;
|
||||||
|
t[l] = 0;
|
||||||
|
return t;
|
||||||
|
|
||||||
|
default:
|
||||||
|
*len = strlen(s);
|
||||||
|
return estrdup(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct {
|
||||||
|
char *mime;
|
||||||
|
char *tcs;
|
||||||
|
} tcstab[] = {
|
||||||
|
"iso-8859-2", "8859-2",
|
||||||
|
"iso-8859-3", "8859-3",
|
||||||
|
"iso-8859-4", "8859-4",
|
||||||
|
"iso-8859-5", "8859-5",
|
||||||
|
"iso-8859-6", "8859-6",
|
||||||
|
"iso-8859-7", "8859-7",
|
||||||
|
"iso-8859-8", "8859-8",
|
||||||
|
"iso-8859-9", "8859-9",
|
||||||
|
"iso-8859-10", "8859-10",
|
||||||
|
"iso-8859-15", "8859-15",
|
||||||
|
"big5", "big5",
|
||||||
|
"iso-2022-jp", "jis-kanji",
|
||||||
|
"windows-1251", "cp1251",
|
||||||
|
"koi8-r", "koi8",
|
||||||
|
};
|
||||||
|
|
||||||
|
char*
|
||||||
|
tcs(char *charset, char *s)
|
||||||
|
{
|
||||||
|
static char buf[4096];
|
||||||
|
int i, n;
|
||||||
|
int fd[3], p[2], pp[2];
|
||||||
|
uchar *us;
|
||||||
|
char *t, *u;
|
||||||
|
char *argv[4];
|
||||||
|
Rune r;
|
||||||
|
|
||||||
|
if(s == nil || charset == nil || *s == 0)
|
||||||
|
return s;
|
||||||
|
|
||||||
|
if(cistrcmp(charset, "utf-8") == 0)
|
||||||
|
return s;
|
||||||
|
if(cistrcmp(charset, "iso-8859-1") == 0 || cistrcmp(charset, "us-ascii") == 0){
|
||||||
|
latin1:
|
||||||
|
n = 0;
|
||||||
|
for(us=(uchar*)s; *us; us++)
|
||||||
|
n += runelen(*us);
|
||||||
|
n++;
|
||||||
|
t = emalloc(n);
|
||||||
|
for(us=(uchar*)s, u=t; *us; us++){
|
||||||
|
r = *us;
|
||||||
|
u += runetochar(u, &r);
|
||||||
|
}
|
||||||
|
*u = 0;
|
||||||
|
free(s);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
for(i=0; i<nelem(tcstab); i++)
|
||||||
|
if(cistrcmp(charset, tcstab[i].mime) == 0)
|
||||||
|
goto tcs;
|
||||||
|
goto latin1;
|
||||||
|
|
||||||
|
tcs:
|
||||||
|
argv[0] = "tcs";
|
||||||
|
argv[1] = "-f";
|
||||||
|
argv[2] = charset;
|
||||||
|
argv[3] = nil;
|
||||||
|
|
||||||
|
if(pipe(p) < 0 || pipe(pp) < 0)
|
||||||
|
sysfatal("pipe: %r");
|
||||||
|
fd[0] = p[0];
|
||||||
|
fd[1] = pp[0];
|
||||||
|
fd[2] = dup(2, -1);
|
||||||
|
if(threadspawnl(fd, "tcs", "tcs", "-f", tcstab[i].tcs, nil) < 0){
|
||||||
|
close(p[0]);
|
||||||
|
close(p[1]);
|
||||||
|
close(pp[0]);
|
||||||
|
close(pp[1]);
|
||||||
|
close(fd[2]);
|
||||||
|
goto latin1;
|
||||||
|
}
|
||||||
|
close(p[0]);
|
||||||
|
close(pp[0]);
|
||||||
|
write(p[1], s, strlen(s));
|
||||||
|
close(p[1]);
|
||||||
|
n = readn(pp[1], buf, sizeof buf-1);
|
||||||
|
close(pp[1]);
|
||||||
|
if(n <= 0)
|
||||||
|
goto latin1;
|
||||||
|
free(s);
|
||||||
|
buf[n] = 0;
|
||||||
|
return estrdup(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
char*
|
||||||
|
unrfc2047(char *s)
|
||||||
|
{
|
||||||
|
char *p, *q, *t, *u, *v;
|
||||||
|
int len;
|
||||||
|
Rune r;
|
||||||
|
Fmt fmt;
|
||||||
|
|
||||||
|
if(s == nil)
|
||||||
|
return nil;
|
||||||
|
|
||||||
|
if(strstr(s, "=?") == nil)
|
||||||
|
return s;
|
||||||
|
|
||||||
|
fmtstrinit(&fmt);
|
||||||
|
for(p=s; *p; ){
|
||||||
|
/* =?charset?e?text?= */
|
||||||
|
if(*p=='=' && *(p+1)=='?'){
|
||||||
|
p += 2;
|
||||||
|
q = strchr(p, '?');
|
||||||
|
if(q == nil)
|
||||||
|
goto emit;
|
||||||
|
q++;
|
||||||
|
if(*q == '?' || *(q+1) != '?')
|
||||||
|
goto emit;
|
||||||
|
t = q+2;
|
||||||
|
u = strchr(t, '?');
|
||||||
|
if(u == nil || *(u+1) != '=')
|
||||||
|
goto emit;
|
||||||
|
switch(*q){
|
||||||
|
case 'q':
|
||||||
|
case 'Q':
|
||||||
|
*u = 0;
|
||||||
|
v = decode(QuotedPrintable, t, &len);
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
case 'B':
|
||||||
|
*u = 0;
|
||||||
|
v = decode(Base64, t, &len);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
goto emit;
|
||||||
|
}
|
||||||
|
*(q-1) = 0;
|
||||||
|
v = tcs(p, v);
|
||||||
|
fmtstrcpy(&fmt, v);
|
||||||
|
free(v);
|
||||||
|
p = u+2;
|
||||||
|
}
|
||||||
|
emit:
|
||||||
|
p += chartorune(&r, p);
|
||||||
|
fmtrune(&fmt, r);
|
||||||
|
}
|
||||||
|
p = fmtstrflush(&fmt);
|
||||||
|
if(p == nil)
|
||||||
|
sysfatal("out of memory");
|
||||||
|
free(s);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef TEST
|
||||||
|
char *test[] =
|
||||||
|
{
|
||||||
|
"hello world",
|
||||||
|
"hello =?iso-8859-1?q?this is some text?=",
|
||||||
|
"=?US-ASCII?Q?Keith_Moore?=",
|
||||||
|
"=?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?=",
|
||||||
|
"=?ISO-8859-1?Q?Andr=E9?= Pirard",
|
||||||
|
"=?ISO-8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?=",
|
||||||
|
"=?ISO-8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?=",
|
||||||
|
"=?ISO-8859-1?Q?Olle_J=E4rnefors?=",
|
||||||
|
"=?iso-2022-jp?B?GyRCTTVKISRKP006SiRyS34kPyQ3JEZKcz03JCIkahsoQg==?=",
|
||||||
|
"=?UTF-8?B?Ik5pbHMgTy4gU2Vsw6VzZGFsIg==?="
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
threadmain(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i=0; i<nelem(test); i++)
|
||||||
|
print("%s\n\t%s\n", test[i], unrfc2047(estrdup(test[i])));
|
||||||
|
threadexitsall(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
1259
src/cmd/upas/nfs/fs.c
Normal file
1259
src/cmd/upas/nfs/fs.c
Normal file
File diff suppressed because it is too large
Load diff
2
src/cmd/upas/nfs/fs.h
Normal file
2
src/cmd/upas/nfs/fs.h
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
extern Srv fs;
|
||||||
|
void fsinit0(void);
|
||||||
1715
src/cmd/upas/nfs/imap.c
Normal file
1715
src/cmd/upas/nfs/imap.c
Normal file
File diff suppressed because it is too large
Load diff
23
src/cmd/upas/nfs/imap.h
Normal file
23
src/cmd/upas/nfs/imap.h
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
typedef struct Imap Imap;
|
||||||
|
|
||||||
|
void imapcheckbox(Imap *z, Box *b);
|
||||||
|
Imap* imapconnect(char *server, int mode);
|
||||||
|
int imapcopylist(Imap *z, char *nbox, Msg **m, uint nm);
|
||||||
|
void imapfetchraw(Imap *z, Part *p);
|
||||||
|
void imapfetchrawbody(Imap *z, Part *p);
|
||||||
|
void imapfetchrawheader(Imap *z, Part *p);
|
||||||
|
void imapfetchrawmime(Imap *z, Part *p);
|
||||||
|
int imapflaglist(Imap *z, int op, int flag, Msg **m, uint nm);
|
||||||
|
void imaphangup(Imap *z, int ticks);
|
||||||
|
int imapremovelist(Imap *z, Msg **m, uint nm);
|
||||||
|
int imapsearchbox(Imap *z, Box *b, char *search, Msg ***mm);
|
||||||
|
|
||||||
|
extern int chattyimap;
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
Unencrypted,
|
||||||
|
Starttls,
|
||||||
|
Tls,
|
||||||
|
Cmd
|
||||||
|
};
|
||||||
68
src/cmd/upas/nfs/main.c
Normal file
68
src/cmd/upas/nfs/main.c
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
TO DO
|
||||||
|
|
||||||
|
can get disposition info out of imap extended structure if needed
|
||||||
|
sizes in stat/ls ?
|
||||||
|
translate character sets in =? subjects
|
||||||
|
|
||||||
|
fetch headers, bodies on demand
|
||||||
|
|
||||||
|
cache headers, bodies on disk
|
||||||
|
|
||||||
|
cache message information on disk across runs
|
||||||
|
|
||||||
|
body.jpg
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "a.h"
|
||||||
|
|
||||||
|
Imap *imap;
|
||||||
|
|
||||||
|
void
|
||||||
|
usage(void)
|
||||||
|
{
|
||||||
|
fprint(2, "usage: mailfs [-t] server\n");
|
||||||
|
threadexitsall("usage");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
threadmain(int argc, char **argv)
|
||||||
|
{
|
||||||
|
char *server;
|
||||||
|
int mode;
|
||||||
|
|
||||||
|
mode = Unencrypted;
|
||||||
|
ARGBEGIN{
|
||||||
|
default:
|
||||||
|
usage();
|
||||||
|
case 'D':
|
||||||
|
chatty9p++;
|
||||||
|
break;
|
||||||
|
case 'V':
|
||||||
|
chattyimap++;
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
mode = Tls;
|
||||||
|
break;
|
||||||
|
case 'x':
|
||||||
|
mode = Cmd;
|
||||||
|
break;
|
||||||
|
}ARGEND
|
||||||
|
|
||||||
|
quotefmtinstall();
|
||||||
|
fmtinstall('$', sxfmt);
|
||||||
|
|
||||||
|
if(argc != 1)
|
||||||
|
usage();
|
||||||
|
server = argv[0];
|
||||||
|
|
||||||
|
mailthreadinit();
|
||||||
|
boxinit();
|
||||||
|
fsinit0();
|
||||||
|
|
||||||
|
if((imap = imapconnect(server, mode)) == nil)
|
||||||
|
sysfatal("imapconnect: %r");
|
||||||
|
threadpostmountsrv(&fs, "mail", nil, 0);
|
||||||
|
}
|
||||||
|
|
||||||
68
src/cmd/upas/nfs/mbox.c
Normal file
68
src/cmd/upas/nfs/mbox.c
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
#include "a.h"
|
||||||
|
|
||||||
|
Mailbox *hash[123];
|
||||||
|
Mailbox **box;
|
||||||
|
uint nbox;
|
||||||
|
|
||||||
|
static void
|
||||||
|
markboxes(int mark)
|
||||||
|
{
|
||||||
|
Mailbox *b;
|
||||||
|
|
||||||
|
for(i=0; i<nbox; i++)
|
||||||
|
if(box[i])
|
||||||
|
box[i]->mark = mark;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sweepboxes(void)
|
||||||
|
{
|
||||||
|
Mailbox *b;
|
||||||
|
|
||||||
|
for(i=0; i<nbox; i++)
|
||||||
|
if(box[i] && box[i]->mark){
|
||||||
|
freembox(box[i]);
|
||||||
|
box[i] = nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Mailbox*
|
||||||
|
mboxbyname(char *name)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i=0; i<nbox; i++)
|
||||||
|
if(box[i] && strcmp(box[i]->name, name) == 0)
|
||||||
|
return box[i];
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Mailbox*
|
||||||
|
mboxbyid(int id)
|
||||||
|
{
|
||||||
|
if(id < 0 || id >= nbox)
|
||||||
|
return nil;
|
||||||
|
return box[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
static Mailbox*
|
||||||
|
mboxcreate(char *name)
|
||||||
|
{
|
||||||
|
Mailbox *b;
|
||||||
|
|
||||||
|
b = emalloc(sizeof *b);
|
||||||
|
b->name = estrdup(name);
|
||||||
|
if(nbox%64 == 0)
|
||||||
|
box = erealloc(box, (nbox+64)*sizeof box[0]);
|
||||||
|
box[nbox++] = b;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
mboxupdate(void)
|
||||||
|
{
|
||||||
|
markboxes();
|
||||||
|
if(imapcmd("LIST \"\" *") < 0)
|
||||||
|
return;
|
||||||
|
sweepboxes();
|
||||||
|
}
|
||||||
18
src/cmd/upas/nfs/mkfile
Normal file
18
src/cmd/upas/nfs/mkfile
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
<$PLAN9/src/mkhdr
|
||||||
|
|
||||||
|
TARG=mailfs
|
||||||
|
|
||||||
|
OFILES=\
|
||||||
|
box.$O\
|
||||||
|
decode.$O\
|
||||||
|
fs.$O\
|
||||||
|
imap.$O\
|
||||||
|
main.$O\
|
||||||
|
sx.$O\
|
||||||
|
thread.$O\
|
||||||
|
util.$O\
|
||||||
|
|
||||||
|
HFILES=a.h box.h imap.h sx.h
|
||||||
|
|
||||||
|
<$PLAN9/src/mkone
|
||||||
|
|
||||||
9
src/cmd/upas/nfs/msg.c
Normal file
9
src/cmd/upas/nfs/msg.c
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
#include "a.h"
|
||||||
|
|
||||||
|
something about a cache of msgs here
|
||||||
|
|
||||||
|
cache flushes optionally to disk
|
||||||
|
before being tossed out
|
||||||
|
|
||||||
|
reload from disk, then from server
|
||||||
|
|
||||||
217
src/cmd/upas/nfs/sx.c
Normal file
217
src/cmd/upas/nfs/sx.c
Normal file
|
|
@ -0,0 +1,217 @@
|
||||||
|
#include "a.h"
|
||||||
|
|
||||||
|
Sx *Brdsx1(Biobuf*);
|
||||||
|
|
||||||
|
Sx*
|
||||||
|
Brdsx(Biobuf *b)
|
||||||
|
{
|
||||||
|
Sx **sx, *x;
|
||||||
|
int nsx;
|
||||||
|
|
||||||
|
nsx = 0;
|
||||||
|
sx = nil;
|
||||||
|
while((x = Brdsx1(b)) != nil){
|
||||||
|
sx = erealloc(sx, (nsx+1)*sizeof sx[0]);
|
||||||
|
sx[nsx++] = x;
|
||||||
|
}
|
||||||
|
x = emalloc(sizeof *x);
|
||||||
|
x->sx = sx;
|
||||||
|
x->nsx = nsx;
|
||||||
|
x->type = SxList;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
sxwalk(Sx *sx)
|
||||||
|
{
|
||||||
|
int i, n;
|
||||||
|
|
||||||
|
if(sx == nil)
|
||||||
|
return 1;
|
||||||
|
switch(sx->type){
|
||||||
|
default:
|
||||||
|
case SxAtom:
|
||||||
|
case SxString:
|
||||||
|
case SxNumber:
|
||||||
|
return 1;
|
||||||
|
case SxList:
|
||||||
|
n = 0;
|
||||||
|
for(i=0; i<sx->nsx; i++)
|
||||||
|
n += sxwalk(sx->sx[i]);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
freesx(Sx *sx)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(sx == nil)
|
||||||
|
return;
|
||||||
|
switch(sx->type){
|
||||||
|
case SxAtom:
|
||||||
|
case SxString:
|
||||||
|
free(sx->data);
|
||||||
|
break;
|
||||||
|
case SxList:
|
||||||
|
for(i=0; i<sx->nsx; i++)
|
||||||
|
freesx(sx->sx[i]);
|
||||||
|
free(sx->sx);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
free(sx);
|
||||||
|
}
|
||||||
|
|
||||||
|
Sx*
|
||||||
|
Brdsx1(Biobuf *b)
|
||||||
|
{
|
||||||
|
int c, len, nbr;
|
||||||
|
char *s;
|
||||||
|
vlong n;
|
||||||
|
Sx *x;
|
||||||
|
|
||||||
|
c = Bgetc(b);
|
||||||
|
if(c == ' ')
|
||||||
|
c = Bgetc(b);
|
||||||
|
if(c < 0)
|
||||||
|
return nil;
|
||||||
|
if(c == '\r')
|
||||||
|
c = Bgetc(b);
|
||||||
|
if(c == '\n')
|
||||||
|
return nil;
|
||||||
|
if(c == ')'){ /* end of list */
|
||||||
|
Bungetc(b);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
if(c == '('){ /* parenthesized list */
|
||||||
|
x = Brdsx(b);
|
||||||
|
c = Bgetc(b);
|
||||||
|
if(c != ')') /* oops! not good */
|
||||||
|
Bungetc(b);
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
if(c == '{'){ /* length-prefixed string */
|
||||||
|
len = 0;
|
||||||
|
while((c = Bgetc(b)) >= 0 && isdigit(c))
|
||||||
|
len = len*10 + c-'0';
|
||||||
|
if(c != '}') /* oops! not good */
|
||||||
|
Bungetc(b);
|
||||||
|
c = Bgetc(b);
|
||||||
|
if(c != '\r') /* oops! not good */
|
||||||
|
;
|
||||||
|
c = Bgetc(b);
|
||||||
|
if(c != '\n') /* oops! not good */
|
||||||
|
;
|
||||||
|
x = emalloc(sizeof *x);
|
||||||
|
x->data = emalloc(len+1);
|
||||||
|
if(Bread(b, x->data, len) != len)
|
||||||
|
; /* oops! */
|
||||||
|
x->data[len] = 0;
|
||||||
|
x->ndata = len;
|
||||||
|
x->type = SxString;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
if(c == '"'){ /* quoted string */
|
||||||
|
s = nil;
|
||||||
|
len = 0;
|
||||||
|
while((c = Bgetc(b)) >= 0 && c != '"'){
|
||||||
|
if(c == '\\')
|
||||||
|
c = Bgetc(b);
|
||||||
|
s = erealloc(s, len+1);
|
||||||
|
s[len++] = c;
|
||||||
|
}
|
||||||
|
s = erealloc(s, len+1);
|
||||||
|
s[len] = 0;
|
||||||
|
x = emalloc(sizeof *x);
|
||||||
|
x->data = s;
|
||||||
|
x->ndata = len;
|
||||||
|
x->type = SxString;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
if(isdigit(c)){ /* number */
|
||||||
|
n = c-'0';;
|
||||||
|
while((c = Bgetc(b)) >= 0 && isdigit(c))
|
||||||
|
n = n*10 + c-'0';
|
||||||
|
Bungetc(b);
|
||||||
|
x = emalloc(sizeof *x);
|
||||||
|
x->number = n;
|
||||||
|
x->type = SxNumber;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
/* atom */
|
||||||
|
len = 1;
|
||||||
|
s = emalloc(1);
|
||||||
|
s[0] = c;
|
||||||
|
nbr = 0;
|
||||||
|
while((c = Bgetc(b)) >= 0 && c > ' ' && !strchr("(){}", c)){
|
||||||
|
/* allow embedded brackets as in BODY[] */
|
||||||
|
if(c == '['){
|
||||||
|
if(s[0] == '[')
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
nbr++;
|
||||||
|
}
|
||||||
|
if(c == ']'){
|
||||||
|
if(nbr > 0)
|
||||||
|
nbr--;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
s = erealloc(s, len+1);
|
||||||
|
s[len++] = c;
|
||||||
|
}
|
||||||
|
if(c != ' ')
|
||||||
|
Bungetc(b);
|
||||||
|
s = erealloc(s, len+1);
|
||||||
|
s[len] = 0;
|
||||||
|
x = emalloc(sizeof *x);
|
||||||
|
x->type = SxAtom;
|
||||||
|
x->data = s;
|
||||||
|
x->ndata = len;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
sxfmt(Fmt *fmt)
|
||||||
|
{
|
||||||
|
int i, paren;
|
||||||
|
Sx *sx;
|
||||||
|
|
||||||
|
sx = va_arg(fmt->args, Sx*);
|
||||||
|
if(sx == nil)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
switch(sx->type){
|
||||||
|
case SxAtom:
|
||||||
|
case SxString:
|
||||||
|
return fmtprint(fmt, "%q", sx->data);
|
||||||
|
|
||||||
|
case SxNumber:
|
||||||
|
return fmtprint(fmt, "%lld", sx->number);
|
||||||
|
|
||||||
|
case SxList:
|
||||||
|
paren = !(fmt->flags&FmtSharp);
|
||||||
|
if(paren)
|
||||||
|
fmtrune(fmt, '(');
|
||||||
|
for(i=0; i<sx->nsx; i++){
|
||||||
|
if(i)
|
||||||
|
fmtrune(fmt, ' ');
|
||||||
|
fmtprint(fmt, "%$", sx->sx[i]);
|
||||||
|
}
|
||||||
|
if(paren)
|
||||||
|
return fmtrune(fmt, ')');
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmtstrcpy(fmt, "?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
oksx(Sx *sx)
|
||||||
|
{
|
||||||
|
return sx->nsx >= 2
|
||||||
|
&& sx->sx[1]->type == SxAtom
|
||||||
|
&& cistrcmp(sx->sx[1]->data, "OK") == 0;
|
||||||
|
}
|
||||||
31
src/cmd/upas/nfs/sx.h
Normal file
31
src/cmd/upas/nfs/sx.h
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* S-expressions as used by IMAP.
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
SxUnknown = 0,
|
||||||
|
SxAtom,
|
||||||
|
SxString,
|
||||||
|
SxNumber,
|
||||||
|
SxList,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct Sx Sx;
|
||||||
|
struct Sx
|
||||||
|
{
|
||||||
|
int type;
|
||||||
|
char *data;
|
||||||
|
int ndata;
|
||||||
|
vlong number;
|
||||||
|
Sx **sx;
|
||||||
|
int nsx;
|
||||||
|
};
|
||||||
|
|
||||||
|
Sx* Brdsx(Biobuf*);
|
||||||
|
Sx* Brdsx1(Biobuf*);
|
||||||
|
void freesx(Sx*);
|
||||||
|
int oksx(Sx*);
|
||||||
|
int sxfmt(Fmt*);
|
||||||
|
int sxwalk(Sx*);
|
||||||
|
|
||||||
37
src/cmd/upas/nfs/thread.c
Normal file
37
src/cmd/upas/nfs/thread.c
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
#include "a.h"
|
||||||
|
|
||||||
|
typedef struct New New;
|
||||||
|
struct New
|
||||||
|
{
|
||||||
|
void (*fn)(void*);
|
||||||
|
void *arg;
|
||||||
|
};
|
||||||
|
|
||||||
|
Channel *mailthreadchan;
|
||||||
|
|
||||||
|
void
|
||||||
|
mailthread(void (*fn)(void*), void *arg)
|
||||||
|
{
|
||||||
|
New n;
|
||||||
|
|
||||||
|
n.fn = fn;
|
||||||
|
n.arg = arg;
|
||||||
|
send(mailthreadchan, &n);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
mailproc(void *v)
|
||||||
|
{
|
||||||
|
New n;
|
||||||
|
|
||||||
|
while(recv(mailthreadchan, &n) == 1)
|
||||||
|
threadcreate(n.fn, n.arg, STACK);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
mailthreadinit(void)
|
||||||
|
{
|
||||||
|
mailthreadchan = chancreate(sizeof(New), 0);
|
||||||
|
proccreate(mailproc, nil, STACK);
|
||||||
|
}
|
||||||
|
|
||||||
13
src/cmd/upas/nfs/util.c
Normal file
13
src/cmd/upas/nfs/util.c
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
#include "a.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
warn(char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list arg;
|
||||||
|
|
||||||
|
va_start(arg, fmt);
|
||||||
|
fprint(2, "warning: ");
|
||||||
|
vfprint(2, fmt, arg);
|
||||||
|
fprint(2, "\n");
|
||||||
|
va_end(arg);
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue