396 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			396 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*  $Id$
 | 
						|
 | 
						|
    Part of SWI-Prolog
 | 
						|
 | 
						|
    Author:        Jan Wielemaker
 | 
						|
    E-mail:        wielemak@science.uva.nl
 | 
						|
    WWW:           http://www.swi-prolog.org
 | 
						|
    Copyright (C): 1985-2007, University of Amsterdam
 | 
						|
 | 
						|
    This library is free software; you can redistribute it and/or
 | 
						|
    modify it under the terms of the GNU Lesser General Public
 | 
						|
    License as published by the Free Software Foundation; either
 | 
						|
    version 2.1 of the License, or (at your option) any later version.
 | 
						|
 | 
						|
    This library is distributed in the hope that it will be useful,
 | 
						|
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
    Lesser General Public License for more details.
 | 
						|
 | 
						|
    You should have received a copy of the GNU Lesser General Public
 | 
						|
    License along with this library; if not, write to the Free Software
 | 
						|
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
						|
*/
 | 
						|
 | 
						|
#ifdef HAVE_CONFIG_H
 | 
						|
#include <config.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#include <SWI-Prolog.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <sys/types.h>
 | 
						|
#include <ctype.h>
 | 
						|
#include <errno.h>
 | 
						|
#ifdef HAVE_UNISTD_H
 | 
						|
#include <unistd.h>
 | 
						|
#endif
 | 
						|
#include <string.h>
 | 
						|
#include <assert.h>
 | 
						|
#include "form.h"
 | 
						|
#ifdef __WINDOWS__
 | 
						|
#include <io.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#include "error.h"
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
						|
Breaks a string holding data from a WWW form into its values.  Outputs a
 | 
						|
sequence of NAME=VALUE commands for a shell.
 | 
						|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
static int
 | 
						|
