nntpfs.c 18 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102
  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. }
  222. int
  223. nntpauth(Netbuf *n)
  224. {
  225. char cmd[256];
  226. snprint(cmd, sizeof cmd, "AUTHINFO USER %s", n->user);
  227. if (nntpcmd(n, cmd, -1) < 0 || n->code != 381) {
  228. fprint(2, "Authentication failed: %s\n", n->response);
  229. return -1;
  230. }
  231. snprint(cmd, sizeof cmd, "AUTHINFO PASS %s", n->pass);
  232. if (nntpcmd(n, cmd, -1) < 0 || n->code != 281) {
  233. fprint(2, "Authentication failed: %s\n", n->response);
  234. return -1;
  235. }
  236. return 0;
  237. }
  238. int
  239. nntpxcmdprobe(Netbuf *n)
  240. {
  241. int i;
  242. char *p;
  243. n->extended = 0;
  244. if (nntpcmd(n, "LIST EXTENSIONS", 0) < 0 || n->code != 202)
  245. return 0;
  246. while((p = Nrdline(n)) != nil) {
  247. if (strcmp(p, ".") == 0)
  248. break;
  249. for(i=0; extensions[i].s != nil; i++)
  250. if (cistrcmp(extensions[i].s, p) == 0) {
  251. n->extended |= extensions[i].n;
  252. break;
  253. }
  254. }
  255. return 0;
  256. }
  257. /* XXX: searching, lazy evaluation */
  258. static int
  259. overcmp(void *v1, void *v2)
  260. {
  261. int a, b;
  262. a = atoi(*(char**)v1);
  263. b = atoi(*(char**)v2);
  264. if(a < b)
  265. return -1;
  266. else if(a > b)
  267. return 1;
  268. return 0;
  269. }
  270. enum {
  271. XoverChunk = 100,
  272. };
  273. char *xover[XoverChunk];
  274. int xoverlo;
  275. int xoverhi;
  276. int xovercount;
  277. Group *xovergroup;
  278. char*
  279. nntpover(Netbuf *n, Group *g, int m)
  280. {
  281. int i, lo, hi, mid, msg;
  282. char *p;
  283. char cmd[64];
  284. if (g->isgroup == 0) /* BUG: should check extension capabilities */
  285. return nil;
  286. if(g != xovergroup || m < xoverlo || m >= xoverhi){
  287. lo = (m/XoverChunk)*XoverChunk;
  288. hi = lo+XoverChunk;
  289. if(lo < g->lo)
  290. lo = g->lo;
  291. else if (lo > g->hi)
  292. lo = g->hi;
  293. if(hi < lo || hi > g->hi)
  294. hi = g->hi;
  295. if(nntpcurrentgroup(n, g) < 0)
  296. return nil;
  297. if(lo == hi)
  298. snprint(cmd, sizeof cmd, "XOVER %d", hi);
  299. else
  300. snprint(cmd, sizeof cmd, "XOVER %d-%d", lo, hi-1);
  301. if(nntpcmd(n, cmd, 224) < 0)
  302. return nil;
  303. for(i=0; (p = Nrdline(n)) != nil; i++) {
  304. if(strcmp(p, ".") == 0)
  305. break;
  306. if(i >= XoverChunk)
  307. sysfatal("news server doesn't play by the rules");
  308. free(xover[i]);
  309. xover[i] = emalloc(strlen(p)+2);
  310. strcpy(xover[i], p);
  311. strcat(xover[i], "\n");
  312. }
  313. qsort(xover, i, sizeof(xover[0]), overcmp);
  314. xovercount = i;
  315. xovergroup = g;
  316. xoverlo = lo;
  317. xoverhi = hi;
  318. }
  319. lo = 0;
  320. hi = xovercount;
  321. /* search for message */
  322. while(lo < hi){
  323. mid = (lo+hi)/2;
  324. msg = atoi(xover[mid]);
  325. if(m == msg)
  326. return xover[mid];
  327. else if(m < msg)
  328. hi = mid;
  329. else
  330. lo = mid+1;
  331. }
  332. return nil;
  333. }
  334. /*
  335. * Return the new Group structure for the group name.
  336. * Destroys name.
  337. */
  338. static int printgroup(char*,Group*);
  339. Group*
  340. findgroup(Group *g, char *name, int mk)
  341. {
  342. int lo, hi, m;
  343. char *p, *q;
  344. static int ngroup;
  345. for(p=name; *p; p=q){
  346. if(q = strchr(p, '.'))
  347. *q++ = '\0';
  348. else
  349. q = p+strlen(p);
  350. lo = 0;
  351. hi = g->nkid;
  352. while(hi-lo > 1){
  353. m = (lo+hi)/2;
  354. if(strcmp(p, g->kid[m]->name) < 0)
  355. hi = m;
  356. else
  357. lo = m;
  358. }
  359. assert(lo==hi || lo==hi-1);
  360. if(lo==hi || strcmp(p, g->kid[lo]->name) != 0){
  361. if(mk==0)
  362. return nil;
  363. if(g->nkid%16 == 0)
  364. g->kid = erealloc(g->kid, (g->nkid+16)*sizeof(g->kid[0]));
  365. /*
  366. * if we're down to a single place 'twixt lo and hi, the insertion might need
  367. * to go at lo or at hi. strcmp to find out. the list needs to stay sorted.
  368. */
  369. if(lo==hi-1 && strcmp(p, g->kid[lo]->name) < 0)
  370. hi = lo;
  371. if(hi < g->nkid)
  372. memmove(g->kid+hi+1, g->kid+hi, sizeof(g->kid[0])*(g->nkid-hi));
  373. g->nkid++;
  374. g->kid[hi] = emalloc(sizeof(*g));
  375. g->kid[hi]->parent = g;
  376. g = g->kid[hi];
  377. g->name = estrdup(p);
  378. g->num = ++ngroup;
  379. g->mtime = time(0);
  380. }else
  381. g = g->kid[lo];
  382. }
  383. if(mk)
  384. g->isgroup = 1;
  385. return g;
  386. }
  387. static int
  388. printgroup(char *s, Group *g)
  389. {
  390. if(g->parent == g)
  391. return 0;
  392. if(printgroup(s, g->parent))
  393. strcat(s, ".");
  394. strcat(s, g->name);
  395. return 1;
  396. }
  397. static char*
  398. Nreaddata(Netbuf *n)
  399. {
  400. char *p, *q;
  401. int l;
  402. p = nil;
  403. l = 0;
  404. for(;;){
  405. q = Nrdline(n);
  406. if(q==nil){
  407. free(p);
  408. return nil;
  409. }
  410. if(strcmp(q, ".")==0)
  411. return p;
  412. if(q[0]=='.')
  413. q++;
  414. p = erealloc(p, l+strlen(q)+1+1);
  415. strcpy(p+l, q);
  416. strcat(p+l, "\n");
  417. l += strlen(p+l);
  418. }
  419. }
  420. /*
  421. * Return the output of a HEAD, BODY, or ARTICLE command.
  422. */
  423. char*
  424. nntpget(Netbuf *n, Group *g, int msg, char *retr)
  425. {
  426. char *s;
  427. char cmd[1024];
  428. if(g->isgroup == 0){
  429. werrstr("not a group");
  430. return nil;
  431. }
  432. if(strcmp(retr, "XOVER") == 0){
  433. s = nntpover(n, g, msg);
  434. if(s == nil)
  435. s = "";
  436. return estrdup(s);
  437. }
  438. if(nntpcurrentgroup(n, g) < 0)
  439. return nil;
  440. sprint(cmd, "%s %d", retr, msg);
  441. nntpcmd(n, cmd, 0);
  442. if(n->code/10 != 22)
  443. return nil;
  444. return Nreaddata(n);
  445. }
  446. int
  447. nntpcurrentgroup(Netbuf *n, Group *g)
  448. {
  449. char cmd[1024];
  450. if(n->currentgroup != g){
  451. strcpy(cmd, "GROUP ");
  452. printgroup(cmd, g);
  453. if(nntpcmd(n, cmd, 21) < 0)
  454. return -1;
  455. n->currentgroup = g;
  456. }
  457. return 0;
  458. }
  459. void
  460. nntprefreshall(Netbuf *n)
  461. {
  462. char *f[10], *p;
  463. int hi, lo, nf;
  464. Group *g;
  465. if(nntpcmd(n, "LIST", 21) < 0)
  466. return;
  467. while(p = Nrdline(n)){
  468. if(strcmp(p, ".")==0)
  469. break;
  470. nf = getfields(p, f, nelem(f), 1, "\t\r\n ");
  471. if(nf != 4){
  472. int i;
  473. for(i=0; i<nf; i++)
  474. fprint(2, "%s%s", i?" ":"", f[i]);
  475. fprint(2, "\n");
  476. fprint(2, "syntax error in group list, line %d", n->lineno);
  477. return;
  478. }
  479. g = findgroup(root, f[0], 1);
  480. hi = strtol(f[1], 0, 10)+1;
  481. lo = strtol(f[2], 0, 10);
  482. if(g->hi != hi){
  483. g->hi = hi;
  484. if(g->lo==0)
  485. g->lo = lo;
  486. g->canpost = f[3][0] == 'y';
  487. g->mtime = time(0);
  488. }
  489. }
  490. }
  491. void
  492. nntprefresh(Netbuf *n, Group *g)
  493. {
  494. char cmd[1024];
  495. char *f[5];
  496. int lo, hi;
  497. if(g->isgroup==0)
  498. return;
  499. if(time(0) - g->atime < 30)
  500. return;
  501. strcpy(cmd, "GROUP ");
  502. printgroup(cmd, g);
  503. if(nntpcmd(n, cmd, 21) < 0){
  504. n->currentgroup = nil;
  505. return;
  506. }
  507. n->currentgroup = g;
  508. if(tokenize(n->response, f, nelem(f)) < 4){
  509. fprint(2, "error reading GROUP response");
  510. return;
  511. }
  512. /* backwards from LIST! */
  513. hi = strtol(f[3], 0, 10)+1;
  514. lo = strtol(f[2], 0, 10);
  515. if(g->hi != hi){
  516. g->mtime = time(0);
  517. if(g->lo==0)
  518. g->lo = lo;
  519. g->hi = hi;
  520. }
  521. g->atime = time(0);
  522. }
  523. char*
  524. nntppost(Netbuf *n, char *msg)
  525. {
  526. char *p, *q;
  527. if(nntpcmd(n, "POST", 34) < 0)
  528. return n->response;
  529. for(p=msg; *p; p=q){
  530. if(q = strchr(p, '\n'))
  531. *q++ = '\0';
  532. else
  533. q = p+strlen(p);
  534. if(p[0]=='.')
  535. Bputc(&n->bw, '.');
  536. Bwrite(&n->bw, p, strlen(p));
  537. Bputc(&n->bw, '\r');
  538. Bputc(&n->bw, '\n');
  539. }
  540. Bprint(&n->bw, ".\r\n");
  541. if(nntpresponse(n, 0, nil) < 0)
  542. return n->response;
  543. if(n->code/100 != 2)
  544. return n->response;
  545. return nil;
  546. }
  547. /*
  548. * Because an expanded QID space makes thngs much easier,
  549. * we sleazily use the version part of the QID as more path bits.
  550. * Since we make sure not to mount ourselves cached, this
  551. * doesn't break anything (unless you want to bind on top of
  552. * things in this file system). In the next version of 9P, we'll
  553. * have more QID bits to play with.
  554. *
  555. * The newsgroup is encoded in the top 15 bits
  556. * of the path. The message number is the bottom 17 bits.
  557. * The file within the message directory is in the version [sic].
  558. */
  559. enum { /* file qids */
  560. Qhead,
  561. Qbody,
  562. Qarticle,
  563. Qxover,
  564. Nfile,
  565. };
  566. char *filename[] = {
  567. "header",
  568. "body",
  569. "article",
  570. "xover",
  571. };
  572. char *nntpname[] = {
  573. "HEAD",
  574. "BODY",
  575. "ARTICLE",
  576. "XOVER",
  577. };
  578. #define GROUP(p) (((p)>>17)&0x3FFF)
  579. #define MESSAGE(p) ((p)&0x1FFFF)
  580. #define FILE(v) ((v)&0x3)
  581. #define PATH(g,m) ((((g)&0x3FFF)<<17)|((m)&0x1FFFF))
  582. #define POST(g) PATH(0,g,0)
  583. #define VERS(f) ((f)&0x3)
  584. typedef struct Aux Aux;
  585. struct Aux {
  586. Group *g;
  587. int n;
  588. int ispost;
  589. int file;
  590. char *s;
  591. int ns;
  592. int offset;
  593. };
  594. static void
  595. fsattach(Req *r)
  596. {
  597. Aux *a;
  598. char *spec;
  599. spec = r->ifcall.aname;
  600. if(spec && spec[0]){
  601. respond(r, "invalid attach specifier");
  602. return;
  603. }
  604. a = emalloc(sizeof *a);
  605. a->g = root;
  606. a->n = -1;
  607. r->fid->aux = a;
  608. r->ofcall.qid = (Qid){0, 0, QTDIR};
  609. r->fid->qid = r->ofcall.qid;
  610. respond(r, nil);
  611. }
  612. static char*
  613. fsclone(Fid *ofid, Fid *fid)
  614. {
  615. Aux *a;
  616. a = emalloc(sizeof(*a));
  617. *a = *(Aux*)ofid->aux;
  618. fid->aux = a;
  619. return nil;
  620. }
  621. static char*
  622. fswalk1(Fid *fid, char *name, Qid *qid)
  623. {
  624. char *p;
  625. int i, isdotdot, n;
  626. Aux *a;
  627. Group *ng;
  628. isdotdot = strcmp(name, "..")==0;
  629. a = fid->aux;
  630. if(a->s) /* file */
  631. return "protocol botch";
  632. if(a->n != -1){
  633. if(isdotdot){
  634. *qid = (Qid){PATH(a->g->num, 0), 0, QTDIR};
  635. fid->qid = *qid;
  636. a->n = -1;
  637. return nil;
  638. }
  639. for(i=0; i<Nfile; i++){
  640. if(strcmp(name, filename[i])==0){
  641. if(a->s = nntpget(net, a->g, a->n, nntpname[i])){
  642. *qid = (Qid){PATH(a->g->num, a->n), Qbody, 0};
  643. fid->qid = *qid;
  644. a->file = i;
  645. return nil;
  646. }else
  647. return "file does not exist";
  648. }
  649. }
  650. return "file does not exist";
  651. }
  652. if(isdotdot){
  653. a->g = a->g->parent;
  654. *qid = (Qid){PATH(a->g->num, 0), 0, QTDIR};
  655. fid->qid = *qid;
  656. return nil;
  657. }
  658. if(a->g->isgroup && !readonly && a->g->canpost
  659. && strcmp(name, "post")==0){
  660. a->ispost = 1;
  661. *qid = (Qid){PATH(a->g->num, 0), 0, 0};
  662. fid->qid = *qid;
  663. return nil;
  664. }
  665. if(ng = findgroup(a->g, name, 0)){
  666. a->g = ng;
  667. *qid = (Qid){PATH(a->g->num, 0), 0, QTDIR};
  668. fid->qid = *qid;
  669. return nil;
  670. }
  671. n = strtoul(name, &p, 0);
  672. if('0'<=name[0] && name[0]<='9' && *p=='\0' && a->g->lo<=n && n<a->g->hi){
  673. a->n = n;
  674. *qid = (Qid){PATH(a->g->num, n+1-a->g->lo), 0, QTDIR};
  675. fid->qid = *qid;
  676. return nil;
  677. }
  678. return "file does not exist";
  679. }
  680. static void
  681. fsopen(Req *r)
  682. {
  683. Aux *a;
  684. a = r->fid->aux;
  685. if((a->ispost && (r->ifcall.mode&~OTRUNC) != OWRITE)
  686. || (!a->ispost && r->ifcall.mode != OREAD))
  687. respond(r, "permission denied");
  688. else
  689. respond(r, nil);
  690. }
  691. static void
  692. fillstat(Dir *d, Aux *a)
  693. {
  694. char buf[32];
  695. Group *g;
  696. memset(d, 0, sizeof *d);
  697. d->uid = estrdup("nntp");
  698. d->gid = estrdup("nntp");
  699. g = a->g;
  700. d->atime = d->mtime = g->mtime;
  701. if(a->ispost){
  702. d->name = estrdup("post");
  703. d->mode = 0222;
  704. d->qid = (Qid){PATH(g->num, 0), 0, 0};
  705. d->length = a->ns;
  706. return;
  707. }
  708. if(a->s){ /* article file */
  709. d->name = estrdup(filename[a->file]);
  710. d->mode = 0444;
  711. d->qid = (Qid){PATH(g->num, a->n+1-g->lo), a->file, 0};
  712. return;
  713. }
  714. if(a->n != -1){ /* article directory */
  715. sprint(buf, "%d", a->n);
  716. d->name = estrdup(buf);
  717. d->mode = DMDIR|0555;
  718. d->qid = (Qid){PATH(g->num, a->n+1-g->lo), 0, QTDIR};
  719. return;
  720. }
  721. /* group directory */
  722. if(g->name[0])
  723. d->name = estrdup(g->name);
  724. else
  725. d->name = estrdup("/");
  726. d->mode = DMDIR|0555;
  727. d->qid = (Qid){PATH(g->num, 0), g->hi-1, QTDIR};
  728. }
  729. static int
  730. dirfillstat(Dir *d, Aux *a, int i)
  731. {
  732. int ndir;
  733. Group *g;
  734. char buf[32];
  735. memset(d, 0, sizeof *d);
  736. d->uid = estrdup("nntp");
  737. d->gid = estrdup("nntp");
  738. g = a->g;
  739. d->atime = d->mtime = g->mtime;
  740. if(a->n != -1){ /* article directory */
  741. if(i >= Nfile)
  742. return -1;
  743. d->name = estrdup(filename[i]);
  744. d->mode = 0444;
  745. d->qid = (Qid){PATH(g->num, a->n), i, 0};
  746. return 0;
  747. }
  748. /* hierarchy directory: child groups */
  749. if(i < g->nkid){
  750. d->name = estrdup(g->kid[i]->name);
  751. d->mode = DMDIR|0555;
  752. d->qid = (Qid){PATH(g->kid[i]->num, 0), g->kid[i]->hi-1, QTDIR};
  753. return 0;
  754. }
  755. i -= g->nkid;
  756. /* group directory: post file */
  757. if(g->isgroup && !readonly && g->canpost){
  758. if(i < 1){
  759. d->name = estrdup("post");
  760. d->mode = 0222;
  761. d->qid = (Qid){PATH(g->num, 0), 0, 0};
  762. return 0;
  763. }
  764. i--;
  765. }
  766. /* group directory: child articles */
  767. ndir = g->hi - g->lo;
  768. if(i < ndir){
  769. sprint(buf, "%d", g->lo+i);
  770. d->name = estrdup(buf);
  771. d->mode = DMDIR|0555;
  772. d->qid = (Qid){PATH(g->num, i+1), 0, QTDIR};
  773. return 0;
  774. }
  775. return -1;
  776. }
  777. static void
  778. fsstat(Req *r)
  779. {
  780. Aux *a;
  781. a = r->fid->aux;
  782. if(r->fid->qid.path == 0 && (r->fid->qid.type & QTDIR))
  783. nntprefreshall(net);
  784. else if(a->g->isgroup)
  785. nntprefresh(net, a->g);
  786. fillstat(&r->d, a);
  787. respond(r, nil);
  788. }
  789. static void
  790. fsread(Req *r)
  791. {
  792. int offset, n;
  793. Aux *a;
  794. char *p, *ep;
  795. Dir d;
  796. a = r->fid->aux;
  797. if(a->s){
  798. readstr(r, a->s);
  799. respond(r, nil);
  800. return;
  801. }
  802. if(r->ifcall.offset == 0)
  803. offset = 0;
  804. else
  805. offset = a->offset;
  806. p = r->ofcall.data;
  807. ep = r->ofcall.data+r->ifcall.count;
  808. for(; p+2 < ep; p += n){
  809. if(dirfillstat(&d, a, offset) < 0)
  810. break;
  811. n=convD2M(&d, (uchar*)p, ep-p);
  812. free(d.name);
  813. free(d.uid);
  814. free(d.gid);
  815. free(d.muid);
  816. if(n <= BIT16SZ)
  817. break;
  818. offset++;
  819. }
  820. a->offset = offset;
  821. r->ofcall.count = p - r->ofcall.data;
  822. respond(r, nil);
  823. }
  824. static void
  825. fswrite(Req *r)
  826. {
  827. Aux *a;
  828. long count;
  829. vlong offset;
  830. a = r->fid->aux;
  831. if(r->ifcall.count == 0){ /* commit */
  832. respond(r, nntppost(net, a->s));
  833. free(a->s);
  834. a->ns = 0;
  835. a->s = nil;
  836. return;
  837. }
  838. count = r->ifcall.count;
  839. offset = r->ifcall.offset;
  840. if(a->ns < count+offset+1){
  841. a->s = erealloc(a->s, count+offset+1);
  842. a->ns = count+offset;
  843. a->s[a->ns] = '\0';
  844. }
  845. memmove(a->s+offset, r->ifcall.data, count);
  846. r->ofcall.count = count;
  847. respond(r, nil);
  848. }
  849. static void
  850. fsdestroyfid(Fid *fid)
  851. {
  852. Aux *a;
  853. a = fid->aux;
  854. if(a==nil)
  855. return;
  856. if(a->ispost && a->s)
  857. nntppost(net, a->s);
  858. free(a->s);
  859. free(a);
  860. }
  861. Srv nntpsrv = {
  862. .destroyfid= fsdestroyfid,
  863. .attach= fsattach,
  864. .clone= fsclone,
  865. .walk1= fswalk1,
  866. .open= fsopen,
  867. .read= fsread,
  868. .write= fswrite,
  869. .stat= fsstat,
  870. };
  871. void
  872. usage(void)
  873. {
  874. fprint(2, "usage: nntpsrv [-a] [-s service] [-m mtpt] [nntp.server]\n");
  875. exits("usage");
  876. }
  877. void
  878. dumpgroups(Group *g, int ind)
  879. {
  880. int i;
  881. print("%*s%s\n", ind*4, "", g->name);
  882. for(i=0; i<g->nkid; i++)
  883. dumpgroups(g->kid[i], ind+1);
  884. }
  885. void
  886. main(int argc, char **argv)
  887. {
  888. int auth, x;
  889. char *mtpt, *service, *where, *user;
  890. Netbuf n;
  891. UserPasswd *up;
  892. mtpt = "/mnt/news";
  893. service = nil;
  894. memset(&n, 0, sizeof n);
  895. user = nil;
  896. auth = 0;
  897. ARGBEGIN{
  898. case 'D':
  899. chatty9p++;
  900. break;
  901. case 'N':
  902. netdebug = 1;
  903. break;
  904. case 'a':
  905. auth = 1;
  906. break;
  907. case 'u':
  908. user = EARGF(usage());
  909. break;
  910. case 's':
  911. service = EARGF(usage());
  912. break;
  913. case 'm':
  914. mtpt = EARGF(usage());
  915. break;
  916. default:
  917. usage();
  918. }ARGEND
  919. if(argc > 1)
  920. usage();
  921. if(argc==0)
  922. where = "$nntp";
  923. else
  924. where = argv[0];
  925. now = time(0);
  926. net = &n;
  927. if(auth) {
  928. n.auth = 1;
  929. if(user)
  930. up = auth_getuserpasswd(auth_getkey, "proto=pass service=nntp server=%q user=%q", where, user);
  931. else
  932. up = auth_getuserpasswd(auth_getkey, "proto=pass service=nntp server=%q", where);
  933. if(up == nil)
  934. sysfatal("no password: %r");
  935. n.user = up->user;
  936. n.pass = up->passwd;
  937. }
  938. n.addr = netmkaddr(where, "tcp", "nntp");
  939. root = emalloc(sizeof *root);
  940. root->name = estrdup("");
  941. root->parent = root;
  942. n.fd = -1;
  943. if(nntpconnect(&n) < 0)
  944. sysfatal("nntpconnect: %s", n.response);
  945. x=netdebug;
  946. netdebug=0;
  947. nntprefreshall(&n);
  948. netdebug=x;
  949. // dumpgroups(root, 0);
  950. postmountsrv(&nntpsrv, service, mtpt, MREPL);
  951. exits(nil);
  952. }