Procházet zdrojové kódy

add cookie support

David Rose před 21 roky
rodič
revize
12c443e6fb

+ 6 - 3
panda/src/downloader/Sources.pp

@@ -22,8 +22,9 @@
     extractor.h \
     extractor.h \
     httpAuthorization.I httpAuthorization.h \
     httpAuthorization.I httpAuthorization.h \
     httpBasicAuthorization.I httpBasicAuthorization.h \
     httpBasicAuthorization.I httpBasicAuthorization.h \
-    httpClient.I httpClient.h \
     httpChannel.I httpChannel.h \
     httpChannel.I httpChannel.h \
+    httpClient.I httpClient.h \
+    httpCookie.I httpCookie.h \
     httpDate.I httpDate.h \
     httpDate.I httpDate.h \
     httpDigestAuthorization.I httpDigestAuthorization.h \
     httpDigestAuthorization.I httpDigestAuthorization.h \
     httpEntityTag.I httpEntityTag.h \
     httpEntityTag.I httpEntityTag.h \
@@ -50,8 +51,9 @@
     extractor.cxx \
     extractor.cxx \
     httpAuthorization.cxx \
     httpAuthorization.cxx \
     httpBasicAuthorization.cxx \
     httpBasicAuthorization.cxx \
-    httpClient.cxx \
     httpChannel.cxx \
     httpChannel.cxx \
+    httpClient.cxx \
+    httpCookie.cxx \
     httpDate.cxx \
     httpDate.cxx \
     httpDigestAuthorization.cxx \
     httpDigestAuthorization.cxx \
     httpEntityTag.cxx \
     httpEntityTag.cxx \
@@ -76,8 +78,9 @@
     extractor.h \
     extractor.h \
     httpAuthorization.I httpAuthorization.h \
     httpAuthorization.I httpAuthorization.h \
     httpBasicAuthorization.I httpBasicAuthorization.h \
     httpBasicAuthorization.I httpBasicAuthorization.h \
-    httpClient.I httpClient.h \
     httpChannel.I httpChannel.h \
     httpChannel.I httpChannel.h \
+    httpClient.I httpClient.h \
+    httpCookie.I httpCookie.h \
     httpDate.I httpDate.h \
     httpDate.I httpDate.h \
     httpDigestAuthorization.I httpDigestAuthorization.h \
     httpDigestAuthorization.I httpDigestAuthorization.h \
     httpEntityTag.I httpEntityTag.h \
     httpEntityTag.I httpEntityTag.h \

+ 1 - 0
panda/src/downloader/downloader_composite2.cxx

@@ -2,6 +2,7 @@
 #include "httpBasicAuthorization.cxx"
 #include "httpBasicAuthorization.cxx"
 #include "httpChannel.cxx"
 #include "httpChannel.cxx"
 #include "httpClient.cxx"
 #include "httpClient.cxx"
+#include "httpCookie.cxx"
 #include "httpDate.cxx"
 #include "httpDate.cxx"
 #include "httpDigestAuthorization.cxx"
 #include "httpDigestAuthorization.cxx"
 #include "httpEntityTag.cxx"
 #include "httpEntityTag.cxx"

+ 20 - 2
panda/src/downloader/httpChannel.cxx

@@ -18,6 +18,7 @@
 
 
 #include "httpChannel.h"
 #include "httpChannel.h"
 #include "httpClient.h"
 #include "httpClient.h"
+#include "httpCookie.h"
 #include "bioStream.h"
 #include "bioStream.h"
 #include "ssl_utils.h"
 #include "ssl_utils.h"
 #include "chunkedStream.h"
 #include "chunkedStream.h"
@@ -791,7 +792,11 @@ reached_done_state() {
           << "Unable to download body.\n";
           << "Unable to download body.\n";
       }
       }
       return false;
       return false;
