Panda3D
|
00001 // Filename: httpCookie.cxx 00002 // Created by: drose (26Aug04) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 00012 // 00013 //////////////////////////////////////////////////////////////////// 00014 00015 #include "httpCookie.h" 00016 00017 #ifdef HAVE_OPENSSL 00018 00019 #include "ctype.h" 00020 #include "httpChannel.h" 00021 00022 //////////////////////////////////////////////////////////////////// 00023 // Function: HTTPCookie::operator < 00024 // Access: Published 00025 // Description: The sorting operator allows the cookies to be stored 00026 // in a single dictionary; it returns nonequal only if 00027 // the cookies are different in name, path, or domain. 00028 //////////////////////////////////////////////////////////////////// 00029 bool HTTPCookie:: 00030 operator < (const HTTPCookie &other) const { 00031 if (_domain != other._domain) { 00032 return _domain < other._domain; 00033 } 00034 00035 if (_path != other._path) { 00036 // We use reverse sorting on the path, so that cookies with longer 00037 // paths will be sent to the server before cookies with shorter 00038 // paths. 00039 return _path > other._path; 00040 } 00041 00042 if (_name != other._name) { 00043 return _name < other._name; 00044 } 00045 00046 return false; 00047 } 00048 00049 //////////////////////////////////////////////////////////////////// 00050 // Function: HTTPCookie::update_from 00051 // Access: Published 00052 // Description: Assuming the operator < method, above, has already 00053 // evaluated these two cookies as equal, then assign the 00054 // remaining values (value, expiration date, secure 00055 // flag) from the indicated cookie. This is guaranteed 00056 // not to change the ordering of the cookie in a set, 00057 // and so can be used to update an existing cookie 00058 // within a set with new values. 00059 //////////////////////////////////////////////////////////////////// 00060 void HTTPCookie:: 00061 update_from(const HTTPCookie &other) { 00062 nassertv(!(other < *this) && !(*this < other)); 00063 00064 _value = other._value; 00065 _expires = other._expires; 00066 _secure = other._secure; 00067 } 00068 00069 //////////////////////////////////////////////////////////////////// 00070 // Function: HTTPCookie::parse_set_cookie 00071 // Access: Published 00072 // Description: Separates out the parameter/value pairs of the 00073 // Set-Cookie header and assigns the values of the 00074 // cookie appropriate. Returns true if the header is 00075 // parsed correctly, false if something is not 00076 // understood. 00077 //////////////////////////////////////////////////////////////////// 00078 bool HTTPCookie:: 00079 parse_set_cookie(const string &format, const URLSpec &url) { 00080 _name = string(); 00081 _value = string(); 00082 _domain = url.get_server(); 00083 _path = url.get_path(); 00084 _expires = HTTPDate(); 00085 _secure = false; 00086 00087 bool okflag = true; 00088 bool first_param = true; 00089 00090 size_t start = 0; 00091 while (start < format.length() && isspace(format[start])) { 00092 start++; 00093 } 00094 size_t semicolon = format.find(';', start); 00095 00096 while (semicolon != string::npos) { 00097 if (!parse_cookie_param(format.substr(start, semicolon - start), 00098 first_param)) { 00099 okflag = false; 00100 } 00101 first_param = false; 00102 start = semicolon + 1; 00103 while (start < format.length() && isspace(format[start])) { 00104 start++; 00105 } 00106 semicolon = format.find(';', start); 00107 } 00108 00109 if (!parse_cookie_param(format.substr(start), first_param)) { 00110 okflag = false; 00111 } 00112 00113 return okflag; 00114 } 00115 00116 //////////////////////////////////////////////////////////////////// 00117 // Function: HTTPCookie::matches_url 00118 // Access: Published 00119 // Description: Returns true if the cookie is appropriate to send 00120 // with the indicated URL request, false otherwise. 00121 //////////////////////////////////////////////////////////////////// 00122 bool HTTPCookie:: 00123 matches_url(const URLSpec &url) const { 00124 if (_domain.empty()) { 00125 return false; 00126 } 00127 string server = url.get_server(); 00128 if (server == _domain || 00129 (string(".") + server) == _domain || 00130 (server.length() > _domain.length() && 00131 server.substr(server.length() - _domain.length()) == _domain && 00132 (_domain[0] == '.' || server[server.length() - _domain.length() - 1] == '.'))) { 00133 // The domain matches. 00134 00135 string path = url.get_path(); 00136 if (path.length() >= _path.length() && 00137 path.substr(0, _path.length()) == _path) { 00138 00139 // The path matches too. 00140 if (_secure && !url.is_ssl()) { 00141 // Oops, can't send a secure cookie over a non-secure connection. 00142 return false; 00143 } 00144 00145 return true; 00146 } 00147 } 00148 00149 return false; 00150 } 00151 00152 //////////////////////////////////////////////////////////////////// 00153 // Function: HTTPCookie::output 00154 // Access: Published 00155 // Description: 00156 //////////////////////////////////////////////////////////////////// 00157 void HTTPCookie:: 00158 output(ostream &out) const { 00159 out << _name << "=" << _value 00160 << "; path=" << _path << "; domain=" << _domain; 00161 00162 if (has_expires()) { 00163 out << "; expires=" << _expires; 00164 } 00165 00166 if (_secure) { 00167 out << "; secure"; 00168 } 00169 } 00170 00171 //////////////////////////////////////////////////////////////////// 00172 // Function: HTTPCookie::parse_cookie_param 00173 // Access: Private 00174 // Description: Called internally by parse_set_cookie() with each 00175 // parameter=value pair split out from the header 00176 // string. first_param will be true for the first 00177 // parameter (which has special meaning). This should 00178 // return true on success, false on failure. 00179 //////////////////////////////////////////////////////////////////// 00180 bool HTTPCookie:: 00181 parse_cookie_param(const string ¶m, bool first_param) { 00182 size_t equals = param.find('='); 00183 00184 string key, value; 00185 if (equals == string::npos) { 00186 key = param; 00187 } else { 00188 key = param.substr(0, equals); 00189 value = param.substr(equals + 1); 00190 } 00191 00192 if (first_param) { 00193 _name = key; 00194 _value = value; 00195 00196 } else { 00197 key = HTTPChannel::downcase(key); 00198 if (key == "expires") { 00199 _expires = HTTPDate(value); 00200 if (!_expires.is_valid()) { 00201 return false; 00202 } 00203 00204 } else if (key == "path") { 00205 _path = value; 00206 00207 } else if (key == "domain") { 00208 _domain = HTTPChannel::downcase(value); 00209 00210 // From RFC 2965: If an explicitly specified value does not 00211 // start with a dot, the user agent supplies a leading dot. 00212 if (!_domain.empty() && _domain[0] != '.') { 00213 _domain = string(".") + _domain; 00214 } 00215 00216 } else if (key == "secure") { 00217 _secure = true; 00218 00219 } else { 00220 return false; 00221 } 00222 } 00223 00224 return true; 00225 } 00226 00227 #endif // HAVE_OPENSSL