|
|
|
|
|
|
#include "global.h" |
#include "config.h" |
|
#include "machine.h" |
|
#include <sys/stat.h> |
#include <fcntl.h> |
#include <ctype.h> |
#include <time.h> |
|
#include "fdlib.h" |
#include "stralloc.h" |
#include "pike_macros.h" |
#include "machine.h" |
#include "object.h" |
#include "constants.h" |
#include "interpret.h" |
#include "svalue.h" |
#include "mapping.h" |
#include "array.h" |
#include "builtin_functions.h" |
#include "module_support.h" |
#include "backend.h" |
#include "threads.h" |
#include "operators.h" |
|
|
|
|
|
|
|
|
|
|
#define FLAG_THROW_ERROR 1 |
#define FLAG_KEEP_CASE 2 |
#define FLAG_NO_FOLD 4 |
|
#define THP ((struct header_buf *)Pike_fp->current_storage) |
struct header_buf |
{ |
unsigned char *headers; |
unsigned char *pnt; |
ptrdiff_t hsize, left; |
int slash_n, tslash_n, spc; |
int mode; |
}; |
|
static void f_hp_init( struct object *UNUSED(o) ) |
{ |
THP->headers = NULL; |
THP->pnt = NULL; |
THP->hsize = 0; |
THP->left = 0; |
THP->spc = THP->slash_n = THP->tslash_n = 0; |
THP->mode = 0; |
} |
|
static void f_hp_exit( struct object *UNUSED(o) ) |
{ |
if( THP->headers ) |
free( THP->headers ); |
THP->headers = NULL; |
THP->pnt = NULL; |
THP->hsize = 0; |
} |
|
static void f_hp_feed( INT32 args ) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{ |
struct pike_string *str = Pike_sp[-args].u.string; |
struct header_buf *hp = THP; |
int keep_case = hp->mode & FLAG_KEEP_CASE; |
int fold = !(hp->mode & FLAG_NO_FOLD); |
int str_len; |
int tot_slash_n=hp->tslash_n, slash_n = hp->slash_n, spc = hp->spc; |
unsigned char *pp,*ep; |
struct svalue *tmp; |
struct mapping *headers; |
ptrdiff_t os=0, i, j, l; |
unsigned char *in; |
|
if( !( args == 1 || args == 2 ) ) |
Pike_error("Bad number of arguments to feed().\n"); |
if( TYPEOF(Pike_sp[-args]) != PIKE_T_STRING ) |
Pike_error("Wrong type of argument to feed()\n"); |
if( args == 2 ) |
{ |
|
|
|
if( TYPEOF(Pike_sp[-args+1]) == PIKE_T_INT ) |
keep_case = Pike_sp[-args+1].u.integer; |
else |
Pike_error("Wrong type of argument to feed()\n"); |
} |
if( str->size_shift ) |
Pike_error("Wide string headers not supported\n"); |
str_len = str->len; |
while( str_len >= hp->left ) |
{ |
unsigned char *buf; |
if( hp->hsize > 512 * 1024 ) |
Pike_error("Too many headers\n"); |
hp->hsize += 8192; |
buf = hp->headers; |
hp->headers = realloc( hp->headers, hp->hsize ); |
if( !hp->headers ) |
{ |
free(buf); |
hp->hsize = 0; |
hp->left = 0; |
hp->spc = hp->slash_n = 0; |
hp->pnt = NULL; |
Pike_error("Running out of memory in header parser\n"); |
} |
hp->left += 8192; |
hp->pnt = (hp->headers + hp->hsize - hp->left); |
} |
|
memcpy( hp->pnt, str->str, str_len ); |
pop_n_elems( args ); |
|
|
for( ep=(hp->pnt+str_len),pp=MAXIMUM(hp->headers,hp->pnt); |
pp<ep && slash_n<2; pp++ ) |
if( *pp == ' ' ) |
{ |
spc++; |
slash_n = 0; |
} |
else if( *pp == '\n' ) |
{ |
slash_n++; |
tot_slash_n++; |
} |
else if( *pp != '\r' ) |
{ |
slash_n=0; |
} |
|
hp->slash_n = slash_n; |
hp->spc = spc; |
hp->tslash_n = tot_slash_n; |
hp->left -= str_len; |
hp->pnt += str_len; |
hp->pnt[0] = 0; |
|
if( slash_n != 2 ) |
{ |
|
if( (spc < 2) && tot_slash_n ) |
{ |
|
|
|
push_empty_string(); |
|
push_string(make_shared_binary_string((char *)hp->headers, |
hp->hsize - hp->left)); |
f_aggregate_mapping( 0 ); |
f_aggregate( 3 ); |
return; |
} |
push_int( 0 ); |
return; |
} |
|
|
push_string(make_shared_binary_string((char *)pp, hp->pnt - pp)); |
headers = allocate_mapping( 5 ); |
in = hp->headers; |
l = pp - hp->headers; |
|
|
for( i = 0; i < l; i++ ) |
if( (in[i] == '\n') || (in[i] == '\r') ) |
break; |
|
push_string(make_shared_binary_string((char *)in, i)); |
|
if((in[i] == '\r') && (in[i+1] == '\n')) |
i++; |
i++; |
|
in += i; l -= i; |
|
|
for(i = 0; i < l; i++) |
{ |
if(!keep_case && (in[i] > 64 && in[i] < 91)) |
{ |
in[i]+=32; |
} |
else if( in[i] == ':' ) |
{ |
|
|
|
|
int val_cnt = 0; |
push_string(make_shared_binary_string((char*)in+os,i-os)); |
|
|
os = i+1; |
while((in[os]==' ') || (in[os]=='\t')) os++; |
|
|
|
|
do { |
for(j=os;j<l;j++) |
if( in[j] == '\n' || in[j]=='\r') |
break; |
|
|
push_string(make_shared_binary_string((char*)in+os,j-os)); |
val_cnt++; |
|
if((in[j] == '\r') && (in[j+1] == '\n')) j++; |
os = j+1; |
i = j; |
|
} while ((os < l) && fold && ((in[os] == ' ') || (in[os] == '\t'))); |
|
if (val_cnt > 1) { |
|
f_add(val_cnt); |
} |
|
if((tmp = low_mapping_lookup(headers, Pike_sp-2))) |
{ |
if( TYPEOF(*tmp) == PIKE_T_ARRAY ) |
{ |
f_aggregate( 1 ); |
ref_push_array(tmp->u.array); |
stack_swap(); |
map_delete(headers, Pike_sp-3); |
f_add(2); |
} else { |
ref_push_string(tmp->u.string); |
stack_swap(); |
map_delete(headers, Pike_sp-3); |
f_aggregate(2); |
} |
} |
mapping_insert(headers, Pike_sp-2, Pike_sp-1); |
|
pop_n_elems(2); |
} |
else if( in[i]=='\r' || in[i]=='\n' ) |
{ |
if( THP->mode & FLAG_THROW_ERROR ) |
{ |
|
Pike_error("Malformed HTTP header.\n"); |
} |
else |
os = i+1; |
} |
} |
push_mapping( headers ); |
f_aggregate( 3 ); |
} |
|
static void f_hp_create( INT32 args ) |
|
|
{ |
INT_TYPE throw_errors = 0; |
INT_TYPE keep_case = 0; |
INT_TYPE no_fold = 0; |
get_all_args("create",args,".%i%i%i", &throw_errors, &keep_case, &no_fold); |
|
if (THP->headers) { |
free(THP->headers); |
THP->headers = NULL; |
} |
|
THP->mode = 0; |
if(throw_errors) THP->mode |= FLAG_THROW_ERROR; |
if(keep_case) THP->mode |= FLAG_KEEP_CASE; |
if(no_fold) THP->mode |= FLAG_NO_FOLD; |
|
THP->headers = xalloc( 8192 ); |
THP->pnt = THP->headers; |
THP->hsize = 8192; |
THP->left = 8192; |
THP->spc = THP->slash_n = 0; |
} |
|
|
|
|
static int valid_header_name(struct pike_string *s) |
{ |
ptrdiff_t i; |
if (s->size_shift) return 0; |
for (i = 0; i < s->len; i++) { |
int c = s->str[i]; |
if ((c == '\n') || (c == '\r') || (c == '\t') || (c == ' ') || (c == ':')) { |
|
return 0; |
} |
} |
return 1; |
} |
|
static int valid_header_value(struct pike_string *s) |
{ |
ptrdiff_t i; |
if (s->size_shift) return 0; |
for (i = 0; i < s->len; i++) { |
int c = s->str[i]; |
if ((c == '\n') || (c == '\r')) { |
|
return 0; |
} |
} |
return 1; |
} |
|
static void f_make_http_headers( INT32 args ) |
|
|
|
|
{ |
int total_len = 0, e; |
unsigned char *pnt; |
struct mapping *m; |
struct keypair *k; |
struct pike_string *res; |
int terminator = 2; |
|
if( TYPEOF(Pike_sp[-args]) != PIKE_T_MAPPING ) |
Pike_error("Wrong argument type to make_http_headers(mapping heads)\n"); |
m = Pike_sp[-args].u.mapping; |
|
if (args > 1) { |
if (TYPEOF(Pike_sp[1-args]) != PIKE_T_INT) |
Pike_error("Bad argument 2 to make_http_headers(). Expected int.\n"); |
if (Pike_sp[1-args].u.integer) |
terminator = 0; |
} |
|
|
NEW_MAPPING_LOOP( m->data ) |
{ |
if( TYPEOF(k->ind) != PIKE_T_STRING || !valid_header_name(k->ind.u.string) ) |
Pike_error("Wrong argument type to make_http_headers(" |
"mapping(string(8bit):string(8bit)|array(string(8bit))) heads)\n"); |
if( TYPEOF(k->val) == PIKE_T_STRING && valid_header_value(k->val.u.string) ) |
total_len += k->val.u.string->len + 2 + k->ind.u.string->len + 2; |
else if( TYPEOF(k->val) == PIKE_T_ARRAY ) |
{ |
struct array *a = k->val.u.array; |
ptrdiff_t i, kl = k->ind.u.string->len + 2 ; |
for( i = 0; i<a->size; i++ ) |
if( TYPEOF(a->item[i]) != PIKE_T_STRING || |
!valid_header_value(a->item[i].u.string) ) |
Pike_error("Wrong argument type to make_http_headers(" |
"mapping(string(8bit):string(8bit)|" |
"array(string(8bit))) heads)\n"); |
else |
total_len += kl + a->item[i].u.string->len + 2; |
} else |
Pike_error("Wrong argument type to make_http_headers(" |
"mapping(string(8bit):string(8bit)|" |
"array(string(8bit))) heads)\n"); |
} |
total_len += terminator; |
|
res = begin_shared_string( total_len ); |
pnt = STR0(res); |
#define STRADD(X) \ |
for( l=(X).u.string->len, s=STR0((X).u.string), c=0; c<l; c++ ) \ |
*(pnt++)=*(s++) |
|
NEW_MAPPING_LOOP( m->data ) |
{ |
unsigned char *s; |
ptrdiff_t l, c; |
if( TYPEOF(k->val) == PIKE_T_STRING ) |
{ |
STRADD( k->ind ); *(pnt++) = ':'; *(pnt++) = ' '; |
STRADD( k->val ); *(pnt++) = '\r'; *(pnt++) = '\n'; |
} |
else |
{ |
struct array *a = k->val.u.array; |
ptrdiff_t i, kl = k->ind.u.string->len + 2; |
for( i = 0; i<a->size; i++ ) |
{ |
STRADD( k->ind ); *(pnt++) = ':'; *(pnt++) = ' '; |
STRADD( a->item[i] );*(pnt++) = '\r';*(pnt++) = '\n'; |
} |
} |
} |
if (terminator) { |
*(pnt++) = '\r'; |
*(pnt++) = '\n'; |
} |
|
pop_n_elems( args ); |
push_string( end_shared_string( res ) ); |
} |
|
static p_wchar2 parse_hexchar(p_wchar2 hex) |
{ |
if(hex>='0' && hex<='9') |
return hex-'0'; |
hex |= 32; |
return hex-'W'; |
} |
|
static void f_http_decode_string(INT32 args) |
|
|
|
|
|
|
|
|
|
|
{ |
int proc = 0; |
int size_shift; |
int got_surrogates = 0; |
PCHARP foo, end; |
struct string_builder newstr; |
|
if (!args || TYPEOF(Pike_sp[-args]) != PIKE_T_STRING) |
Pike_error("Invalid argument to http_decode_string(string).\n"); |
|
foo = MKPCHARP_STR(Pike_sp[-args].u.string); |
end = ADD_PCHARP(foo, Pike_sp[-args].u.string->len); |
|
size_shift = Pike_sp[-args].u.string->size_shift; |
|
|
|
|
|
for (; COMPARE_PCHARP(foo, <, end);) { |
p_wchar2 c = EXTRACT_PCHARP(foo); |
INC_PCHARP(foo, 1); |
if (c != '%') continue; |
|
if (SUBTRACT_PCHARP(end, foo) <= 1) |
Pike_error("Truncated http transport encoded string.\n"); |
c = EXTRACT_PCHARP(foo); |
if (c == 'u' || c == 'U') { |
if (SUBTRACT_PCHARP(end, foo) <= 4) |
Pike_error("Truncated unicode sequence.\n"); |
INC_PCHARP(foo, 1); |
if (!isxdigit(INDEX_PCHARP(foo, 0)) || |
!isxdigit(INDEX_PCHARP(foo, 1)) || |
!isxdigit(INDEX_PCHARP(foo, 2)) || |
!isxdigit(INDEX_PCHARP(foo, 3))) |
Pike_error("Illegal transport encoding.\n"); |
|
if (EXTRACT_PCHARP(foo) != '0' || INDEX_PCHARP(foo, 1) != '0') { |
if (!size_shift) size_shift = 1; |
} |
proc += 5; |
INC_PCHARP(foo, 4); |
} else { |
if (!isxdigit(INDEX_PCHARP(foo, 0)) || |
!isxdigit(INDEX_PCHARP(foo, 1))) |
Pike_error("Illegal transport encoding.\n"); |
proc += 2; |
INC_PCHARP(foo, 2); |
} |
} |
|
if (!proc) { pop_n_elems(args-1); return; } |
|
init_string_builder_alloc(&newstr, Pike_sp[-args].u.string->len - proc, |
size_shift); |
|
foo = MKPCHARP_STR(Pike_sp[-args].u.string); |
|
for (; COMPARE_PCHARP(foo, <, end); INC_PCHARP(foo, 1)) { |
p_wchar2 c = INDEX_PCHARP(foo, 0); |
if (c == '%') { |
c = INDEX_PCHARP(foo, 1); |
|
|
|
|
if (c == 'u' || c == 'U') { |
p_wchar2 hex = INDEX_PCHARP(foo, 2); |
c = parse_hexchar(hex)<<12; |
hex = INDEX_PCHARP(foo, 3); |
c |= parse_hexchar(hex)<<8; |
hex = INDEX_PCHARP(foo, 4); |
c |= parse_hexchar(hex)<<4; |
hex = INDEX_PCHARP(foo, 5); |
c |= parse_hexchar(hex); |
INC_PCHARP(foo, 5); |
if ((c & 0xf800) == 0xd800) { |
got_surrogates = 1; |
} |
} else { |
p_wchar2 hex = INDEX_PCHARP(foo, 1); |
c = parse_hexchar(hex)<<4; |
hex = INDEX_PCHARP(foo, 2); |
c |= parse_hexchar(hex); |
INC_PCHARP(foo, 2); |
} |
} |
string_builder_putchar(&newstr, c); |
} |
|
pop_n_elems(args); |
|
if (got_surrogates) { |
|
newstr.s->size_shift = 0; |
newstr.known_shift = 0; |
newstr.s->len <<= 1; |
|
|
push_string(finish_string_builder(&newstr)); |
push_int(2); |
f_unicode_to_string(2); |
} else { |
push_string(finish_string_builder(&newstr)); |
} |
} |
|
static void f_html_encode_string( INT32 args ) |
|
|
|
|
{ |
struct pike_string *str; |
int newlen; |
INT32 min; |
|
if( args != 1 ) |
Pike_error("Wrong number of arguments to html_encode_string\n" ); |
|
switch( TYPEOF(Pike_sp[-1]) ) |
{ |
void o_cast_to_string(); |
|
case PIKE_T_INT: |
case PIKE_T_FLOAT: |
|
|
|
o_cast_to_string(); |
return; |
|
default: |
o_cast_to_string(); |
case PIKE_T_STRING: |
break; |
} |
|
str = Pike_sp[-1].u.string; |
newlen = str->len; |
|
check_string_range(str, 1, &min, NULL); |
|
if (min > '>') return; |
|
#define COUNT(T) { \ |
T *s = (T *)str->str; \ |
int i; \ |
for( i = 0; i<str->len; i++ ) \ |
switch( s[i] ) \ |
{ \ |
case 0: /* � */ \ |
case '<': /* < */ \ |
case '>': newlen+=3; break;/* > */ \ |
case '&': /* & */ \ |
case '"': /* " */ \ |
case '\'': newlen+=4;break;/* ' */ \ |
} \ |
} |
|
#define ADD(X) if(sizeof(X)-sizeof("")==4) ADD4(X); else ADD5(X) |
|
#define ADD4(X) ((d[0] = X[0]), (d[1] = X[1]), (d[2] = X[2]), (d[3] = X[3]),\ |
(d+=3)) |
|
#define ADD5(X) ((d[0] = X[0]), (d[1] = X[1]), (d[2] = X[2]), (d[3] = X[3]),\ |
(d[4] = X[4]), (d+=4)) |
|
#define REPLACE(T) { \ |
T *s = (T *)str->str; \ |
T *d = (T *)res->str; \ |
int i; \ |
for( i = 0; i<str->len; i++,s++,d++ ) \ |
switch( *s ) \ |
{ \ |
case 0: ADD("�"); break; \ |
case '&': ADD("&"); break; \ |
case '<': ADD("<"); break; \ |
case '>': ADD(">"); break; \ |
case '"': ADD("""); break; \ |
case '\'':ADD("'"); break; \ |
default: *d = *s; break; \ |
} \ |
} \ |
|
switch( str->size_shift ) |
{ |
case 0: COUNT(p_wchar0); break; |
case 1: COUNT(p_wchar1); break; |
case 2: COUNT(p_wchar2); break; |
} |
|
if( newlen == str->len ) |
return; |
|
{ |
struct pike_string *res = begin_wide_shared_string(newlen,str->size_shift); |
switch( str->size_shift ) |
{ |
case 0: REPLACE(p_wchar0); break; |
case 1: REPLACE(p_wchar1); break; |
case 2: REPLACE(p_wchar2); break; |
} |
pop_stack(); |
push_string( low_end_shared_string( res ) ); |
} |
} |
|
|
|
|
PIKE_MODULE_INIT |
{ |
ADD_FUNCTION("make_http_headers", f_make_http_headers, |
tFunc(tMap(tStr,tOr(tStr,tArr(tStr))) tOr(tInt01,tVoid), tStr), |
0); |
|
ADD_FUNCTION("http_decode_string", f_http_decode_string, |
tFunc(tStr,tStr), 0 ); |
|
ADD_FUNCTION("html_encode_string", f_html_encode_string, |
tFunc(tMix,tStr), 0 ); |
|
start_new_program(); |
ADD_STORAGE( struct header_buf ); |
set_init_callback( f_hp_init ); |
set_exit_callback( f_hp_exit ); |
ADD_FUNCTION("feed", f_hp_feed, |
tFunc(tStr tOr(tInt01,tVoid),tArr(tOr(tStr,tMapping))), 0); |
ADD_FUNCTION( "create", f_hp_create, tFunc(tOr(tInt,tVoid) tOr(tInt,tVoid) tOr(tInt,tVoid),tVoid), ID_PROTECTED ); |
end_class( "HeaderParser", 0 ); |
} |
|
PIKE_MODULE_EXIT |
{ |
} |
|
|