Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

693 lines
18 KiB

/* cookiedb.c -- Support for HTTP Cookies (based upon
* preliminary specification available
* November 1995).
*
* Implements cookie database.
*
* Jeff Hostetler, Spyglass, Inc., 1995.
*/
#include "all.h"
#ifdef FEATURE_HTTP_COOKIES
static struct cookie_domain * gCookieDomain = NULL;
/*****************************************************************/
/*****************************************************************/
struct cookie_domain * CookieDB_GetDomain(char * szDomain,
BOOL bExactMatch,
struct cookie_domain * pcdStartAfter)
{
/* return node in cookie_domain list for given domain.
*
* return NULL if domain is not represented.
*/
struct cookie_domain * pcd;
struct cookie_domain * pcdStart;
if (pcdStartAfter)
pcdStart = pcdStartAfter->next;
else
pcdStart = gCookieDomain;
if (bExactMatch)
{
for (pcd=pcdStart; (pcd); pcd=pcd->next)
if (GTR_strcmpi(szDomain,pcd->szDomain)==0)
return pcd;
}
else
{
/* use tail-matching */
int nLenGiven = strlen(szDomain);
for (pcd=pcdStart; (pcd); pcd=pcd->next)
{
int nLenStored = strlen(pcd->szDomain);
int nTail = (nLenGiven - nLenStored);
if ( (nTail >= 0)
&& (GTR_strncmpi(szDomain+nTail,pcd->szDomain,nLenStored)==0))
return pcd;
}
}
return NULL;
}
static struct cookie_domain * x_AddDomain(char * szDomain)
{
/* insert node in cookie_domain list for given domain
* and return node created.
*
* return NULL on error.
*/
struct cookie_domain * pcd;
XX_Assert((CookieDB_GetDomain(szDomain,TRUE,NULL)==NULL),
("x_AddDomain: domain already represented [%s]\n",szDomain));
pcd = (struct cookie_domain *)GTR_CALLOC(1,sizeof(struct cookie_domain));
if (pcd)
{
pcd->szDomain = (char *)GTR_CALLOC(1,strlen(szDomain)+1);
if (pcd->szDomain)
{
strcpy(pcd->szDomain,szDomain);
pcd->next = gCookieDomain;
gCookieDomain = pcd;
return pcd;
}
GTR_FREE(pcd);
}
return NULL;
}
/*****************************************************************/
/*****************************************************************/
static struct cookie_tree * x_CreateNewTree(struct cookie * pcookie)
{
/* create a new cookie tree and insert the
* the given cookie into it.
*
* return NULL if we fail.
*/
struct cookie_tree * pct;
pct = (struct cookie_tree *)GTR_CALLOC(1,sizeof(struct cookie_tree));
if (pct)
{
pct->szPath = (char *)GTR_CALLOC(1,strlen(pcookie->szPath)+1);
if (pct->szPath)
{
strcpy(pct->szPath,pcookie->szPath);
pct->cookies = pcookie;
XX_DMsg(DBG_COOKIE,("COOKIE: creating new tree for [%s=%s] in [%s]\n",
pcookie->szName,pcookie->szValue,
pct->szPath));
return pct;
}
GTR_FREE(pct);
}
return NULL;
}
static void x_InsertCookieIntoExistingTreeNode(struct cookie_tree * pct,
struct cookie * pcookie)
{
/* Insert cookie onto given node and eliminate
* any duplicates.
*/
struct cookie * pc;
struct cookie * prev;
for (prev=NULL, pc=pct->cookies; (pc); prev=pc, pc=pc->next)
if (GTR_strcmpi(pcookie->szName,pc->szName)==0)
{
/* cookie names match; replace existing one
* in list with the new one.
*/
XX_DMsg(DBG_COOKIE,("COOKIE: Replacing [%s=%s] with [%s=%s] in [%s]\n",
pc->szName,pc->szValue,
pcookie->szName,pcookie->szValue,
pct->szPath));
pcookie->next = Cookie_Free(pc);
if (prev)
prev->next = pcookie;
else
pct->cookies = pcookie;
return;
}
/* no match; prepend to list */
XX_DMsg(DBG_COOKIE,("COOKIE: Adding [%s=%s] in [%s]\n",
pcookie->szName,pcookie->szValue,
pct->szPath));
pcookie->next = pct->cookies;
pct->cookies = pcookie;
return;
}
static BOOL x_InsertCookie(struct cookie_domain * pcd,
struct cookie * pcookie)
{
/* Insert the given cookie into the appropriate
* place in the cookie tree for the given domain.
*
* return TRUE if we inserted the cookie.
*/
struct cookie_tree * pct;
struct cookie_tree * pctnew;
if (!pcd->tree)
{
/* no tree for this domain; by-pass a few steps. */
pcd->tree = x_CreateNewTree(pcookie);
return (pcd->tree != NULL);
}
/* tree exists; search for exact match for this cookie.
* if no proper tree node exists, create a new one.
*/
pct=pcd->tree;
while (1)
{
int r;
r = GTR_strcmpi(pcookie->szPath,pct->szPath);
if (r==0)
{
/* exact match; use existing node */
x_InsertCookieIntoExistingTreeNode(pct,pcookie);
return TRUE;
}
else if (r < 0)
{
/* path for new cookie not in list and
* needs to appear before current node.
*/
pctnew = x_CreateNewTree(pcookie);
if (!pctnew)
return FALSE;
pctnew->next = pct;
pctnew->prev = pct->prev;
pct->prev = pctnew;
if (pctnew->prev)
pctnew->prev->next = pctnew;
else
pcd->tree = pctnew;
return TRUE;
}
else if (!pct->next)
{
/* end of the list; append new node */
pctnew = x_CreateNewTree(pcookie);
if (!pctnew)
return FALSE;
pctnew->next = NULL;
pctnew->prev = pct;
pct->next = pctnew;
return TRUE;
}
else
{
/* otherwise, path should be after current node;
* keep searching.
*/
pct=pct->next;
}
}
/*NOTREACHED*/
return FALSE;
}
/*****************************************************************/
/*****************************************************************/
void CookieDB_ExternCookies(struct CharStream * cs,
struct cookie_tree * pct,
BOOL bSecure)
{
/* convert cookie chain into an external representation
* for sending to HTTP server.
*
* first we verify timestamps in the list and remove
* any cookies which have expired.
*
* we return the (possibly) modified chain.
*/
struct cookie * pc;
struct cookie * prev;
time_t tCurrentTime = time(NULL);
for (prev=NULL, pc=pct->cookies; (pc); /**/)
if ( (pc->tExpires == ((time_t)-1)) /* expires with session */
|| (pc->tExpires >= tCurrentTime)) /* or not yet expired */
{
Cookie_AppendToStream(cs,pc,bSecure);
prev = pc;
pc = pc->next;
}
else
{
XX_DMsg(DBG_COOKIE,("COOKIE: Deleting stale cookie [%s=%s] in [%s]\n",
pc->szName,pc->szValue,pct->szPath));
pc = Cookie_Free(pc);
if (prev)
prev->next = pc;
else
pct->cookies = pc;
}
return;
}
/*****************************************************************/
/*****************************************************************/
void CookieDB_GetCookies(struct cookie_domain * pcd,
struct CharStream * cs,
char * szPathname,
BOOL bSecure)
{
/* find all cookies in the current domain
* which match the given pathname and append
* them to the charstream.
*
* we are required to list the cookies which
* exactly match the given path first, followed
* by the cookies whose path is a proper prefix
* to the given path; we reference the longest
* prefix first and the shortest prefix last.
*/
struct cookie_tree * pct;
if (!pcd || !pcd->tree)
return;
/* current implementation of the cookie tree is
* doubly-linked list in alphabetical order.
*
* we start at the end of the list and search
* backwards.
*/
for (pct=pcd->tree; (pct->next); pct=pct->next)
;
while (pct)
{
if ( (GTR_strncmpi(pct->szPath,szPathname,strlen(pct->szPath))==0)
&& (pct->cookies))
CookieDB_ExternCookies(cs,pct,bSecure);
pct = pct->prev;
}
return;
}
/*****************************************************************/
/*****************************************************************/
BOOL CookieDB_AddToDatabase(struct cookie * pcookie)
{
/* insert this cookie into the database.
* if it superceedes any existing cookies,
* remove them.
*
* return FALSE if we did not add it to the database.
*/
struct cookie_domain * pcd;
if (!pcookie)
return FALSE;
/* find the domain in our list; if not present, add it. */
pcd = CookieDB_GetDomain(pcookie->szDomain,TRUE,NULL);
if (!pcd)
{
pcd = x_AddDomain(pcookie->szDomain);
if (!pcd)
return FALSE;
}
/* search prefix-tree looking for where to place this cookie. */
return x_InsertCookie(pcd,pcookie);
}
/*****************************************************************/
/*****************************************************************/
FILE * x_GetCookieJar(BOOL bRead)
{
/* return file handle to permanent storage for the cookies.
* if bRead is set, we try to open an existing cookie jar
* for reading; otherwise we open a new file for writing.
*
* return NULL on error.
*/
char path[_MAX_PATH];
FILE * fp = NULL;
memset(path,0,_MAX_PATH);
#ifdef WIN32
PREF_GetPrefsDirectory(path);
strcat(path,"cookie.jar");
#endif /* WIN32 */
#ifdef MAC
#endif /* MAC */
#ifdef UNIX
strcpy(path,PREF_BuildPREFPath("cookie.jar"));
#endif /* UNIX */
if (*path)
fp = fopen(path,((bRead) ? "r" : "w"));
return fp;
}
/*****************************************************************/
/*****************************************************************/
static void x_LoadCookiesFromCookieJar(FILE * fp)
{
/* read cookie jar file and convert to cookies in database. */
size_t size;
char * buf = NULL;
char * p;
char * peol;
struct cookie * pcookie;
fseek(fp, 0, SEEK_END);
size = ftell(fp);
fseek(fp, 0, SEEK_SET);
if (!size)
goto Done;
buf = (char *)GTR_CALLOC(1,size+1);
if (!buf)
goto Done;
fread(buf,size,1,fp);
for (p=buf; (*p); p=peol+1)
{
/* isolate each line and pretend like we received it
* in an http header value.
*/
for (peol=p; (*peol && (*peol!='\n') && (*peol!='\r')); peol++)
;
if (peol > p)
{
char c = *peol;
*peol = 0;
pcookie = Cookie_Intern(p);
if ( (pcookie)
&& (!CookieDB_AddToDatabase(pcookie)))
Cookie_Free(pcookie);
*peol = c;
if (!*peol)
goto Done;
}
}
Done:
if (buf)
GTR_FREE(buf);
return;
}
void CookieDB_ConstructDB(void)
{
/* Initialize cookie database and load any persistent
* cookies that were saved from a previous session.
*/
FILE * fp;
gCookieDomain = NULL;
fp = x_GetCookieJar(TRUE);
if (fp)
{
x_LoadCookiesFromCookieJar(fp);
fclose(fp);
}
#ifdef XX_DEBUG
CookieDB_DebugDump();
#endif
return;
}
/*****************************************************************/
/*****************************************************************/
static void x_FreeCompleteCT(struct cookie_tree * pct)
{
/* free the entire tree of cookie_tree nodes. */
struct cookie_tree * pctnext;
struct cookie * pc;
while (pct)
{
pctnext = pct->next;
pc = pct->cookies;
while (pc)
pc = Cookie_Free(pc);
GTR_FREE(pct->szPath);
GTR_FREE(pct);
pct = pctnext;
}
return;
}
static struct cookie_domain * x_FreeCD(struct cookie_domain * pcd)
{
/* free pcd and everything under it.
* return pointer to next cd in chain.
*/
struct cookie_domain * pcdnext;
pcdnext = pcd->next;
x_FreeCompleteCT(pcd->tree);
GTR_FREE(pcd->szDomain);
GTR_FREE(pcd);
return pcdnext;
}
void x_GMT_FormatTime(char * buf, time_t * ptime)
{
/* convert 'time_t' time to external cookie time format:
*
* "Wdy, DD-Mon-YY HH:MM:SS GMT"
* 012345678901234567890123456
* 1 2
*
* we use asctime which is:
*
* "Wdy Mon DD HH:MM:SS YYYY"
* 012345678901234567890123
* 1 2
*/
char * szAsc = asctime(gmtime(ptime));
buf[ 0] = szAsc[ 0];
buf[ 1] = szAsc[ 1];
buf[ 2] = szAsc[ 2];
buf[ 3] = ',';
buf[ 4] = ' ';
buf[ 5] = szAsc[ 8];
buf[ 6] = szAsc[ 9];
buf[ 7] = '-';
buf[ 8] = szAsc[ 4];
buf[ 9] = szAsc[ 5];
buf[10] = szAsc[ 6];
buf[11] = '-';
buf[12] = szAsc[22];
buf[13] = szAsc[23];
buf[14] = ' ';
buf[15] = szAsc[11];
buf[16] = szAsc[12];
buf[17] = ':';
buf[18] = szAsc[14];
buf[19] = szAsc[15];
buf[20] = ':';
buf[21] = szAsc[17];
buf[22] = szAsc[18];
buf[23] = ' ';
buf[24] = 'G';
buf[25] = 'M';
buf[26] = 'T';
buf[27] = 0;
return;
}
void x_SaveCookiesInCookieJar(FILE * fp)
{
/* write cookies to cookie jar.
*
* the cookie jar format is a series of lines.
* each line is identical to the <value> portion
* of the 'Set-Cookie: <value>' http header that
* we receive from a server.
*/
char bufDate[40];
struct cookie_domain * pcd;
struct cookie_tree * pct;
struct cookie * pc;
time_t tCurrentTime = time(NULL);
XX_DMsg(DBG_COOKIE,("\n\nCOOKIE: Saving Cookie Jar\n"));
for (pcd=gCookieDomain; (pcd); pcd=pcd->next)
{
XX_DMsg(DBG_COOKIE,("\t%s:\n",pcd->szDomain));
for (pct=pcd->tree; (pct); pct=pct->next)
{
XX_DMsg(DBG_COOKIE,("\t\t%s:\n",pct->szPath));
for (pc=pct->cookies; (pc); pc=pc->next)
{
if ( (pc->tExpires != ((time_t)-1)) /* possibly persistent */
&& (pc->tExpires >= tCurrentTime)) /* and not yet expired */
{
x_GMT_FormatTime(bufDate,&pc->tExpires);
fprintf(fp,"%s=%s; expires=%s; path=%s; domain=%s%s",
pc->szName,
pc->szValue,
bufDate,
pc->szPath,
pc->szDomain,
((pc->bSecure) ? "; secure\n" : "\n"));
XX_DMsg(DBG_COOKIE,("\t\t\t%s=%s %s %s\n",
pc->szName,pc->szValue,
( (pc->bSecure)
? "secure"
: "clear"),
bufDate));
}
}
}
}
return;
}
void CookieDB_DestroyDB(void)
{
/* Save persistent cookies to permanent storage
* and free all memory associated with the
* cookie database.
*/
FILE * fp;
fp = x_GetCookieJar(FALSE);
if (fp)
{
x_SaveCookiesInCookieJar(fp);
fclose(fp);
}
while (gCookieDomain)
gCookieDomain = x_FreeCD(gCookieDomain);
return;
}
/*****************************************************************/
/*****************************************************************/
#ifdef XX_DEBUG
void CookieDB_DebugDump(void)
{
/* dump internal cookie database */
struct cookie_domain * pcd;
struct cookie_tree * pct;
struct cookie * pc;
if (XX_Filter(DBG_COOKIE))
{
XX_DMsg(DBG_COOKIE,("\n\nCOOKIE: Dump\n"));
for (pcd=gCookieDomain; (pcd); pcd=pcd->next)
{
XX_DMsg(DBG_COOKIE,("\t%s:\n",pcd->szDomain));
for (pct=pcd->tree; (pct); pct=pct->next)
{
XX_DMsg(DBG_COOKIE,("\t\t%s:\n",pct->szPath));
for (pc=pct->cookies; (pc); pc=pc->next)
{
XX_DMsg(DBG_COOKIE,("\t\t\t%s=%s %s %s",
pc->szName,pc->szValue,
( (pc->bSecure)
? "secure"
: "clear"),
( (pc->tExpires==((time_t)-1))
? "[end-of-session]\n"
: asctime(gmtime(&pc->tExpires)))));
}
}
}
}
return;
}
#endif /* XX_DEBUG */
#endif /* FEATURE_HTTP_COOKIES */