xref: /plan9/sys/src/cmd/nntpfs.c (revision abfa367da87d91ea019c9536c4052275cb55ac26)
180ee5cbfSDavid du Colombier /*
280ee5cbfSDavid du Colombier  * Network news transport protocol (NNTP) file server.
380ee5cbfSDavid du Colombier  *
480ee5cbfSDavid du Colombier  * Unfortunately, the file system differs from that expected
580ee5cbfSDavid du Colombier  * by Charles Forsyth's rin news reader.  This is partially out
680ee5cbfSDavid du Colombier  * of my own laziness, but it makes the bookkeeping here
780ee5cbfSDavid du Colombier  * a lot easier.
880ee5cbfSDavid du Colombier  */
980ee5cbfSDavid du Colombier 
1080ee5cbfSDavid du Colombier #include <u.h>
1180ee5cbfSDavid du Colombier #include <libc.h>
1280ee5cbfSDavid du Colombier #include <bio.h>
1380ee5cbfSDavid du Colombier #include <auth.h>
1480ee5cbfSDavid du Colombier #include <fcall.h>
1580ee5cbfSDavid du Colombier #include <thread.h>
1680ee5cbfSDavid du Colombier #include <9p.h>
1780ee5cbfSDavid du Colombier 
1880ee5cbfSDavid du Colombier typedef struct Netbuf Netbuf;
1980ee5cbfSDavid du Colombier typedef struct Group Group;
2080ee5cbfSDavid du Colombier 
2180ee5cbfSDavid du Colombier struct Netbuf {
2280ee5cbfSDavid du Colombier 	Biobuf br;
2380ee5cbfSDavid du Colombier 	Biobuf bw;
2480ee5cbfSDavid du Colombier 	int lineno;
2580ee5cbfSDavid du Colombier 	int fd;
2680ee5cbfSDavid du Colombier 	int code;			/* last response code */
279a747e4fSDavid du Colombier 	int auth;			/* Authorization required? */
2880ee5cbfSDavid du Colombier 	char response[128];	/* last response */
2980ee5cbfSDavid du Colombier 	Group *currentgroup;
3080ee5cbfSDavid du Colombier 	char *addr;
319a747e4fSDavid du Colombier 	char *user;
329a747e4fSDavid du Colombier 	char *pass;
339a747e4fSDavid du Colombier 	ulong extended;	/* supported extensions */
3480ee5cbfSDavid du Colombier };
3580ee5cbfSDavid du Colombier 
3680ee5cbfSDavid du Colombier struct Group {
3780ee5cbfSDavid du Colombier 	char *name;
3880ee5cbfSDavid du Colombier 	Group *parent;
3980ee5cbfSDavid du Colombier 	Group **kid;
4080ee5cbfSDavid du Colombier 	int num;
4180ee5cbfSDavid du Colombier 	int nkid;
4280ee5cbfSDavid du Colombier 	int lo, hi;
4380ee5cbfSDavid du Colombier 	int canpost;
4480ee5cbfSDavid du Colombier 	int isgroup;	/* might just be piece of hierarchy */
4580ee5cbfSDavid du Colombier 	ulong mtime;
469a747e4fSDavid du Colombier 	ulong atime;
479a747e4fSDavid du Colombier };
489a747e4fSDavid du Colombier 
499a747e4fSDavid du Colombier /*
509a747e4fSDavid du Colombier  * First eight fields are, in order:
519a747e4fSDavid du Colombier  *	article number, subject, author, date, message-ID,
529a747e4fSDavid du Colombier  *	references, byte count, line count
539a747e4fSDavid du Colombier  * We don't support OVERVIEW.FMT; when I see a server with more
549a747e4fSDavid du Colombier  * interesting fields, I'll implement support then.  In the meantime,
559a747e4fSDavid du Colombier  * the standard defines the first eight fields.
569a747e4fSDavid du Colombier  */
579a747e4fSDavid du Colombier 
589a747e4fSDavid du Colombier /* Extensions */
599a747e4fSDavid du Colombier enum {
609a747e4fSDavid du Colombier 	Nxover   = (1<<0),
619a747e4fSDavid du Colombier 	Nxhdr    = (1<<1),
629a747e4fSDavid du Colombier 	Nxpat    = (1<<2),
639a747e4fSDavid du Colombier 	Nxlistgp = (1<<3),
6480ee5cbfSDavid du Colombier };
6580ee5cbfSDavid du Colombier 
6680ee5cbfSDavid du Colombier Group *root;
6780ee5cbfSDavid du Colombier Netbuf *net;
6880ee5cbfSDavid du Colombier ulong now;
6980ee5cbfSDavid du Colombier int netdebug;
7080ee5cbfSDavid du Colombier int readonly;
7180ee5cbfSDavid du Colombier 
7280ee5cbfSDavid du Colombier void*
erealloc(void * v,ulong n)7380ee5cbfSDavid du Colombier erealloc(void *v, ulong n)
7480ee5cbfSDavid du Colombier {
7580ee5cbfSDavid du Colombier 	v = realloc(v, n);
7680ee5cbfSDavid du Colombier 	if(v == nil)
7780ee5cbfSDavid du Colombier 		sysfatal("out of memory reallocating %lud", n);
7880ee5cbfSDavid du Colombier 	setmalloctag(v, getcallerpc(&v));
7980ee5cbfSDavid du Colombier 	return v;
8080ee5cbfSDavid du Colombier }
8180ee5cbfSDavid du Colombier 
8280ee5cbfSDavid du Colombier void*
emalloc(ulong n)8380ee5cbfSDavid du Colombier emalloc(ulong n)
8480ee5cbfSDavid du Colombier {
8580ee5cbfSDavid du Colombier 	void *v;
8680ee5cbfSDavid du Colombier 
8780ee5cbfSDavid du Colombier 	v = malloc(n);
8880ee5cbfSDavid du Colombier 	if(v == nil)
8980ee5cbfSDavid du Colombier 		sysfatal("out of memory allocating %lud", n);
9080ee5cbfSDavid du Colombier 	memset(v, 0, n);
9180ee5cbfSDavid du Colombier 	setmalloctag(v, getcallerpc(&n));
9280ee5cbfSDavid du Colombier 	return v;
9380ee5cbfSDavid du Colombier }
9480ee5cbfSDavid du Colombier 
9580ee5cbfSDavid du Colombier char*
estrdup(char * s)9680ee5cbfSDavid du Colombier estrdup(char *s)
9780ee5cbfSDavid du Colombier {
9880ee5cbfSDavid du Colombier 	int l;
9980ee5cbfSDavid du Colombier 	char *t;
10080ee5cbfSDavid du Colombier 
10180ee5cbfSDavid du Colombier 	if (s == nil)
10280ee5cbfSDavid du Colombier 		return nil;
10380ee5cbfSDavid du Colombier 	l = strlen(s)+1;
10480ee5cbfSDavid du Colombier 	t = emalloc(l);
10580ee5cbfSDavid du Colombier 	memcpy(t, s, l);
10680ee5cbfSDavid du Colombier 	setmalloctag(t, getcallerpc(&s));
10780ee5cbfSDavid du Colombier 	return t;
10880ee5cbfSDavid du Colombier }
10980ee5cbfSDavid du Colombier 
11080ee5cbfSDavid du Colombier char*
estrdupn(char * s,int n)11180ee5cbfSDavid du Colombier estrdupn(char *s, int n)
11280ee5cbfSDavid du Colombier {
11380ee5cbfSDavid du Colombier 	int l;
11480ee5cbfSDavid du Colombier 	char *t;
11580ee5cbfSDavid du Colombier 
11680ee5cbfSDavid du Colombier 	l = strlen(s);
11780ee5cbfSDavid du Colombier 	if(l > n)
11880ee5cbfSDavid du Colombier 		l = n;
11980ee5cbfSDavid du Colombier 	t = emalloc(l+1);
12080ee5cbfSDavid du Colombier 	memmove(t, s, l);
12180ee5cbfSDavid du Colombier 	t[l] = '\0';
12280ee5cbfSDavid du Colombier 	setmalloctag(t, getcallerpc(&s));
12380ee5cbfSDavid du Colombier 	return t;
12480ee5cbfSDavid du Colombier }
12580ee5cbfSDavid du Colombier 
12680ee5cbfSDavid du Colombier char*
Nrdline(Netbuf * n)12780ee5cbfSDavid du Colombier Nrdline(Netbuf *n)
12880ee5cbfSDavid du Colombier {
12980ee5cbfSDavid du Colombier 	char *p;
13080ee5cbfSDavid du Colombier 	int l;
13180ee5cbfSDavid du Colombier 
13280ee5cbfSDavid du Colombier 	n->lineno++;
13380ee5cbfSDavid du Colombier 	Bflush(&n->bw);
13480ee5cbfSDavid du Colombier 	if((p = Brdline(&n->br, '\n')) == nil){
13580ee5cbfSDavid du Colombier 		werrstr("nntp eof");
13680ee5cbfSDavid du Colombier 		return nil;
13780ee5cbfSDavid du Colombier 	}
13880ee5cbfSDavid du Colombier 	p[l=Blinelen(&n->br)-1] = '\0';
13980ee5cbfSDavid du Colombier 	if(l > 0 && p[l-1] == '\r')
14080ee5cbfSDavid du Colombier 		p[l-1] = '\0';
14180ee5cbfSDavid du Colombier if(netdebug)
14280ee5cbfSDavid du Colombier 	fprint(2, "-> %s\n", p);
14380ee5cbfSDavid du Colombier 	return p;
14480ee5cbfSDavid du Colombier }
14580ee5cbfSDavid du Colombier 
14680ee5cbfSDavid du Colombier int
nntpresponse(Netbuf * n,int e,char * cmd)14780ee5cbfSDavid du Colombier nntpresponse(Netbuf *n, int e, char *cmd)
14880ee5cbfSDavid du Colombier {
14980ee5cbfSDavid du Colombier 	int r;
15080ee5cbfSDavid du Colombier 	char *p;
15180ee5cbfSDavid du Colombier 
15280ee5cbfSDavid du Colombier 	for(;;){
15380ee5cbfSDavid du Colombier 		p = Nrdline(n);
15480ee5cbfSDavid du Colombier 		if(p==nil){
15580ee5cbfSDavid du Colombier 			strcpy(n->response, "early nntp eof");
15680ee5cbfSDavid du Colombier 			return -1;
15780ee5cbfSDavid du Colombier 		}
15880ee5cbfSDavid du Colombier 		r = atoi(p);
15980ee5cbfSDavid du Colombier 		if(r/100 == 1){	/* BUG? */
16080ee5cbfSDavid du Colombier 			fprint(2, "%s\n", p);
16180ee5cbfSDavid du Colombier 			continue;
16280ee5cbfSDavid du Colombier 		}
16380ee5cbfSDavid du Colombier 		break;
16480ee5cbfSDavid du Colombier 	}
16580ee5cbfSDavid du Colombier 
16680ee5cbfSDavid du Colombier 	strecpy(n->response, n->response+sizeof(n->response), p);
16780ee5cbfSDavid du Colombier 
16880ee5cbfSDavid du Colombier 	if((r=atoi(p)) == 0){
16980ee5cbfSDavid du Colombier 		close(n->fd);
17080ee5cbfSDavid du Colombier 		n->fd = -1;
17180ee5cbfSDavid du Colombier 		fprint(2, "bad nntp response: %s\n", p);
17280ee5cbfSDavid du Colombier 		werrstr("bad nntp response");
17380ee5cbfSDavid du Colombier 		return -1;
17480ee5cbfSDavid du Colombier 	}
17580ee5cbfSDavid du Colombier 
17680ee5cbfSDavid du Colombier 	n->code = r;
17780ee5cbfSDavid du Colombier 	if(0 < e && e<10 && r/100 != e){
17880ee5cbfSDavid du Colombier 		fprint(2, "%s: expected %dxx: got %s\n", cmd, e, n->response);
17980ee5cbfSDavid du Colombier 		return -1;
18080ee5cbfSDavid du Colombier 	}
18180ee5cbfSDavid du Colombier 	if(10 <= e && e<100 && r/10 != e){
18280ee5cbfSDavid du Colombier 		fprint(2, "%s: expected %dx: got %s\n", cmd, e, n->response);
18380ee5cbfSDavid du Colombier 		return -1;
18480ee5cbfSDavid du Colombier 	}
18580ee5cbfSDavid du Colombier 	if(100 <= e && r != e){
18680ee5cbfSDavid du Colombier 		fprint(2, "%s: expected %d: got %s\n", cmd, e, n->response);
18780ee5cbfSDavid du Colombier 		return -1;
18880ee5cbfSDavid du Colombier 	}
18980ee5cbfSDavid du Colombier 	return r;
19080ee5cbfSDavid du Colombier }
19180ee5cbfSDavid du Colombier 
1929a747e4fSDavid du Colombier int nntpauth(Netbuf*);
1939a747e4fSDavid du Colombier int nntpxcmdprobe(Netbuf*);
1949a747e4fSDavid du Colombier int nntpcurrentgroup(Netbuf*, Group*);
1959a747e4fSDavid du Colombier 
1969a747e4fSDavid du Colombier /* XXX: bug OVER/XOVER et al. */
1979a747e4fSDavid du Colombier static struct {
1989a747e4fSDavid du Colombier 	ulong n;
1999a747e4fSDavid du Colombier 	char *s;
2009a747e4fSDavid du Colombier } extensions [] = {
2019a747e4fSDavid du Colombier 	{ Nxover, "OVER" },
2029a747e4fSDavid du Colombier 	{ Nxhdr, "HDR" },
2039a747e4fSDavid du Colombier 	{ Nxpat, "PAT" },
2049a747e4fSDavid du Colombier 	{ Nxlistgp, "LISTGROUP" },
2059a747e4fSDavid du Colombier 	{ 0, nil }
2069a747e4fSDavid du Colombier };
2079a747e4fSDavid du Colombier 
2089a747e4fSDavid du Colombier static int indial;
2099a747e4fSDavid du Colombier 
21080ee5cbfSDavid du Colombier int
nntpconnect(Netbuf * n)2119a747e4fSDavid du Colombier nntpconnect(Netbuf *n)
21280ee5cbfSDavid du Colombier {
21380ee5cbfSDavid du Colombier 	n->currentgroup = nil;
21480ee5cbfSDavid du Colombier 	close(n->fd);
21580ee5cbfSDavid du Colombier 	if((n->fd = dial(n->addr, nil, nil, nil)) < 0){
216*abfa367dSDavid du Colombier 		snprint(n->response, sizeof n->response, "dial %s: %r", n->addr);
21780ee5cbfSDavid du Colombier 		return -1;
21880ee5cbfSDavid du Colombier 	}
21980ee5cbfSDavid du Colombier 	Binit(&n->br, n->fd, OREAD);
22080ee5cbfSDavid du Colombier 	Binit(&n->bw, n->fd, OWRITE);
22180ee5cbfSDavid du Colombier 	if(nntpresponse(n, 20, "greeting") < 0)
22280ee5cbfSDavid du Colombier 		return -1;
2239a747e4fSDavid du Colombier 	readonly = (n->code == 201);
2249a747e4fSDavid du Colombier 
2259a747e4fSDavid du Colombier 	indial = 1;
2269a747e4fSDavid du Colombier 	if(n->auth != 0)
2279a747e4fSDavid du Colombier 		nntpauth(n);
2289a747e4fSDavid du Colombier //	nntpxcmdprobe(n);
2299a747e4fSDavid du Colombier 	indial = 0;
2309a747e4fSDavid du Colombier 	return 0;
23180ee5cbfSDavid du Colombier }
2329a747e4fSDavid du Colombier 
2339a747e4fSDavid du Colombier int
nntpcmd(Netbuf * n,char * cmd,int e)2349a747e4fSDavid du Colombier nntpcmd(Netbuf *n, char *cmd, int e)
2359a747e4fSDavid du Colombier {
2369a747e4fSDavid du Colombier 	int tried;
2379a747e4fSDavid du Colombier 
2389a747e4fSDavid du Colombier 	tried = 0;
2399a747e4fSDavid du Colombier 	for(;;){
2409a747e4fSDavid du Colombier 		if(netdebug)
2419a747e4fSDavid du Colombier 			fprint(2, "<- %s\n", cmd);
2429a747e4fSDavid du Colombier 		Bprint(&n->bw, "%s\r\n", cmd);
2439a747e4fSDavid du Colombier 		if(nntpresponse(n, e, cmd)>=0 && (e < 0 || n->code/100 != 5))
2449a747e4fSDavid du Colombier 			return 0;
2459a747e4fSDavid du Colombier 
2469a747e4fSDavid du Colombier 		/* redial */
2479a747e4fSDavid du Colombier 		if(indial || tried++ || nntpconnect(n) < 0)
2489a747e4fSDavid du Colombier 			return -1;
2499a747e4fSDavid du Colombier 	}
2509a747e4fSDavid du Colombier }
2519a747e4fSDavid du Colombier 
2529a747e4fSDavid du Colombier int
nntpauth(Netbuf * n)2539a747e4fSDavid du Colombier nntpauth(Netbuf *n)
2549a747e4fSDavid du Colombier {
2559a747e4fSDavid du Colombier 	char cmd[256];
2569a747e4fSDavid du Colombier 
2579a747e4fSDavid du Colombier 	snprint(cmd, sizeof cmd, "AUTHINFO USER %s", n->user);
2589a747e4fSDavid du Colombier 	if (nntpcmd(n, cmd, -1) < 0 || n->code != 381) {
2599a747e4fSDavid du Colombier 		fprint(2, "Authentication failed: %s\n", n->response);
2609a747e4fSDavid du Colombier 		return -1;
2619a747e4fSDavid du Colombier 	}
2629a747e4fSDavid du Colombier 
2639a747e4fSDavid du Colombier 	snprint(cmd, sizeof cmd, "AUTHINFO PASS %s", n->pass);
2649a747e4fSDavid du Colombier 	if (nntpcmd(n, cmd, -1) < 0 || n->code != 281) {
2659a747e4fSDavid du Colombier 		fprint(2, "Authentication failed: %s\n", n->response);
2669a747e4fSDavid du Colombier 		return -1;
2679a747e4fSDavid du Colombier 	}
2689a747e4fSDavid du Colombier 
2699a747e4fSDavid du Colombier 	return 0;
2709a747e4fSDavid du Colombier }
2719a747e4fSDavid du Colombier 
2729a747e4fSDavid du Colombier int
nntpxcmdprobe(Netbuf * n)2739a747e4fSDavid du Colombier nntpxcmdprobe(Netbuf *n)
2749a747e4fSDavid du Colombier {
2759a747e4fSDavid du Colombier 	int i;
2769a747e4fSDavid du Colombier 	char *p;
2779a747e4fSDavid du Colombier 
2789a747e4fSDavid du Colombier 	n->extended = 0;
2799a747e4fSDavid du Colombier 	if (nntpcmd(n, "LIST EXTENSIONS", 0) < 0 || n->code != 202)
2809a747e4fSDavid du Colombier 		return 0;
2819a747e4fSDavid du Colombier 
2829a747e4fSDavid du Colombier 	while((p = Nrdline(n)) != nil) {
2839a747e4fSDavid du Colombier 		if (strcmp(p, ".") == 0)
2849a747e4fSDavid du Colombier 			break;
2859a747e4fSDavid du Colombier 
2869a747e4fSDavid du Colombier 		for(i=0; extensions[i].s != nil; i++)
2879a747e4fSDavid du Colombier 			if (cistrcmp(extensions[i].s, p) == 0) {
2889a747e4fSDavid du Colombier 				n->extended |= extensions[i].n;
2899a747e4fSDavid du Colombier 				break;
2909a747e4fSDavid du Colombier 			}
2919a747e4fSDavid du Colombier 	}
2929a747e4fSDavid du Colombier 	return 0;
2939a747e4fSDavid du Colombier }
2949a747e4fSDavid du Colombier 
2959a747e4fSDavid du Colombier /* XXX: searching, lazy evaluation */
2969a747e4fSDavid du Colombier static int
overcmp(void * v1,void * v2)2979a747e4fSDavid du Colombier overcmp(void *v1, void *v2)
2989a747e4fSDavid du Colombier {
2999a747e4fSDavid du Colombier 	int a, b;
3009a747e4fSDavid du Colombier 
3019a747e4fSDavid du Colombier 	a = atoi(*(char**)v1);
3029a747e4fSDavid du Colombier 	b = atoi(*(char**)v2);
3039a747e4fSDavid du Colombier 
3049a747e4fSDavid du Colombier 	if(a < b)
3059a747e4fSDavid du Colombier 		return -1;
3069a747e4fSDavid du Colombier 	else if(a > b)
3079a747e4fSDavid du Colombier 		return 1;
3089a747e4fSDavid du Colombier 	return 0;
3099a747e4fSDavid du Colombier }
3109a747e4fSDavid du Colombier 
3119a747e4fSDavid du Colombier enum {
3129a747e4fSDavid du Colombier 	XoverChunk = 100,
3139a747e4fSDavid du Colombier };
3149a747e4fSDavid du Colombier 
3159a747e4fSDavid du Colombier char *xover[XoverChunk];
3169a747e4fSDavid du Colombier int xoverlo;
3179a747e4fSDavid du Colombier int xoverhi;
3189a747e4fSDavid du Colombier int xovercount;
3199a747e4fSDavid du Colombier Group *xovergroup;
3209a747e4fSDavid du Colombier 
3219a747e4fSDavid du Colombier char*
nntpover(Netbuf * n,Group * g,int m)3229a747e4fSDavid du Colombier nntpover(Netbuf *n, Group *g, int m)
3239a747e4fSDavid du Colombier {
3249a747e4fSDavid du Colombier 	int i, lo, hi, mid, msg;
3259a747e4fSDavid du Colombier 	char *p;
3269a747e4fSDavid du Colombier 	char cmd[64];
3279a747e4fSDavid du Colombier 
3289a747e4fSDavid du Colombier 	if (g->isgroup == 0)	/* BUG: should check extension capabilities */
3299a747e4fSDavid du Colombier 		return nil;
3309a747e4fSDavid du Colombier 
331dc5a79c1SDavid du Colombier 	if(g != xovergroup || m < xoverlo || m >= xoverhi){
3329a747e4fSDavid du Colombier 		lo = (m/XoverChunk)*XoverChunk;
3339a747e4fSDavid du Colombier 		hi = lo+XoverChunk;
3349a747e4fSDavid du Colombier 
3359a747e4fSDavid du Colombier 		if(lo < g->lo)
3369a747e4fSDavid du Colombier 			lo = g->lo;
3379a747e4fSDavid du Colombier 		else if (lo > g->hi)
3389a747e4fSDavid du Colombier 			lo = g->hi;
3399a747e4fSDavid du Colombier 		if(hi < lo || hi > g->hi)
3409a747e4fSDavid du Colombier 			hi = g->hi;
3419a747e4fSDavid du Colombier 
3429a747e4fSDavid du Colombier 		if(nntpcurrentgroup(n, g) < 0)
3439a747e4fSDavid du Colombier 			return nil;
3449a747e4fSDavid du Colombier 
3459a747e4fSDavid du Colombier 		if(lo == hi)
3469a747e4fSDavid du Colombier 			snprint(cmd, sizeof cmd, "XOVER %d", hi);
3479a747e4fSDavid du Colombier 		else
3489a747e4fSDavid du Colombier 			snprint(cmd, sizeof cmd, "XOVER %d-%d", lo, hi-1);
3499a747e4fSDavid du Colombier 		if(nntpcmd(n, cmd, 224) < 0)
3509a747e4fSDavid du Colombier 			return nil;
3519a747e4fSDavid du Colombier 
3529a747e4fSDavid du Colombier 		for(i=0; (p = Nrdline(n)) != nil; i++) {
3539a747e4fSDavid du Colombier 			if(strcmp(p, ".") == 0)
3549a747e4fSDavid du Colombier 				break;
3559a747e4fSDavid du Colombier 			if(i >= XoverChunk)
3569a747e4fSDavid du Colombier 				sysfatal("news server doesn't play by the rules");
3579a747e4fSDavid du Colombier 			free(xover[i]);
358fb7f0c93SDavid du Colombier 			xover[i] = emalloc(strlen(p)+2);
359fb7f0c93SDavid du Colombier 			strcpy(xover[i], p);
360fb7f0c93SDavid du Colombier 			strcat(xover[i], "\n");
3619a747e4fSDavid du Colombier 		}
3629a747e4fSDavid du Colombier 		qsort(xover, i, sizeof(xover[0]), overcmp);
3639a747e4fSDavid du Colombier 
3649a747e4fSDavid du Colombier 		xovercount = i;
3659a747e4fSDavid du Colombier 
3669a747e4fSDavid du Colombier 		xovergroup = g;
3679a747e4fSDavid du Colombier 		xoverlo = lo;
3689a747e4fSDavid du Colombier 		xoverhi = hi;
3699a747e4fSDavid du Colombier 	}
3709a747e4fSDavid du Colombier 
3719a747e4fSDavid du Colombier 	lo = 0;
3729a747e4fSDavid du Colombier 	hi = xovercount;
3739a747e4fSDavid du Colombier 	/* search for message */
374dc5a79c1SDavid du Colombier 	while(lo < hi){
3759a747e4fSDavid du Colombier 		mid = (lo+hi)/2;
3769a747e4fSDavid du Colombier 		msg = atoi(xover[mid]);
377dc5a79c1SDavid du Colombier 		if(m == msg)
378dc5a79c1SDavid du Colombier 			return xover[mid];
379dc5a79c1SDavid du Colombier 		else if(m < msg)
3809a747e4fSDavid du Colombier 			hi = mid;
3819a747e4fSDavid du Colombier 		else
382dc5a79c1SDavid du Colombier 			lo = mid+1;
3839a747e4fSDavid du Colombier 	}
384dc5a79c1SDavid du Colombier 	return nil;
38580ee5cbfSDavid du Colombier }
38680ee5cbfSDavid du Colombier 
38780ee5cbfSDavid du Colombier /*
38880ee5cbfSDavid du Colombier  * Return the new Group structure for the group name.
38980ee5cbfSDavid du Colombier  * Destroys name.
39080ee5cbfSDavid du Colombier  */
3919a747e4fSDavid du Colombier static int printgroup(char*,Group*);
39280ee5cbfSDavid du Colombier Group*
findgroup(Group * g,char * name,int mk)39380ee5cbfSDavid du Colombier findgroup(Group *g, char *name, int mk)
39480ee5cbfSDavid du Colombier {
39580ee5cbfSDavid du Colombier 	int lo, hi, m;
39680ee5cbfSDavid du Colombier 	char *p, *q;
39780ee5cbfSDavid du Colombier 	static int ngroup;
39880ee5cbfSDavid du Colombier 
39980ee5cbfSDavid du Colombier 	for(p=name; *p; p=q){
40080ee5cbfSDavid du Colombier 		if(q = strchr(p, '.'))
40180ee5cbfSDavid du Colombier 			*q++ = '\0';
40280ee5cbfSDavid du Colombier 		else
40380ee5cbfSDavid du Colombier 			q = p+strlen(p);
40480ee5cbfSDavid du Colombier 
40580ee5cbfSDavid du Colombier 		lo = 0;
40680ee5cbfSDavid du Colombier 		hi = g->nkid;
40780ee5cbfSDavid du Colombier 		while(hi-lo > 1){
40880ee5cbfSDavid du Colombier 			m = (lo+hi)/2;
40980ee5cbfSDavid du Colombier 			if(strcmp(p, g->kid[m]->name) < 0)
41080ee5cbfSDavid du Colombier 				hi = m;
41180ee5cbfSDavid du Colombier 			else
41280ee5cbfSDavid du Colombier 				lo = m;
41380ee5cbfSDavid du Colombier 		}
41480ee5cbfSDavid du Colombier 		assert(lo==hi || lo==hi-1);
41580ee5cbfSDavid du Colombier 		if(lo==hi || strcmp(p, g->kid[lo]->name) != 0){
41680ee5cbfSDavid du Colombier 			if(mk==0)
41780ee5cbfSDavid du Colombier 				return nil;
41880ee5cbfSDavid du Colombier 			if(g->nkid%16 == 0)
41980ee5cbfSDavid du Colombier 				g->kid = erealloc(g->kid, (g->nkid+16)*sizeof(g->kid[0]));
4209a747e4fSDavid du Colombier 
4219a747e4fSDavid du Colombier 			/*
4229a747e4fSDavid du Colombier 			 * if we're down to a single place 'twixt lo and hi, the insertion might need
4239a747e4fSDavid du Colombier 			 * to go at lo or at hi.  strcmp to find out.  the list needs to stay sorted.
4249a747e4fSDavid du Colombier 		 	 */
4259a747e4fSDavid du Colombier 			if(lo==hi-1 && strcmp(p, g->kid[lo]->name) < 0)
4269a747e4fSDavid du Colombier 				hi = lo;
4279a747e4fSDavid du Colombier 
42880ee5cbfSDavid du Colombier 			if(hi < g->nkid)
42980ee5cbfSDavid du Colombier 				memmove(g->kid+hi+1, g->kid+hi, sizeof(g->kid[0])*(g->nkid-hi));
43080ee5cbfSDavid du Colombier 			g->nkid++;
43180ee5cbfSDavid du Colombier 			g->kid[hi] = emalloc(sizeof(*g));
43280ee5cbfSDavid du Colombier 			g->kid[hi]->parent = g;
43380ee5cbfSDavid du Colombier 			g = g->kid[hi];
43480ee5cbfSDavid du Colombier 			g->name = estrdup(p);
43580ee5cbfSDavid du Colombier 			g->num = ++ngroup;
43680ee5cbfSDavid du Colombier 			g->mtime = time(0);
43780ee5cbfSDavid du Colombier 		}else
43880ee5cbfSDavid du Colombier 			g = g->kid[lo];
43980ee5cbfSDavid du Colombier 	}
44080ee5cbfSDavid du Colombier 	if(mk)
44180ee5cbfSDavid du Colombier 		g->isgroup = 1;
44280ee5cbfSDavid du Colombier 	return g;
44380ee5cbfSDavid du Colombier }
44480ee5cbfSDavid du Colombier 
44580ee5cbfSDavid du Colombier static int
printgroup(char * s,Group * g)44680ee5cbfSDavid du Colombier printgroup(char *s, Group *g)
44780ee5cbfSDavid du Colombier {
44880ee5cbfSDavid du Colombier 	if(g->parent == g)
44980ee5cbfSDavid du Colombier 		return 0;
45080ee5cbfSDavid du Colombier 
45180ee5cbfSDavid du Colombier 	if(printgroup(s, g->parent))
45280ee5cbfSDavid du Colombier 		strcat(s, ".");
45380ee5cbfSDavid du Colombier 	strcat(s, g->name);
45480ee5cbfSDavid du Colombier 	return 1;
45580ee5cbfSDavid du Colombier }
45680ee5cbfSDavid du Colombier 
45780ee5cbfSDavid du Colombier static char*
Nreaddata(Netbuf * n)45880ee5cbfSDavid du Colombier Nreaddata(Netbuf *n)
45980ee5cbfSDavid du Colombier {
46080ee5cbfSDavid du Colombier 	char *p, *q;
46180ee5cbfSDavid du Colombier 	int l;
46280ee5cbfSDavid du Colombier 
46380ee5cbfSDavid du Colombier 	p = nil;
46480ee5cbfSDavid du Colombier 	l = 0;
46580ee5cbfSDavid du Colombier 	for(;;){
46680ee5cbfSDavid du Colombier 		q = Nrdline(n);
46780ee5cbfSDavid du Colombier 		if(q==nil){
46880ee5cbfSDavid du Colombier 			free(p);
46980ee5cbfSDavid du Colombier 			return nil;
47080ee5cbfSDavid du Colombier 		}
47180ee5cbfSDavid du Colombier 		if(strcmp(q, ".")==0)
47280ee5cbfSDavid du Colombier 			return p;
47380ee5cbfSDavid du Colombier 		if(q[0]=='.')
47480ee5cbfSDavid du Colombier 			q++;
47580ee5cbfSDavid du Colombier 		p = erealloc(p, l+strlen(q)+1+1);
47680ee5cbfSDavid du Colombier 		strcpy(p+l, q);
47780ee5cbfSDavid du Colombier 		strcat(p+l, "\n");
47880ee5cbfSDavid du Colombier 		l += strlen(p+l);
47980ee5cbfSDavid du Colombier 	}
48080ee5cbfSDavid du Colombier }
48180ee5cbfSDavid du Colombier 
48280ee5cbfSDavid du Colombier /*
48380ee5cbfSDavid du Colombier  * Return the output of a HEAD, BODY, or ARTICLE command.
48480ee5cbfSDavid du Colombier  */
48580ee5cbfSDavid du Colombier char*
nntpget(Netbuf * n,Group * g,int msg,char * retr)48680ee5cbfSDavid du Colombier nntpget(Netbuf *n, Group *g, int msg, char *retr)
48780ee5cbfSDavid du Colombier {
4889a747e4fSDavid du Colombier 	char *s;
48980ee5cbfSDavid du Colombier 	char cmd[1024];
49080ee5cbfSDavid du Colombier 
49180ee5cbfSDavid du Colombier 	if(g->isgroup == 0){
49280ee5cbfSDavid du Colombier 		werrstr("not a group");
49380ee5cbfSDavid du Colombier 		return nil;
49480ee5cbfSDavid du Colombier 	}
49580ee5cbfSDavid du Colombier 
4969a747e4fSDavid du Colombier 	if(strcmp(retr, "XOVER") == 0){
4979a747e4fSDavid du Colombier 		s = nntpover(n, g, msg);
4989a747e4fSDavid du Colombier 		if(s == nil)
4999a747e4fSDavid du Colombier 			s = "";
5009a747e4fSDavid du Colombier 		return estrdup(s);
50180ee5cbfSDavid du Colombier 	}
50280ee5cbfSDavid du Colombier 
5039a747e4fSDavid du Colombier 	if(nntpcurrentgroup(n, g) < 0)
5049a747e4fSDavid du Colombier 		return nil;
50580ee5cbfSDavid du Colombier 	sprint(cmd, "%s %d", retr, msg);
50680ee5cbfSDavid du Colombier 	nntpcmd(n, cmd, 0);
50780ee5cbfSDavid du Colombier 	if(n->code/10 != 22)
50880ee5cbfSDavid du Colombier 		return nil;
50980ee5cbfSDavid du Colombier 
51080ee5cbfSDavid du Colombier 	return Nreaddata(n);
51180ee5cbfSDavid du Colombier }
51280ee5cbfSDavid du Colombier 
5139a747e4fSDavid du Colombier int
nntpcurrentgroup(Netbuf * n,Group * g)5149a747e4fSDavid du Colombier nntpcurrentgroup(Netbuf *n, Group *g)
5159a747e4fSDavid du Colombier {
5169a747e4fSDavid du Colombier 	char cmd[1024];
5179a747e4fSDavid du Colombier 
5189a747e4fSDavid du Colombier 	if(n->currentgroup != g){
5199a747e4fSDavid du Colombier 		strcpy(cmd, "GROUP ");
5209a747e4fSDavid du Colombier 		printgroup(cmd, g);
5219a747e4fSDavid du Colombier 		if(nntpcmd(n, cmd, 21) < 0)
5229a747e4fSDavid du Colombier 			return -1;
5239a747e4fSDavid du Colombier 		n->currentgroup = g;
5249a747e4fSDavid du Colombier 	}
5259a747e4fSDavid du Colombier 	return 0;
5269a747e4fSDavid du Colombier }
5279a747e4fSDavid du Colombier 
52880ee5cbfSDavid du Colombier void
nntprefreshall(Netbuf * n)52980ee5cbfSDavid du Colombier nntprefreshall(Netbuf *n)
53080ee5cbfSDavid du Colombier {
53180ee5cbfSDavid du Colombier 	char *f[10], *p;
53280ee5cbfSDavid du Colombier 	int hi, lo, nf;
53380ee5cbfSDavid du Colombier 	Group *g;
53480ee5cbfSDavid du Colombier 
53580ee5cbfSDavid du Colombier 	if(nntpcmd(n, "LIST", 21) < 0)
53680ee5cbfSDavid du Colombier 		return;
53780ee5cbfSDavid du Colombier 
53880ee5cbfSDavid du Colombier 	while(p = Nrdline(n)){
53980ee5cbfSDavid du Colombier 		if(strcmp(p, ".")==0)
54080ee5cbfSDavid du Colombier 			break;
541ce31847cSDavid du Colombier 
542ce31847cSDavid du Colombier 		nf = getfields(p, f, nelem(f), 1, "\t\r\n ");
54380ee5cbfSDavid du Colombier 		if(nf != 4){
54480ee5cbfSDavid du Colombier 			int i;
54580ee5cbfSDavid du Colombier 			for(i=0; i<nf; i++)
54680ee5cbfSDavid du Colombier 				fprint(2, "%s%s", i?" ":"", f[i]);
54780ee5cbfSDavid du Colombier 			fprint(2, "\n");
54880ee5cbfSDavid du Colombier 			fprint(2, "syntax error in group list, line %d", n->lineno);
54980ee5cbfSDavid du Colombier 			return;
55080ee5cbfSDavid du Colombier 		}
55180ee5cbfSDavid du Colombier 		g = findgroup(root, f[0], 1);
55280ee5cbfSDavid du Colombier 		hi = strtol(f[1], 0, 10)+1;
55380ee5cbfSDavid du Colombier 		lo = strtol(f[2], 0, 10);
55480ee5cbfSDavid du Colombier 		if(g->hi != hi){
55580ee5cbfSDavid du Colombier 			g->hi = hi;
55680ee5cbfSDavid du Colombier 			if(g->lo==0)
55780ee5cbfSDavid du Colombier 				g->lo = lo;
55880ee5cbfSDavid du Colombier 			g->canpost = f[3][0] == 'y';
55980ee5cbfSDavid du Colombier 			g->mtime = time(0);
56080ee5cbfSDavid du Colombier 		}
56180ee5cbfSDavid du Colombier 	}
56280ee5cbfSDavid du Colombier }
56380ee5cbfSDavid du Colombier 
56480ee5cbfSDavid du Colombier void
nntprefresh(Netbuf * n,Group * g)56580ee5cbfSDavid du Colombier nntprefresh(Netbuf *n, Group *g)
56680ee5cbfSDavid du Colombier {
56780ee5cbfSDavid du Colombier 	char cmd[1024];
56880ee5cbfSDavid du Colombier 	char *f[5];
56980ee5cbfSDavid du Colombier 	int lo, hi;
57080ee5cbfSDavid du Colombier 
57180ee5cbfSDavid du Colombier 	if(g->isgroup==0)
57280ee5cbfSDavid du Colombier 		return;
57380ee5cbfSDavid du Colombier 
5749a747e4fSDavid du Colombier 	if(time(0) - g->atime < 30)
5759a747e4fSDavid du Colombier 		return;
5769a747e4fSDavid du Colombier 
57780ee5cbfSDavid du Colombier 	strcpy(cmd, "GROUP ");
57880ee5cbfSDavid du Colombier 	printgroup(cmd, g);
57980ee5cbfSDavid du Colombier 	if(nntpcmd(n, cmd, 21) < 0){
58080ee5cbfSDavid du Colombier 		n->currentgroup = nil;
58180ee5cbfSDavid du Colombier 		return;
58280ee5cbfSDavid du Colombier 	}
58380ee5cbfSDavid du Colombier 	n->currentgroup = g;
58480ee5cbfSDavid du Colombier 
58580ee5cbfSDavid du Colombier 	if(tokenize(n->response, f, nelem(f)) < 4){
58680ee5cbfSDavid du Colombier 		fprint(2, "error reading GROUP response");
58780ee5cbfSDavid du Colombier 		return;
58880ee5cbfSDavid du Colombier 	}
58980ee5cbfSDavid du Colombier 
59080ee5cbfSDavid du Colombier 	/* backwards from LIST! */
59180ee5cbfSDavid du Colombier 	hi = strtol(f[3], 0, 10)+1;
59280ee5cbfSDavid du Colombier 	lo = strtol(f[2], 0, 10);
59380ee5cbfSDavid du Colombier 	if(g->hi != hi){
59480ee5cbfSDavid du Colombier 		g->mtime = time(0);
59580ee5cbfSDavid du Colombier 		if(g->lo==0)
59680ee5cbfSDavid du Colombier 			g->lo = lo;
59780ee5cbfSDavid du Colombier 		g->hi = hi;
59880ee5cbfSDavid du Colombier 	}
5999a747e4fSDavid du Colombier 	g->atime = time(0);
60080ee5cbfSDavid du Colombier }
60180ee5cbfSDavid du Colombier 
60280ee5cbfSDavid du Colombier char*
nntppost(Netbuf * n,char * msg)60380ee5cbfSDavid du Colombier nntppost(Netbuf *n, char *msg)
60480ee5cbfSDavid du Colombier {
60580ee5cbfSDavid du Colombier 	char *p, *q;
60680ee5cbfSDavid du Colombier 
60780ee5cbfSDavid du Colombier 	if(nntpcmd(n, "POST", 34) < 0)
60880ee5cbfSDavid du Colombier 		return n->response;
60980ee5cbfSDavid du Colombier 
61080ee5cbfSDavid du Colombier 	for(p=msg; *p; p=q){
61180ee5cbfSDavid du Colombier 		if(q = strchr(p, '\n'))
61280ee5cbfSDavid du Colombier 			*q++ = '\0';
61380ee5cbfSDavid du Colombier 		else
61480ee5cbfSDavid du Colombier 			q = p+strlen(p);
61580ee5cbfSDavid du Colombier 
61680ee5cbfSDavid du Colombier 		if(p[0]=='.')
61780ee5cbfSDavid du Colombier 			Bputc(&n->bw, '.');
61880ee5cbfSDavid du Colombier 		Bwrite(&n->bw, p, strlen(p));
619d9306527SDavid du Colombier 		Bputc(&n->bw, '\r');
62080ee5cbfSDavid du Colombier 		Bputc(&n->bw, '\n');
62180ee5cbfSDavid du Colombier 	}
622d9306527SDavid du Colombier 	Bprint(&n->bw, ".\r\n");
62380ee5cbfSDavid du Colombier 
62480ee5cbfSDavid du Colombier 	if(nntpresponse(n, 0, nil) < 0)
62580ee5cbfSDavid du Colombier 		return n->response;
62680ee5cbfSDavid du Colombier 
62780ee5cbfSDavid du Colombier 	if(n->code/100 != 2)
62880ee5cbfSDavid du Colombier 		return n->response;
62980ee5cbfSDavid du Colombier 	return nil;
63080ee5cbfSDavid du Colombier }
63180ee5cbfSDavid du Colombier 
63280ee5cbfSDavid du Colombier /*
63380ee5cbfSDavid du Colombier  * Because an expanded QID space makes thngs much easier,
63480ee5cbfSDavid du Colombier  * we sleazily use the version part of the QID as more path bits.
63580ee5cbfSDavid du Colombier  * Since we make sure not to mount ourselves cached, this
63680ee5cbfSDavid du Colombier  * doesn't break anything (unless you want to bind on top of
63780ee5cbfSDavid du Colombier  * things in this file system).  In the next version of 9P, we'll
63880ee5cbfSDavid du Colombier  * have more QID bits to play with.
63980ee5cbfSDavid du Colombier  *
6409a747e4fSDavid du Colombier  * The newsgroup is encoded in the top 15 bits
64180ee5cbfSDavid du Colombier  * of the path.  The message number is the bottom 17 bits.
64280ee5cbfSDavid du Colombier  * The file within the message directory is in the version [sic].
64380ee5cbfSDavid du Colombier  */
64480ee5cbfSDavid du Colombier 
64580ee5cbfSDavid du Colombier enum {	/* file qids */
64680ee5cbfSDavid du Colombier 	Qhead,
64780ee5cbfSDavid du Colombier 	Qbody,
64880ee5cbfSDavid du Colombier 	Qarticle,
6499a747e4fSDavid du Colombier 	Qxover,
65080ee5cbfSDavid du Colombier 	Nfile,
65180ee5cbfSDavid du Colombier };
65280ee5cbfSDavid du Colombier char *filename[] = {
65380ee5cbfSDavid du Colombier 	"header",
65480ee5cbfSDavid du Colombier 	"body",
65580ee5cbfSDavid du Colombier 	"article",
6569a747e4fSDavid du Colombier 	"xover",
65780ee5cbfSDavid du Colombier };
65880ee5cbfSDavid du Colombier char *nntpname[] = {
65980ee5cbfSDavid du Colombier 	"HEAD",
66080ee5cbfSDavid du Colombier 	"BODY",
66180ee5cbfSDavid du Colombier 	"ARTICLE",
6629a747e4fSDavid du Colombier 	"XOVER",
66380ee5cbfSDavid du Colombier };
66480ee5cbfSDavid du Colombier 
66580ee5cbfSDavid du Colombier #define GROUP(p)	(((p)>>17)&0x3FFF)
66680ee5cbfSDavid du Colombier #define MESSAGE(p)	((p)&0x1FFFF)
66780ee5cbfSDavid du Colombier #define FILE(v)		((v)&0x3)
66880ee5cbfSDavid du Colombier 
6699a747e4fSDavid du Colombier #define PATH(g,m)	((((g)&0x3FFF)<<17)|((m)&0x1FFFF))
67080ee5cbfSDavid du Colombier #define POST(g)	PATH(0,g,0)
67180ee5cbfSDavid du Colombier #define VERS(f)		((f)&0x3)
67280ee5cbfSDavid du Colombier 
67380ee5cbfSDavid du Colombier typedef struct Aux Aux;
67480ee5cbfSDavid du Colombier struct Aux {
67580ee5cbfSDavid du Colombier 	Group *g;
67680ee5cbfSDavid du Colombier 	int n;
67780ee5cbfSDavid du Colombier 	int ispost;
67880ee5cbfSDavid du Colombier 	int file;
67980ee5cbfSDavid du Colombier 	char *s;
68080ee5cbfSDavid du Colombier 	int ns;
6819a747e4fSDavid du Colombier 	int offset;
68280ee5cbfSDavid du Colombier };
68380ee5cbfSDavid du Colombier 
68480ee5cbfSDavid du Colombier static void
fsattach(Req * r)6859a747e4fSDavid du Colombier fsattach(Req *r)
68680ee5cbfSDavid du Colombier {
68780ee5cbfSDavid du Colombier 	Aux *a;
6889a747e4fSDavid du Colombier 	char *spec;
68980ee5cbfSDavid du Colombier 
6909a747e4fSDavid du Colombier 	spec = r->ifcall.aname;
69180ee5cbfSDavid du Colombier 	if(spec && spec[0]){
69280ee5cbfSDavid du Colombier 		respond(r, "invalid attach specifier");
69380ee5cbfSDavid du Colombier 		return;
69480ee5cbfSDavid du Colombier 	}
69580ee5cbfSDavid du Colombier 
69680ee5cbfSDavid du Colombier 	a = emalloc(sizeof *a);
69780ee5cbfSDavid du Colombier 	a->g = root;
69880ee5cbfSDavid du Colombier 	a->n = -1;
6999a747e4fSDavid du Colombier 	r->fid->aux = a;
70080ee5cbfSDavid du Colombier 
7019a747e4fSDavid du Colombier 	r->ofcall.qid = (Qid){0, 0, QTDIR};
7029a747e4fSDavid du Colombier 	r->fid->qid = r->ofcall.qid;
70380ee5cbfSDavid du Colombier 	respond(r, nil);
70480ee5cbfSDavid du Colombier }
70580ee5cbfSDavid du Colombier 
7069a747e4fSDavid du Colombier static char*
fsclone(Fid * ofid,Fid * fid)7079a747e4fSDavid du Colombier fsclone(Fid *ofid, Fid *fid)
7089a747e4fSDavid du Colombier {
7099a747e4fSDavid du Colombier 	Aux *a;
7109a747e4fSDavid du Colombier 
7119a747e4fSDavid du Colombier 	a = emalloc(sizeof(*a));
7129a747e4fSDavid du Colombier 	*a = *(Aux*)ofid->aux;
7139a747e4fSDavid du Colombier 	fid->aux = a;
7149a747e4fSDavid du Colombier 	return nil;
7159a747e4fSDavid du Colombier }
7169a747e4fSDavid du Colombier 
7179a747e4fSDavid du Colombier static char*
fswalk1(Fid * fid,char * name,Qid * qid)7189a747e4fSDavid du Colombier fswalk1(Fid *fid, char *name, Qid *qid)
71980ee5cbfSDavid du Colombier {
72080ee5cbfSDavid du Colombier 	char *p;
72180ee5cbfSDavid du Colombier 	int i, isdotdot, n;
72280ee5cbfSDavid du Colombier 	Aux *a;
72380ee5cbfSDavid du Colombier 	Group *ng;
72480ee5cbfSDavid du Colombier 
72580ee5cbfSDavid du Colombier 	isdotdot = strcmp(name, "..")==0;
7269a747e4fSDavid du Colombier 
72780ee5cbfSDavid du Colombier 	a = fid->aux;
7289a747e4fSDavid du Colombier 	if(a->s)	/* file */
7299a747e4fSDavid du Colombier 		return "protocol botch";
73080ee5cbfSDavid du Colombier 	if(a->n != -1){
73180ee5cbfSDavid du Colombier 		if(isdotdot){
7329a747e4fSDavid du Colombier 			*qid = (Qid){PATH(a->g->num, 0), 0, QTDIR};
7339a747e4fSDavid du Colombier 			fid->qid = *qid;
73480ee5cbfSDavid du Colombier 			a->n = -1;
7359a747e4fSDavid du Colombier 			return nil;
73680ee5cbfSDavid du Colombier 		}
73780ee5cbfSDavid du Colombier 		for(i=0; i<Nfile; i++){
73880ee5cbfSDavid du Colombier 			if(strcmp(name, filename[i])==0){
73980ee5cbfSDavid du Colombier 				if(a->s = nntpget(net, a->g, a->n, nntpname[i])){
7409a747e4fSDavid du Colombier 					*qid = (Qid){PATH(a->g->num, a->n), Qbody, 0};
7419a747e4fSDavid du Colombier 					fid->qid = *qid;
74280ee5cbfSDavid du Colombier 					a->file = i;
7439a747e4fSDavid du Colombier 					return nil;
74480ee5cbfSDavid du Colombier 				}else
7459a747e4fSDavid du Colombier 					return "file does not exist";
74680ee5cbfSDavid du Colombier 			}
74780ee5cbfSDavid du Colombier 		}
7489a747e4fSDavid du Colombier 		return "file does not exist";
74980ee5cbfSDavid du Colombier 	}
75080ee5cbfSDavid du Colombier 
75180ee5cbfSDavid du Colombier 	if(isdotdot){
75280ee5cbfSDavid du Colombier 		a->g = a->g->parent;
7539a747e4fSDavid du Colombier 		*qid = (Qid){PATH(a->g->num, 0), 0, QTDIR};
7549a747e4fSDavid du Colombier 		fid->qid = *qid;
7559a747e4fSDavid du Colombier 		return nil;
75680ee5cbfSDavid du Colombier 	}
75780ee5cbfSDavid du Colombier 
75880ee5cbfSDavid du Colombier 	if(a->g->isgroup && !readonly && a->g->canpost
75980ee5cbfSDavid du Colombier 	&& strcmp(name, "post")==0){
76080ee5cbfSDavid du Colombier 		a->ispost = 1;
7619a747e4fSDavid du Colombier 		*qid = (Qid){PATH(a->g->num, 0), 0, 0};
7629a747e4fSDavid du Colombier 		fid->qid = *qid;
7639a747e4fSDavid du Colombier 		return nil;
76480ee5cbfSDavid du Colombier 	}
76580ee5cbfSDavid du Colombier 
76680ee5cbfSDavid du Colombier 	if(ng = findgroup(a->g, name, 0)){
76780ee5cbfSDavid du Colombier 		a->g = ng;
7689a747e4fSDavid du Colombier 		*qid = (Qid){PATH(a->g->num, 0), 0, QTDIR};
7699a747e4fSDavid du Colombier 		fid->qid = *qid;
7709a747e4fSDavid du Colombier 		return nil;
77180ee5cbfSDavid du Colombier 	}
77280ee5cbfSDavid du Colombier 
77380ee5cbfSDavid du Colombier 	n = strtoul(name, &p, 0);
77480ee5cbfSDavid du Colombier 	if('0'<=name[0] && name[0]<='9' && *p=='\0' && a->g->lo<=n && n<a->g->hi){
77580ee5cbfSDavid du Colombier 		a->n = n;
7769a747e4fSDavid du Colombier 		*qid = (Qid){PATH(a->g->num, n+1-a->g->lo), 0, QTDIR};
7779a747e4fSDavid du Colombier 		fid->qid = *qid;
7789a747e4fSDavid du Colombier 		return nil;
77980ee5cbfSDavid du Colombier 	}
78080ee5cbfSDavid du Colombier 
7819a747e4fSDavid du Colombier 	return "file does not exist";
78280ee5cbfSDavid du Colombier }
78380ee5cbfSDavid du Colombier 
78480ee5cbfSDavid du Colombier static void
fsopen(Req * r)7859a747e4fSDavid du Colombier fsopen(Req *r)
78680ee5cbfSDavid du Colombier {
78780ee5cbfSDavid du Colombier 	Aux *a;
78880ee5cbfSDavid du Colombier 
7899a747e4fSDavid du Colombier 	a = r->fid->aux;
7909a747e4fSDavid du Colombier 	if((a->ispost && (r->ifcall.mode&~OTRUNC) != OWRITE)
7919a747e4fSDavid du Colombier 	|| (!a->ispost && r->ifcall.mode != OREAD))
79280ee5cbfSDavid du Colombier 		respond(r, "permission denied");
79380ee5cbfSDavid du Colombier 	else
79480ee5cbfSDavid du Colombier 		respond(r, nil);
79580ee5cbfSDavid du Colombier }
79680ee5cbfSDavid du Colombier 
79780ee5cbfSDavid du Colombier static void
fillstat(Dir * d,Aux * a)79880ee5cbfSDavid du Colombier fillstat(Dir *d, Aux *a)
79980ee5cbfSDavid du Colombier {
8009a747e4fSDavid du Colombier 	char buf[32];
80180ee5cbfSDavid du Colombier 	Group *g;
80280ee5cbfSDavid du Colombier 
80380ee5cbfSDavid du Colombier 	memset(d, 0, sizeof *d);
8049a747e4fSDavid du Colombier 	d->uid = estrdup("nntp");
8059a747e4fSDavid du Colombier 	d->gid = estrdup("nntp");
80680ee5cbfSDavid du Colombier 	g = a->g;
80780ee5cbfSDavid du Colombier 	d->atime = d->mtime = g->mtime;
80880ee5cbfSDavid du Colombier 
80980ee5cbfSDavid du Colombier 	if(a->ispost){
8109a747e4fSDavid du Colombier 		d->name = estrdup("post");
81180ee5cbfSDavid du Colombier 		d->mode = 0222;
8129a747e4fSDavid du Colombier 		d->qid = (Qid){PATH(g->num, 0), 0, 0};
81380ee5cbfSDavid du Colombier 		d->length = a->ns;
81480ee5cbfSDavid du Colombier 		return;
81580ee5cbfSDavid du Colombier 	}
81680ee5cbfSDavid du Colombier 
81780ee5cbfSDavid du Colombier 	if(a->s){	/* article file */
8189a747e4fSDavid du Colombier 		d->name = estrdup(filename[a->file]);
81980ee5cbfSDavid du Colombier 		d->mode = 0444;
8209a747e4fSDavid du Colombier 		d->qid = (Qid){PATH(g->num, a->n+1-g->lo), a->file, 0};
82180ee5cbfSDavid du Colombier 		return;
82280ee5cbfSDavid du Colombier 	}
82380ee5cbfSDavid du Colombier 
82480ee5cbfSDavid du Colombier 	if(a->n != -1){	/* article directory */
8259a747e4fSDavid du Colombier 		sprint(buf, "%d", a->n);
8269a747e4fSDavid du Colombier 		d->name = estrdup(buf);
8279a747e4fSDavid du Colombier 		d->mode = DMDIR|0555;
8289a747e4fSDavid du Colombier 		d->qid = (Qid){PATH(g->num, a->n+1-g->lo), 0, QTDIR};
82980ee5cbfSDavid du Colombier 		return;
83080ee5cbfSDavid du Colombier 	}
83180ee5cbfSDavid du Colombier 
83280ee5cbfSDavid du Colombier 	/* group directory */
83380ee5cbfSDavid du Colombier 	if(g->name[0])
8349a747e4fSDavid du Colombier 		d->name = estrdup(g->name);
83580ee5cbfSDavid du Colombier 	else
8369a747e4fSDavid du Colombier 		d->name = estrdup("/");
8379a747e4fSDavid du Colombier 	d->mode = DMDIR|0555;
8389a747e4fSDavid du Colombier 	d->qid = (Qid){PATH(g->num, 0), g->hi-1, QTDIR};
83980ee5cbfSDavid du Colombier }
84080ee5cbfSDavid du Colombier 
84180ee5cbfSDavid du Colombier static int
dirfillstat(Dir * d,Aux * a,int i)84280ee5cbfSDavid du Colombier dirfillstat(Dir *d, Aux *a, int i)
84380ee5cbfSDavid du Colombier {
84480ee5cbfSDavid du Colombier 	int ndir;
84580ee5cbfSDavid du Colombier 	Group *g;
8469a747e4fSDavid du Colombier 	char buf[32];
84780ee5cbfSDavid du Colombier 
84880ee5cbfSDavid du Colombier 	memset(d, 0, sizeof *d);
8499a747e4fSDavid du Colombier 	d->uid = estrdup("nntp");
8509a747e4fSDavid du Colombier 	d->gid = estrdup("nntp");
8519a747e4fSDavid du Colombier 
85280ee5cbfSDavid du Colombier 	g = a->g;
85380ee5cbfSDavid du Colombier 	d->atime = d->mtime = g->mtime;
85480ee5cbfSDavid du Colombier 
85580ee5cbfSDavid du Colombier 	if(a->n != -1){	/* article directory */
85680ee5cbfSDavid du Colombier 		if(i >= Nfile)
85780ee5cbfSDavid du Colombier 			return -1;
85880ee5cbfSDavid du Colombier 
8599a747e4fSDavid du Colombier 		d->name = estrdup(filename[i]);
86080ee5cbfSDavid du Colombier 		d->mode = 0444;
8619a747e4fSDavid du Colombier 		d->qid = (Qid){PATH(g->num, a->n), i, 0};
86280ee5cbfSDavid du Colombier 		return 0;
86380ee5cbfSDavid du Colombier 	}
86480ee5cbfSDavid du Colombier 
86580ee5cbfSDavid du Colombier 	/* hierarchy directory: child groups */
86680ee5cbfSDavid du Colombier 	if(i < g->nkid){
8679a747e4fSDavid du Colombier 		d->name = estrdup(g->kid[i]->name);
8689a747e4fSDavid du Colombier 		d->mode = DMDIR|0555;
8699a747e4fSDavid du Colombier 		d->qid = (Qid){PATH(g->kid[i]->num, 0), g->kid[i]->hi-1, QTDIR};
87080ee5cbfSDavid du Colombier 		return 0;
87180ee5cbfSDavid du Colombier 	}
87280ee5cbfSDavid du Colombier 	i -= g->nkid;
87380ee5cbfSDavid du Colombier 
87480ee5cbfSDavid du Colombier 	/* group directory: post file */
87580ee5cbfSDavid du Colombier 	if(g->isgroup && !readonly && g->canpost){
87680ee5cbfSDavid du Colombier 		if(i < 1){
8779a747e4fSDavid du Colombier 			d->name = estrdup("post");
87880ee5cbfSDavid du Colombier 			d->mode = 0222;
8799a747e4fSDavid du Colombier 			d->qid = (Qid){PATH(g->num, 0), 0, 0};
88080ee5cbfSDavid du Colombier 			return 0;
88180ee5cbfSDavid du Colombier 		}
88280ee5cbfSDavid du Colombier 		i--;
88380ee5cbfSDavid du Colombier 	}
88480ee5cbfSDavid du Colombier 
88580ee5cbfSDavid du Colombier 	/* group directory: child articles */
88680ee5cbfSDavid du Colombier 	ndir = g->hi - g->lo;
88780ee5cbfSDavid du Colombier 	if(i < ndir){
8889a747e4fSDavid du Colombier 		sprint(buf, "%d", g->lo+i);
8899a747e4fSDavid du Colombier 		d->name = estrdup(buf);
8909a747e4fSDavid du Colombier 		d->mode = DMDIR|0555;
8919a747e4fSDavid du Colombier 		d->qid = (Qid){PATH(g->num, i+1), 0, QTDIR};
89280ee5cbfSDavid du Colombier 		return 0;
89380ee5cbfSDavid du Colombier 	}
89480ee5cbfSDavid du Colombier 
89580ee5cbfSDavid du Colombier 	return -1;
89680ee5cbfSDavid du Colombier }
89780ee5cbfSDavid du Colombier 
89880ee5cbfSDavid du Colombier static void
fsstat(Req * r)8999a747e4fSDavid du Colombier fsstat(Req *r)
90080ee5cbfSDavid du Colombier {
90180ee5cbfSDavid du Colombier 	Aux *a;
90280ee5cbfSDavid du Colombier 
9039a747e4fSDavid du Colombier 	a = r->fid->aux;
904f6269f58SDavid du Colombier 	if(r->fid->qid.path == 0 && (r->fid->qid.type & QTDIR))
90580ee5cbfSDavid du Colombier 		nntprefreshall(net);
90680ee5cbfSDavid du Colombier 	else if(a->g->isgroup)
90780ee5cbfSDavid du Colombier 		nntprefresh(net, a->g);
9089a747e4fSDavid du Colombier 	fillstat(&r->d, a);
90980ee5cbfSDavid du Colombier 	respond(r, nil);
91080ee5cbfSDavid du Colombier }
91180ee5cbfSDavid du Colombier 
91280ee5cbfSDavid du Colombier static void
fsread(Req * r)9139a747e4fSDavid du Colombier fsread(Req *r)
91480ee5cbfSDavid du Colombier {
9159a747e4fSDavid du Colombier 	int offset, n;
91680ee5cbfSDavid du Colombier 	Aux *a;
9179a747e4fSDavid du Colombier 	char *p, *ep;
91880ee5cbfSDavid du Colombier 	Dir d;
91980ee5cbfSDavid du Colombier 
9209a747e4fSDavid du Colombier 	a = r->fid->aux;
92180ee5cbfSDavid du Colombier 	if(a->s){
9229a747e4fSDavid du Colombier 		readstr(r, a->s);
92380ee5cbfSDavid du Colombier 		respond(r, nil);
92480ee5cbfSDavid du Colombier 		return;
92580ee5cbfSDavid du Colombier 	}
92680ee5cbfSDavid du Colombier 
9279a747e4fSDavid du Colombier 	if(r->ifcall.offset == 0)
9289a747e4fSDavid du Colombier 		offset = 0;
9299a747e4fSDavid du Colombier 	else
9309a747e4fSDavid du Colombier 		offset = a->offset;
9319a747e4fSDavid du Colombier 
9329a747e4fSDavid du Colombier 	p = r->ofcall.data;
9339a747e4fSDavid du Colombier 	ep = r->ofcall.data+r->ifcall.count;
9349a747e4fSDavid du Colombier 	for(; p+2 < ep; p += n){
9359a747e4fSDavid du Colombier 		if(dirfillstat(&d, a, offset) < 0)
93680ee5cbfSDavid du Colombier 			break;
9379a747e4fSDavid du Colombier 		n=convD2M(&d, (uchar*)p, ep-p);
9389a747e4fSDavid du Colombier 		free(d.name);
9399a747e4fSDavid du Colombier 		free(d.uid);
9409a747e4fSDavid du Colombier 		free(d.gid);
9419a747e4fSDavid du Colombier 		free(d.muid);
9429a747e4fSDavid du Colombier 		if(n <= BIT16SZ)
9439a747e4fSDavid du Colombier 			break;
9449a747e4fSDavid du Colombier 		offset++;
94580ee5cbfSDavid du Colombier 	}
9469a747e4fSDavid du Colombier 	a->offset = offset;
9479a747e4fSDavid du Colombier 	r->ofcall.count = p - r->ofcall.data;
94880ee5cbfSDavid du Colombier 	respond(r, nil);
94980ee5cbfSDavid du Colombier }
95080ee5cbfSDavid du Colombier 
95180ee5cbfSDavid du Colombier static void
fswrite(Req * r)9529a747e4fSDavid du Colombier fswrite(Req *r)
95380ee5cbfSDavid du Colombier {
95480ee5cbfSDavid du Colombier 	Aux *a;
9559a747e4fSDavid du Colombier 	long count;
9569a747e4fSDavid du Colombier 	vlong offset;
95780ee5cbfSDavid du Colombier 
9589a747e4fSDavid du Colombier 	a = r->fid->aux;
95980ee5cbfSDavid du Colombier 
9609a747e4fSDavid du Colombier 	if(r->ifcall.count == 0){	/* commit */
96180ee5cbfSDavid du Colombier 		respond(r, nntppost(net, a->s));
96280ee5cbfSDavid du Colombier 		free(a->s);
96380ee5cbfSDavid du Colombier 		a->ns = 0;
96480ee5cbfSDavid du Colombier 		a->s = nil;
96580ee5cbfSDavid du Colombier 		return;
96680ee5cbfSDavid du Colombier 	}
96780ee5cbfSDavid du Colombier 
9689a747e4fSDavid du Colombier 	count = r->ifcall.count;
9699a747e4fSDavid du Colombier 	offset = r->ifcall.offset;
9709a747e4fSDavid du Colombier 	if(a->ns < count+offset+1){
9719a747e4fSDavid du Colombier 		a->s = erealloc(a->s, count+offset+1);
9729a747e4fSDavid du Colombier 		a->ns = count+offset;
97380ee5cbfSDavid du Colombier 		a->s[a->ns] = '\0';
97480ee5cbfSDavid du Colombier 	}
9759a747e4fSDavid du Colombier 	memmove(a->s+offset, r->ifcall.data, count);
9769a747e4fSDavid du Colombier 	r->ofcall.count = count;
97780ee5cbfSDavid du Colombier 	respond(r, nil);
97880ee5cbfSDavid du Colombier }
97980ee5cbfSDavid du Colombier 
98080ee5cbfSDavid du Colombier static void
fsdestroyfid(Fid * fid)9819a747e4fSDavid du Colombier fsdestroyfid(Fid *fid)
98280ee5cbfSDavid du Colombier {
98380ee5cbfSDavid du Colombier 	Aux *a;
98480ee5cbfSDavid du Colombier 
98580ee5cbfSDavid du Colombier 	a = fid->aux;
98680ee5cbfSDavid du Colombier 	if(a==nil)
98780ee5cbfSDavid du Colombier 		return;
98880ee5cbfSDavid du Colombier 
98980ee5cbfSDavid du Colombier 	if(a->ispost && a->s)
99080ee5cbfSDavid du Colombier 		nntppost(net, a->s);
99180ee5cbfSDavid du Colombier 
99280ee5cbfSDavid du Colombier 	free(a->s);
99380ee5cbfSDavid du Colombier 	free(a);
99480ee5cbfSDavid du Colombier }
99580ee5cbfSDavid du Colombier 
99680ee5cbfSDavid du Colombier Srv nntpsrv = {
9979a747e4fSDavid du Colombier .destroyfid=	fsdestroyfid,
99880ee5cbfSDavid du Colombier .attach=	fsattach,
99980ee5cbfSDavid du Colombier .clone=	fsclone,
10009a747e4fSDavid du Colombier .walk1=	fswalk1,
100180ee5cbfSDavid du Colombier .open=	fsopen,
100280ee5cbfSDavid du Colombier .read=	fsread,
100380ee5cbfSDavid du Colombier .write=	fswrite,
100480ee5cbfSDavid du Colombier .stat=	fsstat,
100580ee5cbfSDavid du Colombier };
100680ee5cbfSDavid du Colombier 
100780ee5cbfSDavid du Colombier void
usage(void)100880ee5cbfSDavid du Colombier usage(void)
100980ee5cbfSDavid du Colombier {
10103ff48bf5SDavid du Colombier 	fprint(2, "usage: nntpsrv [-a] [-s service] [-m mtpt] [nntp.server]\n");
101180ee5cbfSDavid du Colombier 	exits("usage");
101280ee5cbfSDavid du Colombier }
101380ee5cbfSDavid du Colombier 
101480ee5cbfSDavid du Colombier void
dumpgroups(Group * g,int ind)10159a747e4fSDavid du Colombier dumpgroups(Group *g, int ind)
10169a747e4fSDavid du Colombier {
10179a747e4fSDavid du Colombier 	int i;
10189a747e4fSDavid du Colombier 
10199a747e4fSDavid du Colombier 	print("%*s%s\n", ind*4, "", g->name);
10209a747e4fSDavid du Colombier 	for(i=0; i<g->nkid; i++)
10219a747e4fSDavid du Colombier 		dumpgroups(g->kid[i], ind+1);
10229a747e4fSDavid du Colombier }
10239a747e4fSDavid du Colombier 
10249a747e4fSDavid du Colombier void
main(int argc,char ** argv)102580ee5cbfSDavid du Colombier main(int argc, char **argv)
102680ee5cbfSDavid du Colombier {
10279a747e4fSDavid du Colombier 	int auth, x;
10289a747e4fSDavid du Colombier 	char *mtpt, *service, *where, *user;
102980ee5cbfSDavid du Colombier 	Netbuf n;
10309a747e4fSDavid du Colombier 	UserPasswd *up;
103180ee5cbfSDavid du Colombier 
103280ee5cbfSDavid du Colombier 	mtpt = "/mnt/news";
103380ee5cbfSDavid du Colombier 	service = nil;
10349a747e4fSDavid du Colombier 	memset(&n, 0, sizeof n);
10359a747e4fSDavid du Colombier 	user = nil;
10369a747e4fSDavid du Colombier 	auth = 0;
103780ee5cbfSDavid du Colombier 	ARGBEGIN{
103880ee5cbfSDavid du Colombier 	case 'D':
10399a747e4fSDavid du Colombier 		chatty9p++;
104080ee5cbfSDavid du Colombier 		break;
104180ee5cbfSDavid du Colombier 	case 'N':
104280ee5cbfSDavid du Colombier 		netdebug = 1;
104380ee5cbfSDavid du Colombier 		break;
10449a747e4fSDavid du Colombier 	case 'a':
10459a747e4fSDavid du Colombier 		auth = 1;
10469a747e4fSDavid du Colombier 		break;
10479a747e4fSDavid du Colombier 	case 'u':
10489a747e4fSDavid du Colombier 		user = EARGF(usage());
10499a747e4fSDavid du Colombier 		break;
105080ee5cbfSDavid du Colombier 	case 's':
105180ee5cbfSDavid du Colombier 		service = EARGF(usage());
105280ee5cbfSDavid du Colombier 		break;
105380ee5cbfSDavid du Colombier 	case 'm':
105480ee5cbfSDavid du Colombier 		mtpt = EARGF(usage());
105580ee5cbfSDavid du Colombier 		break;
10569a747e4fSDavid du Colombier 	default:
10579a747e4fSDavid du Colombier 		usage();
105880ee5cbfSDavid du Colombier 	}ARGEND
105980ee5cbfSDavid du Colombier 
106080ee5cbfSDavid du Colombier 	if(argc > 1)
106180ee5cbfSDavid du Colombier 		usage();
106280ee5cbfSDavid du Colombier 	if(argc==0)
106380ee5cbfSDavid du Colombier 		where = "$nntp";
106480ee5cbfSDavid du Colombier 	else
106580ee5cbfSDavid du Colombier 		where = argv[0];
106680ee5cbfSDavid du Colombier 
106780ee5cbfSDavid du Colombier 	now = time(0);
106880ee5cbfSDavid du Colombier 
106980ee5cbfSDavid du Colombier 	net = &n;
10709a747e4fSDavid du Colombier 	if(auth) {
10719a747e4fSDavid du Colombier 		n.auth = 1;
10729a747e4fSDavid du Colombier 		if(user)
10735d459b5aSDavid du Colombier 			up = auth_getuserpasswd(auth_getkey, "proto=pass service=nntp server=%q user=%q", where, user);
10749a747e4fSDavid du Colombier 		else
10755d459b5aSDavid du Colombier 			up = auth_getuserpasswd(auth_getkey, "proto=pass service=nntp server=%q", where);
10769a747e4fSDavid du Colombier 		if(up == nil)
10779a747e4fSDavid du Colombier 			sysfatal("no password: %r");
107880ee5cbfSDavid du Colombier 
10799a747e4fSDavid du Colombier 		n.user = up->user;
10809a747e4fSDavid du Colombier 		n.pass = up->passwd;
10819a747e4fSDavid du Colombier 	}
108280ee5cbfSDavid du Colombier 
10839a747e4fSDavid du Colombier 	n.addr = netmkaddr(where, "tcp", "nntp");
108480ee5cbfSDavid du Colombier 
108580ee5cbfSDavid du Colombier 	root = emalloc(sizeof *root);
108680ee5cbfSDavid du Colombier 	root->name = estrdup("");
108780ee5cbfSDavid du Colombier 	root->parent = root;
10889a747e4fSDavid du Colombier 
10899a747e4fSDavid du Colombier 	n.fd = -1;
10909a747e4fSDavid du Colombier 	if(nntpconnect(&n) < 0)
10919a747e4fSDavid du Colombier 		sysfatal("nntpconnect: %s", n.response);
10929a747e4fSDavid du Colombier 
10939a747e4fSDavid du Colombier 	x=netdebug;
10949a747e4fSDavid du Colombier 	netdebug=0;
109580ee5cbfSDavid du Colombier 	nntprefreshall(&n);
109680ee5cbfSDavid du Colombier 	netdebug=x;
10979a747e4fSDavid du Colombier //	dumpgroups(root, 0);
109880ee5cbfSDavid du Colombier 
109980ee5cbfSDavid du Colombier 	postmountsrv(&nntpsrv, service, mtpt, MREPL);
110080ee5cbfSDavid du Colombier 	exits(nil);
110180ee5cbfSDavid du Colombier }
110280ee5cbfSDavid du Colombier 
1103