📄 nntpfs.c
字号:
/* * 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;}intnntpresponse(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;intnntpconnect(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;}intnntpcmd(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; }}intnntpauth(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;}intnntpxcmdprobe(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 intovercmp(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 intprintgroup(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);}intnntpcurrentgroup(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;}voidnntprefreshall(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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -