/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public * License Version 1.1 (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.mozilla.org/NPL/ * * 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 mozilla.org code. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): */ #define alphabetize 1 #include "nsCookie.h" #include "nsString.h" #include "nsIServiceManager.h" #include "nsFileStream.h" #include "nsIFileLocator.h" #include "nsIFileSpec.h" #include "nsFileLocations.h" #include "nsINetSupportDialogService.h" #include "nsIIOService.h" #include "nsIURL.h" #include "nsIStringBundle.h" #include "nsVoidArray.h" #include "nsIPref.h" #include "prefapi.h" #include "nsTextFormater.h" extern "C" { #include "prmon.h" #ifdef XP_MAC #include "prpriv.h" /* for NewNamedMonitor */ #else #include "private/prpriv.h" #endif } static NS_DEFINE_IID(kIFileLocatorIID, NS_IFILELOCATOR_IID); static NS_DEFINE_CID(kFileLocatorCID, NS_FILELOCATOR_CID); static NS_DEFINE_CID(kNetSupportDialogCID, NS_NETSUPPORTDIALOG_CID); static NS_DEFINE_IID(kIIOServiceIID, NS_IIOSERVICE_IID); static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID); static NS_DEFINE_IID(kIStringBundleServiceIID, NS_ISTRINGBUNDLESERVICE_IID); static NS_DEFINE_IID(kStringBundleServiceCID, NS_STRINGBUNDLESERVICE_CID); #define MAX_NUMBER_OF_COOKIES 300 #define MAX_COOKIES_PER_SERVER 20 #define MAX_BYTES_PER_COOKIE 4096 /* must be at least 1 */ #ifndef MAX_HOST_NAME_LEN #define MAX_HOST_NAME_LEN 64 #endif #define cookie_behaviorPref "network.cookie.cookieBehavior" #define cookie_warningPref "network.cookie.warnAboutCookies" #define cookie_strictDomainsPref "network.cookie.strictDomains" #define cookie_localization "chrome://wallet/locale/cookie.properties" #define COOKIE_IS_SPACE(x) ((((unsigned int) (x)) > 0x7f) ? 0 : isspace(x)) //#define GET_ALL_PARTS 127 #define GET_PASSWORD_PART 64 #define GET_USERNAME_PART 32 #define GET_PROTOCOL_PART 16 #define GET_HOST_PART 8 #define GET_PATH_PART 4 #define GET_HASH_PART 2 #define GET_SEARCH_PART 1 MODULE_PRIVATE time_t cookie_ParseDate(char *date_string); MODULE_PRIVATE char * cookie_ParseURL (const char *url, int parts_requested); MODULE_PRIVATE char * XP_StripLine (char *string); typedef struct _cookie_CookieStruct { char * path; char * host; char * name; char * cookie; time_t expires; time_t lastAccessed; PRBool xxx; PRBool isDomain; /* is it a domain instead of an absolute host? */ } cookie_CookieStruct; typedef struct _cookie_PermissionStruct { char * host; PRBool permission; } cookie_PermissionStruct; typedef struct _cookie_DeferStruct { char * curURL; char * setCookieHeader; time_t timeToExpire; } cookie_DeferStruct; #include "prthread.h" #include "prmon.h" PRBool cookie_SetCookieStringInUse = PR_FALSE; PRIVATE PRBool cookie_cookiesChanged = PR_FALSE; PRIVATE PRBool cookie_permissionsChanged = PR_FALSE; PRIVATE PRBool cookie_rememberChecked = PR_FALSE; PRIVATE COOKIE_BehaviorEnum cookie_behavior = COOKIE_Accept; PRIVATE PRBool cookie_warning = PR_FALSE; PRIVATE nsVoidArray * cookie_cookieList=0; PRIVATE nsVoidArray * cookie_permissionList=0; PRIVATE nsVoidArray * cookie_deferList=0; static PRMonitor * cookie_cookieLockMonitor = NULL; static PRThread * cookie_cookieLockOwner = NULL; static int cookie_cookieLockCount = 0; static PRMonitor * cookie_deferLockMonitor = NULL; static PRThread * cookie_deferLockOwner = NULL; static int cookie_deferLockCount = 0; #define REAL_DIALOG 1 /* StrAllocCopy and StrAllocCat should really be defined elsewhere */ #include "plstr.h" #include "prmem.h" #undef StrAllocCopy #define StrAllocCopy(dest, src) Local_SACopy (&(dest), src) char * Local_SACopy(char **destination, const char *source) { if(*destination) { PL_strfree(*destination); *destination = 0; } *destination = PL_strdup(source); return *destination; } #undef StrAllocCat #define StrAllocCat(dest, src) Local_SACat (&(dest), src) char * Local_SACat(char **destination, const char *source) { if (source && *source) { if (*destination) { int length = PL_strlen (*destination); *destination = (char *) PR_Realloc(*destination, length + PL_strlen(source) + 1); if (*destination == NULL) { return(NULL); } PL_strcpy (*destination + length, source); } else { *destination = PL_strdup(source); } } return *destination; } PRIVATE PRUnichar* cookie_Localize(char* genericString) { nsresult ret; nsAutoString v(""); /* create a URL for the string resource file */ nsIIOService* pNetService = nsnull; ret = nsServiceManager::GetService(kIOServiceCID, kIIOServiceIID, (nsISupports**) &pNetService); if (NS_FAILED(ret)) { printf("cannot get net service\n"); return v.ToNewUnicode(); } nsIURI *url = nsnull; nsIURI *uri = nsnull; ret = pNetService->NewURI(cookie_localization, nsnull, &uri); if (NS_FAILED(ret)) { printf("cannot create URI\n"); nsServiceManager::ReleaseService(kIOServiceCID, pNetService); return v.ToNewUnicode(); } ret = uri->QueryInterface(nsIURI::GetIID(), (void**)&url); nsServiceManager::ReleaseService(kIOServiceCID, pNetService); if (NS_FAILED(ret)) { printf("cannot create URL\n"); return v.ToNewUnicode(); } /* create a bundle for the localization */ nsIStringBundleService* pStringService = nsnull; ret = nsServiceManager::GetService(kStringBundleServiceCID, kIStringBundleServiceIID, (nsISupports**) &pStringService); if (NS_FAILED(ret)) { printf("cannot get string service\n"); return v.ToNewUnicode(); } nsILocale* locale = nsnull; nsIStringBundle* bundle = nsnull; char* spec = nsnull; ret = url->GetSpec(&spec); if (NS_FAILED(ret)) { printf("cannot get url spec\n"); nsServiceManager::ReleaseService(kStringBundleServiceCID, pStringService); nsCRT::free(spec); return v.ToNewUnicode(); } ret = pStringService->CreateBundle(spec, locale, &bundle); nsCRT::free(spec); if (NS_FAILED(ret)) { printf("cannot create instance\n"); nsServiceManager::ReleaseService(kStringBundleServiceCID, pStringService); return v.ToNewUnicode(); } nsServiceManager::ReleaseService(kStringBundleServiceCID, pStringService); /* localize the given string */ nsString strtmp(genericString); const PRUnichar *ptrtmp = strtmp.GetUnicode(); PRUnichar *ptrv = nsnull; ret = bundle->GetStringFromName(ptrtmp, &ptrv); v = ptrv; NS_RELEASE(bundle); if (NS_FAILED(ret)) { printf("cannot get string from name\n"); return v.ToNewUnicode(); } nsCRT::free(ptrv); return v.ToNewUnicode(); } PRBool cookie_CheckConfirmYN(PRUnichar * szMessage, PRUnichar * szCheckMessage, PRBool* checkValue) { nsresult res; NS_WITH_SERVICE(nsIPrompt, dialog, kNetSupportDialogCID, &res); if (NS_FAILED(res)) { *checkValue = 0; return PR_FALSE; } PRInt32 buttonPressed = 1; /* in case user exits dialog by clickin X */ PRUnichar * yes_string = cookie_Localize("Yes"); PRUnichar * no_string = cookie_Localize("No"); PRUnichar * confirm_string = cookie_Localize("Confirm"); res = dialog->UniversalDialog( NULL, /* title message */ confirm_string, /* title text in top line of window */ szMessage, /* this is the main message */ szCheckMessage, /* This is the checkbox message */ yes_string, /* first button text */ no_string, /* second button text */ NULL, /* third button text */ NULL, /* fourth button text */ NULL, /* first edit field label */ NULL, /* second edit field label */ NULL, /* first edit field initial and final value */ NULL, /* second edit field initial and final value */ nsString("chrome://global/skin/question-icon.gif").GetUnicode() , checkValue, /* initial and final value of checkbox */ 2, /* number of buttons */ 0, /* number of edit fields */ 0, /* is first edit field a password field */ &buttonPressed); if (NS_FAILED(res)) { *checkValue = 0; } if (*checkValue!=0 && *checkValue!=1) { *checkValue = 0; /* this should never happen but it is happening!!! */ } Recycle(yes_string); Recycle(no_string); Recycle(confirm_string); return (buttonPressed == 0); #ifdef yyy /* following is an example of the most general usage of UniversalDialog */ PRUnichar* inoutEdit1 = nsString("Edit field1 initial value").GetUnicode(); PRUnichar* inoutEdit2 = nsString("Edit field2 initial value").GetUnicode(); PRBool inoutCheckbox = PR_TRUE; PRInt32 buttonPressed; res = dialog->UniversalDialog( nsString("Title Message").GetUnicode(), nsString("Dialog Title").GetUnicode(), nsString("This is the main message").GetUnicode(), nsString("This is the checkbox message").GetUnicode(), nsString("First Button").GetUnicode(), nsString("Second Button").GetUnicode(), nsString("Third Button").GetUnicode(), nsString("Fourth Button").GetUnicode(), nsString("First Edit field").GetUnicode(), nsString("Second Edit field").GetUnicode(), &inoutEdit1, &inoutEdit2, nsString("chrome://global/skin/question-icon.gif").GetUnicode() , &inoutCheckbox, 4, /* number of buttons */ 2, /* number of edit fields */ 0, /* is first edit field a password field */ &buttonPressed); #endif } PRIVATE nsresult cookie_ProfileDirectory(nsFileSpec& dirSpec) { nsIFileSpec* spec = NS_LocateFileOrDirectory(nsSpecialFileSpec::App_UserProfileDirectory50); if (!spec) { return NS_ERROR_FAILURE; } nsresult res = spec->GetFileSpec(&dirSpec); NS_RELEASE(spec); return res; } /* * Write a line to a file * return NS_OK if no error occurs */ nsresult cookie_Put(nsOutputFileStream strm, const nsString& aLine) { /* allocate a buffer from the heap */ char * cp = aLine.ToNewCString(); if (! cp) { return NS_ERROR_FAILURE; } /* output each character */ char* p = cp; while (*p) { strm.put(*(p++)); } nsCRT::free(cp); return NS_OK; } /* * get a line from a file * return -1 if end of file reached * strip carriage returns and line feeds from end of line */ PRInt32 cookie_GetLine(nsInputFileStream strm, nsAutoString& aLine) { /* read the line */ aLine.Truncate(); char c; for (;;) { c = strm.get(); if (c == '\n') { break; } /* note that eof is not set until we read past the end of the file */ if (strm.eof()) { return -1; } if (c != '\r') { aLine.Append(c); // aLine += c; } } return 0; } PRIVATE void cookie_LockCookieList(void) { if(!cookie_cookieLockMonitor) { cookie_cookieLockMonitor = PR_NewNamedMonitor("cookie-lock"); } PR_EnterMonitor(cookie_cookieLockMonitor); while(PR_TRUE) { /* no current owner or owned by this thread */ PRThread * t = PR_CurrentThread(); if(cookie_cookieLockOwner == NULL || cookie_cookieLockOwner == t) { cookie_cookieLockOwner = t; cookie_cookieLockCount++; PR_ExitMonitor(cookie_cookieLockMonitor); return; } /* owned by someone else -- wait till we can get it */ PR_Wait(cookie_cookieLockMonitor, PR_INTERVAL_NO_TIMEOUT); } } PRIVATE void cookie_UnlockCookieList(void) { PR_EnterMonitor(cookie_cookieLockMonitor); #ifdef DEBUG /* make sure someone doesn't try to free a lock they don't own */ PR_ASSERT(cookie_cookieLockOwner == PR_CurrentThread()); #endif cookie_cookieLockCount--; if(cookie_cookieLockCount == 0) { cookie_cookieLockOwner = NULL; PR_Notify(cookie_cookieLockMonitor); } PR_ExitMonitor(cookie_cookieLockMonitor); } PRIVATE void cookie_LockDeferList(void) { if(!cookie_deferLockMonitor) { cookie_deferLockMonitor = PR_NewNamedMonitor("defer_cookie-lock"); } PR_EnterMonitor(cookie_deferLockMonitor); while(PR_TRUE) { /* no current owner or owned by this thread */ PRThread * t = PR_CurrentThread(); if(cookie_deferLockOwner == NULL || cookie_deferLockOwner == t) { cookie_deferLockOwner = t; cookie_deferLockCount++; PR_ExitMonitor(cookie_deferLockMonitor); return; } /* owned by someone else -- wait till we can get it */ PR_Wait(cookie_deferLockMonitor, PR_INTERVAL_NO_TIMEOUT); } } PRIVATE void cookie_UnlockDeferList(void) { PR_EnterMonitor(cookie_deferLockMonitor); #ifdef DEBUG /* make sure someone doesn't try to free a lock they don't own */ PR_ASSERT(cookie_deferLockOwner == PR_CurrentThread()); #endif cookie_deferLockCount--; if(cookie_deferLockCount == 0) { cookie_deferLockOwner = NULL; PR_Notify(cookie_deferLockMonitor); } PR_ExitMonitor(cookie_deferLockMonitor); } static PRMonitor * cookie_permission_lock_monitor = NULL; static PRThread * cookie_permission_lock_owner = NULL; static int cookie_permission_lock_count = 0; PRIVATE void cookie_LockPermissionList(void) { if(!cookie_permission_lock_monitor) cookie_permission_lock_monitor = PR_NewNamedMonitor("cookie_permission-lock"); PR_EnterMonitor(cookie_permission_lock_monitor); while(PR_TRUE) { /* no current owner or owned by this thread */ PRThread * t = PR_CurrentThread(); if(cookie_permission_lock_owner == NULL || cookie_permission_lock_owner == t) { cookie_permission_lock_owner = t; cookie_permission_lock_count++; PR_ExitMonitor(cookie_permission_lock_monitor); return; } /* owned by someone else -- wait till we can get it */ PR_Wait(cookie_permission_lock_monitor, PR_INTERVAL_NO_TIMEOUT); } } PRIVATE void cookie_UnlockPermissionListst(void) { PR_EnterMonitor(cookie_permission_lock_monitor); #ifdef DEBUG /* make sure someone doesn't try to free a lock they don't own */ PR_ASSERT(cookie_permission_lock_owner == PR_CurrentThread()); #endif cookie_permission_lock_count--; if(cookie_permission_lock_count == 0) { cookie_permission_lock_owner = NULL; PR_Notify(cookie_permission_lock_monitor); } PR_ExitMonitor(cookie_permission_lock_monitor); } PRIVATE void cookie_SavePermissions(); PRIVATE void cookie_SaveCookies(); PRIVATE void cookie_FreePermission(cookie_PermissionStruct * cookie_permission, PRBool save) { /* * This routine should only be called while holding the * cookie permission list lock */ if(!cookie_permission) { return; } if (cookie_permissionList == nsnull) return; cookie_permissionList->RemoveElement(cookie_permission); PR_FREEIF(cookie_permission->host); PR_Free(cookie_permission); if (save) { cookie_permissionsChanged = PR_TRUE; cookie_SavePermissions(); } } /* blows away all cookie permissions currently in the list */ PRIVATE void cookie_RemoveAllPermissions() { cookie_PermissionStruct * victim; /* check for NULL or empty list */ cookie_LockPermissionList(); if (cookie_permissionList == nsnull) { cookie_UnlockPermissionListst(); return; } PRInt32 count = cookie_permissionList->Count(); for (PRInt32 i = count-1; i >=0; i--) { victim = NS_STATIC_CAST(cookie_PermissionStruct*, cookie_permissionList->ElementAt(i)); if (victim) { cookie_FreePermission(victim, PR_FALSE); } } delete cookie_permissionList; cookie_permissionList = NULL; cookie_UnlockPermissionListst(); } /* This should only get called while holding the cookie-lock */ PRIVATE void cookie_FreeCookie(cookie_CookieStruct * cookie) { if(!cookie) { return; } if (cookie_cookieList == nsnull) { return; } cookie_cookieList->RemoveElement(cookie); PR_FREEIF(cookie->path); PR_FREEIF(cookie->host); PR_FREEIF(cookie->name); PR_FREEIF(cookie->cookie); PR_Free(cookie); cookie_cookiesChanged = PR_TRUE; } /* blows away all cookies currently in the list, then blows away the list itself * nulling it after it's free'd */ PRIVATE void cookie_RemoveAllCookies() { cookie_CookieStruct * victim; /* check for NULL or empty list */ cookie_LockCookieList(); if (cookie_cookieList == nsnull) { cookie_UnlockCookieList(); return; } PRInt32 count = cookie_cookieList->Count(); for (PRInt32 i = count-1; i >=0; i--) { victim = NS_STATIC_CAST(cookie_CookieStruct*, cookie_cookieList->ElementAt(i)); if (victim) { cookie_FreeCookie(victim); } } delete cookie_cookieList; cookie_cookieList = NULL; cookie_UnlockCookieList(); } PUBLIC void COOKIE_RemoveAllCookies() { cookie_RemoveAllPermissions(); cookie_RemoveAllCookies(); } PRIVATE void cookie_RemoveOldestCookie(void) { cookie_CookieStruct * cookie_s; cookie_CookieStruct * oldest_cookie; cookie_LockCookieList(); if (cookie_cookieList == nsnull) { cookie_UnlockCookieList(); return; } PRInt32 count = cookie_cookieList->Count(); if (count == 0) { cookie_UnlockCookieList(); return; } oldest_cookie = NS_STATIC_CAST(cookie_CookieStruct*, cookie_cookieList->ElementAt(0)); for (PRInt32 i = 1; i < count; ++i) { cookie_s = NS_STATIC_CAST(cookie_CookieStruct*, cookie_cookieList->ElementAt(i)); if (cookie_s) { if(cookie_s->lastAccessed < oldest_cookie->lastAccessed) { oldest_cookie = cookie_s; } } } if(oldest_cookie) { // TRACEMSG(("Freeing cookie because global max cookies has been exceeded")); cookie_FreeCookie(oldest_cookie); } cookie_UnlockCookieList(); } /* Remove any expired cookies from memory ** This routine should only be called while holding the cookie list lock */ PRIVATE void cookie_RemoveExpiredCookies() { cookie_CookieStruct * cookie_s; time_t cur_time = time(NULL); if (cookie_cookieList == nsnull) { return; } PRInt32 count = cookie_cookieList->Count(); for (PRInt32 i = 0; i < count; ++i) { cookie_s = NS_STATIC_CAST(cookie_CookieStruct*, cookie_cookieList->ElementAt(i)); if (cookie_s) { /* * Don't get rid of expire time 0 because these need to last for * the entire session. They'll get cleared on exit. */ if( cookie_s->expires && (cookie_s->expires < cur_time) ) { cookie_FreeCookie(cookie_s); /* * Reset the list_ptr to the beginning of the list. * Do this because list_ptr's object was just freed * by the call to cookie_FreeCookie struct, even * though it's inefficient. */ } } } } /* checks to see if the maximum number of cookies per host * is being exceeded and deletes the oldest one in that * case * This routine should only be called while holding the cookie lock */ PRIVATE void cookie_CheckForMaxCookiesFromHo(const char * cur_host) { cookie_CookieStruct * cookie_s; cookie_CookieStruct * oldest_cookie = 0; int cookie_count = 0; if (cookie_cookieList == nsnull) { return; } PRInt32 count = cookie_cookieList->Count(); for (PRInt32 i = 0; i < count; ++i) { cookie_s = NS_STATIC_CAST(cookie_CookieStruct*, cookie_cookieList->ElementAt(i)); if (cookie_s) { if(!PL_strcasecmp(cookie_s->host, cur_host)) { cookie_count++; if(!oldest_cookie || oldest_cookie->lastAccessed > cookie_s->lastAccessed) { oldest_cookie = cookie_s; } } } } if(cookie_count >= MAX_COOKIES_PER_SERVER && oldest_cookie) { // TRACEMSG(("Freeing cookie because max cookies per server has been exceeded")); cookie_FreeCookie(oldest_cookie); } } /* search for previous exact match ** This routine should only be called while holding the cookie lock */ PRIVATE cookie_CookieStruct * cookie_CheckForPrevCookie(char * path, char * hostname, char * name) { cookie_CookieStruct * cookie_s; if (cookie_cookieList == nsnull) { return NULL; } PRInt32 count = cookie_cookieList->Count(); for (PRInt32 i = 0; i < count; ++i) { cookie_s = NS_STATIC_CAST(cookie_CookieStruct*, cookie_cookieList->ElementAt(i)); if (cookie_s) { if(path && hostname && cookie_s->path && cookie_s->host && cookie_s->name && !PL_strcmp(name, cookie_s->name) && !PL_strcmp(path, cookie_s->path) && !PL_strcasecmp(hostname, cookie_s->host)) { return(cookie_s); } } } return(NULL); } /* cookie utility functions */ PRIVATE void cookie_SetBehaviorPref(COOKIE_BehaviorEnum x) { cookie_behavior = x; //HG83330 -- @@?? if(cookie_behavior == COOKIE_DontUse) { // NET_XP_FileRemove("", xpHTTPCookie); // NET_XP_FileRemove("", xpHTTPCookiePermission); } } PRIVATE void cookie_SetWarningPref(PRBool x) { cookie_warning = x; } PUBLIC COOKIE_BehaviorEnum COOKIE_GetBehaviorPref() { return cookie_behavior; } PRIVATE PRBool cookie_GetWarningPref() { return cookie_warning; } MODULE_PRIVATE int PR_CALLBACK cookie_BehaviorPrefChanged(const char * newpref, void * data) { PRInt32 n; nsresult rv; NS_WITH_SERVICE(nsIPref, prefs, "component://netscape/preferences", &rv); if (NS_FAILED(prefs->GetIntPref(cookie_behaviorPref, &n))) { cookie_SetBehaviorPref(COOKIE_Accept); } else { cookie_SetBehaviorPref((COOKIE_BehaviorEnum)n); } return PREF_NOERROR; } MODULE_PRIVATE int PR_CALLBACK cookie_WarningPrefChanged(const char * newpref, void * data) { PRBool x; nsresult rv; NS_WITH_SERVICE(nsIPref, prefs, "component://netscape/preferences", &rv); if (NS_FAILED(prefs->GetBoolPref(cookie_warningPref, &x))) { x = PR_FALSE; } cookie_SetWarningPref(x); return PREF_NOERROR; } /* * search if permission already exists */ PRIVATE cookie_PermissionStruct * cookie_CheckForPermission(char * hostname) { cookie_PermissionStruct * cookie_s; /* ignore leading period in host name */ while (hostname && (*hostname == '.')) { hostname++; } cookie_LockPermissionList(); if (cookie_permissionList == nsnull) { cookie_UnlockPermissionListst(); return(NULL); } PRInt32 count = cookie_permissionList->Count(); for (PRInt32 i = 0; i < count; ++i) { cookie_s = NS_STATIC_CAST(cookie_PermissionStruct*, cookie_permissionList->ElementAt(i)); if (cookie_s) { if(hostname && cookie_s->host && !PL_strcasecmp(hostname, cookie_s->host)) { cookie_UnlockPermissionListst(); return(cookie_s); } } } cookie_UnlockPermissionListst(); return(NULL); } /* called from mkgeturl.c, NET_InitNetLib(). This sets the module local * cookie pref variables and registers the callbacks */ PUBLIC void COOKIE_RegisterCookiePrefCallbacks(void) { PRInt32 n; PRBool x; nsresult rv; NS_WITH_SERVICE(nsIPref, prefs, "component://netscape/preferences", &rv); if (NS_FAILED(prefs->GetIntPref(cookie_behaviorPref, &n))) { cookie_SetBehaviorPref(COOKIE_Accept); } else { cookie_SetBehaviorPref((COOKIE_BehaviorEnum)n); } cookie_SetBehaviorPref((COOKIE_BehaviorEnum)n); prefs->RegisterCallback(cookie_behaviorPref, cookie_BehaviorPrefChanged, NULL); if (NS_FAILED(prefs->GetBoolPref(cookie_warningPref, &x))) { x = PR_FALSE; } cookie_SetWarningPref(x); prefs->RegisterCallback(cookie_warningPref, cookie_WarningPrefChanged, NULL); } /* returns PR_TRUE if authorization is required ** ** ** IMPORTANT: Now that this routine is multi-threaded it is up ** to the caller to free any returned string */ PUBLIC char * COOKIE_GetCookie(char * address) { char *name=0; cookie_CookieStruct * cookie_s; PRBool first=PR_TRUE; PRBool xxx = PR_FALSE; time_t cur_time = time(NULL); int host_length; int domain_length; /* return string to build */ char * rv=0; /* disable cookies if the user's prefs say so */ if(COOKIE_GetBehaviorPref() == COOKIE_DontUse) { return NULL; } if (!PL_strncasecmp(address, "https", 5)) { xxx = PR_TRUE; } /* search for all cookies */ cookie_LockCookieList(); if (cookie_cookieList == nsnull) { cookie_UnlockCookieList(); return NULL; } char *host = cookie_ParseURL(address, GET_HOST_PART); char *path = cookie_ParseURL(address, GET_PATH_PART); PRInt32 count = cookie_cookieList->Count(); for (PRInt32 i = 0; i < count; ++i) { cookie_s = NS_STATIC_CAST(cookie_CookieStruct*, cookie_cookieList->ElementAt(i)); if (cookie_s == nsnull) { continue; } if(!cookie_s->host) { continue; } /* check the host or domain first */ if(cookie_s->isDomain) { char *cp; domain_length = PL_strlen(cookie_s->host); /* calculate the host length by looking at all characters up to a * colon or '\0'. That way we won't include port numbers in domains */ for(cp=host; *cp != '\0' && *cp != ':'; cp++) { ; /* null body */ } host_length = cp - host; if(domain_length > host_length || PL_strncasecmp(cookie_s->host, &host[host_length - domain_length], domain_length)) { // HG20476 -- @@?? /* no match. FAIL */ continue; } } else if(PL_strcasecmp(host, cookie_s->host)) { /* hostname matchup failed. FAIL */ continue; } /* shorter strings always come last so there can be no ambiquity */ if(cookie_s->path && !PL_strncmp(path, cookie_s->path, PL_strlen(cookie_s->path))) { /* if the cookie is xxx and the path isn't, dont send it */ if (cookie_s->xxx & !xxx) { continue; /* back to top of while */ } /* check for expired cookies */ if( cookie_s->expires && (cookie_s->expires < cur_time) ) { /* expire and remove the cookie */ cookie_FreeCookie(cookie_s); /* start the list parsing over :( we must also start the string over */ PR_FREEIF(rv); rv = NULL; i = -1; first = PR_TRUE; /* reset first */ continue; } if(first) { first = PR_FALSE; } else { StrAllocCat(rv, "; "); } if(cookie_s->name && *cookie_s->name != '\0') { cookie_s->lastAccessed = cur_time; StrAllocCopy(name, cookie_s->name); StrAllocCat(name, "="); #ifdef PREVENT_DUPLICATE_NAMES /* make sure we don't have a previous name mapping already in the string */ if(!rv || !PL_strstr(rv, name)) { StrAllocCat(rv, name); StrAllocCat(rv, cookie_s->cookie); } #else StrAllocCat(rv, name); StrAllocCat(rv, cookie_s->cookie); #endif /* PREVENT_DUPLICATE_NAMES */ } else { StrAllocCat(rv, cookie_s->cookie); } } } cookie_UnlockCookieList(); PR_FREEIF(name); PR_FREEIF(path); PR_FREEIF(host); /* may be NULL */ return(rv); } /* Determines whether the inlineHost is in the same domain as the currentHost. * For use with rfc 2109 compliance/non-compliance. */ PRIVATE int cookie_SameDomain(char * currentHost, char * firstHost) { char * dot = 0; char * currentDomain = 0; char * firstDomain = 0; if(!currentHost || !firstHost) { return 0; } /* case insensitive compare */ if(PL_strcasecmp(currentHost, firstHost) == 0) { return 1; } currentDomain = PL_strchr(currentHost, '.'); firstDomain = PL_strchr(firstHost, '.'); if(!currentDomain || !firstDomain) { return 0; } /* check for at least two dots before continuing, if there are * not two dots we don't have enough information to determine * whether or not the firstDomain is within the currentDomain */ dot = PL_strchr(firstDomain, '.'); if(dot) { dot = PL_strchr(dot+1, '.'); } else { return 0; } /* handle .com. case */ if(!dot || (*(dot+1) == '\0')) { return 0; } if(!PL_strcasecmp(firstDomain, currentDomain)) { return 1; } return 0; } PRBool cookie_isForeign (char * curURL, char * firstURL) { char * curHost = cookie_ParseURL(curURL, GET_HOST_PART); char * firstHost = cookie_ParseURL(firstURL, GET_HOST_PART); char * curHostColon = 0; char * firstHostColon = 0; /* strip ports */ curHostColon = PL_strchr(curHost, ':'); if(curHostColon) { *curHostColon = '\0'; } firstHostColon = PL_strchr(firstHost, ':'); if(firstHostColon) { *firstHostColon = '\0'; } /* determine if it's foreign */ PRBool retval = (!cookie_SameDomain(curHost, firstHost)); /* clean up our garbage and return */ if(curHostColon) { *curHostColon = ':'; } if(firstHostColon) { *firstHostColon = ':'; } PR_FREEIF(curHost); PR_FREEIF(firstHost); return retval; } /* returns PR_TRUE if authorization is required ** ** ** IMPORTANT: Now that this routine is multi-threaded it is up ** to the caller to free any returned string */ PUBLIC char * COOKIE_GetCookieFromHttp(char * address, char * firstAddress) { if ((COOKIE_GetBehaviorPref() == COOKIE_DontAcceptForeign) && cookie_isForeign(address, firstAddress)) { /* * WARNING!!! This is a different behavior than 4.x. In 4.x we used this pref to * control the setting of cookies only. Here we are also blocking the getting of * cookies if the pref is set. It may be that we need a separate pref to block the * getting of cookies. But for now we are putting both under one pref since that * is cleaner. If it turns out that this breaks some important websites, we may * have to resort to two prefs */ return NULL; } return COOKIE_GetCookie(address); } void cookie_AddPermission(cookie_PermissionStruct * cookie_permission, PRBool save ) { /* * This routine should only be called while holding the * cookie permission list lock */ if (cookie_permission) { if(!cookie_permissionList) { cookie_permissionList = new nsVoidArray(); if(!cookie_permissionList) { PR_Free(cookie_permission->host); PR_Free(cookie_permission); return; } } #ifdef alphabetize /* add it to the list in alphabetical order */ { cookie_PermissionStruct * tmp_cookie_permission; PRBool permissionAdded = PR_FALSE; PRInt32 count = cookie_permissionList->Count(); for (PRInt32 i = 0; i < count; ++i) { tmp_cookie_permission = NS_STATIC_CAST(cookie_PermissionStruct*, cookie_permissionList->ElementAt(i)); if (tmp_cookie_permission) { if (PL_strcasecmp(cookie_permission->host,tmp_cookie_permission->host)<0) { cookie_permissionList->InsertElementAt(cookie_permission, i); permissionAdded = PR_TRUE; break; } } } if (!permissionAdded) { cookie_permissionList->AppendElement(cookie_permission); } } #else /* add it to the end of the list */ cookie_permissionList->AppendElement(cookie_permission); #endif if (save) { cookie_permissionsChanged = PR_TRUE; cookie_SavePermissions(); } } } MODULE_PRIVATE PRBool cookie_IsFromHost(cookie_CookieStruct *cookie_s, char *host) { if (!cookie_s || !(cookie_s->host)) { return PR_FALSE; } if (cookie_s->isDomain) { char *cp; int domain_length, host_length; domain_length = PL_strlen(cookie_s->host); /* calculate the host length by looking at all characters up to * a colon or '\0'. That way we won't include port numbers * in domains */ for(cp=host; *cp != '\0' && *cp != ':'; cp++) { ; /* null body */ } host_length = cp - host; /* compare the tail end of host to cook_s->host */ return (domain_length <= host_length && PL_strncasecmp (cookie_s->host, &host[host_length - domain_length], domain_length) == 0); } else { return PL_strcasecmp(host, cookie_s->host) == 0; } } /* find out how many cookies this host has already set */ PRIVATE int cookie_Count(char * host) { int count = 0; cookie_CookieStruct * cookie; cookie_LockCookieList(); if (cookie_cookieList == nsnull) { cookie_UnlockCookieList(); return count; } PRInt32 cookie_count = cookie_cookieList->Count(); for (PRInt32 i = 0; i < cookie_count; ++i) { cookie = NS_STATIC_CAST(cookie_CookieStruct*, cookie_cookieList->ElementAt(i)); if (cookie) { if (host && cookie_IsFromHost(cookie, host)) { count++; } } } cookie_UnlockCookieList(); return count; } PRIVATE void cookie_Defer(char * curURL, char * setCookieHeader, time_t timeToExpire) { cookie_DeferStruct * defer_cookie = PR_NEW(cookie_DeferStruct); defer_cookie->curURL = NULL; StrAllocCopy(defer_cookie->curURL, curURL); defer_cookie->setCookieHeader = NULL; StrAllocCopy(defer_cookie->setCookieHeader, setCookieHeader); defer_cookie->timeToExpire = timeToExpire; cookie_LockDeferList(); if (!cookie_deferList) { cookie_deferList = new nsVoidArray(); if (!cookie_deferList) { PR_FREEIF(defer_cookie->curURL); PR_FREEIF(defer_cookie->setCookieHeader); PR_Free(defer_cookie); cookie_UnlockDeferList(); return; } } cookie_deferList->InsertElementAt(defer_cookie, 0); cookie_UnlockDeferList(); } PRIVATE void cookie_SetCookieString(char * curURL, char * setCookieHeader, time_t timeToExpire ); PRIVATE void cookie_Undefer() { cookie_DeferStruct * defer_cookie; cookie_LockDeferList(); if(cookie_deferList == nsnull) { cookie_UnlockDeferList(); return; } PRInt32 count = cookie_deferList->Count(); if (count == 0) { cookie_UnlockDeferList(); return; } defer_cookie = NS_STATIC_CAST(cookie_DeferStruct*, cookie_deferList->ElementAt(count-1)); cookie_deferList->RemoveElementAt(count-1); cookie_UnlockDeferList(); if (defer_cookie) { cookie_SetCookieString (defer_cookie->curURL, defer_cookie->setCookieHeader, defer_cookie->timeToExpire); PR_FREEIF(defer_cookie->curURL); PR_FREEIF(defer_cookie->setCookieHeader); PR_Free(defer_cookie); } } /* Java script is calling COOKIE_SetCookieString, netlib is calling * this via COOKIE_SetCookieStringFromHttp. */ PRIVATE void cookie_SetCookieString(char * curURL, char * setCookieHeader, time_t timeToExpire) { cookie_CookieStruct * prev_cookie; char *path_from_header=NULL, *host_from_header=NULL; char *host_from_header2=NULL; char *name_from_header=NULL, *cookie_from_header=NULL; time_t expires=0; char *cur_path = cookie_ParseURL(curURL, GET_PATH_PART); char *cur_host = cookie_ParseURL(curURL, GET_HOST_PART); char *semi_colon, *ptr, *equal; PRBool xxx=PR_FALSE, isDomain=PR_FALSE, acceptIt=PR_FALSE; PRBool bCookieAdded; PRBool pref_scd = PR_FALSE; /* Only allow cookies to be set in the listed contexts. * We don't want cookies being set in html mail. */ /* We need to come back and work on this - Neeti type = context->type; if(!((type==MWContextBrowser) || (type==MWContextHTMLHelp) || (type==MWContextPane))) { PR_Free(cur_path); PR_Free(cur_host); return; } */ if(COOKIE_GetBehaviorPref() == COOKIE_DontUse) { PR_Free(cur_path); PR_Free(cur_host); return; } /* Don't enter this routine if it is already in use by another thread. Otherwise the "remember this decision" result of the other cookie (which came first) won't get applied to this cookie. */ if (cookie_SetCookieStringInUse) { PR_Free(cur_path); PR_Free(cur_host); cookie_Defer(curURL, setCookieHeader, timeToExpire); return; } cookie_SetCookieStringInUse = PR_TRUE; //HG87358 -- @@?? /* terminate at any carriage return or linefeed */ for(ptr=setCookieHeader; *ptr; ptr++) { if(*ptr == LF || *ptr == CR) { *ptr = '\0'; break; } } /* parse path and expires attributes from header if present */ semi_colon = PL_strchr(setCookieHeader, ';'); if(semi_colon) { /* truncate at semi-colon and advance */ *semi_colon++ = '\0'; /* there must be some attributes. (hopefully) */ if ((ptr=PL_strcasestr(semi_colon, "secure="))) { char cPre=*(ptr-1), cPost=*(ptr+6); if (((cPre==' ') || (cPre==';')) && (!cPost || (cPost==' ') || (cPost==';'))) { xxx = PR_TRUE; } } /* look for the path attribute */ ptr = PL_strcasestr(semi_colon, "path="); if(ptr) { /* allocate more than we need */ StrAllocCopy(path_from_header, XP_StripLine(ptr+5)); /* terminate at first space or semi-colon */ for(ptr=path_from_header; *ptr != '\0'; ptr++) { if(COOKIE_IS_SPACE(*ptr) || *ptr == ';' || *ptr == ',') { *ptr = '\0'; break; } } } /* look for the URI or URL attribute * * This might be a security hole so I'm removing * it for now. */ /* look for a domain */ ptr = PL_strcasestr(semi_colon, "domain="); if(ptr) { char *domain_from_header=NULL; char *dot, *colon; int domain_length, cur_host_length; /* allocate more than we need */ StrAllocCopy(domain_from_header, XP_StripLine(ptr+7)); /* terminate at first space or semi-colon */ for(ptr=domain_from_header; *ptr != '\0'; ptr++) { if(COOKIE_IS_SPACE(*ptr) || *ptr == ';' || *ptr == ',') { *ptr = '\0'; break; } } /* verify that this host has the authority to set for this domain. We do * this by making sure that the host is in the domain. We also require * that a domain have at least two periods to prevent domains of the form * ".com" and ".edu" * * Also make sure that there is more stuff after * the second dot to prevent ".com." */ dot = PL_strchr(domain_from_header, '.'); if(dot) { dot = PL_strchr(dot+1, '.'); } if(!dot || *(dot+1) == '\0') { /* did not pass two dot test. FAIL */ PR_Free(domain_from_header); PR_Free(cur_path); PR_Free(cur_host); // TRACEMSG(("DOMAIN failed two dot test")); cookie_SetCookieStringInUse = PR_FALSE; cookie_Undefer(); return; } /* strip port numbers from the current host for the domain test */ colon = PL_strchr(cur_host, ':'); if(colon) { *colon = '\0'; } domain_length = PL_strlen(domain_from_header); cur_host_length = PL_strlen(cur_host); /* check to see if the host is in the domain */ if(domain_length > cur_host_length || PL_strcasecmp(domain_from_header, &cur_host[cur_host_length-domain_length])) { // TRACEMSG(("DOMAIN failed host within domain test." // " Domain: %s, Host: %s", domain_from_header, cur_host)); PR_Free(domain_from_header); PR_Free(cur_path); PR_Free(cur_host); cookie_SetCookieStringInUse = PR_FALSE; cookie_Undefer(); return; } /* * check that portion of host not in domain does not contain a dot * This satisfies the fourth requirement in section 4.3.2 of the cookie * spec rfc 2109 (see www.cis.ohio-state.edu/htbin/rfc/rfc2109.html). * It prevents host of the form x.y.co.nz from setting cookies in the * entire .co.nz domain. Note that this doesn't really solve the problem, * it justs makes it more unlikely. Sites such as y.co.nz can still set * cookies for the entire .co.nz domain. */ /* * Although this is the right thing to do(tm), it breaks too many sites. * So only do it if the restrictCookieDomains pref is PR_TRUE. * */ nsresult rv; NS_WITH_SERVICE(nsIPref, prefs, "component://netscape/preferences", &rv); if (NS_FAILED(prefs->GetBoolPref(cookie_strictDomainsPref, &pref_scd))) { pref_scd = PR_FALSE; } if ( pref_scd == PR_TRUE ) { cur_host[cur_host_length-domain_length] = '\0'; dot = PL_strchr(cur_host, '.'); cur_host[cur_host_length-domain_length] = '.'; if (dot) { // TRACEMSG(("host minus domain failed no-dot test." // " Domain: %s, Host: %s", domain_from_header, cur_host)); PR_Free(domain_from_header); PR_Free(cur_path); PR_Free(cur_host); cookie_SetCookieStringInUse = PR_FALSE; cookie_Undefer(); return; } } /* all tests passed, copy in domain to hostname field */ StrAllocCopy(host_from_header, domain_from_header); isDomain = PR_TRUE; // TRACEMSG(("Accepted domain: %s", host_from_header)); PR_Free(domain_from_header); } /* now search for the expires header * NOTE: that this part of the parsing * destroys the original part of the string */ ptr = PL_strcasestr(semi_colon, "expires="); if(ptr) { char *date = ptr+8; /* terminate the string at the next semi-colon */ for(ptr=date; *ptr != '\0'; ptr++) { if(*ptr == ';') { *ptr = '\0'; break; } } if(timeToExpire) { expires = timeToExpire; } else { expires = cookie_ParseDate(date); } // TRACEMSG(("Have expires date: %ld", expires)); } } if(!path_from_header) { /* strip down everything after the last slash to get the path. */ char * slash = PL_strrchr(cur_path, '/'); if(slash) { *slash = '\0'; } path_from_header = cur_path; } else { PR_Free(cur_path); } if(!host_from_header) { host_from_header = cur_host; } else { PR_Free(cur_host); } /* keep cookies under the max bytes limit */ if(PL_strlen(setCookieHeader) > MAX_BYTES_PER_COOKIE) { setCookieHeader[MAX_BYTES_PER_COOKIE-1] = '\0'; } /* separate the name from the cookie */ equal = PL_strchr(setCookieHeader, '='); if(equal) { *equal = '\0'; StrAllocCopy(name_from_header, XP_StripLine(setCookieHeader)); StrAllocCopy(cookie_from_header, XP_StripLine(equal+1)); } else { // TRACEMSG(("Warning: no name found for cookie")); StrAllocCopy(cookie_from_header, XP_StripLine(setCookieHeader)); StrAllocCopy(name_from_header, ""); } cookie_PermissionStruct * cookie_permission; cookie_permission = cookie_CheckForPermission(host_from_header); if (cookie_permission != NULL) { if (cookie_permission->permission == PR_FALSE) { PR_FREEIF(path_from_header); PR_FREEIF(host_from_header); PR_FREEIF(name_from_header); PR_FREEIF(cookie_from_header); cookie_SetCookieStringInUse = PR_FALSE; cookie_Undefer(); return; } else { acceptIt = PR_TRUE; } } if((cookie_GetWarningPref()) && !acceptIt) { /* the user wants to know about cookies so let them know about every one that * is set and give them the choice to accept it or not */ PRUnichar * new_string=0; int count; PRUnichar * remember_string = cookie_Localize("RememberThisDecision"); /* find out how many cookies this host has already set */ count = cookie_Count(host_from_header); cookie_LockCookieList(); prev_cookie = cookie_CheckForPrevCookie (path_from_header, host_from_header, name_from_header); cookie_UnlockCookieList(); PRUnichar * message; if (prev_cookie) { message = cookie_Localize("PermissionToModifyCookie"); new_string = nsTextFormater::smprintf(message, host_from_header ? host_from_header : ""); } else if (count>1) { message = cookie_Localize("PermissionToSetAnotherCookie"); new_string = nsTextFormater::smprintf(message, host_from_header ? host_from_header : "", count); } else if (count==1){ message = cookie_Localize("PermissionToSetSecondCookie"); new_string = nsTextFormater::smprintf(message, host_from_header ? host_from_header : ""); } else { message = cookie_Localize("PermissionToSetACookie"); new_string = nsTextFormater::smprintf(message, host_from_header ? host_from_header : ""); } Recycle(message); /* * Who knows what thread we are on. Only the mozilla thread * is allowed to post dialogs so, if need be, go over there */ { PRBool rememberChecked = cookie_rememberChecked; PRBool userHasAccepted = cookie_CheckConfirmYN(new_string, remember_string, &cookie_rememberChecked); PR_FREEIF(new_string); Recycle(remember_string); if (cookie_rememberChecked) { cookie_PermissionStruct * cookie_permission2; cookie_permission2 = PR_NEW(cookie_PermissionStruct); if (cookie_permission2) { cookie_LockPermissionList(); StrAllocCopy(host_from_header2, host_from_header); /* ignore leading periods in host name */ while (host_from_header2 && (*host_from_header2 == '.')) { host_from_header2++; } cookie_permission2->host = host_from_header2; /* set host string */ cookie_permission2->permission = userHasAccepted; cookie_AddPermission(cookie_permission2, PR_TRUE); cookie_UnlockPermissionListst(); } } if (rememberChecked != cookie_rememberChecked) { cookie_permissionsChanged = PR_TRUE; cookie_SavePermissions(); } if (!userHasAccepted) { PR_FREEIF(path_from_header); PR_FREEIF(host_from_header); PR_FREEIF(name_from_header); PR_FREEIF(cookie_from_header); cookie_SetCookieStringInUse = PR_FALSE; return; } } } //TRACEMSG(("mkaccess.c: Setting cookie: %s for host: %s for path: %s", // cookie_from_header, host_from_header, path_from_header)); /* * We have decided we are going to insert a cookie into the list * Get the cookie lock so that we can munge on the list */ cookie_LockCookieList(); /* limit the number of cookies from a specific host or domain */ cookie_CheckForMaxCookiesFromHo(host_from_header); if (cookie_cookieList) { if(cookie_cookieList->Count() > MAX_NUMBER_OF_COOKIES-1) { cookie_RemoveOldestCookie(); } } prev_cookie = cookie_CheckForPrevCookie (path_from_header, host_from_header, name_from_header); if(prev_cookie) { prev_cookie->expires = expires; PR_FREEIF(prev_cookie->cookie); PR_FREEIF(prev_cookie->path); PR_FREEIF(prev_cookie->host); PR_FREEIF(prev_cookie->name); prev_cookie->cookie = cookie_from_header; prev_cookie->path = path_from_header; prev_cookie->host = host_from_header; prev_cookie->name = name_from_header; prev_cookie->xxx = xxx; prev_cookie->isDomain = isDomain; prev_cookie->lastAccessed = time(NULL); } else { cookie_CookieStruct * tmp_cookie_ptr; size_t new_len; /* construct a new cookie_struct */ prev_cookie = PR_NEW(cookie_CookieStruct); if(!prev_cookie) { PR_FREEIF(path_from_header); PR_FREEIF(host_from_header); PR_FREEIF(name_from_header); PR_FREEIF(cookie_from_header); cookie_UnlockCookieList(); cookie_SetCookieStringInUse = PR_FALSE; cookie_Undefer(); return; } /* copy */ prev_cookie->cookie = cookie_from_header; prev_cookie->name = name_from_header; prev_cookie->path = path_from_header; prev_cookie->host = host_from_header; prev_cookie->expires = expires; prev_cookie->xxx = xxx; prev_cookie->isDomain = isDomain; prev_cookie->lastAccessed = time(NULL); if(!cookie_cookieList) { cookie_cookieList = new nsVoidArray(); if(!cookie_cookieList) { PR_FREEIF(path_from_header); PR_FREEIF(name_from_header); PR_FREEIF(host_from_header); PR_FREEIF(cookie_from_header); PR_Free(prev_cookie); cookie_UnlockCookieList(); cookie_SetCookieStringInUse = PR_FALSE; cookie_Undefer(); return; } } /* add it to the list so that it is before any strings of smaller length */ bCookieAdded = PR_FALSE; new_len = PL_strlen(prev_cookie->path); PRInt32 count = cookie_cookieList->Count(); for (PRInt32 i = 0; i < count; ++i) { tmp_cookie_ptr = NS_STATIC_CAST(cookie_CookieStruct*, cookie_cookieList->ElementAt(i)); if (tmp_cookie_ptr) { if(new_len > PL_strlen(tmp_cookie_ptr->path)) { cookie_cookieList->InsertElementAt(prev_cookie, i); bCookieAdded = PR_TRUE; break; } } } if ( !bCookieAdded ) { /* no shorter strings found in list */ cookie_cookieList->AppendElement(prev_cookie); } } /* At this point we know a cookie has changed. Write the cookies to file. */ cookie_cookiesChanged = PR_TRUE; cookie_SaveCookies(); cookie_UnlockCookieList(); cookie_SetCookieStringInUse = PR_FALSE; cookie_Undefer(); return; } PUBLIC void COOKIE_SetCookieString(char * curURL, char * setCookieHeader) { cookie_SetCookieString(curURL, setCookieHeader, 0); } /* This function wrapper wraps COOKIE_SetCookieString for the purposes of * determining whether or not a cookie is inline (we need the URL struct, * and outputFormat to do so). this is called from NET_ParseMimeHeaders * in mkhttp.c * This routine does not need to worry about the cookie lock since all of * the work is handled by sub-routines */ PUBLIC void COOKIE_SetCookieStringFromHttp(char * curURL, char * firstURL, char * setCookieHeader, char * server_date) { /* allow for multiple cookies separated by newlines */ char *newline = PL_strchr(setCookieHeader, '\n'); if(newline) { *newline = '\0'; COOKIE_SetCookieStringFromHttp(curURL, firstURL, setCookieHeader, server_date); *newline = '\n'; COOKIE_SetCookieStringFromHttp(curURL, firstURL, newline+1, server_date); return; } /* If the outputFormat is not PRESENT (the url is not going to the screen), and not * SAVE AS (shift-click) then * the cookie being set is defined as inline so we need to do what the user wants us * to based on his preference to deal with foreign cookies. If it's not inline, just set * the cookie. */ char *ptr=NULL; time_t gmtCookieExpires=0, expires=0, sDate; /* check for foreign cookie if pref says to reject such */ if ((COOKIE_GetBehaviorPref() == COOKIE_DontAcceptForeign) && cookie_isForeign(curURL, firstURL)) { /* it's a foreign cookie so don't set the cookie */ return; } /* Determine when the cookie should expire. This is done by taking the difference between * the server time and the time the server wants the cookie to expire, and adding that * difference to the client time. This localizes the client time regardless of whether or * not the TZ environment variable was set on the client. */ /* Get the time the cookie is supposed to expire according to the attribute*/ ptr = PL_strcasestr(setCookieHeader, "expires="); if(ptr) { char *date = ptr+8; char origLast = '\0'; for(ptr=date; *ptr != '\0'; ptr++) { if(*ptr == ';') { origLast = ';'; *ptr = '\0'; break; } } expires = cookie_ParseDate(date); *ptr=origLast; } if (server_date) { sDate = cookie_ParseDate(server_date); } else { sDate = time(NULL); } if( sDate && expires ) { if( expires < sDate ) { gmtCookieExpires=1; } else { gmtCookieExpires = expires - sDate + time(NULL); // if overflow if( gmtCookieExpires < time(NULL)) { gmtCookieExpires = (((unsigned) (~0) << 1) >> 1); // max int } } } cookie_SetCookieString(curURL, setCookieHeader, gmtCookieExpires); } /* saves the HTTP cookies permissions to disk */ PRIVATE void cookie_SavePermissions() { cookie_PermissionStruct * cookie_permission_s; if (COOKIE_GetBehaviorPref() == COOKIE_DontUse) { return; } if (!cookie_permissionsChanged) { return; } cookie_LockPermissionList(); if (cookie_permissionList == nsnull) { cookie_UnlockPermissionListst(); return; } nsFileSpec dirSpec; nsresult rval = cookie_ProfileDirectory(dirSpec); if (NS_FAILED(rval)) { cookie_UnlockPermissionListst(); return; } nsOutputFileStream strm(dirSpec + "cookperm.txt"); if (!strm.is_open()) { cookie_UnlockPermissionListst(); return; } cookie_Put(strm, "# Netscape HTTP Cookie Permission File\n"); cookie_Put(strm, "# http://www.netscape.com/newsref/std/cookie_spec.html\n"); cookie_Put(strm, "# This is a generated file! Do not edit.\n\n"); /* format shall be: * host \t permission */ PRInt32 count = cookie_permissionList->Count(); for (PRInt32 i = 0; i < count; ++i) { cookie_permission_s = NS_STATIC_CAST(cookie_PermissionStruct*, cookie_permissionList->ElementAt(i)); if (cookie_permission_s) { cookie_Put(strm, cookie_permission_s->host); if (cookie_permission_s->permission) { cookie_Put(strm, "\tTRUE\n"); } else { cookie_Put(strm, "\tFALSE\n"); } } } /* save current state of cookie nag-box's checkmark */ if (cookie_rememberChecked) { cookie_Put(strm, "@@@@\tTRUE\n"); } else { cookie_Put(strm, "@@@@\tFALSE\n"); } cookie_permissionsChanged = PR_FALSE; strm.flush(); strm.close(); cookie_UnlockPermissionListst(); } /* reads the HTTP cookies permission from disk */ PRIVATE void cookie_LoadPermissions() { nsAutoString buffer; cookie_PermissionStruct * new_cookie_permission; nsFileSpec dirSpec; nsresult rv = cookie_ProfileDirectory(dirSpec); if (NS_FAILED(rv)) { return; } nsInputFileStream strm(dirSpec + "cookperm.txt"); if (!strm.is_open()) { /* file doesn't exist -- that's not an error */ return; } /* format is: * host \t permission * if this format isn't respected we move onto the next line in the file. */ cookie_LockPermissionList(); while(cookie_GetLine(strm,buffer) != -1) { if (buffer.CharAt(0) == '#' || buffer.CharAt(0) == CR || buffer.CharAt(0) == LF || buffer.CharAt(0) == 0) { continue; } int hostIndex, permissionIndex; hostIndex = 0; if ((permissionIndex=buffer.FindChar('\t', PR_FALSE,hostIndex)+1) == 0) { continue; } /* ignore leading periods in host name */ while (hostIndex < permissionIndex && (buffer.CharAt(hostIndex) == '.')) { hostIndex++; } nsString host, permission; buffer.Mid(host, hostIndex, permissionIndex-hostIndex-1); buffer.Mid(permission, permissionIndex, buffer.Length()-permissionIndex); /* create a new cookie_struct and fill it in */ new_cookie_permission = PR_NEW(cookie_PermissionStruct); if (!new_cookie_permission) { cookie_UnlockPermissionListst(); strm.close(); return; } new_cookie_permission->host = host.ToNewCString(); if (permission == "TRUE") { new_cookie_permission->permission = PR_TRUE; } else { new_cookie_permission->permission = PR_FALSE; } /* * a host value of "@@@@" is a special code designating the * state of the cookie nag-box's checkmark */ if (host == "@@@@") { cookie_rememberChecked = new_cookie_permission->permission; } else { /* add the permission entry */ cookie_AddPermission(new_cookie_permission, PR_FALSE ); } } strm.close(); cookie_UnlockPermissionListst(); cookie_permissionsChanged = PR_FALSE; return; } /* saves out the HTTP cookies to disk */ PRIVATE void cookie_SaveCookies() { cookie_CookieStruct * cookie_s; time_t cur_date = time(NULL); char date_string[36]; if (COOKIE_GetBehaviorPref() == COOKIE_DontUse) { return; } if (!cookie_cookiesChanged) { return; } cookie_LockCookieList(); if (cookie_cookieList == nsnull) { cookie_UnlockCookieList(); return; } nsFileSpec dirSpec; nsresult rv = cookie_ProfileDirectory(dirSpec); if (NS_FAILED(rv)) { cookie_UnlockCookieList(); return; } nsOutputFileStream strm(dirSpec + "cookies.txt"); if (!strm.is_open()) { /* file doesn't exist -- that's not an error */ cookie_UnlockCookieList(); return; } cookie_Put(strm, "# Netscape HTTP Cookie File\n"); cookie_Put(strm, "# http://www.netscape.com/newsref/std/cookie_spec.html\n"); cookie_Put(strm, "# This is a generated file! Do not edit.\n\n"); /* format shall be: * * host \t isDomain \t path \t xxx \t expires \t name \t cookie * * isDomain is PR_TRUE or PR_FALSE * xxx is PR_TRUE or PR_FALSE * expires is a time_t integer * cookie can have tabs */ PRInt32 count = cookie_cookieList->Count(); for (PRInt32 i = 0; i < count; ++i) { cookie_s = NS_STATIC_CAST(cookie_CookieStruct*, cookie_cookieList->ElementAt(i)); if (cookie_s) { if (cookie_s->expires < cur_date) { /* don't write entry if cookie has expired or has no expiration date */ continue; } cookie_Put(strm, cookie_s->host); if (cookie_s->isDomain) { cookie_Put(strm, "\tTRUE\t"); } else { cookie_Put(strm, "\tFALSE\t"); } cookie_Put(strm, cookie_s->path); if (cookie_s->xxx) { cookie_Put(strm, "\tTRUE\t"); } else { cookie_Put(strm, "\tFALSE\t"); } PR_snprintf(date_string, sizeof(date_string), "%lu", cookie_s->expires); cookie_Put(strm, date_string); cookie_Put(strm, "\t"); cookie_Put(strm, cookie_s->name); cookie_Put(strm, "\t"); cookie_Put(strm, cookie_s->cookie); cookie_Put(strm, "\n"); } } cookie_cookiesChanged = PR_FALSE; strm.flush(); strm.close(); cookie_UnlockCookieList(); } /* reads HTTP cookies from disk */ PRIVATE void cookie_LoadCookies() { cookie_CookieStruct *new_cookie, *tmp_cookie_ptr; size_t new_len; nsAutoString buffer; PRBool added_to_list; nsFileSpec dirSpec; nsresult rv = cookie_ProfileDirectory(dirSpec); if (NS_FAILED(rv)) { return; } nsInputFileStream strm(dirSpec + "cookies.txt"); if (!strm.is_open()) { /* file doesn't exist -- that's not an error */ return; } cookie_LockCookieList(); /* format is: * * host \t isDomain \t path \t xxx \t expires \t name \t cookie * * if this format isn't respected we move onto the next line in the file. * isDomain is PR_TRUE or PR_FALSE -- defaulting to PR_FALSE * xxx is PR_TRUE or PR_FALSE -- should default to PR_TRUE * expires is a time_t integer * cookie can have tabs */ while (cookie_GetLine(strm,buffer) != -1){ added_to_list = PR_FALSE; if (buffer.CharAt(0) == '#' || buffer.CharAt(0) == CR || buffer.CharAt(0) == LF || buffer.CharAt(0) == 0) { continue; } int hostIndex, isDomainIndex, pathIndex, xxxIndex, expiresIndex, nameIndex, cookieIndex; hostIndex = 0; if ((isDomainIndex=buffer.FindChar('\t', PR_FALSE,hostIndex)+1) == 0 || (pathIndex=buffer.FindChar('\t', PR_FALSE,isDomainIndex)+1) == 0 || (xxxIndex=buffer.FindChar('\t', PR_FALSE,pathIndex)+1) == 0 || (expiresIndex=buffer.FindChar('\t', PR_FALSE,xxxIndex)+1) == 0 || (nameIndex=buffer.FindChar('\t', PR_FALSE,expiresIndex)+1) == 0 || (cookieIndex=buffer.FindChar('\t', PR_FALSE,nameIndex)+1) == 0 ) { continue; } nsAutoString host, isDomain, path, xxx, expires, name, cookie; buffer.Mid(host, hostIndex, isDomainIndex-hostIndex-1); buffer.Mid(isDomain, isDomainIndex, pathIndex-isDomainIndex-1); buffer.Mid(path, pathIndex, xxxIndex-pathIndex-1); buffer.Mid(xxx, xxxIndex, expiresIndex-xxxIndex-1); buffer.Mid(expires, expiresIndex, nameIndex-expiresIndex-1); buffer.Mid(name, nameIndex, cookieIndex-nameIndex-1); buffer.Mid(cookie, cookieIndex, buffer.Length()-cookieIndex); /* create a new cookie_struct and fill it in */ new_cookie = PR_NEW(cookie_CookieStruct); if (!new_cookie) { cookie_UnlockCookieList(); strm.close(); return; } memset(new_cookie, 0, sizeof(cookie_CookieStruct)); new_cookie->name = name.ToNewCString(); new_cookie->cookie = cookie.ToNewCString(); new_cookie->host = host.ToNewCString(); new_cookie->path = path.ToNewCString(); if (isDomain == "TRUE") { new_cookie->isDomain = PR_TRUE; } else { new_cookie->isDomain = PR_FALSE; } if (xxx == "TRUE") { new_cookie->xxx = PR_TRUE; } else { new_cookie->xxx = PR_FALSE; } char * expiresCString = expires.ToNewCString(); new_cookie->expires = strtoul(expiresCString, nsnull, 10); nsCRT::free(expiresCString); /* start new cookie list if one does not already exist */ if (!cookie_cookieList) { cookie_cookieList = new nsVoidArray(); if (!cookie_cookieList) { cookie_UnlockCookieList(); strm.close(); return; } } /* add new cookie to the list so that it is before any strings of smaller length */ new_len = PL_strlen(new_cookie->path); PRInt32 count = cookie_cookieList->Count(); for (PRInt32 i = 0; i < count; ++i) { tmp_cookie_ptr = NS_STATIC_CAST(cookie_CookieStruct*, cookie_cookieList->ElementAt(i)); if (tmp_cookie_ptr) { if (new_len > PL_strlen(tmp_cookie_ptr->path)) { cookie_cookieList->InsertElementAt(new_cookie, i); added_to_list = PR_TRUE; break; } } } /* no shorter strings found in list so add new cookie at end */ if (!added_to_list) { cookie_cookieList->AppendElement(new_cookie); } } strm.close(); cookie_UnlockCookieList(); cookie_cookiesChanged = PR_FALSE; return; } PUBLIC int COOKIE_ReadCookies() { static PRBool sReadCookies = PR_FALSE; if (sReadCookies) NS_WARNING("We are reading the cookies more than once. Probably bad"); cookie_LoadCookies(); cookie_LoadPermissions(); sReadCookies = PR_TRUE; return 0; } PRIVATE PRBool CookieCompare (cookie_CookieStruct * cookie1, cookie_CookieStruct * cookie2) { char * host1 = cookie1->host; char * host2 = cookie2->host; /* get rid of leading period on host name, if any */ if (*host1 == '.') { host1++; } if (*host2 == '.') { host2++; } /* make decision based on host name if they are unequal */ if (PL_strcmp (host1, host2) < 0) { return -1; } if (PL_strcmp (host1, host2) > 0) { return 1; } /* if host names are equal, make decision based on cookie name */ return (PL_strcmp (cookie1->name, cookie2->name)); } /* * find the next cookie that is alphabetically after the specified cookie * ordering is based on hostname and the cookie name * if specified cookie is NULL, find the first cookie alphabetically */ PRIVATE cookie_CookieStruct * NextCookieAfter(cookie_CookieStruct * cookie, int * cookieNum) { cookie_CookieStruct *cookie_ptr; cookie_CookieStruct *lowestCookie = NULL; int localCookieNum = 0; int lowestCookieNum; if (cookie_cookieList == nsnull) return NULL; PRInt32 count = cookie_cookieList->Count(); for (PRInt32 i = 0; i < count; ++i) { cookie_ptr = NS_STATIC_CAST(cookie_CookieStruct*, cookie_cookieList->ElementAt(i)); if (cookie_ptr) { if (!cookie || (CookieCompare(cookie_ptr, cookie) > 0)) { if (!lowestCookie || (CookieCompare(cookie_ptr, lowestCookie) < 0)) { lowestCookie = cookie_ptr; lowestCookieNum = localCookieNum; } } localCookieNum++; } } *cookieNum = lowestCookieNum; return lowestCookie; } /* * return a string that has each " of the argument sting * replaced with \" so it can be used inside a quoted string */ PRIVATE char* cookie_FixQuoted(char* s) { char * result; int count = PL_strlen(s); char *quote = s; unsigned int i, j; while ((quote = PL_strchr(quote, '"'))) { count = count++; quote++; } result = (char*)PR_Malloc(count + 1); for (i=0, j=0; i= 0, "bad data"); if (start < 0) { return nsAutoString("").ToNewCString(); } start += PL_strlen(name); /* get passed the |name| part */ length = results.FindChar('|', PR_FALSE,start) - start; results.Mid(value, start, length); return value.ToNewCString(); } PUBLIC void COOKIE_CookieViewerReturn(nsAutoString results) { cookie_CookieStruct * cookie; cookie_PermissionStruct * permission; PRInt32 count = 0; /* step through all cookies and delete those that are in the sequence */ char * gone = cookie_FindValueInArgs(results, "|goneC|"); cookie_LockCookieList(); if (cookie_cookieList) { count = cookie_cookieList->Count(); while (count>0) { count--; cookie = NS_STATIC_CAST(cookie_CookieStruct*, cookie_cookieList->ElementAt(count)); if (cookie && cookie_InSequence(gone, count)) { cookie_FreeCookie(cookie); cookie_cookiesChanged = PR_TRUE; } } } cookie_SaveCookies(); cookie_UnlockCookieList(); nsCRT::free(gone); /* step through all permissions and delete those that are in the sequence */ gone = cookie_FindValueInArgs(results, "|goneP|"); cookie_LockPermissionList(); if (cookie_permissionList) { count = cookie_permissionList->Count(); while (count>0) { count--; permission = NS_STATIC_CAST(cookie_PermissionStruct*, cookie_permissionList->ElementAt(count)); if (permission && cookie_InSequence(gone, count)) { cookie_FreePermission(permission, PR_FALSE); cookie_permissionsChanged = PR_TRUE; } } } cookie_SavePermissions(); cookie_UnlockPermissionListst(); nsCRT::free(gone); } #define BUFLEN2 5000 #define BREAK '\001' PUBLIC void COOKIE_GetCookieListForViewer(nsString& aCookieList) { PRUnichar *buffer = (PRUnichar*)PR_Malloc(2*BUFLEN2); int g, cookieNum; cookie_CookieStruct * cookie; cookie_LockCookieList(); /* Get rid of any expired cookies now so user doesn't * think/see that we're keeping cookies in memory. */ cookie_RemoveExpiredCookies(); /* generate the list of cookies in alphabetical order */ cookie = NULL; while ((cookie = NextCookieAfter(cookie, &cookieNum))) { char *fixed_name = cookie_FixQuoted(cookie->name); char *fixed_value = cookie_FixQuoted(cookie->cookie); char *fixed_domain_or_host = cookie_FixQuoted(cookie->host); char *fixed_path = cookie_FixQuoted(cookie->path); PRUnichar * Domain = cookie_Localize("Domain"); PRUnichar * Host = cookie_Localize("Host"); PRUnichar * Yes = cookie_Localize("Yes"); PRUnichar * No = cookie_Localize("No"); PRUnichar * AtEnd = cookie_Localize("AtEndOfSession"); buffer[0] = '\0'; g = 0; g += nsTextFormater::snprintf(buffer+g, BUFLEN2-g, nsString("%c%d%c%s%c%s%c%S%c%s%c%s%c%S%c%S").GetUnicode(), BREAK, cookieNum, BREAK, fixed_name, BREAK, fixed_value, BREAK, cookie->isDomain ? Domain : Host, BREAK, fixed_domain_or_host, BREAK, fixed_path, BREAK, cookie->xxx ? Yes : No, BREAK, cookie->expires ? (nsString(ctime(&(cookie->expires))).GetUnicode()) : AtEnd ); PR_FREEIF(fixed_name); PR_FREEIF(fixed_value); PR_FREEIF(fixed_domain_or_host); PR_FREEIF(fixed_path); Recycle(Domain); Recycle(Host); Recycle(Yes); Recycle(No); Recycle(AtEnd); aCookieList += buffer; } PR_FREEIF(buffer); cookie_UnlockCookieList(); } PUBLIC void COOKIE_GetPermissionListForViewer(nsString& aPermissionList) { char *buffer = (char*)PR_Malloc(BUFLEN2); int g = 0, permissionNum; cookie_PermissionStruct * permission; cookie_LockPermissionList(); buffer[0] = '\0'; permissionNum = 0; if (cookie_permissionList == nsnull) { cookie_UnlockPermissionListst(); return; } PRInt32 count = cookie_permissionList->Count(); for (PRInt32 i = 0; i < count; ++i) { permission = NS_STATIC_CAST(cookie_PermissionStruct*, cookie_permissionList->ElementAt(i)); if (permission) { g += PR_snprintf(buffer+g, BUFLEN2-g, "%c%d%c%c%s", BREAK, permissionNum, BREAK, permission->permission ? '+' : '-', permission->host ); permissionNum++; } } aPermissionList = buffer; PR_FREEIF(buffer); cookie_UnlockPermissionListst(); } /* Hack - Neeti remove this */ /* remove front and back white space * modifies the original string */ PUBLIC char * XP_StripLine (char *string) { char * ptr; /* remove leading blanks */ while(*string=='\t' || *string==' ' || *string=='\r' || *string=='\n') { string++; } for(ptr=string; *ptr; ptr++) { ; /* NULL BODY; Find end of string */ } /* remove trailing blanks */ for(ptr--; ptr >= string; ptr--) { if(*ptr=='\t' || *ptr==' ' || *ptr=='\r' || *ptr=='\n') { *ptr = '\0'; } else { break; } } return string; } /* Very similar to strdup except it free's too */ PUBLIC char * NET_SACopy (char **destination, const char *source) { if(*destination) { PR_Free(*destination); *destination = 0; } if (! source) { *destination = NULL; } else { *destination = (char *) PR_Malloc (PL_strlen(source) + 1); if (*destination == NULL) { return(NULL); } PL_strcpy (*destination, source); } return *destination; } /* Again like strdup but it concatinates and free's and uses Realloc */ PUBLIC char * NET_SACat (char **destination, const char *source) { if (source && *source) { if (*destination) { int length = PL_strlen (*destination); *destination = (char *) PR_Realloc (*destination, length + PL_strlen(source) + 1); if (*destination == NULL) { return(NULL); } PL_strcpy (*destination + length, source); } else { *destination = (char *) PR_Malloc (PL_strlen(source) + 1); if (*destination == NULL) { return(NULL); } PL_strcpy (*destination, source); } } return *destination; } MODULE_PRIVATE time_t cookie_ParseDate(char *date_string) { #ifndef USE_OLD_TIME_FUNC PRTime prdate; time_t date = 0; // TRACEMSG(("Parsing date string: %s\n",date_string)); /* try using PR_ParseTimeString instead */ if(PR_ParseTimeString(date_string, PR_TRUE, &prdate) == PR_SUCCESS) { PRInt64 r, u; LL_I2L(u, PR_USEC_PER_SEC); LL_DIV(r, prdate, u); LL_L2I(date, r); if (date < 0) { date = 0; } // TRACEMSG(("Parsed date as GMT: %s\n", asctime(gmtime(&date)))); // TRACEMSG(("Parsed date as local: %s\n", ctime(&date))); } else { // TRACEMSG(("Could not parse date")); } return (date); #else struct tm time_info; /* Points to static tm structure */ char *ip; char mname[256]; time_t rv; // TRACEMSG(("Parsing date string: %s\n",date_string)); memset(&time_info, 0, sizeof(struct tm)); /* Whatever format we're looking at, it will start with weekday. */ /* Skip to first space. */ if(!(ip = PL_strchr(date_string,' '))) { return 0; } else { while(COOKIE_IS_SPACE(*ip)) { ++ip; } } /* make sure that the date is less than 256. That will keep name from ever overflowing */ if(255 < PL_strlen(ip)) { return(0); } if(isalpha(*ip)) { /* ctime */ sscanf(ip, (strstr(ip, "DST") ? "%s %d %d:%d:%d %*s %d" : "%s %d %d:%d:%d %d"), mname, &time_info.tm_mday, &time_info.tm_hour, &time_info.tm_min, &time_info.tm_sec, &time_info.tm_year); time_info.tm_year -= 1900; } else if(ip[2] == '-') { /* RFC 850 (normal HTTP) */ char t[256]; sscanf(ip,"%s %d:%d:%d", t, &time_info.tm_hour, &time_info.tm_min, &time_info.tm_sec); t[2] = '\0'; time_info.tm_mday = atoi(t); t[6] = '\0'; PL_strcpy(mname,&t[3]); time_info.tm_year = atoi(&t[7]); /* Prevent wraparound from ambiguity */ if(time_info.tm_year < 70) { time_info.tm_year += 100; } else if(time_info.tm_year > 1900) { time_info.tm_year -= 1900; } } else { /* RFC 822 */ sscanf(ip,"%d %s %d %d:%d:%d",&time_info.tm_mday, mname, &time_info.tm_year, &time_info.tm_hour, &time_info.tm_min, &time_info.tm_sec); /* since tm_year is years since 1900 and the year we parsed * is absolute, we need to subtract 1900 years from it */ time_info.tm_year -= 1900; } time_info.tm_mon = NET_MonthNo(mname); if(time_info.tm_mon == -1) { /* check for error */ return(0); } // TRACEMSG(("Parsed date as: %s\n", asctime(&time_info))); rv = mktime(&time_info); if(time_info.tm_isdst) { rv -= 3600; } if(rv == -1) { // TRACEMSG(("mktime was unable to resolve date/time: %s\n", asctime(&time_info))); return(0); } else { // TRACEMSG(("Parsed date %s\n", asctime(&time_info))); // TRACEMSG(("\tas %s\n", ctime(&rv))); return(rv); } #endif } /* Also skip '>' as part of host name */ MODULE_PRIVATE char * cookie_ParseURL (const char *url, int parts_requested) { char *rv=0,*colon, *slash, *ques_mark, *hash_mark; char *atSign, *host, *passwordColon, *gtThan; assert(url); if(!url) { return(StrAllocCat(rv, "")); } colon = PL_strchr(url, ':'); /* returns a const char */ /* Get the protocol part, not including anything beyond the colon */ if (parts_requested & GET_PROTOCOL_PART) { if(colon) { char val = *(colon+1); *(colon+1) = '\0'; StrAllocCopy(rv, url); *(colon+1) = val; /* If the user wants more url info, tack on extra slashes. */ if( (parts_requested & GET_HOST_PART) || (parts_requested & GET_USERNAME_PART) || (parts_requested & GET_PASSWORD_PART)) { if( *(colon+1) == '/' && *(colon+2) == '/') { StrAllocCat(rv, "//"); } /* If there's a third slash consider it file:/// and tack on the last slash. */ if( *(colon+3) == '/' ) { StrAllocCat(rv, "/"); } } } } /* Get the username if one exists */ if (parts_requested & GET_USERNAME_PART) { if (colon && (*(colon+1) == '/') && (*(colon+2) == '/') && (*(colon+3) != '\0')) { if ( (slash = PL_strchr(colon+3, '/')) != NULL) { *slash = '\0'; } if ( (atSign = PL_strchr(colon+3, '@')) != NULL) { *atSign = '\0'; if ( (passwordColon = PL_strchr(colon+3, ':')) != NULL) { *passwordColon = '\0'; } StrAllocCat(rv, colon+3); /* Get the password if one exists */ if (parts_requested & GET_PASSWORD_PART) { if (passwordColon) { StrAllocCat(rv, ":"); StrAllocCat(rv, passwordColon+1); } } if (parts_requested & GET_HOST_PART) { StrAllocCat(rv, "@"); } if (passwordColon) { *passwordColon = ':'; } *atSign = '@'; } if (slash) { *slash = '/'; } } } /* Get the host part */ if (parts_requested & GET_HOST_PART) { if(colon) { if(*(colon+1) == '/' && *(colon+2) == '/') { slash = PL_strchr(colon+3, '/'); if(slash) { *slash = '\0'; } if( (atSign = PL_strchr(colon+3, '@')) != NULL) { host = atSign+1; } else { host = colon+3; } ques_mark = PL_strchr(host, '?'); if(ques_mark) { *ques_mark = '\0'; } gtThan = PL_strchr(host, '>'); if (gtThan) { *gtThan = '\0'; } /* * Protect systems whose header files forgot to let the client know when * gethostbyname would trash their stack. */ #ifndef MAX_HOST_NAME_LEN #ifdef XP_OS2 #error Managed to get into cookie_ParseURL without defining MAX_HOST_NAME_LEN !!! #endif #endif /* limit hostnames to within MAX_HOST_NAME_LEN characters to keep from crashing */ if(PL_strlen(host) > MAX_HOST_NAME_LEN) { char * cp; char old_char; cp = host+MAX_HOST_NAME_LEN; old_char = *cp; *cp = '\0'; StrAllocCat(rv, host); *cp = old_char; } else { StrAllocCat(rv, host); } if(slash) { *slash = '/'; } if(ques_mark) { *ques_mark = '?'; } if (gtThan) { *gtThan = '>'; } } } } /* Get the path part */ if (parts_requested & GET_PATH_PART) { if(colon) { if(*(colon+1) == '/' && *(colon+2) == '/') { /* skip host part */ slash = PL_strchr(colon+3, '/'); } else { /* path is right after the colon */ slash = colon+1; } if(slash) { ques_mark = PL_strchr(slash, '?'); hash_mark = PL_strchr(slash, '#'); if(ques_mark) { *ques_mark = '\0'; } if(hash_mark) { *hash_mark = '\0'; } StrAllocCat(rv, slash); if(ques_mark) { *ques_mark = '?'; } if(hash_mark) { *hash_mark = '#'; } } } } if(parts_requested & GET_HASH_PART) { hash_mark = PL_strchr(url, '#'); /* returns a const char * */ if(hash_mark) { ques_mark = PL_strchr(hash_mark, '?'); if(ques_mark) { *ques_mark = '\0'; } StrAllocCat(rv, hash_mark); if(ques_mark) { *ques_mark = '?'; } } } if(parts_requested & GET_SEARCH_PART) { ques_mark = PL_strchr(url, '?'); /* returns a const char * */ if(ques_mark) { hash_mark = PL_strchr(ques_mark, '#'); if(hash_mark) { *hash_mark = '\0'; } StrAllocCat(rv, ques_mark); if(hash_mark) { *hash_mark = '#'; } } } /* copy in a null string if nothing was copied in */ if(!rv) { StrAllocCopy(rv, ""); } /* XP_OS2_FIX IBM-MAS: long URLS blowout tracemsg buffer, set max to 1900 chars */ // TRACEMSG(("mkparse: ParseURL: parsed - %-.1900s",rv)); return rv; }