#ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include /* chmod(2) */ #include #include #include #include #include /* strdup(3) */ #include /* basename(3) */ #include /* tolower(3) */ #include #include #include #include #include #include #include #include #include #include #include #include const signed char hexval_table[256] = { -1, -1, -1, -1, -1, -1, -1, -1, /* 00-07 */ -1, -1, -1, -1, -1, -1, -1, -1, /* 08-0f */ -1, -1, -1, -1, -1, -1, -1, -1, /* 10-17 */ -1, -1, -1, -1, -1, -1, -1, -1, /* 18-1f */ -1, -1, -1, -1, -1, -1, -1, -1, /* 20-27 */ -1, -1, -1, -1, -1, -1, -1, -1, /* 28-2f */ 0, 1, 2, 3, 4, 5, 6, 7, /* 30-37 */ 8, 9, -1, -1, -1, -1, -1, -1, /* 38-3f */ -1, 10, 11, 12, 13, 14, 15, -1, /* 40-47 */ -1, -1, -1, -1, -1, -1, -1, -1, /* 48-4f */ -1, -1, -1, -1, -1, -1, -1, -1, /* 50-57 */ -1, -1, -1, -1, -1, -1, -1, -1, /* 58-5f */ -1, 10, 11, 12, 13, 14, 15, -1, /* 60-67 */ -1, -1, -1, -1, -1, -1, -1, -1, /* 68-67 */ -1, -1, -1, -1, -1, -1, -1, -1, /* 70-77 */ -1, -1, -1, -1, -1, -1, -1, -1, /* 78-7f */ -1, -1, -1, -1, -1, -1, -1, -1, /* 80-87 */ -1, -1, -1, -1, -1, -1, -1, -1, /* 88-8f */ -1, -1, -1, -1, -1, -1, -1, -1, /* 90-97 */ -1, -1, -1, -1, -1, -1, -1, -1, /* 98-9f */ -1, -1, -1, -1, -1, -1, -1, -1, /* a0-a7 */ -1, -1, -1, -1, -1, -1, -1, -1, /* a8-af */ -1, -1, -1, -1, -1, -1, -1, -1, /* b0-b7 */ -1, -1, -1, -1, -1, -1, -1, -1, /* b8-bf */ -1, -1, -1, -1, -1, -1, -1, -1, /* c0-c7 */ -1, -1, -1, -1, -1, -1, -1, -1, /* c8-cf */ -1, -1, -1, -1, -1, -1, -1, -1, /* d0-d7 */ -1, -1, -1, -1, -1, -1, -1, -1, /* d8-df */ -1, -1, -1, -1, -1, -1, -1, -1, /* e0-e7 */ -1, -1, -1, -1, -1, -1, -1, -1, /* e8-ef */ -1, -1, -1, -1, -1, -1, -1, -1, /* f0-f7 */ -1, -1, -1, -1, -1, -1, -1, -1, /* f8-ff */ }; static char * url_decode_internal( const char **query, int len, const char *stop_at, struct strbuf *out, int decode_plus ) { const char *q = *query; while( len ) { unsigned char c = *q; if( !c ) break; if( stop_at && strchr( stop_at, c ) ) { ++q; --len; break; } if( c == '%' && (len < 0 || len >= 3) ) { int val = hex2chr( q + 1 ); if( 0 < val ) { strbuf_addch( out, val ); q += 3; len -= 3; continue; } } if( decode_plus && c == '+' ) strbuf_addch( out, ' ' ); else strbuf_addch( out, c ); ++q; --len; } *query = q; return strbuf_detach( out, NULL ); } char *url_decode_mem( const char *url, int len ) { struct strbuf out = STRBUF_INIT; const char *colon = memchr( url, ':', len ); /* Skip protocol part if present */ if( colon && url < colon ) { strbuf_add( &out, url, colon - url ); len -= colon - url; url = colon; } return url_decode_internal( &url, len, NULL, &out, 0 ); } char *url_percent_decode( const char *encoded ) { struct strbuf out = STRBUF_INIT; return url_decode_internal( &encoded, strlen(encoded), NULL, &out, 0 ); } char *url_decode_parameter_name( const char **query ) { struct strbuf out = STRBUF_INIT; return url_decode_internal( query, -1, "&=", &out, 1 ); } char *url_decode_parameter_value( const char **query ) { struct strbuf out = STRBUF_INIT; return url_decode_internal( query, -1, "&", &out, 1 ); } void http_parse_querystring( const char *txt, void (*fn)(const char *name, const char *value) ) { const char *t = txt; while( t && *t ) { char *name = url_decode_parameter_name( &t ); if( *name ) { char *value = url_decode_parameter_value( &t ); fn( name, value ); free( value ); } free( name ); } } #define HTTP_ERRMSG_SIZE 4096 void http_error( const char *fmt, ... ) { va_list arg_ptr; char buf[HTTP_ERRMSG_SIZE]; char msg[HTTP_ERRMSG_SIZE]; char *format = "%s: %s\n"; va_start( arg_ptr, fmt ); vsnprintf( msg, HTTP_ERRMSG_SIZE, (const void *)fmt, arg_ptr ); va_end( arg_ptr ); /* Reset variable arguments. */ snprintf( buf, HTTP_ERRMSG_SIZE, format, "http", msg ); (void)write( STDERR_FILENO, buf, strlen( buf ) ); exit( 1 ); } http_errfunc http_fatal = http_error; static struct { int code; const char *desc; } status[] = { { 100, "Continue" }, { 101, "Switching Protocols" }, { 200, "OK" }, { 201, "Created" }, { 202, "Accepted" }, { 203, "Non-Authoritative Information" }, { 204, "No Content" }, { 205, "Reset Content" }, { 206, "Partial Content" }, { 300, "Multiple Choices" }, { 301, "Moved Permanently" }, { 302, "Found" }, { 303, "See Other" }, { 304, "Not Modified" }, { 305, "Use Proxy" }, { 307, "Temporary Redirect" }, { 400, "Bad Request" }, { 401, "Unauthorized" }, { 402, "Payment Required" }, { 403, "Forbidden" }, { 404, "Not Found" }, { 405, "Method Not Allowed" }, { 406, "Not Acceptable" }, { 407, "Proxy Authentication Required" }, { 408, "Request Timeout" }, { 409, "Conflict" }, { 410, "Gone" }, { 411, "Length Required" }, { 412, "Precondition Failed" }, { 413, "Request Entity Too Large" }, { 414, "Request-URI Too Long" }, { 415, "Unsupported Media Type" }, { 416, "Requested Range Not Satisfiable" }, { 417, "Expectation Failed" }, { 418, "I'm a teapot" }, { 500, "Internal Server Error" }, { 501, "Not Implemented" }, { 502, "Bad Gateway" }, { 503, "Service Unavailable" }, { 504, "Gateway Timeout" }, { 505, "HTTP Version Not Supported" }, { 0, NULL } }; const char *http_status( int status_code ) { int i = 0, code; while( (code = status[i].code) ) { if( code == status_code ) return status[i].desc; ++i; } return NULL; } char *fmt( const char *format, ... ) { static char buf[8][1024]; static int bufidx; int len; va_list args; bufidx++; bufidx &= 7; va_start( args, format ); len = vsnprintf( buf[bufidx], sizeof(buf[bufidx]), format, args ); va_end(args); if( len > sizeof(buf[bufidx]) ) { http_fatal( "string truncated: %s", format ); } return buf[bufidx]; } char *fmtalloc( const char *format, ... ) { struct strbuf sb = STRBUF_INIT; va_list args; va_start( args, format ); strbuf_vaddf( &sb, format, args ); va_end( args ); return strbuf_detach( &sb, NULL ); } char *http_date( time_t t ) { static char day[][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; static char month[][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; struct tm *tm = gmtime(&t); return fmt( "%s, %02d %s %04d %02d:%02d:%02d GMT", day[tm->tm_wday], tm->tm_mday, month[tm->tm_mon], 1900 + tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec ); }