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) (unidiff)
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 @@
1nobase_include_HEADERS = \ 1nobase_include_HEADERS = \
2 kingate/cgi_gateway.h \ 2 kingate/cgi_gateway.h \
3 kingate/cgi_interface.h \ 3 kingate/cgi_interface.h \
4 kingate/fastcgi.h \ 4 kingate/fastcgi.h \
5 kingate/exception.h \ 5 kingate/exception.h \
6 kingate/util.h \ 6 kingate/util.h \
7 kingate/plaincgi.h 7 kingate/plaincgi.h \
8 kingate/cookies.h \
9 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,108 +1,113 @@
1#ifndef __KINGATE_CGI_GATEWAY_H 1#ifndef __KINGATE_CGI_GATEWAY_H
2#define __KINGATE_CGI_GATEWAY_H 2#define __KINGATE_CGI_GATEWAY_H
3 3
4#include <map> 4#include <map>
5#include "kingate/cgi_interface.h" 5#include "kingate/cgi_interface.h"
6#include "kingate/cookies.h"
6 7
7#ifndef __deprecated 8#ifndef __deprecated
8#if ( __GNUC__ == 3 && __GNUC_MINOR__ > 0 ) || __GNUC__ > 3 9#if ( __GNUC__ == 3 && __GNUC_MINOR__ > 0 ) || __GNUC__ > 3
9#define __deprecated __attribute__((deprecated)) 10#define __deprecated __attribute__((deprecated))
10#else 11#else
11#define __deprecated 12#define __deprecated
12#endif 13#endif
13#endif 14#endif
14 15
15/** 16/**
16 * @file 17 * @file
17 * @brief the cgi_gateway -- main interface to CGI. 18 * @brief the cgi_gateway -- main interface to CGI.
18 */ 19 */
19 20
20namespace kingate { 21namespace kingate {
21 using namespace std; 22 using namespace std;
22 23
23 /** 24 /**
24 * The main class interfacing with the CGI environment. 25 * The main class interfacing with the CGI environment.
25 */ 26 */
26 class cgi_gateway { 27 class cgi_gateway {
27 public: 28 public:
28 /** 29 /**
29 * The interface to CGI environment (e.g. fastcgi). 30 * The interface to CGI environment (e.g. fastcgi).
30 */ 31 */
31 cgi_interface& iface; 32 cgi_interface& iface;
32 /** 33 /**
33 * The type describing map holding parameters parsed from query string or input. 34 * The type describing map holding parameters parsed from query string or input.
34 */ 35 */
35 typedef multimap<string,string> params_t; 36 typedef multimap<string,string> params_t;
36 /** 37 /**
37 * The GET-passed parameters. 38 * The GET-passed parameters.
38 */ 39 */
39 params_t get; 40 params_t get;
40 /** 41 /**
41 * The POST-passed parameters. 42 * The POST-passed parameters.
42 */ 43 */
43 params_t post; 44 params_t post;
44 /** 45 /**
46 * Cookies passed.
47 */
48 cookies_t cookies;
49 /**
45 * Was the stdin content parsed? 50 * Was the stdin content parsed?
46 */ 51 */
47 bool b_parsed_content; 52 bool b_parsed_content;
48 53
49 /** 54 /**
50 * @param ci the interface to use. 55 * @param ci the interface to use.
51 */ 56 */
52 cgi_gateway(cgi_interface& ci); 57 cgi_gateway(cgi_interface& ci);
53 58
54 /** 59 /**
55 * Check whether there is an 'environment' meta-variable with specific name 60 * Check whether there is an 'environment' meta-variable with specific name
56 * passed to CGI. 61 * passed to CGI.
57 * @param n variable name. 62 * @param n variable name.
58 * @return true if yes. 63 * @return true if yes.
59 * @see cgi_interface::has_meta() 64 * @see cgi_interface::has_meta()
60 * @see get_meta() 65 * @see get_meta()
61 */ 66 */
62 bool has_meta(const string& n) const { return iface.has_meta(n); } 67 bool has_meta(const string& n) const { return iface.has_meta(n); }
63 /** 68 /**
64 * Retrieve the 'environment' meta-variable value. 69 * Retrieve the 'environment' meta-variable value.
65 * @param n variable name. 70 * @param n variable name.
66 * @return variable contents. 71 * @return variable contents.
67 * @see exception_notfound 72 * @see exception_notfound
68 * @see cgi_interface::get_meta() 73 * @see cgi_interface::get_meta()
69 */ 74 */
70 const string& get_meta(const string& n) const { return iface.get_meta(n); } 75 const string& get_meta(const string& n) const { return iface.get_meta(n); }
71 76
72 /** 77 /**
73 * fetch reference to the 'stdin' stream. 78 * fetch reference to the 'stdin' stream.
74 * @return the reference to the corresponding istream object. 79 * @return the reference to the corresponding istream object.
75 * @see cgi_interface::in() 80 * @see cgi_interface::in()
76 */ 81 */
77 istream& in() { return iface.in(); } 82 istream& in() { return iface.in(); }
78 /** 83 /**
79 * fetch reference to the 'stdout' stream. 84 * fetch reference to the 'stdout' stream.
80 * @return the reference to the corresponding ostream object. 85 * @return the reference to the corresponding ostream object.
81 * @see cgi_interface::out() 86 * @see cgi_interface::out()
82 */ 87 */
83 ostream& out() { return iface.out(); } 88 ostream& out() { return iface.out(); }
84 /** 89 /**
85 * fetch reference to the 'stderr' stream. 90 * fetch reference to the 'stderr' stream.
86 * @return the reference to the corresponding ostream object. 91 * @return the reference to the corresponding ostream object.
87 * @see cgi_interface::err() 92 * @see cgi_interface::err()
88 */ 93 */
89 ostream& err() { return iface.err(); } 94 ostream& err() { return iface.err(); }
90 /** 95 /**
91 * cast to the ostream -- fetches the reference to the 'stdout' 96 * cast to the ostream -- fetches the reference to the 'stdout'
92 * stream. 97 * stream.
93 * @see out() 98 * @see out()
94 */ 99 */
95 operator ostream& (void) { return out(); } 100 operator ostream& (void) { return out(); }
96 101
97 /** 102 /**
98 * Check to see whether the parameter was passed via GET. 103 * Check to see whether the parameter was passed via GET.
99 * @param n the parameter name. 104 * @param n the parameter name.
100 * @return true if yes. 105 * @return true if yes.
101 */ 106 */
102 bool has_GET(const string& n) const; 107 bool has_GET(const string& n) const;
103 /** 108 /**
104 * Retrieve the parameter passed via GET. 109 * Retrieve the parameter passed via GET.
105 * @param n the parameter name. 110 * @param n the parameter name.
106 * @return the parameter contents. 111 * @return the parameter contents.
107 * @see exception_notfound 112 * @see exception_notfound
108 */ 113 */
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 @@
1#ifndef __KINGATE_COOKIES_H
2#define __KINGATE_COOKIES_H
3
4#include <string>
5#include <map>
6#include <ostream>
7
8/**
9 * @file
10 * @brief cookies-related classes.
11 */
12
13namespace kingate {
14 using namespace std;
15
16 /**
17 * Class, holding the cookie with parameters.
18 */
19 class cookie : public map<string,string> {
20 public:
21 /**
22 * Cookie name.
23 */
24 string name;
25 /**
26 * Cookie value.
27 */
28 string value;
29
30 cookie() { }
31 /**
32 * @param n cookie name.
33 * @param v cookie value.
34 */
35 cookie(const string& n,const string& v)
36 : name(n), value(v) { }
37
38 /**
39 * set cookie parameter.
40 * @param p parameter name.
41 * @param v parameter value.
42 * @see _get_string()
43 */
44 void _set_string(const string& p,const string& v);
45
46 /**
47 * @param n cookie name.
48 * @see get_name()
49 */
50 void set_name(const string& n) { name = n; }
51 /**
52 * @param v cookie value.
53 * @see set_value()
54 */
55 void set_value(const string& v) { value = v; }
56 /**
57 * @param c coomment.
58 * @see get_comment()
59 * @see has_comment()
60 * @see unset_comment()
61 */
62 void set_comment(const string& c);
63 /**
64 * @param d domain.
65 * @see get_domain()
66 * @see has_domain()
67 * @see unset_domain()
68 */
69 void set_domain(const string& d);
70 /**
71 * @param ma max-age.
72 * @see get_max_age()
73 * @see has_max_age()
74 * @see unset_max_age()
75 */
76 void set_max_age(const string& ma);
77 /**
78 * @param p path.
79 * @see get_path()
80 * @see has_path()
81 * @see unset_path()
82 */
83 void set_path(const string& p);
84 /**
85 * set cookie security.
86 * @param s true if secure.
87 * @see get_secure()
88 * @see is_secure()
89 */
90 void set_secure(bool s);
91
92 /**
93 * @param e expiration time.
94 * @see get_expires()
95 * @see has_expires()
96 * @see unset_expires()
97 */
98 void set_expires(const string& e);
99
100 /**
101 * get cookie parameter.
102 * @param p parameter name.
103 * @return parameter value.
104 * @see _set_string()
105 */
106 const string& _get_string(const string& p) const;
107
108 /**
109 * @return cookie name.
110 * @see set_name()
111 */
112 const string& get_name() const { return name; }
113 /**
114 * @return cookie value.
115 * @see set_value()
116 */
117 const string& get_value() const { return value; }
118 /**
119 * @return cookie comment.
120 * @see set_comment()
121 * @see has_comment()
122 * @see unset_comment()
123 */
124 const string& get_comment() const;
125 /**
126 * @return cookie domain.
127 * @see set_domain()
128 * @see has_domain()
129 * @see unset_domain()
130 */
131 const string& get_domain() const;
132 /**
133 * @return cookie max-age.
134 * @see set_max_age()
135 * @see has_max_age()
136 * @see unset_max_age()
137 */
138 const string& get_max_age() const;
139 /**
140 * @return cookie path.
141 * @see set_path()
142 * @see has_path()
143 * @see unset_path()
144 */
145 const string& get_path() const;
146 /**
147 * @return cookie security.
148 * @see is_secure()
149 * @see set_secure()
150 */
151 bool get_secure() const;
152 /**
153 * @return cookie security.
154 * @see get_secure()
155 * @see set_secure()
156 */
157 bool is_secure() const { return get_secure(); }
158
159 /**
160 * @return cookie expiration time.
161 * @see set_expires()
162 * @see has_expires()
163 * @see unset_expires()
164 */
165 const string& get_expires() const;
166
167 /**
168 * @return true if cookie has comment.
169 * @see set_comment()
170 * @see get_comment()
171 * @see unset_comment()
172 */
173 bool has_comment() const;
174 /**
175 * @return true if cookie has domain.
176 * @see set_domain()
177 * @see get_domain()
178 * @see unset_domain()
179 */
180 bool has_domain() const;
181 /**
182 * @return true if cookie has max-age.
183 * @see set_max_age()
184 * @see get_max_age()
185 * @see unset_max_age()
186 */
187 bool has_max_age() const;
188 /**
189 * @return true if cookie has path.
190 * @see set_path()
191 * @see get_path()
192 * @see unset_path()
193 */
194 bool has_path() const;
195
196 /**
197 * @return true if cookie has expiration time.
198 * @see set_expires()
199 * @see get_expires()
200 * @see unset_expires()
201 */
202 bool has_expires() const;
203
204 /**
205 * rid cookie of comment.
206 * @see set_comment()
207 * @see get_comment()
208 * @see has_comment()
209 */
210 void unset_comment();
211 /**
212 * rid cookie of domain.
213 * @see set_domain()
214 * @see get_domain()
215 * @see has_domain()
216 */
217 void unset_domain();
218 /**
219 * rid cookie of max-age.
220 * @see set_max_age()
221 * @see get_max_age()
222 * @see has_max_age()
223 */
224 void unset_max_age();
225 /**
226 * rid cookie of path.
227 * @see set_path()
228 * @see get_path()
229 * @see has_path()
230 */
231 void unset_path();
232
233 /**
234 * rid cookie of expiration time.
235 * @see set_expires()
236 * @see get_expires()
237 * @see has_expires()
238 */
239 void unset_expires();
240
241 /**
242 * render the 'Set-Cookie' HTTP header according to RFC2109.
243 * Absolutely useless, only works with lynx.
244 * @return the rendered header content.
245 */
246 string set_cookie_header_rfc2109() const;
247 /**
248 * render the 'Set-Cookie' header according to the early vague
249 * netscape specs and common practice.
250 * @return the rendered header content.
251 */
252 string set_cookie_header() const;
253 };
254
255 /**
256 * Cookies container class.
257 */
258 class cookies_t : public map<string,cookie> {
259 public:
260
261 cookies_t() { }
262 /**
263 * @param s 'Cookie:' HTTP header contents to parse.
264 */
265 cookies_t(const string& s) { parse_cookies(s); }
266
267 /**
268 * @param c cookie to set.
269 */
270 void set_cookie(const cookie& c) { (*this)[c.get_name()]=c; }
271 /**
272 * @param n cookie name to remove.
273 */
274 void unset_cookie(const key_type& n) { erase(n); }
275 /**
276 * @param n cookie name.
277 * @return true if exists.
278 */
279 bool has_cookie(const key_type& n) const;
280 /**
281 * Return the named cookie if one exists.
282 * @param n cookie name.
283 * @return const reference to cookie object.
284 */
285 const cookie& get_cookie(const key_type& n) const;
286 /**
287 * Return the named cookie if one exists.
288 * @param n cookie name.
289 * @return reference to cookie object.
290 */
291 cookie& get_cookie(const key_type& n);
292
293 /**
294 * @param s HTTP 'Cookie' header content.
295 */
296 void parse_cookies(const string& s);
297 };
298}
299
300#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 @@
1#ifndef __KINGATE_HEADERS_H
2#define __KINGATE_HEADERS_H
3
4#include <string>
5#include <map>
6#include <ostream>
7
8/**
9 * @file
10 * @brief the headers -- HTTP headers container class.
11 */
12
13namespace kingate {
14 using namespace std;
15
16 /**
17 * The container class for HTTP headers.
18 */
19 class headers : public multimap<string,string> {
20 public:
21
22 /**
23 * Reference header if one exists.
24 * @param k the header.
25 * @return reference to the content.
26 */
27 const mapped_type& operator[](const key_type& k) const {
28 return get_header(k);
29 }
30 /**
31 * Reference header, creating one if needed.
32 * @param k the header.
33 * @return reference to the content.
34 */
35 mapped_type& operator[](const key_type& k) {
36 return get_header(k);
37 }
38
39 /**
40 * Set HTTP header. Remove all existent occurences of headers with
41 * this name.
42 * @param h header name.
43 * @param c header content.
44 */
45 void set_header(const key_type& h,const mapped_type& c);
46 /**
47 * Add HTTP header.
48 * @param h header name.
49 * @param c header content.
50 */
51 void add_header(const key_type& h,const mapped_type& c);
52 /**
53 * Remove named header.
54 * @param h header name.
55 */
56 void unset_header(const key_type& h);
57 /**
58 * Return const reference to the existing header.
59 * @param h header name.
60 * @return reference to header content.
61 */
62 const mapped_type& get_header(const key_type& h) const;
63 /**
64 * Return reference to the header, creating one if needed.
65 * @param h header name.
66 * @return reference to the content.
67 */
68 mapped_type& get_header(const key_type& h);
69 /**
70 * Return the range of headers with a certain name.
71 * @param h header name.
72 * @return pair of const iterators with the beginning and the end
73 * of range.
74 */
75 pair<const_iterator,const_iterator> get_headers(const key_type& h) const;
76 /**
77 * Return the range of headers with a certain name.
78 * @param h header name.
79 * @return pair of iterators with the beginning and the end
80 * of range.
81 */
82 pair<iterator,iterator> get_headers(const key_type& h);
83 /**
84 * Inquire whether the named header exists.
85 * @param h header name.
86 * @return true if exists.
87 */
88 bool has_header(const key_type& h) const;
89
90 };
91
92}
93
94#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 @@
1#ifndef __KINGATE_UTIL_H 1#ifndef __KINGATE_UTIL_H
2#define __KINGATE_UTIL_H 2#define __KINGATE_UTIL_H
3 3
4#include <string> 4#include <string>
5 5
6#ifndef __deprecated 6#ifndef __deprecated
7#if ( __GNUC__ == 3 && __GNUC_MINOR__ > 0 ) || __GNUC__ > 3 7#if ( __GNUC__ == 3 && __GNUC_MINOR__ > 0 ) || __GNUC__ > 3
8#define __deprecated __attribute__((deprecated)) 8#define __deprecated __attribute__((deprecated))
9#else 9#else
10#define __deprecated 10#define __deprecated
11#endif 11#endif
12#endif 12#endif
13 13
14namespace kingate { 14namespace kingate {
15 using namespace std; 15 using namespace std;
16 16
17 /** 17 /**
18 * Encode string for passing via URL. 18 * Encode string for passing via URL.
19 * @param str string unencoded. 19 * @param str string unencoded.
20 * @return the encoded string. 20 * @return the encoded string.
21 */ 21 */
22 string url_encode(const string& str); 22 string url_encode(const string& str);
23 /** 23 /**
24 * Remove URL-encoding from the string. 24 * Remove URL-encoding from the string.
25 * @param str the URL-encoded string. 25 * @param str the URL-encoded string.
26 * @return the decoded string. 26 * @return the decoded string.
27 */ 27 */
28 string url_decode(const string& str); 28 string url_decode(const string& str);
29 29
30 /** 30 /**
31 * Quote string for use in HTTP header.
32 * @param str the string to quote.
33 * @return the quoted string.
34 */
35 string http_quoted_string(const string& str);
36 /**
37 * Quote string for use in HTTP header if necessary.
38 * @param str the string to quote.
39 * @return the quoted string or token left as is.
40 */
41 string http_quote(const string& str);
42
43 /**
31 * deprecated alias to url_encode. 44 * deprecated alias to url_encode.
32 * @see url_encode 45 * @see url_encode
33 */ 46 */
34 inline string __deprecated url_escape(const string& str) { 47 inline string __deprecated url_escape(const string& str) {
35 return url_encode(str); 48 return url_encode(str);
36 } 49 }
37 /** 50 /**
38 * deprecated alias to url_decode. 51 * deprecated alias to url_decode.
39 * @see url_decode 52 * @see url_decode
40 */ 53 */
41 inline string __deprecated url_unescape(const string& str) { 54 inline string __deprecated url_unescape(const string& str) {
42 return url_decode(str); 55 return url_decode(str);
43 } 56 }
44} 57}
45 58
46#endif /* __KINGATE_UTIL_H */ 59#endif /* __KINGATE_UTIL_H */
47/* 60/*
48 * vim:set ft=cpp: 61 * vim:set ft=cpp:
49 */ 62 */
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 @@
1lib_LTLIBRARIES = libkingate.la libkingate-plaincgi.la 1lib_LTLIBRARIES = libkingate.la libkingate-plaincgi.la
2 2
3if HAVE_FCGI 3if HAVE_FCGI
4lib_LTLIBRARIES += libkingate-fcgi.la 4lib_LTLIBRARIES += libkingate-fcgi.la
5endif 5endif
6 6
7INCLUDES = -I${top_srcdir}/include -I${top_srcdir} 7INCLUDES = -I${top_srcdir}/include -I${top_srcdir}
8AM_CXXFLAGS = ${KONFORKA_CFLAGS} 8AM_CXXFLAGS = ${KONFORKA_CFLAGS}
9LDADD = ${KONFORKA_LIBS} 9LDADD = ${KONFORKA_LIBS}
10 10
11libkingate_la_SOURCES = \ 11libkingate_la_SOURCES = \
12 cgi_gateway.cc \ 12 cgi_gateway.cc \
13 cgi_interface.cc \ 13 cgi_interface.cc \
14 util.cc 14 util.cc \
15 cookies.cc \
16 headers.cc
15libkingate_la_LDFLAGS = -version-info 2:0:0 17libkingate_la_LDFLAGS = -version-info 2:0:0
16 18
17libkingate_fcgi_la_SOURCES = \ 19libkingate_fcgi_la_SOURCES = \
18 fastcgi.cc 20 fastcgi.cc
19libkingate_fcgi_la_LDFLAGS = -version-info 1:0:0 21libkingate_fcgi_la_LDFLAGS = -version-info 1:0:0
20 22
21libkingate_plaincgi_la_SOURCES = \ 23libkingate_plaincgi_la_SOURCES = \
22 plaincgi.cc 24 plaincgi.cc
23libkingate_plaincgi_la_LDFLAGS = -version-info 1:0:0 25libkingate_plaincgi_la_LDFLAGS = -version-info 1:0:0
24 26
25EXTRA_DIST = ${libkingate_fcgi_la_SOURCES} 27EXTRA_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,93 +1,97 @@
1#include <errno.h> 1#include <errno.h>
2#include <ctype.h> 2#include <ctype.h>
3#include "kingate/cgi_gateway.h" 3#include "kingate/cgi_gateway.h"
4#include "kingate/util.h" 4#include "kingate/util.h"
5#include "kingate/exception.h" 5#include "kingate/exception.h"
6 6
7namespace kingate { 7namespace kingate {
8 8
9 static string empty_string; 9 static string empty_string;
10 10
11 cgi_gateway::cgi_gateway(cgi_interface& ci) 11 cgi_gateway::cgi_gateway(cgi_interface& ci)
12 : iface(ci), b_parsed_content(false) { 12 : iface(ci), b_parsed_content(false) {
13 // Fetch GET content 13 // Fetch GET content
14 try { 14 try {
15 string qs = get_meta("QUERY_STRING"); 15 string qs = get_meta("QUERY_STRING");
16 parse_query(qs,get); 16 parse_query(qs,get);
17 }catch(exception_notfound& enf) { } 17 }catch(exception_notfound& enf) { }
18 // Fetch POST content 18 // Fetch POST content
19 if(!strcasecmp(content_type().c_str(),"application/x-www-form-urlencoded")) { 19 if(!strcasecmp(content_type().c_str(),"application/x-www-form-urlencoded")) {
20 unsigned long cl = content_length(); 20 unsigned long cl = content_length();
21 if(cl) { 21 if(cl) {
22 char * tmp = new char[cl]; 22 char * tmp = new char[cl];
23 iface.in().read(tmp,cl); 23 iface.in().read(tmp,cl);
24 string qs(tmp,cl); 24 string qs(tmp,cl);
25 delete tmp; 25 delete tmp;
26 parse_query(qs,post); 26 parse_query(qs,post);
27 } 27 }
28 b_parsed_content = true; 28 b_parsed_content = true;
29 } 29 }
30 // Parse cookies
31 try {
32 cookies.parse_cookies(get_meta("HTTP_COOKIE"));
33 }catch(exception_notfound& enf) { }
30 } 34 }
31 35
32 bool cgi_gateway::has_GET(const string& n) const { 36 bool cgi_gateway::has_GET(const string& n) const {
33 return get.find(n) != get.end(); 37 return get.find(n) != get.end();
34 } 38 }
35 const string& cgi_gateway::get_GET(const string& n) const { 39 const string& cgi_gateway::get_GET(const string& n) const {
36 params_t::const_iterator i = get.find(n); 40 params_t::const_iterator i = get.find(n);
37 if(i==get.end()) 41 if(i==get.end())
38 throw exception_notfound(CODEPOINT,"no such parameter"); 42 throw exception_notfound(CODEPOINT,"no such parameter");
39 return i->second; 43 return i->second;
40 } 44 }
41 bool cgi_gateway::has_POST(const string& n) const { 45 bool cgi_gateway::has_POST(const string& n) const {
42 return post.find(n) != post.end(); 46 return post.find(n) != post.end();
43 } 47 }
44 const string& cgi_gateway::get_POST(const string& n) const { 48 const string& cgi_gateway::get_POST(const string& n) const {
45 params_t::const_iterator i = post.find(n); 49 params_t::const_iterator i = post.find(n);
46 if(i==post.end()) 50 if(i==post.end())
47 throw exception_notfound(CODEPOINT,"no such parameter"); 51 throw exception_notfound(CODEPOINT,"no such parameter");
48 return i->second; 52 return i->second;
49 } 53 }
50 bool cgi_gateway::has_param(const string& n) const { 54 bool cgi_gateway::has_param(const string& n) const {
51 return has_GET(n) || has_POST(n); 55 return has_GET(n) || has_POST(n);
52 } 56 }
53 const string& cgi_gateway::get_param(const string& n) const { 57 const string& cgi_gateway::get_param(const string& n) const {
54 params_t::const_iterator i = get.find(n); 58 params_t::const_iterator i = get.find(n);
55 if(i!=get.end()) 59 if(i!=get.end())
56 return i->second; 60 return i->second;
57 i = post.find(n); 61 i = post.find(n);
58 if(i!=post.end()) 62 if(i!=post.end())
59 return i->second; 63 return i->second;
60 throw exception_notfound(CODEPOINT,"no such parameter"); 64 throw exception_notfound(CODEPOINT,"no such parameter");
61 } 65 }
62 66
63 /* 67 /*
64 * deprecated stuff. 68 * deprecated stuff.
65 */ 69 */
66 const string& cgi_gateway::get_content_type() const { 70 const string& cgi_gateway::get_content_type() const {
67 if(!has_meta("CONTENT_TYPE")) 71 if(!has_meta("CONTENT_TYPE"))
68 return empty_string; 72 return empty_string;
69 return get_meta("CONTENT_TYPE"); 73 return get_meta("CONTENT_TYPE");
70 } 74 }
71 unsigned long cgi_gateway::get_content_length() const { 75 unsigned long cgi_gateway::get_content_length() const {
72 if(!has_meta("CONTENT_LENGTH")) 76 if(!has_meta("CONTENT_LENGTH"))
73 return 0; 77 return 0;
74 string cl = get_meta("CONTENT_LENGTH"); 78 string cl = get_meta("CONTENT_LENGTH");
75 return strtol(cl.c_str(),NULL,10); 79 return strtol(cl.c_str(),NULL,10);
76 } 80 }
77 /* 81 /*
78 * 82 *
79 */ 83 */
80 84
81 const string& cgi_gateway::http_request_header(const string& hn) const { 85 const string& cgi_gateway::http_request_header(const string& hn) const {
82 string mvn = "HTTP_"; 86 string mvn = "HTTP_";
83 for(const char* p=hn.c_str();*p;p++) { 87 for(const char* p=hn.c_str();*p;p++) {
84 if(*p=='-') 88 if(*p=='-')
85 mvn += '_'; 89 mvn += '_';
86 else 90 else
87 mvn += toupper(*p); 91 mvn += toupper(*p);
88 } 92 }
89 return get_meta(mvn); 93 return get_meta(mvn);
90 } 94 }
91 95
92 const string& cgi_gateway::auth_type() const { 96 const string& cgi_gateway::auth_type() const {
93 try { 97 try {
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 @@
1#include "kingate/cookies.h"
2#include "kingate/util.h"
3#include "kingate/exception.h"
4
5namespace kingate {
6
7 /*
8 * RFC 2109:
9 * av-pairs = av-pair *(";" av-pair)
10 * av-pair = attr ["=" value] ; optional value
11 * attr = token
12 * value = word
13 * word = token | quoted-string
14 */
15
16 /* RFC 2109:
17 *
18 * The origin server effectively ends a session by sending the client a
19 * Set-Cookie header with Max-Age=0.
20 *
21 * An origin server may include multiple Set-Cookie headers in a response.
22 * Note that an intervening gateway could fold multiple such headers into a
23 * single header.
24 *
25 *
26 * set-cookie = "Set-Cookie:" cookies
27 * cookies = 1#cookie
28 * cookie = NAME "=" VALUE *(";" cookie-av)
29 * NAME = attr
30 * VALUE = value
31 * cookie-av = "Comment" "=" value
32 * | "Domain" "=" value
33 * | "Max-Age" "=" value
34 * | "Path" "=" value
35 * | "Secure"
36 * | "Version" "=" 1*DIGIT
37 *
38 *
39 * The origin server should send the following additional HTTP/1.1
40 * response headers, depending on circumstances:
41 *
42 * * To suppress caching of the Set-Cookie header: Cache-control: no-
43 * cache="set-cookie".
44 *
45 * and one of the following:
46 *
47 * * To suppress caching of a private document in shared caches: Cache-
48 * control: private.
49 *
50 * * To allow caching of a document and require that it be validated
51 * before returning it to the client: Cache-control: must-revalidate.
52 *
53 * * To allow caching of a document, but to require that proxy caches
54 * (not user agent caches) validate it before returning it to the
55 * client: Cache-control: proxy-revalidate.
56 *
57 * * To allow caching of a document and request that it be validated
58 * before returning it to the client (by "pre-expiring" it):
59 * Cache-control: max-age=0. Not all caches will revalidate the
60 * document in every case.
61 *
62 * HTTP/1.1 servers must send Expires: old-date (where old-date is a
63 * date long in the past) on responses containing Set-Cookie response
64 * headers unless they know for certain (by out of band means) that
65 * there are no downsteam HTTP/1.0 proxies. HTTP/1.1 servers may send
66 * other Cache-Control directives that permit caching by HTTP/1.1
67 * proxies in addition to the Expires: old-date directive; the Cache-
68 * Control directive will override the Expires: old-date for HTTP/1.1
69 * proxies.
70 *
71 */
72
73 void cookie::_set_string(const string& p,const string& v) {
74 (*this)[p]=v;
75 }
76
77 void cookie::set_comment(const string& c) {
78 _set_string("comment",c);
79 }
80 void cookie::set_domain(const string& d) {
81 _set_string("domain",d);
82 }
83 void cookie::set_max_age(const string& ma) {
84 _set_string("max-age",ma);
85 }
86 void cookie::set_path(const string& p) {
87 _set_string("path",p);
88 }
89 void cookie::set_secure(bool s) {
90 if(s)
91 _set_string("secure","");
92 else
93 erase("secure");
94 }
95
96 void cookie::set_expires(const string& e) {
97 (*this)["expires"] = e;
98 }
99
100 const string& cookie::_get_string(const string& s) const {
101 const_iterator i = find(s);
102 if(i==end())
103 throw exception_notfound(CODEPOINT,"No parameter set");
104 return i->second;
105 }
106
107 const string& cookie::get_comment() const {
108 return _get_string("comment");
109 }
110 const string& cookie::get_domain() const {
111 return _get_string("domain");
112 }
113 const string& cookie::get_max_age() const {
114 return _get_string("max-age");
115 }
116 const string& cookie::get_path() const {
117 return _get_string("path");
118 }
119 bool cookie::get_secure() const {
120 return find("secure")!=end();
121 }
122
123 const string& cookie::get_expires() const {
124 return _get_string("expires");
125 }
126
127 bool cookie::has_comment() const {
128 return find("comment")!=end();
129 }
130 bool cookie::has_domain() const {
131 return find("domain")!=end();
132 }
133 bool cookie::has_max_age() const {
134 return find("max-age")!=end();
135 }
136 bool cookie::has_path() const {
137 return find("path")!=end();
138 }
139
140 bool cookie::has_expires() const {
141 return find("expires")!=end();
142 }
143
144 void cookie::unset_comment() {
145 erase("comment");
146 }
147 void cookie::unset_domain() {
148 erase("domain");
149 }
150 void cookie::unset_max_age() {
151 erase("max-age");
152 }
153 void cookie::unset_path() {
154 erase("path");
155 }
156
157 void cookie::unset_expires() {
158 erase("expires");
159 }
160
161 string cookie::set_cookie_header_rfc2109() const {
162 string rv = name + "=" + http_quoted_string(value);
163 for(const_iterator i=begin();i!=end();++i) {
164 if(i->first=="secure") {
165 rv += "; secure";
166 }else{
167 rv += "; "+i->first+"="+http_quote(i->second);
168 }
169 }
170 rv += "; Version=1";
171 return rv;
172 }
173
174 string cookie::set_cookie_header() const {
175 string rv = name + "=" + value;
176 for(const_iterator i=begin();i!=end();++i) {
177 if(i->first=="secure") {
178 rv += "; secure";
179 }else{
180 rv += "; "+i->first+"="+i->second;
181 }
182 }
183 return rv;
184 }
185
186 bool cookies_t::has_cookie(const key_type& n) const {
187 return find(n)!=end();
188 }
189
190 const cookie& cookies_t::get_cookie(const key_type& n) const {
191 const_iterator i=find(n);
192 if(i==end())
193 throw exception_notfound(CODEPOINT,"No cookie with such name found");
194 return i->second;
195 }
196
197 cookie& cookies_t::get_cookie(const key_type& n) {
198 iterator i=find(n);
199 if(i==end())
200 throw exception_notfound(CODEPOINT,"No cookie with such name found");
201 return i->second;
202 }
203
204 void cookies_t::parse_cookies(const string& s) {
205 string str = s;
206 while(!str.empty()) {
207 string::size_type sc = str.find(';');
208 string s;
209 if(sc==string::npos) {
210 s = str;
211 str.erase();
212 }else{
213 s = str.substr(0,sc);
214 str.erase(0,sc+1);
215 }
216 string::size_type nsp=s.find_first_not_of(" \t");
217 if((nsp!=string::npos) && nsp)
218 s.erase(0,nsp);
219 string::size_type eq=s.find('=');
220 if(eq==string::npos)
221 continue;
222 string n = s.substr(0,eq);
223 s.erase(0,eq+1);
224 nsp = n.find_last_not_of(" \t");
225 n.erase(nsp+1);
226 nsp = s.find_first_not_of(" \t");
227 string v;
228 if(nsp!=string::npos)
229 v = s.substr(nsp);
230 else
231 v = s;
232 nsp = v.find_last_not_of(" \t");
233 if(nsp==string::npos)
234 v.erase();
235 else
236 v.erase(nsp+1);
237 cookie& c = (*this)[n];
238 c.set_name(n); c.set_value(v);
239 }
240 }
241
242}
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 @@
1#include "kingate/headers.h"
2#include "kingate/exception.h"
3
4namespace kingate {
5
6 void headers::set_header(const key_type& h,const mapped_type& c) {
7 erase(h);
8 insert(value_type(h,c));
9 }
10 void headers::add_header(const key_type& h,const mapped_type& c) {
11 insert(value_type(h,c));
12 }
13 void headers::unset_header(const key_type& h) {
14 erase(h);
15 }
16 const headers::mapped_type& headers::get_header(const key_type& h) const {
17 const_iterator i=find(h);
18 if(i==end())
19 throw exception_notfound(CODEPOINT,"No such header");
20 return i->second;
21 }
22 headers::mapped_type& headers::get_header(const key_type& h) {
23 // XXX: or should it fail if there's no such thing, unlike operator[]?
24 iterator i=find(h);
25 if(i==end())
26 i = insert(value_type(h,""));
27 return i->second;
28 }
29
30 pair<headers::const_iterator,headers::const_iterator> headers::get_headers(const key_type& h) const {
31 return equal_range(h);
32 }
33 pair<headers::iterator,headers::iterator> headers::get_headers(const key_type& h) {
34 return equal_range(h);
35 }
36
37 bool headers::has_header(const key_type& h) const {
38 return find(h)!=end();
39 }
40}
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 @@
1#include "kingate/util.h" 1#include "kingate/util.h"
2#include "kingate/exception.h" 2#include "kingate/exception.h"
3 3
4namespace kingate { 4namespace kingate {
5 5
6 static const char *safeChars = 6 static const char *safeChars =
7 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 7 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
8 "abcdefghijklmnopqrstuvwxyz" 8 "abcdefghijklmnopqrstuvwxyz"
9 "0123456789" 9 "0123456789"
10 "_-" ; 10 "_-" ;
11 11
12 string url_encode(const string& str) { 12 string url_encode(const string& str) {
13 string rv = str; 13 string rv = str;
14 string::size_type screwed = 0; 14 string::size_type screwed = 0;
15 for(;;) { 15 for(;;) {
16 screwed = rv.find_first_not_of(safeChars,screwed); 16 screwed = rv.find_first_not_of(safeChars,screwed);
17 if(screwed == string::npos) 17 if(screwed == string::npos)
18 break; 18 break;
19 while(screwed<rv.length() && !strchr(safeChars,rv.at(screwed))) { 19 while(screwed<rv.length() && !strchr(safeChars,rv.at(screwed))) {
20 char danger = rv.at(screwed); 20 char danger = rv.at(screwed);
21 if(danger==' ') { 21 if(danger==' ') {
22 rv.replace(screwed++,1,1,'+'); 22 rv.replace(screwed++,1,1,'+');
23 }else{ 23 }else{
24 static char tmp[4] = {'%',0,0,0}; 24 static char tmp[4] = {'%',0,0,0};
25 snprintf(&tmp[1],3,"%02X",0xFF&(int)danger); 25 snprintf(&tmp[1],3,"%02X",0xFF&(int)danger);
26 rv.replace(screwed,1,tmp,3); 26 rv.replace(screwed,1,tmp,3);
27 screwed+=3; 27 screwed+=3;
28 } 28 }
29 } 29 }
30 } 30 }
31 return rv; 31 return rv;
32 } 32 }
33 string url_decode(const string& str) { 33 string url_decode(const string& str) {
34 string rv = str; 34 string rv = str;
35 string::size_type unscrewed = 0; 35 string::size_type unscrewed = 0;
36 for(;;) { 36 for(;;) {
37 unscrewed = rv.find_first_of("%+",unscrewed); 37 unscrewed = rv.find_first_of("%+",unscrewed);
38 if(unscrewed == string::npos) 38 if(unscrewed == string::npos)
39 break; 39 break;
40 if(rv.at(unscrewed)=='+') { 40 if(rv.at(unscrewed)=='+') {
41 rv.replace(unscrewed++,1,1,' '); 41 rv.replace(unscrewed++,1,1,' ');
42 }else{ 42 }else{
43 if((rv.length()-unscrewed)<3) 43 if((rv.length()-unscrewed)<3)
44 throw exception(CODEPOINT,"incorrectly escaped string"); 44 throw exception(CODEPOINT,"incorrectly escaped string");
45 // XXX: ensure it's hex? 45 // XXX: ensure it's hex?
46 int danger = strtol(rv.substr(unscrewed+1,2).c_str(),NULL,16); 46 int danger = strtol(rv.substr(unscrewed+1,2).c_str(),NULL,16);
47 rv.replace(unscrewed,3,1,danger); 47 rv.replace(unscrewed,3,1,danger);
48 unscrewed++; 48 unscrewed++;
49 } 49 }
50 } 50 }
51 return rv; 51 return rv;
52 } 52 }
53
54 /*
55 * RFC 2616:
56 *
57 * separators = "(" | ")" | "<" | ">" | "@"
58 * | "," | ";" | ":" | "\" | <">
59 * | "/" | "[" | "]" | "?" | "="
60 * | "{" | "}" | SP | HT
61 */
62
63 /*
64 * RFC 2616:
65 *
66 * token = 1*<any CHAR except CTLs or separators>
67 */
68
69 static const char *quotible_chars =
70 "\001\002\003\004\005\006\007\010"
71 "\011\012\013\014\015\016\017\020"
72 "\021\022\023\024\025\026\027\030"
73 "\031\032\033\034\035\036\037\040"
74 "()<>@,;:\\\"/[]?={}" /* separator chars (except for SP and HT mentioned elsewhere */
75 "\177"
76 ;
77
78 /*
79 * RFC 2616:
80 *
81 * quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
82 * qdtext = <any TEXT except <">>
83 *
84 * The backslash character ("\") MAY be used as a single-character
85 * quoting mechanism only within quoted-string and comment constructs.
86 *
87 * quoted-pair = "\" CHAR
88 */
89
90 string http_quoted_string(const string& str) {
91 string rv = str;
92 string::size_type sp=0;
93 for(string::size_type q=rv.find('"');(q=rv.find('"',q))!=string::npos;q+=2)
94 rv.insert(q,1,'\\');
95 rv.insert(0,1,'"');
96 rv += '"';
97 return rv;
98 }
99
100 string http_quote(const string& str) {
101 if(str.find_first_of(quotible_chars)==string::npos)
102 return str;
103 return http_quoted_string(str);
104 }
105
53} 106}