nntpfs.c 18 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105
  1. /*
  2. * Network news transport protocol (NNTP) file server.
  3. *
  4. * Unfortunately, the file system differs from that expected
  5. * by Charles Forsyth's rin news reader. This is partially out
  6. * of my own laziness, but it makes the bookkeeping here
  7. * a lot easier.
  8. */
  9. #include <u.h>
  10. #include <libc.h>
  11. #include <bio.h>
  12. #include <auth.h>
  13. #include <fcall.h>
  14. #include <thread.h>
  15. #include <9p.h>
  16. typedef struct Netbuf Netbuf;
  17. typedef struct Group Group;
  18. struct Netbuf {
  19. Biobuf br;
  20. Biobuf bw;
  21. int lineno;
  22. int fd;
  23. int code; /* last response code */
  24. int auth; /* Authorization required? */
  25. char response[128]; /* last response */
  26. Group *currentgroup;
  27. char *addr;
  28. char *user;
  29. char *pass;
  30. ulong extended; /* supported extensions */
  31. };
  32. struct Group {
  33. char *name;
  34. Group *parent;
  35. Group **kid;
  36. int num;
  37. int nkid;
  38. int lo, hi;
  39. int canpost;
  40. int isgroup; /* might just be piece of hierarchy */
  41. ulong mtime;
  42. ulong atime;
  43. };
  44. /*
  45. * First eight fields are, in order:
  46. * article number, subject, author, date, message-ID,
  47. * references, byte count, line count
  48. * We don't support OVERVIEW.FMT; when I see a server with more
  49. * interesting fields, I'll implement support then. In the meantime,
  50. * the standard defines the first eight fields.
  51. */
  52. /* Extensions */
  53. enum {
  54. Nxover = (1<<0),
  55. Nxhdr = (1<<1),
  56. Nxpat = (1<<2),
  57. Nxlistgp = (1<<3),
  58. };
  59. Group *root;
  60. Netbuf *net;
  61. ulong now;
  62. int netdebug;
  63. int readonly;
  64. void*
  65. erealloc(void *v, ulong n)
  66. {
  67. v = realloc(v, n);
  68. if(v == nil)
  69. sysfatal("out of memory reallocating %lud", n);
  70. setmalloctag(v, getcallerpc(&v));
  71. return v;
  72. }
  73. void*
  74. emalloc(ulong n)
  75. {
  76. void *v;
  77. v = malloc(n);
  78. if(v == nil)
  79. sysfatal("out of memory allocating %lud", n);
  80. memset(v, 0, n);
  81. setmalloctag(v, getcallerpc(&n));
  82. return v;
  83. }
  84. char*
  85. estrdup(char *s)
  86. {
  87. int l;
  88. char *t;
  89. if (s == nil)
  90. return nil;
  91. l = strlen(s)+1;
  92. t = emalloc(l);
  93. memcpy(t, s, l);
  94. setmalloctag(t, getcallerpc(&s));
  95. return t;
  96. }
  97. char*
  98. estrdupn(char *s, int n)
  99. {
  100. int l;
  101. char *t;
  102. l = strlen(s);
  103. if(l > n)
  104. l = n;
  105. t = emalloc(l+1);
  106. memmove(t, s, l);
  107. t[l] = '\0';
  108. setmalloctag(t, getcallerpc(&s));
  109. return t;
  110. }
  111. char*
  112. Nrdline(Netbuf *n)
  113. {
  114. char *p;
  115. int l;
  116. n->lineno++;
  117. Bflush(&n->bw);
  118. if((p = Brdline(&n->br, '\n')) == nil){
  119. werrstr("nntp eof");
  120. return nil;
  121. }
  122. p[l=Blinelen(&n->br)-1] = '\0';
  123. if(l > 0 && p[l-1] == '\r')
  124. p[l-1] = '\0';
  125. if(netdebug)
  126. fprint(2, "-> %s\n", p);
  127. return p;
  128. }
  129. int
  130. nntpresponse(Netbuf *n, int e, char *cmd)
  131. {
  132. int r;
  133. char *p;
  134. for(;;){
  135. p = Nrdline(n);
  136. if(p==nil){
  137. strcpy(n->response, "early nntp eof");
  138. return -1;
  139. }
  140. r = atoi(p);
  141. if(r/100 == 1){ /* BUG? */
  142. fprint(2, "%s\n", p);
  143. continue;
  144. }
  145. break;
  146. }
  147. strecpy(n->response, n->response+sizeof(n->response), p);
  148. if((r=atoi(p)) == 0){
  149. close(n->fd);
  150. n->fd = -1;
  151. fprint(2, "bad nntp response: %s\n", p);
  152. werrstr("bad nntp response");
  153. return -1;
  154. }
  155. n->code = r;
  156. if(0 < e && e<10 && r/100 != e){
  157. fprint(2, "%s: expected %dxx: got %s\n", cmd, e, n->response);
  158. return -1;
  159. }
  160. if(10 <= e && e<100 && r/10 != e){
  161. fprint(2, "%s: expected %dx: got %s\n", cmd, e, n->response);
  162. return -1;
  163. }
  164. if(100 <= e && r != e){
  165. fprint(2, "%s: expected %d: got %s\n", cmd, e, n->response);
  166. return -1;
  167. }
  168. return r;
  169. }
  170. int nntpauth(Netbuf*);
  171. int nntpxcmdprobe(Netbuf*);
  172. int nntpcurrentgroup(Netbuf*, Group*);
  173. /* XXX: bug OVER/XOVER et al. */
  174. static struct {
  175. ulong n;
  176. char *s;
  177. } extensions [] = {
  178. { Nxover, "OVER" },
  179. { Nxhdr, "HDR" },
  180. { Nxpat, "PAT" },
  181. { Nxlistgp, "LISTGROUP" },
  182. { 0, nil }
  183. };
  184. static int indial;
  185. int
  186. nntpconnect(Netbuf *n)
  187. {
  188. n->currentgroup = nil;
  189. close(n->fd);
  190. if((n->fd = dial(n->addr, nil, nil, nil)) < 0){
  191. snprint(n->response, sizeof n->response, "dial: %r");
  192. return -1;
  193. }
  194. Binit(&n->br, n->fd, OREAD);
  195. Binit(&n->bw, n->fd, OWRITE);
  196. if(nntpresponse(n, 20, "greeting") < 0)
  197. return -1;
  198. readonly = (n->code == 201);
  199. indial = 1;
  200. if(n->auth != 0)
  201. nntpauth(n);
  202. // nntpxcmdprobe(n);
  203. indial = 0;
  204. return 0;
  205. }
  206. int
  207. nntpcmd(Netbuf *n, char *cmd, int e)
  208. {
  209. int tried;
  210. tried = 0;
  211. for(;;){
  212. if(netdebug)
  213. fprint(2, "<- %s\n", cmd);
  214. Bprint(&n->bw, "%s\r\n", cmd);
  215. if(nntpresponse(n, e, cmd)>=0 && (e < 0 || n->code/100 != 5))
  216. return 0;
  217. /* redial */
  218. if(indial || tried++ || nntpconnect(n) < 0)
  219. return -1;
  220. }
  221. return -1; /* shut up 8c */
  222. }
  223. int
  224. nntpauth(Netbuf *n)
  225. {
  226. char cmd[256];
  227. snprint(cmd, sizeof cmd, "AUTHINFO USER %s", n->user);
  228. if (nntpcmd(n, cmd, -1) < 0 || n->code != 381) {
  229. fprint(2, "Authentication failed: %s\n", n->response);
  230. return -1;
  231. }
  232. snprint(cmd, sizeof cmd, "AUTHINFO PASS %s", n->pass);
  233. if (nntpcmd(n, cmd, -1) < 0 || n->code != 281) {
  234. fprint(2, "Authentication failed: %s\n", n->response);
  235. return -1;
  236. }
  237. return 0;
  238. }
  239. int
  240. nntpxcmdprobe(Netbuf *n)
  241. {
  242. int i;
  243. char *p;
  244. n->extended = 0;
  245. if (nntpcmd(n, "LIST EXTENSIONS", 0) < 0 || n->code != 202)
  246. return 0;
  247. while((p = Nrdline(n)) != nil) {
  248. if (strcmp(p, ".") == 0)
  249. break;
  250. for(i=0; extensions[i].s != nil; i++)
  251. if (cistrcmp(extensions[i].s, p) == 0) {
  252. n->extended |= extensions[i].n;
  253. break;
  254. }
  255. }
  256. return 0;
  257. }
  258. /* XXX: searching, lazy evaluation */
  259. static int
  260. overcmp(void *v1, void *v2)
  261. {
  262. int a, b;
  263. a = atoi(*(char**)v1);
  264. b = atoi(*(char**)v2);
  265. if(a < b)
  266. return -1;
  267. else if(a > b)
  268. return 1;
  269. return 0;
  270. }
  271. enum {
  272. XoverChunk = 100,
  273. };
  274. char *xover[XoverChunk];
  275. int xoverlo;
  276. int xoverhi;
  277. int xovercount;
  278. Group *xovergroup;
  279. char*
  280. nntpover(Netbuf *n, Group *g, int m)
  281. {
  282. int i, lo, hi, mid, msg;
  283. char *p;
  284. char cmd[64];
  285. if (g->isgroup == 0) /* BUG: should check extension capabilities */
  286. return nil;
  287. if(g != xovergroup || m < xoverlo || m >= xoverhi){
  288. lo = (m/XoverChunk)*XoverChunk;
  289. hi = lo+XoverChunk;
  290. if(lo < g->lo)
  291. lo = g->lo;
  292. else if (lo > g->hi)
  293. lo = g->hi;
  294. if(hi < lo || hi > g->hi)
  295. hi = g->hi;
  296. if(nntpcurrentgroup(n, g) < 0)
  297. return nil;
  298. if(lo == hi)
  299. snprint(cmd, sizeof cmd, "XOVER %d", hi);
  300. else
  301. snprint(cmd, sizeof cmd, "XOVER %d-%d", lo, hi-1);
  302. if(nntpcmd(n, cmd, 224) < 0)
  303. return nil;
  304. for(i=0; (p = Nrdline(n)) != nil; i++) {
  305. if(strcmp(p, ".") == 0)
  306. break;
  307. if(i >= XoverChunk)
  308. sysfatal("news server doesn't play by the rules");
  309. free(xover[i]);
  310. xover[i] = emalloc(strlen(p)+2);
  311. strcpy(xover[i], p);
  312. strcat(xover[i], "\n");
  313. }
  314. qsort(xover, i, sizeof(xover[0]), overcmp);
  315. xovercount = i;
  316. xovergroup = g;
  317. xoverlo = lo;
  318. xoverhi = hi;
  319. }
  320. lo = 0;
  321. hi = xovercount;
  322. /* search for message */
  323. while(lo < hi){
  324. mid = (lo+hi)/2;
  325. msg = atoi(xover[mid]);
  326. if(m == msg)
  327. return xover[mid];
  328. else if(m < msg)
  329. hi = mid;
  330. else
  331. lo = mid+1;
  332. }
  333. return nil;
  334. }
  335. /*
  336. * Return the new Group structure for the group name.
  337. * Destroys name.
  338. */
  339. static int printgroup(char*,Group*);
  340. Group*
  341. findgroup(Group *g, char *name, int mk)
  342. {
  343. int lo, hi, m;
  344. char *p, *q;
  345. static int ngroup;
  346. for(p=name; *p; p=q){
  347. if(q = strchr(p, '.'))
  348. *q++ = '\0';
  349. else
  350. q = p+strlen(p);
  351. lo = 0;
  352. hi = g->nkid;
  353. while(hi-lo > 1){
  354. m = (lo+hi)/2;
  355. if(strcmp(p, g->kid[m]->name) < 0)
  356. hi = m;
  357. else
  358. lo = m;
  359. }
  360. assert(lo==hi || lo==hi-1);
  361. if(lo==hi || strcmp(p, g->kid[lo]->name) != 0){
  362. if(mk==0)
  363. return nil;
  364. if(g->nkid%16 == 0)
  365. g->kid = erealloc(g->kid, (g->nkid+16)*sizeof(g->kid[0]));
  366. /*
  367. * if we're down to a single place 'twixt lo and hi, the insertion might need
  368. * to go at lo or at hi. strcmp to find out. the list needs to stay sorted.
  369. */
  370. if(lo==hi-1 && strcmp(p, g->kid[lo]->name) < 0)
  371. hi = lo;
  372. if(hi < g->nkid)
  373. memmove(g->kid+hi+1, g->kid+hi, sizeof(g->kid[0])*(g->nkid-hi));
  374. g->nkid++;
  375. g->kid[hi] = emalloc(sizeof(*g));
  376. g->kid[hi]->parent = g;
  377. g = g->kid[hi];
  378. g->name = estrdup(p);
  379. g->num = ++ngroup;
  380. g->mtime = time(0);
  381. }else
  382. g = g->kid[lo];
  383. }
  384. if(mk)
  385. g->isgroup = 1;
  386. return g;
  387. }
  388. static int
  389. printgroup(char *s, Group *g)
  390. {
  391. if(g->parent == g)
  392. return 0;
  393. if(printgroup(s, g->parent))
  394. strcat(s, ".");
  395. strcat(s, g->name);
  396. return 1;
  397. }
  398. static char*
  399. Nreaddata(Netbuf *n)
  400. {
  401. char *p, *q;
  402. int l;
  403. p = nil;
  404. l = 0;
  405. for(;;){
  406. q = Nrdline(n);
  407. if(q==nil){
  408. free(p);
  409. return nil;
  410. }
  411. if(strcmp(q, ".")==0)
  412. return p;
  413. if(q[0]=='.')
  414. q++;
  415. p = erealloc(p, l+strlen(q)+1+1);
  416. strcpy(p+l, q);
  417. strcat(p+l, "\n");
  418. l += strlen(p+l);
  419. }
  420. return nil; /* shut up 8c */
  421. }
  422. /*
  423. * Return the output of a HEAD, BODY, or ARTICLE command.
  424. */
  425. char*
  426. nntpget(Netbuf *n, Group *g, int msg, char *retr)
  427. {
  428. char *s;
  429. char cmd[1024];
  430. if(g->isgroup == 0){
  431. werrstr("not a group");
  432. return nil;
  433. }
  434. if(strcmp(retr, "XOVER") == 0){
  435. s = nntpover(n, g, msg);
  436. if(s == nil)
  437. s = "";
  438. return estrdup(s);
  439. }
  440. if(nntpcurrentgroup(n, g) < 0)
  441. return nil;
  442. sprint(cmd, "%s %d", retr, msg);
  443. nntpcmd(n, cmd, 0);
  444. if(n->code/10 != 22)
  445. return nil;
  446. return Nreaddata(n);
  447. }
  448. int
  449. nntpcurrentgroup(Netbuf *n, Group *g)
  450. {
  451. char cmd[1024];
  452. if(n->currentgroup != g){
  453. strcpy(cmd, "GROUP ");
  454. printgroup(cmd, g);
  455. if(nntpcmd(n, cmd, 21) < 0)
  456. return -1;
  457. n->currentgroup = g;
  458. }
  459. return 0;
  460. }
  461. void
  462. nntprefreshall(Netbuf *n)
  463. {
  464. char *f[10], *p;
  465. int hi, lo, nf;
  466. Group *g;
  467. if(nntpcmd(n, "LIST", 21) < 0)
  468. return;
  469. while(p = Nrdline(n)){
  470. if(strcmp(p, ".")==0)
  471. break;
  472. nf = getfields(p, f, nelem(f), 1, "\t\r\n ");
  473. if(nf != 4){
  474. int i;
  475. for(i=0; i<nf; i++)
  476. fprint(2, "%s%s", i?" ":"", f[i]);
  477. fprint(2, "\n");
  478. fprint(2, "syntax error in group list, line %d", n->lineno);
  479. return;
  480. }
  481. g = findgroup(root, f[0], 1);
  482. hi = strtol(f[1], 0, 10)+1;
  483. lo = strtol(f[2], 0, 10);
  484. if(g->hi != hi){
  485. g->hi = hi;
  486. if(g->lo==0)
  487. g->lo = lo;
  488. g->canpost = f[3][0] == 'y';
  489. g->mtime = time(0);
  490. }
  491. }
  492. }
  493. void
  494. nntprefresh(Netbuf *n, Group *g)
  495. {
  496. char cmd[1024];
  497. char *f[5];
  498. int lo, hi;
  499. if(g->isgroup==0)
  500. return;
  501. if(time(0) - g->atime < 30)
  502. return;
  503. strcpy(cmd, "GROUP ");
  504. printgroup(cmd, g);
  505. if(nntpcmd(n, cmd, 21) < 0){
  506. n->currentgroup = nil;
  507. return;
  508. }
  509. n->currentgroup = g;
  510. if(tokenize(n->response, f, nelem(f)) < 4){
  511. fprint(2, "error reading GROUP response");
  512. return;
  513. }
  514. /* backwards from LIST! */
  515. hi = strtol(f[3], 0, 10)+1;
  516. lo = strtol(f[2], 0, 10);
  517. if(g->hi != hi){
  518. g->mtime = time(0);
  519. if(g->lo==0)
  520. g->lo = lo;
  521. g->hi = hi;
  522. }
  523. g->atime = time(0);
  524. }
  525. char*
  526. nntppost(Netbuf *n, char *msg)
  527. {
  528. char *p, *q;
  529. if(nntpcmd(n, "POST", 34) < 0)
  530. return n->response;
  531. for(p=msg; *p; p=q){
  532. if(q = strchr(p, '\n'))
  533. *q++ = '\0';
  534. else
  535. q = p+strlen(p);
  536. if(p[0]=='.')
  537. Bputc(&n->bw, '.');
  538. Bwrite(&n->bw, p, strlen(p));
  539. Bputc(&n->bw, '\r');
  540. Bputc(&n->bw, '\n');
  541. }
  542. Bprint(&n->bw, ".\r\n");
  543. if(nntpresponse(n, 0, nil) < 0)
  544. return n->response;
  545. if(n->code/100 != 2)
  546. return n->response;
  547. return nil;
  548. }
  549. /*
  550. * Because an expanded QID space makes thngs much easier,
  551. * we sleazily use the version part of the QID as more path bits.
  552. * Since we make sure not to mount ourselves cached, this
  553. * doesn't break anything (unless you want to bind on top of
  554. * things in this file system). In the next version of 9P, we'll
  555. * have more QID bits to play with.
  556. *
  557. * The newsgroup is encoded in the top 15 bits
  558. * of the path. The message number is the bottom 17 bits.
  559. * The file within the message directory is in the version [sic].
  560. */
  561. enum { /* file qids */
  562. Qhead,
  563. Qbody,
  564. Qarticle,
  565. Qxover,
  566. Nfile,
  567. };
  568. char *filename[] = {
  569. "header",
  570. "body",
  571. "article",
  572. "xover",
  573. };
  574. char *nntpname[] = {
  575. "HEAD",
  576. "BODY",
  577. "ARTICLE",
  578. "XOVER",
  579. };
  580. #define GROUP(p) (((p)>>17)&0x3FFF)
  581. #define MESSAGE(p) ((p)&0x1FFFF)
  582. #define FILE(v) ((v)&0x3)
  583. #define PATH(g,m) ((((g)&0x3FFF)<<17)|((m)&0x1FFFF))
  584. #define POST(g) PATH(0,g,0)
  585. #define VERS(f) ((f)&0x3)
  586. typedef struct Aux Aux;
  587. struct Aux {
  588. Group *g;
  589. int n;
  590. int ispost;
  591. int file;
  592. char *s;
  593. int ns;
  594. int offset;
  595. };
  596. static void
  597. fsattach(Req *r)
  598. {
  599. Aux *a;
  600. char *spec;
  601. spec = r->ifcall.aname;
  602. if(spec && spec[0]){
  603. respond(r, "invalid attach specifier");
  604. return;
  605. }
  606. a = emalloc(sizeof *a);
  607. a->g = root;
  608. a->n = -1;
  609. r->fid->aux = a;
  610. r->ofcall.qid = (Qid){0, 0, QTDIR};
  611. r->fid->qid = r->ofcall.qid;
  612. respond(r, nil);
  613. }
  614. static char*
  615. fsclone(Fid *ofid, Fid *fid)
  616. {
  617. Aux *a;
  618. a = emalloc(sizeof(*a));
  619. *a = *(Aux*)ofid->aux;
  620. fid->aux = a;
  621. return nil;
  622. }
  623. static char*
  624. fswalk1(Fid *fid, char *name, Qid *qid)
  625. {
  626. char *p;
  627. int i, isdotdot, n;
  628. Aux *a;
  629. Group *ng;
  630. isdotdot = strcmp(name, "..")==0;
  631. a = fid->aux;
  632. if(a->s) /* file */
  633. return "protocol botch";
  634. if(a->n != -1){
  635. if(isdotdot){
  636. *qid = (Qid){PATH(a->g->num, 0), 0, QTDIR};
  637. fid->qid = *qid;
  638. a->n = -1;
  639. return nil;
  640. }
  641. for(i=0; i<Nfile; i++){
  642. if(strcmp(name, filename[i])==0){
  643. if(a->s = nntpget(net, a->g, a->n, nntpname[i])){
  644. *qid = (Qid){PATH(a->g->num, a->n), Qbody, 0};
  645. fid->qid = *qid;
  646. a->file = i;
  647. return nil;
  648. }else
  649. return "file does not exist";
  650. }
  651. }
  652. return "file does not exist";
  653. }
  654. if(isdotdot){
  655. a->g = a->g->parent;
  656. *qid = (Qid){PATH(a->g->num, 0), 0, QTDIR};
  657. fid->qid = *qid;
  658. return nil;
  659. }
  660. if(a->g->isgroup && !readonly && a->g->canpost
  661. && strcmp(name, "post")==0){
  662. a->ispost = 1;
  663. *qid = (Qid){PATH(a->g->num, 0), 0, 0};
  664. fid->qid = *qid;
  665. return nil;
  666. }
  667. if(ng = findgroup(a->g, name, 0)){
  668. a->g = ng;
  669. *qid = (Qid){PATH(a->g->num, 0), 0, QTDIR};
  670. fid->qid = *qid;
  671. return nil;
  672. }
  673. n = strtoul(name, &p, 0);
  674. if('0'<=name[0] && name[0]<='9' && *p=='\0' && a->g->lo<=n && n<a->g->hi){
  675. a->n = n;
  676. *qid = (Qid){PATH(a->g->num, n+1-a->g->lo), 0, QTDIR};
  677. fid->qid = *qid;
  678. return nil;
  679. }
  680. return "file does not exist";
  681. }
  682. static void
  683. fsopen(Req *r)
  684. {
  685. Aux *a;
  686. a = r->fid->aux;
  687. if((a->ispost && (r->ifcall.mode&~OTRUNC) != OWRITE)
  688. || (!a->ispost && r->ifcall.mode != OREAD))
  689. respond(r, "permission denied");
  690. else
  691. respond(r, nil);
  692. }
  693. static void
  694. fillstat(Dir *d, Aux *a)
  695. {
  696. char buf[32];
  697. Group *g;
  698. memset(d, 0, sizeof *d);
  699. d->uid = estrdup("nntp");
  700. d->gid = estrdup("nntp");
  701. g = a->g;
  702. d->atime = d->mtime = g->mtime;
  703. if(a->ispost){
  704. d->name = estrdup("post");
  705. d->mode = 0222;
  706. d->qid = (Qid){PATH(g->num, 0), 0, 0};
  707. d->length = a->ns;
  708. return;
  709. }
  710. if(a->s){ /* article file */
  711. d->name = estrdup(filename[a->file]);
  712. d->mode = 0444;
  713. d->qid = (Qid){PATH(g->num, a->n+1-g->lo), a->file, 0};
  714. return;
  715. }
  716. if(a->n != -1){ /* article directory */
  717. sprint(buf, "%d", a->n);
  718. d->name = estrdup(buf);
  719. d->mode = DMDIR|0555;
  720. d->qid = (Qid){PATH(g->num, a->n+1-g->lo), 0, QTDIR};
  721. return;
  722. }
  723. /* group directory */
  724. if(g->name[0])
  725. d->name = estrdup(g->name);
  726. else
  727. d->name = estrdup("/");
  728. d->mode = DMDIR|0555;
  729. d->qid = (Qid){PATH(g->num, 0), g->hi-1, QTDIR};
  730. }
  731. static int
  732. dirfillstat(Dir *d, Aux *a, int i)
  733. {
  734. int ndir;
  735. Group *g;
  736. char buf[32];
  737. memset(d, 0, sizeof *d);
  738. d->uid = estrdup("nntp");
  739. d->gid = estrdup("nntp");
  740. g = a->g;
  741. d->atime = d->mtime = g->mtime;
  742. if(a->n != -1){ /* article directory */
  743. if(i >= Nfile)
  744. return -1;
  745. d->name = estrdup(filename[i]);
  746. d->mode = 0444;
  747. d->qid = (Qid){PATH(g->num, a->n), i, 0};
  748. return 0;
  749. }
  750. /* hierarchy directory: child groups */
  751. if(i < g->nkid){
  752. d->name = estrdup(g->kid[i]->name);
  753. d->mode = DMDIR|0555;
  754. d->qid = (Qid){PATH(g->kid[i]->num, 0), g->kid[i]->hi-1, QTDIR};
  755. return 0;
  756. }
  757. i -= g->nkid;
  758. /* group directory: post file */
  759. if(g->isgroup && !readonly && g->canpost){
  760. if(i < 1){
  761. d->name = estrdup("post");
  762. d->mode = 0222;
  763. d->qid = (Qid){PATH(g->num, 0), 0, 0};
  764. return 0;
  765. }
  766. i--;
  767. }
  768. /* group directory: child articles */
  769. ndir = g->hi - g->lo;
  770. if(i < ndir){
  771. sprint(buf, "%d", g->lo+i);
  772. d->name = estrdup(buf);
  773. d->mode = DMDIR|0555;
  774. d->qid = (Qid){PATH(g->num, i+1), 0, QTDIR};
  775. return 0;
  776. }
  777. return -1;
  778. }
  779. static void
  780. fsstat(Req *r)
  781. {
  782. Aux *a;
  783. a = r->fid->aux;
  784. if(r->fid->qid.path == 0 && (r->fid->qid.type & QTDIR))
  785. nntprefreshall(net);
  786. else if(a->g->isgroup)
  787. nntprefresh(net, a->g);
  788. fillstat(&r->d, a);
  789. respond(r, nil);
  790. }
  791. static void
  792. fsread(Req *r)
  793. {
  794. int offset, n;
  795. Aux *a;
  796. char *p, *ep;
  797. Dir d;
  798. a = r->fid->aux;
  799. if(a->s){
  800. readstr(r, a->s);
  801. respond(r, nil);
  802. return;
  803. }
  804. if(r->ifcall.offset == 0)
  805. offset = 0;
  806. else
  807. offset = a->offset;
  808. p = r->ofcall.data;
  809. ep = r->ofcall.data+r->ifcall.count;
  810. for(; p+2 < ep; p += n){
  811. if(dirfillstat(&d, a, offset) < 0)
  812. break;
  813. n=convD2M(&d, (uchar*)p, ep-p);
  814. free(d.name);
  815. free(d.uid);
  816. free(d.gid);
  817. free(d.muid);
  818. if(n <= BIT16SZ)
  819. break;
  820. offset++;
  821. }
  822. a->offset = offset;
  823. r->ofcall.count = p - r->ofcall.data;
  824. respond(r, nil);
  825. }
  826. static void
  827. fswrite(Req *r)
  828. {
  829. Aux *a;
  830. long count;
  831. vlong offset;
  832. a = r->fid->aux;
  833. if(r->ifcall.count == 0){ /* commit */
  834. respond(r, nntppost(net, a->s));
  835. free(a->s);
  836. a->ns = 0;
  837. a->s = nil;
  838. return;
  839. }
  840. count = r->ifcall.count;
  841. offset = r->ifcall.offset;
  842. if(a->ns < count+offset+1){
  843. a->s = erealloc(a->s, count+offset+1);
  844. a->ns = count+offset;
  845. a->s[a->ns] = '\0';
  846. }
  847. memmove(a->s+offset, r->ifcall.data, count);
  848. r->ofcall.count = count;
  849. respond(r, nil);
  850. }
  851. static void
  852. fsdestroyfid(Fid *fid)
  853. {
  854. Aux *a;
  855. a = fid->aux;
  856. if(a==nil)
  857. return;
  858. if(a->ispost && a->s)
  859. nntppost(net, a->s);
  860. free(a->s);
  861. free(a);
  862. }
  863. Srv nntpsrv = {
  864. .destroyfid= fsdestroyfid,
  865. .attach= fsattach,
  866. .clone= fsclone,
  867. .walk1= fswalk1,
  868. .open= fsopen,
  869. .read= fsread,
  870. .write= fswrite,
  871. .stat= fsstat,
  872. };
  873. void
  874. usage(void)
  875. {
  876. fprint(2, "usage: nntpsrv [-a] [-s service] [-m mtpt] [nntp.server]\n");
  877. exits("usage");
  878. }
  879. void
  880. dumpgroups(Group *g, int ind)
  881. {
  882. int i;
  883. print("%*s%s\n", ind*4, "", g->name);
  884. for(i=0; i<g->nkid; i++)
  885. dumpgroups(g->kid[i], ind+1);
  886. }
  887. void
  888. main(int argc, char **argv)
  889. {
  890. int auth, x;
  891. char *mtpt, *service, *where, *user;
  892. Netbuf n;
  893. UserPasswd *up;
  894. mtpt = "/mnt/news";
  895. service = nil;
  896. memset(&n, 0, sizeof n);
  897. user = nil;
  898. auth = 0;
  899. ARGBEGIN{
  900. case 'D':
  901. chatty9p++;
  902. break;
  903. case 'N':
  904. netdebug = 1;
  905. break;
  906. case 'a':
  907. auth = 1;
  908. break;
  909. case 'u':
  910. user = EARGF(usage());
  911. break;
  912. case 's':
  913. service = EARGF(usage());
  914. break;
  915. case 'm':
  916. mtpt = EARGF(usage());
  917. break;
  918. default:
  919. usage();
  920. }ARGEND
  921. if(argc > 1)
  922. usage();
  923. if(argc==0)
  924. where = "$nntp";
  925. else
  926. where = argv[0];
  927. now = time(0);
  928. net = &n;
  929. if(auth) {
  930. n.auth = 1;
  931. if(user)
  932. up = auth_getuserpasswd(auth_getkey, "proto=pass service=nntp server=%q user=%q", where, user);
  933. else
  934. up = auth_getuserpasswd(auth_getkey, "proto=pass service=nntp server=%q", where);
  935. if(up == nil)
  936. sysfatal("no password: %r");
  937. n.user = up->user;
  938. n.pass = up->passwd;
  939. }
  940. n.addr = netmkaddr(where, "tcp", "nntp");
  941. root = emalloc(sizeof *root);
  942. root->name = estrdup("");
  943. root->parent = root;
  944. n.fd = -1;
  945. if(nntpconnect(&n) < 0)
  946. sysfatal("nntpconnect: %s", n.response);
  947. x=netdebug;
  948. netdebug=0;
  949. nntprefreshall(&n);
  950. netdebug=x;
  951. // dumpgroups(root, 0);
  952. postmountsrv(&nntpsrv, service, mtpt, MREPL);
  953. exits(nil);
  954. }