mirror of
https://github.com/9fans/plan9port.git
synced 2025-01-24 11:41:58 +00:00
add libhttpd
This commit is contained in:
parent
b6afd33e2f
commit
9df487d720
19 changed files with 2722 additions and 0 deletions
35
src/libhttpd/alloc.c
Normal file
35
src/libhttpd/alloc.c
Normal file
|
@ -0,0 +1,35 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <bin.h>
|
||||
#include <httpd.h>
|
||||
|
||||
/*
|
||||
* memory allocators:
|
||||
* h routines call canalloc; they should be used by everything else
|
||||
* note this memory is wiped out at the start of each new request
|
||||
* note: these routines probably shouldn't fatal.
|
||||
*/
|
||||
char*
|
||||
hstrdup(HConnect *c, char *s)
|
||||
{
|
||||
char *t;
|
||||
int n;
|
||||
|
||||
n = strlen(s) + 1;
|
||||
t = binalloc(&c->bin, n, 0);
|
||||
if(t == nil)
|
||||
sysfatal("out of memory");
|
||||
memmove(t, s, n);
|
||||
return t;
|
||||
}
|
||||
|
||||
void*
|
||||
halloc(HConnect *c, ulong n)
|
||||
{
|
||||
void *p;
|
||||
|
||||
p = binalloc(&c->bin, n, 1);
|
||||
if(p == nil)
|
||||
sysfatal("out of memory");
|
||||
return p;
|
||||
}
|
33
src/libhttpd/checkcontent.c
Normal file
33
src/libhttpd/checkcontent.c
Normal file
|
@ -0,0 +1,33 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <bin.h>
|
||||
#include <httpd.h>
|
||||
|
||||
int
|
||||
hcheckcontent(HContent *me, HContent *oks, char *list, int size)
|
||||
{
|
||||
HContent *ok;
|
||||
|
||||
if(oks == nil || me == nil)
|
||||
return 1;
|
||||
for(ok = oks; ok != nil; ok = ok->next){
|
||||
if((cistrcmp(ok->generic, me->generic) == 0 || strcmp(ok->generic, "*") == 0)
|
||||
&& (me->specific == nil || cistrcmp(ok->specific, me->specific) == 0 || strcmp(ok->specific, "*") == 0)){
|
||||
if(ok->mxb > 0 && size > ok->mxb)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
USED(list);
|
||||
if(0){
|
||||
fprint(2, "list: %s/%s not found\n", me->generic, me->specific);
|
||||
for(; oks != nil; oks = oks->next){
|
||||
if(oks->specific)
|
||||
fprint(2, "\t%s/%s\n", oks->generic, oks->specific);
|
||||
else
|
||||
fprint(2, "\t%s\n", oks->generic);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
212
src/libhttpd/date.c
Normal file
212
src/libhttpd/date.c
Normal file
|
@ -0,0 +1,212 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <httpd.h>
|
||||
|
||||
/*
|
||||
* print dates in the format
|
||||
* Wkd, DD Mon YYYY HH:MM:SS GMT
|
||||
* parse dates of formats
|
||||
* Wkd, DD Mon YYYY HH:MM:SS GMT
|
||||
* Weekday, DD-Mon-YY HH:MM:SS GMT
|
||||
* Wkd Mon ( D|DD) HH:MM:SS YYYY
|
||||
* plus anything similar
|
||||
*/
|
||||
static char *
|
||||
weekdayname[7] =
|
||||
{
|
||||
"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
|
||||
};
|
||||
static char *
|
||||
wdayname[7] =
|
||||
{
|
||||
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
|
||||
};
|
||||
|
||||
static char *
|
||||
monname[12] =
|
||||
{
|
||||
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
|
||||
};
|
||||
|
||||
static int dateindex(char*, char**, int);
|
||||
|
||||
static int
|
||||
dtolower(int c)
|
||||
{
|
||||
if(c >= 'A' && c <= 'Z')
|
||||
return c - 'A' + 'a';
|
||||
return c;
|
||||
}
|
||||
|
||||
static int
|
||||
disalpha(int c)
|
||||
{
|
||||
return c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z';
|
||||
}
|
||||
|
||||
static int
|
||||
disdig(int c)
|
||||
{
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
int
|
||||
hdatefmt(Fmt *f)
|
||||
{
|
||||
Tm *tm;
|
||||
ulong t;
|
||||
|
||||
t = va_arg(f->args, ulong);
|
||||
tm = gmtime(t);
|
||||
return fmtprint(f, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
|
||||
wdayname[tm->wday], tm->mday, monname[tm->mon], tm->year+1900,
|
||||
tm->hour, tm->min, tm->sec);
|
||||
}
|
||||
|
||||
static char*
|
||||
dateword(char *date, char *buf)
|
||||
{
|
||||
char *p;
|
||||
int c;
|
||||
|
||||
p = buf;
|
||||
while(!disalpha(c = *date) && !disdig(c) && c)
|
||||
date++;
|
||||
while(disalpha(c = *date)){
|
||||
if(p - buf < 30)
|
||||
*p++ = dtolower(c);
|
||||
date++;
|
||||
}
|
||||
*p = 0;
|
||||
return date;
|
||||
}
|
||||
|
||||
static int
|
||||
datenum(char **d)
|
||||
{
|
||||
char *date;
|
||||
int c, n;
|
||||
|
||||
date = *d;
|
||||
while(!disdig(c = *date) && c)
|
||||
date++;
|
||||
if(c == 0){
|
||||
*d = date;
|
||||
return -1;
|
||||
}
|
||||
n = 0;
|
||||
while(disdig(c = *date)){
|
||||
n = n * 10 + c - '0';
|
||||
date++;
|
||||
}
|
||||
*d = date;
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* parse a date and return the seconds since the epoch
|
||||
* return 0 for a failure
|
||||
*/
|
||||
ulong
|
||||
hdate2sec(char *date)
|
||||
{
|
||||
Tm tm;
|
||||
char buf[32];
|
||||
|
||||
/*
|
||||
* Weekday|Wday
|
||||
*/
|
||||
date = dateword(date, buf);
|
||||
tm.wday = dateindex(buf, wdayname, 7);
|
||||
if(tm.wday < 0)
|
||||
tm.wday = dateindex(buf, weekdayname, 7);
|
||||
if(tm.wday < 0)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* check for the two major formats
|
||||
*/
|
||||
date = dateword(date, buf);
|
||||
tm.mon = dateindex(buf, monname, 12);
|
||||
if(tm.mon >= 0){
|
||||
/*
|
||||
* MM
|
||||
*/
|
||||
tm.mday = datenum(&date);
|
||||
if(tm.mday < 1 || tm.mday > 31)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* HH:MM:SS
|
||||
*/
|
||||
tm.hour = datenum(&date);
|
||||
if(tm.hour < 0 || tm.hour >= 24)
|
||||
return 0;
|
||||
tm.min = datenum(&date);
|
||||
if(tm.min < 0 || tm.min >= 60)
|
||||
return 0;
|
||||
tm.sec = datenum(&date);
|
||||
if(tm.sec < 0 || tm.sec >= 60)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* YYYY
|
||||
*/
|
||||
tm.year = datenum(&date);
|
||||
if(tm.year < 70 || tm.year > 99 && tm.year < 1970)
|
||||
return 0;
|
||||
if(tm.year >= 1970)
|
||||
tm.year -= 1900;
|
||||
}else{
|
||||
/*
|
||||
* MM-Mon-(YY|YYYY)
|
||||
*/
|
||||
tm.mday = datenum(&date);
|
||||
if(tm.mday < 1 || tm.mday > 31)
|
||||
return 0;
|
||||
date = dateword(date, buf);
|
||||
tm.mon = dateindex(buf, monname, 12);
|
||||
if(tm.mon < 0 || tm.mon >= 12)
|
||||
return 0;
|
||||
tm.year = datenum(&date);
|
||||
if(tm.year < 70 || tm.year > 99 && tm.year < 1970)
|
||||
return 0;
|
||||
if(tm.year >= 1970)
|
||||
tm.year -= 1900;
|
||||
|
||||
/*
|
||||
* HH:MM:SS
|
||||
*/
|
||||
tm.hour = datenum(&date);
|
||||
if(tm.hour < 0 || tm.hour >= 24)
|
||||
return 0;
|
||||
tm.min = datenum(&date);
|
||||
if(tm.min < 0 || tm.min >= 60)
|
||||
return 0;
|
||||
tm.sec = datenum(&date);
|
||||
if(tm.sec < 0 || tm.sec >= 60)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* timezone
|
||||
*/
|
||||
dateword(date, buf);
|
||||
if(strncmp(buf, "gmt", 3) != 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
strcpy(tm.zone, "GMT");
|
||||
tm.tzoff = 0;
|
||||
return tm2sec(&tm);
|
||||
}
|
||||
|
||||
static int
|
||||
dateindex(char *d, char **tab, int n)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < n; i++)
|
||||
if(cistrcmp(d, tab[i]) == 0)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
123
src/libhttpd/escape.h
Normal file
123
src/libhttpd/escape.h
Normal file
|
@ -0,0 +1,123 @@
|
|||
|
||||
Htmlesc htmlesc[] =
|
||||
{
|
||||
{ "¡", 0x00a1, },
|
||||
{ "¢", 0x00a2, },
|
||||
{ "£", 0x00a3, },
|
||||
{ "¤", 0x00a4, },
|
||||
{ "¥", 0x00a5, },
|
||||
{ "¦", 0x00a6, },
|
||||
{ "§", 0x00a7, },
|
||||
{ "¨", 0x00a8, },
|
||||
{ "©", 0x00a9, },
|
||||
{ "ª", 0x00aa, },
|
||||
{ "«", 0x00ab, },
|
||||
{ "¬", 0x00ac, },
|
||||
{ "­", 0x00ad, },
|
||||
{ "®", 0x00ae, },
|
||||
{ "¯", 0x00af, },
|
||||
{ "°", 0x00b0, },
|
||||
{ "±", 0x00b1, },
|
||||
{ "²", 0x00b2, },
|
||||
{ "³", 0x00b3, },
|
||||
{ "´", 0x00b4, },
|
||||
{ "µ", 0x00b5, },
|
||||
{ "¶", 0x00b6, },
|
||||
{ "·", 0x00b7, },
|
||||
{ "¸", 0x00b8, },
|
||||
{ "¹", 0x00b9, },
|
||||
{ "º", 0x00ba, },
|
||||
{ "»", 0x00bb, },
|
||||
{ "¼", 0x00bc, },
|
||||
{ "½", 0x00bd, },
|
||||
{ "¾", 0x00be, },
|
||||
{ "¿", 0x00bf, },
|
||||
{ "À", 0x00c0, },
|
||||
{ "Á", 0x00c1, },
|
||||
{ "Â", 0x00c2, },
|
||||
{ "Ã", 0x00c3, },
|
||||
{ "Ä", 0x00c4, },
|
||||
{ "Å", 0x00c5, },
|
||||
{ "Æ", 0x00c6, },
|
||||
{ "Ç", 0x00c7, },
|
||||
{ "È", 0x00c8, },
|
||||
{ "É", 0x00c9, },
|
||||
{ "Ê", 0x00ca, },
|
||||
{ "Ë", 0x00cb, },
|
||||
{ "Ì", 0x00cc, },
|
||||
{ "Í", 0x00cd, },
|
||||
{ "Î", 0x00ce, },
|
||||
{ "Ï", 0x00cf, },
|
||||
{ "Ð", 0x00d0, },
|
||||
{ "Ñ", 0x00d1, },
|
||||
{ "Ò", 0x00d2, },
|
||||
{ "Ó", 0x00d3, },
|
||||
{ "Ô", 0x00d4, },
|
||||
{ "Õ", 0x00d5, },
|
||||
{ "Ö", 0x00d6, },
|
||||
{ "&215;", 0x00d7, },
|
||||
{ "Ø", 0x00d8, },
|
||||
{ "Ù", 0x00d9, },
|
||||
{ "Ú", 0x00da, },
|
||||
{ "Û", 0x00db, },
|
||||
{ "Ü", 0x00dc, },
|
||||
{ "Ý", 0x00dd, },
|
||||
{ "Þ", 0x00de, },
|
||||
{ "ß", 0x00df, },
|
||||
{ "à", 0x00e0, },
|
||||
{ "á", 0x00e1, },
|
||||
{ "â", 0x00e2, },
|
||||
{ "ã", 0x00e3, },
|
||||
{ "ä", 0x00e4, },
|
||||
{ "å", 0x00e5, },
|
||||
{ "æ", 0x00e6, },
|
||||
{ "ç", 0x00e7, },
|
||||
{ "è", 0x00e8, },
|
||||
{ "é", 0x00e9, },
|
||||
{ "ê", 0x00ea, },
|
||||
{ "ë", 0x00eb, },
|
||||
{ "ì", 0x00ec, },
|
||||
{ "í", 0x00ed, },
|
||||
{ "î", 0x00ee, },
|
||||
{ "ï", 0x00ef, },
|
||||
{ "ð", 0x00f0, },
|
||||
{ "ñ", 0x00f1, },
|
||||
{ "ò", 0x00f2, },
|
||||
{ "ó", 0x00f3, },
|
||||
{ "ô", 0x00f4, },
|
||||
{ "õ", 0x00f5, },
|
||||
{ "ö", 0x00f6, },
|
||||
{ "&247;", 0x00f7, },
|
||||
{ "ø", 0x00f8, },
|
||||
{ "ù", 0x00f9, },
|
||||
{ "ú", 0x00fa, },
|
||||
{ "û", 0x00fb, },
|
||||
{ "ü", 0x00fc, },
|
||||
{ "ý", 0x00fd, },
|
||||
{ "þ", 0x00fe, },
|
||||
{ "ÿ", 0x00ff, },
|
||||
|
||||
{ """, 0x0022, },
|
||||
{ "&", 0x0026, },
|
||||
{ "<", 0x003c, },
|
||||
{ ">", 0x003e, },
|
||||
|
||||
{ "CAP-DELTA", 0x0394, },
|
||||
{ "ALPHA", 0x03b1, },
|
||||
{ "BETA", 0x03b2, },
|
||||
{ "DELTA", 0x03b4, },
|
||||
{ "EPSILON", 0x03b5, },
|
||||
{ "THETA", 0x03b8, },
|
||||
{ "MU", 0x03bc, },
|
||||
{ "PI", 0x03c0, },
|
||||
{ "TAU", 0x03c4, },
|
||||
{ "CHI", 0x03c7, },
|
||||
|
||||
{ "<-", 0x2190, },
|
||||
{ "^", 0x2191, },
|
||||
{ "->", 0x2192, },
|
||||
{ "v", 0x2193, },
|
||||
{ "!=", 0x2260, },
|
||||
{ "<=", 0x2264, },
|
||||
{ nil, 0 },
|
||||
};
|
81
src/libhttpd/fail.c
Normal file
81
src/libhttpd/fail.c
Normal file
|
@ -0,0 +1,81 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <bin.h>
|
||||
#include <httpd.h>
|
||||
|
||||
typedef struct Error Error;
|
||||
|
||||
struct Error
|
||||
{
|
||||
char *num;
|
||||
char *concise;
|
||||
char *verbose;
|
||||
};
|
||||
|
||||
Error errormsg[] =
|
||||
{
|
||||
[HInternal] {"500 Internal Error", "Internal Error",
|
||||
"This server could not process your request due to an internal error."},
|
||||
[HTempFail] {"500 Internal Error", "Temporary Failure",
|
||||
"The object %s is currently inaccessible.<p>Please try again later."},
|
||||
[HUnimp] {"501 Not implemented", "Command not implemented",
|
||||
"This server does not implement the %s command."},
|
||||
[HUnkVers] {"501 Not Implemented", "Unknown http version",
|
||||
"This server does not know how to respond to http version %s."},
|
||||
[HBadCont] {"501 Not Implemented", "Impossible format",
|
||||
"This server cannot produce %s in any of the formats your client accepts."},
|
||||
[HBadReq] {"400 Bad Request", "Strange Request",
|
||||
"Your client sent a query that this server could not understand."},
|
||||
[HSyntax] {"400 Bad Request", "Garbled Syntax",
|
||||
"Your client sent a query with incoherent syntax."},
|
||||
[HBadSearch] {"400 Bad Request", "Inapplicable Search",
|
||||
"Your client sent a search that cannot be applied to %s."},
|
||||
[HNotFound] {"404 Not Found", "Object not found",
|
||||
"The object %s does not exist on this server."},
|
||||
[HNoSearch] {"403 Forbidden", "Search not supported",
|
||||
"The object %s does not support the search command."},
|
||||
[HNoData] {"403 Forbidden", "No data supplied",
|
||||
"Search or forms data must be supplied to %s."},
|
||||
[HExpectFail] {"403 Expectation Failed", "Expectation Failed",
|
||||
"This server does not support some of your request's expectations."},
|
||||
[HUnauth] {"403 Forbidden", "Forbidden",
|
||||
"You are not allowed to see the object %s."},
|
||||
[HOK] {"200 OK", "everything is fine"},
|
||||
};
|
||||
|
||||
/*
|
||||
* write a failure message to the net and exit
|
||||
*/
|
||||
int
|
||||
hfail(HConnect *c, int reason, ...)
|
||||
{
|
||||
Hio *hout;
|
||||
char makeup[HBufSize];
|
||||
va_list arg;
|
||||
int n;
|
||||
|
||||
hout = &c->hout;
|
||||
va_start(arg, reason);
|
||||
vseprint(makeup, makeup+HBufSize, errormsg[reason].verbose, arg);
|
||||
va_end(arg);
|
||||
n = snprint(c->xferbuf, HBufSize, "<head><title>%s</title></head>\n<body><h1>%s</h1>\n%s</body>\n",
|
||||
errormsg[reason].concise, errormsg[reason].concise, makeup);
|
||||
|
||||
hprint(hout, "%s %s\r\n", hversion, errormsg[reason].num);
|
||||
hprint(hout, "Date: %D\r\n", time(nil));
|
||||
hprint(hout, "Server: Plan9\r\n");
|
||||
hprint(hout, "Content-Type: text/html\r\n");
|
||||
hprint(hout, "Content-Length: %d\r\n", n);
|
||||
if(c->head.closeit)
|
||||
hprint(hout, "Connection: close\r\n");
|
||||
else if(!http11(c))
|
||||
hprint(hout, "Connection: Keep-Alive\r\n");
|
||||
hprint(hout, "\r\n");
|
||||
|
||||
if(c->req.meth == nil || strcmp(c->req.meth, "HEAD") != 0)
|
||||
hwrite(hout, c->xferbuf, n);
|
||||
|
||||
if(c->replog)
|
||||
c->replog(c, "Reply: %s\nReason: %s\n", errormsg[reason].num, errormsg[reason].concise);
|
||||
return hflush(hout);
|
||||
}
|
40
src/libhttpd/gethead.c
Normal file
40
src/libhttpd/gethead.c
Normal file
|
@ -0,0 +1,40 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <bin.h>
|
||||
#include <httpd.h>
|
||||
|
||||
/*
|
||||
* read in some header lines, either one or all of them.
|
||||
* copy results into header log buffer.
|
||||
*/
|
||||
int
|
||||
hgethead(HConnect *c, int many)
|
||||
{
|
||||
Hio *hin;
|
||||
char *s, *p, *pp;
|
||||
int n;
|
||||
|
||||
hin = &c->hin;
|
||||
for(;;){
|
||||
s = (char*)hin->pos;
|
||||
pp = s;
|
||||
while(p = memchr(pp, '\n', (char*)hin->stop - pp)){
|
||||
if(!many || p == pp || p == pp + 1 && *pp == '\r'){
|
||||
pp = p + 1;
|
||||
break;
|
||||
}
|
||||
pp = p + 1;
|
||||
}
|
||||
hin->pos = (uchar*)pp;
|
||||
n = pp - s;
|
||||
if(c->hstop + n > &c->header[HBufSize])
|
||||
return 0;
|
||||
memmove(c->hstop, s, n);
|
||||
c->hstop += n;
|
||||
*c->hstop = '\0';
|
||||
if(p != nil)
|
||||
return 1;
|
||||
if(hreadbuf(hin, hin->pos) == nil || hin->state == Hend)
|
||||
return 0;
|
||||
}
|
||||
}
|
473
src/libhttpd/hio.c
Normal file
473
src/libhttpd/hio.c
Normal file
|
@ -0,0 +1,473 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <httpd.h>
|
||||
|
||||
static char hstates[] = "nrewE";
|
||||
static char hxfers[] = " x";
|
||||
|
||||
int
|
||||
hinit(Hio *h, int fd, int mode)
|
||||
{
|
||||
if(fd == -1 || mode != Hread && mode != Hwrite)
|
||||
return -1;
|
||||
h->hh = nil;
|
||||
h->fd = fd;
|
||||
h->seek = 0;
|
||||
h->state = mode;
|
||||
h->start = h->buf + 16; /* leave space for chunk length */
|
||||
h->stop = h->pos = h->start;
|
||||
if(mode == Hread){
|
||||
h->bodylen = ~0UL;
|
||||
*h->pos = '\0';
|
||||
}else
|
||||
h->stop = h->start + Hsize;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
hiserror(Hio *h)
|
||||
{
|
||||
return h->state == Herr;
|
||||
}
|
||||
|
||||
int
|
||||
hgetc(Hio *h)
|
||||
{
|
||||
uchar *p;
|
||||
|
||||
p = h->pos;
|
||||
if(p < h->stop){
|
||||
h->pos = p + 1;
|
||||
return *p;
|
||||
}
|
||||
p -= UTFmax;
|
||||
if(p < h->start)
|
||||
p = h->start;
|
||||
if(!hreadbuf(h, p) || h->pos == h->stop)
|
||||
return -1;
|
||||
return *h->pos++;
|
||||
}
|
||||
|
||||
int
|
||||
hungetc(Hio *h)
|
||||
{
|
||||
if(h->state == Hend)
|
||||
h->state = Hread;
|
||||
else if(h->state == Hread)
|
||||
h->pos--;
|
||||
if(h->pos < h->start || h->state != Hread){
|
||||
h->state = Herr;
|
||||
h->pos = h->stop;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* fill the buffer, saving contents from vsave onwards.
|
||||
* nothing is saved if vsave is nil.
|
||||
* returns the beginning of the buffer.
|
||||
*
|
||||
* understands message body sizes and chunked transfer encoding
|
||||
*/
|
||||
void *
|
||||
hreadbuf(Hio *h, void *vsave)
|
||||
{
|
||||
Hio *hh;
|
||||
uchar *save;
|
||||
int c, in, cpy, dpos;
|
||||
|
||||
save = vsave;
|
||||
if(save && (save < h->start || save > h->stop)
|
||||
|| h->state != Hread && h->state != Hend){
|
||||
h->state = Herr;
|
||||
h->pos = h->stop;
|
||||
return nil;
|
||||
}
|
||||
|
||||
dpos = 0;
|
||||
if(save && h->pos > save)
|
||||
dpos = h->pos - save;
|
||||
cpy = 0;
|
||||
if(save){
|
||||
cpy = h->stop - save;
|
||||
memmove(h->start, save, cpy);
|
||||
}
|
||||
h->seek += h->stop - h->start - cpy;
|
||||
h->pos = h->start + dpos;
|
||||
|
||||
in = Hsize - cpy;
|
||||
if(h->state == Hend)
|
||||
in = 0;
|
||||
else if(in > h->bodylen)
|
||||
in = h->bodylen;
|
||||
|
||||
/*
|
||||
* for chunked encoding, fill buffer,
|
||||
* then read in new chunk length and wipe out that line
|
||||
*/
|
||||
hh = h->hh;
|
||||
if(hh != nil){
|
||||
if(!in && h->xferenc && h->state != Hend){
|
||||
if(h->xferenc == 2){
|
||||
c = hgetc(hh);
|
||||
if(c == '\r')
|
||||
c = hgetc(hh);
|
||||
if(c != '\n'){
|
||||
h->pos = h->stop;
|
||||
h->state = Herr;
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
h->xferenc = 2;
|
||||
in = 0;
|
||||
while((c = hgetc(hh)) != '\n'){
|
||||
if(c >= '0' && c <= '9')
|
||||
c -= '0';
|
||||
else if(c >= 'a' && c <= 'f')
|
||||
c -= 'a' - 10;
|
||||
else if(c >= 'A' && c <= 'F')
|
||||
c -= 'A' - 10;
|
||||
else
|
||||
break;
|
||||
in = in * 16 + c;
|
||||
}
|
||||
while(c != '\n'){
|
||||
if(c < 0){
|
||||
h->pos = h->stop;
|
||||
h->state = Herr;
|
||||
return nil;
|
||||
}
|
||||
c = hgetc(hh);
|
||||
}
|
||||
h->bodylen = in;
|
||||
|
||||
in = Hsize - cpy;
|
||||
if(in > h->bodylen)
|
||||
in = h->bodylen;
|
||||
}
|
||||
if(in){
|
||||
while(hh->pos + in > hh->stop){
|
||||
if(hreadbuf(hh, hh->pos) == nil){
|
||||
h->pos = h->stop;
|
||||
h->state = Herr;
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
memmove(h->start + cpy, hh->pos, in);
|
||||
hh->pos += in;
|
||||
}
|
||||
}else if(in && (in = read(h->fd, h->start + cpy, in)) < 0){
|
||||
h->state = Herr;
|
||||
h->pos = h->stop;
|
||||
return nil;
|
||||
}
|
||||
if(in == 0)
|
||||
h->state = Hend;
|
||||
|
||||
h->bodylen -= in;
|
||||
|
||||
h->stop = h->start + cpy + in;
|
||||
*h->stop = '\0';
|
||||
if(h->pos == h->stop)
|
||||
return nil;
|
||||
return h->start;
|
||||
}
|
||||
|
||||
int
|
||||
hbuflen(Hio *h, void *p)
|
||||
{
|
||||
return h->stop - (uchar*)p;
|
||||
}
|
||||
|
||||
/*
|
||||
* prepare to receive a message body
|
||||
* len is the content length (~0 => unspecified)
|
||||
* te is the transfer encoding
|
||||
* returns < 0 if setup failed
|
||||
*/
|
||||
Hio*
|
||||
hbodypush(Hio *hh, ulong len, HFields *te)
|
||||
{
|
||||
Hio *h;
|
||||
int xe;
|
||||
|
||||
if(hh->state != Hread)
|
||||
return nil;
|
||||
xe = 0;
|
||||
if(te != nil){
|
||||
if(te->params != nil || te->next != nil)
|
||||
return nil;
|
||||
if(cistrcmp(te->s, "chunked") == 0){
|
||||
xe = 1;
|
||||
len = 0;
|
||||
}else if(cistrcmp(te->s, "identity") == 0){
|
||||
;
|
||||
}else
|
||||
return nil;
|
||||
}
|
||||
|
||||
h = malloc(sizeof *h);
|
||||
if(h == nil)
|
||||
return nil;
|
||||
|
||||
h->hh = hh;
|
||||
h->fd = -1;
|
||||
h->seek = 0;
|
||||
h->state = Hread;
|
||||
h->xferenc = xe;
|
||||
h->start = h->buf + 16; /* leave space for chunk length */
|
||||
h->stop = h->pos = h->start;
|
||||
*h->pos = '\0';
|
||||
h->bodylen = len;
|
||||
return h;
|
||||
}
|
||||
|
||||
/*
|
||||
* dump the state of the io buffer into a string
|
||||
*/
|
||||
char *
|
||||
hunload(Hio *h)
|
||||
{
|
||||
uchar *p, *t, *stop, *buf;
|
||||
int ne, n, c;
|
||||
|
||||
stop = h->stop;
|
||||
ne = 0;
|
||||
for(p = h->pos; p < stop; p++){
|
||||
c = *p;
|
||||
if(c == 0x80)
|
||||
ne++;
|
||||
}
|
||||
p = h->pos;
|
||||
|
||||
n = (stop - p) + ne + 3;
|
||||
buf = mallocz(n, 1);
|
||||
if(buf == nil)
|
||||
return nil;
|
||||
buf[0] = hstates[h->state];
|
||||
buf[1] = hxfers[h->xferenc];
|
||||
|
||||
t = &buf[2];
|
||||
for(; p < stop; p++){
|
||||
c = *p;
|
||||
if(c == 0 || c == 0x80){
|
||||
*t++ = 0x80;
|
||||
if(c == 0x80)
|
||||
*t++ = 0x80;
|
||||
}else
|
||||
*t++ = c;
|
||||
}
|
||||
*t++ = '\0';
|
||||
if(t != buf + n)
|
||||
return nil;
|
||||
return (char*)buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* read the io buffer state from a string
|
||||
*/
|
||||
int
|
||||
hload(Hio *h, char *buf)
|
||||
{
|
||||
uchar *p, *t, *stop;
|
||||
char *s;
|
||||
int c;
|
||||
|
||||
s = strchr(hstates, buf[0]);
|
||||
if(s == nil)
|
||||
return 0;
|
||||
h->state = s - hstates;
|
||||
|
||||
s = strchr(hxfers, buf[1]);
|
||||
if(s == nil)
|
||||
return 0;
|
||||
h->xferenc = s - hxfers;
|
||||
|
||||
t = h->start;
|
||||
stop = t + Hsize;
|
||||
for(p = (uchar*)&buf[2]; c = *p; p++){
|
||||
if(c == 0x80){
|
||||
if(p[1] != 0x80)
|
||||
c = 0;
|
||||
else
|
||||
p++;
|
||||
}
|
||||
*t++ = c;
|
||||
if(t >= stop)
|
||||
return 0;
|
||||
}
|
||||
*t = '\0';
|
||||
h->pos = h->start;
|
||||
h->stop = t;
|
||||
h->seek = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
hclose(Hio *h)
|
||||
{
|
||||
if(h->fd >= 0){
|
||||
if(h->state == Hwrite)
|
||||
hxferenc(h, 0);
|
||||
close(h->fd);
|
||||
}
|
||||
h->stop = h->pos = nil;
|
||||
h->fd = -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* flush the buffer and possibly change encoding modes
|
||||
*/
|
||||
int
|
||||
hxferenc(Hio *h, int on)
|
||||
{
|
||||
if(h->xferenc && !on && h->pos != h->start)
|
||||
hflush(h);
|
||||
if(hflush(h) < 0)
|
||||
return -1;
|
||||
h->xferenc = !!on;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
hputc(Hio *h, int c)
|
||||
{
|
||||
uchar *p;
|
||||
|
||||
p = h->pos;
|
||||
if(p < h->stop){
|
||||
h->pos = p + 1;
|
||||
return *p = c;
|
||||
}
|
||||
if(hflush(h) < 0)
|
||||
return -1;
|
||||
return *h->pos++ = c;
|
||||
}
|
||||
|
||||
static int
|
||||
fmthflush(Fmt *f)
|
||||
{
|
||||
Hio *h;
|
||||
|
||||
h = f->farg;
|
||||
h->pos = f->to;
|
||||
if(hflush(h) < 0)
|
||||
return 0;
|
||||
f->stop = h->stop;
|
||||
f->to = h->pos;
|
||||
f->start = h->pos;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
hvprint(Hio *h, char *fmt, va_list args)
|
||||
{
|
||||
int n;
|
||||
Fmt f;
|
||||
|
||||
f.runes = 0;
|
||||
f.stop = h->stop;
|
||||
f.to = h->pos;
|
||||
f.start = h->pos;
|
||||
f.flush = fmthflush;
|
||||
f.farg = h;
|
||||
f.nfmt = 0;
|
||||
f.args = args;
|
||||
n = dofmt(&f, fmt);
|
||||
h->pos = f.to;
|
||||
return n;
|
||||
}
|
||||
|
||||
int
|
||||
hprint(Hio *h, char *fmt, ...)
|
||||
{
|
||||
int n;
|
||||
va_list arg;
|
||||
|
||||
va_start(arg, fmt);
|
||||
n = hvprint(h, fmt, arg);
|
||||
va_end(arg);
|
||||
return n;
|
||||
}
|
||||
|
||||
int
|
||||
hflush(Hio *h)
|
||||
{
|
||||
uchar *s;
|
||||
int w;
|
||||
|
||||
if(h->state != Hwrite){
|
||||
h->state = Herr;
|
||||
h->stop = h->pos;
|
||||
return -1;
|
||||
}
|
||||
s = h->start;
|
||||
w = h->pos - s;
|
||||
if(h->xferenc){
|
||||
*--s = '\n';
|
||||
*--s = '\r';
|
||||
do{
|
||||
*--s = "0123456789abcdef"[w & 0xf];
|
||||
w >>= 4;
|
||||
}while(w);
|
||||
h->pos[0] = '\r';
|
||||
h->pos[1] = '\n';
|
||||
w = &h->pos[2] - s;
|
||||
}
|
||||
if(write(h->fd, s, w) != w){
|
||||
h->state = Herr;
|
||||
h->stop = h->pos;
|
||||
return -1;
|
||||
}
|
||||
h->seek += w;
|
||||
h->pos = h->start;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
hwrite(Hio *h, void *vbuf, int len)
|
||||
{
|
||||
uchar *pos, *buf;
|
||||
int n, m;
|
||||
|
||||
buf = vbuf;
|
||||
n = len;
|
||||
if(n < 0 || h->state != Hwrite){
|
||||
h->state = Herr;
|
||||
h->stop = h->pos;
|
||||
return -1;
|
||||
}
|
||||
pos = h->pos;
|
||||
if(pos + n >= h->stop){
|
||||
m = pos - h->start;
|
||||
if(m){
|
||||
m = Hsize - m;
|
||||
if(m){
|
||||
memmove(pos, buf, m);
|
||||
buf += m;
|
||||
n -= m;
|
||||
}
|
||||
if(write(h->fd, h->start, Hsize) != Hsize){
|
||||
h->state = Herr;
|
||||
h->stop = h->pos;
|
||||
return -1;
|
||||
}
|
||||
h->seek += Hsize;
|
||||
}
|
||||
m = n % Hsize;
|
||||
n -= m;
|
||||
if(n != 0 && write(h->fd, buf, n) != n){
|
||||
h->state = Herr;
|
||||
h->stop = h->pos;
|
||||
return -1;
|
||||
}
|
||||
h->seek += n;
|
||||
buf += n;
|
||||
pos = h->pos = h->start;
|
||||
n = m;
|
||||
}
|
||||
memmove(pos, buf, n);
|
||||
h->pos = pos + n;
|
||||
return len;
|
||||
}
|
30
src/libhttpd/httpfmt.c
Normal file
30
src/libhttpd/httpfmt.c
Normal file
|
@ -0,0 +1,30 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <bin.h>
|
||||
#include <httpd.h>
|
||||
|
||||
int
|
||||
httpfmt(Fmt *f)
|
||||
{
|
||||
char buf[HMaxWord*2];
|
||||
Rune r;
|
||||
char *t, *s;
|
||||
Htmlesc *l;
|
||||
|
||||
s = va_arg(f->args, char*);
|
||||
for(t = buf; t < buf + sizeof(buf) - 8; ){
|
||||
s += chartorune(&r, s);
|
||||
if(r == 0)
|
||||
break;
|
||||
for(l = htmlesc; l->name != nil; l++)
|
||||
if(l->value == r)
|
||||
break;
|
||||
if(l->name != nil){
|
||||
strcpy(t, l->name);
|
||||
t += strlen(t);
|
||||
}else
|
||||
*t++ = r;
|
||||
}
|
||||
*t = 0;
|
||||
return fmtstrcpy(f, buf);
|
||||
}
|
49
src/libhttpd/httpunesc.c
Normal file
49
src/libhttpd/httpunesc.c
Normal file
|
@ -0,0 +1,49 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <bin.h>
|
||||
#include <httpd.h>
|
||||
|
||||
/*
|
||||
* go from http with latin1 escapes to utf,
|
||||
* we assume that anything >= Runeself is already in utf
|
||||
*/
|
||||
char *
|
||||
httpunesc(HConnect *cc, char *s)
|
||||
{
|
||||
char *t, *v;
|
||||
int c;
|
||||
Htmlesc *e;
|
||||
|
||||
v = halloc(cc, UTFmax*strlen(s) + 1);
|
||||
for(t = v; c = *s;){
|
||||
if(c == '&'){
|
||||
if(s[1] == '#' && s[2] && s[3] && s[4] && s[5] == ';'){
|
||||
c = atoi(s+2);
|
||||
if(c < Runeself){
|
||||
*t++ = c;
|
||||
s += 6;
|
||||
continue;
|
||||
}
|
||||
if(c < 256 && c >= 161){
|
||||
e = &htmlesc[c-161];
|
||||
t += runetochar(t, &e->value);
|
||||
s += 6;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
for(e = htmlesc; e->name != nil; e++)
|
||||
if(strncmp(e->name, s, strlen(e->name)) == 0)
|
||||
break;
|
||||
if(e->name != nil){
|
||||
t += runetochar(t, &e->value);
|
||||
s += strlen(e->name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
*t++ = c;
|
||||
s++;
|
||||
}
|
||||
*t = 0;
|
||||
return v;
|
||||
}
|
19
src/libhttpd/lower.c
Normal file
19
src/libhttpd/lower.c
Normal file
|
@ -0,0 +1,19 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <bin.h>
|
||||
#include <httpd.h>
|
||||
|
||||
char*
|
||||
hlower(char *p)
|
||||
{
|
||||
char c;
|
||||
char *x;
|
||||
|
||||
if(p == nil)
|
||||
return p;
|
||||
|
||||
for(x = p; c = *x; x++)
|
||||
if(c >= 'A' && c <= 'Z')
|
||||
*x -= 'A' - 'a';
|
||||
return p;
|
||||
}
|
30
src/libhttpd/mkfile
Normal file
30
src/libhttpd/mkfile
Normal file
|
@ -0,0 +1,30 @@
|
|||
PLAN9=../..
|
||||
<$PLAN9/src/mkhdr
|
||||
|
||||
LIB=libhttpd.a
|
||||
|
||||
OFILES=\
|
||||
alloc.$O\
|
||||
checkcontent.$O\
|
||||
date.$O\
|
||||
fail.$O\
|
||||
gethead.$O\
|
||||
hio.$O\
|
||||
httpfmt.$O\
|
||||
httpunesc.$O\
|
||||
lower.$O\
|
||||
okheaders.$O\
|
||||
parse.$O\
|
||||
parsereq.$O\
|
||||
query.$O\
|
||||
redirected.$O\
|
||||
unallowed.$O\
|
||||
urlfmt.$O\
|
||||
urlunesc.$O\
|
||||
|
||||
HFILES=\
|
||||
$PLAN9/include/httpd.h\
|
||||
escape.h\
|
||||
|
||||
<$PLAN9/src/mksyslib
|
||||
|
22
src/libhttpd/okheaders.c
Normal file
22
src/libhttpd/okheaders.c
Normal file
|
@ -0,0 +1,22 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <bin.h>
|
||||
#include <httpd.h>
|
||||
|
||||
/*
|
||||
* write initial part of successful header
|
||||
*/
|
||||
void
|
||||
hokheaders(HConnect *c)
|
||||
{
|
||||
Hio *hout;
|
||||
|
||||
hout = &c->hout;
|
||||
hprint(hout, "%s 200 OK\r\n", hversion);
|
||||
hprint(hout, "Server: Plan9\r\n");
|
||||
hprint(hout, "Date: %D\r\n", time(nil));
|
||||
if(c->head.closeit)
|
||||
hprint(hout, "Connection: close\r\n");
|
||||
else if(!http11(c))
|
||||
hprint(hout, "Connection: Keep-Alive\r\n");
|
||||
}
|
1057
src/libhttpd/parse.c
Normal file
1057
src/libhttpd/parse.c
Normal file
File diff suppressed because it is too large
Load diff
296
src/libhttpd/parsereq.c
Normal file
296
src/libhttpd/parsereq.c
Normal file
|
@ -0,0 +1,296 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <bin.h>
|
||||
#include <httpd.h>
|
||||
|
||||
typedef struct Strings Strings;
|
||||
|
||||
struct Strings
|
||||
{
|
||||
char *s1;
|
||||
char *s2;
|
||||
};
|
||||
|
||||
static char* abspath(HConnect *cc, char *origpath, char *curdir);
|
||||
static int getc(HConnect*);
|
||||
static char* getword(HConnect*);
|
||||
static Strings parseuri(HConnect *c, char*);
|
||||
static Strings stripmagic(char*);
|
||||
static Strings stripsearch(char*);
|
||||
|
||||
/*
|
||||
* parse the next request line
|
||||
* returns:
|
||||
* 1 ok
|
||||
* 0 eof
|
||||
* -1 error
|
||||
*/
|
||||
int
|
||||
hparsereq(HConnect *c, int timeout)
|
||||
{
|
||||
Strings ss;
|
||||
char *vs, *v, *search, *uri, *origuri, *extra;
|
||||
|
||||
if(c->bin != nil){
|
||||
hfail(c, HInternal);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* serve requests until a magic request.
|
||||
* later requests have to come quickly.
|
||||
* only works for http/1.1 or later.
|
||||
*/
|
||||
alarm(timeout);
|
||||
if(!hgethead(c, 0))
|
||||
return 0;
|
||||
alarm(0);
|
||||
c->reqtime = time(nil);
|
||||
c->req.meth = getword(c);
|
||||
if(c->req.meth == nil){
|
||||
hfail(c, HSyntax);
|
||||
return -1;
|
||||
}
|
||||
uri = getword(c);
|
||||
if(uri == nil || strlen(uri) == 0){
|
||||
hfail(c, HSyntax);
|
||||
return -1;
|
||||
}
|
||||
v = getword(c);
|
||||
if(v == nil){
|
||||
if(strcmp(c->req.meth, "GET") != 0){
|
||||
hfail(c, HUnimp, c->req.meth);
|
||||
return -1;
|
||||
}
|
||||
c->req.vermaj = 0;
|
||||
c->req.vermin = 9;
|
||||
}else{
|
||||
vs = v;
|
||||
if(strncmp(vs, "HTTP/", 5) != 0){
|
||||
hfail(c, HUnkVers, vs);
|
||||
return -1;
|
||||
}
|
||||
vs += 5;
|
||||
c->req.vermaj = strtoul(vs, &vs, 10);
|
||||
if(*vs != '.' || c->req.vermaj != 1){
|
||||
hfail(c, HUnkVers, vs);
|
||||
return -1;
|
||||
}
|
||||
vs++;
|
||||
c->req.vermin = strtoul(vs, &vs, 10);
|
||||
if(*vs != '\0'){
|
||||
hfail(c, HUnkVers, vs);
|
||||
return -1;
|
||||
}
|
||||
|
||||
extra = getword(c);
|
||||
if(extra != nil){
|
||||
hfail(c, HSyntax);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* the fragment is not supposed to be sent
|
||||
* strip it 'cause some clients send it
|
||||
*/
|
||||
origuri = uri;
|
||||
uri = strchr(origuri, '#');
|
||||
if(uri != nil)
|
||||
*uri = 0;
|
||||
|
||||
/*
|
||||
* http/1.1 requires the server to accept absolute
|
||||
* or relative uri's. convert to relative with an absolute path
|
||||
*/
|
||||
if(http11(c)){
|
||||
ss = parseuri(c, origuri);
|
||||
uri = ss.s1;
|
||||
c->req.urihost = ss.s2;
|
||||
if(uri == nil){
|
||||
hfail(c, HBadReq, uri);
|
||||
return -1;
|
||||
}
|
||||
origuri = uri;
|
||||
}
|
||||
|
||||
/*
|
||||
* munge uri for search, protection, and magic
|
||||
*/
|
||||
ss = stripsearch(origuri);
|
||||
origuri = ss.s1;
|
||||
search = ss.s2;
|
||||
uri = hurlunesc(c, origuri);
|
||||
uri = abspath(c, uri, "/");
|
||||
if(uri == nil || uri[0] == '\0'){
|
||||
hfail(c, HNotFound, "no object specified");
|
||||
return -1;
|
||||
}
|
||||
|
||||
c->req.uri = uri;
|
||||
c->req.search = search;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static Strings
|
||||
parseuri(HConnect *c, char *uri)
|
||||
{
|
||||
Strings ss;
|
||||
char *urihost, *p;
|
||||
|
||||
urihost = nil;
|
||||
if(uri[0] != '/'){
|
||||
if(cistrncmp(uri, "http://", 7) != 0){
|
||||
ss.s1 = nil;
|
||||
ss.s2 = nil;
|
||||
return ss;
|
||||
}
|
||||
uri += 5; /* skip http: */
|
||||
}
|
||||
|
||||
/*
|
||||
* anything starting with // is a host name or number
|
||||
* hostnames constists of letters, digits, - and .
|
||||
* for now, just ignore any port given
|
||||
*/
|
||||
if(uri[0] == '/' && uri[1] == '/'){
|
||||
urihost = uri + 2;
|
||||
p = strchr(urihost, '/');
|
||||
if(p == nil)
|
||||
uri = hstrdup(c, "/");
|
||||
else{
|
||||
uri = hstrdup(c, p);
|
||||
*p = '\0';
|
||||
}
|
||||
p = strchr(urihost, ':');
|
||||
if(p != nil)
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
if(uri[0] != '/' || uri[1] == '/'){
|
||||
ss.s1 = nil;
|
||||
ss.s2 = nil;
|
||||
return ss;
|
||||
}
|
||||
|
||||
ss.s1 = uri;
|
||||
ss.s2 = hlower(urihost);
|
||||
return ss;
|
||||
}
|
||||
static Strings
|
||||
stripsearch(char *uri)
|
||||
{
|
||||
Strings ss;
|
||||
char *search;
|
||||
|
||||
search = strchr(uri, '?');
|
||||
if(search != nil)
|
||||
*search++ = 0;
|
||||
ss.s1 = uri;
|
||||
ss.s2 = search;
|
||||
return ss;
|
||||
}
|
||||
|
||||
/*
|
||||
* to circumscribe the accessible files we have to eliminate ..'s
|
||||
* and resolve all names from the root.
|
||||
*/
|
||||
static char*
|
||||
abspath(HConnect *cc, char *origpath, char *curdir)
|
||||
{
|
||||
char *p, *sp, *path, *work, *rpath;
|
||||
int len, n, c;
|
||||
|
||||
if(curdir == nil)
|
||||
curdir = "/";
|
||||
if(origpath == nil)
|
||||
origpath = "";
|
||||
work = hstrdup(cc, origpath);
|
||||
path = work;
|
||||
|
||||
/*
|
||||
* remove any really special characters
|
||||
*/
|
||||
for(sp = "`;| "; *sp; sp++){
|
||||
p = strchr(path, *sp);
|
||||
if(p)
|
||||
*p = 0;
|
||||
}
|
||||
|
||||
len = strlen(curdir) + strlen(path) + 2 + UTFmax;
|
||||
if(len < 10)
|
||||
len = 10;
|
||||
rpath = halloc(cc, len);
|
||||
if(*path == '/')
|
||||
rpath[0] = 0;
|
||||
else
|
||||
strcpy(rpath, curdir);
|
||||
n = strlen(rpath);
|
||||
|
||||
while(path){
|
||||
p = strchr(path, '/');
|
||||
if(p)
|
||||
*p++ = 0;
|
||||
if(strcmp(path, "..") == 0){
|
||||
while(n > 1){
|
||||
n--;
|
||||
c = rpath[n];
|
||||
rpath[n] = 0;
|
||||
if(c == '/')
|
||||
break;
|
||||
}
|
||||
}else if(strcmp(path, ".") == 0){
|
||||
;
|
||||
}else if(n == 1)
|
||||
n += snprint(rpath+n, len-n, "%s", path);
|
||||
else
|
||||
n += snprint(rpath+n, len-n, "/%s", path);
|
||||
path = p;
|
||||
}
|
||||
|
||||
if(strncmp(rpath, "/bin/", 5) == 0)
|
||||
strcpy(rpath, "/");
|
||||
return rpath;
|
||||
}
|
||||
|
||||
static char*
|
||||
getword(HConnect *c)
|
||||
{
|
||||
char *buf;
|
||||
int ch, n;
|
||||
|
||||
while((ch = getc(c)) == ' ' || ch == '\t' || ch == '\r')
|
||||
;
|
||||
if(ch == '\n')
|
||||
return nil;
|
||||
n = 0;
|
||||
buf = halloc(c, 1);
|
||||
for(;;){
|
||||
switch(ch){
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\r':
|
||||
case '\n':
|
||||
buf[n] = '\0';
|
||||
return hstrdup(c, buf);
|
||||
}
|
||||
|
||||
if(n < HMaxWord-1){
|
||||
buf = bingrow(&c->bin, buf, n, n + 1, 0);
|
||||
if(buf == nil)
|
||||
return nil;
|
||||
buf[n++] = ch;
|
||||
}
|
||||
ch = getc(c);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
static int
|
||||
getc(HConnect *c)
|
||||
{
|
||||
if(c->hpos < c->hstop)
|
||||
return *c->hpos++;
|
||||
return '\n';
|
||||
}
|
39
src/libhttpd/query.c
Normal file
39
src/libhttpd/query.c
Normal file
|
@ -0,0 +1,39 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <httpd.h>
|
||||
|
||||
/*
|
||||
* parse a search string of the form
|
||||
* tag=val&tag1=val1...
|
||||
*/
|
||||
HSPairs*
|
||||
hparsequery(HConnect *c, char *search)
|
||||
{
|
||||
HSPairs *q;
|
||||
char *tag, *val, *s;
|
||||
|
||||
while((s = strchr(search, '?')) != nil)
|
||||
search = s + 1;
|
||||
s = search;
|
||||
while((s = strchr(s, '+')) != nil)
|
||||
*s++ = ' ';
|
||||
q = nil;
|
||||
while(*search){
|
||||
tag = search;
|
||||
while(*search != '='){
|
||||
if(*search == '\0')
|
||||
return q;
|
||||
search++;
|
||||
}
|
||||
*search++ = 0;
|
||||
val = search;
|
||||
while(*search != '&'){
|
||||
if(*search == '\0')
|
||||
return hmkspairs(c, hurlunesc(c, tag), hurlunesc(c, val), q);
|
||||
search++;
|
||||
}
|
||||
*search++ = '\0';
|
||||
q = hmkspairs(c, hurlunesc(c, tag), hurlunesc(c, val), q);
|
||||
}
|
||||
return q;
|
||||
}
|
64
src/libhttpd/redirected.c
Normal file
64
src/libhttpd/redirected.c
Normal file
|
@ -0,0 +1,64 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <bin.h>
|
||||
#include <httpd.h>
|
||||
|
||||
int
|
||||
hredirected(HConnect *c, char *how, char *uri)
|
||||
{
|
||||
Hio *hout;
|
||||
char *s, *ss, *host;
|
||||
int n;
|
||||
|
||||
host = c->head.host;
|
||||
if(strchr(uri, ':')){
|
||||
host = "";
|
||||
}else if(uri[0] != '/'){
|
||||
s = strrchr(c->req.uri, '/');
|
||||
if(s != nil)
|
||||
*s = '\0';
|
||||
ss = halloc(c, strlen(c->req.uri) + strlen(uri) + 2 + UTFmax);
|
||||
sprint(ss, "%s/%s", c->req.uri, uri);
|
||||
uri = ss;
|
||||
if(s != nil)
|
||||
*s = '/';
|
||||
}
|
||||
|
||||
n = snprint(c->xferbuf, HBufSize,
|
||||
"<head><title>Redirection</title></head>\r\n"
|
||||
"<body><h1>Redirection</h1>\r\n"
|
||||
"Your selection can be found <a href=\"%U\"> here</a>.<p></body>\r\n", uri);
|
||||
|
||||
hout = &c->hout;
|
||||
hprint(hout, "%s %s\r\n", hversion, how);
|
||||
hprint(hout, "Date: %D\r\n", time(nil));
|
||||
hprint(hout, "Server: Plan9\r\n");
|
||||
hprint(hout, "Content-type: text/html\r\n");
|
||||
hprint(hout, "Content-Length: %d\r\n", n);
|
||||
if(host == nil || host[0] == 0)
|
||||
hprint(hout, "Location: %U\r\n", uri);
|
||||
else
|
||||
hprint(hout, "Location: http://%U%U\r\n", host, uri);
|
||||
if(c->head.closeit)
|
||||
hprint(hout, "Connection: close\r\n");
|
||||
else if(!http11(c))
|
||||
hprint(hout, "Connection: Keep-Alive\r\n");
|
||||
hprint(hout, "\r\n");
|
||||
|
||||
if(strcmp(c->req.meth, "HEAD") != 0)
|
||||
hwrite(hout, c->xferbuf, n);
|
||||
|
||||
if(c->replog){
|
||||
if(host == nil || host[0] == 0)
|
||||
c->replog(c, "Reply: %s\nRedirect: %U\n", how, uri);
|
||||
else
|
||||
c->replog(c, "Reply: %s\nRedirect: http://%U%U\n", how, host, uri);
|
||||
}
|
||||
return hflush(hout);
|
||||
}
|
||||
|
||||
int
|
||||
hmoved(HConnect *c, char *uri)
|
||||
{
|
||||
return hredirected(c, "301 Moved Permanently", uri);
|
||||
}
|
35
src/libhttpd/unallowed.c
Normal file
35
src/libhttpd/unallowed.c
Normal file
|
@ -0,0 +1,35 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <bin.h>
|
||||
#include <httpd.h>
|
||||
|
||||
int
|
||||
hunallowed(HConnect *c, char *allowed)
|
||||
{
|
||||
Hio *hout;
|
||||
int n;
|
||||
|
||||
n = snprint(c->xferbuf, HBufSize, "<head><title>Method Not Allowed</title></head>\r\n"
|
||||
"<body><h1>Method Not Allowed</h1>\r\n"
|
||||
"You can't %s on <a href=\"%U\"> here</a>.<p></body>\r\n", c->req.meth, c->req.uri);
|
||||
|
||||
hout = &c->hout;
|
||||
hprint(hout, "%s 405 Method Not Allowed\r\n", hversion);
|
||||
hprint(hout, "Date: %D\r\n", time(nil));
|
||||
hprint(hout, "Server: Plan9\r\n");
|
||||
hprint(hout, "Content-Type: text/html\r\n");
|
||||
hprint(hout, "Allow: %s\r\n", allowed);
|
||||
hprint(hout, "Content-Length: %d\r\n", n);
|
||||
if(c->head.closeit)
|
||||
hprint(hout, "Connection: close\r\n");
|
||||
else if(!http11(c))
|
||||
hprint(hout, "Connection: Keep-Alive\r\n");
|
||||
hprint(hout, "\r\n");
|
||||
|
||||
if(strcmp(c->req.meth, "HEAD") != 0)
|
||||
hwrite(hout, c->xferbuf, n);
|
||||
|
||||
if(c->replog)
|
||||
c->replog(c, "Reply: 405 Method Not Allowed\nReason: Method Not Allowed\n");
|
||||
return hflush(hout);
|
||||
}
|
26
src/libhttpd/urlfmt.c
Normal file
26
src/libhttpd/urlfmt.c
Normal file
|
@ -0,0 +1,26 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <bin.h>
|
||||
#include <httpd.h>
|
||||
|
||||
int
|
||||
hurlfmt(Fmt *f)
|
||||
{
|
||||
char buf[HMaxWord*2];
|
||||
Rune r;
|
||||
char *s;
|
||||
int t;
|
||||
|
||||
s = va_arg(f->args, char*);
|
||||
for(t = 0; t < sizeof(buf) - 8; ){
|
||||
s += chartorune(&r, s);
|
||||
if(r == 0)
|
||||
break;
|
||||
if(r <= ' ' || r == '%' || r >= Runeself)
|
||||
t += snprint(&buf[t], sizeof(buf)-t, "%%%2.2x", r);
|
||||
else
|
||||
buf[t++] = r;
|
||||
}
|
||||
buf[t] = 0;
|
||||
return fmtstrcpy(f, buf);
|
||||
}
|
58
src/libhttpd/urlunesc.c
Normal file
58
src/libhttpd/urlunesc.c
Normal file
|
@ -0,0 +1,58 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <bin.h>
|
||||
#include <httpd.h>
|
||||
|
||||
/* go from url with escaped utf to utf */
|
||||
char *
|
||||
hurlunesc(HConnect *cc, char *s)
|
||||
{
|
||||
char *t, *v, *u;
|
||||
Rune r;
|
||||
int c, n;
|
||||
|
||||
/* unescape */
|
||||
u = halloc(cc, 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<<4)+n;
|
||||
}
|
||||
*t++ = c;
|
||||
}
|
||||
*t = '\0';
|
||||
|
||||
/* convert to valid utf */
|
||||
v = halloc(cc, UTFmax*strlen(u) + 1);
|
||||
s = u;
|
||||
t = v;
|
||||
while(*s){
|
||||
/* in decoding error, assume latin1 */
|
||||
if((n=chartorune(&r, s)) == 1 && r == Runeerror)
|
||||
r = (uchar)*s;
|
||||
s += n;
|
||||
t += runetochar(t, &r);
|
||||
}
|
||||
*t = '\0';
|
||||
|
||||
return v;
|
||||
}
|
Loading…
Reference in a new issue