summaryrefslogtreecommitdiffabout
authorMichael Krelin <hacker@klever.net>2005-07-04 22:54:09 (UTC)
committer Michael Krelin <hacker@klever.net>2005-07-04 22:54:09 (UTC)
commitc5d7daf77e265b53951e1e2b09c51e2fba5e93dc (patch) (side-by-side diff)
tree2337ff96d85d8de6d25fbaed75eb9c0d71a1625d
parentc8bcbfb951eec5fe14dac0b14f4faaf4a9f9f229 (diff)
downloadkingate-c5d7daf77e265b53951e1e2b09c51e2fba5e93dc.zip
kingate-c5d7daf77e265b53951e1e2b09c51e2fba5e93dc.tar.gz
kingate-c5d7daf77e265b53951e1e2b09c51e2fba5e93dc.tar.bz2
1. awkward yet working support for POST file upload (requires mimetic)
2. support for NOT handling/parsing POSTed data (including application/x-www-form-urlencoded)
Diffstat (more/less context) (show whitespace changes)
-rw-r--r--configure.ac16
-rw-r--r--include/kingate/cgi_gateway.h57
-rw-r--r--kingate.pc.in4
-rw-r--r--src/cgi_gateway.cc118
4 files changed, 183 insertions, 12 deletions
diff --git a/configure.ac b/configure.ac
index b3141f1..b51d64d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,9 +1,9 @@
AC_INIT([kingate], [0.0.1], [kingate-bugs@klever.net])
AC_CONFIG_SRCDIR([include/kingate/cgi_gateway.h])
-AC_CONFIG_HEADER([config.h])
+AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE([dist-bzip2])
AC_PROG_INSTALL
AC_PROG_AWK
AC_PROG_CXX
AC_PROG_CC
@@ -57,12 +57,26 @@ if test "${WANT_DOXYGEN}" = "yes" ; then
AC_WITH_DOT
else
AM_CONDITIONAL([HAVE_DOXYGEN],[false])
AM_CONDITIONAL([HAVE_DOT],[false])
fi
+HAVE_MIMETIC=false
+AC_LANG_PUSH(C++)
+ AC_CHECK_LIB([mimetic],[main],[
+ MIMETIC_LIBS=-lmimetic
+ HAVE_MIMETIC=true
+ ]
+ )
+AC_LANG_POP(C++)
+AC_SUBST([MIMETIC_LIBS])
+AC_SUBST([MIMETIC_CFLAGS])
+if ${HAVE_MIMETIC} ; then
+ AC_DEFINE([HAVE_MIMETIC],,[defined in presence of mimetic])
+fi
+
AC_CONFIG_FILES([
Makefile
kingate.pc kingate-fcgi.pc kingate-plaincgi.pc
Doxyfile
include/Makefile
src/Makefile
diff --git a/include/kingate/cgi_gateway.h b/include/kingate/cgi_gateway.h
index f20d72b..a26b0ae 100644
--- a/include/kingate/cgi_gateway.h
+++ b/include/kingate/cgi_gateway.h
@@ -40,24 +40,61 @@ namespace kingate {
params_t get;
/**
* The POST-passed parameters.
*/
params_t post;
/**
+ * Abstract base class for retrieving posted files.
+ */
+ class basic_file_t {
+ public:
+ /**
+ * Retrieve file name.
+ * @return filename
+ */
+ virtual const string& filename() const = 0;
+ /**
+ * Retrieve file content type.
+ * @return content type
+ */
+ virtual const string& content_type() const = 0;
+ /**
+ * Retrieve file contents.
+ * @return reference to the stream for accessing file content.
+ */
+ virtual istream& content() = 0;
+ virtual ~basic_file_t();
+ };
+ typedef basic_file_t *file_t;
+ /**
+ * The map holding information pertaining to files uploaded via post.
+ */
+ typedef multimap<string,file_t> files_t;
+ /**
+ * Files uploaded via post
+ */
+ files_t files;
+ /**
* Cookies passed.
*/
cookies_t cookies;
/**
* Was the stdin content parsed?
*/
bool b_parsed_content;
/**
* @param ci the interface to use.
*/
- cgi_gateway(cgi_interface& ci);
+ cgi_gateway(cgi_interface& ci,bool parsebody = true);
+ virtual ~cgi_gateway() throw();
+
+ /**
+ * Parse request body.
+ */
+ void parse_request_body();
/**
* Check whether there is an 'environment' meta-variable with specific name
* passed to CGI.
* @param n variable name.
* @return true if yes.
@@ -133,16 +170,30 @@ namespace kingate {
*/
bool has_param(const string& n) const;
/**
* Retrieve the parameter passed either via POST or GET
* (GET-parameter takes precedence).
* @param n the parameter name.
- * @return true if yes.
+ * @return the parameter contents.
* @see exception_notfound.
*/
const string& get_param(const string& n) const;
+ /**
+ * Check to see whether the file was uploaded in the request body.
+ * @param n the parameter name.
+ * @return true if yes.
+ */
+ bool has_file(const string& n) const;
+ /**
+ * Retrieve the file uploaded in the request body.
+ * @param n the parameter name.
+ * @return the file.
+ * @see exception_notfound.
+ */
+ const file_t get_file(const string& n) const;
+ file_t get_file(const string& n);
/**
* Retrieve the POST content-type (as passed via CONTENT_TYPE
* environment variable).
* @return the content type.
*/
@@ -174,13 +225,13 @@ namespace kingate {
*/
const string& auth_type() const;
/**
* Retrieve the CONTENT_LENGTH meta-variable (see RFC3875)
* @return size of the request message body.
*/
- unsigned long cgi_gateway::content_length() const;
+ unsigned long content_length() const;
/**
* Retrieve the CONTENT_TYPE meta-variable (see RFC3875)
* @return media type of the request message body.
*/
const string& content_type() const;
/**
diff --git a/kingate.pc.in b/kingate.pc.in
index 671faac..05cfe1d 100644
--- a/kingate.pc.in
+++ b/kingate.pc.in
@@ -4,8 +4,8 @@ libdir=@libdir@
includedir=@includedir@
Name: kingate
Description: C++ CGI support library
Version: @VERSION@
Requires: konforka
-Libs: -L${libdir} -lkingate
-Cflags: -I${includedir}
+Libs: -L${libdir} -lkingate @MIMETIC_LIBS@
+Cflags: -I${includedir} @MIMETIC_CFLAGS@
diff --git a/src/cgi_gateway.cc b/src/cgi_gateway.cc
index ab48f78..1706679 100644
--- a/src/cgi_gateway.cc
+++ b/src/cgi_gateway.cc
@@ -1,39 +1,130 @@
#include <errno.h>
#include <ctype.h>
+#include <sstream>
#include "kingate/cgi_gateway.h"
#include "kingate/util.h"
#include "kingate/exception.h"
+#include "config.h"
+#ifdef HAVE_MIMETIC
+# include <mimetic/mimeentity.h>
+# include <mimetic/parser/itparser.h>
+#endif /* HAVE_MIMETIC */
namespace kingate {
+ using mimetic::MimeEntity;
+
+#ifdef HAVE_MIMETIC
+ struct TornMimeEntity : public MimeEntity {
+ typedef istreambuf_iterator<char> it_type;
+ typedef it_type::iterator_category it_cat;
+ struct IParser : public mimetic::IteratorParser<it_type,it_cat> {
+ typedef mimetic::IteratorParser<it_type,it_cat> BT;
+ IParser(MimeEntity& me)
+ : BT::IteratorParser<it_type,it_cat>(me) { }
+ void loadHeader(it_type bit,it_type eit) {
+ m_bit = bit; m_eit = eit;
+ BT::loadHeader();
+ }
+ void loadBody(it_type bit,it_type eit) {
+ m_bit = bit; m_eit = eit;
+ BT::loadBody();
+ }
+ };
+ void load(istream& hs,istream& bs,int mask=0) {
+ IParser prs(*this);
+ prs.iMask(mask);
+ prs.loadHeader(it_type(hs),it_type());
+ prs.loadBody(it_type(bs),it_type());
+ }
+ };
+#endif /* HAVE_MIMETIC */
static string empty_string;
- cgi_gateway::cgi_gateway(cgi_interface& ci)
+ cgi_gateway::basic_file_t::~basic_file_t() { }
+
+ class string_file_t : public cgi_gateway::basic_file_t {
+ public:
+ string _file_name;
+ string _content_type;
+ stringstream _content;
+
+ string_file_t(const string& fn,const string& ct,const string& s)
+ : _file_name(fn), _content_type(ct), _content(s,ios::in) { }
+ const string& filename() const { return _file_name; }
+ const string& content_type() const { return _content_type; }
+ istream& content() { return _content; }
+ };
+
+ cgi_gateway::cgi_gateway(cgi_interface& ci,bool parsebody)
: iface(ci), b_parsed_content(false) {
// Fetch GET content
try {
string qs = get_meta("QUERY_STRING");
parse_query(qs,get);
}catch(exception_notfound& enf) { }
+ if(parsebody)
+ parse_request_body();
+ // Parse cookies
+ try {
+ cookies.parse_cookies(get_meta("HTTP_COOKIE"));
+ }catch(exception_notfound& enf) { }
+ }
+
+ cgi_gateway::~cgi_gateway() throw() {
+ for(files_t::iterator i=files.begin();i!=files.end();++i)
+ delete i->second;
+ files.clear();
+ }
+
+ void cgi_gateway::parse_request_body() {
+ if(b_parsed_content)
+ throw konforka::exception(CODEPOINT,"request body is already parsed");
// Fetch POST content
- if(!strcasecmp(content_type().c_str(),"application/x-www-form-urlencoded")) {
+ if(!strncasecmp(
+ content_type().c_str(),
+ "application/x-www-form-urlencoded",
+ sizeof("application/x-www-form-urlencoded")-1) ) {
unsigned long cl = content_length();
if(cl) {
char * tmp = new char[cl];
iface.in().read(tmp,cl);
string qs(tmp,cl);
delete tmp;
parse_query(qs,post);
}
b_parsed_content = true;
}
- // Parse cookies
- try {
- cookies.parse_cookies(get_meta("HTTP_COOKIE"));
- }catch(exception_notfound& enf) { }
+#ifdef HAVE_MIMETIC
+ else if(!strncasecmp(
+ content_type().c_str(),
+ "multipart/form-data",
+ sizeof("multipart/form-data")-1) ) {
+ stringstream h;
+ h
+ << "Content-Type: " << content_type() << "\r\n"
+ << "Content-Length: " << content_length() << "\r\n\n";
+ TornMimeEntity me;
+ me.load(h,iface.in(),0);
+ mimetic::MimeEntityList& parts = me.body().parts();
+ for(mimetic::MimeEntityList::iterator i=parts.begin();i!=parts.end();++i) {
+ MimeEntity *p = *i;
+ const mimetic::ContentDisposition& cd = p->header().contentDisposition();
+ string n = cd.param("name");
+ string fn = cd.param("filename");
+ if(fn.empty()) {
+ post.insert(params_t::value_type(n,p->body()));
+ }else{
+ const mimetic::ContentType& ct = p->header().contentType();
+ files.insert(files_t::value_type(n,new string_file_t(fn,ct.str(),p->body())));
+ }
+ }
+ b_parsed_content = true;
+ }
+#endif /* HAVE_MIMETIC */
}
bool cgi_gateway::has_GET(const string& n) const {
return get.find(n) != get.end();
}
const string& cgi_gateway::get_GET(const string& n) const {
@@ -60,12 +151,27 @@ namespace kingate {
return i->second;
i = post.find(n);
if(i!=post.end())
return i->second;
throw exception_notfound(CODEPOINT,"no such parameter");
}
+ bool cgi_gateway::has_file(const string& n) const {
+ return files.find(n) != files.end();
+ }
+ const cgi_gateway::file_t cgi_gateway::get_file(const string& n) const {
+ files_t::const_iterator i = files.find(n);
+ if(i==files.end())
+ throw exception_notfound(CODEPOINT,"no such parameter");
+ return i->second;
+ }
+ cgi_gateway::file_t cgi_gateway::get_file(const string& n) {
+ files_t::const_iterator i = files.find(n);
+ if(i==files.end())
+ throw exception_notfound(CODEPOINT,"no such parameter");
+ return i->second;
+ }
/*
* deprecated stuff.
*/
const string& cgi_gateway::get_content_type() const {
if(!has_meta("CONTENT_TYPE"))