clib package
This commit is contained in:
395
packages/clib/form.c
Normal file
395
packages/clib/form.c
Normal file
@@ -0,0 +1,395 @@
|
||||
/* $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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user