]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/webfs/fs.c
merge
[plan9front.git] / sys / src / cmd / webfs / fs.c
1 #include <u.h>
2 #include <libc.h>
3 #include <ctype.h>
4 #include <fcall.h>
5 #include <thread.h>
6 #include <9p.h>
7
8 #include "dat.h"
9 #include "fns.h"
10
11 typedef struct Webfid Webfid;
12 typedef struct Client Client;
13
14 struct Client
15 {
16         Ref;
17
18         char    request[16];
19         Url     *baseurl;
20         Url     *url;
21         Key     *hdr;
22
23         int     obody;  /* body opend */
24         int     cbody;  /* body closed */
25         Buq     *qbody;
26 };
27
28 struct Webfid
29 {
30         int     level;
31
32         Client  *client;
33         Key     *key;   /* copy for Qheader */
34         Buq     *buq;   /* reference for Qbody, Qpost */
35 };
36
37 enum {
38         Qroot,
39                 Qrctl,
40                 Qclone,
41                 Qclient,
42                         Qctl,
43                         Qbody,
44                         Qpost,
45                         Qparsed,
46                                 Qurl,
47                                 Qurlschm,
48                                 Qurluser,
49                                 Qurlpass,
50                                 Qurlhost,
51                                 Qurlport,
52                                 Qurlpath,
53                                 Qurlqwry,
54                                 Qurlfrag,
55                         Qheader,
56 };
57
58 static char *nametab[] = {
59         "/",
60                 "ctl",
61                 "clone",
62                 nil,
63                         "ctl",
64                         "body",
65                         "postbody",
66                         "parsed",
67                                 "url",
68                                 "scheme",
69                                 "user",
70                                 "passwd",
71                                 "host",
72                                 "port",
73                                 "path",
74                                 "query",
75                                 "fragment",
76                         nil,
77 };
78
79 static char *mtpt;
80 static char *service;
81 static long time0;
82 static char *user;
83 static char *agent;
84 static Client client[64];
85 static int nclient;
86
87 #define CLIENTID(c)     ((int)(((Client*)(c)) - client))
88
89 Client*
90 newclient(void)
91 {
92         Client *cl;
93         int i;
94
95         for(i = 0; i < nclient; i++)
96                 if(client[i].ref == 0)
97                         break;
98         if(i >= nelem(client))
99                 return nil;
100         if(i == nclient)
101                 nclient++;
102         cl = &client[i];
103         incref(cl);
104
105         cl->request[0] = 0;
106         cl->baseurl = nil;
107         cl->url = nil;
108         cl->hdr = nil;
109         cl->qbody = nil;
110         
111         return cl;
112 }
113
114 void
115 freeclient(Client *cl)
116 {
117         Key *k;
118
119         if(cl == nil || decref(cl))
120                 return;
121
122         buclose(cl->qbody, 0);
123         bufree(cl->qbody);
124
125         while(k = cl->hdr){
126                 cl->hdr = k->next;
127                 free(k);
128         }
129
130         freeurl(cl->url);
131         freeurl(cl->baseurl);
132
133         memset(cl, 0, sizeof(*cl));
134 }
135
136 static Url*
137 clienturl(Client *cl)
138 {
139         static Url nullurl;
140
141         if(cl->qbody && cl->qbody->url)
142                 return cl->qbody->url;
143         if(cl->url)
144                 return cl->url;
145         return &nullurl;
146 }
147
148 static void*
149 wfaux(Webfid *f)
150 {
151         if(f->level < Qclient)
152                 return nil;
153         else if(f->level < Qurl)
154                 return f->client;
155         else if(f->level < Qheader)
156                 return clienturl(f->client);
157         else
158                 return f->key;
159 }
160
161 static void
162 fsmkqid(Qid *q, int level, void *aux)
163 {
164         q->type = 0;
165         q->vers = 0;
166         switch(level){
167         case Qroot:
168         case Qparsed:
169         case Qclient:
170                 q->type = QTDIR;
171         default:
172                 q->path = (level<<24) | (((uintptr)aux ^ time0) & 0x00ffffff);
173         }
174 }
175
176 static char*
177 fshdrname(char *s)
178 {
179         char *k, *w;
180
181         for(k=w=s; *k; k++)
182                 if(isalnum(*k))
183                         *w++ = tolower(*k);
184         *w = 0;
185         return s;
186 }
187
188 static int
189 urlstr(char *buf, int nbuf, Url *u, int level)
190 {
191         char *s;
192
193         if(level == Qurl)
194                 return snprint(buf, nbuf, "%U", u);
195         if(level == Qurlpath)
196                 return snprint(buf, nbuf, "%s", Upath(u));
197         if((s = (&u->scheme)[level - Qurlschm]) == nil){
198                 buf[0] = 0;
199                 return 0;
200         }
201         return snprint(buf, nbuf, "%s", s);
202 }
203
204
205 static void
206 fsmkdir(Dir *d, int level, void *aux)
207 {
208         char buf[1024];
209
210         memset(d, 0, sizeof(*d));
211         fsmkqid(&d->qid, level, aux);
212         d->mode = 0444;
213         d->atime = d->mtime = time0;
214         d->uid = estrdup(user);
215         d->gid = estrdup(user);
216         d->muid = estrdup(user);
217         if(d->qid.type & QTDIR)
218                 d->mode |= DMDIR | 0111;
219         switch(level){
220         case Qheader:
221                 d->name = fshdrname(estrdup(((Key*)aux)->key));
222                 d->length = strlen(((Key*)aux)->val);
223                 break;
224         case Qclient:
225                 snprint(buf, sizeof(buf), "%d", CLIENTID(aux));
226                 d->name = estrdup(buf);
227                 break;
228         case Qctl:
229         case Qrctl:
230         case Qclone:
231                 d->mode = 0666;
232                 if(0){
233         case Qpost:
234                 d->mode = 0222;
235                 }
236         default:
237                 d->name = estrdup(nametab[level]);
238                 if(level >= Qurl && level <= Qurlfrag)
239                         d->length = urlstr(buf, sizeof(buf), (Url*)aux, level);
240         }
241 }
242
243 static void
244 fsattach(Req *r)
245 {
246         Webfid *f;
247
248         if(r->ifcall.aname && r->ifcall.aname[0]){
249                 respond(r, "invalid attach specifier");
250                 return;
251         }
252         f = emalloc(sizeof(*f));
253         f->level = Qroot;
254         fsmkqid(&r->fid->qid, f->level, wfaux(f));
255         r->ofcall.qid = r->fid->qid;
256         r->fid->aux = f;
257         respond(r, nil);
258 }
259
260 static void
261 fsstat(Req *r)
262 {
263         Webfid *f;
264
265         f = r->fid->aux;
266         fsmkdir(&r->d, f->level, wfaux(f));
267         respond(r, nil);
268 }
269
270 static char*
271 fswalk1(Fid *fid, char *name, Qid *qid)
272 {
273         Webfid *f;
274         int i, j;
275
276         if(!(fid->qid.type&QTDIR))
277                 return "walk in non-directory";
278
279         f = fid->aux;
280         if(strcmp(name, "..") == 0){
281                 switch(f->level){
282                 case Qroot:
283                         break;
284                 case Qclient:
285                         freeclient(f->client);
286                         f->client = nil;
287                         break;
288                 default:
289                         if(f->level > Qparsed)
290                                 f->level = Qparsed;
291                         else
292                                 f->level = Qclient;
293                 }
294         } else {
295                 for(i=f->level+1; i < nelem(nametab); i++){
296                         if(nametab[i]){
297                                 if(strcmp(name, nametab[i]) == 0)
298                                         break;
299                                 if(i == Qbody && strncmp(name, "body.", 5) == 0)
300                                         break;
301                         }
302                         if(i == Qclient){
303                                 j = atoi(name);
304                                 if(j >= 0 && j < nclient){
305                                         f->client = &client[j];
306                                         incref(f->client);
307                                         break;
308                                 }
309                         }
310                         if(i == Qheader && f->client && f->client->qbody){
311                                 char buf[128];
312                                 Key *k;
313
314                                 for(k = f->client->qbody->hdr; k; k = k->next){
315                                         nstrcpy(buf, k->key, sizeof(buf));
316                                         if(!strcmp(name, fshdrname(buf)))
317                                                 break;
318                                 }
319                                 if(k != nil){
320                                         /* need to copy as key is owned by qbody wich might go away */
321                                         f->key = addkey(0, k->key, k->val);
322                                         break;
323                                 }
324                         }
325                 }
326                 if(i >= nelem(nametab))
327                         return "directory entry not found";
328                 f->level = i;
329         }
330         fsmkqid(qid, f->level, wfaux(f));
331         fid->qid = *qid;
332         return nil;
333 }
334
335 static char*
336 fsclone(Fid *oldfid, Fid *newfid)
337 {
338         Webfid *f, *o;
339
340         o = oldfid->aux;
341         if(o == nil || o->key || o->buq)
342                 return "bad fid";
343         f = emalloc(sizeof(*f));
344         memmove(f, o, sizeof(*f));
345         if(f->client)
346                 incref(f->client);
347         newfid->aux = f;
348         return nil;
349 }
350
351 static void
352 fsopen(Req *r)
353 {
354         Webfid *f;
355         Client *cl;
356
357         f = r->fid->aux;
358         cl = f->client;
359         switch(f->level){
360         case Qclone:
361                 if((cl = newclient()) == nil){
362                         respond(r, "no more clients");
363                         return;
364                 }
365                 f->level = Qctl;
366                 f->client = cl;
367                 fsmkqid(&r->fid->qid, f->level, wfaux(f));
368                 r->ofcall.qid = r->fid->qid;
369                 break;
370         case Qpost:
371                 if(cl->qbody && !cl->cbody){
372                 Inuse:
373                         respond(r, "client in use");
374                         return;
375                 }
376         case Qbody:
377                 if(cl->obody)
378                         goto Inuse;
379                 if(cl->cbody){
380                         bufree(cl->qbody);
381                         cl->qbody = nil;
382                         cl->cbody = 0;
383                 }
384                 if(cl->qbody == nil){
385                         char *m;
386
387                         if(cl->url == nil){
388                                 respond(r, "no url set");
389                                 return;
390                         }
391                         cl->qbody = bualloc(16*1024);
392                         if(f->level != Qbody){
393                                 f->buq = bualloc(64*1024);
394                                 if(!lookkey(cl->hdr, "Content-Type"))
395                                         cl->hdr = addkey(cl->hdr, "Content-Type", 
396                                                 "application/x-www-form-urlencoded");
397                                 m = "POST";
398                         } else
399                                 m = "GET";
400                         if(cl->request[0])
401                                 m = cl->request;
402
403                         /*
404                          * some sites give a 403 Forbidden if we dont include
405                          * a meaningless Accept header in the request.
406                          */
407                         if(!lookkey(cl->hdr, "Accept"))
408                                 cl->hdr = addkey(cl->hdr, "Accept", "*/*");
409
410                         if(!lookkey(cl->hdr, "Connection"))
411                                 cl->hdr = addkey(cl->hdr, "Connection", "keep-alive");
412
413                         if(agent && !lookkey(cl->hdr, "User-Agent"))
414                                 cl->hdr = addkey(cl->hdr, "User-Agent", agent);
415
416                         http(m, cl->url, cl->hdr, cl->qbody, f->buq);
417                         cl->request[0] = 0;
418                         cl->url = nil;
419                         cl->hdr = nil;
420                 }
421                 if(f->buq)
422                         break;
423                 cl->obody = 1;
424                 incref(cl->qbody);
425                 bureq(f->buq = cl->qbody, r);
426                 return;
427         }
428         respond(r, nil);
429 }
430
431 static int
432 rootgen(int i, Dir *d, void *)
433 {
434         i += Qroot+1;
435         if(i < Qclient){
436                 fsmkdir(d, i, 0);
437                 return 0;
438         }
439         i -= Qclient;
440         if(i < nclient){
441                 fsmkdir(d, Qclient, &client[i]);
442                 return 0;
443         }
444         return -1;
445 }
446
447 static int
448 clientgen(int i, Dir *d, void *aux)
449 {
450         i += Qclient+1;
451         if(i > Qparsed){
452                 Client *cl = aux;
453                 Key *k;
454
455                 i -= Qparsed+1;
456                 if(cl == nil || cl->qbody == nil)
457                         return -1;
458                 for(k = cl->qbody->hdr; i > 0 && k; i--, k = k->next)
459                         ;
460                 if(k == nil || i > 0)
461                         return -1;
462                 i = Qheader;
463                 aux = k;
464         }
465         fsmkdir(d, i, aux);
466         return 0;
467 }
468
469 static int
470 parsedgen(int i, Dir *d, void *aux)
471 {
472         i += Qparsed+1;
473         if(i > Qurlfrag)
474                 return -1;
475         fsmkdir(d, i, aux);
476         return 0;
477 }
478
479 static void
480 fsread(Req *r)
481 {
482         char buf[1024];
483         Webfid *f;
484
485         f = r->fid->aux;
486         switch(f->level){
487         case Qroot:
488                 dirread9p(r, rootgen, nil);
489                 respond(r, nil);
490                 return;
491         case Qclient:
492                 dirread9p(r, clientgen, f->client);
493                 respond(r, nil);
494                 return;
495         case Qparsed:
496                 dirread9p(r, parsedgen, clienturl(f->client));
497                 respond(r, nil);
498                 return;
499         case Qrctl:
500                 snprint(buf, sizeof(buf), "useragent %s\ntimeout %d\n", agent, timeout);
501         String:
502                 readstr(r, buf);
503                 respond(r, nil);
504                 return;
505         case Qctl:
506                 snprint(buf, sizeof(buf), "%d\n", CLIENTID(f->client));
507                 goto String;
508         case Qheader:
509                 snprint(buf, sizeof(buf), "%s", f->key->val);
510                 goto String;
511         case Qurl:
512         case Qurlschm:
513         case Qurluser:
514         case Qurlpass:
515         case Qurlhost:
516         case Qurlport:
517         case Qurlpath:
518         case Qurlqwry:
519         case Qurlfrag:
520                 urlstr(buf, sizeof(buf), clienturl(f->client), f->level);
521                 goto String;
522         case Qbody:
523                 bureq(f->buq, r);
524                 return;
525         }
526         respond(r, "not implemented");
527 }
528
529 static char*
530 rootctl(Srv *fs, char *ctl, char *arg)
531 {
532         Url *u;
533
534         if(debug)
535                 fprint(2, "rootctl: %q %q\n", ctl, arg);
536
537         if(!strcmp(ctl, "useragent")){
538                 free(agent);
539                 if(arg && *arg)
540                         agent = estrdup(arg);
541                 else
542                         agent = nil;
543                 return nil;
544         }
545
546         if(!strcmp(ctl, "flushauth")){
547                 u = nil;
548                 if(arg && *arg)
549                         u = saneurl(url(arg, 0));
550                 flushauth(u, 0);
551                 freeurl(u);
552                 return nil;
553         }
554
555         if(!strcmp(ctl, "timeout")){
556                 if(arg && *arg)
557                         timeout = atoi(arg);
558                 else
559                         timeout = 0;
560                 if(timeout < 0)
561                         timeout = 0;
562                 return nil;
563         }
564
565         /* ppreemptive authentication only basic
566          * auth supported, ctl message of the form:
567          *    preauth url realm
568          */
569         if(!strcmp(ctl, "preauth")){
570                 char *a[3], buf[256];
571                 int rc;
572
573                 if(tokenize(arg, a, nelem(a)) != 2)
574                         return "preauth - bad field count";
575                 if((u = saneurl(url(a[0], 0))) == nil)
576                         return "preauth - malformed url";
577                 snprint(buf, sizeof(buf), "BASIC realm=\"%s\"", a[1]);
578                 srvrelease(fs);
579                 rc = authenticate(u, u, "GET", buf);
580                 srvacquire(fs);
581                 freeurl(u);
582                 if(rc == -1)
583                         return "preauth failed";
584                 return nil;
585         }
586
587         return "bad ctl message";
588 }
589
590 static char*
591 clientctl(Client *cl, char *ctl, char *arg)
592 {
593         char *p;
594         Url *u;
595         Key *k;
596
597         if(debug)
598                 fprint(2, "clientctl: %q %q\n", ctl, arg);
599
600         if(!strcmp(ctl, "url")){
601                 if((u = saneurl(url(arg, cl->baseurl))) == nil)
602                         return "bad url";
603                 freeurl(cl->url);
604                 cl->url = u;
605         }
606         else if(!strcmp(ctl, "baseurl")){
607                 if((u = url(arg, 0)) == nil)
608                         return "bad baseurl";
609                 freeurl(cl->baseurl);
610                 cl->baseurl = u;
611         }
612         else if(!strcmp(ctl, "request")){
613                 p = cl->request;
614                 nstrcpy(p, arg, sizeof(cl->request));
615                 for(; *p && isalpha(*p); p++)
616                         *p = toupper(*p);
617                 *p = 0;
618         }
619         else if(!strcmp(ctl, "headers")){
620                 while(arg && *arg){
621                         ctl = arg;
622                         while(*ctl && strchr(whitespace, *ctl))
623                                 ctl++;
624                         if(arg = strchr(ctl, '\n'))
625                                 *arg++ = 0;
626                         if(k = parsehdr(ctl)){
627                                 k->next = cl->hdr;
628                                 cl->hdr = k;
629                         }
630                 }
631         }
632         else {
633                 char buf[128], **t;
634                 static char *tab[] = {
635                         "User-Agent",
636                         "Content-Type",
637                         nil,
638                 };
639                 for(t = tab; *t; t++){
640                         nstrcpy(buf, *t, sizeof(buf));
641                         if(!strcmp(ctl, fshdrname(buf))){
642                                 cl->hdr = delkey(cl->hdr, *t);
643                                 if(arg && *arg)
644                                         cl->hdr = addkey(cl->hdr, *t, arg);
645                                 break;
646                         }
647                 }
648                 if(*t == nil)
649                         return "bad ctl message";
650         }
651         return nil;
652 }
653
654 static void
655 fswrite(Req *r)
656 {
657         int n;
658         Webfid *f;
659         char *s, *t;
660
661         f = r->fid->aux;
662         switch(f->level){
663         case Qrctl:
664         case Qctl:
665                 n = r->ofcall.count = r->ifcall.count;
666                 s = emalloc(n+1);
667                 memmove(s, r->ifcall.data, n);
668                 while(n > 0 && strchr("\r\n", s[n-1]))
669                         n--;
670                 s[n] = 0;
671                 t = s;
672                 while(*t && strchr(whitespace, *t)==0)
673                         t++;
674                 while(*t && strchr(whitespace, *t))
675                         *t++ = 0;
676                 if(f->level == Qctl)
677                         t = clientctl(f->client, s, t);
678                 else
679                         t = rootctl(r->srv, s, t);
680                 free(s);
681                 respond(r, t);
682                 return;
683         case Qpost:
684                 bureq(f->buq, r);
685                 return;
686         }
687         respond(r, "not implemented");
688 }
689
690 static void
691 fsflush(Req *r)
692 {
693         Webfid *f;
694         Req *o;
695
696         if(o = r->oldreq)
697         if(f = o->fid->aux)
698                 buflushreq(f->buq, o);
699         respond(r, nil);
700 }
701
702 static void
703 fsdestroyfid(Fid *fid)
704 {
705         Webfid *f;
706
707         if(f = fid->aux){
708                 fid->aux = nil;
709                 if(f->buq){
710                         buclose(f->buq, 0);
711                         if(f->client->qbody == f->buq){
712                                 f->client->obody = 0;
713                                 f->client->cbody = 1;
714                         }
715                         bufree(f->buq);
716                 }
717                 if(f->key)
718                         free(f->key);
719                 freeclient(f->client);
720                 free(f);
721         }
722 }
723
724 static void
725 fsstart(Srv*)
726 {
727         /* drop reference to old webfs mount */
728         if(mtpt != nil)
729                 unmount(nil, mtpt);
730 }
731
732 static void
733 fsend(Srv*)
734 {
735         postnote(PNGROUP, getpid(), "shutdown");
736         exits(nil);
737 }
738
739 Srv fs = 
740 {
741         .start=fsstart,
742         .attach=fsattach,
743         .stat=fsstat,
744         .walk1=fswalk1,
745         .clone=fsclone,
746         .open=fsopen,
747         .read=fsread,
748         .write=fswrite,
749         .flush=fsflush,
750         .destroyfid=fsdestroyfid,
751         .end=fsend,
752 };
753
754 void
755 usage(void)
756 {
757         fprint(2, "usage: %s [-Dd] [-A useragent] [-T timeout] [-m mtpt] [-s service]\n", argv0);
758         exits("usage");
759 }
760
761 void
762 main(int argc, char *argv[])
763 {
764         char *s;
765
766         quotefmtinstall();
767         fmtinstall('U', Ufmt);
768         fmtinstall('N', Nfmt);
769         fmtinstall(']', Mfmt);
770         fmtinstall('E', Efmt);
771         fmtinstall('[', encodefmt);
772         fmtinstall('H', encodefmt);
773
774         mtpt = "/mnt/web";
775         user = getuser();
776         time0 = time(0);
777         timeout = 10000;
778
779         ARGBEGIN {
780         case 'D':
781                 chatty9p++;
782                 break;
783         case 'A':
784                 agent = EARGF(usage());
785                 break;
786         case 'T':
787                 timeout = atoi(EARGF(usage()));
788                 if(timeout < 0)
789                         timeout = 0;
790                 break;
791         case 'm':
792                 mtpt = EARGF(usage());
793                 break;
794         case 's':
795                 service = EARGF(usage());
796                 break;
797         case 'd':
798                 debug++;
799                 break;
800         default:
801                 usage();
802         } ARGEND;
803
804         rfork(RFNOTEG);
805
806         if(agent == nil)
807                 agent = "Mozilla/5.0 (compatible; hjdicks)";
808         agent = estrdup(agent);
809
810         if(s = getenv("httpproxy")){
811                 proxy = saneurl(url(s, 0));
812                 if(proxy == nil || strcmp(proxy->scheme, "http") && strcmp(proxy->scheme, "https"))
813                         sysfatal("invalid httpproxy url: %s", s);
814                 free(s);
815         }
816
817         postmountsrv(&fs, service, mtpt, MREPL);
818         exits(nil);
819 }