diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/nntpfs.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/nntpfs.c')
-rwxr-xr-x | sys/src/cmd/nntpfs.c | 1102 |
1 files changed, 1102 insertions, 0 deletions
diff --git a/sys/src/cmd/nntpfs.c b/sys/src/cmd/nntpfs.c new file mode 100755 index 000000000..442524d7f --- /dev/null +++ b/sys/src/cmd/nntpfs.c @@ -0,0 +1,1102 @@ +/* + * Network news transport protocol (NNTP) file server. + * + * Unfortunately, the file system differs from that expected + * by Charles Forsyth's rin news reader. This is partially out + * of my own laziness, but it makes the bookkeeping here + * a lot easier. + */ + +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <auth.h> +#include <fcall.h> +#include <thread.h> +#include <9p.h> + +typedef struct Netbuf Netbuf; +typedef struct Group Group; + +struct Netbuf { + Biobuf br; + Biobuf bw; + int lineno; + int fd; + int code; /* last response code */ + int auth; /* Authorization required? */ + char response[128]; /* last response */ + Group *currentgroup; + char *addr; + char *user; + char *pass; + ulong extended; /* supported extensions */ +}; + +struct Group { + char *name; + Group *parent; + Group **kid; + int num; + int nkid; + int lo, hi; + int canpost; + int isgroup; /* might just be piece of hierarchy */ + ulong mtime; + ulong atime; +}; + +/* + * First eight fields are, in order: + * article number, subject, author, date, message-ID, + * references, byte count, line count + * We don't support OVERVIEW.FMT; when I see a server with more + * interesting fields, I'll implement support then. In the meantime, + * the standard defines the first eight fields. + */ + +/* Extensions */ +enum { + Nxover = (1<<0), + Nxhdr = (1<<1), + Nxpat = (1<<2), + Nxlistgp = (1<<3), +}; + +Group *root; +Netbuf *net; +ulong now; +int netdebug; +int readonly; + +void* +erealloc(void *v, ulong n) +{ + v = realloc(v, n); + if(v == nil) + sysfatal("out of memory reallocating %lud", n); + setmalloctag(v, getcallerpc(&v)); + return v; +} + +void* +emalloc(ulong n) +{ + void *v; + + v = malloc(n); + if(v == nil) + sysfatal("out of memory allocating %lud", n); + memset(v, 0, n); + setmalloctag(v, getcallerpc(&n)); + return v; +} + +char* +estrdup(char *s) +{ + int l; + char *t; + + if (s == nil) + return nil; + l = strlen(s)+1; + t = emalloc(l); + memcpy(t, s, l); + setmalloctag(t, getcallerpc(&s)); + return t; +} + +char* +estrdupn(char *s, int n) +{ + int l; + char *t; + + l = strlen(s); + if(l > n) + l = n; + t = emalloc(l+1); + memmove(t, s, l); + t[l] = '\0'; + setmalloctag(t, getcallerpc(&s)); + return t; +} + +char* +Nrdline(Netbuf *n) +{ + char *p; + int l; + + n->lineno++; + Bflush(&n->bw); + if((p = Brdline(&n->br, '\n')) == nil){ + werrstr("nntp eof"); + return nil; + } + p[l=Blinelen(&n->br)-1] = '\0'; + if(l > 0 && p[l-1] == '\r') + p[l-1] = '\0'; +if(netdebug) + fprint(2, "-> %s\n", p); + return p; +} + +int +nntpresponse(Netbuf *n, int e, char *cmd) +{ + int r; + char *p; + + for(;;){ + p = Nrdline(n); + if(p==nil){ + strcpy(n->response, "early nntp eof"); + return -1; + } + r = atoi(p); + if(r/100 == 1){ /* BUG? */ + fprint(2, "%s\n", p); + continue; + } + break; + } + + strecpy(n->response, n->response+sizeof(n->response), p); + + if((r=atoi(p)) == 0){ + close(n->fd); + n->fd = -1; + fprint(2, "bad nntp response: %s\n", p); + werrstr("bad nntp response"); + return -1; + } + + n->code = r; + if(0 < e && e<10 && r/100 != e){ + fprint(2, "%s: expected %dxx: got %s\n", cmd, e, n->response); + return -1; + } + if(10 <= e && e<100 && r/10 != e){ + fprint(2, "%s: expected %dx: got %s\n", cmd, e, n->response); + return -1; + } + if(100 <= e && r != e){ + fprint(2, "%s: expected %d: got %s\n", cmd, e, n->response); + return -1; + } + return r; +} + +int nntpauth(Netbuf*); +int nntpxcmdprobe(Netbuf*); +int nntpcurrentgroup(Netbuf*, Group*); + +/* XXX: bug OVER/XOVER et al. */ +static struct { + ulong n; + char *s; +} extensions [] = { + { Nxover, "OVER" }, + { Nxhdr, "HDR" }, + { Nxpat, "PAT" }, + { Nxlistgp, "LISTGROUP" }, + { 0, nil } +}; + +static int indial; + +int +nntpconnect(Netbuf *n) +{ + n->currentgroup = nil; + close(n->fd); + if((n->fd = dial(n->addr, nil, nil, nil)) < 0){ + snprint(n->response, sizeof n->response, "dial: %r"); + return -1; + } + Binit(&n->br, n->fd, OREAD); + Binit(&n->bw, n->fd, OWRITE); + if(nntpresponse(n, 20, "greeting") < 0) + return -1; + readonly = (n->code == 201); + + indial = 1; + if(n->auth != 0) + nntpauth(n); +// nntpxcmdprobe(n); + indial = 0; + return 0; +} + +int +nntpcmd(Netbuf *n, char *cmd, int e) +{ + int tried; + + tried = 0; + for(;;){ + if(netdebug) + fprint(2, "<- %s\n", cmd); + Bprint(&n->bw, "%s\r\n", cmd); + if(nntpresponse(n, e, cmd)>=0 && (e < 0 || n->code/100 != 5)) + return 0; + + /* redial */ + if(indial || tried++ || nntpconnect(n) < 0) + return -1; + } +} + +int +nntpauth(Netbuf *n) +{ + char cmd[256]; + + snprint(cmd, sizeof cmd, "AUTHINFO USER %s", n->user); + if (nntpcmd(n, cmd, -1) < 0 || n->code != 381) { + fprint(2, "Authentication failed: %s\n", n->response); + return -1; + } + + snprint(cmd, sizeof cmd, "AUTHINFO PASS %s", n->pass); + if (nntpcmd(n, cmd, -1) < 0 || n->code != 281) { + fprint(2, "Authentication failed: %s\n", n->response); + return -1; + } + + return 0; +} + +int +nntpxcmdprobe(Netbuf *n) +{ + int i; + char *p; + + n->extended = 0; + if (nntpcmd(n, "LIST EXTENSIONS", 0) < 0 || n->code != 202) + return 0; + + while((p = Nrdline(n)) != nil) { + if (strcmp(p, ".") == 0) + break; + + for(i=0; extensions[i].s != nil; i++) + if (cistrcmp(extensions[i].s, p) == 0) { + n->extended |= extensions[i].n; + break; + } + } + return 0; +} + +/* XXX: searching, lazy evaluation */ +static int +overcmp(void *v1, void *v2) +{ + int a, b; + + a = atoi(*(char**)v1); + b = atoi(*(char**)v2); + + if(a < b) + return -1; + else if(a > b) + return 1; + return 0; +} + +enum { + XoverChunk = 100, +}; + +char *xover[XoverChunk]; +int xoverlo; +int xoverhi; +int xovercount; +Group *xovergroup; + +char* +nntpover(Netbuf *n, Group *g, int m) +{ + int i, lo, hi, mid, msg; + char *p; + char cmd[64]; + + if (g->isgroup == 0) /* BUG: should check extension capabilities */ + return nil; + + if(g != xovergroup || m < xoverlo || m >= xoverhi){ + lo = (m/XoverChunk)*XoverChunk; + hi = lo+XoverChunk; + + if(lo < g->lo) + lo = g->lo; + else if (lo > g->hi) + lo = g->hi; + if(hi < lo || hi > g->hi) + hi = g->hi; + + if(nntpcurrentgroup(n, g) < 0) + return nil; + + if(lo == hi) + snprint(cmd, sizeof cmd, "XOVER %d", hi); + else + snprint(cmd, sizeof cmd, "XOVER %d-%d", lo, hi-1); + if(nntpcmd(n, cmd, 224) < 0) + return nil; + + for(i=0; (p = Nrdline(n)) != nil; i++) { + if(strcmp(p, ".") == 0) + break; + if(i >= XoverChunk) + sysfatal("news server doesn't play by the rules"); + free(xover[i]); + xover[i] = emalloc(strlen(p)+2); + strcpy(xover[i], p); + strcat(xover[i], "\n"); + } + qsort(xover, i, sizeof(xover[0]), overcmp); + + xovercount = i; + + xovergroup = g; + xoverlo = lo; + xoverhi = hi; + } + + lo = 0; + hi = xovercount; + /* search for message */ + while(lo < hi){ + mid = (lo+hi)/2; + msg = atoi(xover[mid]); + if(m == msg) + return xover[mid]; + else if(m < msg) + hi = mid; + else + lo = mid+1; + } + return nil; +} + +/* + * Return the new Group structure for the group name. + * Destroys name. + */ +static int printgroup(char*,Group*); +Group* +findgroup(Group *g, char *name, int mk) +{ + int lo, hi, m; + char *p, *q; + static int ngroup; + + for(p=name; *p; p=q){ + if(q = strchr(p, '.')) + *q++ = '\0'; + else + q = p+strlen(p); + + lo = 0; + hi = g->nkid; + while(hi-lo > 1){ + m = (lo+hi)/2; + if(strcmp(p, g->kid[m]->name) < 0) + hi = m; + else + lo = m; + } + assert(lo==hi || lo==hi-1); + if(lo==hi || strcmp(p, g->kid[lo]->name) != 0){ + if(mk==0) + return nil; + if(g->nkid%16 == 0) + g->kid = erealloc(g->kid, (g->nkid+16)*sizeof(g->kid[0])); + + /* + * if we're down to a single place 'twixt lo and hi, the insertion might need + * to go at lo or at hi. strcmp to find out. the list needs to stay sorted. + */ + if(lo==hi-1 && strcmp(p, g->kid[lo]->name) < 0) + hi = lo; + + if(hi < g->nkid) + memmove(g->kid+hi+1, g->kid+hi, sizeof(g->kid[0])*(g->nkid-hi)); + g->nkid++; + g->kid[hi] = emalloc(sizeof(*g)); + g->kid[hi]->parent = g; + g = g->kid[hi]; + g->name = estrdup(p); + g->num = ++ngroup; + g->mtime = time(0); + }else + g = g->kid[lo]; + } + if(mk) + g->isgroup = 1; + return g; +} + +static int +printgroup(char *s, Group *g) +{ + if(g->parent == g) + return 0; + + if(printgroup(s, g->parent)) + strcat(s, "."); + strcat(s, g->name); + return 1; +} + +static char* +Nreaddata(Netbuf *n) +{ + char *p, *q; + int l; + + p = nil; + l = 0; + for(;;){ + q = Nrdline(n); + if(q==nil){ + free(p); + return nil; + } + if(strcmp(q, ".")==0) + return p; + if(q[0]=='.') + q++; + p = erealloc(p, l+strlen(q)+1+1); + strcpy(p+l, q); + strcat(p+l, "\n"); + l += strlen(p+l); + } +} + +/* + * Return the output of a HEAD, BODY, or ARTICLE command. + */ +char* +nntpget(Netbuf *n, Group *g, int msg, char *retr) +{ + char *s; + char cmd[1024]; + + if(g->isgroup == 0){ + werrstr("not a group"); + return nil; + } + + if(strcmp(retr, "XOVER") == 0){ + s = nntpover(n, g, msg); + if(s == nil) + s = ""; + return estrdup(s); + } + + if(nntpcurrentgroup(n, g) < 0) + return nil; + sprint(cmd, "%s %d", retr, msg); + nntpcmd(n, cmd, 0); + if(n->code/10 != 22) + return nil; + + return Nreaddata(n); +} + +int +nntpcurrentgroup(Netbuf *n, Group *g) +{ + char cmd[1024]; + + if(n->currentgroup != g){ + strcpy(cmd, "GROUP "); + printgroup(cmd, g); + if(nntpcmd(n, cmd, 21) < 0) + return -1; + n->currentgroup = g; + } + return 0; +} + +void +nntprefreshall(Netbuf *n) +{ + char *f[10], *p; + int hi, lo, nf; + Group *g; + + if(nntpcmd(n, "LIST", 21) < 0) + return; + + while(p = Nrdline(n)){ + if(strcmp(p, ".")==0) + break; + + nf = getfields(p, f, nelem(f), 1, "\t\r\n "); + if(nf != 4){ + int i; + for(i=0; i<nf; i++) + fprint(2, "%s%s", i?" ":"", f[i]); + fprint(2, "\n"); + fprint(2, "syntax error in group list, line %d", n->lineno); + return; + } + g = findgroup(root, f[0], 1); + hi = strtol(f[1], 0, 10)+1; + lo = strtol(f[2], 0, 10); + if(g->hi != hi){ + g->hi = hi; + if(g->lo==0) + g->lo = lo; + g->canpost = f[3][0] == 'y'; + g->mtime = time(0); + } + } +} + +void +nntprefresh(Netbuf *n, Group *g) +{ + char cmd[1024]; + char *f[5]; + int lo, hi; + + if(g->isgroup==0) + return; + + if(time(0) - g->atime < 30) + return; + + strcpy(cmd, "GROUP "); + printgroup(cmd, g); + if(nntpcmd(n, cmd, 21) < 0){ + n->currentgroup = nil; + return; + } + n->currentgroup = g; + + if(tokenize(n->response, f, nelem(f)) < 4){ + fprint(2, "error reading GROUP response"); + return; + } + + /* backwards from LIST! */ + hi = strtol(f[3], 0, 10)+1; + lo = strtol(f[2], 0, 10); + if(g->hi != hi){ + g->mtime = time(0); + if(g->lo==0) + g->lo = lo; + g->hi = hi; + } + g->atime = time(0); +} + +char* +nntppost(Netbuf *n, char *msg) +{ + char *p, *q; + + if(nntpcmd(n, "POST", 34) < 0) + return n->response; + + for(p=msg; *p; p=q){ + if(q = strchr(p, '\n')) + *q++ = '\0'; + else + q = p+strlen(p); + + if(p[0]=='.') + Bputc(&n->bw, '.'); + Bwrite(&n->bw, p, strlen(p)); + Bputc(&n->bw, '\r'); + Bputc(&n->bw, '\n'); + } + Bprint(&n->bw, ".\r\n"); + + if(nntpresponse(n, 0, nil) < 0) + return n->response; + + if(n->code/100 != 2) + return n->response; + return nil; +} + +/* + * Because an expanded QID space makes thngs much easier, + * we sleazily use the version part of the QID as more path bits. + * Since we make sure not to mount ourselves cached, this + * doesn't break anything (unless you want to bind on top of + * things in this file system). In the next version of 9P, we'll + * have more QID bits to play with. + * + * The newsgroup is encoded in the top 15 bits + * of the path. The message number is the bottom 17 bits. + * The file within the message directory is in the version [sic]. + */ + +enum { /* file qids */ + Qhead, + Qbody, + Qarticle, + Qxover, + Nfile, +}; +char *filename[] = { + "header", + "body", + "article", + "xover", +}; +char *nntpname[] = { + "HEAD", + "BODY", + "ARTICLE", + "XOVER", +}; + +#define GROUP(p) (((p)>>17)&0x3FFF) +#define MESSAGE(p) ((p)&0x1FFFF) +#define FILE(v) ((v)&0x3) + +#define PATH(g,m) ((((g)&0x3FFF)<<17)|((m)&0x1FFFF)) +#define POST(g) PATH(0,g,0) +#define VERS(f) ((f)&0x3) + +typedef struct Aux Aux; +struct Aux { + Group *g; + int n; + int ispost; + int file; + char *s; + int ns; + int offset; +}; + +static void +fsattach(Req *r) +{ + Aux *a; + char *spec; + + spec = r->ifcall.aname; + if(spec && spec[0]){ + respond(r, "invalid attach specifier"); + return; + } + + a = emalloc(sizeof *a); + a->g = root; + a->n = -1; + r->fid->aux = a; + + r->ofcall.qid = (Qid){0, 0, QTDIR}; + r->fid->qid = r->ofcall.qid; + respond(r, nil); +} + +static char* +fsclone(Fid *ofid, Fid *fid) +{ + Aux *a; + + a = emalloc(sizeof(*a)); + *a = *(Aux*)ofid->aux; + fid->aux = a; + return nil; +} + +static char* +fswalk1(Fid *fid, char *name, Qid *qid) +{ + char *p; + int i, isdotdot, n; + Aux *a; + Group *ng; + + isdotdot = strcmp(name, "..")==0; + + a = fid->aux; + if(a->s) /* file */ + return "protocol botch"; + if(a->n != -1){ + if(isdotdot){ + *qid = (Qid){PATH(a->g->num, 0), 0, QTDIR}; + fid->qid = *qid; + a->n = -1; + return nil; + } + for(i=0; i<Nfile; i++){ + if(strcmp(name, filename[i])==0){ + if(a->s = nntpget(net, a->g, a->n, nntpname[i])){ + *qid = (Qid){PATH(a->g->num, a->n), Qbody, 0}; + fid->qid = *qid; + a->file = i; + return nil; + }else + return "file does not exist"; + } + } + return "file does not exist"; + } + + if(isdotdot){ + a->g = a->g->parent; + *qid = (Qid){PATH(a->g->num, 0), 0, QTDIR}; + fid->qid = *qid; + return nil; + } + + if(a->g->isgroup && !readonly && a->g->canpost + && strcmp(name, "post")==0){ + a->ispost = 1; + *qid = (Qid){PATH(a->g->num, 0), 0, 0}; + fid->qid = *qid; + return nil; + } + + if(ng = findgroup(a->g, name, 0)){ + a->g = ng; + *qid = (Qid){PATH(a->g->num, 0), 0, QTDIR}; + fid->qid = *qid; + return nil; + } + + n = strtoul(name, &p, 0); + if('0'<=name[0] && name[0]<='9' && *p=='\0' && a->g->lo<=n && n<a->g->hi){ + a->n = n; + *qid = (Qid){PATH(a->g->num, n+1-a->g->lo), 0, QTDIR}; + fid->qid = *qid; + return nil; + } + + return "file does not exist"; +} + +static void +fsopen(Req *r) +{ + Aux *a; + + a = r->fid->aux; + if((a->ispost && (r->ifcall.mode&~OTRUNC) != OWRITE) + || (!a->ispost && r->ifcall.mode != OREAD)) + respond(r, "permission denied"); + else + respond(r, nil); +} + +static void +fillstat(Dir *d, Aux *a) +{ + char buf[32]; + Group *g; + + memset(d, 0, sizeof *d); + d->uid = estrdup("nntp"); + d->gid = estrdup("nntp"); + g = a->g; + d->atime = d->mtime = g->mtime; + + if(a->ispost){ + d->name = estrdup("post"); + d->mode = 0222; + d->qid = (Qid){PATH(g->num, 0), 0, 0}; + d->length = a->ns; + return; + } + + if(a->s){ /* article file */ + d->name = estrdup(filename[a->file]); + d->mode = 0444; + d->qid = (Qid){PATH(g->num, a->n+1-g->lo), a->file, 0}; + return; + } + + if(a->n != -1){ /* article directory */ + sprint(buf, "%d", a->n); + d->name = estrdup(buf); + d->mode = DMDIR|0555; + d->qid = (Qid){PATH(g->num, a->n+1-g->lo), 0, QTDIR}; + return; + } + + /* group directory */ + if(g->name[0]) + d->name = estrdup(g->name); + else + d->name = estrdup("/"); + d->mode = DMDIR|0555; + d->qid = (Qid){PATH(g->num, 0), g->hi-1, QTDIR}; +} + +static int +dirfillstat(Dir *d, Aux *a, int i) +{ + int ndir; + Group *g; + char buf[32]; + + memset(d, 0, sizeof *d); + d->uid = estrdup("nntp"); + d->gid = estrdup("nntp"); + + g = a->g; + d->atime = d->mtime = g->mtime; + + if(a->n != -1){ /* article directory */ + if(i >= Nfile) + return -1; + + d->name = estrdup(filename[i]); + d->mode = 0444; + d->qid = (Qid){PATH(g->num, a->n), i, 0}; + return 0; + } + + /* hierarchy directory: child groups */ + if(i < g->nkid){ + d->name = estrdup(g->kid[i]->name); + d->mode = DMDIR|0555; + d->qid = (Qid){PATH(g->kid[i]->num, 0), g->kid[i]->hi-1, QTDIR}; + return 0; + } + i -= g->nkid; + + /* group directory: post file */ + if(g->isgroup && !readonly && g->canpost){ + if(i < 1){ + d->name = estrdup("post"); + d->mode = 0222; + d->qid = (Qid){PATH(g->num, 0), 0, 0}; + return 0; + } + i--; + } + + /* group directory: child articles */ + ndir = g->hi - g->lo; + if(i < ndir){ + sprint(buf, "%d", g->lo+i); + d->name = estrdup(buf); + d->mode = DMDIR|0555; + d->qid = (Qid){PATH(g->num, i+1), 0, QTDIR}; + return 0; + } + + return -1; +} + +static void +fsstat(Req *r) +{ + Aux *a; + + a = r->fid->aux; + if(r->fid->qid.path == 0 && (r->fid->qid.type & QTDIR)) + nntprefreshall(net); + else if(a->g->isgroup) + nntprefresh(net, a->g); + fillstat(&r->d, a); + respond(r, nil); +} + +static void +fsread(Req *r) +{ + int offset, n; + Aux *a; + char *p, *ep; + Dir d; + + a = r->fid->aux; + if(a->s){ + readstr(r, a->s); + respond(r, nil); + return; + } + + if(r->ifcall.offset == 0) + offset = 0; + else + offset = a->offset; + + p = r->ofcall.data; + ep = r->ofcall.data+r->ifcall.count; + for(; p+2 < ep; p += n){ + if(dirfillstat(&d, a, offset) < 0) + break; + n=convD2M(&d, (uchar*)p, ep-p); + free(d.name); + free(d.uid); + free(d.gid); + free(d.muid); + if(n <= BIT16SZ) + break; + offset++; + } + a->offset = offset; + r->ofcall.count = p - r->ofcall.data; + respond(r, nil); +} + +static void +fswrite(Req *r) +{ + Aux *a; + long count; + vlong offset; + + a = r->fid->aux; + + if(r->ifcall.count == 0){ /* commit */ + respond(r, nntppost(net, a->s)); + free(a->s); + a->ns = 0; + a->s = nil; + return; + } + + count = r->ifcall.count; + offset = r->ifcall.offset; + if(a->ns < count+offset+1){ + a->s = erealloc(a->s, count+offset+1); + a->ns = count+offset; + a->s[a->ns] = '\0'; + } + memmove(a->s+offset, r->ifcall.data, count); + r->ofcall.count = count; + respond(r, nil); +} + +static void +fsdestroyfid(Fid *fid) +{ + Aux *a; + + a = fid->aux; + if(a==nil) + return; + + if(a->ispost && a->s) + nntppost(net, a->s); + + free(a->s); + free(a); +} + +Srv nntpsrv = { +.destroyfid= fsdestroyfid, +.attach= fsattach, +.clone= fsclone, +.walk1= fswalk1, +.open= fsopen, +.read= fsread, +.write= fswrite, +.stat= fsstat, +}; + +void +usage(void) +{ + fprint(2, "usage: nntpsrv [-a] [-s service] [-m mtpt] [nntp.server]\n"); + exits("usage"); +} + +void +dumpgroups(Group *g, int ind) +{ + int i; + + print("%*s%s\n", ind*4, "", g->name); + for(i=0; i<g->nkid; i++) + dumpgroups(g->kid[i], ind+1); +} + +void +main(int argc, char **argv) +{ + int auth, x; + char *mtpt, *service, *where, *user; + Netbuf n; + UserPasswd *up; + + mtpt = "/mnt/news"; + service = nil; + memset(&n, 0, sizeof n); + user = nil; + auth = 0; + ARGBEGIN{ + case 'D': + chatty9p++; + break; + case 'N': + netdebug = 1; + break; + case 'a': + auth = 1; + break; + case 'u': + user = EARGF(usage()); + break; + case 's': + service = EARGF(usage()); + break; + case 'm': + mtpt = EARGF(usage()); + break; + default: + usage(); + }ARGEND + + if(argc > 1) + usage(); + if(argc==0) + where = "$nntp"; + else + where = argv[0]; + + now = time(0); + + net = &n; + if(auth) { + n.auth = 1; + if(user) + up = auth_getuserpasswd(auth_getkey, "proto=pass service=nntp server=%q user=%q", where, user); + else + up = auth_getuserpasswd(auth_getkey, "proto=pass service=nntp server=%q", where); + if(up == nil) + sysfatal("no password: %r"); + + n.user = up->user; + n.pass = up->passwd; + } + + n.addr = netmkaddr(where, "tcp", "nntp"); + + root = emalloc(sizeof *root); + root->name = estrdup(""); + root->parent = root; + + n.fd = -1; + if(nntpconnect(&n) < 0) + sysfatal("nntpconnect: %s", n.response); + + x=netdebug; + netdebug=0; + nntprefreshall(&n); + netdebug=x; +// dumpgroups(root, 0); + + postmountsrv(&nntpsrv, service, mtpt, MREPL); + exits(nil); +} + |