Bug 295994 part 1 - add cookieservice accessors for individual cookies by name, obeying privacy preferences (second try, I reversed the string logic in nsCookieService::SetCookieInternal the first time - r=dwitte+darin

git-svn-id: svn://10.0.0.236/trunk@180276 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
bsmedberg%covad.net
2005-09-15 11:03:09 +00:00
parent 78376c88cb
commit 2fd763dd14
4 changed files with 264 additions and 72 deletions

View File

@@ -53,6 +53,7 @@ SDK_XPIDLSRCS = \
XPIDLSRCS = \
nsICookieService.idl \
nsICookieServiceInternal.idl \
nsICookie2.idl \
nsICookieConsent.idl \
nsICookieManager2.idl \

View File

@@ -0,0 +1,79 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla 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/MPL/
*
* 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 the Mozilla networking engine (necko).
*
* The Initial Developer of the Original Code is
* Benjamin Smedberg <benjamin@smedbergs.us>.
*
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsICookieService.idl"
/**
* Set and get cookie values using the security policy.
*
* @status INTERNAL This interface is not frozen and is subject to change
* at any time.
*/
[scriptable, uuid(7cf4b3d9-bdc6-4763-b912-47c526aacc37)]
interface nsICookieServiceInternal : nsICookieService
{
/**
* Set a cookie name/value pair, performing privacy checks as appropriate.
*
* @see nsICookieService.setCookieString
* @see nsICookieManager2.add
*/
void setCookieValue(in nsIURI aURI,
in nsIChannel aChannel,
in AUTF8String aDomain,
in AUTF8String aPath,
in ACString aName,
in ACString aValue,
in boolean aIsSession,
in PRInt64 aExpiry);
/**
* Get a single cookie associated with the URI. This returns the first
* cookie which matches, if there are multiple cookies with the same name.
*
* @param aURI
* the URI of the document for which cookies are being queried.
* @param aChannel
* the channel used to load the document. this parameter may be null,
* but it is strongly recommended that a non-null value be provided to
* ensure that the cookie privacy preferences are honored.
*
* @return the resulting cookie value
* @throws NS_ERROR_NOT_AVAILABLE if no cookie with this name has been set.
*/
ACString getCookieValue(in nsIURI aURI, in nsIChannel aChannel, in ACString aCookieName);
};

View File

@@ -1,5 +1,5 @@
// vim:ts=2:sw=2:et:
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
@@ -371,8 +371,9 @@ nsCookieService::GetSingleton()
* public methods
******************************************************************************/
NS_IMPL_ISUPPORTS5(nsCookieService,
NS_IMPL_ISUPPORTS6(nsCookieService,
nsICookieService,
nsICookieServiceInternal,
nsICookieManager,
nsICookieManager2,
nsIObserver,
@@ -481,36 +482,19 @@ nsCookieService::Observe(nsISupports *aSubject,
return NS_OK;
}
NS_IMETHODIMP
nsCookieService::GetCookieString(nsIURI *aHostURI,
nsIChannel *aChannel,
char **aCookie)
{
// try to determine first party URI
nsCOMPtr<nsIURI> firstURI;
if (aChannel) {
nsCOMPtr<nsIHttpChannelInternal> httpInternal = do_QueryInterface(aChannel);
if (httpInternal)
httpInternal->GetDocumentURI(getter_AddRefs(firstURI));
}
return GetCookieStringFromHttp(aHostURI, firstURI, aChannel, aCookie);
}
// helper function for GetCookieStringFromHttp
static inline PRBool ispathdelimiter(char c) { return c == '/' || c == '?' || c == '#' || c == ';'; }
NS_IMETHODIMP
nsCookieService::GetCookieStringFromHttp(nsIURI *aHostURI,
nsIURI *aFirstURI,
nsIChannel *aChannel,
char **aCookie)
void
nsCookieService::GetCookieList(nsIURI *aHostURI,
nsIURI *aFirstURI,
nsIChannel *aChannel,
const nsACString *aName,
nsAutoVoidArray &aResult)
{
*aCookie = nsnull;
if (!aHostURI) {
COOKIE_LOGFAILURE(GET_COOKIE, nsnull, nsnull, "host URI is null");
return NS_OK;
return;
}
// check default prefs
@@ -520,7 +504,7 @@ nsCookieService::GetCookieStringFromHttp(nsIURI *aHostURI,
switch (cookieStatus) {
case nsICookie::STATUS_REJECTED:
case STATUS_REJECTED_WITH_ERROR:
return NS_OK;
return;
}
// get host and path from the nsIURI
@@ -530,7 +514,7 @@ nsCookieService::GetCookieStringFromHttp(nsIURI *aHostURI,
if (NS_FAILED(aHostURI->GetAsciiHost(hostFromURI)) ||
NS_FAILED(aHostURI->GetPath(pathFromURI))) {
COOKIE_LOGFAILURE(GET_COOKIE, aHostURI, nsnull, "couldn't get host/path from URI");
return NS_OK;
return;
}
// trim trailing dots
hostFromURI.Trim(".");
@@ -548,7 +532,6 @@ nsCookieService::GetCookieStringFromHttp(nsIURI *aHostURI,
}
nsCookie *cookie;
nsAutoVoidArray foundCookieList;
nsInt64 currentTime = NOW_IN_SECONDS;
const char *currentDot = hostFromURI.get();
const char *nextDot = currentDot + 1;
@@ -559,6 +542,11 @@ nsCookieService::GetCookieStringFromHttp(nsIURI *aHostURI,
nsCookieEntry *entry = mHostTable.GetEntry(currentDot);
cookie = entry ? entry->Head() : nsnull;
for (; cookie; cookie = cookie->Next()) {
// check the cookie name, if appropriate
if (aName && !aName->Equals(cookie->Name())) {
continue;
}
// if the cookie is secure and the host scheme isn't, we can't send it
if (cookie->IsSecure() && !isSecure) {
continue;
@@ -594,7 +582,7 @@ nsCookieService::GetCookieStringFromHttp(nsIURI *aHostURI,
}
// all checks passed - add to list and update lastAccessed stamp of cookie
foundCookieList.AppendElement(cookie);
aResult.AppendElement(cookie);
cookie->SetLastAccessed(currentTime);
}
@@ -607,12 +595,70 @@ nsCookieService::GetCookieStringFromHttp(nsIURI *aHostURI,
// return cookies in order of path length; longest to shortest.
// this is required per RFC2109. if cookies match in length,
// then sort by creation time (see bug 236772).
foundCookieList.Sort(compareCookiesForSending, nsnull);
aResult.Sort(compareCookiesForSending, nsnull);
}
NS_IMETHODIMP
nsCookieService::GetCookieValue(nsIURI *aHostURI,
nsIChannel *aChannel,
const nsACString& aName,
nsACString& aResult)
{
aResult.Truncate();
// try to determine first party URI
nsCOMPtr<nsIURI> firstURI;
if (aChannel) {
nsCOMPtr<nsIHttpChannelInternal> httpInternal = do_QueryInterface(aChannel);
if (httpInternal)
httpInternal->GetDocumentURI(getter_AddRefs(firstURI));
}
nsAutoVoidArray foundCookieList;
GetCookieList(aHostURI, firstURI, aChannel, &aName,
foundCookieList);
if (!foundCookieList.Count())
return NS_ERROR_NOT_AVAILABLE;
nsCookie *cookie = NS_STATIC_CAST(nsCookie*, foundCookieList[0]);
aResult.Assign(cookie->Value());
return NS_OK;
}
NS_IMETHODIMP
nsCookieService::GetCookieString(nsIURI *aHostURI,
nsIChannel *aChannel,
char **aCookie)
{
// try to determine first party URI
nsCOMPtr<nsIURI> firstURI;
if (aChannel) {
nsCOMPtr<nsIHttpChannelInternal> httpInternal = do_QueryInterface(aChannel);
if (httpInternal)
httpInternal->GetDocumentURI(getter_AddRefs(firstURI));
}
return GetCookieStringFromHttp(aHostURI, firstURI, aChannel, aCookie);
}
NS_IMETHODIMP
nsCookieService::GetCookieStringFromHttp(nsIURI *aHostURI,
nsIURI *aFirstURI,
nsIChannel *aChannel,
char **aCookie)
{
*aCookie = nsnull;
nsAutoVoidArray foundCookieList;
GetCookieList(aHostURI, aFirstURI, aChannel, nsnull,
foundCookieList);
nsCAutoString cookieData;
PRInt32 count = foundCookieList.Count();
for (PRInt32 i = 0; i < count; ++i) {
cookie = NS_STATIC_CAST(nsCookie*, foundCookieList.ElementAt(i));
nsCookie *cookie = NS_STATIC_CAST(nsCookie*, foundCookieList.ElementAt(i));
// check if we have anything to write
if (!cookie->Name().IsEmpty() || !cookie->Value().IsEmpty()) {
@@ -642,6 +688,52 @@ nsCookieService::GetCookieStringFromHttp(nsIURI *aHostURI,
return NS_OK;
}
NS_IMETHODIMP
nsCookieService::SetCookieValue(nsIURI *aHostURI,
nsIChannel *aChannel,
const nsACString& aDomain,
const nsACString& aPath,
const nsACString& aName,
const nsACString& aValue,
PRBool aIsSession,
PRInt64 aExpiry)
{
// try to determine first party URI
nsCOMPtr<nsIURI> firstURI;
if (aChannel) {
nsCOMPtr<nsIHttpChannelInternal> httpInternal = do_QueryInterface(aChannel);
if (httpInternal)
httpInternal->GetDocumentURI(getter_AddRefs(firstURI));
}
// check default prefs
nsCookiePolicy cookiePolicy = nsICookie::POLICY_UNKNOWN;
nsCookieStatus cookieStatus = CheckPrefs(aHostURI, firstURI, aChannel, "", cookiePolicy);
// fire a notification if cookie was rejected (but not if there was an error)
switch (cookieStatus) {
case nsICookie::STATUS_REJECTED:
NotifyRejected(aHostURI);
case STATUS_REJECTED_WITH_ERROR:
return NS_OK;
}
nsCookieAttributes attributes;
attributes.name = aName;
attributes.value = aValue;
attributes.host = aDomain;
attributes.path = aPath;
attributes.expiryTime = aExpiry;
attributes.isSession = aIsSession;
attributes.isSecure = PR_FALSE;
aHostURI->SchemeIs("https", &attributes.isSecure);
CheckAndAdd(aHostURI, aChannel, attributes,
cookieStatus, cookiePolicy, EmptyCString());
return NS_OK;
}
NS_IMETHODIMP
nsCookieService::SetCookieString(nsIURI *aHostURI,
nsIPrompt *aPrompt,
@@ -1141,14 +1233,10 @@ PRBool
nsCookieService::SetCookieInternal(nsIURI *aHostURI,
nsIChannel *aChannel,
nsDependentCString &aCookieHeader,
nsInt64 aServerTime,
nsCookieStatus aStatus,
nsCookiePolicy aPolicy)
nsInt64 aServerTime,
nsCookieStatus aStatus,
nsCookiePolicy aPolicy)
{
// keep a |const char*| version of the unmodified aCookieHeader,
// for logging purposes
const char *cookieHeader = aCookieHeader.get();
// create a stack-based nsCookieAttributes, to store all the
// attributes parsed from the cookie
nsCookieAttributes cookieAttributes;
@@ -1156,16 +1244,13 @@ nsCookieService::SetCookieInternal(nsIURI *aHostURI,
// init expiryTime such that session cookies won't prematurely expire
cookieAttributes.expiryTime = LL_MAXINT;
// newCookie says whether there are multiple cookies in the header; so we can handle them separately.
// after this function, we don't need the cookieHeader string for processing this cookie anymore;
// so this function uses it as an outparam to point to the next cookie, if there is one.
const PRBool newCookie = ParseAttributes(aCookieHeader, cookieAttributes);
// aCookieHeader is an in/out param to point to the next cookie, if
// there is one. Save the present value to pass to CheckAndAdd
nsDependentCString savedCookieHeader(aCookieHeader);
// reject cookie if it's over the size limit, per RFC2109
if ((cookieAttributes.name.Length() + cookieAttributes.value.Length()) > kMaxBytesPerCookie) {
COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, cookieHeader, "cookie too big (> 4kb)");
return newCookie;
}
// newCookie says whether there are multiple cookies in the header;
// so we can handle them separately.
const PRBool newCookie = ParseAttributes(aCookieHeader, cookieAttributes);
// calculate expiry time of cookie. we need to pass in cookieStatus, since
// the cookie may have been downgraded to a session cookie by p3p.
@@ -1173,30 +1258,52 @@ nsCookieService::SetCookieInternal(nsIURI *aHostURI,
cookieAttributes.isSession = GetExpiry(cookieAttributes, aServerTime,
currentTime, aStatus);
CheckAndAdd(aHostURI, aChannel, cookieAttributes,
aStatus, aPolicy, savedCookieHeader);
return newCookie;
}
void
nsCookieService::CheckAndAdd(nsIURI *aHostURI,
nsIChannel *aChannel,
nsCookieAttributes &aAttributes,
nsCookieStatus aStatus,
const nsCookiePolicy aPolicy,
const nsAFlatCString &aCookieHeader)
{
// reject cookie if it's over the size limit, per RFC2109
if ((aAttributes.name.Length() + aAttributes.value.Length()) > kMaxBytesPerCookie) {
COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader, "cookie too big (> 4kb)");
return;
}
// domain & path checks
if (!CheckDomain(cookieAttributes, aHostURI)) {
COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, cookieHeader, "failed the domain tests");
return newCookie;
if (!CheckDomain(aAttributes, aHostURI)) {
COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader, "failed the domain tests");
return;
}
if (!CheckPath(cookieAttributes, aHostURI)) {
COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, cookieHeader, "failed the path tests");
return newCookie;
if (!CheckPath(aAttributes, aHostURI)) {
COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader, "failed the path tests");
return;
}
const nsInt64 currentTime = NOW_IN_SECONDS;
// create a new nsCookie and copy attributes
nsRefPtr<nsCookie> cookie =
nsCookie::Create(cookieAttributes.name,
cookieAttributes.value,
cookieAttributes.host,
cookieAttributes.path,
cookieAttributes.expiryTime,
nsCookie::Create(aAttributes.name,
aAttributes.value,
aAttributes.host,
aAttributes.path,
aAttributes.expiryTime,
currentTime,
cookieAttributes.isSession,
cookieAttributes.isSecure,
aAttributes.isSession,
aAttributes.isSecure,
aStatus,
aPolicy);
if (!cookie) {
return newCookie;
return;
}
// check permissions from site permission list, or ask the user,
@@ -1208,23 +1315,22 @@ nsCookieService::SetCookieInternal(nsIURI *aHostURI,
mPermissionService->CanSetCookie(aHostURI,
aChannel,
NS_STATIC_CAST(nsICookie2*, NS_STATIC_CAST(nsCookie*, cookie)),
&cookieAttributes.isSession,
&cookieAttributes.expiryTime.mValue,
&aAttributes.isSession,
&aAttributes.expiryTime.mValue,
&permission);
if (!permission) {
COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, cookieHeader, "cookie rejected by permission manager");
COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader, "cookie rejected by permission manager");
NotifyRejected(aHostURI);
return newCookie;
return;
}
// update isSession and expiry attributes, in case they changed
cookie->SetIsSession(cookieAttributes.isSession);
cookie->SetExpiry(cookieAttributes.expiryTime);
cookie->SetIsSession(aAttributes.isSession);
cookie->SetExpiry(aAttributes.expiryTime);
}
// add the cookie to the list. AddInternal() takes care of logging.
AddInternal(cookie, NOW_IN_SECONDS, aHostURI, cookieHeader);
return newCookie;
AddInternal(cookie, NOW_IN_SECONDS, aHostURI, aCookieHeader.get());
}
// this is a backend function for adding a cookie to the list, via SetCookie.

View File

@@ -40,7 +40,7 @@
#ifndef nsCookieService_h__
#define nsCookieService_h__
#include "nsICookieService.h"
#include "nsICookieServiceInternal.h"
#include "nsICookieManager.h"
#include "nsICookieManager2.h"
#include "nsIObserver.h"
@@ -55,6 +55,9 @@
struct nsCookieAttributes;
struct nsListIter;
struct nsEnumerationData;
class nsAutoVoidArray;
class nsIPrefBranch;
class nsICookieConsent;
class nsICookiePermission;
@@ -149,7 +152,7 @@ class nsCookieEntry : public PLDHashEntryHdr
* class declaration
******************************************************************************/
class nsCookieService : public nsICookieService
class nsCookieService : public nsICookieServiceInternal
, public nsICookieManager2
, public nsIObserver
, public nsSupportsWeakReference
@@ -159,6 +162,7 @@ class nsCookieService : public nsICookieService
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
NS_DECL_NSICOOKIESERVICE
NS_DECL_NSICOOKIESERVICEINTERNAL
NS_DECL_NSICOOKIEMANAGER
NS_DECL_NSICOOKIEMANAGER2
@@ -171,7 +175,9 @@ class nsCookieService : public nsICookieService
void PrefChanged(nsIPrefBranch *aPrefBranch);
nsresult Read();
nsresult Write();
void GetCookieList(nsIURI *aHostURI, nsIURI *aFirstURI, nsIChannel *aChannel, const nsACString *aName, nsAutoVoidArray &aResult);
PRBool SetCookieInternal(nsIURI *aHostURI, nsIChannel *aChannel, nsDependentCString &aCookieHeader, nsInt64 aServerTime, nsCookieStatus aStatus, nsCookiePolicy aPolicy);
void CheckAndAdd(nsIURI *aHostURI, nsIChannel *aChannel, nsCookieAttributes &aAttributes, nsCookieStatus aStatus, nsCookiePolicy aPolicy, const nsAFlatCString &aCookieHeader);
void AddInternal(nsCookie *aCookie, nsInt64 aCurrentTime, nsIURI *aHostURI, const char *aCookieHeader);
void RemoveCookieFromList(nsListIter &aIter);
PRBool AddCookieToList(nsCookie *aCookie);