mirror of https://github.com/lianthony/NT4.0
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.
704 lines
19 KiB
704 lines
19 KiB
/* cookie.c -- Support for HTTP Cookies (based upon
|
|
* preliminary specification available
|
|
* November 1995).
|
|
*
|
|
* Implements cookie parsing/generation in HTTP messages.
|
|
*
|
|
* Jeff Hostetler, Spyglass, Inc., 1995.
|
|
*/
|
|
|
|
#include "all.h"
|
|
|
|
#ifdef FEATURE_HTTP_COOKIES
|
|
|
|
static char * gszServerCookie = "Set-Cookie";
|
|
static char * gszClientCookie = "Cookie";
|
|
|
|
/*****************************************************************/
|
|
/*****************************************************************/
|
|
|
|
struct cookie * Cookie_Free(struct cookie * p)
|
|
{
|
|
struct cookie * next = NULL;
|
|
|
|
if (p)
|
|
{
|
|
next = p->next;
|
|
|
|
if (p->szName)
|
|
GTR_FREE(p->szName);
|
|
if (p->szValue)
|
|
GTR_FREE(p->szValue);
|
|
if (p->szExpires)
|
|
GTR_FREE(p->szExpires);
|
|
if (p->szDomain)
|
|
GTR_FREE(p->szDomain);
|
|
if (p->szPath)
|
|
GTR_FREE(p->szPath);
|
|
|
|
GTR_FREE(p);
|
|
}
|
|
|
|
return next;
|
|
}
|
|
|
|
/*****************************************************************/
|
|
/*****************************************************************/
|
|
|
|
#define X_IsWhite(c) (((c)==' ')||((c)=='\t'))
|
|
#define X_IsKeyword(p,np,k,nk) ((np==nk)&&(GTR_strncmpi(p,k,nk)==0))
|
|
#define X_PrintString(sz) (((sz)?(sz):"[null]"))
|
|
|
|
static BOOL x_DupString(char ** pp, char * p, int n)
|
|
{
|
|
XX_Assert((!*pp),("Cookie: x_DupString -- target possibly not freed."));
|
|
|
|
*pp = GTR_CALLOC(1,n+1);
|
|
|
|
if (!*pp)
|
|
return FALSE;
|
|
|
|
strncpy(*pp,p,n);
|
|
(*pp)[n] = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
static int x_GetToken(char * sz, int nmax, char cDelimiter, char ** pnext)
|
|
{
|
|
char * q;
|
|
|
|
/* find the end of the first token and return its length.
|
|
* also return pointer to where the caller should start
|
|
* looking for the next token.
|
|
*
|
|
* we assume that we have no leading white space.
|
|
*/
|
|
|
|
if (!sz || !*sz)
|
|
{
|
|
if (pnext)
|
|
*pnext = NULL;
|
|
return 0;
|
|
}
|
|
|
|
for (q=sz; (*q && (q < sz+nmax) && (*q != cDelimiter)); q++)
|
|
;
|
|
|
|
/* set beginning of next token */
|
|
|
|
if (pnext)
|
|
*pnext = ((*q) ? q+1 : NULL);
|
|
|
|
/* dismiss whitespace prior to delimiter */
|
|
|
|
for (q--; ((q>sz) && X_IsWhite(*q)); q--)
|
|
;
|
|
return (q+1-sz);
|
|
}
|
|
|
|
static char * x_SkipLeadingWhite(char * sz)
|
|
{
|
|
if (!sz)
|
|
return NULL;
|
|
|
|
while (*sz && X_IsWhite(*sz))
|
|
sz++;
|
|
|
|
if (!*sz)
|
|
return NULL;
|
|
|
|
return sz;
|
|
}
|
|
|
|
#define X_CopyString(pd,ps,n) do { strncpy((pd),(ps),(n)); (pd)[(n)]=0; } while (0)
|
|
|
|
static int x_ConvertMonth(char *s)
|
|
{
|
|
if (0 == GTR_strncmpi(s, "jan", 3))
|
|
return 0;
|
|
if (0 == GTR_strncmpi(s, "feb", 3))
|
|
return 1;
|
|
if (0 == GTR_strncmpi(s, "mar", 3))
|
|
return 2;
|
|
if (0 == GTR_strncmpi(s, "apr", 3))
|
|
return 3;
|
|
if (0 == GTR_strncmpi(s, "may", 3))
|
|
return 4;
|
|
if (0 == GTR_strncmpi(s, "jun", 3))
|
|
return 5;
|
|
if (0 == GTR_strncmpi(s, "jul", 3))
|
|
return 6;
|
|
if (0 == GTR_strncmpi(s, "aug", 3))
|
|
return 7;
|
|
if (0 == GTR_strncmpi(s, "sep", 3))
|
|
return 8;
|
|
if (0 == GTR_strncmpi(s, "oct", 3))
|
|
return 9;
|
|
if (0 == GTR_strncmpi(s, "nov", 3))
|
|
return 10;
|
|
if (0 == GTR_strncmpi(s, "dec", 3))
|
|
return 11;
|
|
return 0;
|
|
}
|
|
static time_t x_GMT_mktime(struct tm * ptmGMT)
|
|
{
|
|
/* interlude to mktime() to force it to realize
|
|
* that the input is in GMT time not local time.
|
|
*
|
|
* my testing shows that mktime() (at least on
|
|
* win32) assumes that the input is in localtime.
|
|
*/
|
|
|
|
time_t tTime;
|
|
time_t tTimeNow;
|
|
time_t tTimeSkewed;
|
|
time_t tTimeDelta;
|
|
|
|
struct tm * ptmNow;
|
|
|
|
tTime = mktime(ptmGMT);
|
|
|
|
/* see if mktime() is broken */
|
|
|
|
tTimeNow = time(NULL); /* get current time in 'time_t' in UCT */
|
|
ptmNow = gmtime(&tTimeNow); /* convert to 'struct tm' in UCT */
|
|
tTimeSkewed = mktime(ptmNow); /* convert it back to 'time_t' in UCT */
|
|
|
|
if (tTimeNow == tTimeSkewed)
|
|
return tTime; /* mktime() not broken, return */
|
|
|
|
/* counteract the (timezone) skewing effect introduced by mktime().
|
|
* we do this the hard way since time_t might be unsigned and to
|
|
* avoid overflow/underflow.
|
|
*/
|
|
|
|
if (tTimeSkewed > tTimeNow)
|
|
{
|
|
tTimeDelta = tTimeSkewed - tTimeNow;
|
|
tTime = tTime - tTimeDelta;
|
|
}
|
|
else
|
|
{
|
|
tTimeDelta = tTimeNow - tTimeSkewed;
|
|
tTime = tTime + tTimeDelta;
|
|
}
|
|
|
|
return tTime;
|
|
}
|
|
|
|
static time_t x_InternDate(char * szDate)
|
|
{
|
|
/* internalize "Wdy, DD-Mon-YY HH:MM:SS GMT"
|
|
* 012345678901234567890123456
|
|
* 1 2
|
|
*
|
|
* NOTE: our input is *always* in GMT per
|
|
* NOTE: the cookies spec.
|
|
*/
|
|
|
|
time_t tTime;
|
|
struct tm tm;
|
|
char buf[10];
|
|
char * pch;
|
|
|
|
if (!szDate || !*szDate || (strlen(szDate)<26))
|
|
return ((time_t)-1);
|
|
|
|
pch = strchr(szDate, ',');
|
|
if (pch)
|
|
{
|
|
X_CopyString(buf,pch+2,2); tm.tm_mday = atoi(buf);
|
|
X_CopyString(buf,pch+5,3); tm.tm_mon = x_ConvertMonth(buf);
|
|
X_CopyString(buf,pch+9,2); tm.tm_year = atoi(buf);
|
|
X_CopyString(buf,pch+12,2); tm.tm_hour = atoi(buf);
|
|
X_CopyString(buf,pch+15,2); tm.tm_min = atoi(buf);
|
|
X_CopyString(buf,pch+18,2); tm.tm_sec = atoi(buf);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Old style way of doing things
|
|
//
|
|
X_CopyString(buf,&szDate[15],2); tm.tm_hour = atoi(buf);
|
|
X_CopyString(buf,&szDate[18],2); tm.tm_min = atoi(buf);
|
|
X_CopyString(buf,&szDate[21],2); tm.tm_sec = atoi(buf);
|
|
X_CopyString(buf,&szDate[ 5],2); tm.tm_mday = atoi(buf);
|
|
X_CopyString(buf,&szDate[ 8],3); tm.tm_mon = x_ConvertMonth(buf);
|
|
X_CopyString(buf,&szDate[12],2); tm.tm_year = atoi(buf);
|
|
}
|
|
|
|
tm.tm_isdst = 0;
|
|
tm.tm_wday = 0;
|
|
tm.tm_yday = 0;
|
|
|
|
tTime = x_GMT_mktime(&tm);
|
|
|
|
{
|
|
struct tm * pgmt = gmtime(&tTime);
|
|
XX_DMsg(DBG_COOKIE,("TIME: [given %d][computed %d]\n",
|
|
tm.tm_hour,pgmt->tm_hour));
|
|
}
|
|
|
|
return tTime;
|
|
}
|
|
|
|
struct cookie * Cookie_Intern(char * text)
|
|
{
|
|
/* given a text-based-cookie, convert it into an internal cookie.
|
|
* this is used to parse the value of incomming HTTP headers.
|
|
*
|
|
* return NULL on error.
|
|
*/
|
|
|
|
struct cookie * pcookie = NULL;
|
|
char * ptoken = NULL;
|
|
char * pname = NULL;
|
|
char * pvalue = NULL;
|
|
char * pnext = NULL;
|
|
int ntoken, nname, nvalue;
|
|
|
|
pcookie = (struct cookie *)GTR_CALLOC(1,sizeof(struct cookie));
|
|
if (!pcookie)
|
|
return NULL;
|
|
|
|
XX_DMsg(DBG_COOKIE,("COOKIE: Intern [%s]\n",text));
|
|
|
|
/* set default values */
|
|
|
|
pcookie->tExpires = ((time_t)-1);
|
|
pcookie->bSecure = FALSE;
|
|
|
|
|
|
/* find: [<name>=<value>[;]]+ */
|
|
|
|
while ( (ptoken = x_SkipLeadingWhite(text)) )
|
|
{
|
|
/* find whole <name>[=<value>][;] pattern in [pn]token.
|
|
* then find <name> within it into [pn]name.
|
|
*/
|
|
|
|
ntoken = x_GetToken(ptoken,strlen(ptoken),';',&pnext);
|
|
|
|
pname = ptoken;
|
|
nname = x_GetToken(pname,ntoken,'=',&pvalue);
|
|
|
|
pvalue = x_SkipLeadingWhite(pvalue);
|
|
nvalue = ntoken - (pvalue - ptoken);
|
|
|
|
if (X_IsKeyword(pname,nname,"expires",7))
|
|
{
|
|
if (nvalue)
|
|
if (!x_DupString(&pcookie->szExpires,pvalue,nvalue))
|
|
goto Fail;
|
|
else
|
|
pcookie->tExpires = x_InternDate(pcookie->szExpires);
|
|
else
|
|
pcookie->tExpires = ((time_t)-1);
|
|
}
|
|
else if (X_IsKeyword(pname,nname,"path",4))
|
|
{
|
|
if (nvalue)
|
|
if (!x_DupString(&pcookie->szPath,pvalue,nvalue))
|
|
goto Fail;
|
|
}
|
|
else if (X_IsKeyword(pname,nname,"domain",6))
|
|
{
|
|
if (nvalue)
|
|
if (!x_DupString(&pcookie->szDomain,pvalue,nvalue))
|
|
goto Fail;
|
|
}
|
|
else if (X_IsKeyword(pname,nname,"secure",6))
|
|
{
|
|
/* [; secure]
|
|
*
|
|
* 'secure' does not have a value; if present, it
|
|
* indicates that the cookie will only be sent
|
|
* back to the server over HTTPS (ie, SSL).
|
|
*/
|
|
|
|
pcookie->bSecure = TRUE;
|
|
}
|
|
else
|
|
{
|
|
/* anything else is a generic name/value pair
|
|
* (only one of these per cookie). we ignore
|
|
* additional pairs.
|
|
*/
|
|
|
|
if (nname && !pcookie->szName)
|
|
{
|
|
if (!x_DupString(&pcookie->szName,pname,nname))
|
|
goto Fail;
|
|
if (nvalue)
|
|
if (!x_DupString(&pcookie->szValue,pvalue,nvalue))
|
|
goto Fail;
|
|
}
|
|
}
|
|
|
|
text = pnext;
|
|
}
|
|
|
|
/* Note: let caller verify 'domain' and 'path' based upon current URL. */
|
|
|
|
XX_DMsg(DBG_COOKIE,
|
|
("COOKIE: Intern [%s %s]\n\t\t[expires %s ===>> %s]\n\t\t[domain %s]\n"
|
|
"\t\t[path %s]\n\t\t[secure %d]\n",
|
|
X_PrintString(pcookie->szName),
|
|
X_PrintString(pcookie->szValue),
|
|
X_PrintString(pcookie->szExpires),
|
|
( (pcookie->tExpires == ((time_t)-1))
|
|
? "[end-of-session]"
|
|
: asctime(gmtime(&pcookie->tExpires))),
|
|
X_PrintString(pcookie->szDomain),
|
|
X_PrintString(pcookie->szPath),
|
|
pcookie->bSecure));
|
|
|
|
return pcookie;
|
|
|
|
Fail:
|
|
(void)Cookie_Free(pcookie);
|
|
|
|
XX_DMsg(DBG_COOKIE,("COOKIE: could not intern\n"));
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static BOOL x_DomainNameTooShort(char * szDomain)
|
|
{
|
|
/* verify that the given domainname is long enough
|
|
* (ie, has at least 2 components)
|
|
*/
|
|
|
|
int nDots;
|
|
char * p;
|
|
|
|
for (nDots=0, p=szDomain; ((*p) && (nDots<2)); p++)
|
|
if (*p == '.')
|
|
nDots++;
|
|
|
|
XX_DMsg(DBG_COOKIE,("COOKIE: domain name contains [%d] dots.\n",nDots));
|
|
|
|
return (nDots < 2);
|
|
}
|
|
|
|
static BOOL x_DetermineIfSecure(HTRequest * request)
|
|
{
|
|
/* return TRUE iff we will use a secure channel. */
|
|
|
|
BOOL bResult = FALSE;
|
|
char * szProtocol = HTParse(request->destination->szActualURL,"",PARSE_ACCESS);
|
|
|
|
if (szProtocol)
|
|
{
|
|
bResult = ( (GTR_strcmpi(szProtocol,"https")==0)
|
|
/* || any other choices... */
|
|
);
|
|
GTR_FREE(szProtocol);
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
static char * x_ExtractFQDN(const char * szURL)
|
|
{
|
|
/* extract the Fully-Qualified-Domain-Name from the
|
|
* given url.
|
|
*
|
|
* returns a string which the caller must free.
|
|
*/
|
|
|
|
char * szHostname;
|
|
char * p;
|
|
|
|
szHostname = HTParse(szURL,"",PARSE_HOST);
|
|
|
|
/* trim port, if present */
|
|
|
|
p = strchr(szHostname,':');
|
|
if (p)
|
|
*p = 0;
|
|
|
|
/* NOTE: we cannot be guarenteed of a FQDN without a
|
|
* NOTE: DNS and Reverse-DNS lookup. this is a bit
|
|
* NOTE: expensive. we can get the DNS info from the
|
|
* NOTE: DNS cache (in httcp.c), but we would still
|
|
* NOTE: need to go back for the FQDN.
|
|
* NOTE:
|
|
* NOTE: as long as the network hyperlinks are using
|
|
* NOTE: FQDN's everything is ok.
|
|
*/
|
|
|
|
return szHostname;
|
|
}
|
|
|
|
static char * x_ExtractPath(const char * szURL)
|
|
{
|
|
/* extract the Pathname portion from the given url.
|
|
*
|
|
* returns a string which the caller must free.
|
|
*/
|
|
|
|
char * szPathname;
|
|
char * p;
|
|
|
|
szPathname = HTParse(szURL,"",PARSE_PATH|PARSE_PUNCTUATION);
|
|
|
|
/* NOTE: The spec is a bit vague here. It says that if a
|
|
* NOTE: path is not specified in the cookie, it is assumed
|
|
* NOTE: to be the same path as the document. Does this
|
|
* NOTE: mean the pathname with or without the filename ?
|
|
* NOTE: The feature is a bit useless if include the filename
|
|
* NOTE: portion, so let's assume it's without.
|
|
* NOTE:
|
|
* NOTE: We truncate the string just past the last slash.
|
|
*/
|
|
|
|
p = strrchr(szPathname,'/');
|
|
if (p)
|
|
p[1]=0;
|
|
|
|
return szPathname;
|
|
}
|
|
|
|
static void x_VerifyDomain(struct cookie * pcookie, char * szHostname)
|
|
{
|
|
/* examine pcookie->szDomain and check its validity given
|
|
* the hostname that we think we sent the request to.
|
|
*
|
|
* if the cookie had a domain=<domain_name> pair, we must
|
|
* verify that it contains at least 2 dots (to prevent
|
|
* devious servers from saying ".com") and verify that
|
|
* the tails of the <domain_name> and our <hostname> match.
|
|
* if it fails to pass our tests, we silently substitute
|
|
* the hostname.
|
|
*
|
|
* if the cookie did not specify a domain (or did not
|
|
* specify a value for it), we substitute the hostname.
|
|
*/
|
|
|
|
if (pcookie->szDomain)
|
|
{
|
|
int nLenDomain, nLenHostname, nTail;
|
|
|
|
nLenDomain = strlen(pcookie->szDomain);
|
|
nLenHostname = strlen(szHostname);
|
|
nTail = nLenHostname - nLenDomain;
|
|
|
|
if ( (nTail >= 0)
|
|
&& (GTR_strncmpi(pcookie->szDomain,szHostname+nTail,nLenDomain)==0)
|
|
&& ( ! x_DomainNameTooShort(pcookie->szDomain)))
|
|
{
|
|
/* domain name is ok */
|
|
return;
|
|
}
|
|
|
|
XX_DMsg(DBG_COOKIE,
|
|
("COOKIE: Fixed up domain name from [%s] to [%s]\n",
|
|
pcookie->szDomain,szHostname));
|
|
|
|
GTR_FREE(pcookie->szDomain);
|
|
pcookie->szDomain = NULL;
|
|
}
|
|
|
|
(void)x_DupString(&pcookie->szDomain,szHostname,strlen(szHostname));
|
|
|
|
return;
|
|
}
|
|
|
|
static void x_VerifyPath(struct cookie * pcookie, char * szPathname)
|
|
{
|
|
/* examine pcookie->szPath and check its validity.
|
|
*
|
|
* if omitted or invalid, substitute the pathname of the
|
|
* current url.
|
|
*
|
|
*/
|
|
|
|
/* TODO verify that filename is not included in given pathname. */
|
|
|
|
if (!pcookie->szPath)
|
|
(void)x_DupString(&pcookie->szPath,szPathname,strlen(szPathname));
|
|
|
|
return;
|
|
}
|
|
|
|
/*****************************************************************/
|
|
/*****************************************************************/
|
|
|
|
void Cookie_AppendToStream(struct CharStream * cs,
|
|
struct cookie * pc,
|
|
BOOL bSecure)
|
|
{
|
|
/* convert cookie into an external representation for sending
|
|
* to the HTTP server.
|
|
*/
|
|
|
|
/* if the cookie is marked as secure-only, only send it
|
|
* if we're transmitting on a secure channel.
|
|
*/
|
|
|
|
if (pc->bSecure && !bSecure)
|
|
{
|
|
XX_DMsg(DBG_COOKIE,
|
|
(("COOKIE: [%s=%s] is marked secure; "
|
|
"cannot send on open channel.\n"),
|
|
pc->szName,pc->szValue));
|
|
return;
|
|
}
|
|
|
|
XX_DMsg(DBG_COOKIE,("COOKIE: Sending [%s=%s]\n",
|
|
pc->szName,pc->szValue));
|
|
|
|
if (CS_GetLength(cs))
|
|
CS_AddString(cs,"; ",2);
|
|
CS_AddString(cs,pc->szName,strlen(pc->szName));
|
|
CS_AddString(cs,"=",1);
|
|
CS_AddString(cs,pc->szValue,strlen(pc->szValue));
|
|
|
|
return;
|
|
}
|
|
|
|
/*****************************************************************/
|
|
/*****************************************************************/
|
|
|
|
void Cookie_FetchCookies(HTHeader * header, HTRequest * request)
|
|
{
|
|
/* extract cookies from the incomming header and update
|
|
* our database.
|
|
*/
|
|
|
|
HTHeaderList * hl;
|
|
struct cookie * pcookie;
|
|
char * szHostname = NULL;
|
|
char * szPathname = NULL;
|
|
|
|
for (hl=header->first; hl; hl=hl->next)
|
|
if (GTR_strcmpi(hl->name,gszServerCookie)==0)
|
|
{
|
|
pcookie = Cookie_Intern(hl->value);
|
|
|
|
if (pcookie)
|
|
{
|
|
if (!szHostname)
|
|
szHostname = x_ExtractFQDN(request->destination->szActualURL);
|
|
if (!szPathname)
|
|
szPathname = x_ExtractPath(request->destination->szActualURL);
|
|
|
|
x_VerifyDomain(pcookie,szHostname);
|
|
x_VerifyPath(pcookie,szPathname);
|
|
|
|
if (!CookieDB_AddToDatabase(pcookie))
|
|
Cookie_Free(pcookie);
|
|
}
|
|
}
|
|
|
|
if (szHostname)
|
|
GTR_FREE(szHostname);
|
|
if (szPathname)
|
|
GTR_FREE(szPathname);
|
|
|
|
#ifdef XX_DEBUG
|
|
CookieDB_DebugDump();
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
|
|
/*****************************************************************/
|
|
/*****************************************************************/
|
|
|
|
BOOL Cookie_SendCookies(HTHeader * header, HTRequest * request)
|
|
{
|
|
/* based upon the details of the current outgoing
|
|
* HTTP request we examine our cookie database and
|
|
* place the proper headers in the header list
|
|
* being constructed.
|
|
*
|
|
* return FALSE if any error occurs.
|
|
*/
|
|
|
|
char * szHostname = NULL;
|
|
char * szPathname = NULL;
|
|
struct cookie_domain * pcdExactMatch = NULL;
|
|
struct cookie_domain * pcdTailMatch = NULL;
|
|
struct CharStream * cs = NULL;
|
|
BOOL bSecure;
|
|
BOOL bResult = FALSE;
|
|
|
|
bSecure = x_DetermineIfSecure(request);
|
|
|
|
szHostname = x_ExtractFQDN(request->destination->szActualURL);
|
|
szPathname = x_ExtractPath(request->destination->szActualURL);
|
|
|
|
if (!szHostname || !szPathname)
|
|
goto Done;
|
|
|
|
/* this is not in the spec (the pseudo-spec does not include
|
|
* details on such boundary conditions), but i think it is how
|
|
* it should be done.
|
|
*
|
|
* first we do an exact match on the domain with the destination
|
|
* hostname and then tail matches on domains. we send any
|
|
* appropriate cookies found under any domain match. we send
|
|
* them in the order the domains are identified. that is,
|
|
* we do the exact match first; domains from tail matches are
|
|
* in an undefined order.
|
|
*
|
|
* for example, if the cookie database contains domains for
|
|
* foo.bar.spyglass.com, .bar.spyglass.com, and .spyglass.com
|
|
* and we are visiting foo.bar.spyglass.com, we would send
|
|
* cookies found under all three. if we are visiting
|
|
* www.bar.spyglass.com, we would send cookies found under
|
|
* the second two domains.
|
|
*
|
|
* we capture all out-going cookies into a CharStream.
|
|
*/
|
|
|
|
pcdExactMatch = CookieDB_GetDomain(szHostname,TRUE,NULL);
|
|
if (pcdExactMatch)
|
|
{
|
|
cs = CS_Create();
|
|
if (!cs)
|
|
goto Done;
|
|
CookieDB_GetCookies(pcdExactMatch,cs,szPathname,bSecure);
|
|
}
|
|
|
|
pcdTailMatch = CookieDB_GetDomain(szHostname,FALSE,NULL);
|
|
while (pcdTailMatch)
|
|
{
|
|
if (!cs)
|
|
{
|
|
cs = CS_Create();
|
|
if (!cs)
|
|
goto Done;
|
|
}
|
|
if (pcdTailMatch != pcdExactMatch)
|
|
CookieDB_GetCookies(pcdTailMatch,cs,szPathname,bSecure);
|
|
|
|
pcdTailMatch = CookieDB_GetDomain(szHostname,FALSE,pcdTailMatch);
|
|
}
|
|
|
|
if (cs)
|
|
{
|
|
if (CS_GetLength(cs)) /* we have some cookies to deliver. */
|
|
HTHeaderList_SetNameValue(HTHeaderList_Append(header,
|
|
HTHeaderList_New()),
|
|
gszClientCookie,
|
|
CS_GetPool(cs));
|
|
|
|
CS_Destroy(cs);
|
|
}
|
|
|
|
bResult = TRUE;
|
|
|
|
Done:
|
|
|
|
if (szHostname)
|
|
GTR_FREE(szHostname);
|
|
if (szPathname)
|
|
GTR_FREE(szPathname);
|
|
|
|
return bResult;
|
|
}
|
|
|
|
#endif /* FEATURE_HTTP_COOKIES */
|