180ee5cbfSDavid du Colombier /* 280ee5cbfSDavid du Colombier * Accept new wiki pages or modifications to existing ones via POST method. 380ee5cbfSDavid du Colombier * 480ee5cbfSDavid du Colombier * Talks to the server at /srv/wiki.service. 580ee5cbfSDavid du Colombier */ 680ee5cbfSDavid du Colombier #include <u.h> 780ee5cbfSDavid du Colombier #include <libc.h> 880ee5cbfSDavid du Colombier #include <bio.h> 980ee5cbfSDavid du Colombier #include "httpd.h" 109a747e4fSDavid du Colombier #include "httpsrv.h" 1180ee5cbfSDavid du Colombier 1280ee5cbfSDavid du Colombier #define LOG "wiki" 1380ee5cbfSDavid du Colombier 149a747e4fSDavid du Colombier HConnect *hc; 159a747e4fSDavid du Colombier HSPriv *hp; 169a747e4fSDavid du Colombier 179a747e4fSDavid du Colombier 1880ee5cbfSDavid du Colombier /* go from possibly-latin1 url with escapes to utf */ 1980ee5cbfSDavid du Colombier char * 2080ee5cbfSDavid du Colombier _urlunesc(char *s) 2180ee5cbfSDavid du Colombier { 2280ee5cbfSDavid du Colombier char *t, *v, *u; 2380ee5cbfSDavid du Colombier Rune r; 2480ee5cbfSDavid du Colombier int c, n; 2580ee5cbfSDavid du Colombier 2680ee5cbfSDavid du Colombier /* unescape */ 279a747e4fSDavid du Colombier u = halloc(hc, strlen(s)+1); 2880ee5cbfSDavid du Colombier for(t = u; c = *s; s++){ 2980ee5cbfSDavid du Colombier if(c == '%'){ 3080ee5cbfSDavid du Colombier n = s[1]; 3180ee5cbfSDavid du Colombier if(n >= '0' && n <= '9') 3280ee5cbfSDavid du Colombier n = n - '0'; 3380ee5cbfSDavid du Colombier else if(n >= 'A' && n <= 'F') 3480ee5cbfSDavid du Colombier n = n - 'A' + 10; 3580ee5cbfSDavid du Colombier else if(n >= 'a' && n <= 'f') 3680ee5cbfSDavid du Colombier n = n - 'a' + 10; 3780ee5cbfSDavid du Colombier else 3880ee5cbfSDavid du Colombier break; 3980ee5cbfSDavid du Colombier r = n; 4080ee5cbfSDavid du Colombier n = s[2]; 4180ee5cbfSDavid du Colombier if(n >= '0' && n <= '9') 4280ee5cbfSDavid du Colombier n = n - '0'; 4380ee5cbfSDavid du Colombier else if(n >= 'A' && n <= 'F') 4480ee5cbfSDavid du Colombier n = n - 'A' + 10; 4580ee5cbfSDavid du Colombier else if(n >= 'a' && n <= 'f') 4680ee5cbfSDavid du Colombier n = n - 'a' + 10; 4780ee5cbfSDavid du Colombier else 4880ee5cbfSDavid du Colombier break; 4980ee5cbfSDavid du Colombier s += 2; 5080ee5cbfSDavid du Colombier c = r*16+n; 5180ee5cbfSDavid du Colombier } 5280ee5cbfSDavid du Colombier *t++ = c; 5380ee5cbfSDavid du Colombier } 5480ee5cbfSDavid du Colombier *t = 0; 5580ee5cbfSDavid du Colombier 5680ee5cbfSDavid du Colombier /* latin1 heuristic */ 579a747e4fSDavid du Colombier v = halloc(hc, UTFmax*strlen(u) + 1); 5880ee5cbfSDavid du Colombier s = u; 5980ee5cbfSDavid du Colombier t = v; 6080ee5cbfSDavid du Colombier while(*s){ 6180ee5cbfSDavid du Colombier /* in decoding error, assume latin1 */ 6280ee5cbfSDavid du Colombier if((n=chartorune(&r, s)) == 1 && r == 0x80) 6380ee5cbfSDavid du Colombier r = *s; 6480ee5cbfSDavid du Colombier s += n; 6580ee5cbfSDavid du Colombier t += runetochar(t, &r); 6680ee5cbfSDavid du Colombier } 6780ee5cbfSDavid du Colombier *t = 0; 6880ee5cbfSDavid du Colombier 6980ee5cbfSDavid du Colombier return v; 7080ee5cbfSDavid du Colombier } 7180ee5cbfSDavid du Colombier 7280ee5cbfSDavid du Colombier enum 7380ee5cbfSDavid du Colombier { 7480ee5cbfSDavid du Colombier MaxLog = 100*1024, /* limit on length of any one log request */ 7580ee5cbfSDavid du Colombier }; 7680ee5cbfSDavid du Colombier 7780ee5cbfSDavid du Colombier static int 7880ee5cbfSDavid du Colombier dangerous(char *s) 7980ee5cbfSDavid du Colombier { 8080ee5cbfSDavid du Colombier if(s == nil) 8180ee5cbfSDavid du Colombier return 1; 8280ee5cbfSDavid du Colombier 8380ee5cbfSDavid du Colombier /* 8480ee5cbfSDavid du Colombier * This check shouldn't be needed; 8580ee5cbfSDavid du Colombier * filename folding is already supposed to have happened. 8680ee5cbfSDavid du Colombier * But I'm paranoid. 8780ee5cbfSDavid du Colombier */ 8880ee5cbfSDavid du Colombier while(s = strchr(s,'/')){ 8980ee5cbfSDavid du Colombier if(s[1]=='.' && s[2]=='.') 9080ee5cbfSDavid du Colombier return 1; 9180ee5cbfSDavid du Colombier s++; 9280ee5cbfSDavid du Colombier } 9380ee5cbfSDavid du Colombier return 0; 9480ee5cbfSDavid du Colombier } 9580ee5cbfSDavid du Colombier 9680ee5cbfSDavid du Colombier char* 9780ee5cbfSDavid du Colombier unhttp(char *s) 9880ee5cbfSDavid du Colombier { 9980ee5cbfSDavid du Colombier char *p, *r, *w; 10080ee5cbfSDavid du Colombier 10180ee5cbfSDavid du Colombier if(s == nil) 10280ee5cbfSDavid du Colombier return nil; 10380ee5cbfSDavid du Colombier 10480ee5cbfSDavid du Colombier for(p=s; *p; p++) 10580ee5cbfSDavid du Colombier if(*p=='+') 10680ee5cbfSDavid du Colombier *p = ' '; 10780ee5cbfSDavid du Colombier s = _urlunesc(s); 10880ee5cbfSDavid du Colombier 10980ee5cbfSDavid du Colombier for(r=w=s; *r; r++){ 11080ee5cbfSDavid du Colombier if(*r != '\r') 11180ee5cbfSDavid du Colombier *w++ = *r; 11280ee5cbfSDavid du Colombier } 11380ee5cbfSDavid du Colombier *w = '\0'; 11480ee5cbfSDavid du Colombier return s; 11580ee5cbfSDavid du Colombier } 11680ee5cbfSDavid du Colombier 11780ee5cbfSDavid du Colombier void 1189a747e4fSDavid du Colombier mountwiki(HConnect *c, char *service) 11980ee5cbfSDavid du Colombier { 1209a747e4fSDavid du Colombier char buf[64]; 12180ee5cbfSDavid du Colombier int fd; 12280ee5cbfSDavid du Colombier 12380ee5cbfSDavid du Colombier snprint(buf, sizeof buf, "/srv/wiki.%s", service); 12480ee5cbfSDavid du Colombier if((fd = open(buf, ORDWR)) < 0){ 1259a747e4fSDavid du Colombier syslog(0, LOG, "%s open %s failed: %r", buf, hp->remotesys); 1269a747e4fSDavid du Colombier hfail(c, HNotFound); 12780ee5cbfSDavid du Colombier exits("failed"); 12880ee5cbfSDavid du Colombier } 1299a747e4fSDavid du Colombier if(mount(fd, -1, "/mnt/wiki", MREPL, "") < 0){ 1309a747e4fSDavid du Colombier syslog(0, LOG, "%s mount /mnt/wiki failed: %r", hp->remotesys); 1319a747e4fSDavid du Colombier hfail(c, HNotFound); 13280ee5cbfSDavid du Colombier exits("failed"); 13380ee5cbfSDavid du Colombier } 13480ee5cbfSDavid du Colombier close(fd); 13580ee5cbfSDavid du Colombier } 13680ee5cbfSDavid du Colombier 13780ee5cbfSDavid du Colombier char* 1389a747e4fSDavid du Colombier dowiki(HConnect *c, char *title, char *author, char *comment, char *base, ulong version, char *text) 13980ee5cbfSDavid du Colombier { 14080ee5cbfSDavid du Colombier int fd, l, n, err; 1419a747e4fSDavid du Colombier char *p, tmp[256]; 1429a747e4fSDavid du Colombier int i; 14380ee5cbfSDavid du Colombier 14480ee5cbfSDavid du Colombier if((fd = open("/mnt/wiki/new", ORDWR)) < 0){ 1459a747e4fSDavid du Colombier syslog(0, LOG, "%s open /mnt/wiki/new failed: %r", hp->remotesys); 1469a747e4fSDavid du Colombier hfail(c, HNotFound); 14780ee5cbfSDavid du Colombier exits("failed"); 14880ee5cbfSDavid du Colombier } 14980ee5cbfSDavid du Colombier 1509a747e4fSDavid du Colombier i=0; 1519a747e4fSDavid du Colombier if((i++,fprint(fd, "%s\nD%lud\nA%s (%s)\n", title, version, author, hp->remotesys) < 0) 1529a747e4fSDavid du Colombier || (i++,(comment && comment[0] && fprint(fd, "C%s\n", comment) < 0)) 1539a747e4fSDavid du Colombier || (i++,fprint(fd, "\n") < 0) 154*453ee12cSDavid du Colombier || (i++,(text[0] && write(fd, text, strlen(text)) != strlen(text)))){ 1559a747e4fSDavid du Colombier syslog(0, LOG, "%s write failed %d %ld fd %d: %r", hp->remotesys, i, strlen(text), fd); 1569a747e4fSDavid du Colombier hfail(c, HInternal); 15780ee5cbfSDavid du Colombier exits("failed"); 15880ee5cbfSDavid du Colombier } 15980ee5cbfSDavid du Colombier 16080ee5cbfSDavid du Colombier err = write(fd, "", 0); 16180ee5cbfSDavid du Colombier if(err) 1629a747e4fSDavid du Colombier syslog(0, LOG, "%s commit failed %d: %r", hp->remotesys, err); 16380ee5cbfSDavid du Colombier 16480ee5cbfSDavid du Colombier seek(fd, 0, 0); 16580ee5cbfSDavid du Colombier if((n = read(fd, tmp, sizeof(tmp)-1)) <= 0){ 1669a747e4fSDavid du Colombier if(n == 0) 1679a747e4fSDavid du Colombier werrstr("short read"); 1689a747e4fSDavid du Colombier syslog(0, LOG, "%s read failed: %r", hp->remotesys); 1699a747e4fSDavid du Colombier hfail(c, HInternal); 17080ee5cbfSDavid du Colombier exits("failed"); 17180ee5cbfSDavid du Colombier } 17280ee5cbfSDavid du Colombier 17380ee5cbfSDavid du Colombier tmp[n] = '\0'; 17480ee5cbfSDavid du Colombier 1759a747e4fSDavid du Colombier p = halloc(c, l=strlen(base)+strlen(tmp)+40); 1769a747e4fSDavid du Colombier snprint(p, l, "%s/%s/%s.html", base, tmp, err ? "werror" : "index"); 17780ee5cbfSDavid du Colombier return p; 17880ee5cbfSDavid du Colombier } 17980ee5cbfSDavid du Colombier 18080ee5cbfSDavid du Colombier 18180ee5cbfSDavid du Colombier void 18280ee5cbfSDavid du Colombier main(int argc, char **argv) 18380ee5cbfSDavid du Colombier { 18480ee5cbfSDavid du Colombier Hio *hin, *hout; 18580ee5cbfSDavid du Colombier char *s, *t, *p, *f[10]; 18680ee5cbfSDavid du Colombier char *text, *title, *service, *base, *author, *comment, *url; 18780ee5cbfSDavid du Colombier int i, nf; 18880ee5cbfSDavid du Colombier ulong version; 18980ee5cbfSDavid du Colombier 1909a747e4fSDavid du Colombier hc = init(argc, argv); 1919a747e4fSDavid du Colombier hp = hc->private; 19280ee5cbfSDavid du Colombier 1939a747e4fSDavid du Colombier if(dangerous(hc->req.uri)){ 1949a747e4fSDavid du Colombier hfail(hc, HSyntax); 19580ee5cbfSDavid du Colombier exits("failed"); 19680ee5cbfSDavid du Colombier } 19780ee5cbfSDavid du Colombier 1989a747e4fSDavid du Colombier if(hparseheaders(hc, HSTIMEOUT) < 0) 19980ee5cbfSDavid du Colombier exits("failed"); 2009a747e4fSDavid du Colombier hout = &hc->hout; 2019a747e4fSDavid du Colombier if(hc->head.expectother){ 2029a747e4fSDavid du Colombier hfail(hc, HExpectFail, nil); 20380ee5cbfSDavid du Colombier exits("failed"); 20480ee5cbfSDavid du Colombier } 2059a747e4fSDavid du Colombier if(hc->head.expectcont){ 20680ee5cbfSDavid du Colombier hprint(hout, "100 Continue\r\n"); 20780ee5cbfSDavid du Colombier hprint(hout, "\r\n"); 20880ee5cbfSDavid du Colombier hflush(hout); 20980ee5cbfSDavid du Colombier } 21080ee5cbfSDavid du Colombier 21180ee5cbfSDavid du Colombier s = nil; 2129a747e4fSDavid du Colombier if(strcmp(hc->req.meth, "POST") == 0){ 2139a747e4fSDavid du Colombier hin = hbodypush(&hc->hin, hc->head.contlen, hc->head.transenc); 21480ee5cbfSDavid du Colombier if(hin != nil){ 21580ee5cbfSDavid du Colombier alarm(15*60*1000); 21680ee5cbfSDavid du Colombier s = hreadbuf(hin, hin->pos); 21780ee5cbfSDavid du Colombier alarm(0); 21880ee5cbfSDavid du Colombier } 21980ee5cbfSDavid du Colombier if(s == nil){ 2209a747e4fSDavid du Colombier hfail(hc, HBadReq, nil); 22180ee5cbfSDavid du Colombier exits("failed"); 22280ee5cbfSDavid du Colombier } 22380ee5cbfSDavid du Colombier t = strchr(s, '\n'); 22480ee5cbfSDavid du Colombier if(t != nil) 22580ee5cbfSDavid du Colombier *t = '\0'; 22680ee5cbfSDavid du Colombier }else{ 2279a747e4fSDavid du Colombier hunallowed(hc, "GET, HEAD, PUT"); 22880ee5cbfSDavid du Colombier exits("unallowed"); 2299a747e4fSDavid du Colombier } 23080ee5cbfSDavid du Colombier 23180ee5cbfSDavid du Colombier if(s == nil){ 2329a747e4fSDavid du Colombier hfail(hc, HNoData, "wiki"); 23380ee5cbfSDavid du Colombier exits("failed"); 23480ee5cbfSDavid du Colombier } 23580ee5cbfSDavid du Colombier 23680ee5cbfSDavid du Colombier text = nil; 23780ee5cbfSDavid du Colombier title = nil; 23880ee5cbfSDavid du Colombier service = nil; 23980ee5cbfSDavid du Colombier author = "???"; 24080ee5cbfSDavid du Colombier comment = ""; 24180ee5cbfSDavid du Colombier base = nil; 24280ee5cbfSDavid du Colombier version = ~0; 24380ee5cbfSDavid du Colombier nf = getfields(s, f, nelem(f), 1, "&"); 24480ee5cbfSDavid du Colombier for(i=0; i<nf; i++){ 24580ee5cbfSDavid du Colombier if((p = strchr(f[i], '=')) == nil) 24680ee5cbfSDavid du Colombier continue; 24780ee5cbfSDavid du Colombier *p++ = '\0'; 24880ee5cbfSDavid du Colombier if(strcmp(f[i], "title")==0) 24980ee5cbfSDavid du Colombier title = p; 25080ee5cbfSDavid du Colombier else if(strcmp(f[i], "version")==0) 25180ee5cbfSDavid du Colombier version = strtoul(unhttp(p), 0, 10); 25280ee5cbfSDavid du Colombier else if(strcmp(f[i], "text")==0) 25380ee5cbfSDavid du Colombier text = p; 25480ee5cbfSDavid du Colombier else if(strcmp(f[i], "service")==0) 25580ee5cbfSDavid du Colombier service = p; 25680ee5cbfSDavid du Colombier else if(strcmp(f[i], "comment")==0) 25780ee5cbfSDavid du Colombier comment = p; 25880ee5cbfSDavid du Colombier else if(strcmp(f[i], "author")==0) 25980ee5cbfSDavid du Colombier author = p; 26080ee5cbfSDavid du Colombier else if(strcmp(f[i], "base")==0) 26180ee5cbfSDavid du Colombier base = p; 26280ee5cbfSDavid du Colombier } 26380ee5cbfSDavid du Colombier 26480ee5cbfSDavid du Colombier syslog(0, LOG, "%s post s %s t '%s' v %ld a %s c %s b %s t 0x%p", 2659a747e4fSDavid du Colombier hp->remotesys, service, title, (long)version, author, comment, base, text); 26680ee5cbfSDavid du Colombier 26780ee5cbfSDavid du Colombier title = unhttp(title); 26880ee5cbfSDavid du Colombier comment = unhttp(comment); 26980ee5cbfSDavid du Colombier service = unhttp(service); 27080ee5cbfSDavid du Colombier text = unhttp(text); 27180ee5cbfSDavid du Colombier author = unhttp(author); 27280ee5cbfSDavid du Colombier base = unhttp(base); 27380ee5cbfSDavid du Colombier 27480ee5cbfSDavid du Colombier if(title==nil || version==~0 || text==nil || text[0]=='\0' || base == nil 27580ee5cbfSDavid du Colombier || service == nil || strchr(title, '\n') || strchr(comment, '\n') 2769a747e4fSDavid du Colombier || dangerous(service) || strchr(service, '/') || strlen(service)>20){ 2779a747e4fSDavid du Colombier syslog(0, LOG, "%s failed dangerous", hp->remotesys); 2789a747e4fSDavid du Colombier hfail(hc, HSyntax); 27980ee5cbfSDavid du Colombier exits("failed"); 28080ee5cbfSDavid du Colombier } 28180ee5cbfSDavid du Colombier 28280ee5cbfSDavid du Colombier syslog(0, LOG, "%s post s %s t '%s' v %ld a %s c %s", 2839a747e4fSDavid du Colombier hp->remotesys, service, title, (long)version, author, comment); 28480ee5cbfSDavid du Colombier 28580ee5cbfSDavid du Colombier if(strlen(text) > MaxLog) 28680ee5cbfSDavid du Colombier text[MaxLog] = '\0'; 28780ee5cbfSDavid du Colombier 2889a747e4fSDavid du Colombier mountwiki(hc, service); 2899a747e4fSDavid du Colombier url = dowiki(hc, title, author, comment, base, version, text); 2909a747e4fSDavid du Colombier hredirected(hc, "303 See Other", url); 29180ee5cbfSDavid du Colombier exits(nil); 29280ee5cbfSDavid du Colombier } 293