/* -----------------------------------------------------------------------------
* parse.c
*
* This file contains code to parse HTTP requests into various subcomponents
*
* Author(s) : David Beazley (beazley@cs.uchicago.edu)
* Mike Sliczniak (mzsliczn@midway.uchicago.edu)
*
* Copyright (C) 1999-2000. The University of Chicago
* See the file LICENSE for information on usage and redistribution.
* ----------------------------------------------------------------------------- */
static char cvsroot[] = "$Header: /home/pastacvs/cvs-rep/vermicelli/tot/src/SWILL-0.1/Source/SWILL/parse.c,v 1.1 2005/01/27 08:46:25 dillema Exp $";
#include "swillint.h"
/* -----------------------------------------------------------------------------
* swill_read_rawrequest()
*
* Reads a raw HTTP request from a file descriptor. This function is responsible
* for reading directly from the underlying socket. It is very picky and only
* allows headers smaller than SWILL_MAX_HEADER.
*
* Returns 0 on error. Otherwise, it returns 1 and places the request in the
* passed request string. excess is a string containing excess data that was
* supplied on the connection, but which is not part of the request header.
* ----------------------------------------------------------------------------- */
int
swill_read_rawrequest(int fd, String **request, String **excess)
{
char buffer[SWILL_MAX_HEADER];
char requestbuf[SWILL_MAX_HEADER];
int buf_in = 0;
int request_in = 0;
int state = 0;
int len;
fd_set reading;
struct timeval tv;
char c;
int retval;
FD_ZERO(&reading);
while (buf_in < SWILL_MAX_HEADER) {
FD_SET(fd,&reading);
tv.tv_sec = SwillTimeout;
tv.tv_usec = 0;
retval = select(fd+1,&reading,NULL,NULL,&tv);
if (retval <= 0) {
/* Timeout. We're gone */
swill_logprintf("Request read timeout! ");
return 0;
}
len = read(fd, buffer+buf_in, SWILL_MAX_HEADER-buf_in);
if (len <= 0) {
if (errno == EINTR) continue;
else return 0; /* Read error. Failure!! */
}
while (len > 0) {
c = buffer[buf_in];
if (c == '\r') { /* Nuke carriage returns (A Windows bogosity) */
buf_in++;
len--;
continue;
}
switch (state) {
case 0: /* Request line */
requestbuf[request_in++] = c;
if (c == '\n') {
state = 1;
}
break;
case 1: /* End of headers? */
if (c == '\n') {
*request = NewString("");
Write(*request,requestbuf,request_in); /* Save request */
*excess = NewString("");
Write(*excess,buffer+buf_in, len); /* Write remaining data in excess string */
return 1;
} else {
requestbuf[request_in++] = c;
state = 0;
}
break;
}
len--;
buf_in++;
}
}
/* Header is too large. Bail out with an error */
return 0;
}
/* -----------------------------------------------------------------------------
* swill_read_post()
*
* Reads data supplied with a POST operation. Returns as a string on success.
* Returns NULL on failure.
* ----------------------------------------------------------------------------- */
String *
swill_read_post(int fd, int length, String *excess)
{
char buffer[8192];
int rlen;
int elen;
String *post;
fd_set reading;
struct timeval tv;
char c;
int retval;
FD_ZERO(&reading);
if (length > SWILL_MAX_QUERY) {
return 0;
}
post = NewString("");
/* First copy data in the excess string over */
elen = Len(excess);
if (length < elen) elen = length;
Write(post,Char(excess),elen);
length -= elen;
while (length > 0) {
FD_SET(fd,&reading);
tv.tv_sec = SwillTimeout;
tv.tv_usec = 0;
retval = select(fd+1,&reading,NULL,NULL,&tv);
if (retval <= 0) {
/* Timeout. We're gone */
Delete(post);
return 0;
}
rlen = read(fd,buffer,8192);
if (rlen <= 0) {
if (errno == EINTR) continue;
Delete(post);
return 0;
}
Write(post,buffer,rlen);
length -= rlen;
}
return post;
}
/* -----------------------------------------------------------------------------
* swill_parse_url()
*
* Parses the request line of a HTTP header, splitting it into an operation,
* URI, and query string. Returns 1 on success, 0 otherwise.
* ----------------------------------------------------------------------------- */
int
swill_parse_url(String *request, String **op_out, String **url_out, String **query_out) {
List *fields;
String *rawurl;
String *url;
List *urlparts;
fields = Split(request," ", -1);
if (Len(fields) != 3) {
Delete(fields);
return 0;
}
*op_out = Copy(Getitem(fields,0));
rawurl = Getitem(fields,1);
Delitem(rawurl,0); /* Nuke first character */
urlparts = Split(rawurl,"?",1);
*url_out = Copy(Getitem(urlparts,0));
if (Len(urlparts) > 1) {
*query_out = Copy(Getitem(urlparts,1));
} else {
*query_out = 0;
}
Delete(urlparts);
Delete(fields);
return 1;
}
/* -----------------------------------------------------------------------------
* swill_parse_query()
*
* Parse a query string into a hash-table of attribute-value pairs
* ----------------------------------------------------------------------------- */
Hash *
swill_parse_query(String *qs) {
List *list;
Hash *map;
int i,j;
map = NewHash();
if (!qs) return map;
list = Split(qs,"&",-1); /* Split into fields */
for (i = 0; i < Len(list); i++) {
String *item, *name, *value, *decoded;
List *pair;
item = Getitem(list,i);
pair = Split(item,"=",1);
if (Len(pair) != 2) {
Delete(pair);
Delete(list);
Delete(map);
return 0;
}
name = Getitem(pair,0);
value = Getitem(pair,1);
if (name && value) {
decoded = NewString("");
Seek(value,0,SEEK_SET);
swill_url_decode(value,decoded);
Setattr(map,name,decoded);
Delete(decoded);
}
Delete(pair);
}
Delete(list);
return map;
}
/* -----------------------------------------------------------------------------
* convert_tolower()
*
* Convert a string to all lower-case. Used in HTTP header parsing.
* ----------------------------------------------------------------------------- */
static String *
convert_tolower(String *in) {
String *str;
int ch;
str = NewString("");
Seek(in,0,SEEK_SET);
while (1) {
ch = Getc(in);
if (ch != EOF) {
Putc(tolower(ch),str);
} else {
break;
}
}
return str;
}
/* -----------------------------------------------------------------------------
* swill_parse_headers()
*
* Parse a list of RFC822 lines into a hash table
* ----------------------------------------------------------------------------- */
Hash *
swill_parse_headers(List *lines) {
/* Grab all of the HTTP headers and put into a hash object */
Hash *headers;
String *header;
List *pair;
String *name, *value = 0;
int i;
headers = NewHash();
for (i = 0; i < Len(lines); i++) {
header = Getitem(lines,i);
if (!Len(header)) return headers; /* Blank line. End of headers */
if (isspace(*(Char(header)))) {
/* Must be a continuation */
if (value)
Append(value,header);
continue;
}
pair = Split(header,":",1); /* Split into components */
if (Len(pair) == 2) {
String *nlower;
name = Getitem(pair,0);
value = Getitem(pair,1);
Delitem(value,0); /* Get rid of leading space */
nlower = convert_tolower(name);
Setattr(headers,nlower,value);
Delete(nlower);
}
Delete(pair);
}
return headers;
}
/* -----------------------------------------------------------------------------
* swill_parse_request_headers()
*
* Takes a request string and parses it into a full request object. Returns a
* hash table that contains the following fields:
*
* uri - HTTP URI
* method - HTTP Method
* query - Hash table of query variables
* headers - Hash table of HTTP headers
* request - Raw request string that can be used to regenerate the request
* querystring - Raw query string
*
* Returns 0 on failure.
* ----------------------------------------------------------------------------- */
Hash *
swill_parse_request_headers(String *request) {
List *lines;
String *urlline;
String *method = 0, *uri = 0, *querystring = 0;
Hash *headers = 0;
Hash *reqh = NewHash();
/* Split request into lines */
lines = Split(request,"\n",-1);
if (Len(lines) < 1) {
Delete(reqh);
Delete(lines);
return 0;
}
urlline = Getitem(lines,0);
if (!swill_parse_url(urlline, &method, &uri, &querystring)) {
/* Bad HTTP request */
Delete(reqh);
Delete(lines);
return 0;
}
Delitem(lines,0);
headers = swill_parse_headers(lines);
if (!headers) {
Delete(reqh);
Delete(lines);
Delete(method);
Delete(uri);
Delete(querystring);
return 0;
}
/* If the uri has zero length, then we use the SWILL default document.
Usually this is "index.html" */
if ((Len(uri) == 0)) {
Append(uri, SWILL_DEFAULT);
}
/* Request looks okay for now */
Setattr(reqh,"uri", uri);
Setattr(reqh,"method",method);
Setattr(reqh,"headers", headers);
Setattr(reqh,"request", request);
Setattr(reqh,"querystring", querystring);
Delete(lines);
Delete(headers);
Delete(uri);
Delete(method);
Delete(querystring);
return reqh;
}
/* -----------------------------------------------------------------------------
* swill_parse_request_data()
*
* Takes a hash returned by swill_parse_request_headers and parses form variables
* ----------------------------------------------------------------------------- */
int
swill_parse_request_data(Hash *reqh) {
String *method;
String *qs = 0;
Hash *headers;
Hash *query;
int post = 0;
method = Getattr(reqh,"method");
if (!method) return 0;
headers = Getattr(reqh,"headers");
if (!headers) return 0;
if (Strcmp(method,"GET") == 0) {
qs = Getattr(reqh,"querystring");
} else if (Strcmp(method,"POST") == 0) {
/* Data follows the headers */
String *req = Getattr(reqh,"request");
int length = GetInt(headers,"content-length");
if (length > 0) {
/* Find start of the headers */
char *qsc = Strstr(req,"\n\n");
if (qsc) qs = NewString(qsc+2);
Chop(qs);
}
post = 1;
}
if (qs) {
query = swill_parse_query(qs);
if (post) {
Setattr(reqh,"querystring",qs);
Delete(qs);
}
if (!query) {
return 0;
}
Setattr(reqh,"query",query);
Delete(query);
} else {
query = NewHash();
Setattr(reqh,"query", query);
Delete(query);
}
/* Set up some additional query variables */
Setattr(query,"__uri__", Getattr(reqh,"uri"));
Setattr(query,"__method__",method);
return 1;
}
/* -----------------------------------------------------------------------------
* swill_parse_request()
*
* Parses a complete request in a string
* ----------------------------------------------------------------------------- */
Hash *
swill_parse_request(String *request) {
Hash *h = swill_parse_request_headers(request);
if (!h) return 0;
if (!(swill_parse_request_data(h))) {
Delete(h);
return 0;
}
return h;
}
syntax highlighted by Code2HTML, v. 0.9.1