/* $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 #endif #include #include #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include "form.h" #ifdef __WINDOWS__ #include #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); } }