e576bb2002-10-11Martin Nilsson /* || 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. */
33805b2000-08-12Per Hedbor #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"
ced8a32001-02-13Henrik Grubbström (Grubba) /*! @module _Roxen */ /*! @class HeaderParser
4883852009-07-17Henrik Grubbström (Grubba)  *! *! Class for parsing HTTP-requests.
ced8a32001-02-13Henrik Grubbström (Grubba)  */
33805b2000-08-12Per Hedbor 
a979c32016-07-25Martin Nilsson #define FLAG_THROW_ERROR 1 #define FLAG_KEEP_CASE 2
38877f2016-07-27Martin Nilsson #define FLAG_NO_FOLD 4
a979c32016-07-25Martin Nilsson 
6b87882001-06-21Martin Stjernholm #define THP ((struct header_buf *)Pike_fp->current_storage)
33805b2000-08-12Per Hedbor struct header_buf {
7e11cd2016-07-29Henrik Grubbström (Grubba)  unsigned char *headers; /* Buffer containing the data so far. */ unsigned char *pnt; /* End of headers. */ ptrdiff_t hsize, left; /* Size of buffer, amount remaining. */
9529d42016-07-30Martin Nilsson  int slash_n, tslash_n, spc; /* Number of consecutive nl, nl, spaces. */
b675152009-07-29Martin Nilsson  int mode;
33805b2000-08-12Per Hedbor };
74dfe82012-12-30Jonas Walldén static void f_hp_init( struct object *UNUSED(o) )
e15d142002-10-14Henrik Grubbström (Grubba) { THP->headers = NULL; THP->pnt = NULL; THP->hsize = 0;
8f77192004-01-27Henrik Grubbström (Grubba)  THP->left = 0;
117d2c2005-11-28Per Hedbor  THP->spc = THP->slash_n = THP->tslash_n = 0;
b675152009-07-29Martin Nilsson  THP->mode = 0;
e15d142002-10-14Henrik Grubbström (Grubba) }
74dfe82012-12-30Jonas Walldén static void f_hp_exit( struct object *UNUSED(o) )
2d41d42001-10-02Per Hedbor { if( THP->headers ) free( THP->headers );
e15d142002-10-14Henrik Grubbström (Grubba)  THP->headers = NULL; THP->pnt = NULL; THP->hsize = 0;
2d41d42001-10-02Per Hedbor } static void f_hp_feed( INT32 args )
33ace42016-06-03Henrik Grubbström (Grubba) /*! @decl array(string|mapping) feed(string data, void|int(0..1) keep_case) *! *! @param data *! Fragment of data to parse. *! *! @param keep_case *! By default headers are @[lower_case()]'d in the resulting @tt{Headers@} *! mapping. If this parameter is @expr{1@} the header names are kept as is.
4883852009-07-17Henrik Grubbström (Grubba)  *! *! @returns *! @array *! @elem string 0 *! Trailing data. *! @elem string 1 *! First line of request. *! @elem mapping(string:string|array(string)) 2 *! Headers. *! @endarray
ced8a32001-02-13Henrik Grubbström (Grubba)  */
33805b2000-08-12Per Hedbor {
3d08b02016-05-30Grzegorz Malachowski  struct pike_string *str = Pike_sp[-args].u.string;
3c64ee2001-02-20Per Hedbor  struct header_buf *hp = THP;
a979c32016-07-25Martin Nilsson  int keep_case = hp->mode & FLAG_KEEP_CASE;
38877f2016-07-27Martin Nilsson  int fold = !(hp->mode & FLAG_NO_FOLD);
21dc262003-04-14Martin Stjernholm  int str_len;
935e742016-07-27Martin Nilsson  int tot_slash_n=hp->tslash_n, slash_n = hp->slash_n, spc = hp->spc;
fc03522005-04-08Henrik Grubbström (Grubba)  unsigned char *pp,*ep;
33805b2000-08-12Per Hedbor  struct svalue *tmp; struct mapping *headers;
aad5842000-08-12Henrik Grubbström (Grubba)  ptrdiff_t os=0, i, j, l;
33805b2000-08-12Per Hedbor  unsigned char *in;
3d08b02016-05-30Grzegorz Malachowski  if( !( args == 1 || args == 2 ) )
4883852009-07-17Henrik Grubbström (Grubba)  Pike_error("Bad number of arguments to feed().\n");
3d08b02016-05-30Grzegorz Malachowski  if( TYPEOF(Pike_sp[-args]) != PIKE_T_STRING )
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Wrong type of argument to feed()\n");
3d08b02016-05-30Grzegorz Malachowski  if( args == 2 )
c049672016-05-31Martin Nilsson  {
a979c32016-07-25Martin Nilsson  /* It doesn't make sense to allow each different feed call to handle case sensitivity differently. Deprecate this when we rewrite the HTTP module. */
3d08b02016-05-30Grzegorz Malachowski  if( TYPEOF(Pike_sp[-args+1]) == PIKE_T_INT )
a979c32016-07-25Martin Nilsson  keep_case = Pike_sp[-args+1].u.integer;
3d08b02016-05-30Grzegorz Malachowski  else Pike_error("Wrong type of argument to feed()\n");
c049672016-05-31Martin Nilsson  }
2d41d42001-10-02Per Hedbor  if( str->size_shift ) Pike_error("Wide string headers not supported\n");
21dc262003-04-14Martin Stjernholm  str_len = str->len; while( str_len >= hp->left )
2d41d42001-10-02Per Hedbor  {
fc03522005-04-08Henrik Grubbström (Grubba)  unsigned char *buf;
117d2c2005-11-28Per Hedbor  if( hp->hsize > 512 * 1024 )
2d41d42001-10-02Per Hedbor  Pike_error("Too many headers\n");
117d2c2005-11-28Per Hedbor  hp->hsize += 8192; buf = hp->headers; hp->headers = realloc( hp->headers, hp->hsize ); if( !hp->headers )
2d41d42001-10-02Per Hedbor  {
9e361c2002-02-14Henrik Grubbström (Grubba)  free(buf);
117d2c2005-11-28Per Hedbor  hp->hsize = 0; hp->left = 0; hp->spc = hp->slash_n = 0; hp->pnt = NULL;
2d41d42001-10-02Per Hedbor  Pike_error("Running out of memory in header parser\n"); }
117d2c2005-11-28Per Hedbor  hp->left += 8192; hp->pnt = (hp->headers + hp->hsize - hp->left);
2d41d42001-10-02Per Hedbor  }
33805b2000-08-12Per Hedbor 
59fc9e2014-09-03Martin Nilsson  memcpy( hp->pnt, str->str, str_len );
d5ea292001-02-01Per Hedbor  pop_n_elems( args );
9e361c2002-02-14Henrik Grubbström (Grubba)  /* FIXME: The below does not support lines terminated with just \r. */
935e742016-07-27Martin Nilsson  for( ep=(hp->pnt+str_len),pp=MAXIMUM(hp->headers,hp->pnt);
d5ea292001-02-01Per Hedbor  pp<ep && slash_n<2; pp++ )
13670c2015-05-25Martin Nilsson  if( *pp == ' ' )
117d2c2005-11-28Per Hedbor  { spc++; slash_n = 0; } else if( *pp == '\n' ) { slash_n++; tot_slash_n++; }
13670c2015-05-25Martin Nilsson  else if( *pp != '\r' )
117d2c2005-11-28Per Hedbor  { slash_n=0; }
33805b2000-08-12Per Hedbor 
935e742016-07-27Martin Nilsson  hp->slash_n = slash_n;
3c64ee2001-02-20Per Hedbor  hp->spc = spc;
935e742016-07-27Martin Nilsson  hp->tslash_n = tot_slash_n;
21dc262003-04-14Martin Stjernholm  hp->left -= str_len; hp->pnt += str_len;
3c64ee2001-02-20Per Hedbor  hp->pnt[0] = 0;
d5ea292001-02-01Per Hedbor 
33805b2000-08-12Per Hedbor  if( slash_n != 2 ) {
7e11cd2016-07-29Henrik Grubbström (Grubba)  /* No header terminating double newline. */
d5ea292001-02-01Per Hedbor  if( (spc < 2) && tot_slash_n ) {
7e11cd2016-07-29Henrik Grubbström (Grubba)  /* one newline, but less than 2 space, * --> HTTP/0.9 or broken request */
7863d62005-05-06Martin Nilsson  push_empty_string();
d5ea292001-02-01Per Hedbor  /* This includes (all eventual) \r\n etc. */
7e11cd2016-07-29Henrik Grubbström (Grubba)  push_string(make_shared_binary_string((char *)hp->headers, hp->hsize - hp->left));
d5ea292001-02-01Per Hedbor  f_aggregate_mapping( 0 ); f_aggregate( 3 ); return; }
33805b2000-08-12Per Hedbor  push_int( 0 ); return; }
13670c2015-05-25Martin Nilsson 
571b7c2005-04-09Henrik Grubbström (Grubba)  /*leftovers*/ push_string(make_shared_binary_string((char *)pp, hp->pnt - pp));
33805b2000-08-12Per Hedbor  headers = allocate_mapping( 5 );
3c64ee2001-02-20Per Hedbor  in = hp->headers; l = pp - hp->headers;
4a84492000-08-13David Hedbor 
33805b2000-08-12Per Hedbor  /* find first line here */ for( i = 0; i < l; i++ )
117d2c2005-11-28Per Hedbor  if( (in[i] == '\n') || (in[i] == '\r') )
33805b2000-08-12Per Hedbor  break;
571b7c2005-04-09Henrik Grubbström (Grubba)  push_string(make_shared_binary_string((char *)in, i));
9e361c2002-02-14Henrik Grubbström (Grubba)  if((in[i] == '\r') && (in[i+1] == '\n'))
33805b2000-08-12Per Hedbor  i++;
9e361c2002-02-14Henrik Grubbström (Grubba)  i++;
33805b2000-08-12Per Hedbor  in += i; l -= i;
9e361c2002-02-14Henrik Grubbström (Grubba)  /* Parse headers. */
33805b2000-08-12Per Hedbor  for(i = 0; i < l; i++) {
33ace42016-06-03Henrik Grubbström (Grubba)  if(!keep_case && (in[i] > 64 && in[i] < 91))
3d08b02016-05-30Grzegorz Malachowski  {
33ace42016-06-03Henrik Grubbström (Grubba)  in[i]+=32; /* lower_case */
3d08b02016-05-30Grzegorz Malachowski  }
4a84492000-08-13David Hedbor  else if( in[i] == ':' )
33805b2000-08-12Per Hedbor  {
1496722016-07-27Martin Nilsson  /* Does not support white space before the colon. This is in line with RFC 7230 product header-field = field-name ":" OWS field-value OWS */
33805b2000-08-12Per Hedbor  /* in[os..i-1] == the header */
9e361c2002-02-14Henrik Grubbström (Grubba)  int val_cnt = 0;
33805b2000-08-12Per Hedbor  push_string(make_shared_binary_string((char*)in+os,i-os));
d5ea292001-02-01Per Hedbor 
9e361c2002-02-14Henrik Grubbström (Grubba)  /* Skip the colon and initial white space. */ os = i+1; 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')
13670c2015-05-25Martin Nilsson  break;
9e361c2002-02-14Henrik Grubbström (Grubba) 
1496722016-07-27Martin Nilsson  /* FIXME: Remove header value trailing spaces. */
9e361c2002-02-14Henrik Grubbström (Grubba)  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. */
38877f2016-07-27Martin Nilsson  } while ((os < l) && fold && ((in[os] == ' ') || (in[os] == '\t')));
9e361c2002-02-14Henrik Grubbström (Grubba)  if (val_cnt > 1) { /* Join partial header values. */ f_add(val_cnt); }
33805b2000-08-12Per Hedbor  if((tmp = low_mapping_lookup(headers, Pike_sp-2))) {
017b572011-10-28Henrik Grubbström (Grubba)  if( TYPEOF(*tmp) == PIKE_T_ARRAY )
9e361c2002-02-14Henrik Grubbström (Grubba)  {
11b8af2006-01-02Martin Nilsson  f_aggregate( 1 );
50ea682003-03-14Henrik Grubbström (Grubba)  ref_push_array(tmp->u.array);
f540ce2006-11-10Per Hedbor  stack_swap();
9e361c2002-02-14Henrik Grubbström (Grubba)  map_delete(headers, Pike_sp-3); f_add(2); } else {
50ea682003-03-14Henrik Grubbström (Grubba)  ref_push_string(tmp->u.string);
13670c2015-05-25Martin Nilsson  stack_swap();
9e361c2002-02-14Henrik Grubbström (Grubba)  map_delete(headers, Pike_sp-3);
1b25eb2006-01-02Martin Nilsson  f_aggregate(2);
9e361c2002-02-14Henrik Grubbström (Grubba)  }
33805b2000-08-12Per Hedbor  } mapping_insert(headers, Pike_sp-2, Pike_sp-1);
9e361c2002-02-14Henrik Grubbström (Grubba) 
33805b2000-08-12Per Hedbor  pop_n_elems(2); }
b675152009-07-29Martin Nilsson  else if( in[i]=='\r' || in[i]=='\n' ) {
a979c32016-07-25Martin Nilsson  if( THP->mode & FLAG_THROW_ERROR )
b675152009-07-29Martin Nilsson  { /* FIXME: Reset stack so that backtrace shows faulty header. */ Pike_error("Malformed HTTP header.\n"); } else os = i+1; }
33805b2000-08-12Per Hedbor  } push_mapping( headers );
d5ea292001-02-01Per Hedbor  f_aggregate( 3 ); /* data, firstline, headers */
33805b2000-08-12Per Hedbor }
2d41d42001-10-02Per Hedbor static void f_hp_create( INT32 args )
0ff8d22016-08-02Martin Nilsson /*! @decl void create(void|int throw_errors, void|int keep_case, @ *! void|int no_fold) *! *! @param throw_errors *! If true the parser will throw an error instead of silently *! trying to recover from broken HTTP headers. *! *! @param keep_case *! If true the parser will not normalize the case of HTTP headers *! throughout the lifetime of the parser object (as opposed to the *! similar argument to @[feed] that only applies for unparsed data *! fed into the parser up to that point). *! *! @param no_fold *! If true the parser will not parse folded headers. Instead the *! first line of the header will be parsed as normal and any *! subsequent lines ignored (or casue an exception if throw_errors *! is set).
ced8a32001-02-13Henrik Grubbström (Grubba)  */
33805b2000-08-12Per Hedbor {
a979c32016-07-25Martin Nilsson  INT_TYPE throw_errors = 0; INT_TYPE keep_case = 0;
38877f2016-07-27Martin Nilsson  INT_TYPE no_fold = 0; get_all_args("create",args,".%i%i%i", &throw_errors, &keep_case, &no_fold);
a979c32016-07-25Martin Nilsson 
e15d142002-10-14Henrik Grubbström (Grubba)  if (THP->headers) { free(THP->headers); THP->headers = NULL; }
b675152009-07-29Martin Nilsson 
ddc1a32010-07-27Martin Stjernholm  THP->mode = 0;
a979c32016-07-25Martin Nilsson  if(throw_errors) THP->mode |= FLAG_THROW_ERROR; if(keep_case) THP->mode |= FLAG_KEEP_CASE;
38877f2016-07-27Martin Nilsson  if(no_fold) THP->mode |= FLAG_NO_FOLD;
b675152009-07-29Martin Nilsson 
e15d142002-10-14Henrik Grubbström (Grubba)  THP->headers = xalloc( 8192 );
33805b2000-08-12Per Hedbor  THP->pnt = THP->headers;
2d41d42001-10-02Per Hedbor  THP->hsize = 8192;
33805b2000-08-12Per Hedbor  THP->left = 8192;
3c64ee2001-02-20Per Hedbor  THP->spc = THP->slash_n = 0;
33805b2000-08-12Per Hedbor }
4183032001-10-03Martin Nilsson /*! @endclass */
936dd92016-06-29Henrik Grubbström (Grubba) 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 == ':')) { // The header formatting should not be broken by strange header names. 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')) { // The header formatting should not be broken by strange header values. return 0; } } return 1; }
2d41d42001-10-02Per Hedbor static void f_make_http_headers( INT32 args )
ced8a32001-02-13Henrik Grubbström (Grubba) /*! @decl string @
d7a5342005-10-27Henrik Grubbström (Grubba)  *! make_http_headers(mapping(string:string|array(string)) headers, @ *! int(0..1)|void no_terminator)
ced8a32001-02-13Henrik Grubbström (Grubba)  */
33805b2000-08-12Per Hedbor { int total_len = 0, e;
fc03522005-04-08Henrik Grubbström (Grubba)  unsigned char *pnt;
33805b2000-08-12Per Hedbor  struct mapping *m; struct keypair *k; struct pike_string *res;
d7a5342005-10-27Henrik Grubbström (Grubba)  int terminator = 2;
017b572011-10-28Henrik Grubbström (Grubba)  if( TYPEOF(Pike_sp[-args]) != PIKE_T_MAPPING )
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Wrong argument type to make_http_headers(mapping heads)\n");
d7a5342005-10-27Henrik Grubbström (Grubba)  m = Pike_sp[-args].u.mapping; if (args > 1) {
017b572011-10-28Henrik Grubbström (Grubba)  if (TYPEOF(Pike_sp[1-args]) != PIKE_T_INT)
d7a5342005-10-27Henrik Grubbström (Grubba)  Pike_error("Bad argument 2 to make_http_headers(). Expected int.\n"); if (Pike_sp[1-args].u.integer) terminator = 0; }
33805b2000-08-12Per Hedbor  /* loop to check len */ NEW_MAPPING_LOOP( m->data ) {
936dd92016-06-29Henrik Grubbström (Grubba)  if( TYPEOF(k->ind) != PIKE_T_STRING || !valid_header_name(k->ind.u.string) )
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Wrong argument type to make_http_headers("
33805b2000-08-12Per Hedbor  "mapping(string(8bit):string(8bit)|array(string(8bit))) heads)\n");
936dd92016-06-29Henrik Grubbström (Grubba)  if( TYPEOF(k->val) == PIKE_T_STRING && valid_header_value(k->val.u.string) )
33805b2000-08-12Per Hedbor  total_len += k->val.u.string->len + 2 + k->ind.u.string->len + 2;
017b572011-10-28Henrik Grubbström (Grubba)  else if( TYPEOF(k->val) == PIKE_T_ARRAY )
33805b2000-08-12Per Hedbor  { struct array *a = k->val.u.array;
aad5842000-08-12Henrik Grubbström (Grubba)  ptrdiff_t i, kl = k->ind.u.string->len + 2 ;
33805b2000-08-12Per Hedbor  for( i = 0; i<a->size; i++ )
017b572011-10-28Henrik Grubbström (Grubba)  if( TYPEOF(a->item[i]) != PIKE_T_STRING ||
936dd92016-06-29Henrik Grubbström (Grubba)  !valid_header_value(a->item[i].u.string) )
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Wrong argument type to make_http_headers("
33805b2000-08-12Per Hedbor  "mapping(string(8bit):string(8bit)|" "array(string(8bit))) heads)\n"); else total_len += kl + a->item[i].u.string->len + 2; } else
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Wrong argument type to make_http_headers("
33805b2000-08-12Per Hedbor  "mapping(string(8bit):string(8bit)|" "array(string(8bit))) heads)\n"); }
d7a5342005-10-27Henrik Grubbström (Grubba)  total_len += terminator;
33805b2000-08-12Per Hedbor  res = begin_shared_string( total_len );
fc03522005-04-08Henrik Grubbström (Grubba)  pnt = STR0(res);
571b7c2005-04-09Henrik Grubbström (Grubba) #define STRADD(X) \ for( l=(X).u.string->len, s=STR0((X).u.string), c=0; c<l; c++ ) \ *(pnt++)=*(s++)
33805b2000-08-12Per Hedbor  NEW_MAPPING_LOOP( m->data ) {
fc03522005-04-08Henrik Grubbström (Grubba)  unsigned char *s;
aad5842000-08-12Henrik Grubbström (Grubba)  ptrdiff_t l, c;
017b572011-10-28Henrik Grubbström (Grubba)  if( TYPEOF(k->val) == PIKE_T_STRING )
33805b2000-08-12Per Hedbor  { STRADD( k->ind ); *(pnt++) = ':'; *(pnt++) = ' '; STRADD( k->val ); *(pnt++) = '\r'; *(pnt++) = '\n'; } else { struct array *a = k->val.u.array;
aad5842000-08-12Henrik Grubbström (Grubba)  ptrdiff_t i, kl = k->ind.u.string->len + 2;
33805b2000-08-12Per Hedbor  for( i = 0; i<a->size; i++ ) { STRADD( k->ind ); *(pnt++) = ':'; *(pnt++) = ' '; STRADD( a->item[i] );*(pnt++) = '\r';*(pnt++) = '\n'; } } }
d7a5342005-10-27Henrik Grubbström (Grubba)  if (terminator) { *(pnt++) = '\r'; *(pnt++) = '\n'; }
33805b2000-08-12Per Hedbor  pop_n_elems( args ); push_string( end_shared_string( res ) ); }
4865642014-10-31Martin Nilsson static p_wchar2 parse_hexchar(p_wchar2 hex) { if(hex>='0' && hex<='9') return hex-'0'; hex |= 32;
95e9662014-10-31Arne Goedeke  return hex-'W';
4865642014-10-31Martin Nilsson }
2d41d42001-10-02Per Hedbor static void f_http_decode_string(INT32 args)
ced8a32001-02-13Henrik Grubbström (Grubba) /*! @decl string http_decode_string(string encoded) *!
d13a212016-03-02Henrik Grubbström (Grubba)  *! Decodes an http transport-encoded string. Knows about @tt{%XX@} and *! @tt{%uXXXX@} syntax. Treats @tt{%UXXXX@} as @tt{%uXXXX@}. It will *! treat '+' as '+' and not ' ', so form decoding needs to replace that *! in a second step. *! *! It also knows about UTF-16 surrogate pairs when decoding @tt{%UXXXX@} *! sequences.
ced8a32001-02-13Henrik Grubbström (Grubba)  */
33805b2000-08-12Per Hedbor {
380a152014-10-08Henrik Grubbström (Grubba)  int proc = 0; int size_shift;
d13a212016-03-02Henrik Grubbström (Grubba)  int got_surrogates = 0;
380a152014-10-08Henrik Grubbström (Grubba)  PCHARP foo, end; struct string_builder newstr;
e8bce62014-10-08Henrik Grubbström (Grubba)  if (!args || TYPEOF(Pike_sp[-args]) != PIKE_T_STRING)
380a152014-10-08Henrik Grubbström (Grubba)  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; /* Count '%' and wide characters. * * proc counts the number of characters that are to be removed. */
6812792014-10-16Arne Goedeke  for (; COMPARE_PCHARP(foo, <, end);) { p_wchar2 c = EXTRACT_PCHARP(foo); INC_PCHARP(foo, 1); if (c != '%') continue; /* there are at least 2 more characters */ 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");
95e9662014-10-31Arne Goedeke  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");
6812792014-10-16Arne Goedeke  /* %uXXXX */ if (EXTRACT_PCHARP(foo) != '0' || INDEX_PCHARP(foo, 1) != '0') { if (!size_shift) size_shift = 1;
8f77192004-01-27Henrik Grubbström (Grubba)  }
6812792014-10-16Arne Goedeke  proc += 5;
95e9662014-10-31Arne Goedeke  INC_PCHARP(foo, 4);
6812792014-10-16Arne Goedeke  } else {
95e9662014-10-31Arne Goedeke  if (!isxdigit(INDEX_PCHARP(foo, 0)) || !isxdigit(INDEX_PCHARP(foo, 1))) Pike_error("Illegal transport encoding.\n");
6812792014-10-16Arne Goedeke  proc += 2; INC_PCHARP(foo, 2);
8f77192004-01-27Henrik Grubbström (Grubba)  } }
33805b2000-08-12Per Hedbor  if (!proc) { pop_n_elems(args-1); return; }
380a152014-10-08Henrik Grubbström (Grubba)  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);
95e9662014-10-31Arne Goedeke  /* The above loop checks that the following sequences * are correct, i.e. that they are not truncated and consist * of hexadecimal chars. */
380a152014-10-08Henrik Grubbström (Grubba)  if (c == 'u' || c == 'U') {
95e9662014-10-31Arne Goedeke  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);
380a152014-10-08Henrik Grubbström (Grubba)  INC_PCHARP(foo, 5);
d13a212016-03-02Henrik Grubbström (Grubba)  if ((c & 0xf800) == 0xd800) { got_surrogates = 1; }
8f77192004-01-27Henrik Grubbström (Grubba)  } else {
95e9662014-10-31Arne Goedeke  p_wchar2 hex = INDEX_PCHARP(foo, 1); c = parse_hexchar(hex)<<4; hex = INDEX_PCHARP(foo, 2); c |= parse_hexchar(hex);
380a152014-10-08Henrik Grubbström (Grubba)  INC_PCHARP(foo, 2);
8f77192004-01-27Henrik Grubbström (Grubba)  }
380a152014-10-08Henrik Grubbström (Grubba)  } string_builder_putchar(&newstr, c);
8f77192004-01-27Henrik Grubbström (Grubba)  }
380a152014-10-08Henrik Grubbström (Grubba) 
33805b2000-08-12Per Hedbor  pop_n_elems(args);
d13a212016-03-02Henrik Grubbström (Grubba)  if (got_surrogates) { /* Convert the result string to a byte string. */ newstr.s->size_shift = 0; newstr.known_shift = 0; newstr.s->len <<= 1; /* Then run unicode_to_string() in native byte-order. */ push_string(finish_string_builder(&newstr)); push_int(2); f_unicode_to_string(2); } else { push_string(finish_string_builder(&newstr)); }
33805b2000-08-12Per Hedbor }
77ef812001-06-30Per Hedbor static void f_html_encode_string( INT32 args )
d579c82001-12-08Martin Nilsson /*! @decl string html_encode_string(mixed in) *! *! Encodes the @[in] data as an HTML safe string. */
77ef812001-06-30Per Hedbor { struct pike_string *str; int newlen;
0514f32014-11-01Arne Goedeke  INT32 min;
77ef812001-06-30Per Hedbor  if( args != 1 ) Pike_error("Wrong number of arguments to html_encode_string\n" );
13670c2015-05-25Martin Nilsson 
017b572011-10-28Henrik Grubbström (Grubba)  switch( TYPEOF(Pike_sp[-1]) )
77ef812001-06-30Per Hedbor  {
2a33f02002-07-02Per Hedbor  void o_cast_to_string();
77ef812001-06-30Per Hedbor  case PIKE_T_INT: case PIKE_T_FLOAT: /* Optimization, no need to check the resultstring for
13670c2015-05-25Martin Nilsson  * unsafe characters.
77ef812001-06-30Per Hedbor  */
2a33f02002-07-02Per Hedbor  o_cast_to_string();
77ef812001-06-30Per Hedbor  return; default:
2a33f02002-07-02Per Hedbor  o_cast_to_string();
77ef812001-06-30Per Hedbor  case PIKE_T_STRING: break; } str = Pike_sp[-1].u.string; newlen = str->len;
0514f32014-11-01Arne Goedeke  check_string_range(str, 1, &min, NULL); if (min > '>') return;
77ef812001-06-30Per Hedbor #define COUNT(T) { \ T *s = (T *)str->str; \ int i; \ for( i = 0; i<str->len; i++ ) \ switch( s[i] ) \ { \
8f77192004-01-27Henrik Grubbström (Grubba)  case 0: /* &#0; */ \ case '<': /* &lt; */ \
77ef812001-06-30Per Hedbor  case '>': newlen+=3; break;/* &gt; */ \
8f77192004-01-27Henrik Grubbström (Grubba)  case '&': /* &amp; */ \ case '"': /* &#34; */ \
77ef812001-06-30Per Hedbor  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 ) {
8f77192004-01-27Henrik Grubbström (Grubba)  case 0: COUNT(p_wchar0); break; case 1: COUNT(p_wchar1); break; case 2: COUNT(p_wchar2); break;
77ef812001-06-30Per Hedbor  } if( newlen == str->len ) return; /* Already on stack. */ { struct pike_string *res = begin_wide_shared_string(newlen,str->size_shift); switch( str->size_shift ) {
8f77192004-01-27Henrik Grubbström (Grubba)  case 0: REPLACE(p_wchar0); break; case 1: REPLACE(p_wchar1); break; case 2: REPLACE(p_wchar2); break;
77ef812001-06-30Per Hedbor  } pop_stack(); push_string( low_end_shared_string( res ) ); } }
2d41d42001-10-02Per Hedbor 
ced8a32001-02-13Henrik Grubbström (Grubba) /*! @endmodule */
33805b2000-08-12Per Hedbor 
51ef5c2002-10-21Marcus Comstedt PIKE_MODULE_INIT
33805b2000-08-12Per Hedbor {
09959a2005-01-20Martin Nilsson  ADD_FUNCTION("make_http_headers", f_make_http_headers,
d7a5342005-10-27Henrik Grubbström (Grubba)  tFunc(tMap(tStr,tOr(tStr,tArr(tStr))) tOr(tInt01,tVoid), tStr), 0);
33805b2000-08-12Per Hedbor 
09959a2005-01-20Martin Nilsson  ADD_FUNCTION("http_decode_string", f_http_decode_string, tFunc(tStr,tStr), 0 );
33805b2000-08-12Per Hedbor 
09959a2005-01-20Martin Nilsson  ADD_FUNCTION("html_encode_string", f_html_encode_string, tFunc(tMix,tStr), 0 );
2d41d42001-10-02Per Hedbor 
33805b2000-08-12Per Hedbor  start_new_program(); ADD_STORAGE( struct header_buf );
18b0822002-10-14Henrik Grubbström (Grubba)  set_init_callback( f_hp_init );
2d41d42001-10-02Per Hedbor  set_exit_callback( f_hp_exit );
33ace42016-06-03Henrik Grubbström (Grubba)  ADD_FUNCTION("feed", f_hp_feed, tFunc(tStr tOr(tInt01,tVoid),tArr(tOr(tStr,tMapping))), 0);
38877f2016-07-27Martin Nilsson  ADD_FUNCTION( "create", f_hp_create, tFunc(tOr(tInt,tVoid) tOr(tInt,tVoid) tOr(tInt,tVoid),tVoid), ID_PROTECTED );
33805b2000-08-12Per Hedbor  end_class( "HeaderParser", 0 ); }
51ef5c2002-10-21Marcus Comstedt PIKE_MODULE_EXIT
33805b2000-08-12Per Hedbor { }