/*******************************************************************************/
/* */
/* Copyright 2005 Pascal Gloor <pascal.gloor@spale.com> */
/* */
/* Licensed under the Apache License, Version 2.0 (the "License"); */
/* you may not use this file except in compliance with the License. */
/* You may obtain a copy of the License at */
/* */
/* http://www.apache.org/licenses/LICENSE-2.0 */
/* */
/* Unless required by applicable law or agreed to in writing, software */
/* distributed under the License is distributed on an "AS IS" BASIS, */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
/* See the License for the specific language governing permissions and */
/* limitations under the License. */
/* */
/*******************************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <regex.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
#ifndef __FILE__
#define __FILE__ unknown_file
#endif
#ifndef __LINE__
#define __LINE__ unknown_line
#endif
#include "user.h"
#include "internal.h"
#include "memmem.h"
#include "hexval.h"
#include "errors.h"
void __parsepost(CGI_CTX *ctx)
{
static char *multipart = "multipart/form-data";
static char *multibound = "multipart/form-data; boundary=";
char *ctype, *cur, *cl;
size_t clen,pos=0;
assert(ctx!=NULL);
if ( ( cl = getenv("CONTENT_LENGTH") ) == NULL )
return;
if ( ( ctype = getenv("CONTENT_TYPE") ) == NULL )
return;
if ( ( ctx->ct = malloc(strlen(ctype)+1) ) == NULL )
Error(__FILE__, __LINE__, 1, "malloc failed");
strcpy(ctx->ct, ctype);
if ( memcmp(ctype,multibound,strlen(multibound)) == 0 )
{
char *boundary = ctx->ct + strlen(multibound);
/* add '--' before the boundary */
boundary--;
*boundary = '-';
boundary--;
*boundary = '-';
ctx->boundary = boundary;
*(ctx->ct + strlen(multipart)) = '\0';
}
ctx->ctype = ctype;
clen = atol(cl);
if ( clen < 1 || clen > ctx->max_len )
{
Error(__FILE__, __LINE__, 0, "Invalid CONTENT_LENGTH too big or lower than 1");
return;
}
if ( ( ctx->buffer = malloc(clen+1) ) == NULL )
Error(__FILE__, __LINE__,1, "malloc failed");
memset(ctx->buffer, 0, clen);
while(1)
{
size_t rlen = fread(ctx->buffer+pos, 1, clen-pos, stdin);
if ( rlen < 1 )
{
Error(__FILE__, __LINE__, 0, "Could not read everything from STDIN");
break;
}
pos += rlen;
if ( pos == clen )
break;
}
fclose(stdin);
*(ctx->buffer+clen) = '\0';
cur = ctx->buffer;
if ( ctx->boundary != NULL )
{
regex_t re[2];
regmatch_t rematch[4];
if ( regcomp(&re[0], "^Content-Disposition: form-data; name=\"([^\"]+)\"(; filename=\"([^\"]+)\")?",REG_EXTENDED|REG_ICASE) != 0 )
Error(__FILE__, __LINE__, 1, "regcomp failed");
if ( regcomp(&re[1], "^Content-Type: ([^(\r|\n)]+)",REG_EXTENDED|REG_ICASE) != 0 )
Error(__FILE__, __LINE__, 1, "regcomp failed");
while( ( cur = __memmem(cur, pos-(cur-ctx->buffer), ctx->boundary, strlen(ctx->boundary)) ) != NULL )
{
size_t nl;
struct CGIVar *myvar;
cur += strlen(ctx->boundary);
if ( ( nl = __is_newline(cur,(pos - (cur-ctx->buffer))) ) != 0 )
{
cur+=nl;
}
else if ( (pos-(cur-ctx->buffer)) >= 1 && *cur == '-' && *(cur+1) == '-' )
break;
else
{
Error(__FILE__, __LINE__, 0, "parsing error in HTTP POST");
break;
}
if ( regexec(&re[0], cur, 4, rematch, 0) != 0)
{
Error(__FILE__, __LINE__, 0, "could not find Content-Disposition after boundary");
continue;
}
if ( rematch[0].rm_so == -1 || rematch[0].rm_eo == -1 )
Error(__FILE__, __LINE__, 1, "regexec failure");
if ( rematch[1].rm_so == -1 || rematch[1].rm_eo == -1 )
Error(__FILE__, __LINE__, 1, "regexec failure");
/* we're ready for a new var */
if ( ( myvar = malloc(sizeof(struct CGIVar)) ) == NULL )
Error(__FILE__, __LINE__,1, "malloc failed");
ctx->var[ctx->var_num] = myvar;
memset(myvar, 0, sizeof(struct CGIVar));
*(cur + rematch[1].rm_eo) = '\0';
myvar->name = cur + rematch[1].rm_so;
if ( rematch[3].rm_so != -1 && rematch[3].rm_eo != -1 )
{
*(cur + rematch[3].rm_eo) = '\0';
myvar->filename = cur + rematch[3].rm_so;
}
cur += rematch[0].rm_eo;
if ( ( nl = __is_newline(cur, pos - (cur-ctx->buffer)) ) == 0 )
{
Error(__FILE__, __LINE__, 0, "could not find newline when expected");
continue;
}
cur+=nl;
if ( regexec(&re[1], cur, 2, rematch, 0) == 0)
{
if ( rematch[1].rm_so != -1 && rematch[1].rm_eo != -1 )
{
myvar->ctype = cur + rematch[1].rm_so;
cur += rematch[0].rm_eo;
if ( ( nl = __is_newline(cur, pos - (cur-ctx->buffer)) ) == 0 )
{
Error(__FILE__, __LINE__, 0, "could not find newline when expected");
continue;
}
*cur = '\0';
cur+=nl;
}
else
Error(__FILE__, __LINE__, 1, "regexec failure");
}
if ( ( nl = __is_newline(cur, pos - (cur-ctx->buffer)) ) != 0 )
cur+=nl;
else
{
Error(__FILE__, __LINE__, 0, "could not find newline when expected");
continue;
}
{
char *end = __memmem(cur, pos-(cur-ctx->buffer), ctx->boundary, strlen(ctx->boundary));
if ( end == NULL )
{
Error(__FILE__, __LINE__, 0, "missing boundary");
break;
}
if ( *(end-1) == '\n' )
end--;
if ( *(end-1) == '\r' )
end--;
*end = '\0';
myvar->value = cur;
myvar->value_len = end-cur;
}
ctx->var_num++;
}
regfree(&re[0]);
regfree(&re[1]);
}
else
{
__parse_urlencoded(ctx, ctx->buffer, pos);
}
}
void __alloc_var(CGI_CTX *ctx)
{
assert(ctx!=NULL);
if ( ( ctx->var[ctx->var_num] = malloc(sizeof(CGI_VAR)) ) == NULL )
Error(__FILE__, __LINE__, 1, "malloc failed");
memset(ctx->var[ctx->var_num], 0, sizeof(CGI_VAR));
}
void __parse_urlencoded(CGI_CTX *ctx, char *buf, size_t buflen)
{
char *cur = buf;
regex_t re;
regmatch_t rematch[3];
assert(ctx!=NULL);
assert(buf!=NULL);
if ( regcomp(&re, "^([^=]*)=([^&]*)", REG_EXTENDED) != 0 )
Error(__FILE__, __LINE__,1, "regcomp failed");
while( regexec(&re,cur,3,rematch,0) == 0 )
{
if ( rematch[1].rm_so != -1 \
&& rematch[1].rm_eo != -1 \
&& rematch[1].rm_so < rematch[1].rm_eo )
{
assert(ctx->var[ctx->var_num]==NULL);
__alloc_var(ctx);
__decodeurl(cur+rematch[1].rm_so, \
rematch[1].rm_eo-rematch[1].rm_so);
ctx->var[ctx->var_num]->name = cur+rematch[1].rm_so;
if ( rematch[2].rm_so != -1
&& rematch[2].rm_eo != -1 )
{
assert(ctx->var[ctx->var_num]!=NULL);
ctx->var[ctx->var_num]->value_len = __decodeurl( \
cur+rematch[2].rm_so, \
rematch[2].rm_eo-rematch[2].rm_so);
ctx->var[ctx->var_num]->value = cur+rematch[2].rm_so;
ctx->var_num++;
}
}
else
Error(__FILE__, __LINE__, 1, "regexec failed");
if ( rematch[0].rm_eo > 0 )
cur += rematch[0].rm_eo;
if ( cur < buf+buflen )
cur++;
rematch[0].rm_so = 0;
rematch[0].rm_eo = buflen - (cur-buf);
}
regfree(&re);
}
size_t __decodeurl(char *buf, size_t len)
{
char *r,*w=buf;
assert(buf!=NULL);
for(r=buf; r<buf+len; ++r)
{
if ( *r == '%'
&& r+2 < buf+len
&& __is_hex(*(r+1))
&& __is_hex(*(r+2)) )
{
*w = ( __hex_val(*(r+1)) * 16 ) + __hex_val(*(r+2));
r+=2;
}
else if ( *r == '+' )
*w = ' ';
else if ( r != w )
*w = *r;
++w;
}
*w = '\0';
return w - buf;
}
void __parseget(CGI_CTX *ctx)
{
char *buf;
assert(ctx!=NULL);
if ( ( buf = getenv("QUERY_STRING") ) == NULL )
return;
if ( ( ctx->qs = malloc(strlen(buf)+1) ) == NULL )
Error(__FILE__, __LINE__, 1, "malloc failed");
strcpy(ctx->qs,buf);
__parse_urlencoded(ctx, buf, strlen(buf));
}
size_t __is_newline(char *buf, size_t len)
{
if ( len >= 1 && *buf == '\n' )
return 1;
if ( len >= 2 && *buf == '\r' && *(buf+1) == '\n' )
return 2;
return 0;
}