]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/webfs/fs.c
fix wrong use of qid.type == QTDIR comparsion
[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 long time0;
80 static char *user;
81 static Client client[64];
82 static int nclient;
83
84 #define CLIENTID(c)     (((Client*)(c)) - client)
85
86 Client*
87 newclient(void)
88 {
89         Client *cl;
90         int i;
91
92         for(i = 0; i < nclient; i++)
93                 if(client[i].ref == 0)
94                         break;
95         if(i >= nelem(client))
96                 return nil;
97         if(i == nclient)
98                 nclient++;
99         cl = &client[i];
100         incref(cl);
101
102         cl->request[0] = 0;
103         cl->baseurl = nil;
104         cl->url = nil;
105         cl->hdr = nil;
106         cl->qbody = nil;
107         
108         return cl;
109 }
110
111 void
112 freeclient(Client *cl)
113 {
114         Key *k;
115
116         if(cl == nil || decref(cl))
117                 return;
118
119         buclose(cl->qbody, 0);
120         bufree(cl->qbody);
121
122         while(k = cl->hdr){
123                 cl->hdr = k->next;
124                 free(k);
125         }
126
127         freeurl(cl->url);
128         freeurl(cl->baseurl);
129
130         memset(cl, 0, sizeof(*cl));
131 }
132
133 static Url*
134 clienturl(Client *cl)
135 {
136         static Url nullurl;
137
138         if(cl->qbody && cl->qbody->url)
139                 return cl->qbody->url;
140         if(cl->url)
141                 return cl->url;
142         return &nullurl;
143 }
144
145 static void*
146 wfaux(Webfid *f)
147 {
148         if(f->level < Qclient)
149                 return nil;
150         else if(f->level < Qurl)
151                 return f->client;
152         else if(f->level < Qheader)
153                 return clienturl(f->client);
154         else
155                 return f->key;
156 }
157
158 static void
159 fsmkqid(Qid *q, int level, void *aux)
160 {
161         q->type = 0;
162         q->vers = 0;
163         switch(level){
164         case Qroot:
165         case Qparsed:
166         case Qclient:
167                 q->type = QTDIR;
168         default:
169                 q->path = (level<<24) | (((ulong)aux ^ time0) & 0x00ffffff);
170         }
171 }
172
173 static char*
174 fshdrname(char *s)
175 {
176         char *k, *w;
177
178         for(k=w=s; *k; k++)
179                 if(isalnum(*k))
180                         *w++ = tolower(*k);
181         *w = 0;
182         return s;
183 }
184
185 static int
186 urlstr(char *buf, int nbuf, Url *u, int level)
187 {
188         char *s;
189
190         if(level == Qurl)
191                 return snprint(buf, nbuf, "%U", u);
192         if(level == Qurlpath)
193                 return snprint(buf, nbuf, "%s", Upath(u));
194         if((s = (&u->scheme)[level - Qurlschm]) == nil){
195                 buf[0] = 0;
196                 return 0;
197         }
198         return snprint(buf, nbuf, "%s", s);
199 }
200
201
202 static void
203 fsmkdir(Dir *d, int level, void *aux)
204 {
205         char buf[1024];
206
207         memset(d, 0, sizeof(*d));
208         fsmkqid(&d->qid, level, aux);
209         d->mode = 0444;
210         d->atime = d->mtime = time0;
211         d->uid = estrdup(user);
212         d->gid = estrdup(user);
213         d->muid = estrdup(user);
214         if(d->qid.type & QTDIR)
215                 d->mode |= DMDIR | 0111;
216         switch(level){
217         case Qheader:
218                 d->name = fshdrname(estrdup(((Key*)aux)->key));
219                 d->length = strlen(((Key*)aux)->val);
220                 break;
221         case Qclient:
222                 snprint(buf, sizeof(buf), "%ld", CLIENTID(aux));
223                 d->name = estrdup(buf);
224                 break;
225         case Qctl:
226         case Qrctl:
227         case Qclone:
228                 d->mode = 0666;
229                 if(0){
230         case Qpost:
231                 d->mode = 0222;
232                 }
233         default:
234                 d->name = estrdup(nametab[level]);
235                 if(level >= Qurl && level <= Qurlfrag)
236                         d->length = urlstr(buf, sizeof(buf), (Url*)aux, level);
237         }
238 }
239
240 static void
241 fsattach(Req *r)
242 {
243         Webfid *f;
244
245         if(r->ifcall.aname && r->ifcall.aname[0]){
246                 respond(r, "invalid attach specifier");
247                 return;
248         }
249         f = emalloc(sizeof(*f));
250         f->level = Qroot;
251         fsmkqid(&r->fid->qid, f->level, wfaux(f));
252         r->ofcall.qid = r->fid->qid;
253         r->fid->aux = f;
254         respond(r, nil);
255 }
256
257 static void
258 fsstat(Req *r)
259 {
260         Webfid *f;
261
262         f = r->fid->aux;
263         fsmkdir(&r->d, f->level, wfaux(f));
264         respond(r, nil);
265 }
266
267 static char*
268 fswalk1(Fid *fid, char *name, Qid *qid)
269 {
270         Webfid *f;
271         int i, j;
272
273         if(!(fid->qid.type&QTDIR))
274                 return "walk in non-directory";
275
276         f = fid->aux;
277         if(strcmp(name, "..") == 0){
278                 switch(f->level){
279                 case Qroot:
280                         break;
281                 case Qclient:
282                         freeclient(f->client);
283                         f->client = nil;
284                         break;
285                 default:
286                         if(f->level > Qparsed)
287                                 f->level = Qparsed;
288                         else
289                                 f->level = Qclient;
290                 }
291         } else {
292                 for(i=f->level+1; i < nelem(nametab); i++){
293                         if(nametab[i]){
294                                 if(strcmp(name, nametab[i]) == 0)
295                                         break;
296                                 if(i == Qbody && strncmp(name, "body.", 5) == 0)
297                                         break;
298                         }
299                         if(i == Qclient){
300                                 j = atoi(name);
301                                 if(j >= 0 && j < nclient){
302                                         f->client = &client[j];
303                                         incref(f->client);
304                                         break;
305                                 }
306                         }
307                         if(i == Qheader && f->client && f->client->qbody){
308                                 char buf[128];
309                                 Key *k;
310
311                                 for(k = f->client->qbody->hdr; k; k = k->next){
312                                         strncpy(buf, k->key, sizeof(buf));
313                                         if(!strcmp(name, fshdrname(buf)))
314                                                 break;
315                                 }
316                                 if(k != nil){
317                                         /* need to copy as key is owned by qbody wich might go away */
318                                         f->key = addkey(0, k->key, k->val);
319                                         break;
320                                 }
321                         }
322                 }
323                 if(i >= nelem(nametab))
324                         return "directory entry not found";
325                 f->level = i;
326         }
327         fsmkqid(qid, f->level, wfaux(f));
328         fid->qid = *qid;
329         return nil;
330 }
331
332 static char*
333 fsclone(Fid *oldfid, Fid *newfid)
334 {
335         Webfid *f, *o;
336
337         o = oldfid->aux;
338         if(o == nil || o->key || o->buq)
339                 return "bad fid";
340         f = emalloc(sizeof(*f));
341         memmove(f, o, sizeof(*f));
342         if(f->client)
343                 incref(f->client);
344         newfid->aux = f;
345         return nil;
346 }
347
348 static void
349 fsopen(Req *r)
350 {
351         Webfid *f;
352         Client *cl;
353
354         f = r->fid->aux;
355         cl = f->client;
356         switch(f->level){
357         case Qclone:
358                 if((cl = newclient()) == nil){
359                         respond(r, "no more clients");
360                         return;
361                 }
362                 f->level = Qctl;
363                 f->client = cl;
364                 fsmkqid(&r->fid->qid, f->level, wfaux(f));
365                 r->ofcall.qid = r->fid->qid;
366                 break;
367         case Qpost:
368                 if(cl->qbody && !cl->cbody){
369                 Inuse:
370                         respond(r, "client in use");
371                         return;
372                 }
373         case Qbody:
374                 if(cl->obody)
375                         goto Inuse;
376                 if(cl->cbody){
377                         bufree(cl->qbody);
378                         cl->qbody = nil;
379                         cl->cbody = 0;
380                 }
381                 if(cl->qbody == nil){
382                         char *m;
383
384                         if(cl->url == nil){
385                                 respond(r, "no url set");
386                                 return;
387                         }
388                         cl->qbody = bualloc(16*1024);
389                         if(f->level != Qbody){
390                                 f->buq = bualloc(64*1024);
391                                 if(!lookkey(cl->hdr, "Content-Type"))
392                                         cl->hdr = addkey(cl->hdr, "Content-Type", 
393                                                 "application/x-www-form-urlencoded");
394                                 m = "POST";
395                         } else
396                                 m = "GET";
397                         if(cl->request[0])
398                                 m = cl->request;
399                         if(!lookkey(cl->hdr, "Connection"))
400                                 cl->hdr = addkey(cl->hdr, "Connection", "keep-alive");
401                         if(!lookkey(cl->hdr, "User-Agent"))
402                                 cl->hdr = addkey(cl->hdr, "User-Agent", "webfs/0.1 (Plan 9 Front)");
403                         http(m, cl->url, cl->hdr, cl->qbody, f->buq);
404                         cl->request[0] = 0;
405                         cl->url = nil;
406                         cl->hdr = nil;
407                 }
408                 if(f->buq)
409                         break;
410                 cl->obody = 1;
411                 incref(cl->qbody);
412                 bureq(f->buq = cl->qbody, r);
413                 return;
414         }
415         respond(r, nil);
416 }
417
418 static int
419 rootgen(int i, Dir *d, void *)
420 {
421         i += Qroot+1;
422         if(i < Qclient){
423                 fsmkdir(d, i, 0);
424                 return 0;
425         }
426         i -= Qclient;
427         if(i < nclient){
428                 fsmkdir(d, Qclient, &client[i]);
429                 return 0;
430         }
431         return -1;
432 }
433
434 static int
435 clientgen(int i, Dir *d, void *aux)
436 {
437         i += Qclient+1;
438         if(i > Qparsed){
439                 Client *cl = aux;
440                 Key *k;
441
442                 i -= Qparsed+1;
443                 if(cl == nil || cl->qbody == nil)
444                         return -1;
445                 for(k = cl->qbody->hdr; i > 0 && k; i--, k = k->next)
446                         ;
447                 if(k == nil || i > 0)
448                         return -1;
449                 i = Qheader;
450                 aux = k;
451         }
452         fsmkdir(d, i, aux);
453         return 0;
454 }
455
456 static int
457 parsedgen(int i, Dir *d, void *aux)
458 {
459         i += Qparsed+1;
460         if(i > Qurlfrag)
461                 return -1;
462         fsmkdir(d, i, aux);
463         return 0;
464 }
465
466 static void
467 fsread(Req *r)
468 {
469         char buf[1024];
470         Webfid *f;
471
472         f = r->fid->aux;
473         switch(f->level){
474         case Qroot:
475                 dirread9p(r, rootgen, nil);
476                 respond(r, nil);
477                 return;
478         case Qclient:
479                 dirread9p(r, clientgen, f->client);
480                 respond(r, nil);
481                 return;
482         case Qparsed:
483                 dirread9p(r, parsedgen, clienturl(f->client));
484                 respond(r, nil);
485                 return;
486         case Qrctl:
487                 buf[0] = 0;
488         String:
489                 readstr(r, buf);
490                 respond(r, nil);
491                 return;
492         case Qctl:
493                 snprint(buf, sizeof(buf), "%ld\n", CLIENTID(f->client));
494                 goto String;
495         case Qheader:
496                 snprint(buf, sizeof(buf), "%s", f->key->val);
497                 goto String;
498         case Qurl:
499         case Qurlschm:
500         case Qurluser:
501         case Qurlpass:
502         case Qurlhost:
503         case Qurlport:
504         case Qurlpath:
505         case Qurlqwry:
506         case Qurlfrag:
507                 urlstr(buf, sizeof(buf), clienturl(f->client), f->level);
508                 goto String;
509         case Qbody:
510                 bureq(f->buq, r);
511                 return;
512         }
513         respond(r, "not implemented");
514 }
515
516 static char*
517 rootctl(char *ctl, char *arg)
518 {
519         Url *u;
520
521         if(debug)
522                 fprint(2, "rootctl: %q %q\n", ctl, arg);
523
524         if(!strcmp(ctl, "flushauth")){
525                 u = nil;
526                 if(arg && *arg)
527                         u = saneurl(url(arg, 0));
528                 flushauth(u, 0);
529                 freeurl(u);
530                 return nil;
531         }
532         return "bad ctl message";
533 }
534
535 static char*
536 clientctl(Client *cl, char *ctl, char *arg)
537 {
538         char *p;
539         Url *u;
540         Key *k;
541
542         if(debug)
543                 fprint(2, "clientctl: %q %q\n", ctl, arg);
544
545         if(!strcmp(ctl, "url")){
546                 if((u = saneurl(url(arg, cl->baseurl))) == nil)
547                         return "bad url";
548                 freeurl(cl->url);
549                 cl->url = u;
550         }
551         else if(!strcmp(ctl, "baseurl")){
552                 if((u = url(arg, 0)) == nil)
553                         return "bad baseurl";
554                 freeurl(cl->baseurl);
555                 cl->baseurl = u;
556         }
557         else if(!strcmp(ctl, "request")){
558                 p = cl->request;
559                 strncpy(p, arg, sizeof(cl->request));
560                 for(; *p && isalpha(*p); p++)
561                         *p = toupper(*p);
562                 *p = 0;
563         }
564         else if(!strcmp(ctl, "headers")){
565                 while(arg && *arg){
566                         ctl = arg;
567                         while(*ctl && strchr("\r\n\t ", *ctl))
568                                 ctl++;
569                         if(arg = strchr(ctl, '\n'))
570                                 *arg++ = 0;
571                         if(k = parsehdr(ctl)){
572                                 k->next = cl->hdr;
573                                 cl->hdr = k;
574                         }
575                 }
576         }
577         else {
578                 char buf[128], **t;
579                 static char *tab[] = {
580                         "User-Agent",
581                         "Content-Type",
582                         nil,
583                 };
584                 for(t = tab; *t; t++){
585                         strncpy(buf, *t, sizeof(buf));
586                         if(!strcmp(ctl, fshdrname(buf))){
587                                 cl->hdr = delkey(cl->hdr, *t);
588                                 if(arg && *arg)
589                                         cl->hdr = addkey(cl->hdr, *t, arg);
590                                 break;
591                         }
592                 }
593                 if(*t == nil)
594                         return "bad ctl message";
595         }
596         return nil;
597 }
598
599 static void
600 fswrite(Req *r)
601 {
602         int n;
603         Webfid *f;
604         char *s, *t;
605
606         f = r->fid->aux;
607         switch(f->level){
608         case Qrctl:
609         case Qctl:
610                 n = r->ofcall.count = r->ifcall.count;
611                 s = emalloc(n+1);
612                 memmove(s, r->ifcall.data, n);
613                 while(n > 0 && strchr("\r\n", s[n-1]))
614                         n--;
615                 s[n] = 0;
616                 t = s;
617                 while(*t && strchr("\r\n\t ", *t)==0)
618                         t++;
619                 while(*t && strchr("\r\n\t ", *t))
620                         *t++ = 0;
621                 if(f->level == Qctl)
622                         t = clientctl(f->client, s, t);
623                 else
624                         t = rootctl(s, t);
625                 free(s);
626                 respond(r, t);
627                 return;
628         case Qpost:
629                 bureq(f->buq, r);
630                 return;
631         }
632         respond(r, "not implemented");
633 }
634
635 static void
636 fsflush(Req *r)
637 {
638         Webfid *f;
639         Req *o;
640
641         if(o = r->oldreq)
642         if(f = o->fid->aux)
643                 buflushreq(f->buq, o);
644         respond(r, nil);
645 }
646
647 static void
648 fsdestroyfid(Fid *fid)
649 {
650         Webfid *f;
651
652         if(f = fid->aux){
653                 fid->aux = nil;
654                 if(f->buq){
655                         buclose(f->buq, 0);
656                         if(f->client->qbody == f->buq){
657                                 f->client->obody = 0;
658                                 f->client->cbody = 1;
659                         }
660                         bufree(f->buq);
661                 }
662                 if(f->key)
663                         free(f->key);
664                 freeclient(f->client);
665                 free(f);
666         }
667 }
668
669 Srv fs = 
670 {
671         .attach=fsattach,
672         .stat=fsstat,
673         .walk1=fswalk1,
674         .clone=fsclone,
675         .open=fsopen,
676         .read=fsread,
677         .write=fswrite,
678         .flush=fsflush,
679         .destroyfid=fsdestroyfid,
680 };
681
682 void
683 usage(void)
684 {
685         fprint(2, "usage: %s [-D] [-m mtpt] [-s srv]\n", argv0);
686         exits("usage");
687 }
688
689 void
690 main(int argc, char *argv[])
691 {
692         char *srv, *mtpt, *s;
693
694         quotefmtinstall();
695         fmtinstall('U', Ufmt);
696         fmtinstall('E', Efmt);
697
698         srv = nil;
699         mtpt = "/mnt/web";
700         user = getuser();
701         time0 = time(0);
702
703         ARGBEGIN {
704         case 'D':
705                 chatty9p++;
706                 break;
707         case 'm':
708                 mtpt = EARGF(usage());
709                 break;
710         case 's':
711                 srv = EARGF(usage());
712                 break;
713         case 'd':
714                 debug++;
715                 break;
716         default:
717                 usage();
718         } ARGEND;
719
720         rfork(RFNOTEG);
721
722         if(s = getenv("httpproxy")){
723                 proxy = saneurl(url(s, 0));
724                 free(s);
725         }
726
727         postmountsrv(&fs, srv, mtpt, MREPL);
728 }