dehex(int chr)
 | 
						|
{ chr &= 0xff;
 | 
						|
 | 
						|
  if ( chr >= '0' && chr <= '9' )
 | 
						|
    return chr - '0';
 | 
						|
  if ( chr >= 'A' && chr <= 'F' )
 | 
						|
    return chr - 'A' + 10;
 | 
						|
  if ( chr >= 'a' && chr <= 'f' )
 | 
						|
    return chr - 'a' + 10;
 | 
						|
 | 
						|
  return -1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static size_t
 | 
						|
form_argument_decode(const char *in, size_t inlen, char *out, size_t outlen)
 | 
						|
{ const char *ein  = in+inlen;
 | 
						|
  size_t written = 0;
 | 
						|
 | 
						|
  for(; in < ein; in++)
 | 
						|
  { switch(*in)
 | 
						|
    { case '+':
 | 
						|
	if ( ++written < outlen )
 | 
						|
	  *out++ = ' ';
 | 
						|
        break;
 | 
						|
      case '%':
 | 
						|
	if ( in+2 < ein )
 | 
						|
	{ int h1 = dehex(*(++in));
 | 
						|
	  int h2 = dehex(*(++in));
 | 
						|
 | 
						|
	  if ( h1 < 0 || h2 < 0 )
 | 
						|
	    return (size_t)-1;
 | 
						|
 | 
						|
	  if ( ++written < outlen )
 | 
						|
	    *out++ = h1<<4|h2;
 | 
						|
	} else
 | 
						|
	  return (size_t)-1;		/* syntax error */
 | 
						|
	break;
 | 
						|
      default:
 | 
						|
	if ( ++written < outlen )
 | 
						|
	  *out++ = *in;
 | 
						|
        break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if ( written < outlen )
 | 
						|
    *out++ = '\0';
 | 
						|
 | 
						|
  return written;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#define SHORTVALUE 512
 | 
						|
 | 
						|
int
 | 
						|
break_form_argument(const char *formdata,
 | 
						|
		    int (*func)(const char* name,
 | 
						|
				size_t namelen,
 | 
						|
				const char *value,
 | 
						|
				size_t valuelen,
 | 
						|
				void *closure),
 | 
						|
		    void *closure)
 | 
						|
{ while ( *formdata )
 | 
						|
  { char value[SHORTVALUE];
 | 
						|
    char *eq = strchr(formdata, '=');
 | 
						|
 | 
						|
    if ( eq )
 | 
						|
    { size_t nlen = eq-formdata;
 | 
						|
      char *end;
 | 
						|
      size_t vlen;
 | 
						|
 | 
						|
      eq++;
 | 
						|
      end = strchr(eq, '&');
 | 
						|
      if ( !end )
 | 
						|
	end = eq+strlen(eq);		/* end of the string */
 | 
						|
 | 
						|
      if ( (vlen=form_argument_decode(eq, end-eq, value, SHORTVALUE)) >= SHORTVALUE )
 | 
						|
      { char *buf;
 | 
						|
 | 
						|
	if ( (buf=malloc(vlen+1)) )
 | 
						|
	{ size_t vlen2 = form_argument_decode(eq, end-eq, buf, vlen+1);
 | 
						|
	  int rc;
 | 
						|
 | 
						|
	  assert(vlen2 == vlen);
 | 
						|
	  rc = (func)(formdata, nlen, buf, vlen2, closure);
 | 
						|
	  free(buf);
 | 
						|
 | 
						|
	  if ( !rc )
 | 
						|
	    return rc;
 | 
						|
	} else
 | 
						|
	  return ERROR_NOMEM;
 | 
						|
      } else if ( vlen == (size_t)-1 )
 | 
						|
      { return ERROR_SYNTAX_ERROR;
 | 
						|
      } else
 | 
						|
      { int rc = (func)(formdata, nlen, value, vlen, closure);
 | 
						|
 | 
						|
	if ( !rc )
 | 
						|
	  return rc;
 | 
						|
      }
 | 
						|
 | 
						|
      if ( *end )
 | 
						|
	formdata = end+1;
 | 
						|
      else
 | 
						|
	formdata = end;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static char *
 | 
						|
find_boundary(const char *data, const char *end, const char *boundary)
 | 
						|
{ size_t blen = strlen(boundary);
 | 
						|
 | 
						|
  while ( data < end &&
 | 
						|
	  !(strncmp(data, boundary, blen) == 0) )
 | 
						|
    data++;
 | 
						|
 | 
						|
  if ( data < end )
 | 
						|
  { while(data[-1] == '-')
 | 
						|
      data--;
 | 
						|
    return (char *)data;
 | 
						|
  }
 | 
						|
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
						|
Find a named attribute in a mime header of a multipart form
 | 
						|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
static char *
 | 
						|
attribute_of_multipart_header(const char *name, char *header, char *endheader)
 | 
						|
{ char *value;
 | 
						|
  size_t nlen = strlen(name);
 | 
						|
 | 
						|
  while( header < endheader &&
 | 
						|
	 !(header[nlen] == '=' && strncmp(header, name, nlen) == 0) )
 | 
						|
    header++;
 | 
						|
 | 
						|
  if ( header < endheader )
 | 
						|
  { header += nlen+1;
 | 
						|
 | 
						|
    if ( header[0] == '"' )
 | 
						|
    { char *end;
 | 
						|
 | 
						|
      value = ++header;
 | 
						|
      if ( (end = strchr(value, '"')) )
 | 
						|
      { *end = '\0';
 | 
						|
        return value;
 | 
						|
      }
 | 
						|
    } else
 | 
						|
    { char *end;
 | 
						|
 | 
						|
      value = header;
 | 
						|
 | 
						|
      for(end=header; *end && isalnum(*end&0xff); end++)
 | 
						|
	;
 | 
						|
      *end = '\0';
 | 
						|
      return value;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static char *
 | 
						|
looking_at_blank_lines(const char *line, int n)
 | 
						|
{ while(n-- > 0)
 | 
						|
  { if ( (line[0] == '\r' && line[1] == '\n') )
 | 
						|
      line += 2;
 | 
						|
    else if ( line[0] == '\n' )
 | 
						|
      line += 1;
 | 
						|
    else
 | 
						|
      return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  return (char *)line;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
char *
 | 
						|
next_line(const char *in)
 | 
						|
{ if ( (in = strchr(in, '\n')) )
 | 
						|
    return (char *)(in+1);
 | 
						|
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
break_multipart(char *formdata, size_t len,
 | 
						|
		const char *boundary,
 | 
						|
		int (*func)(const char *name,
 | 
						|
			    size_t namelen,
 | 
						|
			    const char *value,
 | 
						|
			    size_t valuelen,
 | 
						|
			    const char *filename,
 | 
						|
			    void *closure),
 | 
						|
		void *closure)
 | 
						|
{ char *enddata = formdata+len;
 | 
						|
 | 
						|
  while(formdata < enddata)
 | 
						|
  { char *header;
 | 
						|
    char *name, *filename;
 | 
						|
    char *data = NULL;
 | 
						|
    char *end;
 | 
						|
 | 
						|
    if ( !(formdata=find_boundary(formdata, enddata, boundary)) ||
 | 
						|
	 !(formdata=next_line(formdata)) )
 | 
						|
      break;
 | 
						|
 | 
						|
    header = formdata;
 | 
						|
					/* find the end of the header */
 | 
						|
    for( ; formdata < enddata; formdata++ )
 | 
						|
    { char *end;
 | 
						|
 | 
						|
      if ( (end = looking_at_blank_lines(formdata, 2)) )
 | 
						|
      { formdata[0] = '\0';
 | 
						|
	formdata = data = end;
 | 
						|
	break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    if ( !data )
 | 
						|
      break;
 | 
						|
 | 
						|
    if ( !(name = attribute_of_multipart_header("name", header, data)) )
 | 
						|
    { term_t t = PL_new_term_ref();
 | 
						|
      PL_put_atom_chars(t, "name");
 | 
						|
 | 
						|
      return pl_error(NULL, 0, NULL, ERR_EXISTENCE, "field", t);
 | 
						|
    }
 | 
						|
    filename = attribute_of_multipart_header("filename", header, data);
 | 
						|
 | 
						|
    if ( !(formdata=find_boundary(data, enddata, boundary)) )
 | 
						|
      break;
 | 
						|
    end = formdata-1;
 | 
						|
    if ( end[-1] == '\r' )
 | 
						|
      end--;
 | 
						|
    end[0] = '\0';
 | 
						|
 | 
						|
    if ( !(func)(name, strlen(name), data, end-data, filename, closure) )
 | 
						|
      return FALSE;
 | 
						|
  }
 | 
						|
 | 
						|
  return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
						|
Get the raw data from the standard   input or QUERY_STRING. If `lenp' is
 | 
						|
provided, it is filled with the length  of the contents. The input value
 | 
						|
for lenp is the maximum acceptable content-length.
 | 
						|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | 
						|
 | 
						|
int
 | 
						|
get_raw_form_data(char **data, size_t *lenp, int *must_free)
 | 
						|
{ char *method;
 | 
						|
  char *s;
 | 
						|
 | 
						|
  if ( (method = getenv("REQUEST_METHOD")) &&
 | 
						|
       strcmp(method, "POST") == 0 )
 | 
						|
  { char *lenvar = getenv("CONTENT_LENGTH");
 | 
						|
    char *q;
 | 
						|
    long len;
 | 
						|
 | 
						|
    if ( !lenvar )
 | 
						|
    { term_t env = PL_new_term_ref();
 | 
						|
      PL_put_atom_chars(env, "CONTENT_LENGTH");
 | 
						|
 | 
						|
      return pl_error(NULL, 0, NULL, ERR_EXISTENCE, "environment", env);
 | 
						|
    }
 | 
						|
    len = atol(lenvar);
 | 
						|
    if ( len < 0 )
 | 
						|
    { term_t t = PL_new_term_ref();
 | 
						|
 | 
						|
      if ( !PL_put_integer(t, len) )
 | 
						|
	return FALSE;
 | 
						|
      return pl_error(NULL, 0, "< 0", ERR_DOMAIN, t, "content_length");
 | 
						|
    }
 | 
						|
    if ( lenp )
 | 
						|
    { if ( *lenp && (size_t)len > *lenp )
 | 
						|
      { term_t t = PL_new_term_ref();
 | 
						|
	char msg[100];
 | 
						|
 | 
						|
	if ( !PL_put_integer(t, len) )
 | 
						|
	  return FALSE;
 | 
						|
	sprintf(msg, "> %ld", *lenp);
 | 
						|
 | 
						|
	return pl_error(NULL, 0, msg, ERR_DOMAIN, t, "content_length");
 | 
						|
      }
 | 
						|
      *lenp = len;
 | 
						|
    }
 | 
						|
 | 
						|
    q = s = malloc(len+1);
 | 
						|
    if ( !q )
 | 
						|
      return pl_error(NULL, 0, NULL, ERR_RESOURCE, "memory");
 | 
						|
    while(len > 0)
 | 
						|
    { int done;
 | 
						|
 | 
						|
      while( (done=read(fileno(stdin), q, len)) > 0 )
 | 
						|
      { q+=done;
 | 
						|
	len-=done;
 | 
						|
      }
 | 
						|
      if ( done < 0 )
 | 
						|
      { int e;
 | 
						|
	term_t obj;
 | 
						|
 | 
						|
      no_data:
 | 
						|
	e = errno;
 | 
						|
	obj = PL_new_term_ref();
 | 
						|
 | 
						|
	free(s);
 | 
						|
	PL_put_nil(obj);
 | 
						|
	return pl_error(NULL, 0, NULL, ERR_ERRNO, e, "read", "cgi_data", obj);
 | 
						|
      }
 | 
						|
    }
 | 
						|
    if ( len == 0 )
 | 
						|
    { *q = '\0';
 | 
						|
      *data = s;
 | 
						|
      *must_free = TRUE;
 | 
						|
      return TRUE;
 | 
						|
    } else
 | 
						|
      goto no_data;
 | 
						|
  } else if ( (s = getenv("QUERY_STRING")) )
 | 
						|
  { if ( lenp )
 | 
						|
      *lenp = strlen(s);
 | 
						|
    *data = s;
 | 
						|
    *must_free = FALSE;
 | 
						|
    return TRUE;
 | 
						|
  } else
 | 
						|
  { term_t env = PL_new_term_ref();
 | 
						|
    PL_put_atom_chars(env, "QUERY_STRING");
 | 
						|
 | 
						|
    return pl_error(NULL, 0, NULL, ERR_EXISTENCE, "environment", env);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 |