ref: 712fcc55cd95148faefb531d9c19cd870725bb96
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 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
permredirect(char *location)
{
fprint(1, "Status: 301 Moved Permanently\r\n");
fprint(1, "Location: %s\r\n", location);
fprint(1, "\r\n");
exits(0);
}
static void
postredirect(char *location)
{
fprint(1, "Status: 303 See Other\r\n");
fprint(1, "Location: %s\r\n", location);
fprint(1, "\r\n");
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
skipto(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){
free(buf);
return -1;
}
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){
free(buf);
return 1;
}
c = Bgetc(stdin);
if(c < 0){
free(buf);
return -1;
}
buf[i] = c;
i = (i+1)%buflen;
}
}
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;
}
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");
error(500, "Internal Server Error");
exits("missing CONTENT_TYPE");
}
if(strncmp(ctype, "multipart/form-data;", 20) != 0){
fprint(2, "expected multipart/form-data content-type; got %s\n", ctype);
error(400, "Bad Request");
exits("unexpected content-type");
}
boundary = findboundary(ctype);
if(boundary == nil){
fprint(2, "could not find boundary in CONTENT_TYPE\n");
error(400, "Bad Request");
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");
error(500, "Internal Server Error");
exits("mktemp");
}
target = nil;
i = 0;
nfiles = 0;
filename = nil;
if(skipto(mark) != 1){
error(400, "Bad Request");
exits(0);
}
for(;;){
if(dashdash())
break;
if(!crlf()){
fprint(2, "expected CRLF sequence after boundary marker\n");
error(400, "Bad Request");
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 = smprint("%s/%s", dir, val);
}
}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);
error(500, "Internal Server Error");
exits("open");
}
if(copydata(out, mark) < 0){
fprint(2, "error writing to %s: %r\n", filename);
error(500, "Internal Server Error");
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(copydata(nil, mark) < 0){
fprint(2, "unexpected EOF\n");
error(400, "Bad Request");
exits(0);
}
}
postredirect(target);
exits(0);
}
static void
dirindex(int fd)
{
long n;
Dir *d, *dirs;
n = dirreadall(fd, &dirs);
if(n == 0)
return;
if(n < 0){
fprint(2, "paste: dirreadall: %r\n");
error(500, "Internal Server Error");
exits("dirreadall");
}
fprint(1, "Status: 200 OK\r\n");
fprint(1, "\r\n");
fprint(1, "<!DOCTYPE html>\r\n");
d = dirs;
while(n-- > 0){
fprint(1, "<a href=\"%s\">%s</a><br>\n", d->name, 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){
error(404, "Not Found");
exits(0);
}
if((d->mode&DMDIR) != 0){
dirindex(fd);
exits(0);
}
fprint(1, "Status: 200 OK\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 *requri;
ARGBEGIN{
default:
usage();
}ARGEND;
srand(nsec());
requri = getenv("REQUEST_URI");
if(requri == nil){
fprint(2, "CGI variable REQUEST_URI not set!\n");
error(500, "Internal Server Error");
exits("missing REQUEST_URI");
}
if(strcmp(requri, "/") == 0)
permredirect("/index.html");
if(strcmp(requri, "/uploads/") == 0){
error(403, "Forbidden");
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();
error(404, "Not Found");
}