Bug 536650 - Cookie values not saved for pages loaded from file://; part 2: deal with empty hosts in the

cookieservice . r=sdwilsh, sr=bz
This commit is contained in:
Dan Witte 2010-01-12 10:29:20 -08:00
Родитель ac5ec695ab
Коммит d55818f5b3
6 изменённых файлов: 368 добавлений и 128 удалений

Просмотреть файл

@ -2,6 +2,8 @@ const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
Components.utils.import("resource://gre/modules/NetUtil.jsm");
function do_check_throws(f, result, stack)
{
if (!stack)
@ -20,14 +22,69 @@ function do_check_throws(f, result, stack)
function run_test() {
var cs = Cc["@mozilla.org/cookieService;1"].getService(Ci.nsICookieService);
var cm = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2);
var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
var expiry = (Date.now() + 1000) * 1000;
cm.removeAll();
// test that an empty host results in a no-op
var uri = ios.newURI("http://baz.com/", null, null);
var emptyuri = ios.newURI("http:///", null, null);
var doturi = ios.newURI("http://./", null, null);
// test that variants of 'baz.com' get normalized appropriately, but that
// malformed hosts are rejected
cm.add("baz.com", "/", "foo", "bar", false, false, true, expiry);
do_check_eq(cm.countCookiesFromHost("baz.com"), 1);
do_check_eq(cm.countCookiesFromHost("BAZ.com"), 1);
do_check_eq(cm.countCookiesFromHost(".baz.com"), 1);
do_check_eq(cm.countCookiesFromHost("baz.com."), 1);
do_check_eq(cm.countCookiesFromHost(".baz.com."), 1);
do_check_throws(function() {
cm.countCookiesFromHost("baz.com..");
}, Cr.NS_ERROR_ILLEGAL_VALUE);
do_check_throws(function() {
cm.countCookiesFromHost("baz..com");
}, Cr.NS_ERROR_ILLEGAL_VALUE);
do_check_throws(function() {
cm.countCookiesFromHost("..baz.com");
}, Cr.NS_ERROR_ILLEGAL_VALUE);
cm.remove("BAZ.com.", "foo", "/", false);
do_check_eq(cm.countCookiesFromHost("baz.com"), 0);
// test that domain cookies are illegal for IP addresses, aliases such as
// 'localhost', and eTLD's such as 'co.uk'
cm.add("192.168.0.1", "/", "foo", "bar", false, false, true, expiry);
do_check_eq(cm.countCookiesFromHost("192.168.0.1"), 1);
do_check_eq(cm.countCookiesFromHost("192.168.0.1."), 1);
do_check_throws(function() {
cm.countCookiesFromHost(".192.168.0.1");
}, Cr.NS_ERROR_ILLEGAL_VALUE);
do_check_throws(function() {
cm.countCookiesFromHost(".192.168.0.1.");
}, Cr.NS_ERROR_ILLEGAL_VALUE);
cm.add("localhost", "/", "foo", "bar", false, false, true, expiry);
do_check_eq(cm.countCookiesFromHost("localhost"), 1);
do_check_eq(cm.countCookiesFromHost("localhost."), 1);
do_check_throws(function() {
cm.countCookiesFromHost(".localhost");
}, Cr.NS_ERROR_ILLEGAL_VALUE);
do_check_throws(function() {
cm.countCookiesFromHost(".localhost.");
}, Cr.NS_ERROR_ILLEGAL_VALUE);
cm.add("co.uk", "/", "foo", "bar", false, false, true, expiry);
do_check_eq(cm.countCookiesFromHost("co.uk"), 1);
do_check_eq(cm.countCookiesFromHost("co.uk."), 1);
do_check_throws(function() {
cm.countCookiesFromHost(".co.uk");
}, Cr.NS_ERROR_ILLEGAL_VALUE);
do_check_throws(function() {
cm.countCookiesFromHost(".co.uk.");
}, Cr.NS_ERROR_ILLEGAL_VALUE);
cm.removeAll();
// test that setting an empty or '.' http:// host results in a no-op
var uri = NetUtil.newURI("http://baz.com/");
var emptyuri = NetUtil.newURI("http:///");
var doturi = NetUtil.newURI("http://./");
do_check_eq(uri.asciiHost, "baz.com");
do_check_eq(emptyuri.asciiHost, "");
do_check_eq(doturi.asciiHost, ".");
cs.setCookieString(emptyuri, null, "foo2=bar", null);
@ -41,42 +98,98 @@ function run_test() {
do_check_eq(cs.getCookieString(emptyuri, null), null);
do_check_eq(cs.getCookieString(doturi, null), null);
// test that an empty host to add() or remove() throws
var expiry = (Date.now() + 1000) * 1000;
do_check_throws(function() {
cm.add("", "/", "foo2", "bar", false, false, true, expiry);
}, Cr.NS_ERROR_ILLEGAL_VALUE);
do_check_eq(getCookieCount(), 1);
do_check_throws(function() {
cm.add(".", "/", "foo3", "bar", false, false, true, expiry);
}, Cr.NS_ERROR_ILLEGAL_VALUE);
do_check_eq(getCookieCount(), 1);
cm.add("test.com", "/", "foo", "bar", false, false, true, expiry);
do_check_eq(getCookieCount(), 2);
do_check_throws(function() {
cm.remove("", "foo2", "/", false);
}, Cr.NS_ERROR_ILLEGAL_VALUE);
do_check_eq(getCookieCount(), 2);
do_check_throws(function() {
cm.remove(".", "foo3", "/", false);
}, Cr.NS_ERROR_ILLEGAL_VALUE);
do_check_eq(getCookieCount(), 2);
cm.remove("test.com", "foo", "/", false);
do_check_eq(getCookieCount(), 1);
do_check_eq(cm.countCookiesFromHost("baz.com"), 1);
do_check_eq(cm.countCookiesFromHost(""), 0);
do_check_eq(cm.countCookiesFromHost("."), 0);
do_check_throws(function() {
cm.countCookiesFromHost(".");
}, Cr.NS_ERROR_ILLEGAL_VALUE);
do_check_throws(function() {
cm.countCookiesFromHost("..");
}, Cr.NS_ERROR_ILLEGAL_VALUE);
var e = cm.getCookiesFromHost("baz.com");
var e = cm.getCookiesFromHost("");
do_check_false(e.hasMoreElements());
do_check_throws(function() {
cm.getCookiesFromHost(".");
}, Cr.NS_ERROR_ILLEGAL_VALUE);
do_check_throws(function() {
cm.getCookiesFromHost("..");
}, Cr.NS_ERROR_ILLEGAL_VALUE);
e = cm.getCookiesFromHost("baz.com");
do_check_true(e.hasMoreElements());
do_check_eq(e.getNext().QueryInterface(Ci.nsICookie2).name, "foo");
do_check_false(e.hasMoreElements());
e = cm.getCookiesFromHost("");
do_check_false(e.hasMoreElements());
e = cm.getCookiesFromHost(".");
do_check_throws(function() {
cm.getCookiesFromHost(".");
}, Cr.NS_ERROR_ILLEGAL_VALUE);
do_check_throws(function() {
cm.getCookiesFromHost("..");
}, Cr.NS_ERROR_ILLEGAL_VALUE);
cm.removeAll();
// test that an empty file:// host works
emptyuri = NetUtil.newURI("file:///");
do_check_eq(emptyuri.asciiHost, "");
do_check_eq(NetUtil.newURI("file://./").asciiHost, "");
do_check_eq(NetUtil.newURI("file://foo.bar/").asciiHost, "");
cs.setCookieString(emptyuri, null, "foo2=bar", null);
do_check_eq(getCookieCount(), 1);
cs.setCookieString(emptyuri, null, "foo3=bar; domain=", null);
do_check_eq(getCookieCount(), 2);
cs.setCookieString(emptyuri, null, "foo4=bar; domain=.", null);
do_check_eq(getCookieCount(), 3);
cs.setCookieString(emptyuri, null, "foo5=bar; domain=bar.com", null);
do_check_eq(getCookieCount(), 3);
do_check_eq(cs.getCookieString(emptyuri, null), "foo2=bar; foo3=bar; foo4=bar");
do_check_eq(cm.countCookiesFromHost("baz.com"), 0);
do_check_eq(cm.countCookiesFromHost(""), 3);
do_check_throws(function() {
cm.countCookiesFromHost(".");
}, Cr.NS_ERROR_ILLEGAL_VALUE);
e = cm.getCookiesFromHost("baz.com");
do_check_false(e.hasMoreElements());
e = cm.getCookiesFromHost("");
do_check_true(e.hasMoreElements());
e.getNext();
do_check_true(e.hasMoreElements());
e.getNext();
do_check_true(e.hasMoreElements());
e.getNext();
do_check_false(e.hasMoreElements());
do_check_throws(function() {
cm.getCookiesFromHost(".");
}, Cr.NS_ERROR_ILLEGAL_VALUE);
cm.removeAll();
// test that an empty host to add() or remove() works,
// but a host of '.' doesn't
cm.add("", "/", "foo2", "bar", false, false, true, expiry);
do_check_eq(getCookieCount(), 1);
do_check_throws(function() {
cm.add(".", "/", "foo3", "bar", false, false, true, expiry);
}, Cr.NS_ERROR_ILLEGAL_VALUE);
do_check_eq(getCookieCount(), 1);
cm.remove("", "foo2", "/", false);
do_check_eq(getCookieCount(), 0);
do_check_throws(function() {
cm.remove(".", "foo3", "/", false);
}, Cr.NS_ERROR_ILLEGAL_VALUE);
// test that the 'domain' attribute accepts a leading dot for IP addresses,
// aliases such as 'localhost', eTLD's such as 'co.uk', and the empty host;
// but that the resulting cookie is for the exact host only.
testDomainCookie("http://192.168.0.1/", "192.168.0.1");
testDomainCookie("http://localhost/", "localhost");
testDomainCookie("http://co.uk/", "co.uk");
testDomainCookie("file:///", "");
cm.removeAll();
}
@ -93,3 +206,23 @@ function getCookieCount() {
return count;
}
function testDomainCookie(uriString, domain) {
var cs = Cc["@mozilla.org/cookieService;1"].getService(Ci.nsICookieService);
var cm = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2);
cm.removeAll();
var uri = NetUtil.newURI(uriString);
cs.setCookieString(uri, null, "foo=bar; domain=" + domain, null);
var e = cm.getCookiesFromHost(domain);
do_check_true(e.hasMoreElements());
do_check_eq(e.getNext().QueryInterface(Ci.nsICookie2).host, domain);
cm.removeAll();
cs.setCookieString(uri, null, "foo=bar; domain=." + domain, null);
e = cm.getCookiesFromHost(domain);
do_check_true(e.hasMoreElements());
do_check_eq(e.getNext().QueryInterface(Ci.nsICookie2).host, domain);
cm.removeAll();
}

