summaryrefslogtreecommitdiffabout
path: root/src/cookies.cc
blob: 1ee4f7cc803c56a4001136772d73e31622b152b7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
#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;
    }

    void cookies_t::set_cookie(const cookie& c) {
	insert(value_type(c.get_name(),c));
    }

    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);
	    set_cookie(cookie(n,v));
	}
    }

}