/***************************************************************************
*
* Copyright 2001,2013 by Sean Conner.
*
* 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 3 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, see .
*
* Comments, questions and criticisms can be sent to: sean@conman.org
*
*************************************************************************/
#define _GNU_SOURCE
#include
#include
#include
#include
#include
#include
#include "../util.h"
#include "../cgi.h"
/**************************************************************************/
static http__e makelist(List *vars,char const *data)
{
while (*data != '\0')
{
struct pair *psp = PairNew(&data,'=','&');
if (psp == NULL)
return HTTP_ISERVERERR;
if ((UrlDecodeString(psp->name) == NULL) || (UrlDecodeString(psp->value) == NULL))
{
PairFree(psp);
PairListFree(vars);
return HTTP_BADREQ;
}
ListAddTail(vars,&psp->node);
if (*data == '&')
data++;
}
return HTTP_OKAY;
}
/**************************************************************************/
static int crq(int c)
{
return c == '\r';
}
/**************************************************************************/
static http__e makeplainlist(List *vars,char const *data)
{
while(*data != '\0')
{
struct pair *psp = PairNew(&data,'=','\n');
if (psp == NULL)
return HTTP_ISERVERERR;
remove_char(psp->name,crq);
remove_char(psp->value,crq);
ListAddTail(vars,&psp->node);
if (*data == '\n')
data++;
}
return HTTP_OKAY;
}
/**************************************************************************/
static bool parsequery(Cgi cgi)
{
assert(cgi != NULL);
/*-----------------------------------------------------------------------
; CGI-3875 mandates the QUERY environment variable must be sent. If it's
; not, it's a server error.
;------------------------------------------------------------------------*/
char *query = getenv("QUERY_STRING");
/*----------------------------------------------------------------------
; If there's no '=', then the query is NOT a series of name/value pairs,
; but a, or lack of a better term, plain query string.
;-----------------------------------------------------------------------*/
if (strchr(query,'=') == NULL)
{
cgi->query = strdup(query);
if (cgi->query == NULL)
return false;
UrlDecodeString(cgi->query);
return true;
}
/*--------------------------------------
; We have name/value pairs to deal with
;---------------------------------------*/
else
return makelist(&cgi->qvars,query);
}
/***************************************************************/
static http__e cgi_new_head(Cgi cgi)
{
assert(cgi != NULL);
cgi->method = HEAD;
return HTTP_OKAY;
}
/***************************************************************/
static http__e cgi_new_get(Cgi cgi)
{
assert(cgi != NULL);
cgi->method = GET;
return HTTP_OKAY;
}
/***************************************************************/
static http__e cgi_new_post(Cgi cgi)
{
char *content_length;
char *buffer;
http__e result;
assert(cgi != NULL);
cgi->method = POST;
cgi->content_type = getenv("CONTENT_TYPE");
content_length = getenv("CONTENT_LENGTH");
if (cgi->content_type == NULL)
return HTTP_MEDIATYPE;
if (content_length == NULL)
return HTTP_LENGTHREQ;
errno = 0;
cgi->content_length = strtoul(content_length,NULL,10);
if ((cgi->content_length == LONG_MAX) && (errno == ERANGE))
return HTTP_TOOLARGE;
buffer = malloc(cgi->content_length + 1);
if (buffer == NULL)
return HTTP_ISERVERERR;
if (fread(buffer,1,cgi->content_length,stdin) < cgi->content_length)
{
free(buffer);
return HTTP_METHODFAILURE;
}
buffer[cgi->content_length] = '\0';
if (strncmp(cgi->content_type,"application/x-www-form-urlencoded",33) == 0)
result = makelist(&cgi->pvars,buffer);
else if (strncmp(cgi->content_type,"multipart/form-data",19) == 0)
result = HTTP_MEDIATYPE;
else if (strncmp(cgi->content_type,"text/plain",10) == 0)
result = makeplainlist(&cgi->pvars,buffer);
else
result = HTTP_MEDIATYPE;
free(buffer);
return result;
}
/**********************************************************************/
static int cgi_new_put(Cgi cgi)
{
char *content_length;
cgi->method = PUT;
cgi->content_type = getenv("CONTENT_TYPE");
content_length = getenv("CONTENT_LENGTH");
if (cgi->content_type == NULL)
return HTTP_MEDIATYPE;
if (content_length == NULL)
return HTTP_LENGTHREQ;
errno = 0;
cgi->content_length = strtoul(content_length,NULL,10);
if ((cgi->content_length == LONG_MAX) && (errno == ERANGE))
return HTTP_TOOLARGE;
return HTTP_OKAY;
}
/*************************************************************/
static int cgi_new_delete(Cgi cgi)
{
assert(cgi != NULL);
cgi->method = DELETE;
return HTTP_OKAY;
}
/***************************************************************/
Cgi CgiNew(void)
{
char *gateway_interface;
char *request_method;
Cgi cgi;
gateway_interface = getenv("GATEWAY_INTERFACE");
if ((gateway_interface == NULL) || (strcmp(gateway_interface,"CGI/1.1") != 0))
return NULL;
request_method = getenv("REQUEST_METHOD");
if (request_method == NULL)
return NULL;
cgi = malloc(sizeof(struct cgi));
if (cgi == NULL)
return NULL;
cgi->query = NULL;
cgi->content_type = NULL;
cgi->content_length = 0;
cgi->status = HTTP_OKAY;
cgi->method = OTHER;
ListInit(&cgi->qvars);
ListInit(&cgi->pvars);
if (strcmp(request_method,"HEAD") == 0)
cgi->status = cgi_new_head(cgi);
else if (strcmp(request_method,"GET") == 0)
cgi->status = cgi_new_get(cgi);
else if (strcmp(request_method,"POST") == 0)
cgi->status = cgi_new_post(cgi);
else if (strcmp(request_method,"PUT") == 0)
cgi->status = cgi_new_put(cgi);
else if (strcmp(request_method,"DELETE") == 0)
cgi->status = cgi_new_delete(cgi);
if ((cgi->status == HTTP_OKAY) && !parsequery(cgi))
cgi->status = HTTP_BADREQ;
return cgi;
}
/*******************************************************************/
.