author | Michael Krelin <hacker@klever.net> | 2005-05-09 11:00:28 (UTC) |
---|---|---|
committer | Michael Krelin <hacker@klever.net> | 2005-05-09 11:00:28 (UTC) |
commit | 43d47575878e4eaf3c8da84bf609fcd0bde595fb (patch) (unidiff) | |
tree | f7ec4d1f0d0a01b43feb5c9b4f414e870036522c | |
parent | d9578a5ae0ac4e44ff5e3c13d3f39f400f51bcf2 (diff) | |
download | kingate-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
-rw-r--r-- | include/Makefile.am | 4 | ||||
-rw-r--r-- | include/kingate/cgi_gateway.h | 5 | ||||
-rw-r--r-- | include/kingate/cookies.h | 300 | ||||
-rw-r--r-- | include/kingate/headers.h | 94 | ||||
-rw-r--r-- | include/kingate/util.h | 13 | ||||
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/cgi_gateway.cc | 4 | ||||
-rw-r--r-- | src/cookies.cc | 242 | ||||
-rw-r--r-- | src/headers.cc | 40 | ||||
-rw-r--r-- | src/util.cc | 53 |
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 @@ | |||
1 | nobase_include_HEADERS = \ | 1 | nobase_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 | ||
20 | namespace kingate { | 21 | namespace 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 | |||
13 | namespace 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 | |||
13 | namespace 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 | ||
14 | namespace kingate { | 14 | namespace 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 @@ | |||
1 | lib_LTLIBRARIES = libkingate.la libkingate-plaincgi.la | 1 | lib_LTLIBRARIES = libkingate.la libkingate-plaincgi.la |
2 | 2 | ||
3 | if HAVE_FCGI | 3 | if HAVE_FCGI |
4 | lib_LTLIBRARIES += libkingate-fcgi.la | 4 | lib_LTLIBRARIES += libkingate-fcgi.la |
5 | endif | 5 | endif |
6 | 6 | ||
7 | INCLUDES = -I${top_srcdir}/include -I${top_srcdir} | 7 | INCLUDES = -I${top_srcdir}/include -I${top_srcdir} |
8 | AM_CXXFLAGS = ${KONFORKA_CFLAGS} | 8 | AM_CXXFLAGS = ${KONFORKA_CFLAGS} |
9 | LDADD = ${KONFORKA_LIBS} | 9 | LDADD = ${KONFORKA_LIBS} |
10 | 10 | ||
11 | libkingate_la_SOURCES = \ | 11 | libkingate_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 | ||
15 | libkingate_la_LDFLAGS = -version-info 2:0:0 | 17 | libkingate_la_LDFLAGS = -version-info 2:0:0 |
16 | 18 | ||
17 | libkingate_fcgi_la_SOURCES = \ | 19 | libkingate_fcgi_la_SOURCES = \ |
18 | fastcgi.cc | 20 | fastcgi.cc |
19 | libkingate_fcgi_la_LDFLAGS = -version-info 1:0:0 | 21 | libkingate_fcgi_la_LDFLAGS = -version-info 1:0:0 |
20 | 22 | ||
21 | libkingate_plaincgi_la_SOURCES = \ | 23 | libkingate_plaincgi_la_SOURCES = \ |
22 | plaincgi.cc | 24 | plaincgi.cc |
23 | libkingate_plaincgi_la_LDFLAGS = -version-info 1:0:0 | 25 | libkingate_plaincgi_la_LDFLAGS = -version-info 1:0:0 |
24 | 26 | ||
25 | EXTRA_DIST = ${libkingate_fcgi_la_SOURCES} | 27 | 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,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 | ||
7 | namespace kingate { | 7 | namespace 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 | |||
5 | namespace 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 | |||
4 | namespace 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 | ||
4 | namespace kingate { | 4 | namespace 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 | } |