www.pudn.com > pueblo.zip > ChHttpCookie.cpp
/*----------------------------------------------------------------------------
_ _ _
/\ | | | (_)
/ \ _ __ __| |_ __ ___ _ __ ___ ___ __| |_ __ _
/ /\ \ | '_ \ / _` | '__/ _ \| '_ ` _ \ / _ \/ _` | |/ _` |
/ ____ \| | | | (_| | | | (_) | | | | | | __/ (_| | | (_| |
/_/ \_\_| |_|\__,_|_| \___/|_| |_| |_|\___|\__,_|_|\__,_|
The contents of this file are subject to the Andromedia Public
License Version 1.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of
the License at http://www.andromedia.com/APL/
Software distributed under the License is distributed on an
"AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
implied. See the License for the specific language governing
rights and limitations under the License.
The Original Code is Pueblo client code, released November 4, 1998.
The Initial Developer of the Original Code is Andromedia Incorporated.
Portions created by Andromedia are Copyright (C) 1998 Andromedia
Incorporated. All Rights Reserved.
Andromedia Incorporated 415.365.6700
818 Mission Street - 2nd Floor 415.365.6701 fax
San Francisco, CA 94103
Contributor(s):
--------------------------------------------------------------------------
Chaco team: Dan Greening, Glenn Crocker, Jim Doubek,
Coyote Lussier, Pritham Shetty.
Wrote and designed original codebase.
------------------------------------------------------------------------------
This file contains the implementation of the Netscape HTTP cookies.
----------------------------------------------------------------------------*/
#include "headers.h"
#include
#include
#include
#include
#include "ChHttpCookie.h"
#if !defined(CH_PUEBLO_PLUGIN)
#include "resource.h"
#else
#include "vwrres.h"
#endif
CH_INTERN_VAR const char* pstrCookieTokens[] =
{
TEXT( "domain" ),
TEXT( "path" ),
TEXT( "expires" ),
TEXT( "secure" ),
};
#define COOKIE_DOMAIN 0
#define COOKIE_PATH 1
#define COOKIE_EXPIRES 2
#define COOKIE_SECURE 3
#define COOKIE_DELIMITOR TEXT( '\t' )
#define COOKIE_NEWLINE TEXT( "\r\n" )
#define COOKIE_FILE TEXT( "cookie.txt" )
/*----------------------------------------------------------------------------
ChHttpCookie::ChHttpCookie
----------------------------------------------------------------------------*/
ChHttpCookie::ChHttpCookie() :
m_boolSaved( true )
{
if ( !GetModuleFileName( NULL, m_strCookieFile.GetBuffer( 512 ), 512 ) )
{
m_strCookieFile.ReleaseBuffer();
TRACE( "GetModuleFileName function failed !!!!" );
ASSERT( 0 );
}
m_strCookieFile.ReleaseBuffer();
// path of application, scrpit modules are stored relative to app path
m_strCookieFile = m_strCookieFile.Left( m_strCookieFile.ReverseFind( TEXT( '\\' ) ) + 1 );
m_strCookieFile += COOKIE_FILE;
}
/*----------------------------------------------------------------------------
ChHttpCookie::~ChHttpCookie
----------------------------------------------------------------------------*/
class ChDeleteAll : public ChVisitor2
{
public:
ChDeleteAll() {}
virtual bool Visit( const string& strDomain, ChCookieList* const& pList );
};
bool ChDeleteAll::Visit( const string& strDomain, ChCookieList* const& pList )
{
pList->Empty();
delete (ChCookieList*)pList;
return true;
}
ChHttpCookie::~ChHttpCookie()
{
ChDeleteAll delAll;
m_cookieTree.Infix( delAll );
m_cookieTree.Erase();
}
/*----------------------------------------------------------------------------
ChHttpCookie::ReadCookieFile
----------------------------------------------------------------------------*/
bool ChHttpCookie::ReadCookieFile( const char* pstrFile /*= 0 */ )
{
if ( !pstrFile )
{
pstrFile = m_strCookieFile;
}
fstream streamIn( pstrFile, ios::in, 0 );
if ( !streamIn.is_open() )
{
return false;
}
char* pstrBuffer = new char[4094];
ASSERT( pstrBuffer );
int iCount = streamIn.read( pstrBuffer, 4093 ).gcount();
if ( 0 == iCount )
{
delete []pstrBuffer;
streamIn.close();
return false;
}
int iNext = 0;
string strLine;
do
{
iNext = ReadLine( pstrBuffer, iCount, iNext, strLine, &streamIn );
{
string strDomain;
ChCookie cookie;
if ( MakeCookie( strLine, strDomain, cookie ) )
{
AddCookie( strDomain, cookie );
}
}
}
while( strLine.GetLength() && iCount );
delete [] pstrBuffer;
streamIn.close();
return true;
}
/*----------------------------------------------------------------------------
ChHttpCookie::WriteCookieFile
----------------------------------------------------------------------------*/
class ChWriteCookie : public ChVisitor2
{
public:
ChWriteCookie( fstream* pStream ) : m_pStream( pStream ) {}
virtual bool Visit( const string& strDomain, ChCookieList* const& pList );
private :
fstream* m_pStream;
};
bool ChWriteCookie::Visit( const string& strDomain, ChCookieList* const& pList )
{
ChPosition pos = pList->GetHeadPosition();
while ( pos )
{
ChCookie cookie = pList->Get( pos );
if ( (unsigned long)cookie.GetExpiresValue().GetTime() != 0 )
{ // Store only if we have a expiration date
string strWrite( strDomain );
strWrite += COOKIE_DELIMITOR;
ChCookie cookie = pList->Get( pos );
strWrite += cookie.GetPath();
strWrite += COOKIE_DELIMITOR;
strWrite += cookie.IsSecure() ? TEXT( "TRUE" ) : TEXT( "FALSE" );
strWrite += COOKIE_DELIMITOR;
string strTime;
strTime.Format( "%lu", (unsigned long)cookie.GetExpiresValue().GetTime() );
strWrite += strTime;
strWrite += COOKIE_DELIMITOR;
strWrite += cookie.GetNameValue();
strWrite += COOKIE_DELIMITOR;
strWrite += COOKIE_NEWLINE;
m_pStream->write( strWrite, strWrite.GetLength() );
}
pList->GetNext( pos );
}
return true;
}
bool ChHttpCookie::WriteCookieFile( const char* pstrFile /*= 0 */)
{
if ( !pstrFile )
{
pstrFile = m_strCookieFile;
}
fstream streamOut( pstrFile, ios::out | ios::trunc , filebuf::sh_none );
if ( !streamOut.is_open() )
{
return false;
}
// Write the file header
string strHdr;
// Calling loastring from a DLL is not very safe if the
// hResource handle is not set properly
ChUtil::LoadString( IDS_COOKIE_HDR1, strHdr );
strHdr += COOKIE_NEWLINE;
streamOut.write( strHdr, strHdr.GetLength() );
ChUtil::LoadString( IDS_COOKIE_HDR2, strHdr );
strHdr += COOKIE_NEWLINE;
streamOut.write( strHdr, strHdr.GetLength() );
ChUtil::LoadString( IDS_COOKIE_HDR3, strHdr );
strHdr += COOKIE_NEWLINE;
streamOut.write( strHdr, strHdr.GetLength() );
ChWriteCookie writeCookie( &streamOut );
m_cookieTree.Infix( writeCookie );
return true;
}
/*----------------------------------------------------------------------------
ChHttpCookie::GetCookie
----------------------------------------------------------------------------*/
bool ChHttpCookie::GetCookie( const string& strURL, string& strCookie, bool& boolSecure )
{
string strDomain;
string strPath;
strCookie.Empty();
boolSecure = false;
if ( !strURL.IsEmpty() )
{
ChURLParts urlParts;
if ( !urlParts.GetURLParts( strURL ) )
{
return false;
}
string strTemp( urlParts.GetHostName() );
strDomain = strTemp.Right( strTemp.GetLength() - strTemp.Find( TEXT( '.' ) ) );
strPath = TEXT( "/" );
strPath += (urlParts.GetAbsPath() ? urlParts.GetAbsPath() : TEXT( "" ));
if ( urlParts.GetRelPath() )
{
if ( strPath.GetLength() &&
TEXT( '/' ) != strPath[strPath.GetLength()-1] )
{
strPath += TEXT( '/' );
}
strPath += urlParts.GetRelPath();
}
}
strDomain.MakeLower();
// Do tail match
int iIndex = strDomain.ReverseFind( TEXT( '.' ) );
while ( iIndex > 0 )
{
iIndex--;
while( iIndex && strDomain[iIndex] != TEXT( '.' ) )
{
iIndex--;
}
string strTail( strDomain.Right( strDomain.GetLength() - iIndex ) );
ChCookieList** ppList;
ppList = m_cookieTree.Find( strTail );
if ( ppList && *ppList )
{
DoPathMatch( *ppList, strPath, strCookie );
}
}
return !strCookie.IsEmpty();
}
/*----------------------------------------------------------------------------
ChHttpCookie::SetCookie
----------------------------------------------------------------------------*/
bool ChHttpCookie::SetCookie( const string& strURL, const string& strCookie )
{ // What we get here is a HTTP response header Set Cookie result, scan this string
// and set all the values in our cookie list
string strCookieNameValue;
ChTime timeExpires;
string strPath( '/' );
string strDomain;
bool boolSecure = false;
LPCSTR pstrParse = strCookie;
bool boolSuccess = false;
while ( *pstrParse )
{
string strType, strValue;
// Skip white space
while ( *pstrParse && isspace( *pstrParse ) )
{
++pstrParse;
}
// Get the token
while ( *pstrParse && !isspace( *pstrParse ) && *pstrParse != TEXT( '=' ) )
{
strType += *pstrParse++;
}
// Skip white space
while ( *pstrParse && isspace( *pstrParse ) )
{
++pstrParse;
}
if ( *pstrParse == TEXT( '=' ) )
{
++pstrParse;
// Skip white space
while ( *pstrParse && isspace( *pstrParse ) )
{
++pstrParse;
}
// Get the value
while ( *pstrParse && *pstrParse != TEXT( ';' ) )
{
strValue += *pstrParse++;
}
}
if ( *pstrParse == TEXT( ';' ) )
{
pstrParse++;
}
// Find the type of token we have
if ( strType.CompareNoCase( pstrCookieTokens[COOKIE_DOMAIN] ) == 0 )
{
if ( strValue[0] != TEXT( '.' ) )
{
strDomain = TEXT( '.' );
}
strDomain += strValue;
}
else if ( strType.CompareNoCase( pstrCookieTokens[COOKIE_PATH] ) == 0 )
{
if ( !strValue.IsEmpty() )
{
if ( strValue[0] != TEXT( '/' ) )
{
strPath += strValue;
}
else
{
strPath = strValue;
}
}
}
else if ( strType.CompareNoCase( pstrCookieTokens[COOKIE_EXPIRES] ) == 0 )
{
ChTime time( strValue );
timeExpires = time;
}
else if ( strType.CompareNoCase( pstrCookieTokens[COOKIE_SECURE] ) == 0 )
{
boolSecure = true;
}
else if ( !strValue.IsEmpty() )
{
if ( !strCookieNameValue.IsEmpty() )
{
strCookieNameValue += TEXT( "; " );
}
strCookieNameValue += strType;
strCookieNameValue += TEXT( '=' );
strCookieNameValue += strValue;
}
}
if ( !strCookieNameValue.IsEmpty() )
{
// If there is no doamin then use the one in the URL
if ( strDomain.IsEmpty() )
{
ChURLParts urlParts;
urlParts.GetURLParts( strURL );
string strTemp( urlParts.GetHostName() );
strDomain = strTemp.Right( strTemp.GetLength() - strTemp.Find( TEXT( '.' )) );
}
if ( IsValidDomain( strDomain ) )
{
// Add the cookie
ChCookie cookie( timeExpires, boolSecure, strCookieNameValue, strPath );
strDomain.MakeLower();
AddCookie( strDomain, cookie );
boolSuccess = true;
}
}
return boolSuccess;
}
///////////////////////////////////////////////////////////////////////////////
///////////
////////// Private methods
//////////
//////////////////////////////////////////////////////////////////////////////
/*----------------------------------------------------------------------------
ChHttpCookie::AddCookie
----------------------------------------------------------------------------*/
void ChHttpCookie::AddCookie( const string& strDomain, ChCookie& cookie )
{
ChCookieList** ppList;
ppList = m_cookieTree.Find( strDomain );
if ( (unsigned long)cookie.GetExpiresValue().GetTime() != 0 )
{
m_boolSaved = false;
}
if ( ppList && *ppList )
{ // We have a cookie list for this domain, Add, replace or delete based on cookie
// values.
ChCookieList* pList = *ppList;
bool boolDelete = false;
if ( cookie.GetExpiresValue() != 0 && cookie.GetExpiresValue() < ChTime::GetCurrentTime() )
{ // delete the cookie if present
boolDelete = true;
}
bool boolAdd = true;
ChPosition pos = pList->GetHeadPosition();
while ( pos )
{
ChCookie oldCookie = pList->Get( pos );
if ( oldCookie.GetPath() == cookie.GetPath()
&& MatchNames( oldCookie.GetNameValue(), cookie.GetNameValue() ) )
{ // does the names match
if ( boolDelete )
{
pList->Remove( pos );
boolAdd = false;
if ( pList->GetCount() == 0 )
{ // No more cokkies for this domain
m_cookieTree.Delete( strDomain );
delete pList;
}
}
else
{
pList->Set( pos, cookie );
boolAdd = false;
}
pos = 0;
}
else
{
pList->GetNext( pos );
}
}
if ( boolAdd && !boolDelete )
{
pList->AddTail( cookie );
}
}
else
{
ChCookieList* pList = new ChCookieList();
ASSERT( pList );
pList->AddHead( cookie );
m_cookieTree.Insert( strDomain, pList );
}
}
/*----------------------------------------------------------------------------
ChHttpCookie::MatchNames
----------------------------------------------------------------------------*/
bool ChHttpCookie::MatchNames( const string& strName1, const string& strName2 )
{
if ( strName1.IsEmpty() || strName2.IsEmpty() )
{
return false;
}
string strChunk = strName1;
int iIndex = strChunk.Find( TEXT( ';' ) );
while ( iIndex != -1 )
{
string strTemp = strChunk.Left( iIndex );
// Get the name
strTemp = strTemp.Left( strChunk.Find( TEXT( '=' ) ));
int iIndex2;
if ( (iIndex2 = strName2.Find(strTemp )) == -1 )
{
return false;
}
else
{ // check if it is a exact match
bool boolMatch = true;
if ( iIndex2 )
{
boolMatch = (strName2[iIndex2] == TEXT( ' ' ));
}
if ( (iIndex2 + strTemp.GetLength() ) < strName2.GetLength() )
{
boolMatch = (strName2[iIndex2 + strTemp.GetLength() ] == TEXT( '=' ));
}
else
{
boolMatch = false;
}
if ( !boolMatch )
{
return false;
}
}
// Go to the next name
strChunk = strChunk.Right( strChunk.GetLength() - iIndex - 1 );
iIndex = strChunk.Find( TEXT( ';' ) );
}
return true;
}
/*----------------------------------------------------------------------------
ChHttpCookie::IsValidDomain
----------------------------------------------------------------------------*/
bool ChHttpCookie::IsValidDomain( const string& strDomain )
{
int iNumDots = 0;
LPCSTR pstrDomain = strDomain;
while ( *pstrDomain )
{
if ( *pstrDomain == TEXT( '.' ) )
{
iNumDots++;
}
pstrDomain++;
}
if ( iNumDots < 2 )
{
return false;
}
else if ( iNumDots == 2 )
{ // see if it is a standard domain
// "COM", "EDU", "NET", "ORG", "GOV", "MIL", and "INT".
if ( strDomain.Find( ".com" ) != -1 ||
strDomain.Find( ".net" ) != -1 ||
strDomain.Find( ".edu" ) != -1 ||
strDomain.Find( ".org" ) != -1 ||
strDomain.Find( ".gov" ) != -1 ||
strDomain.Find( ".mil" ) != -1 ||
strDomain.Find( ".int" ) != -1 )
{
return true;
}
}
else
{
return true;
}
return false;
}
/*----------------------------------------------------------------------------
ChHttpCookie::DoPathMatch
----------------------------------------------------------------------------*/
bool ChHttpCookie::DoPathMatch( ChCookieList* pList, const char* pstrPath, string& strCookie )
{
ChPosition pos = pList->GetHeadPosition();
while ( pos )
{
ChCookie cookie = pList->Get( pos );
LPCSTR pstrCookiePath = cookie.GetPath();
// do the match
int iIndex = 0;
while( pstrPath[iIndex] && pstrCookiePath[iIndex] &&
pstrCookiePath[iIndex] == pstrPath[iIndex] )
{
iIndex++;
}
if ( pstrCookiePath[iIndex] == 0 )
{
strCookie += cookie.GetNameValue();
}
// Get the next path to match
pList->GetNext( pos );
}
return !strCookie.IsEmpty();
}
/*----------------------------------------------------------------------------
ChHttpCookie::MakeCookie
----------------------------------------------------------------------------*/
bool ChHttpCookie::MakeCookie( string& strBuffer, string& strDomain, ChCookie& cookie )
{
if ( strBuffer.GetLength() == 0 ||
( strBuffer.GetLength() && strBuffer[0] == TEXT( '#' ) ) )
{
return false;
}
// Domain
int iIndex = strBuffer.Find( COOKIE_DELIMITOR );
if ( iIndex == -1 )
{
return false;
}
strDomain = strBuffer.Left( iIndex );
strBuffer = strBuffer.Right( strBuffer.GetLength() - iIndex - 1 );
// Path
iIndex = strBuffer.Find( COOKIE_DELIMITOR );
if ( iIndex == -1 )
{
return false;
}
string strTemp;
strTemp = strBuffer.Left( iIndex );
strBuffer = strBuffer.Right( strBuffer.GetLength() - iIndex - 1 );
cookie.SetPath( strTemp );
// Secure
iIndex = strBuffer.Find( COOKIE_DELIMITOR );
if ( iIndex == -1 )
{
return false;
}
strTemp = strBuffer.Left( iIndex );
strBuffer = strBuffer.Right( strBuffer.GetLength() - iIndex - 1 );
cookie.SetSecure( strTemp == TEXT( "FALSE" ) ? false : true );
// Expires
iIndex = strBuffer.Find( COOKIE_DELIMITOR );
if ( iIndex == -1 )
{
return false;
}
strTemp = strBuffer.Left( iIndex );
strBuffer = strBuffer.Right( strBuffer.GetLength() - iIndex - 1 );
ChTime timeExpires( ((ChTime_t)atol( strTemp )) );
cookie.SetExpiresValue( timeExpires );
// Name value
strBuffer.TrimLeft();
strBuffer.TrimRight();
cookie.SetNameValue( strBuffer );
return true;
}
int ChHttpCookie::ReadLine( char*& pstrBuffer, int& iCount, int iNext, string& strLine, fstream* pstreamIn )
{
int iStop = iNext;
strLine.Empty();
// skip all new lines
while( iStop < iCount &&
( pstrBuffer[iStop] == TEXT( '\r' ) || pstrBuffer[iStop] == TEXT( '\n' ) ) )
{
iStop++;
}
iNext = iStop;
while ( iCount )
{
while( iStop < iCount &&
( pstrBuffer[iStop] != TEXT( '\r' ) && pstrBuffer[iStop] != TEXT( '\n' ) ) )
{
iStop++;
}
if ( pstrBuffer[iStop] == TEXT( '\r' ) || pstrBuffer[iStop] == TEXT( '\n' ) )
{
pstrBuffer[iStop] = 0;
iStop++;
strLine += &pstrBuffer[iNext];
return iStop;
}
else
{
pstrBuffer[iCount] = 0;
strLine += &pstrBuffer[iNext];
iNext = iStop = 0;
iCount = pstreamIn->read( pstrBuffer, 4093 ).gcount();
}
}
return 0;
}