Panda3D
httpCookie.cxx
1 // Filename: httpCookie.cxx
2 // Created by: drose (26Aug04)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "httpCookie.h"
16 
17 #ifdef HAVE_OPENSSL
18 
19 #include "ctype.h"
20 #include "httpChannel.h"
21 
22 ////////////////////////////////////////////////////////////////////
23 // Function: HTTPCookie::operator <
24 // Access: Published
25 // Description: The sorting operator allows the cookies to be stored
26 // in a single dictionary; it returns nonequal only if
27 // the cookies are different in name, path, or domain.
28 ////////////////////////////////////////////////////////////////////
29 bool HTTPCookie::
30 operator < (const HTTPCookie &other) const {
31  if (_domain != other._domain) {
32  return _domain < other._domain;
33  }
34 
35  if (_path != other._path) {
36  // We use reverse sorting on the path, so that cookies with longer
37  // paths will be sent to the server before cookies with shorter
38  // paths.
39  return _path > other._path;
40  }
41 
42  if (_name != other._name) {
43  return _name < other._name;
44  }
45 
46  return false;
47 }
48 
49 ////////////////////////////////////////////////////////////////////
50 // Function: HTTPCookie::update_from
51 // Access: Published
52 // Description: Assuming the operator < method, above, has already
53 // evaluated these two cookies as equal, then assign the
54 // remaining values (value, expiration date, secure
55 // flag) from the indicated cookie. This is guaranteed
56 // not to change the ordering of the cookie in a set,
57 // and so can be used to update an existing cookie
58 // within a set with new values.
59 ////////////////////////////////////////////////////////////////////
60 void HTTPCookie::
61 update_from(const HTTPCookie &other) {
62  nassertv(!(other < *this) && !(*this < other));
63 
64  _value = other._value;
65  _expires = other._expires;
66  _secure = other._secure;
67 }
68 
69 ////////////////////////////////////////////////////////////////////
70 // Function: HTTPCookie::parse_set_cookie
71 // Access: Published
72 // Description: Separates out the parameter/value pairs of the
73 // Set-Cookie header and assigns the values of the
74 // cookie appropriate. Returns true if the header is
75 // parsed correctly, false if something is not
76 // understood.
77 ////////////////////////////////////////////////////////////////////
78 bool HTTPCookie::
79 parse_set_cookie(const string &format, const URLSpec &url) {
80  _name = string();
81  _value = string();
82  _domain = url.get_server();
83  _path = url.get_path();
84  _expires = HTTPDate();
85  _secure = false;
86 
87  bool okflag = true;
88  bool first_param = true;
89 
90  size_t start = 0;
91  while (start < format.length() && isspace(format[start])) {
92  start++;
93  }
94  size_t semicolon = format.find(';', start);
95 
96  while (semicolon != string::npos) {
97  if (!parse_cookie_param(format.substr(start, semicolon - start),
98  first_param)) {
99  okflag = false;
100  }
101  first_param = false;
102  start = semicolon + 1;
103  while (start < format.length() && isspace(format[start])) {
104  start++;
105  }
106  semicolon = format.find(';', start);
107  }
108 
109  if (!parse_cookie_param(format.substr(start), first_param)) {
110  okflag = false;
111  }
112 
113  return okflag;
114 }
115 
116 ////////////////////////////////////////////////////////////////////
117 // Function: HTTPCookie::matches_url
118 // Access: Published
119 // Description: Returns true if the cookie is appropriate to send
120 // with the indicated URL request, false otherwise.
121 ////////////////////////////////////////////////////////////////////
122 bool HTTPCookie::
123 matches_url(const URLSpec &url) const {
124  if (_domain.empty()) {
125  return false;
126  }
127  string server = url.get_server();
128  if (server == _domain ||
129  (string(".") + server) == _domain ||
130  (server.length() > _domain.length() &&
131  server.substr(server.length() - _domain.length()) == _domain &&
132  (_domain[0] == '.' || server[server.length() - _domain.length() - 1] == '.'))) {
133  // The domain matches.
134 
135  string path = url.get_path();
136  if (path.length() >= _path.length() &&
137  path.substr(0, _path.length()) == _path) {
138 
139  // The path matches too.
140  if (_secure && !url.is_ssl()) {
141  // Oops, can't send a secure cookie over a non-secure connection.
142  return false;
143  }
144 
145  return true;
146  }
147  }
148 
149  return false;
150 }
151 
152 ////////////////////////////////////////////////////////////////////
153 // Function: HTTPCookie::output
154 // Access: Published
155 // Description:
156 ////////////////////////////////////////////////////////////////////
157 void HTTPCookie::
158 output(ostream &out) const {
159  out << _name << "=" << _value
160  << "; path=" << _path << "; domain=" << _domain;
161 
162  if (has_expires()) {
163  out << "; expires=" << _expires;
164  }
165 
166  if (_secure) {
167  out << "; secure";
168  }
169 }
170 
171 ////////////////////////////////////////////////////////////////////
172 // Function: HTTPCookie::parse_cookie_param
173 // Access: Private
174 // Description: Called internally by parse_set_cookie() with each
175 // parameter=value pair split out from the header
176 // string. first_param will be true for the first
177 // parameter (which has special meaning). This should
178 // return true on success, false on failure.
179 ////////////////////////////////////////////////////////////////////
180 bool HTTPCookie::
181 parse_cookie_param(const string &param, bool first_param) {
182  size_t equals = param.find('=');
183 
184  string key, value;
185  if (equals == string::npos) {
186  key = param;
187  } else {
188  key = param.substr(0, equals);
189  value = param.substr(equals + 1);
190  }
191 
192  if (first_param) {
193  _name = key;
194  _value = value;
195 
196  } else {
197  key = HTTPChannel::downcase(key);
198  if (key == "expires") {
199  _expires = HTTPDate(value);
200  if (!_expires.is_valid()) {
201  return false;
202  }
203 
204  } else if (key == "path") {
205  _path = value;
206 
207  } else if (key == "domain") {
208  _domain = HTTPChannel::downcase(value);
209 
210  // From RFC 2965: If an explicitly specified value does not
211  // start with a dot, the user agent supplies a leading dot.
212  if (!_domain.empty() && _domain[0] != '.') {
213  _domain = string(".") + _domain;
214  }
215 
216  } else if (key == "secure") {
217  _secure = true;
218 
219  } else {
220  return false;
221  }
222  }
223 
224  return true;
225 }
226 
227 #endif // HAVE_OPENSSL
A container for a URL, e.g.
Definition: urlSpec.h:29
string get_server() const
Returns the server name specified by the URL, if any.
Definition: urlSpec.I:198
bool is_ssl() const
Returns true if the URL&#39;s scheme specifies an SSL-secured protocol such as https, or false otherwise...
Definition: urlSpec.I:234
string get_path() const
Returns the path specified by the URL, or "/" if no path is specified.
Definition: urlSpec.cxx:153
A container for an HTTP-legal time/date indication.
Definition: httpDate.h:30