Просмотреть файл

@ -62,13 +62,22 @@ interface nsICookieManager : nsISupports
readonly attribute nsISimpleEnumerator enumerator;
/**
* Called to remove an individual cookie from the cookie list
* Called to remove an individual cookie from the cookie list, specified
* by host, name, and path. If the cookie cannot be found, no exception
* is thrown. Typically, the arguments to this method will be obtained
* directly from the desired nsICookie object.
*
* @param aDomain The host or domain for which the cookie was set
* @param aHost The host or domain for which the cookie was set. @see
* nsICookieManager2::add for a description of acceptable host
* strings. If the target cookie is a domain cookie, a leading
* dot must be present.
* @param aName The name specified in the cookie
* @param aPath The path for which the cookie was set
* @param aBlocked Indicates if cookies from this host should be permanently blocked
*
*/
void remove(in AUTF8String aDomain, in ACString aName, in AUTF8String aPath, in boolean aBlocked);
void remove(in AUTF8String aHost,
in ACString aName,
in AUTF8String aPath,
in boolean aBlocked);
};

Просмотреть файл

@ -44,17 +44,22 @@ interface nsIFile;
* Additions to the frozen nsICookieManager
*/
[scriptable, uuid(d1e9e50f-b78b-4e3b-a474-f3cbca59b013)]
[scriptable, uuid(94628d1d-8b31-4baa-b474-9c872c440f90)]
interface nsICookieManager2 : nsICookieManager
{
/**
* Add a cookie. nsICookieService is the normal way to do this. This
* method is something of a backdoor.
*
* @param aDomain
* @param aHost
* the host or domain for which the cookie is set. presence of a
* leading dot indicates a domain cookie; otherwise, the cookie
* is treated as a non-domain cookie. see RFC2109.
* is treated as a non-domain cookie (see RFC2109). The host string
* will be normalized to ASCII or ACE; any trailing dot will be
* stripped. To be a domain cookie, the host must have at least two
* subdomain parts (e.g. '.foo.com', not '.com'), otherwise an
* exception will be thrown. An empty string is acceptable
* (e.g. file:// URI's).
* @param aPath
* path within the domain for which the cookie is valid
* @param aName
@ -74,7 +79,7 @@ interface nsICookieManager2 : nsICookieManager
* 1970 UTC. note that expiry time will also be honored for session cookies;
* in this way, the more restrictive of the two will take effect.
*/
void add(in AUTF8String aDomain,
void add(in AUTF8String aHost,
in AUTF8String aPath,
in ACString aName,
in ACString aValue,
@ -101,13 +106,13 @@ interface nsICookieManager2 : nsICookieManager
* counted.
*
* @param aHost
* the host string to begin from, e.g. "google.com". this should consist
* of only the host portion of a URI, and should not contain a leading
* dot, a port, etc.
* the host string to search for, e.g. "google.com". this should consist
* of only the host portion of a URI. see @add for a description of
* acceptable host strings.
*
* @return the number of cookies found.
*/
unsigned long countCookiesFromHost(in ACString aHost);
unsigned long countCookiesFromHost(in AUTF8String aHost);
/**
* Returns an enumerator of cookies that exist within the base domain of
@ -116,15 +121,15 @@ interface nsICookieManager2 : nsICookieManager
* subdomains would be returned.
*
* @param aHost
* the host string to begin from, e.g. "google.com". this should consist
* of only the host portion of a URI, and should not contain a leading
* dot, a port, etc.
* the host string to search for, e.g. "google.com". this should consist
* of only the host portion of a URI. see @add for a description of
* acceptable host strings.
*
* @return an nsISimpleEnumerator of nsICookie2 objects.
*
* @see countCookiesFromHost
*/
nsISimpleEnumerator getCookiesFromHost(in ACString aHost);
nsISimpleEnumerator getCookiesFromHost(in AUTF8String aHost);
/**
* Import an old-style cookie file. Imported cookies will be added to the

Просмотреть файл

@ -89,6 +89,9 @@ interface nsICookieService : nsISupports
*
* @param aURI
* the URI of the document for which cookies are being queried.
* file:// URI's (i.e. with an empty host) are allowed, but any other
* scheme must have a non-empty host. A trailing dot in the host
* is acceptable, and will be stripped.
* @param aChannel
* the channel used to load the document. this parameter should not
* be null, otherwise the cookies will not be returned if third-party
@ -108,6 +111,9 @@ interface nsICookieService : nsISupports
*
* @param aURI
* the URI of the document for which cookies are being queried.
* file:// URI's (i.e. with an empty host) are allowed, but any other
* scheme must have a non-empty host. A trailing dot in the host
* is acceptable, and will be stripped.
* @param aFirstURI
* the URI that the user originally typed in or clicked on to initiate
* the load of the document referenced by aURI.
@ -127,6 +133,9 @@ interface nsICookieService : nsISupports
*
* @param aURI
* the URI of the document for which cookies are being set.
* file:// URI's (i.e. with an empty host) are allowed, but any other
* scheme must have a non-empty host. A trailing dot in the host
* is acceptable, and will be stripped.
* @param aPrompt
* the prompt to use for all user-level cookie notifications.
* @param aCookie
@ -151,6 +160,9 @@ interface nsICookieService : nsISupports
*
* @param aURI
* the URI of the document for which cookies are being set.
* file:// URI's (i.e. with an empty host) are allowed, but any other
* scheme must have a non-empty host. A trailing dot in the host
* is acceptable, and will be stripped.
* @param aFirstURI
* the URI that the user originally typed in or clicked on to initiate
* the load of the document referenced by aURI.

Просмотреть файл

@ -54,6 +54,7 @@
#include "nsIObserverService.h"
#include "nsILineInputStream.h"
#include "nsIEffectiveTLDService.h"
#include "nsIIDNService.h"
#include "nsTArray.h"
#include "nsCOMArray.h"
@ -405,6 +406,9 @@ nsCookieService::Init()
mTLDService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
mIDNService = do_GetService(NS_IDNSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
// init our pref and observer
nsCOMPtr<nsIPrefBranch2> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID);
if (prefBranch) {
@ -810,9 +814,12 @@ nsCookieService::SetCookieStringInternal(nsIURI *aHostURI,
// get the base domain for the host URI.
// e.g. for "www.bbc.co.uk", this would be "bbc.co.uk".
PRBool isIPAddress;
// file:// URI's (i.e. with an empty host) are allowed, but any other
// scheme must have a non-empty host. A trailing dot in the host
// is acceptable, and will be stripped.
PRBool requireHostMatch;
nsCAutoString baseDomain;
nsresult rv = GetBaseDomain(aHostURI, baseDomain, isIPAddress);
nsresult rv = GetBaseDomain(aHostURI, baseDomain, requireHostMatch);
if (NS_FAILED(rv)) {
COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader,
"couldn't get base domain from URI");
@ -821,7 +828,7 @@ nsCookieService::SetCookieStringInternal(nsIURI *aHostURI,
// check default prefs
PRUint32 cookieStatus = CheckPrefs(aHostURI, aChannel, baseDomain,
isIPAddress, aCookieHeader);
requireHostMatch, aCookieHeader);
// fire a notification if cookie was rejected (but not if there was an error)
switch (cookieStatus) {
case STATUS_REJECTED:
@ -850,7 +857,7 @@ nsCookieService::SetCookieStringInternal(nsIURI *aHostURI,
// switch to a nice string type now, and process each cookie in the header
nsDependentCString cookieHeader(aCookieHeader);
while (SetCookieInternal(aHostURI, aChannel, baseDomain, isIPAddress,
while (SetCookieInternal(aHostURI, aChannel, baseDomain, requireHostMatch,
cookieHeader, serverTime, aFromHttp));
return NS_OK;
@ -972,7 +979,7 @@ nsCookieService::GetEnumerator(nsISimpleEnumerator **aEnumerator)
}
NS_IMETHODIMP
nsCookieService::Add(const nsACString &aDomain,
nsCookieService::Add(const nsACString &aHost,
const nsACString &aPath,
const nsACString &aName,
const nsACString &aValue,
@ -981,16 +988,21 @@ nsCookieService::Add(const nsACString &aDomain,
PRBool aIsSession,
PRInt64 aExpiry)
{
// first, normalize the hostname, and fail if it contains illegal characters.
nsCAutoString host(aHost);
nsresult rv = NormalizeHost(host);
NS_ENSURE_SUCCESS(rv, rv);
// get the base domain for the host URI.
// e.g. for "www.bbc.co.uk", this would be "bbc.co.uk".
nsCAutoString baseDomain;
nsresult rv = GetBaseDomainFromHost(aDomain, baseDomain);
rv = GetBaseDomainFromHost(host, baseDomain);
NS_ENSURE_SUCCESS(rv, rv);
PRInt64 currentTimeInUsec = PR_Now();
nsRefPtr<nsCookie> cookie =
nsCookie::Create(aName, aValue, aDomain, aPath,
nsCookie::Create(aName, aValue, host, aPath,
aExpiry,
currentTimeInUsec,
currentTimeInUsec,
@ -1011,13 +1023,18 @@ nsCookieService::Remove(const nsACString &aHost,
const nsACString &aPath,
PRBool aBlocked)
{
// first, normalize the hostname, and fail if it contains illegal characters.
nsCAutoString host(aHost);
nsresult rv = NormalizeHost(host);
NS_ENSURE_SUCCESS(rv, rv);
nsCAutoString baseDomain;
nsresult rv = GetBaseDomainFromHost(aHost, baseDomain);
rv = GetBaseDomainFromHost(host, baseDomain);
NS_ENSURE_SUCCESS(rv, rv);
nsListIter matchIter;
if (FindCookie(baseDomain,
PromiseFlatCString(aHost),
host,
PromiseFlatCString(aName),
PromiseFlatCString(aPath),
matchIter,
@ -1029,13 +1046,11 @@ nsCookieService::Remove(const nsACString &aHost,
// check if we need to add the host to the permissions blacklist.
if (aBlocked && mPermissionService) {
nsCAutoString host(NS_LITERAL_CSTRING("http://"));
// strip off the domain dot, if necessary
if (aHost.First() == '.')
host.Append(Substring(aHost, 1, aHost.Length() - 1));
else
host.Append(aHost);
if (!host.IsEmpty() && host.First() == '.')
host.Cut(0, 1);
host.Insert(NS_LITERAL_CSTRING("http://"), 0);
nsCOMPtr<nsIURI> uri;
NS_NewURI(getter_AddRefs(uri), host);
@ -1323,23 +1338,27 @@ nsCookieService::GetCookieInternal(nsIURI *aHostURI,
// get the base domain, host, and path from the URI.
// e.g. for "www.bbc.co.uk", the base domain would be "bbc.co.uk".
PRBool isIPAddress;
// file:// URI's (i.e. with an empty host) are allowed, but any other
// scheme must have a non-empty host. A trailing dot in the host
// is acceptable, and will be stripped.
PRBool requireHostMatch;
nsCAutoString baseDomain, hostFromURI, pathFromURI;
nsresult rv = GetBaseDomain(aHostURI, baseDomain, isIPAddress);
nsresult rv = GetBaseDomain(aHostURI, baseDomain, requireHostMatch);
if (NS_SUCCEEDED(rv))
rv = aHostURI->GetAsciiHost(hostFromURI);
if (NS_SUCCEEDED(rv))
rv = aHostURI->GetPath(pathFromURI);
// trim trailing dots
hostFromURI.Trim(".");
if (NS_FAILED(rv) || hostFromURI.IsEmpty()) {
// trim any trailing dot
if (!hostFromURI.IsEmpty() && hostFromURI.Last() == '.')
hostFromURI.Truncate(hostFromURI.Length() - 1);
if (NS_FAILED(rv)) {
COOKIE_LOGFAILURE(GET_COOKIE, aHostURI, nsnull, "invalid host/path from URI");
return;
}
// check default prefs
PRUint32 cookieStatus = CheckPrefs(aHostURI, aChannel, baseDomain,
isIPAddress, nsnull);
requireHostMatch, nsnull);
// for GetCookie(), we don't fire rejection notifications.
switch (cookieStatus) {
case STATUS_REJECTED:
@ -1481,7 +1500,7 @@ PRBool
nsCookieService::SetCookieInternal(nsIURI *aHostURI,
nsIChannel *aChannel,
const nsCString &aBaseDomain,
PRBool aIsIPAddress,
PRBool aRequireHostMatch,
nsDependentCString &aCookieHeader,
PRInt64 aServerTime,
PRBool aFromHttp)
@ -1519,7 +1538,7 @@ nsCookieService::SetCookieInternal(nsIURI *aHostURI,
}
// domain & path checks
if (!CheckDomain(cookieAttributes, aHostURI, aBaseDomain, aIsIPAddress)) {
if (!CheckDomain(cookieAttributes, aHostURI, aBaseDomain, aRequireHostMatch)) {
COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader, "failed the domain tests");
return newCookie;
}
@ -1920,55 +1939,108 @@ nsCookieService::ParseAttributes(nsDependentCString &aCookieHeader,
* private domain & permission compliance enforcement functions
******************************************************************************/
// Get the base domain for aHostURI; e.g. for "www.bbc.co.uk", this would be
// "bbc.co.uk". Only properly-formed URI's are tolerated, though a trailing
// dot may be present (and will be stripped). If aHostURI is an IP address,
// an alias such as 'localhost', an eTLD such as 'co.uk', or the empty string,
// aBaseDomain will be the exact host, and aRequireHostMatch will be true to
// indicate that substring matches should not be performed.
nsresult
nsCookieService::GetBaseDomain(nsIURI *aHostURI,
nsCString &aBaseDomain,
PRBool &aIsIPAddress)
PRBool &aRequireHostMatch)
{
// get the base domain for the host URI.
// e.g. for "www.bbc.co.uk", this would be "bbc.co.uk".
// get the base domain. this will fail if the host contains a leading dot,
// more than one trailing dot, or is otherwise malformed.
nsresult rv = mTLDService->GetBaseDomain(aHostURI, 0, aBaseDomain);
aIsIPAddress = rv == NS_ERROR_HOST_IS_IP_ADDRESS;
if (rv == NS_ERROR_HOST_IS_IP_ADDRESS ||
rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
// address is either an IP address or an alias such as 'localhost'.
// use the host as a key in such cases.
aRequireHostMatch = rv == NS_ERROR_HOST_IS_IP_ADDRESS ||
rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS;
if (aRequireHostMatch) {
// aHostURI is either an IP address, an alias such as 'localhost', an eTLD
// such as 'co.uk', or the empty string. use the host as a key in such
// cases.
rv = aHostURI->GetAsciiHost(aBaseDomain);
}
NS_ENSURE_SUCCESS(rv, rv);
// trim trailing dots
aBaseDomain.Trim(".");
if (aBaseDomain.IsEmpty())
return NS_ERROR_INVALID_ARG;
// aHost (and thus aBaseDomain) may contain a trailing dot; if so, trim it.
if (!aBaseDomain.IsEmpty() && aBaseDomain.Last() == '.')
aBaseDomain.Truncate(aBaseDomain.Length() - 1);
// block any URIs without a host that aren't file:// URIs.
if (aBaseDomain.IsEmpty()) {
PRBool isFileURI = PR_FALSE;
aHostURI->SchemeIs("file", &isFileURI);
if (!isFileURI)
return NS_ERROR_INVALID_ARG;
}
aIsIPAddress = PR_FALSE;
return NS_OK;
}
nsresult
// Get the base domain for aHost; e.g. for "www.bbc.co.uk", this would be
// "bbc.co.uk". This is done differently than GetBaseDomain(): it is assumed
// that aHost is already normalized, and it may contain a leading dot
// (indicating that it represents a domain). A trailing dot must not be present.
// If aHost is an IP address, an alias such as 'localhost', an eTLD such as
// 'co.uk', or the empty string, aBaseDomain will be the exact host, and a
// leading dot will be treated as an error.
nsresult
nsCookieService::GetBaseDomainFromHost(const nsACString &aHost,
nsCString &aBaseDomain)
{
// trim leading and trailing dots
nsCAutoString host(aHost);
host.Trim(".");
if (host.IsEmpty())
// aHost must not contain a trailing dot, or be the string '.'.
if (!aHost.IsEmpty() && aHost.Last() == '.')
return NS_ERROR_INVALID_ARG;
// get the base domain for the host.
// e.g. for "www.bbc.co.uk", this would be "bbc.co.uk".
// aHost may contain a leading dot; if so, strip it now.
nsDependentCString host(aHost);
PRBool domain = !host.IsEmpty() && host.First() == '.';
if (domain)
host.Rebind(host.BeginReading() + 1, host.EndReading());
// get the base domain. this will fail if the host contains a leading dot,
// more than one trailing dot, or is otherwise malformed.
nsresult rv = mTLDService->GetBaseDomainFromHost(host, 0, aBaseDomain);
if (rv == NS_ERROR_HOST_IS_IP_ADDRESS ||
rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
// address is either an IP address or an alias such as 'localhost'.
// use the host as a key in such cases.
// aHost is either an IP address, an alias such as 'localhost', an eTLD
// such as 'co.uk', or the empty string. use the host as a key in such
// cases; however, we reject any such hosts with a leading dot, since it
// doesn't make sense for them to be domain cookies.
if (domain)
return NS_ERROR_INVALID_ARG;
aBaseDomain = host;
return NS_OK;
}
return rv;
}
// Normalizes the given hostname, component by component. ASCII/ACE
// components are lower-cased, and UTF-8 components are normalized per
// RFC 3454 and converted to ACE. Any trailing dot is stripped.
nsresult
nsCookieService::NormalizeHost(nsCString &aHost)
{
if (!IsASCII(aHost)) {
nsCAutoString host;
nsresult rv = mIDNService->ConvertUTF8toACE(aHost, host);
if (NS_FAILED(rv))
return rv;
aHost = host;
}
// Only strip the trailing dot if it wouldn't result in the empty string;
// in that case, treat it like a leading dot.
if (aHost.Length() > 1 && aHost.Last() == '.')
aHost.Truncate(aHost.Length() - 1);
ToLowerCase(aHost);
return NS_OK;
}
// returns PR_TRUE if 'a' is equal to or a subdomain of 'b',
// assuming no leading or trailing dots are present.
static inline PRBool IsSubdomainOf(const nsCString &a, const nsCString &b)
@ -1982,7 +2054,7 @@ static inline PRBool IsSubdomainOf(const nsCString &a, const nsCString &b)
PRBool
nsCookieService::IsForeign(const nsCString &aBaseDomain,
PRBool aHostIsIPAddress,
PRBool aRequireHostMatch,
nsIURI *aFirstURI)
{
nsCAutoString firstHost;
@ -1990,19 +2062,19 @@ nsCookieService::IsForeign(const nsCString &aBaseDomain,
// assume foreign
return PR_TRUE;
}
// trim trailing dots
firstHost.Trim(".");
// if we're dealing with IP addresses, require an exact match. this
// eliminates any chance of IP address funkiness (e.g. the alias 127.1
// domain-matching 99.54.127.1). note that the base domain parameter will be
// equivalent to the host IP in this case.
if (aHostIsIPAddress)
// trim any trailing dot
if (!firstHost.IsEmpty() && firstHost.Last() == '.')
firstHost.Truncate(firstHost.Length() - 1);
// check whether the host is either an IP address, an alias such as
// 'localhost', an eTLD such as 'co.uk', or the empty string. in these
// cases, require an exact string match for the domain. note that the base
// domain parameter will be equivalent to the host in this case.
if (aRequireHostMatch)
return !firstHost.Equals(aBaseDomain);
// ensure the originating domain is also derived from the host's base domain.
// note that if the host is an alias such as 'localhost', the base domain
// parameter will also be 'localhost', and this comparison will work.
return !IsSubdomainOf(firstHost, aBaseDomain);
}
@ -2010,7 +2082,7 @@ PRUint32
nsCookieService::CheckPrefs(nsIURI *aHostURI,
nsIChannel *aChannel,
const nsCString &aBaseDomain,
PRBool aIsIPAddress,
PRBool aRequireHostMatch,
const char *aCookieHeader)
{
nsresult rv;
@ -2057,7 +2129,7 @@ nsCookieService::CheckPrefs(nsIURI *aHostURI,
nsCOMPtr<nsIURI> firstURI;
rv = mPermissionService->GetOriginatingURI(aChannel, getter_AddRefs(firstURI));
if (NS_FAILED(rv) || IsForeign(aBaseDomain, aIsIPAddress, firstURI)) {
if (NS_FAILED(rv) || IsForeign(aBaseDomain, aRequireHostMatch, firstURI)) {
COOKIE_LOGFAILURE(aCookieHeader ? SET_COOKIE : GET_COOKIE, aHostURI, aCookieHeader, "originating server test failed");
return STATUS_REJECTED;
}
@ -2072,15 +2144,15 @@ PRBool
nsCookieService::CheckDomain(nsCookieAttributes &aCookieAttributes,
nsIURI *aHostURI,
const nsCString &aBaseDomain,
PRBool aIsIPAddress)
PRBool aRequireHostMatch)
{
// get host from aHostURI
nsCAutoString hostFromURI;
aHostURI->GetAsciiHost(hostFromURI);
// trim trailing dots
hostFromURI.Trim(".");
NS_ASSERTION(!hostFromURI.IsEmpty(), "empty host");
// trim any trailing dot
if (!hostFromURI.IsEmpty() && hostFromURI.Last() == '.')
hostFromURI.Truncate(hostFromURI.Length() - 1);
// if a domain is given, check the host has permission
if (!aCookieAttributes.host.IsEmpty()) {
@ -2091,12 +2163,12 @@ nsCookieService::CheckDomain(nsCookieAttributes &aCookieAttributes,
// switch to lowercase now, to avoid case-insensitive compares everywhere
ToLowerCase(aCookieAttributes.host);
// check whether the host is an IP address, and leave the cookie as
// a non-domain one. this will require an exact host match for the cookie,
// so we eliminate any chance of IP address funkiness (e.g. the alias 127.1
// domain-matching 99.54.127.1). bug 105917 originally noted the
// requirement to deal with IP addresses.
if (aIsIPAddress)
// check whether the host is either an IP address, an alias such as
// 'localhost', an eTLD such as 'co.uk', or the empty string. in these
// cases, require an exact string match for the domain, and leave the cookie
// as a non-domain one. bug 105917 originally noted the requirement to deal
// with IP addresses.
if (aRequireHostMatch)
return hostFromURI.Equals(aCookieAttributes.host);
// ensure the proposed domain is derived from the base domain; and also
@ -2468,12 +2540,14 @@ NS_IMETHODIMP
nsCookieService::CountCookiesFromHost(const nsACString &aHost,
PRUint32 *aCountFromHost)
{
// first, normalize the hostname, and fail if it contains illegal characters.
nsCAutoString host(aHost);
nsresult rv = NormalizeHost(host);
NS_ENSURE_SUCCESS(rv, rv);
nsCAutoString baseDomain;
nsresult rv = GetBaseDomainFromHost(aHost, baseDomain);
if (NS_FAILED(rv)) {
*aCountFromHost = 0;
return NS_OK;
}
rv = GetBaseDomainFromHost(host, baseDomain);
NS_ENSURE_SUCCESS(rv, rv);
// we don't care about finding the oldest cookie here, so disable the search
nsEnumerationData data(PR_Now() / PR_USEC_PER_SEC, LL_MININT);
@ -2487,10 +2561,14 @@ NS_IMETHODIMP
nsCookieService::GetCookiesFromHost(const nsACString &aHost,
nsISimpleEnumerator **aEnumerator)
{
// first, normalize the hostname, and fail if it contains illegal characters.
nsCAutoString host(aHost);
nsresult rv = NormalizeHost(host);
NS_ENSURE_SUCCESS(rv, rv);
nsCAutoString baseDomain;
nsresult rv = GetBaseDomainFromHost(aHost, baseDomain);
if (NS_FAILED(rv))
return NS_NewEmptyEnumerator(aEnumerator);
rv = GetBaseDomainFromHost(host, baseDomain);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMArray<nsICookie> cookieList(mMaxCookiesPerHost);
PRInt64 currentTime = PR_Now() / PR_USEC_PER_SEC;

Просмотреть файл

@ -62,6 +62,7 @@ struct nsEnumerationData;
class nsICookiePermission;
class nsIEffectiveTLDService;
class nsIIDNService;
class nsIPrefBranch;
class nsIObserverService;
class nsIURI;
@ -168,20 +169,21 @@ class nsCookieService : public nsICookieService
nsresult CreateTable();
void CloseDB();
nsresult Read();
nsresult GetBaseDomain(nsIURI *aHostURI, nsCString &aBaseDomain, PRBool &aIsIPAddress);
nsresult NormalizeHost(nsCString &aHost);
nsresult GetBaseDomain(nsIURI *aHostURI, nsCString &aBaseDomain, PRBool &aRequireHostMatch);
nsresult GetBaseDomainFromHost(const nsACString &aHost, nsCString &aBaseDomain);
void GetCookieInternal(nsIURI *aHostURI, nsIChannel *aChannel, PRBool aHttpBound, char **aCookie);
nsresult SetCookieStringInternal(nsIURI *aHostURI, nsIPrompt *aPrompt, const char *aCookieHeader, const char *aServerTime, nsIChannel *aChannel, PRBool aFromHttp);
PRBool SetCookieInternal(nsIURI *aHostURI, nsIChannel *aChannel, const nsCString& aBaseDomain, PRBool aIsIPAddress, nsDependentCString &aCookieHeader, PRInt64 aServerTime, PRBool aFromHttp);
PRBool SetCookieInternal(nsIURI *aHostURI, nsIChannel *aChannel, const nsCString& aBaseDomain, PRBool aRequireHostMatch, nsDependentCString &aCookieHeader, PRInt64 aServerTime, PRBool aFromHttp);
void AddInternal(const nsCString& aBaseDomain, nsCookie *aCookie, PRInt64 aCurrentTimeInUsec, nsIURI *aHostURI, const char *aCookieHeader, PRBool aFromHttp);
void RemoveCookieFromList(const nsListIter &aIter);
PRBool AddCookieToList(const nsCString& aBaseDomain, nsCookie *aCookie, PRBool aWriteToDB = PR_TRUE);
void UpdateCookieInList(nsCookie *aCookie, PRInt64 aLastAccessed);
static PRBool GetTokenValue(nsASingleFragmentCString::const_char_iterator &aIter, nsASingleFragmentCString::const_char_iterator &aEndIter, nsDependentCSubstring &aTokenString, nsDependentCSubstring &aTokenValue, PRBool &aEqualsFound);
static PRBool ParseAttributes(nsDependentCString &aCookieHeader, nsCookieAttributes &aCookie);
PRBool IsForeign(const nsCString &aBaseDomain, PRBool aHostIsIPAddress, nsIURI *aFirstURI);
PRUint32 CheckPrefs(nsIURI *aHostURI, nsIChannel *aChannel, const nsCString &aBaseDomain, PRBool aIsIPAddress, const char *aCookieHeader);
PRBool CheckDomain(nsCookieAttributes &aCookie, nsIURI *aHostURI, const nsCString &aBaseDomain, PRBool aIsIPAddress);
PRBool IsForeign(const nsCString &aBaseDomain, PRBool aRequireHostMatch, nsIURI *aFirstURI);
PRUint32 CheckPrefs(nsIURI *aHostURI, nsIChannel *aChannel, const nsCString &aBaseDomain, PRBool aRequireHostMatch, const char *aCookieHeader);
PRBool CheckDomain(nsCookieAttributes &aCookie, nsIURI *aHostURI, const nsCString &aBaseDomain, PRBool aRequireHostMatch);
static PRBool CheckPath(nsCookieAttributes &aCookie, nsIURI *aHostURI);
static PRBool GetExpiry(nsCookieAttributes &aCookie, PRInt64 aServerTime, PRInt64 aCurrentTime);
void RemoveAllFromMemory();
@ -196,6 +198,7 @@ class nsCookieService : public nsICookieService
nsCOMPtr<nsIObserverService> mObserverService;
nsCOMPtr<nsICookiePermission> mPermissionService;
nsCOMPtr<nsIEffectiveTLDService> mTLDService;
nsCOMPtr<nsIIDNService> mIDNService;
// we have two separate DB states: one for normal browsing and one for
// private browsing, switching between them as appropriate. this state