pike.git
/
src
/
modules
/
_Roxen
/
roxen.c
version
»
Context lines:
10
20
40
80
file
none
3
pike.git/src/modules/_Roxen/roxen.c:1:
+
/*
+
|| This file is part of Pike. For copyright information see COPYRIGHT.
+
|| Pike is distributed under GPL, LGPL and MPL. See the file COPYING
+
|| for more information.
+
*/
+
#define NO_PIKE_SHORTHAND #include "global.h" #include "config.h" #include "machine.h" #include <sys/types.h> #include <sys/stat.h>
pike.git/src/modules/_Roxen/roxen.c:23:
#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"
-
/* must be last include! */
-
#include "module_magic.h"
+
-
/*
***
CLASS
HeaderParser
*/
+
/*
!
@module
_Roxen
+
*/
-
#define THP ((struct header_buf *)Pike_fp->current_
object->
storage)
+
/*! @class HeaderParser
+
*!
+
*! Class for parsing HTTP-requests.
+
*/
+
+
#define THP ((struct header_buf *)Pike_fp->current_storage)
struct header_buf {
-
char headers
[8192]
;
-
char *pnt;
-
ptrdiff_t left;
+
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 )
-
+
/*! @decl array(string|mapping) feed(string data)
+
*!
+
*! @returns
+
*! @array
+
*! @elem string 0
+
*! Trailing data.
+
*! @elem string 1
+
*! First line of request.
+
*! @elem mapping(string:string|array(string)) 2
+
*! Headers.
+
*! @endarray
+
*/
{ struct pike_string *str = Pike_sp[-1].u.string;
-
int slash_n =
0
,
cnt,
num
;
-
char *pp,*ep;
+
struct header_buf *hp = THP;
+
int
str_len;
+
int tot_
slash_n
=hp->slash_n,
slash_n
=
hp->tslash_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( Pike_sp[-1]
.type
!= PIKE_T_STRING )
+
if
(
args
!= 1)
+
Pike_
error("Bad number of arguments to feed().\n");
+
if( TYPEOF(Pike_
sp[-1]
)
!= PIKE_T_STRING )
Pike_error("Wrong type of argument to feed()\n");
-
-
if( str->len >=
THP
->left )
+
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(
THP
->pnt, str->str, str
->
len );
+
MEMCPY(
hp
->pnt, str->str, str
_
len );
+
pop_n_elems( args );
-
for( ep=(
THP
->pnt+str
->
len),
-
pp=MAXIMUM(
THP
->headers,
THP
->pnt-3);
-
pp<ep && slash_n<2;
-
pp++
)
-
if( *pp == '\n' )
+
/* FIXME: The below does not support lines terminated with just \r. */
+
for( ep=(
hp
->pnt+str
_
len),pp=MAXIMUM(
hp
->headers,
hp
->pnt-3);
+
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;
-
+
}
-
THP
->
left
-=
str
->len;
-
THP
->pnt += str
->
len;
-
THP
->pnt[0] = 0;
-
pop_n_elems( args );
+
hp
->
slash_n = tot_slash_n;
+
hp->spc = spc;
+
hp->tslash_n = slash_n;
+
hp->
left -= str
_
len;
+
hp
->pnt += str
_
len;
+
hp
->pnt[0] = 0;
+
if( slash_n != 2 ) {
-
/*
check
for
HTTP/0.9
?
*/
+
/*
one
newline,
but less than 2 space,
+
* -->
HTTP/0.9
or broken request
+
*/
+
if( (spc < 2) && tot_slash_n )
+
{
+
push_empty_string();
+
/* This includes (all eventual) \r\n etc. */
+
push_text((char *)hp->headers);
+
f_aggregate_mapping( 0 );
+
f_aggregate( 3 );
+
return;
+
}
push_int( 0 ); return; }
-
push_string(
make_shared_binary_string( pp,
THP
->pnt - pp
)
);
/*leftovers*/
+
/*leftovers*/
+
push_string(make_shared_binary_string(
(char
*)
pp,
hp
->pnt - pp));
headers = allocate_mapping( 5 );
-
in =
THP
->headers;
-
l = pp -
THP
->headers;
+
in =
hp
->headers;
+
l = pp -
hp
->headers;
/* find first line here */ for( i = 0; i < l; i++ )
-
if( in[i] == '\n' )
+
if(
(
in[i] == '\n'
)
|| (in[i] == '\r'
)
)
break;
-
if
( in[i
-1
]
!
= '\r'
)
+
push_string
(
make_shared_binary_string((char
*)
in
, i));
+
+
if((in
[i] =
=
'\r'
)
&& (in[i+1] == '\n'
)
)
i++;
-
+
i++;
-
push_string( make_shared_binary_string( in, i-1 ) );
+
in += i; l -= i;
-
if( *in == '\n' ) (in++),(l--);
+
-
+
/* Parse headers. */
for(i = 0; i < l; i++) {
-
if(in[i] > 64 && in[i] < 91) in[i]+=32;
+
if(in[i] > 64 && in[i] < 91)
+
in[i]+=32;
/* lower_case */
else if( in[i] == ':' ) {
-
+
/* FIXME: Does not support white space before the colon. */
/* in[os..i-1] == the header */
-
+
int val_cnt = 0;
push_string(make_shared_binary_string((char*)in+os,i-os));
-
+
+
/* Skip the colon and initial white space. */
os = i+1;
-
while(in[os]==' ') os++;
-
for(j=os;j<l;j++)
+
while(
(
in[os]==' ')
|| (in[os]=='\t'))
os++;
+
+
/* NOTE: We need to support MIME header continuation lines
+
* (Opera uses this...).
+
*/
+
do {
+
for(j=os;j<l;j++)
/* Find end of line */
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;
+
/* Check for continuation line. */
+
} while ((os < l) && ((in[os] == ' ') || (in[os] == '\t')));
+
+
if (val_cnt > 1) {
+
/* Join partial header values. */
+
f_add(val_cnt);
+
}
+
if((tmp = low_mapping_lookup(headers, Pike_sp-2))) {
-
f_aggregate( 1 );
-
if( tmp
->type
== PIKE_T_ARRAY )
+
if
(
TYPEOF
(
*
tmp
)
== PIKE_T_ARRAY )
{
-
tmp->u.array->refs++
;
-
push_array(tmp->u.array);
+
f_aggregate( 1 )
;
+
ref_
push_array(tmp->u.array);
+
stack_swap();
map_delete(headers, Pike_sp-3); f_add(2); } else {
-
tmp->u.string->refs++;
-
push_string(tmp->u.string);
-
f
_
aggregate
(
1
);
+
ref_
push_string(tmp->u.string);
+
stack
_
swap
();
map_delete(headers, Pike_sp-3);
-
f_
add
(2);
+
f_
aggregate
(2);
} } mapping_insert(headers, Pike_sp-2, Pike_sp-1);
-
+
pop_n_elems(2);
-
if( in[j+1] == '\n' ) j++;
-
os = j+1;
-
i = j;
+
}
-
+
else if( in[i]=='\r' || in[i]=='\n' )
+
{
+
if( THP->mode == 1 )
+
{
+
/* FIXME: Reset stack so that backtrace shows faulty header. */
+
Pike_error("Malformed HTTP header.\n");
}
-
+
else
+
os = i+1;
+
}
+
}
push_mapping( headers );
-
f_aggregate( 3 ); /* data, headers */
+
f_aggregate( 3 ); /* data,
firstline,
headers */
} static void f_hp_create( INT32 args )
-
+
/*! @decl void create(int throw_errors)
+
*/
{
-
+
if (THP->headers) {
+
free(THP->headers);
+
THP->headers = NULL;
+
}
+
+
THP->mode = 0;
+
get_all_args("create",args,".%i",&THP->mode);
+
+
THP->headers = xalloc( 8192 );
THP->pnt = THP->headers;
-
+
THP->hsize = 8192;
THP->left = 8192;
-
+
THP->spc = THP->slash_n = 0;
+
pop_n_elems(args);
+
push_int(0);
}
-
/**** END CLASS HeaderParser */
+
-
+
/*! @endclass
+
*/
+
static void f_make_http_headers( INT32 args )
-
+
/*! @decl string @
+
*! make_http_headers(mapping(string:string|array(string)) headers, @
+
*! int(0..1)|void no_terminator)
+
*/
{ int total_len = 0, e;
-
char *pnt;
+
unsigned
char *pnt;
struct mapping *m; struct keypair *k; struct pike_string *res;
-
if( Pike_sp[-
1
]
.type
!= PIKE_T_MAPPING )
+
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;
-
m
=
Pike_sp[-1].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
;
+
}
+
/* loop to check len */ NEW_MAPPING_LOOP( m->data ) {
-
if( k->ind
.type
!= PIKE_T_STRING || k->ind.u.string->size_shift )
+
if(
TYPEOF(
k->ind
)
!= PIKE_T_STRING || k->ind.u.string->size_shift )
Pike_error("Wrong argument type to make_http_headers(" "mapping(string(8bit):string(8bit)|array(string(8bit))) heads)\n");
-
if( k->val
.type
== PIKE_T_STRING )
+
if(
TYPEOF(
k->val
)
== PIKE_T_STRING
&& !k->val.u.string->size_shift
)
total_len += k->val.u.string->len + 2 + k->ind.u.string->len + 2;
-
else if( k->val
.type
== PIKE_T_ARRAY )
+
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( a->item[i]
.type
!= PIKE_T_STRING||a->item[i].u.string->size_shift )
+
if(
TYPEOF(
a->item[i]
)
!= PIKE_T_STRING
||
+
a->item[i].u.string->size_shift )
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 +=
2
;
+
total_len +=
terminator
;
res = begin_shared_string( total_len );
-
pnt = (
char *)
res
->str
;
-
#define STRADD(X)
\
-
for( l=X.u.string->len,s=X.u.string
->str
,c=0; c<l; c++ )
\
-
*(pnt++)=*(s++)
;
+
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 ) {
-
char *s;
+
unsigned
char *s;
ptrdiff_t l, c;
-
if( k->val
.type
== PIKE_T_STRING )
+
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 void f_http_decode_string(INT32 args)
-
+
/*! @decl string http_decode_string(string encoded)
+
*!
+
*! Decodes an http transport-encoded string. Knows about %XX and
+
*! %uXXXX syntax. Treats %UXXXX as %uXXXX. It will treat '+' as '+'
+
*! and not ' ', so form decoding needs to replace that in a second
+
*! step.
+
*/
{ int proc;
-
char
*foo,*bar,*end;
+
int
size_shift = 0;
+
int adjust_len = 0;
+
p_wchar0
*foo,
*bar,
*end;
struct pike_string *newstr;
-
if (!args || Pike_sp[-args]
.type
!= PIKE_T_STRING)
-
Pike_error("Invalid argument to http_decode_string(
STRING
);\n");
+
if (!args ||
TYPEOF(
Pike_sp[-args]
)
!= PIKE_T_STRING
||
+
Pike_sp[-args].u.string->size_shift
)
+
Pike_error("Invalid argument to http_decode_string(
string(8bit
)
)
;\n");
-
foo=bar=Pike_sp[-args].u.string
->str
;
-
end=
foo+Pike
_sp[-args].u.string->len;
+
foo
=
bar
=
STR0(
Pike_sp[-args].u.string
)
;
+
end
=
foo + Pike
_sp[-args].u.string->len;
-
/* count '%' characters */
-
for (proc=0; foo<end; ) if (*foo=='%') { proc++;
foo+
=3; } else
foo++
;
+
/* count '%'
and wide
characters */
+
for (proc=0; foo<end;
foo++
)
{
+
if (*foo=='%') {
+
proc++;
+
if (foo[1]
=
= 'u' || foo[1] == 'U') {
+
/* %uXXXX */
+
if (foo[2] != '0' || foo[
3
] != '0') {
+
size_shift = 1
;
+
}
+
foo += 5;
+
if (foo < end) {
+
adjust_len += 5;
+
}
else
{
+
adjust_len += end - (foo - 4)
;
+
}
+
} else {
+
foo += 2;
+
if (foo < end) {
+
adjust_len += 2;
+
} else {
+
adjust_len += end - (foo - 1);
+
}
+
}
+
}
+
}
if (!proc) { pop_n_elems(args-1); return; }
-
/*
new
string len
is
(
foo-
bar)-
proc
*2
*/
-
newstr=begin_shared_string
((
foo-
bar)-
proc
*2);
-
foo=newstr
->str
;
+
newstr
=
begin_wide_shared_
string
(Pike_sp[-args].u.string->len
- adjust_
len
,
+
size_shift);
+
if
(
size_shift) {
+
p_wchar1 *dest = STR1(newstr);
+
+
for (proc=0;
bar
<end; dest++
)
+
if (*bar=='%') {
+
if (bar[1] == 'u' || bar[1] == 'U') {
+
if (bar<end
-
5)
+
*
dest = (((bar[
2
]<'A')?(bar[2]&15):((bar[2]+9)&15))<<12)|
+
(((bar[3]<'A')?(bar[3]&15):((bar[3]+9)&15))<<8)|
+
(((bar[4]<'A')?(bar[4]&15):((bar[4]+9)&15))<<4)|
+
((bar[5]<'A')?(bar[5]&15):((bar[5]+9)&15));
+
else
+
*
dest=0;
+
bar+
=
6;
+
} else {
+
if
(
bar<end-2)
+
*dest=
(
((
bar
[1]<'A'
)
?(bar[1]&15):((bar[1]+9)&15))<<4)|
+
((bar[
2
]<'A'
)
?(bar[2]&15):((bar[2]+9)&15))
;
+
else
+
*dest=0;
+
bar+=3;
+
}
+
} else {
+
*dest=*(bar++);
+
}
+
} else {
+
foo
=
STR0(
newstr
)
;
for (proc=0; bar<end; foo++)
-
if (*bar=='%')
-
{
+
if (*bar=='%')
{
+
if (bar[1] == 'u' || bar[1] == 'U')
{
+
/* We know that the following two characters are zeros. */
+
bar+=3;
+
}
if (bar<end-2) *foo=(((bar[1]<'A')?(bar[1]&15):((bar[1]+9)&15))<<4)| ((bar[2]<'A')?(bar[2]&15):((bar[2]+9)&15)); else *foo=0; bar+=3;
-
+
} else {
+
*foo=*(bar++);
}
-
else { *foo=*(bar++);
}
+
}
pop_n_elems(args); push_string(end_shared_string(newstr)); }
-
+
static void f_html_encode_string( INT32 args )
+
/*! @decl string html_encode_string(mixed in)
+
*!
+
*! Encodes the @[in] data as an HTML safe string.
+
*/
+
{
+
struct pike_string *str;
+
int newlen;
-
void
pike
_
module
_
init
()
+
if( args != 1 )
+
Pike
_
error("Wrong number of arguments to html
_
encode_string\n" );
+
+
switch
(
TYPEOF(Pike_sp[-1]
)
)
{
-
pike_add_function("make_http_headers",
f
_
make
_
http
_
headers,
-
"function(mapping(
string
:string|array
(
string
)
):string)", 0 )
;
+
void o
_
cast
_
to
_string();
-
pike
_
add
_
function("http_decode_string",
f
_
http
_
decode_string
,
-
"function(
string
:string
)
",
0
)
;
+
case PIKE
_
T
_
INT:
+
case PIKE
_
T
_
FLOAT:
+
/* Optimization
,
no need to check the resultstring for
+
*
unsafe
characters.
+
*/
+
o_cast_to_string
()
;
+
return
;
-
+
default:
+
o_cast_to_string();
+
case PIKE_T_STRING:
+
break;
+
}
+
+
str = Pike_sp[-1].u.string;
+
newlen = str->len;
+
+
#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; /* Already on stack. */
+
+
{
+
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 ) );
+
}
+
}
+
+
/*! @endmodule
+
*/
+
+
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 );
-
pike
_
add
_
function
( "feed", f_hp_feed,
"function
(
string:array
(
string|mapping
))
"
,0 );
-
pike
_
add_function
( "create", f_hp_create,
"function
(
void:void
)
"
,
0
);
+
set
_
init
_
callback
(
f_hp_init );
+
set_exit_callback( f_hp_exit );
+
ADD_FUNCTION(
"feed", f_hp_feed,
tFunc
(
tStr,tArr
(
tOr(tStr,tMapping
))
)
,
0 );
+
ADD
_
FUNCTION
( "create", f_hp_create,
tFunc
(
tOr(tInt,tVoid
),
tVoid),
ID_PROTECTED
);
end_class( "HeaderParser", 0 ); }
-
void pike
_
module
_
exit()
+
PIKE
_
MODULE
_
EXIT
{ }