ref: cf032667921f9b25f5b6f0b7ade743087dc64e5c
dir: /bin/paste.c/
#include <u.h> #include <libc.h> #include <ctype.h> #include <bio.h> Biobuf *stdin; int Bpeek(Biobuf *b) { int c; c = Bgetc(b); if(c >= 0) Bungetc(b); return c; } static char hex[] = "0123456789abcdef"; static char Hex[] = "0123456789ABCDEF"; static int hexdigit(int c) { char *p; if(c > 0){ if((p = strchr(Hex, c)) != 0) return p - Hex; if((p = strchr(hex, c)) != 0) return p - hex; } return -1; } static char* urldecode(char *dst, int len, char *src) { char c; char *p; p = dst; while((c = *src++) != 0){ if(len < 2) return nil; if(c == '%'){ int c1, c2, x1, x2; if((c1 = *src++) == 0) break; if((x1 = hexdigit(c1)) < 0){ src--; *p++ = c1; len--; continue; } if((c2 = *src++) == 0) break; if((x2 = hexdigit(c2)) < 0){ if(len < 3) return nil; src--; *p++ = c; *p++ = c1; len -= 2; continue; } c = x1<<4 | x2; } else if(c == '+') c = ' '; *p++ = c; len--; } *p = 0; return dst; } static char* urlencode(char *dst, int len, char *src) { char c; char *p; p = dst; while((c = *src++) != 0){ if(len < 2) return nil; len--; if(c>0 && strchr("/$-_@.!*'(),", c) || 'a'<=c && c<='z' || 'A'<=c && c<='Z' || '0'<=c && c<='9') *p++ = c; else if(c == ' ') *p++ = '+'; else { *p++ = '%'; if(len < 3) return nil; len -= 2; *p++ = Hex[c>>4]; *p++ = Hex[c&15]; } } *p = 0; return dst; } static void error(int code, char *text) { fprint(1, "Status: %d %s\r\n", code, text); fprint(1, "\r\n"); fprint(1, "%s\r\n", text); } static void error400(void) { error(400, "Bad request"); } static void error403(void) { error(403, "Forbidden"); } static void error404(void) { error(404, "Not found"); } static void error500(void) { error(500, "Internal server error"); } static void permredirect(char *location) { char buf[1024]; if(urlencode(buf, sizeof(buf), location) != nil){ fprint(1, "Status: 301 Moved Permanently\r\n"); fprint(1, "Location: %s\r\n", buf); fprint(1, "\r\n"); }else error500(); exits(0); } static void postredirect(char *location) { char buf[1024]; if(urlencode(buf, sizeof(buf), location) != nil){ fprint(1, "Status: 303 See Other\r\n"); fprint(1, "Location: %s\r\n", buf); fprint(1, "\r\n"); }else error500(); exits(0); } static char * stredup(char *s, char *e) { char *x; x = malloc(e - s + 1); strecpy(x, x + (e - s) + 1, s); return x; } static char* findboundary(char *s) { int quoted; char *e; quoted = 0; while(*s){ if(*s++ == ';') break; } for(;;){ while(isspace(*s)) s++; if(*s == 0) return nil; if(strncmp(s, "boundary=", 9) == 0){ s += 9; if(*s == '"'){ quoted = 1; s++; } e = s; while(*e){ if(quoted){ if(*e == '"') break; }else{ if(isspace(*e)) break; } e++; } return stredup(s, e); } while(!isspace(*s)) s++; } } static char base62(ulong n) { n = n % 62; if(n < 10) return '0' + n; if(n < 36) return 'a' + n - 10; return 'A' + n - 36; } static char* nmktemp0(char *s, char *as) { ulong n; strcpy(s, as); n = lrand(); while(*s) s++; while(*--s == 'X'){ *s = base62(n); n = n/62; if(n == 0) n = lrand(); } return s; } static char* nmktemp(char *as) { int fd; char *s; s = malloc(strlen(as)+1); if(s == nil) return "/"; for(;;){ nmktemp0(s, as); fd = create(s, OEXCL|OREAD, 0755|DMDIR); if(fd < 0){ if(access(s, AEXIST) == 0) continue; free(s); return "/"; } close(fd); strcpy(as, s); free(s); return as; } } static void skipws(void) { int c; for(;;){ c = Bgetc(stdin); if(c < 0) return; if(!isspace(c) || c == '\r' || c == '\n'){ Bungetc(stdin); return; } } } static int copydata(Biobuf *out, char *marker) { int c, i, buflen; char *buf; buflen = strlen(marker); buf = malloc(buflen); for(i = 0; i < buflen; i++){ c = Bgetc(stdin); if(c < 0) goto Error; buf[i] = c; } i = 0; for(;;){ //fprint(2, "buf[%d:] = %.*s\n", i, buflen-i, buf+i); //fprint(2, "buf[:%d] = %.*s\n", i, i, buf); if(memcmp(buf+i, marker, buflen-i) == 0) if(memcmp(buf, marker+buflen-i, i) == 0) break; c = Bgetc(stdin); if(c < 0) goto Error; if(out != nil){ if(Bputc(out, buf[i]) < 0) goto Error; } buf[i] = c; i = (i+1)%buflen; } free(buf); if(out != nil) if(Bflush(out) < 0) return -1; return 1; Error: free(buf); return -1; } static int skipto(char *marker) { return copydata(nil, marker); } enum{ KeyMax = 256, ValueMax = 1024, }; static int crlf(void) { int c; c = Bgetc(stdin); if(c < 0) return 0; if(c != '\r'){ Bungetc(stdin); return 0; } c = Bgetc(stdin); if(c < 0) return 0; if(c == '\n') return 1; return 0; } static int parsekey(char *key, char sep) { int c; char *kp; skipws(); if(crlf()) return 0; kp = key; for(;;){ c = Bgetc(stdin); if(c < 0 || isspace(c)) return 0; if(c == sep){ *kp = 0; break; } *kp++ = c; if(kp - key > KeyMax){ return 0; } } return 1; } static int parsequoted(char *val, char end) { int c; char *vp; vp = val; for(;;){ c = Bgetc(stdin); if(c < 0) return 0; if(c == '"'){ skipws(); c = Bgetc(stdin); if(c >= 0){ if(c == '\r') Bungetc(stdin); else if(c != end) return 0; } *vp = 0; break; } *vp++ = c; if(vp - val > ValueMax){ return 0; } } return 1; } static int parseval(char *val, char end) { int c; char *vp; vp = val; skipws(); c = Bgetc(stdin); if(c < 0){ return 0; } if(c == '"') return parsequoted(val, end); Bungetc(stdin); for(;;){ c = Bgetc(stdin); if(c < 0){ return 0; } if(c == '\r' || c == '\n' || c == end){ if(c == '\r') Bungetc(stdin); *vp = 0; break; } *vp++ = c; if(vp - val > ValueMax){ return 0; } } return 1; } static int dashdash(void) { int c; c = Bgetc(stdin); if(c < 0) return 0; if(c != '-'){ Bungetc(stdin); return 0; } c = Bgetc(stdin); if(c < 0 || c != '-') return 0; return 1; } static void handlepost(void) { int i, nfiles; char *ctype; char *boundary, *mark; char *dir, *filename, *target; char hdr[KeyMax], key[KeyMax], val[ValueMax]; Biobuf *out; ctype = getenv("CONTENT_TYPE"); if(ctype == nil){ fprint(2, "CGI variable CONTENT_TYPE not set!\n"); error500(); exits("missing CONTENT_TYPE"); } if(strncmp(ctype, "multipart/form-data;", 20) != 0){ fprint(2, "expected multipart/form-data content-type; got %s\n", ctype); error400(); exits("unexpected content-type"); } boundary = findboundary(ctype); if(boundary == nil){ fprint(2, "could not find boundary in CONTENT_TYPE\n"); error400(); exits("missing boundary for multipart/form-data"); } mark = smprint("--%s", boundary); free(boundary); dir = smprint("%s/uploads/XXXXXXXXXXX", getenv("FS_ROOT")); if(strcmp(nmktemp(dir), "/") == 0){ fprint(2, "could not create upload directory: mktemp: %r\n"); error500(); exits("mktemp"); } target = nil; i = 0; nfiles = 0; filename = nil; if(skipto(mark) != 1){ error400(); exits(0); } for(;;){ if(dashdash()) break; if(!crlf()){ fprint(2, "expected CRLF sequence after boundary marker\n"); error400(); exits(0); } free(filename); filename = nil; for(;;){ if(crlf()) break; if(!parsekey(hdr, ':')) break; if(!parseval(val, ';')) break; if(!crlf()){ for(;;){ if(!parsekey(key, '=')) break; if(!parseval(val, ';')) break; if(cistrcmp(hdr, "content-disposition") == 0){ if(cistrcmp(key, "filename") == 0){ if(strlen(val) > 0){ free(filename); filename = strrchr(val, '/'); if(filename == nil) filename = val; else filename++; filename = smprint("%s/%s", dir, filename); } }else if(cistrcmp(key, "name") == 0){ if(strcmp(val, "text") == 0) if(filename == nil) filename = smprint("%s/text", dir); } } skipws(); if(crlf()) break; } } } if(i == 0) mark = smprint("\r\n%s", mark); i++; if(filename != nil && Bpeek(stdin) != '\r'){ out = Bopen(filename, OWRITE); if(out == nil){ fprint(2, "could not open %s: %r\n", filename); error500(); exits("open"); } if(copydata(out, mark) < 0){ fprint(2, "error writing to %s: %r\n", filename); error500(); exits("copydata"); } Bterm(out); nfiles++; if(nfiles == 1){ free(target); target = smprint("/uploads/%s/%s", strrchr(dir, '/')+1, strrchr(filename, '/')+1); }else if(nfiles == 2){ free(target); target = smprint("/uploads/%s/", strrchr(dir, '/')+1); } }else if(skipto(mark) < 0){ fprint(2, "unexpected EOF\n"); error400(); exits(0); } } postredirect(target); exits(0); } static void dirindex(int fd) { char url[1024]; long n; Dir *d, *dirs; n = dirreadall(fd, &dirs); if(n == 0) return; if(n < 0){ fprint(2, "paste: dirreadall: %r\n"); error500(); exits("dirreadall"); } fprint(1, "Status: 200 OK\r\n"); fprint(1, "\r\n"); fprint(1, "<!DOCTYPE html>\r\n"); d = dirs; while(n-- > 0){ if(urlencode(url, sizeof(url), d->name) != nil) fprint(1, "<a href=\"%s\">%s</a><br>\n", url, d->name); else fprint(1, "%s<br>\n", d->name); d++; } free(dirs); } static void serve(char *path) { int fd, n; Dir *d; char buf[4*1024]; d = dirstat(path); fd = open(path, OREAD); if(fd < 0){ error404(); exits(0); } if((d->mode&DMDIR) != 0){ dirindex(fd); exits(0); } fprint(1, "Status: 200 OK\r\n"); if(strcmp(d->name, "text") == 0) fprint(1, "Content-type: text/plain; charset=utf-8\r\n"); fprint(1, "Content-length: %lld\r\n", d->length); fprint(1, "\r\n"); for(;;){ n = read(fd, buf, sizeof(buf)); if(n == 0) exits(0); if(n < 0){ fprint(2, "paste: error reading from %s: %r\n", path); exits("read error"); } if(write(1, buf, n) != n){ fprint(2, "paste: write: %r\n"); exits("write error"); } } } static void usage(void) { fprint(2, "usage: paste\n"); exits("usage"); } void main(int argc, char **argv) { char buf[1024]; char *requri; ARGBEGIN{ default: usage(); }ARGEND; srand(nsec()); requri = getenv("REQUEST_URI"); if(requri == nil){ fprint(2, "CGI variable REQUEST_URI not set!\n"); error500(); exits("missing REQUEST_URI"); } requri = urldecode(buf, sizeof(buf), requri); if(requri == nil){ error500(); exits(0); } if(strcmp(requri, "/") == 0) permredirect("/index.html"); if(strcmp(requri, "/uploads") == 0) permredirect("/uploads/"); if(strcmp(requri, "/uploads/") == 0){ error403(); exits(0); } stdin = Bfdopen(0, OREAD); if(strcmp(requri, "/index.html") == 0 || strcmp(requri, "/style.css") == 0) serve(smprint("%s/%s", getenv("FS_ROOT"), requri)); if(strncmp(requri, "/uploads/", 9) == 0) serve(smprint("%s/%s", getenv("FS_ROOT"), requri)); if(strcmp(requri, "/post") == 0) handlepost(); error404(); }