+
     } else {
     } else {
+      if (_state != S_reading_body) {
+        _body_stream = NULL;
+      }
       _started_download = true;
       _started_download = true;
       _last_run_time = ClockObject::get_global_clock()->get_real_time();
       _last_run_time = ClockObject::get_global_clock()->get_real_time();
       return true;
       return true;
@@ -1831,7 +1836,12 @@ run_begin_body() {
     reset_to_new();
     reset_to_new();
 
 
   } else {
   } else {
-    nassertr(_body_stream == NULL, false);
+    // We shouldn't already be in the middle of reading some other
+    // body when we come here.
+    nassertd(_body_stream == NULL) {
+      reset_to_new();
+      return false;
+    }
     _body_stream = read_body();
     _body_stream = read_body();
     if (_body_stream == (ISocketStream *)NULL) {
     if (_body_stream == (ISocketStream *)NULL) {
       if (downloader_cat.is_debug()) {
       if (downloader_cat.is_debug()) {
@@ -1841,7 +1851,9 @@ run_begin_body() {
       reset_to_new();
       reset_to_new();
       
       
     } else {
     } else {
-      _state = S_reading_body;
+      if (_state != S_reading_body) {
+        _body_stream = NULL;
+      }
     }
     }
   }
   }
 
 
@@ -3115,6 +3127,8 @@ make_header() {
     break;
     break;
   }
   }
 
 
+  _client->send_cookies(stream, _request.get_url());
+
   if (!_body.empty()) {
   if (!_body.empty()) {
     stream
     stream
       << "Content-Type: application/x-www-form-urlencoded\r\n"
       << "Content-Type: application/x-www-form-urlencoded\r\n"
@@ -3220,6 +3234,10 @@ store_header_field(const string &field_name, const string &field_value) {
     (*hi).second += ", ";
     (*hi).second += ", ";
     (*hi).second += field_value;
     (*hi).second += field_value;
   }
   }
+
+  if (field_name == "set-cookie") {
+    _client->set_cookie(HTTPCookie(field_value, _request.get_url()));
+  }
 }
 }
 
 
 #ifndef NDEBUG
 #ifndef NDEBUG

+ 134 - 0
panda/src/downloader/httpClient.cxx

@@ -609,6 +609,140 @@ get_username(const string &server, const string &realm) const {
   return string();
   return string();
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPClient::set_cookie
+//       Access: Published
+//  Description: Stores the indicated cookie in the client's list of
+//               cookies, as if it had been received from a server.
+////////////////////////////////////////////////////////////////////
+void HTTPClient::
+set_cookie(const HTTPCookie &cookie) {
+  if (cookie.is_expired()) {
+    clear_cookie(cookie);
+
+  } else {
+    pair<Cookies::iterator, bool> result = _cookies.insert(cookie);
+    if (!result.second) {
+      // We already had a cookie matching the supplied domain/path/name,
+      // so replace it.
+      const HTTPCookie &orig_cookie = *result.first;
+      ((HTTPCookie &)orig_cookie).update_from(cookie);
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPClient::clear_cookie
+//       Access: Published
+//  Description: Removes the cookie with the matching domain/path/name
+//               from the client's list of cookies.  Returns true if
+//               it was removed, false if the cookie was not matched.
+////////////////////////////////////////////////////////////////////
+bool HTTPClient::
+clear_cookie(const HTTPCookie &cookie) {
+  Cookies::iterator ci = _cookies.find(cookie);
+  if (ci != _cookies.end()) {
+    _cookies.erase(ci);
+    return true;
+  }
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPClient::clear_all_cookies
+//       Access: Published
+//  Description: Removes the all stored cookies from the client.
+////////////////////////////////////////////////////////////////////
+void HTTPClient::
+clear_all_cookies() {
+  _cookies.clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPClient::has_cookie
+//       Access: Published
+//  Description: Returns true if there is a cookie in the client
+//               matching the given cookie's domain/path/name, false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+bool HTTPClient::
+has_cookie(const HTTPCookie &cookie) const {
+  Cookies::const_iterator ci = _cookies.find(cookie);
+  return (ci != _cookies.end());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPClient::get_cookie
+//       Access: Published
+//  Description: Looks up and returns the cookie in the client
+//               matching the given cookie's domain/path/name.  If
+//               there is no matching cookie, returns an empty cookie.
+////////////////////////////////////////////////////////////////////
+HTTPCookie HTTPClient::
+get_cookie(const HTTPCookie &cookie) const {
+  Cookies::const_iterator ci = _cookies.find(cookie);
+  if (ci != _cookies.end()) {
+    return (*ci);
+  }
+
+  return HTTPCookie();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPClient::write_cookies
+//       Access: Published
+//  Description: Outputs the complete list of cookies stored on the
+//               client, for all domains, including the expired
+//               cookies (which will normally not be sent back to a
+//               host).
+////////////////////////////////////////////////////////////////////
+void HTTPClient::
+write_cookies(ostream &out) const {
+  Cookies::const_iterator ci;
+  for (ci = _cookies.begin(); ci != _cookies.end(); ++ci) {
+    out << *ci << "\n";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPClient::send_cookies
+//       Access: Published
+//  Description: Writes to the indicated ostream a set of Cookie:
+//               lines for sending the cookies appropriate to the
+//               indicated URL along with an HTTP request.  This also
+//               removes expired cookies.
+////////////////////////////////////////////////////////////////////
+void HTTPClient::
+send_cookies(ostream &out, const URLSpec &url) {
+  HTTPDate now = HTTPDate::now();
+  bool any_expired = false;
+
+  Cookies::const_iterator ci;
+  for (ci = _cookies.begin(); ci != _cookies.end(); ++ci) {
+    const HTTPCookie &cookie = (*ci);
+    if (cookie.is_expired(now)) {
+      any_expired = true;
+
+    } else if (cookie.matches_url(url)) {
+      out << "Cookie: " << cookie.get_name() << "=" 
+          << cookie.get_value() << "\r\n";
+    }
+  }
+
+  if (any_expired) {
+    Cookies new_cookies;
+    Cookies::const_iterator ci;
+    for (ci = _cookies.begin(); ci != _cookies.end(); ++ci) {
+      const HTTPCookie &cookie = (*ci);
+      if (!cookie.is_expired(now)) {
+        new_cookies.insert(new_cookies.end(), cookie);
+      }
+    }
+    _cookies.swap(new_cookies);
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: HTTPClient::load_client_certificate
 //     Function: HTTPClient::load_client_certificate
 //       Access: Published
 //       Access: Published

+ 15 - 1
panda/src/downloader/httpClient.h

@@ -31,10 +31,12 @@
 #include "urlSpec.h"
 #include "urlSpec.h"
 #include "httpAuthorization.h"
 #include "httpAuthorization.h"
 #include "httpEnum.h"
 #include "httpEnum.h"
+#include "httpCookie.h"
 #include "globPattern.h"
 #include "globPattern.h"
 #include "pointerTo.h"
 #include "pointerTo.h"
 #include "pvector.h"
 #include "pvector.h"
 #include "pmap.h"
 #include "pmap.h"
+#include "pset.h"
 
 
 #include <openssl/ssl.h>
 #include <openssl/ssl.h>
 
 
@@ -82,6 +84,15 @@ PUBLISHED:
   void set_username(const string &server, const string &realm, const string &username);
   void set_username(const string &server, const string &realm, const string &username);
   string get_username(const string &server, const string &realm) const;
   string get_username(const string &server, const string &realm) const;
 
 
+  void set_cookie(const HTTPCookie &cookie);
+  bool clear_cookie(const HTTPCookie &cookie);
+  void clear_all_cookies();
+  bool has_cookie(const HTTPCookie &cookie) const;
+  HTTPCookie get_cookie(const HTTPCookie &cookie) const;
+
+  void write_cookies(ostream &out) const;
+  void send_cookies(ostream &out, const URLSpec &url);
+  
   INLINE void set_client_certificate_filename(const Filename &filename);
   INLINE void set_client_certificate_filename(const Filename &filename);
   INLINE void set_client_certificate_pem(const string &pem);
   INLINE void set_client_certificate_pem(const string &pem);
   INLINE void set_client_certificate_passphrase(const string &passphrase);
   INLINE void set_client_certificate_passphrase(const string &passphrase);
@@ -157,7 +168,7 @@ private:
   typedef pmap<string, string> Usernames;
   typedef pmap<string, string> Usernames;
   Usernames _usernames;
   Usernames _usernames;
 
 
-  typedef map<string, PT(HTTPAuthorization) > Realms;
+  typedef pmap<string, PT(HTTPAuthorization) > Realms;
   class Domain {
   class Domain {
   public:
   public:
     Realms _realms;
     Realms _realms;
@@ -165,6 +176,9 @@ private:
   typedef pmap<string, Domain> Domains;
   typedef pmap<string, Domain> Domains;
   Domains _proxy_domains, _www_domains;
   Domains _proxy_domains, _www_domains;
 
 
+  typedef pset<HTTPCookie> Cookies;
+  Cookies _cookies;
+
   Filename _client_certificate_filename;
   Filename _client_certificate_filename;
   string _client_certificate_pem;
   string _client_certificate_pem;
   string _client_certificate_passphrase;
   string _client_certificate_passphrase;

+ 232 - 0
panda/src/downloader/httpCookie.I

@@ -0,0 +1,232 @@
+// Filename: httpCookie.I
+// Created by:  drose (26Aug04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPCookie::Constructor
+//       Access: Published
+//  Description: Constructs an empty cookie.
+////////////////////////////////////////////////////////////////////
+INLINE HTTPCookie::
+HTTPCookie() :
+  _secure(false)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPCookie::Constructor
+//       Access: Published
+//  Description: Constructs a cookie according to the indicated
+//               string, presumably the tag of a Set-Cookie header.
+//               There is no way to detect a formatting error in the
+//               string with this constructor.
+////////////////////////////////////////////////////////////////////
+INLINE HTTPCookie::
+HTTPCookie(const string &format, const URLSpec &url) {
+  parse_set_cookie(format, url);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPCookie::Constructor
+//       Access: Published
+//  Description: Constructs a cookie with the indicated name, path,
+//               and domain values, but no other data.  This is most
+//               useful for looking up an existing cookie in the
+//               HTTPClient.
+////////////////////////////////////////////////////////////////////
+INLINE HTTPCookie::
+HTTPCookie(const string &name, const string &path, const string &domain) :
+  _name(name),
+  _path(path),
+  _domain(domain),
+  _secure(false)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPCookie::Destructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE HTTPCookie::
+~HTTPCookie() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPCookie::set_name
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void HTTPCookie::
+set_name(const string &name) {
+  _name = name;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPCookie::get_name
+//       Access: Published
+//  Description: Returns the name of the cookie.  This is the key
+//               value specified by the server.
+////////////////////////////////////////////////////////////////////
+INLINE const string &HTTPCookie::
+get_name() const {
+  return _name;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPCookie::set_value
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void HTTPCookie::
+set_value(const string &value) {
+  _value = value;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPCookie::get_value
+//       Access: Published
+//  Description: Returns the value of the cookie.  This is the
+//               arbitrary string associated with the cookie's name,
+//               as specified by the server.
+////////////////////////////////////////////////////////////////////
+INLINE const string &HTTPCookie::
+get_value() const {
+  return _value;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPCookie::set_domain
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void HTTPCookie::
+set_domain(const string &domain) {
+  _domain = domain;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPCookie::get_domain
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE const string &HTTPCookie::
+get_domain() const {
+  return _domain;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPCookie::set_path
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void HTTPCookie::
+set_path(const string &path) {
+  _path = path;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPCookie::get_path
+//       Access: Published
+//  Description: Returns the prefix of the URL paths on the server for
+//               which this cookie will be sent.
+////////////////////////////////////////////////////////////////////
+INLINE const string &HTTPCookie::
+get_path() const {
+  return _path;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPCookie::set_expires
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void HTTPCookie::
+set_expires(const HTTPDate &expires) {
+  _expires = expires;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPCookie::clear_expires
+//       Access: Published
+//  Description: Removes the expiration date on the cookie.
+////////////////////////////////////////////////////////////////////
+INLINE void HTTPCookie::
+clear_expires() {
+  _expires = HTTPDate();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPCookie::has_expires
+//       Access: Published
+//  Description: Returns true if the cookie has an expiration date,
+//               false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool HTTPCookie::
+has_expires() const {
+  return _expires.is_valid();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPCookie::get_expires
+//       Access: Published
+//  Description: Returns the expiration date of the cookie if it is
+//               set, or an invalid date if it is not.
+////////////////////////////////////////////////////////////////////
+INLINE HTTPDate HTTPCookie::
+get_expires() const {
+  return _expires;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPCookie::set_secure
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void HTTPCookie::
+set_secure(bool secure) {
+  _secure = secure;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPCookie::get_secure
+//       Access: Published
+//  Description: Returns true if the server has indicated this is a
+//               "secure" cookie which should only be sent over an
+//               HTTPS channel.
+////////////////////////////////////////////////////////////////////
+INLINE bool HTTPCookie::
+get_secure() const {
+  return _secure;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPCookie::is_expired
+//       Access: Published
+//  Description: Returns true if the cookie's expiration date is
+//               before the indicated date, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool HTTPCookie::
+is_expired(const HTTPDate &now) const {
+  return _expires.is_valid() && _expires < now;
+}
+
+INLINE ostream &operator << (ostream &out, const HTTPCookie &cookie) {
+  cookie.output(out);
+  return out;
+}

+ 221 - 0
panda/src/downloader/httpCookie.cxx

@@ -0,0 +1,221 @@
+// Filename: httpCookie.cxx
+// Created by:  drose (26Aug04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "httpCookie.h"
+
+#ifdef HAVE_SSL
+
+#include "ctype.h"
+#include "httpChannel.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPCookie::operator <
+//       Access: Published
+//  Description: The sorting operator allows the cookies to be stored
+//               in a single dictionary; it returns nonequal only if
+//               the cookies are different in name, path, or domain.
+////////////////////////////////////////////////////////////////////
+bool HTTPCookie::
+operator < (const HTTPCookie &other) const {
+  if (_domain != other._domain) {
+    return _domain < other._domain;
+  }
+
+  if (_path != other._path) {
+    // We use reverse sorting on the path, so that cookies with longer
+    // paths will be sent to the server before cookies with shorter
+    // paths.
+    return _path > other._path;
+  }
+
+  if (_name != other._name) {
+    return _name < other._name;
+  }
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPCookie::update_from
+//       Access: Published
+//  Description: Assuming the operator < method, above, has already
+//               evaluated these two cookies as equal, then assign the
+//               remaining values (value, expiration date, secure
+//               flag) from the indicated cookie.  This is guaranteed
+//               not to change the ordering of the cookie in a set,
+//               and so can be used to update an existing cookie
+//               within a set with new values.
+////////////////////////////////////////////////////////////////////
+void HTTPCookie::
+update_from(const HTTPCookie &other) {
+  nassertv(!(other < *this) && !(*this < other));
+
+  _value = other._value;
+  _expires = other._expires;
+  _secure = other._secure;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPCookie::parse_set_cookie
+//       Access: Published
+//  Description: Separates out the parameter/value pairs of the
+//               Set-Cookie header and assigns the values of the
+//               cookie appropriate.  Returns true if the header is
+//               parsed correctly, false if something is not
+//               understood.
+////////////////////////////////////////////////////////////////////
+bool HTTPCookie::
+parse_set_cookie(const string &format, const URLSpec &url) {
+  _name = string();
+  _value = string();
+  _domain = url.get_server();
+  _path = url.get_path();
+  _expires = HTTPDate();
+  _secure = false;
+
+  bool okflag = true;
+  bool first_param = true;
+  
+  size_t start = 0;
+  while (start < format.length() && isspace(format[start])) {
+    start++;
+  }
+  size_t semicolon = format.find(';', start);
+
+  while (semicolon != string::npos) {
+    if (!parse_cookie_param(format.substr(start, semicolon - start), 
+                            first_param)) {
+      okflag = false;
+    }
+    first_param = false;
+    start = semicolon + 1;
+    while (start < format.length() && isspace(format[start])) {
+      start++;
+    }
+    semicolon = format.find(';', start);
+  }
+
+  if (!parse_cookie_param(format.substr(start), first_param)) {
+    okflag = false;
+  }
+
+  return okflag;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPCookie::matches_url
+//       Access: Published
+//  Description: Returns true if the cookie is appropriate to send
+//               with the indicated URL request, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool HTTPCookie::
+matches_url(const URLSpec &url) const {
+  string server = url.get_server();
+  if (server == _domain || 
+      (server.length() > _domain.length() &&
+       server.substr(server.length() - _domain.length()) == _domain &&
+       server[server.length() - _domain.length() - 1] == '.')) {
+    // The domain matches.
+
+    string path = url.get_path();
+    if (path.length() >= _path.length() &&
+        path.substr(0, _path.length()) == _path) {
+
+      // The path matches too.
+      if (_secure && !url.is_ssl()) {
+        // Oops, can't send a secure cookie over a non-secure connection.
+        return false;
+      }
+
+      return true;
+    }
+  }
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPCookie::output
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void HTTPCookie::
+output(ostream &out) const {
+  out << _name << "=" << _value
+      << "; path=" << _path << "; domain=" << _domain;
+
+  if (has_expires()) {
+    out << "; expires=" << _expires;
+  }
+  
+  if (_secure) {
+    out << "; secure";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HTTPCookie::parse_cookie_param
+//       Access: Private
+//  Description: Called internally by parse_set_cookie() with each
+//               parameter=value pair split out from the header
+//               string.  first_param will be true for the first
+//               parameter (which has special meaning).  This should
+//               return true on success, false on failure.
+////////////////////////////////////////////////////////////////////
+bool HTTPCookie::
+parse_cookie_param(const string &param, bool first_param) {
+  size_t equals = param.find('=');
+
+  string key, value;
+  if (equals == string::npos) {
+    key = param;
+  } else {
+    key = param.substr(0, equals);
+    value = param.substr(equals + 1);
+  }
+
+  if (first_param) {
+    _name = key;
+    _value = value;
+
+  } else {
+    key = HTTPChannel::downcase(key);
+    if (key == "expires") {
+      _expires = HTTPDate(value);
+      if (!_expires.is_valid()) {
+        return false;
+      }
+
+    } else if (key == "path") {
+      _path = value;
+
+    } else if (key == "domain") {
+      _domain = HTTPChannel::downcase(value);
+
+    } else if (key == "secure") {
+      _secure = true;
+
+    } else {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+#endif  // HAVE_SSL

+ 93 - 0
panda/src/downloader/httpCookie.h

@@ -0,0 +1,93 @@
+// Filename: httpCookie.h
+// Created by:  drose (26Aug04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef HTTPCOOKIE_H
+#define HTTPCOOKIE_H
+
+#include "pandabase.h"
+
+// This module requires OpenSSL to compile, even if you do not intend
+// to use this to establish https connections; this is because it uses
+// the OpenSSL library to portably handle all of the socket
+// communications.
+
+#ifdef HAVE_SSL
+
+#include "httpDate.h"
+#include "urlSpec.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : HTTPCookie
+// Description : A cookie sent from an HTTP server to be stored on the
+//               client and returned when the path and/or domain
+//               matches.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAEXPRESS HTTPCookie {
+PUBLISHED:
+  INLINE HTTPCookie();
+  INLINE HTTPCookie(const string &format, const URLSpec &url);
+  INLINE HTTPCookie(const string &name, const string &path, const string &domain);
+  INLINE ~HTTPCookie();
+
+  INLINE void set_name(const string &name);
+  INLINE const string &get_name() const;
+
+  INLINE void set_value(const string &value);
+  INLINE const string &get_value() const;
+
+  INLINE void set_domain(const string &domain);
+  INLINE const string &get_domain() const;
+
+  INLINE void set_path(const string &path);
+  INLINE const string &get_path() const;
+
+  INLINE void set_expires(const HTTPDate &expires);
+  INLINE void clear_expires();
+  INLINE bool has_expires() const;
+  INLINE HTTPDate get_expires() const;
+
+  INLINE void set_secure(bool flag);
+  INLINE bool get_secure() const;
+
+  bool operator < (const HTTPCookie &other) const;
+  void update_from(const HTTPCookie &other);
+
+  bool parse_set_cookie(const string &format, const URLSpec &url);
+  INLINE bool is_expired(const HTTPDate &now = HTTPDate::now()) const;
+  bool matches_url(const URLSpec &url) const;
+
+  void output(ostream &out) const;
+
+private:
+  bool parse_cookie_param(const string &param, bool first_param);
+
+  string _name;
+  string _value;
+  string _domain;
+  string _path;
+  HTTPDate _expires;
+  bool _secure;
+};
+
+INLINE ostream &operator << (ostream &out, const HTTPCookie &cookie);
+
+#include "httpCookie.I"
+
+#endif  // HAVE_SSL
+
+#endif