You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
514 lines
11 KiB
514 lines
11 KiB
//
|
|
// 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 <http://www.htdig.org/>
|
|
// Part of the ht://Check package <http://htcheck.sourceforge.net/>
|
|
// 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
|
|
// <http://www.gnu.org/copyleft/lgpl.html>
|
|
//
|
|
// $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 <stdlib.h>
|
|
#include <ctype.h>
|
|
|
|
#ifdef HAVE_STD
|
|
#include <iostream>
|
|
#ifdef HAVE_NAMESPACES
|
|
using namespace std;
|
|
#endif
|
|
#else
|
|
#include <iostream.h>
|
|
#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;
|
|
|
|
}
|
|
|
|
|