mxoemu/Dependencies10/Sockets/HttpdForm.cpp
2010-08-28 06:30:32 +02:00

671 lines
14 KiB
C++

/** \file HttpdForm.cpp - read stdin, parse cgi input
**
** Written: 1999-Feb-10 grymse@alhem.net
**/
/*
Copyright (C) 1999-2010 Anders Hedstrom
This library is made available under the terms of the GNU GPL, with
the additional exemption that compiling, linking, and/or using OpenSSL
is allowed.
If you would like to use this library in a closed-source application,
a separate license agreement is available. For information about
the closed-source license agreement for the C++ sockets library,
please visit http://www.alhem.net/Sockets/license.html and/or
email license@alhem.net.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifdef _MSC_VER
#pragma warning(disable:4786)
#endif
#include "socket_include.h"
#include "Parse.h"
#include "IFile.h"
#include "HttpdForm.h"
#include "IFileUpload.h"
#include "IStream.h"
#include "File.h"
#ifdef SOCKETS_NAMESPACE
namespace SOCKETS_NAMESPACE {
#endif
#define TMPSIZE 32000
#ifdef _DEBUG
#define DEB(x) x
#else
#define DEB(x)
#endif
HttpdForm::HttpdForm(IFile *infil, const std::string& content_type, size_t content_length) : raw(false)
, m_file_upload(NULL)
, m_upload_stream(NULL)
{
CGI *cgi = NULL;
size_t extra = 2;
int cl = (int)content_length;
m_current = m_cgi.end();
if (content_type.size() >= 19 && content_type.substr(0, 19) == "multipart/form-data")
{
Parse pa(content_type,";=");
char *tempcmp = NULL;
size_t tc = 0;
size_t l = 0;
std::string str = pa.getword();
m_strBoundary = "";
while (!str.empty())
{
if (!strcmp(str.c_str(),"boundary"))
{
m_strBoundary = pa.getword();
l = m_strBoundary.size();
tempcmp = new char[l + extra];
}
//
str = pa.getword();
}
if (!m_strBoundary.empty())
{
std::string content_type;
std::string current_name;
std::string current_filename;
char *slask = new char[TMPSIZE];
infil -> fgets(slask, TMPSIZE);
cl -= (int)strlen(slask);
while (cl >= 0 && !infil -> eof())
{
while (strlen(slask) && (slask[strlen(slask) - 1] == 13 || slask[strlen(slask) - 1] == 10))
{
slask[strlen(slask) - 1] = 0;
}
content_type = "";
current_name = "";
current_filename = "";
if ((strstr(slask,m_strBoundary.c_str()) || strstr(m_strBoundary.c_str(),slask)) && strcmp(slask, m_strBoundary.c_str()))
{
m_strBoundary = slask;
l = m_strBoundary.size();
delete[] tempcmp;
tempcmp = new char[l + extra];
}
if (!strcmp(slask, m_strBoundary.c_str()))
{
// Get headers until empty line
infil -> fgets(slask, TMPSIZE);
cl -= (int)strlen(slask);
while (strlen(slask) && (slask[strlen(slask) - 1] == 13 || slask[strlen(slask) - 1] == 10))
{
slask[strlen(slask) - 1] = 0;
}
while (cl >= 0 && !infil -> eof() && *slask)
{
Parse pa(slask,":");
std::string h = pa.getword();
std::string h2 = pa.getrest();
if (!strcasecmp(h.c_str(),"Content-type"))
{
Parse pa(h2, ";");
content_type = pa.getword();
}
else
if (!strcasecmp(h.c_str(),"Content-Disposition"))
{
Parse pa(h2, ";");
h = pa.getword();
if (!strcmp(h.c_str(),"form-data"))
{
pa.EnableQuote(true);
h = pa.getword();
while (!h.empty())
{
Parse pa2(h,"=");
std::string name = pa2.getword();
h = pa2.getrest();
if (!strcmp(name.c_str(),"name"))
{
if (!h.empty() && h[0] == '"')
{
current_name = h.substr(1, h.size() - 2);
}
else
{
current_name = h;
}
}
else
if (!strcmp(name.c_str(),"filename"))
{
if (!h.empty() && h[0] == '"')
{
current_filename = h.substr(1, h.size() - 2);
}
else
{
current_filename = h;
}
size_t x = 0;
for (size_t i = 0; i < current_filename.size(); i++)
{
if (current_filename[i] == '/' || current_filename[i] == '\\')
x = i + 1;
}
if (x)
{
current_filename = current_filename.substr(x);
}
}
h = pa.getword();
}
}
}
// get next header value
infil -> fgets(slask, TMPSIZE);
cl -= (int)strlen(slask);
while (strlen(slask) && (slask[strlen(slask) - 1] == 13 || slask[strlen(slask) - 1] == 10))
{
slask[strlen(slask) - 1] = 0;
}
}
// Read content, save...?
if (current_filename.empty()) // not a file
{
std::string val;
infil -> fgets(slask, TMPSIZE);
cl -= (int)strlen(slask);
while (cl >= 0 && !infil -> eof() && strncmp(slask,m_strBoundary.c_str(),m_strBoundary.size() ))
{
val += slask;
infil -> fgets(slask, TMPSIZE);
cl -= (int)strlen(slask);
}
// remove trailing cr/linefeed
while (!val.empty() && (val[val.size() - 1] == 13 || val[val.size() - 1] == 10))
{
val = val.substr(0,val.size() - 1);
}
cgi = new CGI(current_name, val);
m_cgi.push_back(cgi);
if (!cl)
{
break;
}
}
else // current_filename.size() > 0
{
// read until m_strBoundary...
IFile *fil = NULL;
int out = 0;
char c;
char fn[2000]; // where post'd file will be saved
#ifdef _WIN32
{
char tmp_path[2000];
::GetTempPathA(2000, tmp_path);
if (tmp_path[strlen(tmp_path) - 1] != '\\')
{
#ifdef __CYGWIN__
strcat(tmp_path, "\\");
#else
strcat_s(tmp_path, sizeof(tmp_path), "\\");
#endif
}
snprintf(fn,sizeof(fn),"%s%s",tmp_path,current_filename.c_str());
}
#else
snprintf(fn,sizeof(fn),"/tmp/%s",current_filename.c_str());
#endif
if (m_file_upload && !m_upload_stream)
m_upload_stream = &m_file_upload -> IFileUploadBegin(current_name, current_filename, content_type);
else
{
fil = new File;
if (!fil -> fopen(fn, "wb"))
{
delete fil;
fil = NULL;
}
}
if (fil || m_upload_stream)
{
infil -> fread(&c,1,1);
cl -= 1;
while (cl >= 0 && !infil -> eof())
{
if (out)
{
if (m_upload_stream)
m_upload_stream -> IStreamWrite(&tempcmp[tc], 1);
else
fil -> fwrite(&tempcmp[tc],1,1);
}
tempcmp[tc] = c;
tc++;
if (tc >= l + extra)
{
tc = 0;
out = 1;
}
if (tc)
{
if (!strncmp(tempcmp + tc + extra, m_strBoundary.c_str(), l - tc) &&
!strncmp(tempcmp, m_strBoundary.c_str() + l - tc, tc))
{
break;
}
}
else
{
if (!strncmp(tempcmp + extra, m_strBoundary.c_str(), l))
{
break;
}
}
infil -> fread(&c,1,1);
cl -= 1;
}
if (m_file_upload && m_upload_stream)
{
m_file_upload -> IFileUploadEnd();
m_upload_stream = NULL;
}
else
if (fil)
{
fil -> fclose();
delete fil;
}
cgi = new CGI(current_name,fn,fn);
m_cgi.push_back(cgi);
#if defined( _WIN32) && !defined(__CYGWIN__)
strcpy_s(slask, TMPSIZE, m_strBoundary.c_str());
#else
strcpy(slask, m_strBoundary.c_str());
#endif
size_t l = strlen(slask);
infil -> fgets(slask + l, TMPSIZE - (int)l); // next line
cl -= (int)strlen(slask + l);
}
else
{
// couldn't open file
break;
}
if (!cl)
{
break;
}
}
}
else
{
// Probably '<m_strBoundary>--'
break;
}
} // while (!infil -> eof())
delete[] slask;
} // if (m_strBoundary)
if (tempcmp)
{
delete[] tempcmp;
}
} // end of multipart
else
if (strstr(content_type.c_str(), "x-www-form-urlencoded"))
{
bool got_name = false; // tnx to FatherNitwit
int cl = (int)content_length;
char c,chigh,clow;
std::string slask;
m_current = m_cgi.end();
std::string name;
infil -> fread(&c,1,1);
cl--;
while (cl >= 0 && !infil -> eof())
{
switch (c) // built-in url decoder
{
case '=': /* end of name */
name = slask;
slask.resize(0);
got_name = true;
break;
case '&': /* end of value */
if (got_name)
{
cgi = new CGI(name,slask);
got_name = false;
}
else
{
cgi = new CGI(slask, "");
}
slask.resize(0);
m_cgi.push_back(cgi);
break;
case '+': /* space */
slask += " ";
break;
case '%': /* hex value */
infil -> fread(&chigh,1,1);
cl--;
chigh -= 48 + (chigh > '9' ? 7 : 0) + (chigh >= 'a' ? 32 : 0);
infil -> fread(&clow,1,1);
cl--;
clow -= 48 + (clow > '9' ? 7 : 0) + (clow >= 'a' ? 32 : 0);
slask += (char)(chigh * 16 + clow);
break;
default: /* just another char */
slask += c;
break;
}
//
if (cl > 0)
{
infil -> fread(&c,1,1);
}
cl--;
}
if (got_name)
{
cgi = new CGI(name,slask);
}
else
{
cgi = new CGI(slask, "");
}
m_cgi.push_back(cgi);
}
}
// HttpdForm(buffer,l) -- request_method GET
HttpdForm::HttpdForm(const std::string& buffer,size_t l) : raw(false)
, m_file_upload(NULL)
, m_upload_stream(NULL)
{
CGI *cgi = NULL;
std::string slask;
std::string name;
char c,chigh,clow;
size_t ptr = 0;
bool got_name = false;
m_current = m_cgi.end();
ptr = 0;
while (ptr < l)
{
c = buffer[ptr++];
switch (c)
{
case '=': /* end of name */
name = slask;
slask.resize(0);
got_name = true;
break;
case '&': /* end of value */
if (got_name)
{
cgi = new CGI(name,slask);
got_name = false;
}
else
{
cgi = new CGI(slask, "");
}
slask.resize(0);
m_cgi.push_back(cgi);
break;
case '+': /* space */
slask += " ";
break;
case '%': /* hex value */
chigh = buffer[ptr++];
chigh -= 48 + (chigh > '9' ? 7 : 0) + (chigh >= 'a' ? 32 : 0);
clow = buffer[ptr++];
clow -= 48 + (clow > '9' ? 7 : 0) + (clow >= 'a' ? 32 : 0);
slask += (char)(chigh * 16 + clow);
break;
default: /* just another char */
slask += c;
break;
}
}
if (got_name)
{
cgi = new CGI(name,slask);
}
else
{
cgi = new CGI(slask, "");
}
m_cgi.push_back(cgi);
}
HttpdForm::~HttpdForm()
{
CGI *cgi = NULL; //,*tmp;
for (cgi_v::iterator it = m_cgi.begin(); it != m_cgi.end(); ++it)
{
cgi = *it;
delete cgi;
}
}
void HttpdForm::EnableRaw(bool b)
{
raw = b;
}
void HttpdForm::strcpyval(std::string& v,const char *value) const
{
v = "";
for (size_t i = 0; i < strlen(value); i++)
{
if (value[i] == '<')
{
v += "&lt;";
}
else
if (value[i] == '>')
{
v += "&gt;";
}
else
if (value[i] == '&')
{
v += "&amp;";
}
else
{
v += value[i];
}
}
}
bool HttpdForm::getfirst(std::string& n) const
{
m_current = m_cgi.begin();
return getnext(n);
}
bool HttpdForm::getnext(std::string& n) const
{
if (m_current != m_cgi.end() )
{
CGI *current = *m_current;
n = current -> name;
m_current++;
return true;
}
else
{
n = "";
}
return false;
}
bool HttpdForm::getfirst(std::string& n,std::string& v) const
{
m_current = m_cgi.begin();
return getnext(n,v);
}
bool HttpdForm::getnext(std::string& n,std::string& v) const
{
if (m_current != m_cgi.end() )
{
CGI *current = *m_current;
n = current -> name;
if (raw)
{
v = current -> value;
}
else
{
strcpyval(v,current -> value.c_str());
}
m_current++;
return true;
}
else
{
n = "";
}
return false;
}
int HttpdForm::getvalue(const std::string& n,std::string& v) const
{
CGI *cgi = NULL;
int r = 0;
for (cgi_v::const_iterator it = m_cgi.begin(); it != m_cgi.end(); ++it)
{
cgi = *it;
if (cgi -> name == n)
break;
cgi = NULL;
}
if (cgi)
{
if (raw)
{
v = cgi -> value;
}
else
{
strcpyval(v,cgi -> value.c_str());
}
r++;
}
else
{
v = "";
}
return r;
}
std::string HttpdForm::getvalue(const std::string& n) const
{
for (cgi_v::const_iterator it = m_cgi.begin(); it != m_cgi.end(); ++it)
{
CGI *cgi = *it;
if (cgi -> name == n)
{
return cgi -> value;
}
}
return "";
}
size_t HttpdForm::getlength(const std::string& n) const
{
CGI *cgi = NULL;
size_t l;
for (cgi_v::const_iterator it = m_cgi.begin(); it != m_cgi.end(); ++it)
{
cgi = *it;
if (cgi -> name == n)
break;
cgi = NULL;
}
l = cgi ? cgi -> value.size() : 0;
if (cgi && !raw)
{
for (size_t i = 0; i < cgi -> value.size(); i++)
{
switch (cgi -> value[i])
{
case '<': // &lt;
case '>': // &gt;
l += 4;
break;
case '&': // &amp;
l += 5;
break;
}
}
}
return l;
}
HttpdForm::cgi_v& HttpdForm::getbase()
{
return m_cgi;
}
const std::string& HttpdForm::GetBoundary() const
{
return m_strBoundary;
}
void HttpdForm::SetFileUpload(IFileUpload& cb)
{
m_file_upload = &cb;
}
#ifdef SOCKETS_NAMESPACE
}
#endif