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);
|
|
}
|
|
}
|
|
|
|
|