//
// 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;
}