This repository has been archived on 2023-08-20. You can view files and clone it, but cannot push or open issues or pull requests.
yap-6.3/packages/clib/form.c

396 lines
8.5 KiB
C
Raw Normal View History

2010-06-17 00:40:25 +01:00
/* $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);
}
}