gecko-dev/extensions/cookie/nsCookie.cpp

2896 lines
84 KiB
C++

/* -*- 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):
* Pierre Phaneuf <pp@ludusdesign.com>
* Henrik Gemal <gemal@gemal.dk>
*/
#define alphabetize 1
#include "nsCookie.h"
#include "nsString.h"
#include "nsIServiceManager.h"
#include "nsFileStream.h"
#include "nsIDirectoryService.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsIFile.h"
#include "nsXPIDLString.h"
#include "nsIFileSpec.h"
#include "nsINetSupportDialogService.h"
#include "nsIURL.h"
#include "nsIStringBundle.h"
#include "nsVoidArray.h"
#include "prprf.h"
#include "xp_core.h" // for time_t
#include "nsXPIDLString.h"
#include "nsIPref.h"
#include "nsTextFormatter.h"
static NS_DEFINE_CID(kNetSupportDialogCID, NS_NETSUPPORTDIALOG_CID);
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 image_behaviorPref "network.image.imageBehavior"
#define image_warningPref "network.image.warnAboutImages"
#define cookie_behaviorPref "network.cookie.cookieBehavior"
#define cookie_warningPref "network.cookie.warnAboutCookies"
#define cookie_strictDomainsPref "network.cookie.strictDomains"
#define cookie_lifetimePref "network.cookie.lifetimeOption"
#define cookie_lifetimeValue "network.cookie.lifetimeLimit"
#define cookie_localization "chrome://communicator/locale/wallet/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);
typedef struct _cookie_CookieStruct {
char * path;
char * host;
char * name;
char * cookie;
time_t expires;
time_t lastAccessed;
PRBool secure;
PRBool isDomain; /* is it a domain instead of an absolute host? */
} cookie_CookieStruct;
#define COOKIEPERMISSION 0
#define IMAGEPERMISSION 1
#define NUMBER_OF_PERMISSIONS 2
typedef struct _permission_HostStruct {
char * host;
nsVoidArray * permissionList;
} permission_HostStruct;
typedef struct _permission_TypeStruct {
PRInt32 type;
PRBool permission;
} permission_TypeStruct;
typedef enum {
COOKIE_Normal,
COOKIE_Discard,
COOKIE_Trim,
COOKIE_Ask
} COOKIE_LifetimeEnum;
PRIVATE PRBool cookie_cookiesChanged = PR_FALSE;
PRIVATE PRBool cookie_permissionsChanged = PR_FALSE;
PRIVATE PRBool cookie_rememberChecked;
PRIVATE PRBool image_rememberChecked;
PRIVATE COOKIE_BehaviorEnum cookie_behavior = COOKIE_Accept;
PRIVATE PRBool cookie_warning = PR_FALSE;
PRIVATE COOKIE_BehaviorEnum image_behavior = COOKIE_Accept;
PRIVATE PRBool image_warning = PR_FALSE;
PRIVATE COOKIE_LifetimeEnum cookie_lifetimeOpt = COOKIE_Normal;
PRIVATE time_t cookie_lifetimeLimit = 90*24*60*60;
PRIVATE nsVoidArray * cookie_cookieList=0;
PRIVATE nsVoidArray * cookie_permissionList=0;
#define REAL_DIALOG 1
static
time_t
get_current_time()
/*
We call this routine instead of |time()| because the latter returns
different values on the Mac than on all other platforms (i.e., based on
the Mac's 1900 epoch, vs all others 1970). We can't call |PR_Now|
directly, since the value is 64bits and too hard to manipulate.
Hence, this cross-platform convenience routine.
*/
{
PRInt64 usecPerSec;
LL_I2L(usecPerSec, 1000000L);
PRTime now_useconds = PR_Now();
PRInt64 now_seconds;
LL_DIV(now_seconds, now_useconds, usecPerSec);
time_t current_time_in_seconds;
LL_L2I(current_time_in_seconds, now_seconds);
return current_time_in_seconds;
}
/* 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 bundle for the localization */
NS_WITH_SERVICE(nsIStringBundleService, pStringService, kStringBundleServiceCID, &ret);
if (NS_FAILED(ret)) {
#ifdef DEBUG
printf("cannot get string service\n");
#endif
return v.ToNewUnicode();
}
nsCOMPtr<nsILocale> locale;
nsCOMPtr<nsIStringBundle> bundle;
ret = pStringService->CreateBundle(cookie_localization, locale, getter_AddRefs(bundle));
if (NS_FAILED(ret)) {
#ifdef DEBUG
printf("cannot create instance\n");
#endif
return v.ToNewUnicode();
}
/* localize the given string */
nsString strtmp; strtmp.AssignWithConversion(genericString);
const PRUnichar *ptrtmp = strtmp.GetUnicode();
nsXPIDLString ptrv;
ret = bundle->GetStringFromName(ptrtmp, getter_Copies(ptrv));
v = ptrv;
if (NS_FAILED(ret)) {
#ifdef DEBUG
printf("cannot get string from name\n");
#endif
return v.ToNewUnicode();
}
return v.ToNewUnicode();
}
PRBool
cookie_CheckConfirmYN(nsIPrompt *aPrompter, PRUnichar * szMessage, PRUnichar * szCheckMessage, PRBool* checkValue) {
nsresult res;
nsCOMPtr<nsIPrompt> dialog;
if (aPrompter)
dialog = aPrompter;
else
dialog = do_GetService(kNetSupportDialogCID);
if (!dialog) {
*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");
nsString tempStr; tempStr.AssignWithConversion("chrome://global/skin/question-icon.gif");
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 */
tempStr.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) {
nsresult res;
nsCOMPtr<nsIFile> aFile;
nsCOMPtr<nsIFileSpec> tempSpec;
res = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(aFile));
if (NS_FAILED(res)) return res;
// TODO: When the calling code can take an nsIFile,
// this conversion to nsFileSpec can be avoided.
nsXPIDLCString pathBuf;
aFile->GetPath(getter_Copies(pathBuf));
res = NS_NewFileSpec(getter_AddRefs(tempSpec));
if (NS_FAILED(res)) return res;
res = tempSpec->SetNativePath(pathBuf);
if (NS_FAILED(res)) return res;
res = tempSpec->GetFileSpec(&dirSpec);
return res;
}
nsresult cookie_Get(nsInputFileStream strm, char& c) {
#define BUFSIZE 128
static char buffer[BUFSIZE];
static PRInt32 next = BUFSIZE, count = BUFSIZE;
if (next == count) {
if (BUFSIZE > count) { // never say "count < ..." vc6.0 thinks this is a template beginning and crashes
next = BUFSIZE;
count = BUFSIZE;
return NS_ERROR_FAILURE;
}
count = strm.read(buffer, BUFSIZE);
next = 0;
if (count == 0) {
next = BUFSIZE;
count = BUFSIZE;
return NS_ERROR_FAILURE;
}
}
c = buffer[next++];
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 (;;) {
if NS_FAILED(cookie_Get(strm, c)) {
return -1;
}
if (c == '\n') {
break;
}
if (c != '\r') {
aLine.AppendWithConversion(c);
}
}
return 0;
}
PRIVATE void
permission_Save();
PRIVATE void
cookie_Save();
PRIVATE void
permission_Free(PRInt32 hostNumber, PRInt32 type, PRBool save) {
/* get the indicated host in the list */
if (cookie_permissionList == nsnull) {
return;
}
permission_HostStruct * hostStruct;
permission_TypeStruct * typeStruct;
PRInt32 actualHostNumber = -1;
PRInt32 typeIndex;
PRInt32 count = cookie_permissionList->Count();
for (PRInt32 j=0, k=0; j<count; j++) {
hostStruct = NS_STATIC_CAST
(permission_HostStruct*, cookie_permissionList->ElementAt(j));
if (!hostStruct) {
return;
}
PRBool present = PR_FALSE;
PRInt32 count2 = hostStruct->permissionList->Count();
for (typeIndex=0; typeIndex<count2; typeIndex++) {
typeStruct = NS_STATIC_CAST
(permission_TypeStruct*, hostStruct->permissionList->ElementAt(typeIndex));
if (typeStruct->type == type) {
present = PR_TRUE;
break;
}
}
if (present) {
if (k == hostNumber) {
actualHostNumber = j;
break;
}
k++;
}
}
if (actualHostNumber == -1) {
/* host not found, something is wrong */
return;
}
/* remove the particular type */
hostStruct->permissionList->RemoveElementAt(typeIndex);
/* if no types are present, remove the entry */
PRInt32 count2 = hostStruct->permissionList->Count();
if (count2 == 0) {
PR_FREEIF(hostStruct->permissionList);
cookie_permissionList->RemoveElementAt(actualHostNumber);
PR_FREEIF(hostStruct->host);
PR_Free(hostStruct);
}
/* write the changes out to disk */
if (save) {
cookie_permissionsChanged = PR_TRUE;
permission_Save();
}
}
/* blows away all permissions currently in the list */
PRIVATE void
cookie_RemoveAllPermissions() {
permission_HostStruct * hostStruct;
permission_TypeStruct * typeStruct;
/* check for NULL or empty list */
if (cookie_permissionList == nsnull) {
return;
}
PRInt32 count = cookie_permissionList->Count();
for (PRInt32 i = count-1; i >=0; i--) {
hostStruct = NS_STATIC_CAST
(permission_HostStruct*, cookie_permissionList->ElementAt(i));
if (!hostStruct) {
return;
}
PRInt32 count2 = hostStruct->permissionList->Count();
for (PRInt32 typeIndex = count2-1; typeIndex >=0; typeIndex--) {
typeStruct = NS_STATIC_CAST
(permission_TypeStruct*, hostStruct->permissionList->ElementAt(typeIndex));
permission_Free(i, typeStruct->type, PR_FALSE);
}
}
delete cookie_permissionList;
cookie_permissionList = NULL;
}
PRBool PR_CALLBACK deleteCookie(void *aElement, void *aData) {
cookie_CookieStruct *cookie = (cookie_CookieStruct*)aElement;
PR_FREEIF(cookie->path);
PR_FREEIF(cookie->host);
PR_FREEIF(cookie->name);
PR_FREEIF(cookie->cookie);
PR_Free(cookie);
return PR_TRUE;
}
PUBLIC void
COOKIE_RemoveAllCookies()
{
cookie_RemoveAllPermissions();
if (cookie_cookieList) {
cookie_cookieList->EnumerateBackwards(deleteCookie, nsnull);
cookie_cookiesChanged = PR_TRUE;
delete cookie_cookieList;
cookie_cookieList = nsnull;
}
}
PRIVATE void
cookie_RemoveOldestCookie(void) {
cookie_CookieStruct * cookie_s;
cookie_CookieStruct * oldest_cookie;
if (cookie_cookieList == nsnull) {
return;
}
PRInt32 count = cookie_cookieList->Count();
if (count == 0) {
return;
}
oldest_cookie = NS_STATIC_CAST(cookie_CookieStruct*, cookie_cookieList->ElementAt(0));
PRInt32 oldestLoc = 0;
for (PRInt32 i = 1; i < count; ++i) {
cookie_s = NS_STATIC_CAST(cookie_CookieStruct*, cookie_cookieList->ElementAt(i));
NS_ASSERTION(cookie_s, "cookie list is corrupt");
if(cookie_s->lastAccessed < oldest_cookie->lastAccessed) {
oldest_cookie = cookie_s;
oldestLoc = i;
}
}
if(oldest_cookie) {
cookie_cookieList->RemoveElementAt(oldestLoc);
deleteCookie((void*)oldest_cookie, nsnull);
cookie_cookiesChanged = PR_TRUE;
}
}
/* Remove any expired cookies from memory */
PRIVATE void
cookie_RemoveExpiredCookies() {
cookie_CookieStruct * cookie_s;
time_t cur_time = get_current_time();
if (cookie_cookieList == nsnull) {
return;
}
for (PRInt32 i = cookie_cookieList->Count(); i > 0;) {
i--;
cookie_s = NS_STATIC_CAST(cookie_CookieStruct*, cookie_cookieList->ElementAt(i));
NS_ASSERTION(cookie_s, "corrupt cookie list");
/* 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_cookieList->RemoveElementAt(i);
deleteCookie((void*)cookie_s, nsnull);
cookie_cookiesChanged = PR_TRUE;
}
}
}
/* checks to see if the maximum number of cookies per host
* is being exceeded and deletes the oldest one in that
* case
*/
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();
PRInt32 oldestLoc = -1;
for (PRInt32 i = 0; i < count; ++i) {
cookie_s = NS_STATIC_CAST(cookie_CookieStruct*, cookie_cookieList->ElementAt(i));
NS_ASSERTION(cookie_s, "corrupt cookie list");
if(!PL_strcasecmp(cookie_s->host, cur_host)) {
cookie_count++;
if(!oldest_cookie || oldest_cookie->lastAccessed > cookie_s->lastAccessed) {
oldest_cookie = cookie_s;
oldestLoc = i;
}
}
}
if(cookie_count >= MAX_COOKIES_PER_SERVER && oldest_cookie) {
NS_ASSERTION(oldestLoc > -1, "oldestLoc got out of sync with oldest_cookie");
cookie_cookieList->RemoveElementAt(oldestLoc);
deleteCookie((void*)oldest_cookie, nsnull);
cookie_cookiesChanged = PR_TRUE;
}
}
/* search for previous exact match */
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));
NS_ASSERTION(cookie_s, "corrupt cookie list");
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;
}
PRIVATE void
cookie_SetWarningPref(PRBool x) {
cookie_warning = x;
}
PRIVATE void
cookie_SetLifetimePref(COOKIE_LifetimeEnum x) {
cookie_lifetimeOpt = x;
}
PRIVATE void
cookie_SetLifetimeLimit(PRInt32 x) {
// save limit as seconds instead of days
cookie_lifetimeLimit = x*24*60*60;
}
PUBLIC COOKIE_BehaviorEnum
COOKIE_GetBehaviorPref() {
return cookie_behavior;
}
PRIVATE PRBool
cookie_GetWarningPref() {
return cookie_warning;
}
PRIVATE COOKIE_LifetimeEnum
cookie_GetLifetimePref() {
return cookie_lifetimeOpt;
}
PRIVATE time_t
cookie_GetLifetimeTime() {
// return time after which lifetime is excessive
return get_current_time() + cookie_lifetimeLimit;
}
#if 0
PRIVATE PRBool
cookie_GetLifetimeAsk(time_t expireTime) {
// return true if we should ask about this cookie
return (cookie_GetLifetimePref() == COOKIE_Ask)
&& (cookie_GetLifetimeTime() < expireTime);
}
#endif
PRIVATE time_t
cookie_TrimLifetime(time_t expires) {
// return potentially-trimmed lifetime
if (cookie_GetLifetimePref() == COOKIE_Trim) {
// a limit of zero means expire cookies at end of session
if (cookie_lifetimeLimit == 0) {
return 0;
}
time_t limit = cookie_GetLifetimeTime();
if ((unsigned)expires > (unsigned)limit) {
return limit;
}
}
return expires;
}
MODULE_PRIVATE int PR_CALLBACK
cookie_BehaviorPrefChanged(const char * newpref, void * data) {
PRInt32 n;
nsresult rv;
NS_WITH_SERVICE(nsIPref, prefs, "@mozilla.org/preferences;1", &rv);
if (NS_FAILED(prefs->GetIntPref(cookie_behaviorPref, &n))) {
n = COOKIE_Accept;
}
cookie_SetBehaviorPref((COOKIE_BehaviorEnum)n);
return 0;
}
MODULE_PRIVATE int PR_CALLBACK
cookie_WarningPrefChanged(const char * newpref, void * data) {
PRBool x;
nsresult rv;
NS_WITH_SERVICE(nsIPref, prefs, "@mozilla.org/preferences;1", &rv);
if (NS_FAILED(prefs->GetBoolPref(cookie_warningPref, &x))) {
x = PR_FALSE;
}
cookie_SetWarningPref(x);
return 0;
}
/* cookie utility functions */
PRIVATE void
image_SetBehaviorPref(COOKIE_BehaviorEnum x) {
image_behavior = x;
}
PRIVATE void
image_SetWarningPref(PRBool x) {
image_warning = x;
}
PRIVATE COOKIE_BehaviorEnum
image_GetBehaviorPref() {
return image_behavior;
}
PRIVATE PRBool
image_GetWarningPref() {
return image_warning;
}
MODULE_PRIVATE int PR_CALLBACK
image_BehaviorPrefChanged(const char * newpref, void * data) {
PRInt32 n;
nsresult rv;
NS_WITH_SERVICE(nsIPref, prefs, "@mozilla.org/preferences;1", &rv);
if (NS_FAILED(prefs->GetIntPref(image_behaviorPref, &n))) {
image_SetBehaviorPref(COOKIE_Accept);
} else {
image_SetBehaviorPref((COOKIE_BehaviorEnum)n);
}
return 0;
}
MODULE_PRIVATE int PR_CALLBACK
image_WarningPrefChanged(const char * newpref, void * data) {
PRBool x;
nsresult rv;
NS_WITH_SERVICE(nsIPref, prefs, "@mozilla.org/preferences;1", &rv);
if (NS_FAILED(prefs->GetBoolPref(image_warningPref, &x))) {
x = PR_FALSE;
}
image_SetWarningPref(x);
return 0;
}
MODULE_PRIVATE int PR_CALLBACK
cookie_LifetimeOptPrefChanged(const char * newpref, void * data) {
PRInt32 n;
nsresult rv;
NS_WITH_SERVICE(nsIPref, prefs, "@mozilla.org/preferences;1", &rv);
if (NS_FAILED(prefs->GetIntPref(cookie_lifetimePref, &n))) {
n = COOKIE_Normal;
}
cookie_SetLifetimePref((COOKIE_LifetimeEnum)n);
return 0;
}
MODULE_PRIVATE int PR_CALLBACK
cookie_LifetimeLimitPrefChanged(const char * newpref, void * data) {
PRInt32 n;
nsresult rv;
NS_WITH_SERVICE(nsIPref, prefs, "@mozilla.org/preferences;1", &rv);
if (!NS_FAILED(prefs->GetIntPref(cookie_lifetimeValue, &n))) {
cookie_SetLifetimeLimit(n);
}
return 0;
}
/*
* search if permission already exists
*/
nsresult
permission_CheckFromList(char * hostname, PRBool &permission, PRInt32 type) {
permission_HostStruct * hostStruct;
permission_TypeStruct * typeStruct;
/* ignore leading period in host name */
while (hostname && (*hostname == '.')) {
hostname++;
}
/* return if cookie_permission list does not exist */
if (cookie_permissionList == nsnull) {
return NS_ERROR_FAILURE;
}
/* find host name within list */
PRInt32 count = cookie_permissionList->Count();
for (PRInt32 i = 0; i < count; ++i) {
hostStruct = NS_STATIC_CAST(permission_HostStruct*, cookie_permissionList->ElementAt(i));
if (hostStruct) {
if(hostname && hostStruct->host && !PL_strcasecmp(hostname, hostStruct->host)) {
/* search for type in the permission list for this host */
PRInt32 count2 = hostStruct->permissionList->Count();
for (PRInt32 typeIndex=0; typeIndex<count2; typeIndex++) {
typeStruct = NS_STATIC_CAST
(permission_TypeStruct*, hostStruct->permissionList->ElementAt(typeIndex));
if (typeStruct->type == type) {
/* type found. Obtain the corresponding permission */
permission = typeStruct->permission;
return NS_OK;
}
}
/* type not found, return failure */
return NS_ERROR_FAILURE;
}
}
}
/* not found, return failure */
return NS_ERROR_FAILURE;
}
PRBool
permission_GetRememberChecked(PRInt32 type) {
if (type == COOKIEPERMISSION) {
return cookie_rememberChecked;
} else if (type == IMAGEPERMISSION) {
return image_rememberChecked;
} else {
return PR_FALSE;
}
}
void
permission_SetRememberChecked(PRInt32 type, PRBool value) {
if (type == COOKIEPERMISSION) {
cookie_rememberChecked = value;
} else if (type == IMAGEPERMISSION) {
image_rememberChecked = value;
}
}
nsresult
permission_Add(char * host, PRBool permission, PRInt32 type, PRBool save);
PRBool
permission_Check(
nsIPrompt *aPrompter,
char * hostname,
PRInt32 type,
PRBool warningPref,
PRUnichar * message)
{
PRBool permission;
/* try to make decision based on saved permissions */
if (NS_SUCCEEDED(permission_CheckFromList(hostname, permission, type))) {
return permission;
}
/* see if we need to prompt */
if(!warningPref) {
return PR_TRUE;
}
/* we need to prompt */
PRBool rememberChecked = permission_GetRememberChecked(type);
PRUnichar * remember_string = cookie_Localize("RememberThisDecision");
permission = cookie_CheckConfirmYN(aPrompter, message, remember_string, &rememberChecked);
/* see if we need to remember this decision */
if (rememberChecked) {
char * hostname2 = NULL;
/* ignore leading periods in host name */
char * hostnameAfterDot = hostname;
while (hostnameAfterDot && (*hostnameAfterDot == '.')) {
hostnameAfterDot++;
}
StrAllocCopy(hostname2, hostnameAfterDot);
permission_Add(hostname2, permission, type, PR_TRUE);
}
if (rememberChecked != permission_GetRememberChecked(type)) {
permission_SetRememberChecked(type, rememberChecked);
cookie_permissionsChanged = PR_TRUE;
permission_Save();
}
return permission;
}
PRIVATE int
cookie_SameDomain(char * currentHost, char * firstHost);
PUBLIC nsresult
Image_CheckForPermission(char * hostname, char * firstHostname, PRBool &permission) {
/* exit if imageblocker is not enabled */
nsresult rv;
PRBool prefvalue = PR_FALSE;
NS_WITH_SERVICE(nsIPref, prefs, "@mozilla.org/preferences;1", &rv);
if (NS_FAILED(rv) ||
NS_FAILED(prefs->GetBoolPref("imageblocker.enabled", &prefvalue)) ||
!prefvalue) {
permission = (image_GetBehaviorPref() != COOKIE_DontUse);
return NS_OK;
}
/* try to make a decision based on pref settings */
if ((image_GetBehaviorPref() == COOKIE_DontUse)) {
permission = PR_FALSE;
return NS_OK;
}
if (image_GetBehaviorPref() == COOKIE_DontAcceptForeign) {
/* compare tails of names checking to see if they have a common domain */
/* we do this by comparing the tails of both names where each tail includes at least one dot */
PRInt32 dotcount = 0;
char * tailHostname = hostname + PL_strlen(hostname) - 1;
while (tailHostname > hostname) {
if (*tailHostname == '.') {
dotcount++;
}
if (dotcount == 2) {
tailHostname++;
break;
}
tailHostname--;
}
dotcount = 0;
char * tailFirstHostname = firstHostname + PL_strlen(firstHostname) - 1;
while (tailFirstHostname > firstHostname) {
if (*tailFirstHostname == '.') {
dotcount++;
}
if (dotcount == 2) {
tailFirstHostname++;
break;
}
tailFirstHostname--;
}
if (PL_strcmp(tailFirstHostname, tailHostname)) {
permission = PR_FALSE;
return NS_OK;
}
}
/* use common routine to make decision */
PRUnichar * message = cookie_Localize("PermissionToAcceptImage");
PRUnichar * new_string = nsTextFormatter::smprintf(message, hostname ? hostname : "");
permission = permission_Check(0, hostname, IMAGEPERMISSION,
image_GetWarningPref(), new_string);
PR_FREEIF(new_string);
Recycle(message);
return NS_OK;
}
/* 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, "@mozilla.org/preferences;1", &rv);
// Initialize for cookie_behaviorPref
if (NS_FAILED(prefs->GetIntPref(cookie_behaviorPref, &n))) {
n = COOKIE_Accept;
}
cookie_SetBehaviorPref((COOKIE_BehaviorEnum)n);
prefs->RegisterCallback(cookie_behaviorPref, cookie_BehaviorPrefChanged, NULL);
// Initialize for cookie_warningPref
if (NS_FAILED(prefs->GetBoolPref(cookie_warningPref, &x))) {
x = PR_FALSE;
}
cookie_SetWarningPref(x);
prefs->RegisterCallback(cookie_warningPref, cookie_WarningPrefChanged, NULL);
if (NS_FAILED(prefs->GetIntPref(image_behaviorPref, &n))) {
n = COOKIE_Accept;
}
image_SetBehaviorPref((COOKIE_BehaviorEnum)n);
prefs->RegisterCallback(image_behaviorPref, image_BehaviorPrefChanged, NULL);
if (NS_FAILED(prefs->GetBoolPref(image_warningPref, &x))) {
x = PR_FALSE;
}
image_SetWarningPref(x);
prefs->RegisterCallback(image_warningPref, image_WarningPrefChanged, NULL);
// Initialize for cookie_lifetimePref
if (NS_FAILED(prefs->GetIntPref(cookie_lifetimePref, &n))) {
n = COOKIE_Normal;
}
cookie_SetLifetimePref((COOKIE_LifetimeEnum)n);
prefs->RegisterCallback(cookie_lifetimePref, cookie_LifetimeOptPrefChanged, NULL);
// Initialize for cookie_lifetimeValue
if (!NS_FAILED(prefs->GetIntPref(cookie_lifetimeValue, &n))) {
cookie_SetLifetimeLimit(n);
}
prefs->RegisterCallback(cookie_lifetimeValue, cookie_LifetimeLimitPrefChanged, NULL);
}
PRBool
cookie_IsInDomain(char* domain, char* host, int hostLength) {
int domainLength = PL_strlen(domain);
/*
* special case for domainName = .hostName
* e.g., hostName = netscape.com
* domainName = .netscape.com
*/
if ((domainLength == hostLength+1) && (domain[0] == '.') &&
!PL_strncasecmp(&domain[1], host, hostLength)) {
return PR_TRUE;
}
/*
* normal case for hostName = x<domainName>
* e.g., hostName = home.netscape.com
* domainName = .netscape.com
*/
if(domainLength <= hostLength &&
!PL_strncasecmp(domain, &host[hostLength-domainLength], domainLength)) {
return PR_TRUE;
}
/* tests failed, not in domain */
return PR_FALSE;
}
/* 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 secure = PR_FALSE;
time_t cur_time = get_current_time();
int host_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)) {
secure = PR_TRUE;
}
/* search for all cookies */
if (cookie_cookieList == nsnull) {
return NULL;
}
char *host = cookie_ParseURL(address, GET_HOST_PART);
char *path = cookie_ParseURL(address, GET_PATH_PART);
for (PRInt32 i = cookie_cookieList->Count(); i > 0;) {
i--;
cookie_s = NS_STATIC_CAST(cookie_CookieStruct*, cookie_cookieList->ElementAt(i));
NS_ASSERTION(cookie_s, "corrupt cookie list");
if(!cookie_s->host) continue;
/* check the host or domain first */
if(cookie_s->isDomain) {
char *cp;
/* 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(!cookie_IsInDomain(cookie_s->host, host, host_length)) {
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 secure and the path isn't, dont send it */
if (cookie_s->secure & !secure) {
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_cookieList->RemoveElementAt(i);
deleteCookie((void*)cookie_s, nsnull);
cookie_cookiesChanged = PR_TRUE;
continue;
}
/* if we've already added a cookie to the return list, append a "; " so
* subsequent cookies are delimited in the final list. */
if (rv) 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);
}
}
}
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);
}
nsresult
permission_Add(char * host, PRBool permission, PRInt32 type, PRBool save) {
/* create permission list if it does not yet exist */
if(!cookie_permissionList) {
cookie_permissionList = new nsVoidArray();
if(!cookie_permissionList) {
Recycle(host);
return NS_ERROR_FAILURE;
}
}
/* find existing entry for host */
permission_HostStruct * hostStruct;
PRBool HostFound = PR_FALSE;
PRInt32 count = cookie_permissionList->Count();
PRInt32 i;
for (i = 0; i < count; ++i) {
hostStruct = NS_STATIC_CAST(permission_HostStruct*, cookie_permissionList->ElementAt(i));
if (hostStruct) {
if (PL_strcasecmp(host,hostStruct->host)==0) {
/* host found in list */
Recycle(host);
HostFound = PR_TRUE;
break;
#ifdef alphabetize
} else if (PL_strcasecmp(host,hostStruct->host)<0) {
/* need to insert new entry here */
break;
#endif
}
}
}
if (!HostFound) {
/* create a host structure for the host */
hostStruct = PR_NEW(permission_HostStruct);
if (!hostStruct) {
Recycle(host);
return NS_ERROR_FAILURE;
}
hostStruct->host = host;
hostStruct->permissionList = new nsVoidArray();
if(!hostStruct->permissionList) {
PR_Free(hostStruct);
Recycle(host);
return NS_ERROR_FAILURE;
}
/* insert host structure into the list */
if (i == cookie_permissionList->Count()) {
cookie_permissionList->AppendElement(hostStruct);
} else {
cookie_permissionList->InsertElementAt(hostStruct, i);
}
}
/* see if host already has an entry for this type */
permission_TypeStruct * typeStruct;
PRBool typeFound = PR_FALSE;
PRInt32 count2 = hostStruct->permissionList->Count();
for (PRInt32 typeIndex=0; typeIndex<count2; typeIndex++) {
typeStruct = NS_STATIC_CAST
(permission_TypeStruct*, hostStruct->permissionList->ElementAt(typeIndex));
if (typeStruct->type == type) {
/* type found. Modify the corresponding permission */
typeStruct->permission = permission;
typeFound = PR_TRUE;
break;
}
}
/* create a type structure and attach it to the host structure */
if (!typeFound) {
typeStruct = PR_NEW(permission_TypeStruct);
typeStruct->type = type;
typeStruct->permission = permission;
hostStruct->permissionList->AppendElement(typeStruct);
}
/* write the changes out to a file */
if (save) {
cookie_permissionsChanged = PR_TRUE;
permission_Save();
}
return NS_OK;
}
PRIVATE void
permission_Unblock(char * host, PRInt32 type) {
/* nothing to do if permission list does not exist */
if(!cookie_permissionList) {
return;
}
/* find existing entry for host */
permission_HostStruct * hostStruct;
PRInt32 count = cookie_permissionList->Count();
for (PRInt32 hostIndex = 0; hostIndex < count; ++hostIndex) {
hostStruct = NS_STATIC_CAST(permission_HostStruct*, cookie_permissionList->ElementAt(hostIndex));
if (hostStruct && PL_strcasecmp(host, hostStruct->host)==0) {
/* host found in list, see if it has an entry for this type */
permission_TypeStruct * typeStruct;
PRInt32 count2 = hostStruct->permissionList->Count();
for (PRInt32 typeIndex=0; typeIndex<count2; typeIndex++) {
typeStruct = NS_STATIC_CAST
(permission_TypeStruct*, hostStruct->permissionList->ElementAt(typeIndex));
if (typeStruct && typeStruct->type == type) {
/* type found. Remove the permission if it is PR_FALSE */
if (typeStruct->permission == PR_FALSE) {
hostStruct->permissionList->RemoveElementAt(typeIndex);
/* if no more types are present, remove the entry */
PRInt32 count2 = hostStruct->permissionList->Count();
if (count2 == 0) {
PR_FREEIF(hostStruct->permissionList);
cookie_permissionList->RemoveElementAt(hostIndex);
PR_FREEIF(hostStruct->host);
PR_Free(hostStruct);
}
permission_Save();
return;
}
break;
}
}
break;
}
}
return;
}
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 host_length;
/* 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 cookie_IsInDomain(cookie_s->host, host, host_length);
} 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;
if (!cookie_cookieList || !host) return 0;
for (PRInt32 i = cookie_cookieList->Count(); i > 0;) {
i--;
cookie = NS_STATIC_CAST(cookie_CookieStruct*, cookie_cookieList->ElementAt(i));
NS_ASSERTION(cookie, "corrupt cookie list");
if (cookie_IsFromHost(cookie, host)) count++;
}
return count;
}
PRIVATE void
cookie_SetCookieString(char * curURL, nsIPrompt *aPrompter, char * setCookieHeader, time_t timeToExpire );
/* Java script is calling COOKIE_SetCookieString, netlib is calling
* this via COOKIE_SetCookieStringFromHttp.
*/
PRIVATE void
cookie_SetCookieString(char * curURL, nsIPrompt *aPrompter, char * setCookieHeader, time_t timeToExpire) {
cookie_CookieStruct * prev_cookie;
char *path_from_header=NULL, *host_from_header=NULL;
char *name_from_header=NULL, *cookie_from_header=NULL;
char *cur_path = cookie_ParseURL(curURL, GET_PATH_PART);
char *cur_host = cookie_ParseURL(curURL, GET_HOST_PART);
char *semi_colon, *ptr, *equal;
PRBool secure=PR_FALSE, isDomain=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;
}
//printf("\nSetCookieString(URL '%s', header '%s') time %d == %s\n",curURL,setCookieHeader,timeToExpire,asctime(gmtime(&timeToExpire)));
if(cookie_GetLifetimePref() == COOKIE_Discard) {
if(cookie_GetLifetimeTime() < timeToExpire) {
PR_Free(cur_path);
PR_Free(cur_host);
return;
}
}
//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==';'))) {
secure = PR_TRUE;
}
}
/* look for the path attribute */
ptr = PL_strcasestr(semi_colon, "path=");
if(ptr) {
nsCString path(ptr+5);
path.CompressWhitespace();
StrAllocCopy(path_from_header, path.GetBuffer());
/* 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 */
nsCString domain(ptr+7);
domain.CompressWhitespace();
StrAllocCopy(domain_from_header, domain.GetBuffer());
/* 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_FREEIF(path_from_header);
PR_Free(domain_from_header);
PR_Free(cur_path);
PR_Free(cur_host);
// TRACEMSG(("DOMAIN failed two dot test"));
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 (!cookie_IsInDomain(domain_from_header, cur_host, cur_host_length)) {
// TRACEMSG(("DOMAIN failed host within domain test."
// " Domain: %s, Host: %s", domain_from_header, cur_host));
PR_FREEIF(path_from_header);
PR_Free(domain_from_header);
PR_Free(cur_path);
PR_Free(cur_host);
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, "@mozilla.org/preferences;1", &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_FREEIF(path_from_header);
PR_Free(domain_from_header);
PR_Free(cur_path);
PR_Free(cur_host);
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 == 0) {
timeToExpire = cookie_ParseDate(date);
}
// TRACEMSG(("Have expires date: %ld", timeToExpire));
}
}
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';
nsCString cookieHeader(setCookieHeader);
cookieHeader.CompressWhitespace();
if(equal) {
StrAllocCopy(name_from_header, cookieHeader.GetBuffer());
nsCString value(equal+1);
value.CompressWhitespace();
StrAllocCopy(cookie_from_header, value.GetBuffer());
} else {
StrAllocCopy(cookie_from_header, cookieHeader.GetBuffer());
StrAllocCopy(name_from_header, "");
}
/* generate the message for the nag box */
PRUnichar * new_string=0;
int count = cookie_Count(host_from_header);
prev_cookie = cookie_CheckForPrevCookie
(path_from_header, host_from_header, name_from_header);
PRUnichar * message;
if (prev_cookie) {
message = cookie_Localize("PermissionToModifyCookie");
new_string = nsTextFormatter::smprintf(message, host_from_header ? host_from_header : "");
} else if (count>1) {
message = cookie_Localize("PermissionToSetAnotherCookie");
new_string = nsTextFormatter::smprintf(message, host_from_header ? host_from_header : "", count);
} else if (count==1){
message = cookie_Localize("PermissionToSetSecondCookie");
new_string = nsTextFormatter::smprintf(message, host_from_header ? host_from_header : "");
} else {
message = cookie_Localize("PermissionToSetACookie");
new_string = nsTextFormatter::smprintf(message, host_from_header ? host_from_header : "");
}
Recycle(message);
//TRACEMSG(("mkaccess.c: Setting cookie: %s for host: %s for path: %s",
// cookie_from_header, host_from_header, path_from_header));
/* use common code to determine if we can set the cookie */
PRBool permission = permission_Check(aPrompter, host_from_header, COOKIEPERMISSION,
// I believe this is the right place to eventually add the logic to ask
// about cookies that have excessive lifetimes, but it shouldn't be done
// until generalized per-site preferences are available.
//cookie_GetLifetimeAsk(timeToExpire) ||
cookie_GetWarningPref(),
new_string);
PR_FREEIF(new_string);
if (!permission) {
PR_FREEIF(path_from_header);
PR_FREEIF(host_from_header);
PR_FREEIF(name_from_header);
PR_FREEIF(cookie_from_header);
return;
}
/* 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 = cookie_TrimLifetime(timeToExpire);
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->secure = secure;
prev_cookie->isDomain = isDomain;
prev_cookie->lastAccessed = get_current_time();
} 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);
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 = cookie_TrimLifetime(timeToExpire);
prev_cookie->secure = secure;
prev_cookie->isDomain = isDomain;
prev_cookie->lastAccessed = get_current_time();
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);
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);
for (PRInt32 i = cookie_cookieList->Count(); i > 0;) {
i--;
tmp_cookie_ptr = NS_STATIC_CAST(cookie_CookieStruct*, cookie_cookieList->ElementAt(i));
NS_ASSERTION(tmp_cookie_ptr, "corrupt cookie list");
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_Save();
return;
}
PUBLIC void
COOKIE_SetCookieString(char * curURL, nsIPrompt *aPrompter, char * setCookieHeader) {
cookie_SetCookieString(curURL, aPrompter, 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, nsIPrompt *aPrompter, 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, aPrompter, setCookieHeader, server_date);
*newline = '\n';
COOKIE_SetCookieStringFromHttp(curURL, firstURL, aPrompter, 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 = get_current_time();
}
if( sDate && expires ) {
if( expires < sDate ) {
gmtCookieExpires=1;
} else {
gmtCookieExpires = expires - sDate + get_current_time();
// if overflow
if( gmtCookieExpires < get_current_time()) {
gmtCookieExpires = (((unsigned) (~0) << 1) >> 1); // max int
}
}
}
cookie_SetCookieString(curURL, aPrompter, setCookieHeader, gmtCookieExpires);
}
/* saves the HTTP cookies permissions to disk */
PRIVATE void
permission_Save() {
permission_HostStruct * hostStruct;
permission_TypeStruct * typeStruct;
if (!cookie_permissionsChanged) {
return;
}
if (cookie_permissionList == nsnull) {
return;
}
nsFileSpec dirSpec;
nsresult rval = cookie_ProfileDirectory(dirSpec);
if (NS_FAILED(rval)) {
return;
}
nsOutputFileStream strm(dirSpec + "cookperm.txt");
if (!strm.is_open()) {
return;
}
strm.write("# HTTP Cookie Permission File\n", 30);
strm.write("# http://www.netscape.com/newsref/std/cookie_spec.html\n", 55);
strm.write("# This is a generated file! Do not edit.\n\n", 43);
/* format shall be:
* host \t permission \t permission ... \n
*/
PRInt32 count = cookie_permissionList->Count();
for (PRInt32 i = 0; i < count; ++i) {
hostStruct = NS_STATIC_CAST(permission_HostStruct*, cookie_permissionList->ElementAt(i));
if (hostStruct) {
strm.write(hostStruct->host, nsCRT::strlen(hostStruct->host));
PRInt32 count2 = hostStruct->permissionList->Count();
for (PRInt32 typeIndex=0; typeIndex<count2; typeIndex++) {
typeStruct = NS_STATIC_CAST
(permission_TypeStruct*, hostStruct->permissionList->ElementAt(typeIndex));
strm.write("\t", 1);
nsCAutoString tmp; tmp.AppendInt(typeStruct->type);
strm.write(tmp.GetBuffer(), tmp.Length());
if (typeStruct->permission) {
strm.write("T", 1);
} else {
strm.write("F", 1);
}
}
strm.write("\n", 1);
}
}
/* save current state of nag boxs' checkmarks */
strm.write("@@@@", 4);
for (PRInt32 type = 0; type < NUMBER_OF_PERMISSIONS; type++) {
strm.write("\t", 1);
nsCAutoString tmp; tmp.AppendInt(type);
strm.write(tmp.GetBuffer(), tmp.Length());
if (permission_GetRememberChecked(type)) {
strm.write("T", 1);
} else {
strm.write("F", 1);
}
}
strm.write("\n", 1);
cookie_permissionsChanged = PR_FALSE;
strm.flush();
strm.close();
}
/* reads the HTTP cookies permission from disk */
PRIVATE void
permission_Load() {
nsAutoString buffer;
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 */
for (PRInt32 type=0; type<NUMBER_OF_PERMISSIONS; type++) {
permission_SetRememberChecked(type, PR_FALSE);
}
return;
}
/* format is:
* host \t number permission \t number permission ... \n
* if this format isn't respected we move onto the next line in the file.
*/
while(cookie_GetLine(strm,buffer) != -1) {
if ( !buffer.IsEmpty() ) {
PRUnichar firstChar = buffer.CharAt(0);
if (firstChar == '#' || firstChar == CR ||
firstChar == LF || firstChar == 0) {
continue;
}
}
int hostIndex, permissionIndex;
PRUint32 nextPermissionIndex = 0;
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;
buffer.Mid(host, hostIndex, permissionIndex-hostIndex-1);
nsString permissionString;
for (;;) {
if (nextPermissionIndex == buffer.Length()+1) {
break;
}
if ((nextPermissionIndex=buffer.FindChar('\t', PR_FALSE, permissionIndex)+1) == 0) {
nextPermissionIndex = buffer.Length()+1;
}
buffer.Mid(permissionString, permissionIndex, nextPermissionIndex-permissionIndex-1);
permissionIndex = nextPermissionIndex;
PRInt32 type = 0;
PRUint32 index = 0;
if (permissionString.IsEmpty()) {
continue; /* empty permission entry -- should never happen */
}
char c = (char)permissionString.CharAt(index);
while (index < permissionString.Length() && c >= '0' && c <= '9') {
type = 10*type + (c-'0');
c = (char)permissionString.CharAt(++index);
}
if (index >= permissionString.Length()) {
continue; /* bad format for this permission entry */
}
PRBool permission = (permissionString.CharAt(index) == 'T');
/*
* a host value of "@@@@" is a special code designating the
* state of the cookie nag-box's checkmark
*/
if (host.EqualsWithConversion("@@@@")) {
if (!permissionString.IsEmpty()) {
permission_SetRememberChecked(type, permission);
}
} else {
if (!permissionString.IsEmpty()) {
rv = permission_Add(host.ToNewCString(), permission, type, PR_FALSE);
if (NS_FAILED(rv)) {
strm.close();
return;
}
}
}
}
}
strm.close();
cookie_permissionsChanged = PR_FALSE;
return;
}
/* saves out the HTTP cookies to disk */
PRIVATE void
cookie_Save() {
cookie_CookieStruct * cookie_s;
time_t cur_date = get_current_time();
char date_string[36];
if (!cookie_cookiesChanged) {
return;
}
if (cookie_cookieList == nsnull) {
return;
}
nsFileSpec dirSpec;
nsresult rv = cookie_ProfileDirectory(dirSpec);
if (NS_FAILED(rv)) {
return;
}
nsOutputFileStream strm(dirSpec + "cookies.txt");
if (!strm.is_open()) {
/* file doesn't exist -- that's not an error */
return;
}
strm.write("# HTTP Cookie File\n", 19);
strm.write("# http://www.netscape.com/newsref/std/cookie_spec.html\n", 55);
strm.write("# This is a generated file! Do not edit.\n", 42);
strm.write("# To delete cookies, use the Cookie Manager.\n\n", 46);
/* format shall be:
*
* host \t isDomain \t path \t secure \t expires \t name \t cookie
*
* isDomain is PR_TRUE or PR_FALSE
* secure 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));
NS_ASSERTION(cookie_s, "corrupt cookie list");
if (cookie_s->expires < cur_date) {
/* don't write entry if cookie has expired or has no expiration date */
continue;
}
strm.write(cookie_s->host, nsCRT::strlen(cookie_s->host));
if (cookie_s->isDomain) {
strm.write("\tTRUE\t", 6);
} else {
strm.write("\tFALSE\t", 7);
}
strm.write(cookie_s->path, nsCRT::strlen(cookie_s->path));
if (cookie_s->secure) {
strm.write("\tTRUE\t", 6);
} else {
strm.write("\tFALSE\t", 7);
}
PR_snprintf(date_string, sizeof(date_string), "%lu", cookie_s->expires);
strm.write(date_string, nsCRT::strlen(date_string));
strm.write("\t", 1);
strm.write(cookie_s->name, nsCRT::strlen(cookie_s->name));
strm.write("\t", 1);
strm.write(cookie_s->cookie, nsCRT::strlen(cookie_s->cookie));
strm.write("\n", 1);
}
cookie_cookiesChanged = PR_FALSE;
strm.flush();
strm.close();
}
/* reads HTTP cookies from disk */
PRIVATE void
cookie_Load() {
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;
}
/* format is:
*
* host \t isDomain \t path \t secure \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
* secure 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.IsEmpty() ) {
PRUnichar firstChar = buffer.CharAt(0);
if (firstChar == '#' || firstChar == CR ||
firstChar == LF || firstChar == 0) {
continue;
}
}
int hostIndex, isDomainIndex, pathIndex, secureIndex, expiresIndex, nameIndex, cookieIndex;
hostIndex = 0;
if ((isDomainIndex=buffer.FindChar('\t', PR_FALSE,hostIndex)+1) == 0 ||
(pathIndex=buffer.FindChar('\t', PR_FALSE,isDomainIndex)+1) == 0 ||
(secureIndex=buffer.FindChar('\t', PR_FALSE,pathIndex)+1) == 0 ||
(expiresIndex=buffer.FindChar('\t', PR_FALSE,secureIndex)+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, secure, expires, name, cookie;
buffer.Mid(host, hostIndex, isDomainIndex-hostIndex-1);
buffer.Mid(isDomain, isDomainIndex, pathIndex-isDomainIndex-1);
buffer.Mid(path, pathIndex, secureIndex-pathIndex-1);
buffer.Mid(secure, secureIndex, expiresIndex-secureIndex-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) {
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.EqualsWithConversion("TRUE")) {
new_cookie->isDomain = PR_TRUE;
} else {
new_cookie->isDomain = PR_FALSE;
}
if (secure.EqualsWithConversion("TRUE")) {
new_cookie->secure = PR_TRUE;
} else {
new_cookie->secure = 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) {
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);
for (PRInt32 i = cookie_cookieList->Count(); i > 0;) {
i--;
tmp_cookie_ptr = NS_STATIC_CAST(cookie_CookieStruct*, cookie_cookieList->ElementAt(i));
NS_ASSERTION(tmp_cookie_ptr, "corrupt cookie list");
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_cookiesChanged = PR_FALSE;
return;
}
PUBLIC int
COOKIE_ReadCookies()
{
if (cookie_cookieList || cookie_permissionList)
NS_WARNING("We are reading the cookies when we already have some. Probably bad");
cookie_Load();
permission_Load();
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 if they are unequal */
if (PL_strcmp (cookie1->name, cookie2->name) < 0) {
return -1;
}
if (PL_strcmp (cookie1->name, cookie2->name) > 0) {
return 1;
}
/* if host and cookie names are equal, make decision based on path */
/* It may seem like this should never happen but it does.
* Go to groups.aol.com and you will get two cookies with
* identical host and cookie names but different paths
*/
return (PL_strcmp (cookie1->path, cookie2->path));
}
/*
* 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;
if (!cookie_cookieList) return NULL;
for (PRInt32 i = cookie_cookieList->Count(); i > 0;) {
i--;
cookie_ptr = NS_STATIC_CAST(cookie_CookieStruct*, cookie_cookieList->ElementAt(i));
NS_ASSERTION(cookie_ptr, "corrupt cookie list");
if (!cookie || (CookieCompare(cookie_ptr, cookie) > 0)) {
if (!lowestCookie || (CookieCompare(cookie_ptr, lowestCookie) < 0)) {
lowestCookie = cookie_ptr;
*cookieNum = i;
}
}
}
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++;
quote++;
}
result = (char*)PR_Malloc(count + 1);
for (i=0, j=0; i<PL_strlen(s); i++) {
if (s[i] == '"') {
result[i+(j++)] = '\\';
}
result[i+j] = s[i];
}
result[i+j] = '\0';
return result;
}
/* return PR_TRUE if "number" is in sequence of comma-separated numbers */
PRIVATE
PRBool cookie_InSequence(char* sequence, int number) {
char* ptr;
char* endptr;
char* undo = NULL;
PRBool retval = PR_FALSE;
int i;
/* not necessary -- routine will work even with null sequence */
if (!*sequence) {
return PR_FALSE;
}
for (ptr = sequence ; ptr ; ptr = endptr) {
/* get to next comma */
endptr = PL_strchr(ptr, ',');
/* if comma found, process it */
if (endptr) {
/* restore last comma-to-null back to comma */
if (undo) {
*undo = ',';
}
/* set the comma to a null */
undo = endptr;
*endptr++ = '\0';
/* compare the number before the comma with "number" */
if (*ptr) {
i = atoi(ptr);
if (i == number) {
/* "number" was in the sequence so return PR_TRUE */
retval = PR_TRUE;
break;
}
}
}
}
/* restore last comma-to-null back to comma */
if (undo) {
*undo = ',';
}
return retval;
}
PRIVATE char*
cookie_FindValueInArgs(nsAutoString results, char* name) {
/* note: name must start and end with a vertical bar */
nsAutoString value;
PRInt32 start, length;
start = results.Find(name);
NS_ASSERTION(start >= 0, "bad data");
if (start < 0) {
return nsCAutoString().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;
PRInt32 count = 0;
/* step through all cookies and delete those that are in the sequence */
char * gone = cookie_FindValueInArgs(results, "|goneC|");
char * block = cookie_FindValueInArgs(results, "|block|");
if (cookie_cookieList) {
count = cookie_cookieList->Count();
while (count>0) {
count--;
cookie = NS_STATIC_CAST(cookie_CookieStruct*, cookie_cookieList->ElementAt(count));
NS_ASSERTION(cookie, "corrupt cookie list");
if (cookie_InSequence(gone, count)) {
if (block[0] == 't' && cookie->host) {
char * hostname = nsnull;
char * hostnameAfterDot = cookie->host;
while (*hostnameAfterDot == '.') {
hostnameAfterDot++;
}
StrAllocCopy(hostname, hostnameAfterDot);
if (hostname) {
permission_Add(hostname, PR_FALSE, COOKIEPERMISSION, PR_TRUE);
}
}
cookie_cookieList->RemoveElementAt(count);
deleteCookie((void*)cookie, nsnull);
cookie_cookiesChanged = PR_TRUE;
}
}
}
cookie_Save();
nsCRT::free(gone);
nsCRT::free(block);
/* step through all permissions and delete those that are in the sequence */
for (PRInt32 type = 0; type < NUMBER_OF_PERMISSIONS; type++) {
switch (type) {
case COOKIEPERMISSION:
gone = cookie_FindValueInArgs(results, "|goneP|");
break;
case IMAGEPERMISSION:
gone = cookie_FindValueInArgs(results, "|goneI|");
break;
}
if (cookie_permissionList) {
count = cookie_permissionList->Count();
while (count>0) {
count--;
if (cookie_InSequence(gone, count)) {
permission_Free(count, type, PR_FALSE);
cookie_permissionsChanged = PR_TRUE;
}
}
}
permission_Save();
nsCRT::free(gone);
}
}
#define BREAK '\001'
PUBLIC void
COOKIE_GetCookieListForViewer(nsString& aCookieList) {
int cookieNum = 0;
cookie_CookieStruct * cookie;
/* 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");
aCookieList.AppendWithConversion(BREAK);
aCookieList.AppendInt(cookieNum);
aCookieList.AppendWithConversion(BREAK);
aCookieList.AppendWithConversion(fixed_name);
aCookieList.AppendWithConversion(BREAK);
aCookieList.AppendWithConversion(fixed_value);
aCookieList.AppendWithConversion(BREAK);
aCookieList.Append(cookie->isDomain ? Domain : Host);
aCookieList.AppendWithConversion(BREAK);
aCookieList.AppendWithConversion(fixed_domain_or_host);
aCookieList.AppendWithConversion(BREAK);
aCookieList.AppendWithConversion(fixed_path);
aCookieList.AppendWithConversion(BREAK);
aCookieList.Append(cookie->secure ? Yes : No);
aCookieList.AppendWithConversion(BREAK);
if (cookie->expires) {
/*
* Cookie expiration times on mac will not be decoded correctly because
* they were based on get_current_time() instead of time(NULL) -- see comments in
* get_current_time. So we need to adjust for that now in order for the
* display of the expiration time to be correct
*/
time_t expires;
expires = cookie->expires + (time(NULL) - get_current_time());
nsString expireString; expireString.AssignWithConversion(ctime(&(expires)));
aCookieList.Append(expireString);
} else {
aCookieList.Append(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);
}
}
PUBLIC void
COOKIE_GetPermissionListForViewer(nsString& aPermissionList, PRInt32 type) {
int permissionNum = 0;
permission_HostStruct * hostStruct;
permission_TypeStruct * typeStruct;
if (cookie_permissionList == nsnull) {
return;
}
aPermissionList.SetLength(0);
PRInt32 count = cookie_permissionList->Count();
for (PRInt32 i = 0; i < count; ++i) {
hostStruct = NS_STATIC_CAST(permission_HostStruct*, cookie_permissionList->ElementAt(i));
if (hostStruct) {
PRInt32 count2 = hostStruct->permissionList->Count();
for (PRInt32 typeIndex=0; typeIndex<count2; typeIndex++) {
typeStruct = NS_STATIC_CAST
(permission_TypeStruct*, hostStruct->permissionList->ElementAt(typeIndex));
if (typeStruct->type == type) {
aPermissionList.AppendWithConversion(BREAK);
aPermissionList.AppendInt(permissionNum);
aPermissionList.AppendWithConversion(BREAK);
aPermissionList.AppendWithConversion((typeStruct->permission) ? '+' : '-');
aPermissionList.AppendWithConversion(hostStruct->host);
permissionNum++;
}
}
}
}
}
PUBLIC void
Image_Block(nsString imageURL) {
if (imageURL.Length() == 0) {
return;
}
char * imageURLCString = imageURL.ToNewCString();
char *host = cookie_ParseURL(imageURLCString, GET_HOST_PART);
Recycle(imageURLCString);
char * hostname = nsnull;
StrAllocCopy(hostname, host);
Recycle(host);
permission_Add(hostname, PR_FALSE, IMAGEPERMISSION, PR_TRUE);
}
PUBLIC void
Permission_Add(nsString& objectURL, PRBool permission, PRInt32 type) {
if (objectURL.IsEmpty()) {
return;
}
char * objectURLCString = objectURL.ToNewCString();
char *host = cookie_ParseURL(objectURLCString, GET_HOST_PART);
Recycle(objectURLCString);
/*
* if permission is false, it will be added to the permission list
* if permission is true, any false permissions will be removed rather than a
* true permission being added
*/
if (permission) {
char * hostPtr = host;
while (PR_TRUE) {
permission_Unblock(hostPtr, type);
hostPtr = PL_strchr(hostPtr, '.');
if (!hostPtr) {
break;
}
hostPtr++; /* get passed the period */
}
Recycle(host);
return;
}
permission_Add(host, permission, type, PR_TRUE);
}
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;
}