pike.git / src / modules / _Roxen / roxen.c

version» Context lines:

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: /* &#0; */ \ +  case '<': /* &lt; */ \ +  case '>': newlen+=3; break;/* &gt; */ \ +  case '&': /* &amp; */ \ +  case '"': /* &#34; */ \ +  case '\'': newlen+=4;break;/* &#39; */ \ +  } \ +  } +  + #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("&#0;"); break; \ +  case '&': ADD("&amp;"); break; \ +  case '<': ADD("&lt;"); break; \ +  case '>': ADD("&gt;"); break; \ +  case '"': ADD("&#34;"); break; \ +  case '\'':ADD("&#39;"); 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   {   }