123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292 |
- /*
- * Accept new wiki pages or modifications to existing ones via POST method.
- *
- * Talks to the server at /srv/wiki.service.
- */
- #include <u.h>
- #include <libc.h>
- #include <bio.h>
- #include "httpd.h"
- #include "httpsrv.h"
- #define LOG "wiki"
- HConnect *hc;
- HSPriv *hp;
- /* go from possibly-latin1 url with escapes to utf */
- char *
- _urlunesc(char *s)
- {
- char *t, *v, *u;
- Rune r;
- int c, n;
- /* unescape */
- u = halloc(hc, strlen(s)+1);
- for(t = u; c = *s; s++){
- if(c == '%'){
- n = s[1];
- if(n >= '0' && n <= '9')
- n = n - '0';
- else if(n >= 'A' && n <= 'F')
- n = n - 'A' + 10;
- else if(n >= 'a' && n <= 'f')
- n = n - 'a' + 10;
- else
- break;
- r = n;
- n = s[2];
- if(n >= '0' && n <= '9')
- n = n - '0';
- else if(n >= 'A' && n <= 'F')
- n = n - 'A' + 10;
- else if(n >= 'a' && n <= 'f')
- n = n - 'a' + 10;
- else
- break;
- s += 2;
- c = r*16+n;
- }
- *t++ = c;
- }
- *t = 0;
- /* latin1 heuristic */
- v = halloc(hc, UTFmax*strlen(u) + 1);
- s = u;
- t = v;
- while(*s){
- /* in decoding error, assume latin1 */
- if((n=chartorune(&r, s)) == 1 && r == 0x80)
- r = *s;
- s += n;
- t += runetochar(t, &r);
- }
- *t = 0;
- return v;
- }
- enum
- {
- MaxLog = 100*1024, /* limit on length of any one log request */
- };
- static int
- dangerous(char *s)
- {
- if(s == nil)
- return 1;
- /*
- * This check shouldn't be needed;
- * filename folding is already supposed to have happened.
- * But I'm paranoid.
- */
- while(s = strchr(s,'/')){
- if(s[1]=='.' && s[2]=='.')
- return 1;
- s++;
- }
- return 0;
- }
- char*
- unhttp(char *s)
- {
- char *p, *r, *w;
- if(s == nil)
- return nil;
- for(p=s; *p; p++)
- if(*p=='+')
- *p = ' ';
- s = _urlunesc(s);
- for(r=w=s; *r; r++){
- if(*r != '\r')
- *w++ = *r;
- }
- *w = '\0';
- return s;
- }
- void
- mountwiki(HConnect *c, char *service)
- {
- char buf[64];
- int fd;
- snprint(buf, sizeof buf, "/srv/wiki.%s", service);
- if((fd = open(buf, ORDWR)) < 0){
- syslog(0, LOG, "%s open %s failed: %r", buf, hp->remotesys);
- hfail(c, HNotFound);
- exits("failed");
- }
- if(mount(fd, -1, "/mnt/wiki", MREPL, "") < 0){
- syslog(0, LOG, "%s mount /mnt/wiki failed: %r", hp->remotesys);
- hfail(c, HNotFound);
- exits("failed");
- }
- close(fd);
- }
- char*
- dowiki(HConnect *c, char *title, char *author, char *comment, char *base, ulong version, char *text)
- {
- int fd, l, n, err;
- char *p, tmp[256];
- int i;
- if((fd = open("/mnt/wiki/new", ORDWR)) < 0){
- syslog(0, LOG, "%s open /mnt/wiki/new failed: %r", hp->remotesys);
- hfail(c, HNotFound);
- exits("failed");
- }
- i=0;
- if((i++,fprint(fd, "%s\nD%lud\nA%s (%s)\n", title, version, author, hp->remotesys) < 0)
- || (i++,(comment && comment[0] && fprint(fd, "C%s\n", comment) < 0))
- || (i++,fprint(fd, "\n") < 0)
- || (i++,(text[0] && write(fd, text, strlen(text)) != strlen(text)))){
- syslog(0, LOG, "%s write failed %d %ld fd %d: %r", hp->remotesys, i, strlen(text), fd);
- hfail(c, HInternal);
- exits("failed");
- }
- err = write(fd, "", 0);
- if(err)
- syslog(0, LOG, "%s commit failed %d: %r", hp->remotesys, err);
- seek(fd, 0, 0);
- if((n = read(fd, tmp, sizeof(tmp)-1)) <= 0){
- if(n == 0)
- werrstr("short read");
- syslog(0, LOG, "%s read failed: %r", hp->remotesys);
- hfail(c, HInternal);
- exits("failed");
- }
- tmp[n] = '\0';
- p = halloc(c, l=strlen(base)+strlen(tmp)+40);
- snprint(p, l, "%s/%s/%s.html", base, tmp, err ? "werror" : "index");
- return p;
- }
- void
- main(int argc, char **argv)
- {
- Hio *hin, *hout;
- char *s, *t, *p, *f[10];
- char *text, *title, *service, *base, *author, *comment, *url;
- int i, nf;
- ulong version;
- hc = init(argc, argv);
- hp = hc->private;
- if(dangerous(hc->req.uri)){
- hfail(hc, HSyntax);
- exits("failed");
- }
- if(hparseheaders(hc, HSTIMEOUT) < 0)
- exits("failed");
- hout = &hc->hout;
- if(hc->head.expectother){
- hfail(hc, HExpectFail, nil);
- exits("failed");
- }
- if(hc->head.expectcont){
- hprint(hout, "100 Continue\r\n");
- hprint(hout, "\r\n");
- hflush(hout);
- }
- s = nil;
- if(strcmp(hc->req.meth, "POST") == 0){
- hin = hbodypush(&hc->hin, hc->head.contlen, hc->head.transenc);
- if(hin != nil){
- alarm(15*60*1000);
- s = hreadbuf(hin, hin->pos);
- alarm(0);
- }
- if(s == nil){
- hfail(hc, HBadReq, nil);
- exits("failed");
- }
- t = strchr(s, '\n');
- if(t != nil)
- *t = '\0';
- }else{
- hunallowed(hc, "GET, HEAD, PUT");
- exits("unallowed");
- }
- if(s == nil){
- hfail(hc, HNoData, "wiki");
- exits("failed");
- }
- text = nil;
- title = nil;
- service = nil;
- author = "???";
- comment = "";
- base = nil;
- version = ~0;
- nf = getfields(s, f, nelem(f), 1, "&");
- for(i=0; i<nf; i++){
- if((p = strchr(f[i], '=')) == nil)
- continue;
- *p++ = '\0';
- if(strcmp(f[i], "title")==0)
- title = p;
- else if(strcmp(f[i], "version")==0)
- version = strtoul(unhttp(p), 0, 10);
- else if(strcmp(f[i], "text")==0)
- text = p;
- else if(strcmp(f[i], "service")==0)
- service = p;
- else if(strcmp(f[i], "comment")==0)
- comment = p;
- else if(strcmp(f[i], "author")==0)
- author = p;
- else if(strcmp(f[i], "base")==0)
- base = p;
- }
- syslog(0, LOG, "%s post s %s t '%s' v %ld a %s c %s b %s t 0x%p",
- hp->remotesys, service, title, (long)version, author, comment, base, text);
- title = unhttp(title);
- comment = unhttp(comment);
- service = unhttp(service);
- text = unhttp(text);
- author = unhttp(author);
- base = unhttp(base);
- if(title==nil || version==~0 || text==nil || text[0]=='\0' || base == nil
- || service == nil || strchr(title, '\n') || strchr(comment, '\n')
- || dangerous(service) || strchr(service, '/') || strlen(service)>20){
- syslog(0, LOG, "%s failed dangerous", hp->remotesys);
- hfail(hc, HSyntax);
- exits("failed");
- }
- syslog(0, LOG, "%s post s %s t '%s' v %ld a %s c %s",
- hp->remotesys, service, title, (long)version, author, comment);
- if(strlen(text) > MaxLog)
- text[MaxLog] = '\0';
- mountwiki(hc, service);
- url = dowiki(hc, title, author, comment, base, version, text);
- hredirected(hc, "303 See Other", url);
- exits(nil);
- }
|