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.
1516 lines
40 KiB
1516 lines
40 KiB
//---------------------------------------------------------------------------
|
|
//
|
|
// COOKIE.CXX
|
|
//
|
|
// Cookie Jar
|
|
//
|
|
// This file implements cookies as defined by Navigator 4 behavior and the
|
|
// specification at http://www.netscape.com/newsref/std/cookie_spec.html.
|
|
// If Navigator 4 and the specification are not in agreement, we try to
|
|
// match the Navigator 4 behavior.
|
|
//
|
|
// The following describes some interesting aspects of cookie behavior.
|
|
//
|
|
// SYNTAX
|
|
//
|
|
// Syntax for cookie is
|
|
//
|
|
// [[name]=] value [; options]
|
|
//
|
|
// The name is everything before "=" with leading and trailing whitespace
|
|
// removed. The value is everything after "=" and before ";" with leading
|
|
// and trailing whitespace removed. The name and value can contain spaces,
|
|
// quotes or any other character except ";" and "=". The name and equal
|
|
// sign are optional.
|
|
//
|
|
// Example: =foo -> name: <blank> value: foo
|
|
// foo -> name: <blank> value: foo
|
|
// foo= -> name: foo value: <blank>
|
|
// ; -> name: <blank> value: <blank>
|
|
//
|
|
// ORDER
|
|
//
|
|
// Cookies with a more specific path are sent before cookies with
|
|
// a less specific path mapping. The domain does not contibute
|
|
// to the ordering of cookies.
|
|
//
|
|
// If the path length of two cookies are equal, then the cookies
|
|
// are ordered by time of creation. Navigator maintains this
|
|
// ordering across domain and path boundaries. IE maintains this
|
|
// ordering for a specific domain and path. It is difficult to match
|
|
// the Navigator behavior and there are no known bugs because of
|
|
// this difference.
|
|
//
|
|
// MATCHING
|
|
//
|
|
// Path matches are done at the character level. Any
|
|
// directory structure in the path is ignored.
|
|
//
|
|
// Navigator matches domains at the character level and ignores
|
|
// the structure of the domain name.
|
|
//
|
|
// Previous versions of IE tossed the leading "." on a domain name.
|
|
// With out this information, character by character compares are
|
|
// can produce incorrect results. For backwards compatibilty with
|
|
// old cookie we continue to match on a component by component basis.
|
|
//
|
|
// Some examples of the difference are:
|
|
//
|
|
// Cookie domain Document domain Navigator match IE match
|
|
// .foo.com foo.com no yes
|
|
// bar.x.com foobar.x.com yes no
|
|
//
|
|
// ACCEPTING COOKIES
|
|
//
|
|
// A cookie is rejected if the path specified in the set cookie
|
|
// header is not a prefix of document's path.
|
|
//
|
|
// Navigator rejects a cookie if the domain specified in the
|
|
// set cookie header does not contain at least two periods
|
|
// or the domain is not a suffix of the document's domain.
|
|
// The suffix match is done on a character by character basis.
|
|
//
|
|
// Navigator ignores all the stuff in the specification about
|
|
// three period matching and the seven special top level domains.
|
|
//
|
|
// IE rejects a cookie if the domain specified by the cookie
|
|
// header does not contain at least one embedded period or the
|
|
// domain is not a suffix of the documents domain.
|
|
//
|
|
// Cookies are accepted if the path specified in the set cookie
|
|
// header is a prefix of the document's path and the domain
|
|
// specified in the set cookie header.
|
|
//
|
|
// The difference in behavior is a result of the matching rules
|
|
// described in the previous section.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
#include <wininetp.h>
|
|
#include "httpp.h"
|
|
|
|
#include "cookiejar.h"
|
|
|
|
#define CCH_COOKIE_MAX (5 * 1024)
|
|
|
|
static char s_achEmpty[] = "";
|
|
|
|
// Hard-coded list of special domains. If any of these are present between the
|
|
// second-to-last and last dot we will require 2 embedded dots.
|
|
// The domain strings are reversed to make the compares easier
|
|
|
|
static const char *s_pachSpecialDomains[] =
|
|
{"MOC", "UDE", "TEN", "GRO", "VOG", "LIM", "TNI" };
|
|
|
|
|
|
struct CookieInfo {
|
|
|
|
char *pchRDomain;
|
|
char *pchPath;
|
|
char *pchName;
|
|
char *pchValue;
|
|
unsigned long dwFlags;
|
|
FILETIME ftExpiration;
|
|
};
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// String utilities
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
static BOOL
|
|
IsZero(FILETIME *pft)
|
|
{
|
|
return pft->dwLowDateTime == 0 && pft->dwHighDateTime == 0;
|
|
}
|
|
|
|
static char *
|
|
StrnDup(const char *pch, int cch)
|
|
{
|
|
char *pchAlloc = (char *)ALLOCATE_MEMORY(LMEM_FIXED, cch + 1);
|
|
if (!pchAlloc)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return NULL;
|
|
}
|
|
|
|
memcpy(pchAlloc, pch, cch);
|
|
pchAlloc[cch] = 0;
|
|
|
|
return pchAlloc;
|
|
}
|
|
|
|
static BOOL
|
|
IsPathMatch(const char *pchPrefix, const char *pchStr)
|
|
{
|
|
while (*pchPrefix == *pchStr && *pchStr)
|
|
{
|
|
pchPrefix += 1;
|
|
pchStr += 1;
|
|
}
|
|
|
|
return *pchPrefix == 0;
|
|
}
|
|
|
|
static BOOL
|
|
IsDomainMatch(const char *pchPrefix, const char *pchStr)
|
|
{
|
|
while (*pchPrefix == *pchStr && *pchStr)
|
|
{
|
|
pchPrefix += 1;
|
|
pchStr += 1;
|
|
}
|
|
|
|
return *pchPrefix == 0 && (*pchStr == 0 || *pchStr == '.');
|
|
}
|
|
|
|
static BOOL
|
|
IsPathLegal(const char *pchHeader, const char *pchDocument)
|
|
{
|
|
return TRUE;
|
|
|
|
/*
|
|
|
|
We attempted to implement the specification here.
|
|
It looks like Navigator does not reject cookies
|
|
based on the path attribute. We now consider
|
|
all path attributes to be legal.
|
|
|
|
while (*pchHeader == *pchDocument && *pchDocument)
|
|
{
|
|
pchHeader += 1;
|
|
pchDocument += 1;
|
|
}
|
|
|
|
if (*pchDocument == 0)
|
|
{
|
|
while (*pchHeader && *pchHeader != '/' && *pchHeader != '\\')
|
|
{
|
|
pchHeader += 1;
|
|
}
|
|
}
|
|
|
|
return *pchHeader == 0;
|
|
*/
|
|
}
|
|
|
|
extern PTSTR GlobalSpecialDomains;
|
|
extern PTSTR *GlobalSDOffsets;
|
|
|
|
static BOOL
|
|
IsVerySpecialDomain(const char *pch, int nTopLevel, int nSecond)
|
|
{
|
|
if (!GlobalSpecialDomains)
|
|
{
|
|
HKEY hk;
|
|
if (ERROR_SUCCESS==RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\5.0"),
|
|
0,
|
|
KEY_READ,
|
|
&hk))
|
|
{
|
|
DWORD dwType, dwSize;
|
|
|
|
if ((ERROR_SUCCESS==RegQueryValueEx(hk, "SpecialDomains", NULL, &dwType, NULL, &dwSize))
|
|
&& (REG_SZ==dwType))
|
|
{
|
|
GlobalSpecialDomains = New TCHAR[dwSize];
|
|
if (GlobalSpecialDomains
|
|
&& (ERROR_SUCCESS==RegQueryValueEx(hk, "SpecialDomains", NULL, &dwType, (LPBYTE)GlobalSpecialDomains, &dwSize)))
|
|
{
|
|
|
|
// We're going to scan a string stored in the registry to gather the domains we should
|
|
// allow. Format:
|
|
// [domain] [domain] [domain]
|
|
// The delimiter is a space character.
|
|
|
|
PTSTR psz = GlobalSpecialDomains;
|
|
DWORD dwDomains = 0;
|
|
BOOL fWord = FALSE;
|
|
while (*psz)
|
|
{
|
|
if (*psz==TEXT(' '))
|
|
{
|
|
if (fWord)
|
|
{
|
|
fWord = FALSE;
|
|
dwDomains++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fWord = TRUE;
|
|
}
|
|
psz++;
|
|
}
|
|
if (fWord)
|
|
{
|
|
dwDomains++;
|
|
}
|
|
GlobalSDOffsets = (PTSTR*)New PTSTR[dwDomains+1];
|
|
if (GlobalSDOffsets)
|
|
{
|
|
psz = GlobalSpecialDomains;
|
|
for (DWORD dw = 0; dw < dwDomains; dw++)
|
|
{
|
|
INET_ASSERT(*psz);
|
|
|
|
while (*psz==TEXT(' '))
|
|
psz++;
|
|
|
|
INET_ASSERT(*psz);
|
|
GlobalSDOffsets[dw] = psz;
|
|
|
|
while (*psz && *psz!=TEXT(' '))
|
|
{
|
|
psz++;
|
|
}
|
|
*psz = TEXT('\0');
|
|
}
|
|
GlobalSDOffsets[dwDomains] = NULL;
|
|
}
|
|
}
|
|
}
|
|
RegCloseKey(hk);
|
|
}
|
|
}
|
|
|
|
// WARNING: The following lines of code make it possible for cookies to be set for *.uk,
|
|
// (for example) if "ku." is in the registry
|
|
BOOL fRet = FALSE;
|
|
if (GlobalSDOffsets)
|
|
{
|
|
for (DWORD i = 0; GlobalSDOffsets[i]; i++)
|
|
{
|
|
if (!StrCmpNI(pch, GlobalSDOffsets[i], nTopLevel)
|
|
|| !StrCmpNI(pch, GlobalSDOffsets[i], nTopLevel+nSecond+1))
|
|
{
|
|
fRet = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return fRet;
|
|
}
|
|
|
|
|
|
static BOOL
|
|
IsSpecialDomain(const char *pch, int nCount)
|
|
{
|
|
// Currently all the special strings are exactly 3 characters long.
|
|
if (pch == NULL || nCount != 3)
|
|
return FALSE;
|
|
|
|
for (int i = 0 ; i < ARRAY_ELEMENTS(s_pachSpecialDomains) ; i++ )
|
|
{
|
|
if (StrCmpNIC(pch, s_pachSpecialDomains[i], nCount) == 0)
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOL
|
|
IsDomainLegal(const char *pchHeader, const char *pchDocument)
|
|
{
|
|
const char *pchCurrent = pchHeader;
|
|
int nSegment = 0;
|
|
int dwCharCount = 0;
|
|
int rgcch[2] = { 0, 0 }; // How many characters between dots
|
|
|
|
// Must have at least one period in name.
|
|
// and contains nothing but '.' is illegal
|
|
|
|
int dwSegmentLength = 0;
|
|
const char * pchSecondPart = NULL; // for a domain string such as
|
|
for (; *pchCurrent; pchCurrent++)
|
|
{
|
|
if (*pchCurrent == '.')
|
|
{
|
|
if (nSegment < 2)
|
|
{
|
|
// Remember how many characters we have between the last two dots
|
|
// For example if domain header is .microsoft.com
|
|
// rgcch[0] should be 3 for "com"
|
|
// rgcch[1] should be 9 for "microsoft"
|
|
rgcch[nSegment] = dwSegmentLength;
|
|
|
|
if (nSegment == 1)
|
|
{
|
|
pchSecondPart = pchCurrent - dwSegmentLength;
|
|
}
|
|
}
|
|
dwSegmentLength = 0;
|
|
nSegment += 1;
|
|
}
|
|
else
|
|
{
|
|
dwSegmentLength++;
|
|
}
|
|
dwCharCount++;
|
|
}
|
|
|
|
// The code below depends on the leading dot being removed from the domain header.
|
|
// The parse code does that, but an assert here just in case something changes in the
|
|
// parse code.
|
|
INET_ASSERT(*(pchCurrent - 1) != '.');
|
|
|
|
// Remember the count of the characters between the begining of the header and
|
|
// the first dot. So for domain=abc.com this will set rgch[1] = 3.
|
|
// Note that this assumes that if domain=.abc.com the leading dot has been stripped
|
|
// out in the parse code. See assert above.
|
|
if (nSegment < 2 )
|
|
{
|
|
rgcch[nSegment] = dwSegmentLength;
|
|
if (nSegment==1)
|
|
{
|
|
pchSecondPart = pchCurrent - dwSegmentLength;
|
|
}
|
|
}
|
|
|
|
// If the domain name is of the form abc.xx.yy where the number of characters between the last two dots is less than
|
|
// 2 we require a minimum of two embedded dots. This is so you are not allowed to set cookies readable by all of .co.nz for
|
|
// example. However this rule is not sufficient and we special case things like .edu.nz as well.
|
|
|
|
int cEmbeddedDotsNeeded = 1;
|
|
|
|
if (rgcch[0] <= 2)
|
|
{
|
|
if ((rgcch[1] <= 2 && !IsVerySpecialDomain(pchHeader, rgcch[0], rgcch[1]))
|
|
|| (pchSecondPart && IsSpecialDomain(pchSecondPart, rgcch[1])))
|
|
cEmbeddedDotsNeeded = 2;
|
|
}
|
|
|
|
if (nSegment < cEmbeddedDotsNeeded || dwCharCount == nSegment)
|
|
return FALSE;
|
|
|
|
// Mismatch between header and document not allowed.
|
|
// Must match full components of domain name.
|
|
|
|
while (*pchHeader == *pchDocument && *pchDocument)
|
|
{
|
|
pchHeader += 1;
|
|
pchDocument += 1;
|
|
}
|
|
|
|
return *pchHeader == 0 && (*pchDocument == 0 || *pchDocument == '.' );
|
|
}
|
|
|
|
|
|
void
|
|
LowerCaseString(char *pch)
|
|
{
|
|
for (; *pch; pch++)
|
|
{
|
|
if (*pch >= 'A' && *pch <= 'Z')
|
|
*pch += 'a' - 'A';
|
|
}
|
|
}
|
|
|
|
static void
|
|
ReverseString(char *pchFront)
|
|
{
|
|
char *pchBack;
|
|
char ch;
|
|
int cch;
|
|
|
|
cch = strlen(pchFront);
|
|
|
|
pchBack = pchFront + cch - 1;
|
|
|
|
cch = cch / 2;
|
|
while (--cch >= 0)
|
|
{
|
|
ch = tolower(*pchFront);
|
|
*pchFront = tolower(*pchBack);
|
|
*pchBack = ch;
|
|
|
|
pchFront += 1;
|
|
pchBack -= 1;
|
|
}
|
|
}
|
|
|
|
static BOOL
|
|
PathAndRDomainFromURL(const char *pchURL, char **ppchRDomain, char **ppchPath, BOOL *pfSecure, BOOL bStrip = TRUE)
|
|
{
|
|
char *pchDomainBuf;
|
|
char *pchRDomain = NULL;
|
|
char *pchPathBuf;
|
|
char *pchPath = NULL;
|
|
char *pchExtra;
|
|
DWORD cchDomain;
|
|
DWORD cchPath;
|
|
DWORD cchExtra;
|
|
BOOL fSuccess = FALSE;
|
|
DWORD dwError;
|
|
INTERNET_SCHEME ustScheme;
|
|
|
|
dwError = CrackUrl((char *)pchURL,
|
|
0,
|
|
FALSE,
|
|
&ustScheme,
|
|
NULL, // Scheme Name
|
|
NULL, // Scheme Lenth
|
|
&pchDomainBuf,
|
|
&cchDomain,
|
|
NULL, // Internet Port
|
|
NULL, // UserName
|
|
NULL, // UserName Length
|
|
NULL, // Password
|
|
NULL, // Password Lenth
|
|
&pchPathBuf,
|
|
&cchPath,
|
|
&pchExtra, // Extra
|
|
&cchExtra, // Extra Length
|
|
NULL);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(dwError);
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( ustScheme != INTERNET_SCHEME_HTTP &&
|
|
ustScheme != INTERNET_SCHEME_HTTPS &&
|
|
ustScheme != INTERNET_SCHEME_UNKNOWN)
|
|
{
|
|
//
|
|
// known scheme should be supported
|
|
// e.g. 3rd party pluggable protocol should be able to
|
|
// call cookie api to setup cookies...
|
|
//
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
goto Cleanup;
|
|
}
|
|
|
|
*pfSecure = ustScheme == INTERNET_SCHEME_HTTPS;
|
|
|
|
if(bStrip)
|
|
{
|
|
while (cchPath > 0)
|
|
{
|
|
if (pchPathBuf[cchPath - 1] == '/' || pchPathBuf[cchPath - 1] == '\\')
|
|
{
|
|
break;
|
|
}
|
|
cchPath -= 1;
|
|
}
|
|
}
|
|
|
|
pchRDomain = StrnDup(pchDomainBuf, cchDomain);
|
|
if (!pchRDomain)
|
|
goto Cleanup;
|
|
|
|
LowerCaseString(pchRDomain);
|
|
ReverseString(pchRDomain);
|
|
|
|
pchPath = (char *)ALLOCATE_MEMORY(LMEM_FIXED, cchPath + 2);
|
|
if (!pchPath)
|
|
goto Cleanup;
|
|
|
|
if (*pchPathBuf != '/')
|
|
{
|
|
*pchPath = '/';
|
|
memcpy(pchPath + 1, pchPathBuf, cchPath);
|
|
pchPath[cchPath + 1] = TEXT('\0');
|
|
}
|
|
else
|
|
{
|
|
memcpy(pchPath, pchPathBuf, cchPath);
|
|
pchPath[cchPath] = TEXT('\0');
|
|
}
|
|
|
|
fSuccess = TRUE;
|
|
|
|
Cleanup:
|
|
if (!fSuccess)
|
|
{
|
|
if (pchRDomain)
|
|
FREE_MEMORY(pchRDomain);
|
|
if (pchPath)
|
|
FREE_MEMORY(pchPath);
|
|
}
|
|
else
|
|
{
|
|
*ppchRDomain = pchRDomain;
|
|
*ppchPath = pchPath;
|
|
}
|
|
|
|
return fSuccess;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// CCookieBase implementation
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
void *
|
|
CCookieBase::operator new(size_t cb, size_t cbExtra)
|
|
{
|
|
void *pv = ALLOCATE_MEMORY(LMEM_FIXED, cb + cbExtra);
|
|
if (!pv)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return NULL;
|
|
}
|
|
|
|
memset(pv, 0, cb);
|
|
return pv;
|
|
}
|
|
|
|
inline void
|
|
CCookieBase::operator delete(void *pv)
|
|
{
|
|
FREE_MEMORY(pv);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// CCookie implementation
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
CCookie *
|
|
CCookie::Construct(const char *pchName)
|
|
{
|
|
CCookie *pCookie = new(strlen(pchName) + 1) CCookie();
|
|
if (!pCookie)
|
|
return NULL;
|
|
|
|
pCookie->_pchName = (char *)(pCookie + 1);
|
|
pCookie->_pchValue = s_achEmpty;
|
|
strcpy(pCookie->_pchName, pchName);
|
|
|
|
pCookie->_dwFlags = COOKIE_SESSION;
|
|
|
|
return pCookie;
|
|
}
|
|
|
|
CCookie::~CCookie()
|
|
{
|
|
if (_pchValue != s_achEmpty)
|
|
FREE_MEMORY(_pchValue);
|
|
}
|
|
|
|
BOOL
|
|
CCookie::SetValue(const char *pchValue)
|
|
{
|
|
int cchValue;
|
|
|
|
if (_pchValue != s_achEmpty)
|
|
FREE_MEMORY(_pchValue);
|
|
|
|
if (!pchValue || !*pchValue)
|
|
{
|
|
_pchValue = s_achEmpty;
|
|
}
|
|
else
|
|
{
|
|
cchValue = strlen(pchValue) + 1;
|
|
_pchValue = (char *)ALLOCATE_MEMORY(LMEM_FIXED, cchValue);
|
|
if (!_pchValue)
|
|
{
|
|
_pchValue = s_achEmpty;
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
memcpy(_pchValue, pchValue, cchValue);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
CCookie::CanSend(BOOL fSecure)
|
|
{
|
|
return (fSecure || !(_dwFlags & COOKIE_SECURE));
|
|
}
|
|
|
|
BOOL CCookie::PurgeAll(void *)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static BOOL
|
|
WriteString(HANDLE hFile, const char *pch)
|
|
{
|
|
DWORD cb;
|
|
return pch && *pch ? WriteFile(hFile, pch, strlen(pch), &cb, NULL) : TRUE;
|
|
}
|
|
|
|
static BOOL
|
|
WriteStringLF(HANDLE hFile, const char *pch)
|
|
{
|
|
DWORD cb;
|
|
|
|
if (!WriteString(hFile, pch)) return FALSE;
|
|
return WriteFile(hFile, "\n", 1, &cb, NULL);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// CCookieLocation implementation
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
CCookieLocation *
|
|
CCookieLocation::Construct(const char *pchRDomain, const char *pchPath)
|
|
{
|
|
int cchPath = strlen(pchPath);
|
|
|
|
CCookieLocation *pLocation = new(strlen(pchRDomain) + cchPath + 2) CCookieLocation();
|
|
if (!pLocation)
|
|
return NULL;
|
|
|
|
pLocation->_cchPath = cchPath;
|
|
pLocation->_pchPath = (char *)(pLocation + 1);
|
|
pLocation->_pchRDomain = pLocation->_pchPath + cchPath + 1;
|
|
|
|
strcpy(pLocation->_pchRDomain, pchRDomain);
|
|
strcpy(pLocation->_pchPath, pchPath);
|
|
|
|
return pLocation;
|
|
}
|
|
|
|
CCookieLocation::~CCookieLocation()
|
|
{
|
|
Purge(CCookie::PurgeAll, NULL);
|
|
}
|
|
|
|
CCookie *
|
|
CCookieLocation::GetCookie(const char *pchName, BOOL fCreate)
|
|
{
|
|
CCookie *pCookie;
|
|
|
|
CCookie **ppCookie = &_pCookieKids;
|
|
|
|
for (pCookie = _pCookieKids; pCookie; pCookie = pCookie->_pCookieNext)
|
|
{
|
|
if (strcmp(pchName, pCookie->_pchName) == 0)
|
|
return pCookie;
|
|
ppCookie = &pCookie->_pCookieNext;
|
|
}
|
|
|
|
if (!fCreate)
|
|
return NULL;
|
|
|
|
pCookie = CCookie::Construct(pchName);
|
|
if (!pCookie)
|
|
return NULL;
|
|
|
|
//
|
|
// Insert cookie at end of list to match Navigator's behavior.
|
|
//
|
|
|
|
pCookie->_pCookieNext = NULL;
|
|
*ppCookie = pCookie;
|
|
|
|
return pCookie;
|
|
}
|
|
|
|
void
|
|
CCookieLocation::Purge(BOOL (CCookie::*pfnPurge)(void *), void *pv)
|
|
{
|
|
CCookie **ppCookie = &_pCookieKids;
|
|
CCookie *pCookie;
|
|
|
|
while ((pCookie = *ppCookie) != NULL)
|
|
{
|
|
if ((pCookie->*pfnPurge)(pv))
|
|
{
|
|
*ppCookie = pCookie->_pCookieNext;
|
|
delete pCookie;
|
|
}
|
|
else
|
|
{
|
|
ppCookie = &pCookie->_pCookieNext;
|
|
}
|
|
}
|
|
}
|
|
|
|
static char *
|
|
ScanString(char *pch, char **pchStr)
|
|
{
|
|
*pchStr = pch;
|
|
|
|
for (; *pch; *pch++)
|
|
{
|
|
if (*pch == '\n')
|
|
{
|
|
*pch = 0;
|
|
pch += 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return pch;
|
|
}
|
|
|
|
static char *
|
|
ScanNumber(char *pch, DWORD *pdw)
|
|
{
|
|
DWORD dw = 0;
|
|
char *pchJunk;
|
|
|
|
for (; *pch >= '0' && *pch <= '9'; *pch++)
|
|
{
|
|
dw = (dw * 10) + *pch - '0';
|
|
}
|
|
|
|
*pdw = dw;
|
|
|
|
return ScanString(pch, &pchJunk);
|
|
}
|
|
|
|
|
|
BOOL
|
|
CCookieLocation::IsMatch(const char *pchRDomain, const char *pchPath)
|
|
{
|
|
return IsDomainMatch(_pchRDomain, pchRDomain) &&
|
|
IsPathMatch(_pchPath, pchPath);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// CCookieJar implementation
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
CCookieJar *
|
|
CCookieJar::Construct()
|
|
{
|
|
return new(0) CCookieJar();
|
|
}
|
|
|
|
CCookieJar::CCookieJar()
|
|
{
|
|
_csCookieJar.Init();
|
|
}
|
|
|
|
CCookieJar::~CCookieJar()
|
|
{
|
|
for (int i = ARRAY_ELEMENTS(_apLocation); --i >= 0; )
|
|
{
|
|
CCookieLocation *pLocation = _apLocation[i];
|
|
while (pLocation)
|
|
{
|
|
CCookieLocation *pLocationT = pLocation->_pLocationNext;
|
|
delete pLocation;
|
|
pLocation = pLocationT;
|
|
}
|
|
}
|
|
}
|
|
|
|
CCookieLocation **
|
|
CCookieJar::GetBucket(const char *pchRDomain)
|
|
{
|
|
int ch;
|
|
int cPeriod = 0;
|
|
unsigned int hash = 0;
|
|
|
|
for (; (ch = *pchRDomain) != 0; pchRDomain++)
|
|
{
|
|
if (ch == '.')
|
|
{
|
|
cPeriod += 1;
|
|
if (cPeriod >= 2)
|
|
break;
|
|
}
|
|
hash = (hash * 29) + ch;
|
|
}
|
|
|
|
hash = hash % ARRAY_ELEMENTS(_apLocation);
|
|
|
|
return &_apLocation[hash];
|
|
}
|
|
|
|
CCookieLocation *
|
|
CCookieJar::GetLocation(const char *pchRDomain, const char *pchPath, BOOL fCreate)
|
|
{
|
|
int cchPath = strlen(pchPath);
|
|
CCookieLocation *pLocation = NULL;
|
|
CCookieLocation **ppLocation = GetBucket(pchRDomain);
|
|
|
|
// To support sending more specific cookies before less specific,
|
|
// we keep list sorted by path length.
|
|
|
|
while ((pLocation = *ppLocation) != NULL)
|
|
{
|
|
if (pLocation->_cchPath < cchPath)
|
|
break;
|
|
|
|
if (strcmp(pLocation->_pchPath, pchPath) == 0 &&
|
|
strcmp(pLocation->_pchRDomain, pchRDomain) == 0)
|
|
return pLocation;
|
|
|
|
ppLocation = &pLocation->_pLocationNext;
|
|
}
|
|
|
|
if (!fCreate)
|
|
goto Cleanup;
|
|
|
|
pLocation = CCookieLocation::Construct(pchRDomain, pchPath);
|
|
if (!pLocation)
|
|
goto Cleanup;
|
|
|
|
pLocation->_pLocationNext = *ppLocation;
|
|
*ppLocation = pLocation;
|
|
|
|
Cleanup:
|
|
return pLocation;
|
|
}
|
|
|
|
void
|
|
CCookieJar::expireCookies(CCookieLocation *pLocation, FILETIME *pftNow) {
|
|
|
|
FILETIME ftCurrent;
|
|
|
|
if (pftNow==NULL)
|
|
GetSystemTimeAsFileTime(&ftCurrent);
|
|
else
|
|
ftCurrent = *pftNow;
|
|
|
|
CCookie **previous = & pLocation->_pCookieKids;
|
|
|
|
CCookie *pCookie = pLocation->_pCookieKids;
|
|
|
|
while (pCookie)
|
|
{
|
|
/* Session cookies do not expire so we only check persistent cookies */
|
|
if ((pCookie->_dwFlags & COOKIE_SESSION) == 0)
|
|
{
|
|
|
|
/* "CompareFileTime" macro returns {+1, 0, -1} similar to "strcmp" */
|
|
int cmpresult = CompareFileTime(ftCurrent, pCookie->_ftExpiry);
|
|
|
|
if (cmpresult==1) /* Cookie has expired: remove from linked list & delete */
|
|
{
|
|
*previous = pCookie->_pCookieNext;
|
|
delete pCookie;
|
|
pCookie = *previous;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/* Otherwise cookie is still valid: advance to next node */
|
|
previous = & (pCookie->_pCookieNext);
|
|
pCookie = pCookie->_pCookieNext;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
CCookieLocation*
|
|
CCookieJar::GetCookies(const char *pchRDomain, const char *pchPath, CCookieLocation *pLast, FILETIME *ftCurrentTime) {
|
|
|
|
for (CCookieLocation *pLocation = pLast ? pLast->_pLocationNext : *GetBucket(pchRDomain);
|
|
pLocation;
|
|
pLocation = pLocation->_pLocationNext)
|
|
{
|
|
if (pLocation->IsMatch(pchRDomain, pchPath))
|
|
{
|
|
/* Found matching cookies...
|
|
Before returning linked list to the user, check expiration
|
|
times, deleting cookies which are no longer valid */
|
|
expireCookies(pLocation, ftCurrentTime);
|
|
return pLocation;
|
|
}
|
|
}
|
|
|
|
/* Reaching this point means no matching cookies were found */
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
CCookieJar::Purge()
|
|
{
|
|
// NOT IMPLEMENTED
|
|
}
|
|
|
|
|
|
struct PARSE
|
|
{
|
|
char *pchBuffer;
|
|
char *pchToken;
|
|
BOOL fEqualFound;
|
|
};
|
|
|
|
static char *
|
|
SkipWS(char *pch)
|
|
{
|
|
while (*pch == ' ' || *pch == '\t')
|
|
pch += 1;
|
|
|
|
return pch;
|
|
}
|
|
|
|
static BOOL
|
|
ParseToken(PARSE *pParse, BOOL fBreakOnSpecialTokens, BOOL fBreakOnEqual)
|
|
{
|
|
char ch;
|
|
char *pch;
|
|
char *pchEndToken;
|
|
|
|
pParse->fEqualFound = FALSE;
|
|
|
|
pch = SkipWS(pParse->pchBuffer);
|
|
if (*pch == 0)
|
|
{
|
|
pParse->pchToken = pch;
|
|
return FALSE;
|
|
}
|
|
|
|
pParse->pchToken = pch;
|
|
pchEndToken = pch;
|
|
|
|
while ((ch = *pch) != 0)
|
|
{
|
|
pch += 1;
|
|
if (ch == ';')
|
|
{
|
|
break;
|
|
}
|
|
else if (fBreakOnEqual && ch == '=')
|
|
{
|
|
pParse->fEqualFound = TRUE;
|
|
break;
|
|
}
|
|
else if (ch == ' ' || ch == '\t')
|
|
{
|
|
if (fBreakOnSpecialTokens)
|
|
{
|
|
if ((strnicmp(pch, "expires", sizeof("expires") - 1) == 0) ||
|
|
(strnicmp(pch, "path", sizeof("path") - 1) == 0) ||
|
|
(strnicmp(pch, "domain", sizeof("domain") - 1) == 0) ||
|
|
(strnicmp(pch, "secure", sizeof("secure") - 1) == 0))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pchEndToken = pch;
|
|
}
|
|
}
|
|
|
|
*pchEndToken = 0;
|
|
pParse->pchBuffer = pch;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static void
|
|
ParseHeader(
|
|
char *pchHeader,
|
|
CookieInfo *pCookie
|
|
)
|
|
{
|
|
char **ppchName = & (pCookie->pchName);
|
|
char **ppchValue = & (pCookie->pchValue);
|
|
char **ppchPath = & (pCookie->pchPath);
|
|
char **ppchRDomain = & (pCookie->pchRDomain);
|
|
|
|
PARSE parse;
|
|
|
|
parse.pchBuffer = pchHeader;
|
|
|
|
*ppchName = NULL;
|
|
*ppchValue = NULL;
|
|
*ppchPath = NULL;
|
|
*ppchRDomain = NULL;
|
|
pCookie->dwFlags = COOKIE_SESSION;
|
|
|
|
// If only one of name or value is specified, Navigator
|
|
// uses name=<blank> and value as what ever was specified.
|
|
// Example: =foo -> name: <blank> value: foo
|
|
// foo -> name: <blank> value: foo
|
|
// foo= -> name: foo value: <blank>
|
|
|
|
if (ParseToken(&parse, FALSE, TRUE))
|
|
{
|
|
*ppchName = parse.pchToken;
|
|
if (parse.fEqualFound)
|
|
{
|
|
if (ParseToken(&parse, FALSE, FALSE))
|
|
{
|
|
*ppchValue = parse.pchToken;
|
|
}
|
|
else
|
|
{
|
|
*ppchValue = s_achEmpty;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*ppchValue = *ppchName;
|
|
*ppchName = s_achEmpty;
|
|
}
|
|
}
|
|
|
|
while (ParseToken(&parse, FALSE, TRUE))
|
|
{
|
|
if (stricmp(parse.pchToken, "expires") == 0)
|
|
{
|
|
if (parse.fEqualFound && ParseToken(&parse, TRUE, FALSE))
|
|
{
|
|
// WinHttpX treats persistent cookies as session cookies with expiration
|
|
if (FParseHttpDate(& pCookie->ftExpiration, parse.pchToken))
|
|
{
|
|
// Don't make the cookie persistent if the parsing fails
|
|
pCookie->dwFlags &= ~COOKIE_SESSION;
|
|
}
|
|
}
|
|
}
|
|
else if (stricmp(parse.pchToken, "domain") == 0)
|
|
{
|
|
if (parse.fEqualFound )
|
|
{
|
|
if( ParseToken(&parse, TRUE, FALSE))
|
|
{
|
|
// Previous versions of IE tossed the leading
|
|
// "." on domain names. We continue this behavior
|
|
// to maintain compatiblity with old cookie files.
|
|
// See comments at the top of this file for more
|
|
// information.
|
|
|
|
if (*parse.pchToken == '.') parse.pchToken += 1;
|
|
ReverseString(parse.pchToken);
|
|
*ppchRDomain = parse.pchToken;
|
|
}
|
|
else
|
|
{
|
|
*ppchRDomain = parse.pchToken;
|
|
}
|
|
}
|
|
}
|
|
else if (stricmp(parse.pchToken, "path") == 0)
|
|
{
|
|
if (parse.fEqualFound && ParseToken(&parse, TRUE, FALSE))
|
|
{
|
|
*ppchPath = parse.pchToken;
|
|
}
|
|
else
|
|
{
|
|
*ppchPath = s_achEmpty;
|
|
}
|
|
}
|
|
else if (stricmp(parse.pchToken, "secure") == 0)
|
|
{
|
|
pCookie->dwFlags |= COOKIE_SECURE;
|
|
|
|
if (parse.fEqualFound)
|
|
{
|
|
ParseToken(&parse, TRUE, FALSE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (parse.fEqualFound)
|
|
{
|
|
ParseToken(&parse, TRUE, FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!*ppchName)
|
|
{
|
|
*ppchName = *ppchValue = s_achEmpty;
|
|
}
|
|
}
|
|
|
|
// free's an INTERNET_COOKIE structure
|
|
static VOID
|
|
DestroyInternetCookie(INTERNET_COOKIE *pic)
|
|
{
|
|
if ( pic != NULL )
|
|
{
|
|
if ( pic->pszDomain ) {
|
|
FREE_MEMORY(pic->pszDomain);
|
|
}
|
|
if ( pic->pszPath ) {
|
|
FREE_MEMORY(pic->pszPath);
|
|
}
|
|
if ( pic->pszName ) {
|
|
FREE_MEMORY(pic->pszName);
|
|
}
|
|
if ( pic->pszData ) {
|
|
FREE_MEMORY(pic->pszData);
|
|
}
|
|
if ( pic->pszUrl ) {
|
|
FREE_MEMORY(pic->pszUrl);
|
|
}
|
|
if( pic->pftExpires ) {
|
|
delete pic->pftExpires;
|
|
pic->pftExpires = NULL;
|
|
}
|
|
|
|
FREE_MEMORY(pic);
|
|
}
|
|
}
|
|
|
|
// allocate's an INTERNET_COOKIE structure
|
|
static INTERNET_COOKIE *
|
|
MakeInternetCookie(
|
|
const char *pchURL,
|
|
char *pchRDomain,
|
|
char *pchPath,
|
|
char *pchName,
|
|
char *pchValue,
|
|
DWORD dwFlags,
|
|
FILETIME ftExpire
|
|
)
|
|
{
|
|
INTERNET_COOKIE *pic = NULL;
|
|
|
|
pic = (INTERNET_COOKIE *) ALLOCATE_MEMORY(LMEM_ZEROINIT, sizeof(INTERNET_COOKIE));
|
|
|
|
if ( pic == NULL ) {
|
|
return NULL;
|
|
}
|
|
|
|
pic->cbSize = sizeof(INTERNET_COOKIE);
|
|
|
|
pic->pszDomain = pchRDomain ? NewString(pchRDomain) : NULL;
|
|
if (pic->pszDomain) {
|
|
ReverseString(pic->pszDomain);
|
|
}
|
|
pic->pszPath = pchPath ? NewString(pchPath) : NULL;
|
|
pic->pszName = pchName ? NewString(pchName) : NULL;
|
|
pic->pszData = pchValue ? NewString(pchValue) : NULL;
|
|
pic->pszUrl = pchURL ? NewString(pchURL) : NULL;
|
|
|
|
#if COOKIE_SECURE != INTERNET_COOKIE_IS_SECURE
|
|
#error MakeInternetCookie depends on cookie flags to remain the same
|
|
#endif
|
|
pic->dwFlags = dwFlags;
|
|
|
|
if( dwFlags & COOKIE_SESSION )
|
|
{
|
|
pic->pftExpires = NULL;
|
|
}
|
|
else
|
|
{
|
|
pic->pftExpires = New FILETIME;
|
|
if( pic->pftExpires )
|
|
{
|
|
memcpy(pic->pftExpires, &ftExpire, sizeof(FILETIME));
|
|
}
|
|
}
|
|
|
|
return pic;
|
|
}
|
|
|
|
|
|
DWORD
|
|
CCookieJar::SetCookie(HTTP_REQUEST_HANDLE_OBJECT *pRequest, const char *pchURL, char *pchHeader, DWORD dwFlags = 0)
|
|
{
|
|
char *pchDocumentRDomain = NULL;
|
|
char *pchDocumentPath = NULL;
|
|
BOOL fDocumentSecure;
|
|
BOOL fDelete;
|
|
DWORD dwRet = SET_COOKIE_FAIL;
|
|
CCookieLocation *pLocation;
|
|
|
|
CookieInfo cookieStats;
|
|
|
|
ParseHeader(pchHeader, &cookieStats);
|
|
|
|
char *pchName = cookieStats.pchName;
|
|
char *pchValue = cookieStats.pchValue;
|
|
char *pchHeaderPath = cookieStats.pchPath;
|
|
char *pchHeaderRDomain = cookieStats.pchRDomain;
|
|
DWORD dwFlagsFromParse = cookieStats.dwFlags;
|
|
|
|
// merge flags given with those found by the parser.
|
|
dwFlags |= dwFlagsFromParse;
|
|
|
|
if (!PathAndRDomainFromURL(pchURL, &pchDocumentRDomain, &pchDocumentPath, &fDocumentSecure))
|
|
goto Cleanup;
|
|
|
|
//
|
|
// Verify domain and path
|
|
//
|
|
|
|
if ((pchHeaderRDomain && !IsDomainLegal(pchHeaderRDomain, pchDocumentRDomain)) ||
|
|
(pchHeaderPath && !IsPathLegal(pchHeaderPath, pchDocumentPath)))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (!pchHeaderRDomain)
|
|
pchHeaderRDomain = pchDocumentRDomain;
|
|
|
|
if (!pchHeaderPath)
|
|
pchHeaderPath = pchDocumentPath;
|
|
|
|
// We need to discard any extra info (i.e. query strings and fragments)
|
|
// from the url.
|
|
if (pchHeaderPath)
|
|
{
|
|
PTSTR psz = pchHeaderPath;
|
|
while (*psz)
|
|
{
|
|
if (*psz==TEXT('?') || *psz==TEXT('#'))
|
|
{
|
|
*psz = TEXT('\0');
|
|
break;
|
|
}
|
|
psz++;
|
|
}
|
|
}
|
|
|
|
// WinHttpX treats persistent cookies as session cookies, subject
|
|
// to the expiration rules
|
|
// Also it does not implement zone policies set by URLMON
|
|
//
|
|
// Finally, we can add the cookie!
|
|
//
|
|
|
|
{
|
|
if (_csCookieJar.Lock())
|
|
{
|
|
pLocation = GetLocation(pchHeaderRDomain, pchHeaderPath, TRUE);
|
|
|
|
if (pLocation)
|
|
{
|
|
CCookie *pCookie;
|
|
|
|
pCookie = pLocation->GetCookie(pchName, TRUE);
|
|
if (!pCookie)
|
|
goto Cleanup;
|
|
|
|
//
|
|
// If the cookie's value or flags have changed, update it.
|
|
//
|
|
if (strcmp(pchValue, pCookie->_pchValue) || dwFlags != pCookie->_dwFlags)
|
|
{
|
|
pCookie->_dwFlags = dwFlags;
|
|
pCookie->_ftExpiry = cookieStats.ftExpiration;
|
|
pCookie->SetValue(pchValue);
|
|
}
|
|
}
|
|
_csCookieJar.Unlock();
|
|
}
|
|
}
|
|
|
|
dwRet = SET_COOKIE_SUCCESS;
|
|
|
|
Cleanup:
|
|
|
|
if (pchDocumentRDomain)
|
|
FREE_MEMORY(pchDocumentRDomain);
|
|
if (pchDocumentPath)
|
|
FREE_MEMORY(pchDocumentPath);
|
|
|
|
return dwRet;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// External APIs
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
CCookieJar *
|
|
CreateCookieJar()
|
|
{
|
|
return CCookieJar::Construct();
|
|
}
|
|
|
|
void
|
|
CloseCookieJar(CCookieJar * CookieJar)
|
|
{
|
|
if (CookieJar)
|
|
{
|
|
delete CookieJar;
|
|
}
|
|
}
|
|
|
|
#ifndef WININET_SERVER_CORE
|
|
void
|
|
PurgeCookieJar()
|
|
{
|
|
}
|
|
#endif
|
|
|
|
|
|
//
|
|
// rambling comments, delete before checkin...
|
|
//
|
|
// returns struc, and pending, error
|
|
// on subsequent attempts passes back, with index, or index incremented
|
|
// perhaps can store index in fsm, and the rest in UI
|
|
// need to handle multi dlgs, perhaps via checking added Cookie.
|
|
//
|
|
|
|
|
|
DWORD
|
|
HTTP_REQUEST_HANDLE_OBJECT::ExtractSetCookieHeaders(LPDWORD lpdwHeaderIndex)
|
|
{
|
|
char *pchHeader = NULL;
|
|
DWORD cbHeader;
|
|
DWORD iQuery = 0;
|
|
int cCookies = 0;
|
|
DWORD error = ERROR_WINHTTP_HEADER_NOT_FOUND;
|
|
const DWORD cbHeaderInit = CCH_COOKIE_MAX * sizeof(char) - 1;
|
|
|
|
pchHeader = New char[CCH_COOKIE_MAX];
|
|
|
|
if (pchHeader == NULL || !_ResponseHeaders.LockHeaders())
|
|
{
|
|
error = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
INET_ASSERT(lpdwHeaderIndex);
|
|
|
|
cbHeader = cbHeaderInit;
|
|
|
|
iQuery = *lpdwHeaderIndex;
|
|
|
|
while (QueryResponseHeader(HTTP_QUERY_SET_COOKIE,
|
|
pchHeader,
|
|
&cbHeader,
|
|
0,
|
|
&iQuery) == ERROR_SUCCESS)
|
|
{
|
|
pchHeader[cbHeader] = 0;
|
|
|
|
INTERNET_HANDLE_OBJECT *pRoot = GetRootHandle (this);
|
|
CCookieJar* pcj = pRoot->_CookieJar;
|
|
DWORD dwRet = pcj->SetCookie(this, GetURL(), pchHeader);
|
|
|
|
if (dwRet == SET_COOKIE_SUCCESS)
|
|
{
|
|
cCookies += 1;
|
|
*lpdwHeaderIndex = iQuery;
|
|
error = ERROR_SUCCESS;
|
|
}
|
|
else if (dwRet == SET_COOKIE_PENDING)
|
|
{
|
|
error = ERROR_IO_PENDING;
|
|
|
|
INET_ASSERT(iQuery != 0);
|
|
*lpdwHeaderIndex = iQuery - 1; // back up and retry this cookie
|
|
|
|
break;
|
|
}
|
|
|
|
cbHeader = cbHeaderInit;
|
|
}
|
|
|
|
_ResponseHeaders.UnlockHeaders();
|
|
|
|
Cleanup:
|
|
if (pchHeader)
|
|
delete [] pchHeader;
|
|
|
|
return error;
|
|
}
|
|
|
|
int
|
|
HTTP_REQUEST_HANDLE_OBJECT::CreateCookieHeaderIfNeeded(VOID)
|
|
{
|
|
int cCookie = 0;
|
|
char * pchRDomain = NULL;
|
|
char * pchPath = NULL;
|
|
BOOL fSecure;
|
|
DWORD cch;
|
|
int cchName;
|
|
int cchValue;
|
|
char * pchHeader = NULL;
|
|
char * pchHeaderStart = NULL;
|
|
|
|
pchHeaderStart = (char *) ALLOCATE_FIXED_MEMORY(CCH_COOKIE_MAX * sizeof(char));
|
|
if (pchHeaderStart == NULL)
|
|
goto Cleanup;
|
|
|
|
// remove cookie header if it exists
|
|
// BUGBUG - we are overriding the app. Original cookie code has this. Don't know why.
|
|
|
|
ReplaceRequestHeader(HTTP_QUERY_COOKIE, NULL, 0, 0, 0);
|
|
|
|
if (!PathAndRDomainFromURL(GetURL(), &pchRDomain, &pchPath, &fSecure, FALSE))
|
|
goto Cleanup;
|
|
|
|
fSecure = GetOpenFlags() & WINHTTP_FLAG_SECURE;
|
|
|
|
if (LockHeaders())
|
|
{
|
|
INTERNET_HANDLE_OBJECT *pRoot = GetRootHandle (this);
|
|
CCookieJar* pcj = pRoot->_CookieJar;
|
|
|
|
if (pcj->_csCookieJar.Lock())
|
|
{
|
|
FILETIME ftNow;
|
|
GetSystemTimeAsFileTime(&ftNow);
|
|
|
|
CCookieLocation *pLocation = pcj->GetCookies(pchRDomain, pchPath, NULL, &ftNow);
|
|
|
|
while (pLocation)
|
|
{
|
|
for (CCookie *pCookie = pLocation->_pCookieKids; pCookie; pCookie = pCookie->_pCookieNext)
|
|
{
|
|
if (pCookie->CanSend(fSecure))
|
|
{
|
|
pchHeader = pchHeaderStart;
|
|
|
|
cch = 0;
|
|
cch += cchName = strlen(pCookie->_pchName);
|
|
cch += cchValue = strlen(pCookie->_pchValue);
|
|
if (cchName) cch += 1; // for equal sign
|
|
|
|
if (cch < CCH_COOKIE_MAX)
|
|
{
|
|
if (cchName > 0)
|
|
{
|
|
memcpy(pchHeader, pCookie->_pchName, cchName);
|
|
pchHeader += cchName;
|
|
*pchHeader++ = '=';
|
|
}
|
|
|
|
if (cchValue > 0)
|
|
{
|
|
memcpy(pchHeader, pCookie->_pchValue, cchValue);
|
|
pchHeader += cchValue;
|
|
}
|
|
|
|
cCookie += 1;
|
|
|
|
AddRequestHeader(HTTP_QUERY_COOKIE,
|
|
pchHeaderStart,
|
|
cch,
|
|
0,
|
|
HTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON);
|
|
}
|
|
} // if (CanSend)
|
|
} // for (pCookie)
|
|
|
|
pLocation = pcj->GetCookies(pchRDomain, pchPath, pLocation, &ftNow);
|
|
} // while (pLocation)
|
|
pcj->_csCookieJar.Unlock();
|
|
} // if pcj->_csCookieJar.Lock()
|
|
UnlockHeaders();
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if (pchHeaderStart)
|
|
FREE_MEMORY(pchHeaderStart);
|
|
if (pchRDomain)
|
|
FREE_MEMORY(pchRDomain);
|
|
if (pchPath)
|
|
FREE_MEMORY(pchPath);
|
|
|
|
return cCookie;
|
|
}
|