summaryrefslogtreecommitdiffabout
authorMichael Krelin <hacker@klever.net>2005-05-09 11:00:28 (UTC)
committer Michael Krelin <hacker@klever.net>2005-05-09 11:00:28 (UTC)
commit43d47575878e4eaf3c8da84bf609fcd0bde595fb (patch) (side-by-side diff)
treef7ec4d1f0d0a01b43feb5c9b4f414e870036522c
parentd9578a5ae0ac4e44ff5e3c13d3f39f400f51bcf2 (diff)
downloadkingate-43d47575878e4eaf3c8da84bf609fcd0bde595fb.zip
kingate-43d47575878e4eaf3c8da84bf609fcd0bde595fb.tar.gz
kingate-43d47575878e4eaf3c8da84bf609fcd0bde595fb.tar.bz2
1. http headers container added
2. preliminary cookies support 3. absolutely useless http_quoted_string and http_quote utility functions added
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--include/Makefile.am4
-rw-r--r--include/kingate/cgi_gateway.h5
-rw-r--r--include/kingate/cookies.h300
-rw-r--r--include/kingate/headers.h94
-rw-r--r--include/kingate/util.h13
-rw-r--r--src/Makefile.am4
-rw-r--r--src/cgi_gateway.cc4
-rw-r--r--src/cookies.cc242
-rw-r--r--src/headers.cc40
-rw-r--r--src/util.cc53
10 files changed, 757 insertions, 2 deletions
diff --git a/include/Makefile.am b/include/Makefile.am
index e0b778b..ee5cd51 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -1,7 +1,9 @@
nobase_include_HEADERS = \
kingate/cgi_gateway.h \
kingate/cgi_interface.h \
kingate/fastcgi.h \
kingate/exception.h \
kingate/util.h \
- kingate/plaincgi.h
+ kingate/plaincgi.h \
+ kingate/cookies.h \
+ kingate/headers.h
diff --git a/include/kingate/cgi_gateway.h b/include/kingate/cgi_gateway.h
index a5c4056..f20d72b 100644
--- a/include/kingate/cgi_gateway.h
+++ b/include/kingate/cgi_gateway.h
@@ -1,269 +1,274 @@
#ifndef __KINGATE_CGI_GATEWAY_H
#define __KINGATE_CGI_GATEWAY_H
#include <map>
#include "kingate/cgi_interface.h"
+#include "kingate/cookies.h"
#ifndef __deprecated
#if ( __GNUC__ == 3 && __GNUC_MINOR__ > 0 ) || __GNUC__ > 3
#define __deprecated __attribute__((deprecated))
#else
#define __deprecated
#endif
#endif
/**
* @file
* @brief the cgi_gateway -- main interface to CGI.
*/
namespace kingate {
using namespace std;
/**
* The main class interfacing with the CGI environment.
*/
class cgi_gateway {
public:
/**
* The interface to CGI environment (e.g. fastcgi).
*/
cgi_interface& iface;
/**
* The type describing map holding parameters parsed from query string or input.
*/
typedef multimap<string,string> params_t;
/**
* The GET-passed parameters.
*/
params_t get;
/**
* The POST-passed parameters.
*/
params_t post;
/**
+ * 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);
/**
* Check whether there is an 'environment' meta-variable with specific name
* passed to CGI.
* @param n variable name.
* @return true if yes.
* @see cgi_interface::has_meta()
* @see get_meta()
*/
bool has_meta(const string& n) const { return iface.has_meta(n); }
/**
* Retrieve the 'environment' meta-variable value.
* @param n variable name.
* @return variable contents.
* @see exception_notfound
* @see cgi_interface::get_meta()
*/
const string& get_meta(const string& n) const { return iface.get_meta(n); }
/**
* fetch reference to the 'stdin' stream.
* @return the reference to the corresponding istream object.
* @see cgi_interface::in()
*/
istream& in() { return iface.in(); }
/**
* fetch reference to the 'stdout' stream.
* @return the reference to the corresponding ostream object.
* @see cgi_interface::out()
*/
ostream& out() { return iface.out(); }
/**
* fetch reference to the 'stderr' stream.
* @return the reference to the corresponding ostream object.
* @see cgi_interface::err()
*/
ostream& err() { return iface.err(); }
/**
* cast to the ostream -- fetches the reference to the 'stdout'
* stream.
* @see out()
*/
operator ostream& (void) { return out(); }
/**
* Check to see whether the parameter was passed via GET.
* @param n the parameter name.
* @return true if yes.
*/
bool has_GET(const string& n) const;
/**
* Retrieve the parameter passed via GET.
* @param n the parameter name.
* @return the parameter contents.
* @see exception_notfound
*/
const string& get_GET(const string& n) const;
/**
* Check to see whether the parameter was passed via POST.
* @param n the parameter name.
* @return true if yes.
*/
bool has_POST(const string& n) const;
/**
* Retrieve the POST-parameter.
* @param n the parameter name.
* @return the parameter contents.
* @see exception_notfound
*/
const string& get_POST(const string& n) const;
/**
* Check to see whether the parameter was passed either via POST or
* GET.
* @param n the parameter name.
* @return true if yes.
*/
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.
* @see exception_notfound.
*/
const string& get_param(const string& n) const;
/**
* Retrieve the POST content-type (as passed via CONTENT_TYPE
* environment variable).
* @return the content type.
*/
const string& __deprecated get_content_type() const;
/**
* Retrieve the POST content length (as passed via the
* CONTENT_LENGTH environment variable).
* @return the content length.
*/
unsigned long __deprecated get_content_length() const;
/**
* Check to see whether the content from stdin stream was parsed.
* @return true if yes.
*/
bool is_content_parsed() const { return b_parsed_content; }
/**
* Retrieve the HTTP header value from the HTTP_ meta-variable.
* (see RFC3875)
* @param hn header field name.
* @return the HTTP header value.
*/
const string& http_request_header(const string& hn) const;
/**
* Retrieve the AUTH_TYPE meta-variable (see RFC3875)
* @return authentication type.
*/
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;
/**
* Retrieve the CONTENT_TYPE meta-variable (see RFC3875)
* @return media type of the request message body.
*/
const string& content_type() const;
/**
* Retrieve the GATEWAY_INTERFACE meta-variable (see RFC3875)
* @return the gateway interface dialect.
*/
const string& gateway_interface() const;
/**
* Retrieve the PATH_INFO meta-variable (see RFC3875)
* @return path to be interpreted by the script.
*/
const string& path_info() const;
/**
* Retrieve the PATH_TRANSLATED meta-variable (see RFC3875)
* @return the translated path to the document.
*/
const string& path_translated() const;
/**
* Retrieve the QUERY_STRING meta-variable (see RFC3875)
* @return the query string.
*/
const string& query_string() const;
/**
* Retrieve the REMOTE_ADDR meta-variable (see RFC3875)
* @return the network address of the remote host.
*/
const string& remote_addr() const;
/**
* Retrieve the REMOTE_HOST meta-variable (see RFC3875)
* @return the fully qualified domain name of the client if
* available. REMOTE_ADDR otherwise.
* @see remote_addr()
*/
const string& remote_host() const;
/**
* Retrieve the REMOTE_IDENT meta-variable (see RFC3875)
* @return remote user identity (see RFC1413).
*/
const string& remote_ident() const;
/**
* Retrieve the REMOTE_USER meta-variable (see RFC3875)
* @return the authenticated user name.
*/
const string& remote_user() const;
/**
* Retrieve the REQUEST_METHOD meta-variable (see RFC3875)
* @return the http request method.
*/
const string& request_method() const;
/**
* Retrieve the SCRIPT_NAME meta-variable (see RFC3875)
* @return the uri path identifying the script.
*/
const string& script_name() const;
/**
* Retrieve the SERVER_NAME meta-variable (see RFC3875)
* @return the server name of the script.
*/
const string& server_name() const;
/**
* Retrieve the SERVER_PORT meta-variable (see RFC3875)
* @return the port on which request was received.
*/
unsigned int server_port() const;
/**
* Retrieve the SERVER_PROTOCOL meta-variable (see RFC3875)
* @return the protocol used for the request.
*/
const string& server_protocol() const;
/**
* Retrieve the SERVER_SOFTWARE meta-variable (see RFC3875)
* @return the name and version of server software.
*/
const string& server_software() const;
private:
/**
* Parse the query string, putting the parameters into the map
* specified.
* @param q the query string.
* @param p destination parameters map.
*/
static void parse_query(string& q,params_t& p);
};
}
#endif /* __KINGATE_CGI_GATEWAY_H */
/*
* vim:set ft=cpp:
*/
diff --git a/include/kingate/cookies.h b/include/kingate/cookies.h
new file mode 100644
index 0000000..83ef0c6
--- a/dev/null
+++ b/include/kingate/cookies.h
@@ -0,0 +1,300 @@
+#ifndef __KINGATE_COOKIES_H
+#define __KINGATE_COOKIES_H
+
+#include <string>
+#include <map>
+#include <ostream>
+
+/**
+ * @file
+ * @brief cookies-related classes.
+ */
+
+namespace kingate {
+ using namespace std;
+
+ /**
+ * Class, holding the cookie with parameters.
+ */
+ class cookie : public map<string,string> {
+ public:
+ /**
+ * Cookie name.
+ */
+ string name;
+ /**
+ * Cookie value.
+ */
+ string value;
+
+ cookie() { }
+ /**
+ * @param n cookie name.
+ * @param v cookie value.
+ */
+ cookie(const string& n,const string& v)
+ : name(n), value(v) { }
+
+ /**
+ * set cookie parameter.
+ * @param p parameter name.
+ * @param v parameter value.
+ * @see _get_string()
+ */
+ void _set_string(const string& p,const string& v);
+
+ /**
+ * @param n cookie name.
+ * @see get_name()
+ */
+ void set_name(const string& n) { name = n; }
+ /**
+ * @param v cookie value.
+ * @see set_value()
+ */
+ void set_value(const string& v) { value = v; }
+ /**
+ * @param c coomment.
+ * @see get_comment()
+ * @see has_comment()
+ * @see unset_comment()
+ */
+ void set_comment(const string& c);
+ /**
+ * @param d domain.
+ * @see get_domain()
+ * @see has_domain()
+ * @see unset_domain()
+ */
+ void set_domain(const string& d);
+ /**
+ * @param ma max-age.
+ * @see get_max_age()
+ * @see has_max_age()
+ * @see unset_max_age()
+ */
+ void set_max_age(const string& ma);
+ /**
+ * @param p path.
+ * @see get_path()
+ * @see has_path()
+ * @see unset_path()
+ */
+ void set_path(const string& p);
+ /**
+ * set cookie security.
+ * @param s true if secure.
+ * @see get_secure()
+ * @see is_secure()
+ */
+ void set_secure(bool s);
+
+ /**
+ * @param e expiration time.
+ * @see get_expires()
+ * @see has_expires()
+ * @see unset_expires()
+ */
+ void set_expires(const string& e);
+
+ /**
+ * get cookie parameter.
+ * @param p parameter name.
+ * @return parameter value.
+ * @see _set_string()
+ */
+ const string& _get_string(const string& p) const;
+
+ /**
+ * @return cookie name.
+ * @see set_name()
+ */
+ const string& get_name() const { return name; }
+ /**
+ * @return cookie value.
+ * @see set_value()
+ */
+ const string& get_value() const { return value; }
+ /**
+ * @return cookie comment.
+ * @see set_comment()
+ * @see has_comment()
+ * @see unset_comment()
+ */
+ const string& get_comment() const;
+ /**
+ * @return cookie domain.
+ * @see set_domain()
+ * @see has_domain()
+ * @see unset_domain()
+ */
+ const string& get_domain() const;
+ /**
+ * @return cookie max-age.
+ * @see set_max_age()
+ * @see has_max_age()
+ * @see unset_max_age()
+ */
+ const string& get_max_age() const;
+ /**
+ * @return cookie path.
+ * @see set_path()
+ * @see has_path()
+ * @see unset_path()
+ */
+ const string& get_path() const;
+ /**
+ * @return cookie security.
+ * @see is_secure()
+ * @see set_secure()
+ */
+ bool get_secure() const;
+ /**
+ * @return cookie security.
+ * @see get_secure()
+ * @see set_secure()
+ */
+ bool is_secure() const { return get_secure(); }
+
+ /**
+ * @return cookie expiration time.
+ * @see set_expires()
+ * @see has_expires()
+ * @see unset_expires()
+ */
+ const string& get_expires() const;
+
+ /**
+ * @return true if cookie has comment.
+ * @see set_comment()
+ * @see get_comment()
+ * @see unset_comment()
+ */
+ bool has_comment() const;
+ /**
+ * @return true if cookie has domain.
+ * @see set_domain()
+ * @see get_domain()
+ * @see unset_domain()
+ */
+ bool has_domain() const;
+ /**
+ * @return true if cookie has max-age.
+ * @see set_max_age()
+ * @see get_max_age()
+ * @see unset_max_age()
+ */
+ bool has_max_age() const;
+ /**
+ * @return true if cookie has path.
+ * @see set_path()
+ * @see get_path()
+ * @see unset_path()
+ */
+ bool has_path() const;
+
+ /**
+ * @return true if cookie has expiration time.
+ * @see set_expires()
+ * @see get_expires()
+ * @see unset_expires()
+ */
+ bool has_expires() const;
+
+ /**
+ * rid cookie of comment.
+ * @see set_comment()
+ * @see get_comment()
+ * @see has_comment()
+ */
+ void unset_comment();
+ /**
+ * rid cookie of domain.
+ * @see set_domain()
+ * @see get_domain()
+ * @see has_domain()
+ */
+ void unset_domain();
+ /**
+ * rid cookie of max-age.
+ * @see set_max_age()
+ * @see get_max_age()
+ * @see has_max_age()
+ */
+ void unset_max_age();
+ /**
+ * rid cookie of path.
+ * @see set_path()
+ * @see get_path()
+ * @see has_path()
+ */
+ void unset_path();
+
+ /**
+ * rid cookie of expiration time.
+ * @see set_expires()
+ * @see get_expires()
+ * @see has_expires()
+ */
+ void unset_expires();
+
+ /**
+ * render the 'Set-Cookie' HTTP header according to RFC2109.
+ * Absolutely useless, only works with lynx.
+ * @return the rendered header content.
+ */
+ string set_cookie_header_rfc2109() const;
+ /**
+ * render the 'Set-Cookie' header according to the early vague
+ * netscape specs and common practice.
+ * @return the rendered header content.
+ */
+ string set_cookie_header() const;
+ };
+
+ /**
+ * Cookies container class.
+ */
+ class cookies_t : public map<string,cookie> {
+ public:
+
+ cookies_t() { }
+ /**
+ * @param s 'Cookie:' HTTP header contents to parse.
+ */
+ cookies_t(const string& s) { parse_cookies(s); }
+
+ /**
+ * @param c cookie to set.
+ */
+ void set_cookie(const cookie& c) { (*this)[c.get_name()]=c; }
+ /**
+ * @param n cookie name to remove.
+ */
+ void unset_cookie(const key_type& n) { erase(n); }
+ /**
+ * @param n cookie name.
+ * @return true if exists.
+ */
+ bool has_cookie(const key_type& n) const;
+ /**
+ * Return the named cookie if one exists.
+ * @param n cookie name.
+ * @return const reference to cookie object.
+ */
+ const cookie& get_cookie(const key_type& n) const;
+ /**
+ * Return the named cookie if one exists.
+ * @param n cookie name.
+ * @return reference to cookie object.
+ */
+ cookie& get_cookie(const key_type& n);
+
+ /**
+ * @param s HTTP 'Cookie' header content.
+ */
+ void parse_cookies(const string& s);
+ };
+}
+
+#endif /* __KINGATE_COOKIES_H */
diff --git a/include/kingate/headers.h b/include/kingate/headers.h
new file mode 100644
index 0000000..fb37fec
--- a/dev/null
+++ b/include/kingate/headers.h
@@ -0,0 +1,94 @@
+#ifndef __KINGATE_HEADERS_H
+#define __KINGATE_HEADERS_H
+
+#include <string>
+#include <map>
+#include <ostream>
+
+/**
+ * @file
+ * @brief the headers -- HTTP headers container class.
+ */
+
+namespace kingate {
+ using namespace std;
+
+ /**
+ * The container class for HTTP headers.
+ */
+ class headers : public multimap<string,string> {
+ public:
+
+ /**
+ * Reference header if one exists.
+ * @param k the header.
+ * @return reference to the content.
+ */
+ const mapped_type& operator[](const key_type& k) const {
+ return get_header(k);
+ }
+ /**
+ * Reference header, creating one if needed.
+ * @param k the header.
+ * @return reference to the content.
+ */
+ mapped_type& operator[](const key_type& k) {
+ return get_header(k);
+ }
+
+ /**
+ * Set HTTP header. Remove all existent occurences of headers with
+ * this name.
+ * @param h header name.
+ * @param c header content.
+ */
+ void set_header(const key_type& h,const mapped_type& c);
+ /**
+ * Add HTTP header.
+ * @param h header name.
+ * @param c header content.
+ */
+ void add_header(const key_type& h,const mapped_type& c);
+ /**
+ * Remove named header.
+ * @param h header name.
+ */
+ void unset_header(const key_type& h);
+ /**
+ * Return const reference to the existing header.
+ * @param h header name.
+ * @return reference to header content.
+ */
+ const mapped_type& get_header(const key_type& h) const;
+ /**
+ * Return reference to the header, creating one if needed.
+ * @param h header name.
+ * @return reference to the content.
+ */
+ mapped_type& get_header(const key_type& h);
+ /**
+ * Return the range of headers with a certain name.
+ * @param h header name.
+ * @return pair of const iterators with the beginning and the end
+ * of range.
+ */
+ pair<const_iterator,const_iterator> get_headers(const key_type& h) const;
+ /**
+ * Return the range of headers with a certain name.
+ * @param h header name.
+ * @return pair of iterators with the beginning and the end
+ * of range.
+ */
+ pair<iterator,iterator> get_headers(const key_type& h);
+ /**
+ * Inquire whether the named header exists.
+ * @param h header name.
+ * @return true if exists.
+ */
+ bool has_header(const key_type& h) const;
+
+ };
+
+}
+
+#endif /* __KINGATE_HEADERS_H */
diff --git a/include/kingate/util.h b/include/kingate/util.h
index 6024ccf..3fd96f6 100644
--- a/include/kingate/util.h
+++ b/include/kingate/util.h
@@ -1,49 +1,62 @@
#ifndef __KINGATE_UTIL_H
#define __KINGATE_UTIL_H
#include <string>
#ifndef __deprecated
#if ( __GNUC__ == 3 && __GNUC_MINOR__ > 0 ) || __GNUC__ > 3
#define __deprecated __attribute__((deprecated))
#else
#define __deprecated
#endif
#endif
namespace kingate {
using namespace std;
/**
* Encode string for passing via URL.
* @param str string unencoded.
* @return the encoded string.
*/
string url_encode(const string& str);
/**
* Remove URL-encoding from the string.
* @param str the URL-encoded string.
* @return the decoded string.
*/
string url_decode(const string& str);
/**
+ * Quote string for use in HTTP header.
+ * @param str the string to quote.
+ * @return the quoted string.
+ */
+ string http_quoted_string(const string& str);
+ /**
+ * Quote string for use in HTTP header if necessary.
+ * @param str the string to quote.
+ * @return the quoted string or token left as is.
+ */
+ string http_quote(const string& str);
+
+ /**
* deprecated alias to url_encode.
* @see url_encode
*/
inline string __deprecated url_escape(const string& str) {
return url_encode(str);
}
/**
* deprecated alias to url_decode.
* @see url_decode
*/
inline string __deprecated url_unescape(const string& str) {
return url_decode(str);
}
}
#endif /* __KINGATE_UTIL_H */
/*
* vim:set ft=cpp:
*/
diff --git a/src/Makefile.am b/src/Makefile.am
index 12bb1f8..2d462c8 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,25 +1,27 @@
lib_LTLIBRARIES = libkingate.la libkingate-plaincgi.la
if HAVE_FCGI
lib_LTLIBRARIES += libkingate-fcgi.la
endif
INCLUDES = -I${top_srcdir}/include -I${top_srcdir}
AM_CXXFLAGS = ${KONFORKA_CFLAGS}
LDADD = ${KONFORKA_LIBS}
libkingate_la_SOURCES = \
cgi_gateway.cc \
cgi_interface.cc \
- util.cc
+ util.cc \
+ cookies.cc \
+ headers.cc
libkingate_la_LDFLAGS = -version-info 2:0:0
libkingate_fcgi_la_SOURCES = \
fastcgi.cc
libkingate_fcgi_la_LDFLAGS = -version-info 1:0:0
libkingate_plaincgi_la_SOURCES = \
plaincgi.cc
libkingate_plaincgi_la_LDFLAGS = -version-info 1:0:0
EXTRA_DIST = ${libkingate_fcgi_la_SOURCES}
diff --git a/src/cgi_gateway.cc b/src/cgi_gateway.cc
index 30410f2..ab48f78 100644
--- a/src/cgi_gateway.cc
+++ b/src/cgi_gateway.cc
@@ -1,241 +1,245 @@
#include <errno.h>
#include <ctype.h>
#include "kingate/cgi_gateway.h"
#include "kingate/util.h"
#include "kingate/exception.h"
namespace kingate {
static string empty_string;
cgi_gateway::cgi_gateway(cgi_interface& ci)
: iface(ci), b_parsed_content(false) {
// Fetch GET content
try {
string qs = get_meta("QUERY_STRING");
parse_query(qs,get);
}catch(exception_notfound& enf) { }
// Fetch POST content
if(!strcasecmp(content_type().c_str(),"application/x-www-form-urlencoded")) {
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) { }
}
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 {
params_t::const_iterator i = get.find(n);
if(i==get.end())
throw exception_notfound(CODEPOINT,"no such parameter");
return i->second;
}
bool cgi_gateway::has_POST(const string& n) const {
return post.find(n) != post.end();
}
const string& cgi_gateway::get_POST(const string& n) const {
params_t::const_iterator i = post.find(n);
if(i==post.end())
throw exception_notfound(CODEPOINT,"no such parameter");
return i->second;
}
bool cgi_gateway::has_param(const string& n) const {
return has_GET(n) || has_POST(n);
}
const string& cgi_gateway::get_param(const string& n) const {
params_t::const_iterator i = get.find(n);
if(i!=get.end())
return i->second;
i = post.find(n);
if(i!=post.end())
return i->second;
throw exception_notfound(CODEPOINT,"no such parameter");
}
/*
* deprecated stuff.
*/
const string& cgi_gateway::get_content_type() const {
if(!has_meta("CONTENT_TYPE"))
return empty_string;
return get_meta("CONTENT_TYPE");
}
unsigned long cgi_gateway::get_content_length() const {
if(!has_meta("CONTENT_LENGTH"))
return 0;
string cl = get_meta("CONTENT_LENGTH");
return strtol(cl.c_str(),NULL,10);
}
/*
*
*/
const string& cgi_gateway::http_request_header(const string& hn) const {
string mvn = "HTTP_";
for(const char* p=hn.c_str();*p;p++) {
if(*p=='-')
mvn += '_';
else
mvn += toupper(*p);
}
return get_meta(mvn);
}
const string& cgi_gateway::auth_type() const {
try {
return get_meta("AUTH_TYPE");
}catch(exception_notfound& enf) {
return empty_string;
}
}
unsigned long cgi_gateway::content_length() const {
try {
const string& cl = get_meta("CONTENT_LENGTH");
errno = 0;
const char *clp = cl.c_str();
unsigned long rv = strtol(clp,(char**)&clp,10);
if(errno || *clp)
throw server_error(CODEPOINT,"Invalid CONTENT_LENGTH value passed from server");
return rv;
}catch(exception_notfound& enf) {
return 0;
}
}
const string& cgi_gateway::content_type() const {
try {
return get_meta("CONTENT_TYPE");
}catch(exception_notfound& enf) {
return empty_string;
}
}
const string& cgi_gateway::gateway_interface() const {
try {
return get_meta("GATEWAY_INTERFACE");
}catch(exception_notfound& enf) {
return empty_string;
}
}
const string& cgi_gateway::path_info() const {
try {
return get_meta("PATH_INFO");
}catch(exception_notfound& enf) {
return empty_string;
}
}
const string& cgi_gateway::path_translated() const {
try {
return get_meta("PATH_TRANSLATED");
}catch(exception_notfound& enf) {
return empty_string;
}
}
const string& cgi_gateway::query_string() const {
try {
return get_meta("QUERY_STRING");
}catch(exception_notfound& enf) {
return empty_string;
}
}
const string& cgi_gateway::remote_addr() const {
try {
return get_meta("REMOTE_ADDR");
}catch(exception_notfound& enf) {
return empty_string;
}
}
const string& cgi_gateway::remote_host() const {
try {
return get_meta("REMOTE_HOST");
}catch(exception_notfound& enf) {
return remote_addr();
}
}
const string& cgi_gateway::remote_ident() const {
try {
return get_meta("REMOTE_IDENT");
}catch(exception_notfound& enf) {
return empty_string;
}
}
const string& cgi_gateway::remote_user() const {
try {
return get_meta("REMOTE_USER");
}catch(exception_notfound& enf) {
return empty_string;
}
}
const string& cgi_gateway::request_method() const {
try {
return get_meta("REQUEST_METHOD");
}catch(exception_notfound& enf) {
throw server_error(CODEPOINT,"No REQUEST_METHOD passed from server");
}
}
const string& cgi_gateway::script_name() const {
try {
return get_meta("SCRIPT_NAME");
}catch(exception_notfound& enf) {
throw server_error(CODEPOINT,"No SCRIPT_NAME passed from server");
}
}
const string& cgi_gateway::server_name() const {
try {
return get_meta("SERVER_NAME");
}catch(exception_notfound& enf) {
throw server_error(CODEPOINT,"No SERVER_NAME passed from server");
}
}
unsigned int cgi_gateway::server_port() const {
try {
const string& sp = get_meta("SERVER_PORT");
errno = 0;
const char *spp = sp.c_str();
unsigned int rv = strtol(spp,(char**)&spp,10);
if(errno || *spp)
throw server_error(CODEPOINT,"Invalid SERVER_PORT value passed from server");
return rv;
}catch(exception_notfound& enf) {
throw server_error(CODEPOINT,"No SERVER_PORT passed from server");
}
}
const string& cgi_gateway::server_protocol() const {
try {
return get_meta("SERVER_PROTOCOL");
}catch(exception_notfound& enf) {
throw server_error(CODEPOINT,"No SERVER_PROTOCOL passed from server");
}
}
const string& cgi_gateway::server_software() const {
try {
return get_meta("SERVER_SOFTWARE");
}catch(exception_notfound& enf) {
throw server_error(CODEPOINT,"No SERVER_SOFTWARE passed from server");
}
}
void cgi_gateway::parse_query(string& q,params_t& p) {
while(!q.empty()) {
string::size_type amp = q.find('&');
string pp = (amp==string::npos)?q:q.substr(0,amp);
if(amp==string::npos)
q.clear();
else
q.erase(0,amp+1);
string::size_type eq = pp.find('=');
if(eq == string::npos) {
p.insert(params_t::value_type("",url_decode(pp)));
}else{
p.insert(params_t::value_type(url_decode(pp.substr(0,eq)),url_decode(pp.substr(eq+1))));
}
}
}
}
diff --git a/src/cookies.cc b/src/cookies.cc
new file mode 100644
index 0000000..40a0c8b
--- a/dev/null
+++ b/src/cookies.cc
@@ -0,0 +1,242 @@
+#include "kingate/cookies.h"
+#include "kingate/util.h"
+#include "kingate/exception.h"
+
+namespace kingate {
+
+ /*
+ * RFC 2109:
+ * av-pairs = av-pair *(";" av-pair)
+ * av-pair = attr ["=" value] ; optional value
+ * attr = token
+ * value = word
+ * word = token | quoted-string
+ */
+
+ /* RFC 2109:
+ *
+ * The origin server effectively ends a session by sending the client a
+ * Set-Cookie header with Max-Age=0.
+ *
+ * An origin server may include multiple Set-Cookie headers in a response.
+ * Note that an intervening gateway could fold multiple such headers into a
+ * single header.
+ *
+ *
+ * set-cookie = "Set-Cookie:" cookies
+ * cookies = 1#cookie
+ * cookie = NAME "=" VALUE *(";" cookie-av)
+ * NAME = attr
+ * VALUE = value
+ * cookie-av = "Comment" "=" value
+ * | "Domain" "=" value
+ * | "Max-Age" "=" value
+ * | "Path" "=" value
+ * | "Secure"
+ * | "Version" "=" 1*DIGIT
+ *
+ *
+ * The origin server should send the following additional HTTP/1.1
+ * response headers, depending on circumstances:
+ *
+ * * To suppress caching of the Set-Cookie header: Cache-control: no-
+ * cache="set-cookie".
+ *
+ * and one of the following:
+ *
+ * * To suppress caching of a private document in shared caches: Cache-
+ * control: private.
+ *
+ * * To allow caching of a document and require that it be validated
+ * before returning it to the client: Cache-control: must-revalidate.
+ *
+ * * To allow caching of a document, but to require that proxy caches
+ * (not user agent caches) validate it before returning it to the
+ * client: Cache-control: proxy-revalidate.
+ *
+ * * To allow caching of a document and request that it be validated
+ * before returning it to the client (by "pre-expiring" it):
+ * Cache-control: max-age=0. Not all caches will revalidate the
+ * document in every case.
+ *
+ * HTTP/1.1 servers must send Expires: old-date (where old-date is a
+ * date long in the past) on responses containing Set-Cookie response
+ * headers unless they know for certain (by out of band means) that
+ * there are no downsteam HTTP/1.0 proxies. HTTP/1.1 servers may send
+ * other Cache-Control directives that permit caching by HTTP/1.1
+ * proxies in addition to the Expires: old-date directive; the Cache-
+ * Control directive will override the Expires: old-date for HTTP/1.1
+ * proxies.
+ *
+ */
+
+ void cookie::_set_string(const string& p,const string& v) {
+ (*this)[p]=v;
+ }
+
+ void cookie::set_comment(const string& c) {
+ _set_string("comment",c);
+ }
+ void cookie::set_domain(const string& d) {
+ _set_string("domain",d);
+ }
+ void cookie::set_max_age(const string& ma) {
+ _set_string("max-age",ma);
+ }
+ void cookie::set_path(const string& p) {
+ _set_string("path",p);
+ }
+ void cookie::set_secure(bool s) {
+ if(s)
+ _set_string("secure","");
+ else
+ erase("secure");
+ }
+
+ void cookie::set_expires(const string& e) {
+ (*this)["expires"] = e;
+ }
+
+ const string& cookie::_get_string(const string& s) const {
+ const_iterator i = find(s);
+ if(i==end())
+ throw exception_notfound(CODEPOINT,"No parameter set");
+ return i->second;
+ }
+
+ const string& cookie::get_comment() const {
+ return _get_string("comment");
+ }
+ const string& cookie::get_domain() const {
+ return _get_string("domain");
+ }
+ const string& cookie::get_max_age() const {
+ return _get_string("max-age");
+ }
+ const string& cookie::get_path() const {
+ return _get_string("path");
+ }
+ bool cookie::get_secure() const {
+ return find("secure")!=end();
+ }
+
+ const string& cookie::get_expires() const {
+ return _get_string("expires");
+ }
+
+ bool cookie::has_comment() const {
+ return find("comment")!=end();
+ }
+ bool cookie::has_domain() const {
+ return find("domain")!=end();
+ }
+ bool cookie::has_max_age() const {
+ return find("max-age")!=end();
+ }
+ bool cookie::has_path() const {
+ return find("path")!=end();
+ }
+
+ bool cookie::has_expires() const {
+ return find("expires")!=end();
+ }
+
+ void cookie::unset_comment() {
+ erase("comment");
+ }
+ void cookie::unset_domain() {
+ erase("domain");
+ }
+ void cookie::unset_max_age() {
+ erase("max-age");
+ }
+ void cookie::unset_path() {
+ erase("path");
+ }
+
+ void cookie::unset_expires() {
+ erase("expires");
+ }
+
+ string cookie::set_cookie_header_rfc2109() const {
+ string rv = name + "=" + http_quoted_string(value);
+ for(const_iterator i=begin();i!=end();++i) {
+ if(i->first=="secure") {
+ rv += "; secure";
+ }else{
+ rv += "; "+i->first+"="+http_quote(i->second);
+ }
+ }
+ rv += "; Version=1";
+ return rv;
+ }
+
+ string cookie::set_cookie_header() const {
+ string rv = name + "=" + value;
+ for(const_iterator i=begin();i!=end();++i) {
+ if(i->first=="secure") {
+ rv += "; secure";
+ }else{
+ rv += "; "+i->first+"="+i->second;
+ }
+ }
+ return rv;
+ }
+
+ bool cookies_t::has_cookie(const key_type& n) const {
+ return find(n)!=end();
+ }
+
+ const cookie& cookies_t::get_cookie(const key_type& n) const {
+ const_iterator i=find(n);
+ if(i==end())
+ throw exception_notfound(CODEPOINT,"No cookie with such name found");
+ return i->second;
+ }
+
+ cookie& cookies_t::get_cookie(const key_type& n) {
+ iterator i=find(n);
+ if(i==end())
+ throw exception_notfound(CODEPOINT,"No cookie with such name found");
+ return i->second;
+ }
+
+ void cookies_t::parse_cookies(const string& s) {
+ string str = s;
+ while(!str.empty()) {
+ string::size_type sc = str.find(';');
+ string s;
+ if(sc==string::npos) {
+ s = str;
+ str.erase();
+ }else{
+ s = str.substr(0,sc);
+ str.erase(0,sc+1);
+ }
+ string::size_type nsp=s.find_first_not_of(" \t");
+ if((nsp!=string::npos) && nsp)
+ s.erase(0,nsp);
+ string::size_type eq=s.find('=');
+ if(eq==string::npos)
+ continue;
+ string n = s.substr(0,eq);
+ s.erase(0,eq+1);
+ nsp = n.find_last_not_of(" \t");
+ n.erase(nsp+1);
+ nsp = s.find_first_not_of(" \t");
+ string v;
+ if(nsp!=string::npos)
+ v = s.substr(nsp);
+ else
+ v = s;
+ nsp = v.find_last_not_of(" \t");
+ if(nsp==string::npos)
+ v.erase();
+ else
+ v.erase(nsp+1);
+ cookie& c = (*this)[n];
+ c.set_name(n); c.set_value(v);
+ }
+ }
+
+}
diff --git a/src/headers.cc b/src/headers.cc
new file mode 100644
index 0000000..89ac519
--- a/dev/null
+++ b/src/headers.cc
@@ -0,0 +1,40 @@
+#include "kingate/headers.h"
+#include "kingate/exception.h"
+
+namespace kingate {
+
+ void headers::set_header(const key_type& h,const mapped_type& c) {
+ erase(h);
+ insert(value_type(h,c));
+ }
+ void headers::add_header(const key_type& h,const mapped_type& c) {
+ insert(value_type(h,c));
+ }
+ void headers::unset_header(const key_type& h) {
+ erase(h);
+ }
+ const headers::mapped_type& headers::get_header(const key_type& h) const {
+ const_iterator i=find(h);
+ if(i==end())
+ throw exception_notfound(CODEPOINT,"No such header");
+ return i->second;
+ }
+ headers::mapped_type& headers::get_header(const key_type& h) {
+ // XXX: or should it fail if there's no such thing, unlike operator[]?
+ iterator i=find(h);
+ if(i==end())
+ i = insert(value_type(h,""));
+ return i->second;
+ }
+
+ pair<headers::const_iterator,headers::const_iterator> headers::get_headers(const key_type& h) const {
+ return equal_range(h);
+ }
+ pair<headers::iterator,headers::iterator> headers::get_headers(const key_type& h) {
+ return equal_range(h);
+ }
+
+ bool headers::has_header(const key_type& h) const {
+ return find(h)!=end();
+ }
+}
diff --git a/src/util.cc b/src/util.cc
index 3166e62..48e486a 100644
--- a/src/util.cc
+++ b/src/util.cc
@@ -1,53 +1,106 @@
#include "kingate/util.h"
#include "kingate/exception.h"
namespace kingate {
static const char *safeChars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789"
"_-" ;
string url_encode(const string& str) {
string rv = str;
string::size_type screwed = 0;
for(;;) {
screwed = rv.find_first_not_of(safeChars,screwed);
if(screwed == string::npos)
break;
while(screwed<rv.length() && !strchr(safeChars,rv.at(screwed))) {
char danger = rv.at(screwed);
if(danger==' ') {
rv.replace(screwed++,1,1,'+');
}else{
static char tmp[4] = {'%',0,0,0};
snprintf(&tmp[1],3,"%02X",0xFF&(int)danger);
rv.replace(screwed,1,tmp,3);
screwed+=3;
}
}
}
return rv;
}
string url_decode(const string& str) {
string rv = str;
string::size_type unscrewed = 0;
for(;;) {
unscrewed = rv.find_first_of("%+",unscrewed);
if(unscrewed == string::npos)
break;
if(rv.at(unscrewed)=='+') {
rv.replace(unscrewed++,1,1,' ');
}else{
if((rv.length()-unscrewed)<3)
throw exception(CODEPOINT,"incorrectly escaped string");
// XXX: ensure it's hex?
int danger = strtol(rv.substr(unscrewed+1,2).c_str(),NULL,16);
rv.replace(unscrewed,3,1,danger);
unscrewed++;
}
}
return rv;
}
+
+ /*
+ * RFC 2616:
+ *
+ * separators = "(" | ")" | "<" | ">" | "@"
+ * | "," | ";" | ":" | "\" | <">
+ * | "/" | "[" | "]" | "?" | "="
+ * | "{" | "}" | SP | HT
+ */
+
+ /*
+ * RFC 2616:
+ *
+ * token = 1*<any CHAR except CTLs or separators>
+ */
+
+ static const char *quotible_chars =
+ "\001\002\003\004\005\006\007\010"
+ "\011\012\013\014\015\016\017\020"
+ "\021\022\023\024\025\026\027\030"
+ "\031\032\033\034\035\036\037\040"
+ "()<>@,;:\\\"/[]?={}" /* separator chars (except for SP and HT mentioned elsewhere */
+ "\177"
+ ;
+
+ /*
+ * RFC 2616:
+ *
+ * quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
+ * qdtext = <any TEXT except <">>
+ *
+ * The backslash character ("\") MAY be used as a single-character
+ * quoting mechanism only within quoted-string and comment constructs.
+ *
+ * quoted-pair = "\" CHAR
+ */
+
+ string http_quoted_string(const string& str) {
+ string rv = str;
+ string::size_type sp=0;
+ for(string::size_type q=rv.find('"');(q=rv.find('"',q))!=string::npos;q+=2)
+ rv.insert(q,1,'\\');
+ rv.insert(0,1,'"');
+ rv += '"';
+ return rv;
+ }
+
+ string http_quote(const string& str) {
+ if(str.find_first_of(quotible_chars)==string::npos)
+ return str;
+ return http_quoted_string(str);
+ }
+
}