// // HtCookie.cc // // HtCookie: This class represents a HTTP cookie. // // HtCookie.cc // // by Robert La Ferla. Started 12/5/2000. // Reviewed by G.Bartolini - since 24 Feb 2001 // Cookies input file by G.Bartolini - since 27 Jan 2003 // //////////////////////////////////////////////////////////// // // The HtCookie class represents a single HTTP cookie. // // See "PERSISTENT CLIENT STATE HTTP COOKIES" Specification // at http://www.netscape.com/newsref/std/cookie_spec.html // Modified according to RFC2109 (max age and version attributes) // // This class also manages the creation of a cookie from a line // of a cookie file format, which is a text file as proposed by Netscape; // each line contains a name-value pair for a cookie. // Fields within a single line are separated by the 'tab' character; // /////// // // Part of the ht://Dig package // Part of the ht://Check package // Copyright (c) 2001-2004 The ht://Dig Group // For copyright details, see the file COPYING in your distribution // or the GNU Library General Public License (LGPL) version 2 or later // // // $Id: HtCookie.cc,v 1.14 2004/05/28 13:15:22 lha Exp $ // #ifdef HAVE_CONFIG_H #include "htconfig.h" #endif /* HAVE_CONFIG_H */ #include "HtCookie.h" #include #include #ifdef HAVE_STD #include #ifdef HAVE_NAMESPACES using namespace std; #endif #else #include #endif /* HAVE_STD */ /////// // Static variables initialization /////// // Debug level int HtCookie::debug = 0; // Precompiled constants regarding the cookies file format (field order) #define COOKIES_FILE_DOMAIN 0 #define COOKIES_FILE_FLAG 1 #define COOKIES_FILE_PATH 2 #define COOKIES_FILE_SECURE 3 #define COOKIES_FILE_EXPIRES 4 #define COOKIES_FILE_NAME 5 #define COOKIES_FILE_VALUE 6 // Default constructor HtCookie::HtCookie() : name(0), value(0), path(0), domain(0), expires(0), isSecure(false), isDomainValid(true), srcURL(0), issue_time(), max_age(-1), rfc_version(0) { } // Constructor that accepts a name and a value // and the calling URL HtCookie::HtCookie(const String &aName, const String &aValue, const String& aURL) : name(aName), value(aValue), path(0), domain(0), expires(0), isSecure(false), isDomainValid(true), srcURL(aURL), issue_time(), max_age(-1), rfc_version(0) { } // Constructor from a server response header HtCookie::HtCookie(const String &setCookieLine, const String& aURL) : name(0), value(0), path(0), domain(0), expires(0), isSecure(false), isDomainValid(true), srcURL(aURL), issue_time(), max_age(-1), rfc_version(0) { String cookieLineStr(setCookieLine); char * token; const char * str; if (debug > 5) cout << "Creating cookie from response header: " << cookieLineStr << endl; // Parse the cookie line token = strtok(cookieLineStr, "="); if (token != NULL) { SetName(token); token = strtok(NULL, ";"); SetValue(token); } // Get all the fields returned by the server while ((str = strtok(NULL, "="))) { const char * ctoken; token = stripAllWhitespace(str); if (mystrcasecmp(token, "path") == 0) { // Let's grab the path ctoken = strtok(NULL, ";"); SetPath(ctoken); } else if (mystrcasecmp(token, "expires") == 0) { // Let's grab the expiration date HtDateTime dt; ctoken = strtok(NULL, ";"); if (ctoken && SetDate(ctoken, dt)) SetExpires(&dt); else SetExpires(0); } else if (mystrcasecmp(token, "secure") == 0) SetIsSecure(true); else if (mystrcasecmp(token, "domain") == 0) { ctoken = strtok(NULL, ";"); SetDomain(ctoken); } else if (mystrcasecmp(token, "max-age") == 0) { ctoken = strtok(NULL, ";"); SetMaxAge(atoi(ctoken)); } else if (mystrcasecmp(token, "version") == 0) { ctoken = strtok(NULL, ";"); SetVersion(atoi(ctoken)); } if (token) delete[](token); } if (debug>3) printDebug(); } // Constructor from a line of a cookie file (according to Netscape format) HtCookie::HtCookie(const String &CookieFileLine) : name(0), value(0), path(0), domain(0), expires(0), isSecure(false), isDomainValid(true), srcURL(0), issue_time(), max_age(-1), rfc_version(0) { String cookieLineStr(CookieFileLine); char * token; const char * str; if (debug > 5) cout << "Creating cookie from a cookie file line: " << cookieLineStr << endl; // Parse the cookie line if ((str = strtok(cookieLineStr, "\t"))) { int num_field = 0; int expires_value; // Holds the expires value that will be read // According to the field number, set the appropriate object member's value do { token = stripAllWhitespace(str); switch(num_field) { case COOKIES_FILE_DOMAIN: SetDomain(token); break; case COOKIES_FILE_FLAG: // Ignored break; case COOKIES_FILE_PATH: SetPath(token); break; case COOKIES_FILE_SECURE: if (mystrcasecmp(token, "false")) SetIsSecure(true); else SetIsSecure(false); break; case COOKIES_FILE_EXPIRES: if ((expires_value = atoi(token) > 0)) // Sets the expires value only if > 0 { time_t tmp = atoi(token); // avoid ambiguous arg list expires = new HtDateTime(tmp); } break; case COOKIES_FILE_NAME: SetName(token); break; case COOKIES_FILE_VALUE: SetValue(token); break; } ++num_field; } while((str = strtok(NULL, "\t"))); } if (debug>3) printDebug(); } // Copy constructor HtCookie::HtCookie(const HtCookie& rhs) : name(rhs.name), value(rhs.value), path(rhs.path), domain(rhs.domain), expires(0), isSecure(rhs.isSecure), isDomainValid(rhs.isDomainValid), srcURL(rhs.srcURL), issue_time(rhs.issue_time), max_age(rhs.max_age), rfc_version(rhs.rfc_version) { if (rhs.expires) expires = new HtDateTime(*rhs.expires); } // Destructor HtCookie::~HtCookie() { // Delete the DateTime info if (expires) delete expires; } // Set the expires datetime void HtCookie::SetExpires(const HtDateTime *aDateTime) { // // If expires has not yet been set, // we just copy the reference // otherwise, we just change the contents // of our internal attribute // // We don't have a valid datetime, it's null if (!aDateTime) { if (expires) delete expires; expires=0; } else { // We do have a valid datetime // Let's check whether expires has already been created if (!expires) expires = new HtDateTime(*aDateTime); // No ... let's create it and copy it } } // Strip all the whitespaces char * HtCookie::stripAllWhitespace(const char * str) { int len; int i; int j; char * newstr; len = strlen(str); newstr = new char[len + 1]; j = 0; for (i = 0; i < len; i++) { char c; c = str[i]; if (isspace(c) == 0) newstr[j++] = c; } newstr[j++] = (char)0; return newstr; } // Copy operator overload const HtCookie &HtCookie::operator = (const HtCookie &rhs) { // Prevent from copying itself if (this == &rhs) return *this; // Copy all the values name = rhs.name; value = rhs.value; path = rhs.path; domain = rhs.domain; srcURL = rhs.srcURL; // Set the expiration time SetExpires(rhs.expires); isSecure = rhs.isSecure; isDomainValid = rhs.isDomainValid; issue_time = rhs.issue_time; max_age = rhs.max_age; return *this; } // Print a debug message ostream& HtCookie::printDebug(ostream &out) { out << " - "; out << "NAME=" << name << " VALUE=" << value << " PATH=" << path; if (expires) out << " EXPIRES=" << expires->GetRFC850(); if (domain.length()) out << " DOMAIN=" << domain << " (" << (isDomainValid?"VALID":"INVALID") << ")"; if (max_age >= 0) out << " MAX-AGE=" << max_age; if (isSecure) out << " SECURE"; if (srcURL.length() > 0) out << " - Issued by: " << srcURL; out << endl; return out; } // // Set the date time value of a cookie's expires // Given an HtDateTime object and a datestring // It returns true if everything goes ok // and false otherwise. // int HtCookie::SetDate(const char *datestring, HtDateTime &dt) { if (!datestring) // for any reason we don't have a string for the date return 0; // and we exit DateFormat df; while (*datestring && isspace(*datestring)) datestring++; // skip initial spaces df = RecognizeDateFormat(datestring); if (df == DateFormat_NotRecognized) { // Not recognized if (debug > 0) cout << "Cookie '" << name << "' date format not recognized: " << datestring << endl; return false; } dt.ToGMTime(); // Set to GM time switch(df) { // Asc Time format case DateFormat_AscTime: dt.SetAscTime((char *)datestring); break; // RFC 1123 case DateFormat_RFC1123: dt.SetRFC1123((char *)datestring); break; // RFC 850 case DateFormat_RFC850: dt.SetRFC850((char *)datestring); break; default: if (debug > 0) cout << "Cookie '" << name << "' date format not handled: " << (int)df << endl; break; } return !(df==DateFormat_NotRecognized); } // Recognize the date sent by the server // // The expires attribute specifies a date string that defines the valid life time // of that cookie. Once the expiration date has been reached, the cookie will no // longer be stored or given out. // // The date string is formatted as: // Wdy, DD-Mon-YYYY HH:MM:SS GMT // This is based on RFC 822, RFC 850, RFC 1036, and RFC 1123, with the variations // that the only legal time zone is GMT and the separators between the elements // of the date must be dashes. // HtCookie::DateFormat HtCookie::RecognizeDateFormat(const char *datestring) { register const char *s; if (datestring) { if ((s=strchr(datestring, ','))) { // A comma is present. // Two chances: RFC1123 or RFC850 if(strchr(s, '-')) return DateFormat_RFC850; // RFC 850 recognized else return DateFormat_RFC1123; // RFC 1123 recognized } else { // No comma present // Let's try C Asctime: Sun Nov 6 08:49:37 1994 if (strlen(datestring) == 24) { return DateFormat_AscTime; } } } return DateFormat_NotRecognized; }