Follow up to bug 451392, add hgToolsTag to older configs
[mozilla-1.9.git] / caps / src / nsPrincipal.cpp
blob330821245a665cbfab14affcfa16a62a7f41bc2c
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et tw=80: */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 2003
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
24 * Christopher A. Aillon <christopher@aillon.com>
25 * Giorgio Maone <g.maone@informaction.com>
27 * Alternatively, the contents of this file may be used under the terms of
28 * either the GNU General Public License Version 2 or later (the "GPL"), or
29 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
41 #include "nscore.h"
42 #include "nsScriptSecurityManager.h"
43 #include "nsString.h"
44 #include "nsReadableUtils.h"
45 #include "plstr.h"
46 #include "nsCRT.h"
47 #include "nsIURI.h"
48 #include "nsIFileURL.h"
49 #include "nsIProtocolHandler.h"
50 #include "nsNetUtil.h"
51 #include "nsJSPrincipals.h"
52 #include "nsVoidArray.h"
53 #include "nsHashtable.h"
54 #include "nsIObjectInputStream.h"
55 #include "nsIObjectOutputStream.h"
56 #include "nsIPrefBranch.h"
57 #include "nsIPrefService.h"
58 #include "nsIClassInfoImpl.h"
59 #include "nsDOMError.h"
61 #include "nsPrincipal.h"
63 static PRBool URIIsImmutable(nsIURI* aURI)
65 nsCOMPtr<nsIMutable> mutableObj(do_QueryInterface(aURI));
66 PRBool isMutable;
67 return
68 mutableObj &&
69 NS_SUCCEEDED(mutableObj->GetMutable(&isMutable)) &&
70 !isMutable;
73 // Static member variables
74 PRInt32 nsPrincipal::sCapabilitiesOrdinal = 0;
75 const char nsPrincipal::sInvalid[] = "Invalid";
78 NS_IMPL_QUERY_INTERFACE2_CI(nsPrincipal,
79 nsIPrincipal,
80 nsISerializable)
81 NS_IMPL_CI_INTERFACE_GETTER2(nsPrincipal,
82 nsIPrincipal,
83 nsISerializable)
85 NS_IMETHODIMP_(nsrefcnt)
86 nsPrincipal::AddRef()
88 NS_PRECONDITION(PRInt32(mJSPrincipals.refcount) >= 0, "illegal refcnt");
89 // XXXcaa does this need to be threadsafe? See bug 143559.
90 nsrefcnt count = PR_AtomicIncrement((PRInt32 *)&mJSPrincipals.refcount);
91 NS_LOG_ADDREF(this, count, "nsPrincipal", sizeof(*this));
92 return count;
95 NS_IMETHODIMP_(nsrefcnt)
96 nsPrincipal::Release()
98 NS_PRECONDITION(0 != mJSPrincipals.refcount, "dup release");
99 nsrefcnt count = PR_AtomicDecrement((PRInt32 *)&mJSPrincipals.refcount);
100 NS_LOG_RELEASE(this, count, "nsPrincipal");
101 if (count == 0) {
102 NS_DELETEXPCOM(this);
105 return count;
108 nsPrincipal::nsPrincipal()
109 : mCapabilities(nsnull),
110 mSecurityPolicy(nsnull),
111 mTrusted(PR_FALSE),
112 mInitialized(PR_FALSE),
113 mCodebaseImmutable(PR_FALSE),
114 mDomainImmutable(PR_FALSE)
118 nsresult
119 nsPrincipal::Init(const nsACString& aCertFingerprint,
120 const nsACString& aSubjectName,
121 const nsACString& aPrettyName,
122 nsISupports* aCert,
123 nsIURI *aCodebase)
125 NS_ENSURE_STATE(!mInitialized);
126 NS_ENSURE_ARG(!aCertFingerprint.IsEmpty() || aCodebase); // better have one of these.
128 mInitialized = PR_TRUE;
130 mCodebase = NS_TryToMakeImmutable(aCodebase);
131 mCodebaseImmutable = URIIsImmutable(mCodebase);
133 nsresult rv;
134 if (!aCertFingerprint.IsEmpty()) {
135 rv = SetCertificate(aCertFingerprint, aSubjectName, aPrettyName, aCert);
136 if (NS_SUCCEEDED(rv)) {
137 rv = mJSPrincipals.Init(this, mCert->fingerprint);
140 else {
141 nsCAutoString spec;
142 rv = mCodebase->GetSpec(spec);
143 if (NS_SUCCEEDED(rv)) {
144 rv = mJSPrincipals.Init(this, spec);
148 NS_ASSERTION(NS_SUCCEEDED(rv), "nsPrincipal::Init() failed");
150 return rv;
153 nsPrincipal::~nsPrincipal(void)
155 SetSecurityPolicy(nsnull);
156 delete mCapabilities;
159 NS_IMETHODIMP
160 nsPrincipal::GetJSPrincipals(JSContext *cx, JSPrincipals **jsprin)
162 NS_PRECONDITION(mJSPrincipals.nsIPrincipalPtr, "mJSPrincipals is uninitialized!");
164 JSPRINCIPALS_HOLD(cx, &mJSPrincipals);
165 *jsprin = &mJSPrincipals;
166 return NS_OK;
169 NS_IMETHODIMP
170 nsPrincipal::GetOrigin(char **aOrigin)
172 *aOrigin = nsnull;
174 nsCOMPtr<nsIURI> origin;
175 if (mCodebase) {
176 origin = NS_GetInnermostURI(mCodebase);
179 if (!origin) {
180 NS_ASSERTION(mCert, "No Domain or Codebase for a non-cert principal");
181 return NS_ERROR_FAILURE;
184 nsCAutoString hostPort;
186 // chrome: URLs don't have a meaningful origin, so make
187 // sure we just get the full spec for them.
188 // XXX this should be removed in favor of the solution in
189 // bug 160042.
190 PRBool isChrome;
191 nsresult rv = origin->SchemeIs("chrome", &isChrome);
192 if (NS_SUCCEEDED(rv) && !isChrome) {
193 rv = origin->GetHostPort(hostPort);
196 if (NS_SUCCEEDED(rv) && !isChrome) {
197 nsCAutoString scheme;
198 rv = origin->GetScheme(scheme);
199 NS_ENSURE_SUCCESS(rv, rv);
200 *aOrigin = ToNewCString(scheme + NS_LITERAL_CSTRING("://") + hostPort);
202 else {
203 // Some URIs (e.g., nsSimpleURI) don't support host. Just
204 // get the full spec.
205 nsCAutoString spec;
206 rv = origin->GetSpec(spec);
207 NS_ENSURE_SUCCESS(rv, rv);
208 *aOrigin = ToNewCString(spec);
211 return *aOrigin ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
214 NS_IMETHODIMP
215 nsPrincipal::GetSecurityPolicy(void** aSecurityPolicy)
217 if (mSecurityPolicy && mSecurityPolicy->IsInvalid())
218 SetSecurityPolicy(nsnull);
220 *aSecurityPolicy = (void *) mSecurityPolicy;
221 return NS_OK;
224 NS_IMETHODIMP
225 nsPrincipal::SetSecurityPolicy(void* aSecurityPolicy)
227 DomainPolicy *newPolicy = reinterpret_cast<DomainPolicy *>(aSecurityPolicy);
228 if (newPolicy)
229 newPolicy->Hold();
231 if (mSecurityPolicy)
232 mSecurityPolicy->Drop();
234 mSecurityPolicy = newPolicy;
235 return NS_OK;
238 NS_IMETHODIMP
239 nsPrincipal::Equals(nsIPrincipal *aOther, PRBool *aResult)
241 *aResult = PR_FALSE;
243 if (!aOther) {
244 NS_WARNING("Need a principal to compare this to!");
245 return NS_OK;
248 if (this != aOther) {
249 PRBool otherHasCert;
250 aOther->GetHasCertificate(&otherHasCert);
251 if (otherHasCert != (mCert != nsnull)) {
252 // One has a cert while the other doesn't. Not equal.
253 return NS_OK;
256 if (mCert) {
257 nsCAutoString str;
258 aOther->GetFingerprint(str);
259 *aResult = str.Equals(mCert->fingerprint);
261 // If either subject name is empty, just let the result stand (so that
262 // nsScriptSecurityManager::SetCanEnableCapability works), but if they're
263 // both non-empty, only claim equality if they're equal.
264 if (*aResult && !mCert->subjectName.IsEmpty()) {
265 // Check the other principal's subject name
266 aOther->GetSubjectName(str);
267 *aResult = str.Equals(mCert->subjectName) || str.IsEmpty();
270 if (!*aResult) {
271 return NS_OK;
274 // If either principal has no URI, it's the saved principal from
275 // preferences; in that case, test true. Do NOT test true if the two
276 // principals have URIs with different codebases.
277 nsCOMPtr<nsIURI> otherURI;
278 nsresult rv = aOther->GetURI(getter_AddRefs(otherURI));
279 if (NS_FAILED(rv)) {
280 *aResult = PR_FALSE;
281 return rv;
284 if (!otherURI || !mCodebase) {
285 return NS_OK;
288 // Fall through to the codebase comparison.
291 // Codebases are equal if they have the same origin.
292 *aResult =
293 NS_SUCCEEDED(nsScriptSecurityManager::CheckSameOriginPrincipal(this,
294 aOther,
295 PR_FALSE));
296 return NS_OK;
299 *aResult = PR_TRUE;
300 return NS_OK;
303 NS_IMETHODIMP
304 nsPrincipal::Subsumes(nsIPrincipal *aOther, PRBool *aResult)
306 return Equals(aOther, aResult);
309 static PRBool
310 URIIsLocalFile(nsIURI *aURI)
312 PRBool isFile;
313 nsCOMPtr<nsINetUtil> util = do_GetIOService();
315 return util && NS_SUCCEEDED(util->ProtocolHasFlags(aURI,
316 nsIProtocolHandler::URI_IS_LOCAL_FILE,
317 &isFile)) &&
318 isFile;
321 NS_IMETHODIMP
322 nsPrincipal::CheckMayLoad(nsIURI* aURI, PRBool aReport)
324 if (!nsScriptSecurityManager::SecurityCompareURIs(mCodebase, aURI)) {
325 if (nsScriptSecurityManager::GetStrictFileOriginPolicy() &&
326 URIIsLocalFile(aURI)) {
327 nsCOMPtr<nsIFileURL> fileURL(do_QueryInterface(aURI));
329 if (!URIIsLocalFile(mCodebase)) {
330 // If the codebase is not also a file: uri then forget it
331 // (don't want resource: principals in a file: doc)
333 // note: we're not de-nesting jar: uris here, we want to
334 // keep archive content bottled up in its own little island
336 if (aReport) {
337 nsScriptSecurityManager::ReportError(
338 nsnull, NS_LITERAL_STRING("CheckSameOriginError"), mCodebase, aURI);
341 return NS_ERROR_DOM_BAD_URI;
345 // pull out the internal files
347 nsCOMPtr<nsIFileURL> codebaseFileURL(do_QueryInterface(mCodebase));
348 nsCOMPtr<nsIFile> targetFile;
349 nsCOMPtr<nsIFile> codebaseFile;
350 PRBool targetIsDir;
352 // Make sure targetFile is not a directory (bug 209234)
353 // and that it exists w/out unescaping (bug 395343)
355 if (!codebaseFileURL || !fileURL ||
356 NS_FAILED(fileURL->GetFile(getter_AddRefs(targetFile))) ||
357 NS_FAILED(codebaseFileURL->GetFile(getter_AddRefs(codebaseFile))) ||
358 !targetFile || !codebaseFile ||
359 NS_FAILED(targetFile->Normalize()) ||
360 NS_FAILED(codebaseFile->Normalize()) ||
361 NS_FAILED(targetFile->IsDirectory(&targetIsDir)) ||
362 targetIsDir) {
363 if (aReport) {
364 nsScriptSecurityManager::ReportError(
365 nsnull, NS_LITERAL_STRING("CheckSameOriginError"), mCodebase, aURI);
368 return NS_ERROR_DOM_BAD_URI;
372 // If the file to be loaded is in a subdirectory of the codebase
373 // (or same-dir if codebase is not a directory) then it will
374 // inherit its codebase principal and be scriptable by that codebase.
376 PRBool codebaseIsDir;
377 PRBool contained = PR_FALSE;
378 nsresult rv = codebaseFile->IsDirectory(&codebaseIsDir);
379 if (NS_SUCCEEDED(rv) && codebaseIsDir) {
380 rv = codebaseFile->Contains(targetFile, PR_TRUE, &contained);
382 else {
383 nsCOMPtr<nsIFile> codebaseParent;
384 rv = codebaseFile->GetParent(getter_AddRefs(codebaseParent));
385 if (NS_SUCCEEDED(rv) && codebaseParent) {
386 rv = codebaseParent->Contains(targetFile, PR_TRUE, &contained);
390 if (NS_SUCCEEDED(rv) && contained) {
391 return NS_OK;
395 if (aReport) {
396 nsScriptSecurityManager::ReportError(
397 nsnull, NS_LITERAL_STRING("CheckSameOriginError"), mCodebase, aURI);
400 return NS_ERROR_DOM_BAD_URI;
403 return NS_OK;
406 NS_IMETHODIMP
407 nsPrincipal::CanEnableCapability(const char *capability, PRInt16 *result)
409 // If this principal is marked invalid, can't enable any capabilities
410 if (mCapabilities) {
411 nsCStringKey invalidKey(sInvalid);
412 if (mCapabilities->Exists(&invalidKey)) {
413 *result = nsIPrincipal::ENABLE_DENIED;
415 return NS_OK;
419 if (!mCert && !mTrusted) {
420 NS_ASSERTION(mInitialized, "Trying to enable a capability on an "
421 "uninitialized principal");
423 // If we are a non-trusted codebase principal, capabilities can not
424 // be enabled if the user has not set the pref allowing scripts to
425 // request enhanced capabilities; however, the file: and resource:
426 // schemes are special and may be able to get extra capabilities
427 // even with the pref disabled.
429 static const char pref[] = "signed.applets.codebase_principal_support";
430 nsCOMPtr<nsIPrefBranch> prefBranch =
431 do_GetService(NS_PREFSERVICE_CONTRACTID);
432 if (prefBranch) {
433 PRBool mightEnable;
434 nsresult rv = prefBranch->GetBoolPref(pref, &mightEnable);
435 if (NS_FAILED(rv) || !mightEnable) {
436 rv = mCodebase->SchemeIs("file", &mightEnable);
437 if (NS_FAILED(rv) || !mightEnable) {
438 rv = mCodebase->SchemeIs("resource", &mightEnable);
439 if (NS_FAILED(rv) || !mightEnable) {
440 *result = nsIPrincipal::ENABLE_DENIED;
442 return NS_OK;
449 const char *start = capability;
450 *result = nsIPrincipal::ENABLE_GRANTED;
451 for(;;) {
452 const char *space = PL_strchr(start, ' ');
453 PRInt32 len = space ? space - start : strlen(start);
454 nsCAutoString capString(start, len);
455 nsCStringKey key(capString);
456 PRInt16 value =
457 mCapabilities ? (PRInt16)NS_PTR_TO_INT32(mCapabilities->Get(&key)) : 0;
458 if (value == 0 || value == nsIPrincipal::ENABLE_UNKNOWN) {
459 // We don't know whether we can enable this capability,
460 // so we should ask the user.
461 value = nsIPrincipal::ENABLE_WITH_USER_PERMISSION;
464 if (value < *result) {
465 *result = value;
468 if (!space) {
469 break;
472 start = space + 1;
475 return NS_OK;
478 NS_IMETHODIMP
479 nsPrincipal::SetCanEnableCapability(const char *capability,
480 PRInt16 canEnable)
482 // If this principal is marked invalid, can't enable any capabilities
483 if (!mCapabilities) {
484 mCapabilities = new nsHashtable(7); // XXXbz gets bumped up to 16 anyway
485 NS_ENSURE_TRUE(mCapabilities, NS_ERROR_OUT_OF_MEMORY);
488 nsCStringKey invalidKey(sInvalid);
489 if (mCapabilities->Exists(&invalidKey)) {
490 return NS_OK;
493 if (PL_strcmp(capability, sInvalid) == 0) {
494 mCapabilities->Reset();
497 const char *start = capability;
498 for(;;) {
499 const char *space = PL_strchr(start, ' ');
500 int len = space ? space - start : strlen(start);
501 nsCAutoString capString(start, len);
502 nsCStringKey key(capString);
503 mCapabilities->Put(&key, NS_INT32_TO_PTR(canEnable));
504 if (!space) {
505 break;
508 start = space + 1;
511 return NS_OK;
514 NS_IMETHODIMP
515 nsPrincipal::IsCapabilityEnabled(const char *capability, void *annotation,
516 PRBool *result)
518 *result = PR_FALSE;
519 nsHashtable *ht = (nsHashtable *) annotation;
520 if (!ht) {
521 return NS_OK;
523 const char *start = capability;
524 for(;;) {
525 const char *space = PL_strchr(start, ' ');
526 int len = space ? space - start : strlen(start);
527 nsCAutoString capString(start, len);
528 nsCStringKey key(capString);
529 *result = (ht->Get(&key) == (void *) AnnotationEnabled);
530 if (!*result) {
531 // If any single capability is not enabled, then return false.
532 return NS_OK;
535 if (!space) {
536 return NS_OK;
539 start = space + 1;
542 return NS_OK;
545 NS_IMETHODIMP
546 nsPrincipal::EnableCapability(const char *capability, void **annotation)
548 return SetCapability(capability, annotation, AnnotationEnabled);
551 NS_IMETHODIMP
552 nsPrincipal::DisableCapability(const char *capability, void **annotation)
554 return SetCapability(capability, annotation, AnnotationDisabled);
557 NS_IMETHODIMP
558 nsPrincipal::RevertCapability(const char *capability, void **annotation)
560 if (*annotation) {
561 nsHashtable *ht = (nsHashtable *) *annotation;
562 const char *start = capability;
563 for(;;) {
564 const char *space = PL_strchr(start, ' ');
565 int len = space ? space - start : strlen(start);
566 nsCAutoString capString(start, len);
567 nsCStringKey key(capString);
568 ht->Remove(&key);
569 if (!space) {
570 return NS_OK;
573 start = space + 1;
576 return NS_OK;
579 nsresult
580 nsPrincipal::SetCapability(const char *capability, void **annotation,
581 AnnotationValue value)
583 if (*annotation == nsnull) {
584 nsHashtable* ht = new nsHashtable(5);
586 if (!ht) {
587 return NS_ERROR_OUT_OF_MEMORY;
590 // This object owns its annotations. Save them so we can release
591 // them when we destroy this object.
592 if (!mAnnotations.AppendElement(ht)) {
593 delete ht;
594 return NS_ERROR_OUT_OF_MEMORY;
597 *annotation = ht;
600 const char *start = capability;
601 for(;;) {
602 const char *space = PL_strchr(start, ' ');
603 int len = space ? space - start : strlen(start);
604 nsCAutoString capString(start, len);
605 nsCStringKey key(capString);
606 nsHashtable *ht = static_cast<nsHashtable *>(*annotation);
607 ht->Put(&key, (void *) value);
608 if (!space) {
609 break;
612 start = space + 1;
615 return NS_OK;
618 NS_IMETHODIMP
619 nsPrincipal::GetHasCertificate(PRBool* aResult)
621 *aResult = (mCert != nsnull);
623 return NS_OK;
626 NS_IMETHODIMP
627 nsPrincipal::GetURI(nsIURI** aURI)
629 if (mCodebaseImmutable) {
630 NS_ADDREF(*aURI = mCodebase);
631 return NS_OK;
634 if (!mCodebase) {
635 *aURI = nsnull;
636 return NS_OK;
639 return NS_EnsureSafeToReturn(mCodebase, aURI);
642 void
643 nsPrincipal::SetURI(nsIURI* aURI)
645 mCodebase = NS_TryToMakeImmutable(aURI);
646 mCodebaseImmutable = URIIsImmutable(mCodebase);
650 nsresult
651 nsPrincipal::SetCertificate(const nsACString& aFingerprint,
652 const nsACString& aSubjectName,
653 const nsACString& aPrettyName,
654 nsISupports* aCert)
656 NS_ENSURE_STATE(!mCert);
658 if (aFingerprint.IsEmpty()) {
659 return NS_ERROR_INVALID_ARG;
662 mCert = new Certificate(aFingerprint, aSubjectName, aPrettyName, aCert);
663 if (!mCert) {
664 return NS_ERROR_OUT_OF_MEMORY;
667 return NS_OK;
670 NS_IMETHODIMP
671 nsPrincipal::GetFingerprint(nsACString& aFingerprint)
673 NS_ENSURE_STATE(mCert);
675 aFingerprint = mCert->fingerprint;
677 return NS_OK;
680 NS_IMETHODIMP
681 nsPrincipal::GetPrettyName(nsACString& aName)
683 NS_ENSURE_STATE(mCert);
685 aName = mCert->prettyName;
687 return NS_OK;
690 NS_IMETHODIMP
691 nsPrincipal::GetSubjectName(nsACString& aName)
693 NS_ENSURE_STATE(mCert);
695 aName = mCert->subjectName;
697 return NS_OK;
700 NS_IMETHODIMP
701 nsPrincipal::GetCertificate(nsISupports** aCertificate)
703 if (mCert) {
704 NS_IF_ADDREF(*aCertificate = mCert->cert);
706 else {
707 *aCertificate = nsnull;
709 return NS_OK;
712 NS_IMETHODIMP
713 nsPrincipal::GetHashValue(PRUint32* aValue)
715 NS_PRECONDITION(mCert || mCodebase, "Need a cert or codebase");
717 // If there is a certificate, it takes precendence over the codebase.
718 if (mCert) {
719 *aValue = nsCRT::HashCode(mCert->fingerprint.get());
721 else {
722 nsCAutoString str;
723 mCodebase->GetSpec(str);
724 *aValue = nsCRT::HashCode(str.get());
727 return NS_OK;
730 NS_IMETHODIMP
731 nsPrincipal::GetDomain(nsIURI** aDomain)
733 if (!mDomain) {
734 *aDomain = nsnull;
735 return NS_OK;
738 if (mDomainImmutable) {
739 NS_ADDREF(*aDomain = mDomain);
740 return NS_OK;
743 return NS_EnsureSafeToReturn(mDomain, aDomain);
746 NS_IMETHODIMP
747 nsPrincipal::SetDomain(nsIURI* aDomain)
749 mDomain = NS_TryToMakeImmutable(aDomain);
750 mDomainImmutable = URIIsImmutable(mDomain);
752 // Domain has changed, forget cached security policy
753 SetSecurityPolicy(nsnull);
755 return NS_OK;
758 nsresult
759 nsPrincipal::InitFromPersistent(const char* aPrefName,
760 const nsCString& aToken,
761 const nsCString& aSubjectName,
762 const nsACString& aPrettyName,
763 const char* aGrantedList,
764 const char* aDeniedList,
765 nsISupports* aCert,
766 PRBool aIsCert,
767 PRBool aTrusted)
769 NS_PRECONDITION(!mCapabilities || mCapabilities->Count() == 0,
770 "mCapabilities was already initialized?");
771 NS_PRECONDITION(mAnnotations.Length() == 0,
772 "mAnnotations was already initialized?");
773 NS_PRECONDITION(!mInitialized, "We were already initialized?");
775 mInitialized = PR_TRUE;
777 nsresult rv;
778 if (aIsCert) {
779 rv = SetCertificate(aToken, aSubjectName, aPrettyName, aCert);
781 if (NS_FAILED(rv)) {
782 return rv;
785 else {
786 rv = NS_NewURI(getter_AddRefs(mCodebase), aToken, nsnull);
787 if (NS_FAILED(rv)) {
788 NS_ERROR("Malformed URI in capability.principal preference.");
789 return rv;
792 NS_TryToSetImmutable(mCodebase);
793 mCodebaseImmutable = URIIsImmutable(mCodebase);
795 mTrusted = aTrusted;
798 rv = mJSPrincipals.Init(this, aToken);
799 NS_ENSURE_SUCCESS(rv, rv);
801 //-- Save the preference name
802 mPrefName = aPrefName;
804 const char* ordinalBegin = PL_strpbrk(aPrefName, "1234567890");
805 if (ordinalBegin) {
806 PRIntn n = atoi(ordinalBegin);
807 if (sCapabilitiesOrdinal <= n) {
808 sCapabilitiesOrdinal = n + 1;
812 //-- Store the capabilities
813 rv = NS_OK;
814 if (aGrantedList) {
815 rv = SetCanEnableCapability(aGrantedList, nsIPrincipal::ENABLE_GRANTED);
818 if (NS_SUCCEEDED(rv) && aDeniedList) {
819 rv = SetCanEnableCapability(aDeniedList, nsIPrincipal::ENABLE_DENIED);
822 return rv;
825 nsresult
826 nsPrincipal::EnsureCertData(const nsACString& aSubjectName,
827 const nsACString& aPrettyName,
828 nsISupports* aCert)
830 NS_ENSURE_STATE(mCert);
832 if (!mCert->subjectName.IsEmpty() &&
833 !mCert->subjectName.Equals(aSubjectName)) {
834 return NS_ERROR_INVALID_ARG;
837 mCert->subjectName = aSubjectName;
838 mCert->prettyName = aPrettyName;
839 mCert->cert = aCert;
840 return NS_OK;
843 struct CapabilityList
845 nsCString* granted;
846 nsCString* denied;
849 PR_STATIC_CALLBACK(PRBool)
850 AppendCapability(nsHashKey *aKey, void *aData, void *capListPtr)
852 CapabilityList* capList = (CapabilityList*)capListPtr;
853 PRInt16 value = (PRInt16)NS_PTR_TO_INT32(aData);
854 nsCStringKey* key = (nsCStringKey *)aKey;
855 if (value == nsIPrincipal::ENABLE_GRANTED) {
856 capList->granted->Append(key->GetString(), key->GetStringLength());
857 capList->granted->Append(' ');
859 else if (value == nsIPrincipal::ENABLE_DENIED) {
860 capList->denied->Append(key->GetString(), key->GetStringLength());
861 capList->denied->Append(' ');
864 return PR_TRUE;
867 NS_IMETHODIMP
868 nsPrincipal::GetPreferences(char** aPrefName, char** aID,
869 char** aSubjectName,
870 char** aGrantedList, char** aDeniedList,
871 PRBool* aIsTrusted)
873 if (mPrefName.IsEmpty()) {
874 if (mCert) {
875 mPrefName.Assign("capability.principal.certificate.p");
877 else {
878 mPrefName.Assign("capability.principal.codebase.p");
881 mPrefName.AppendInt(sCapabilitiesOrdinal++);
882 mPrefName.Append(".id");
885 *aPrefName = nsnull;
886 *aID = nsnull;
887 *aSubjectName = nsnull;
888 *aGrantedList = nsnull;
889 *aDeniedList = nsnull;
890 *aIsTrusted = mTrusted;
892 char *prefName = nsnull;
893 char *id = nsnull;
894 char *subjectName = nsnull;
895 char *granted = nsnull;
896 char *denied = nsnull;
898 //-- Preference name
899 prefName = ToNewCString(mPrefName);
900 if (!prefName) {
901 return NS_ERROR_OUT_OF_MEMORY;
904 //-- ID
905 nsresult rv = NS_OK;
906 if (mCert) {
907 id = ToNewCString(mCert->fingerprint);
908 if (!id) {
909 rv = NS_ERROR_OUT_OF_MEMORY;
912 else {
913 rv = GetOrigin(&id);
916 if (NS_FAILED(rv)) {
917 nsMemory::Free(prefName);
918 return rv;
921 if (mCert) {
922 subjectName = ToNewCString(mCert->subjectName);
923 } else {
924 subjectName = ToNewCString(EmptyCString());
927 if (!subjectName) {
928 nsMemory::Free(prefName);
929 nsMemory::Free(id);
930 return NS_ERROR_OUT_OF_MEMORY;
933 //-- Capabilities
934 nsCAutoString grantedListStr, deniedListStr;
935 if (mCapabilities) {
936 CapabilityList capList = CapabilityList();
937 capList.granted = &grantedListStr;
938 capList.denied = &deniedListStr;
939 mCapabilities->Enumerate(AppendCapability, (void*)&capList);
942 if (!grantedListStr.IsEmpty()) {
943 grantedListStr.Truncate(grantedListStr.Length() - 1);
944 granted = ToNewCString(grantedListStr);
945 if (!granted) {
946 nsMemory::Free(prefName);
947 nsMemory::Free(id);
948 nsMemory::Free(subjectName);
949 return NS_ERROR_OUT_OF_MEMORY;
953 if (!deniedListStr.IsEmpty()) {
954 deniedListStr.Truncate(deniedListStr.Length() - 1);
955 denied = ToNewCString(deniedListStr);
956 if (!denied) {
957 nsMemory::Free(prefName);
958 nsMemory::Free(id);
959 nsMemory::Free(subjectName);
960 if (granted) {
961 nsMemory::Free(granted);
963 return NS_ERROR_OUT_OF_MEMORY;
967 *aPrefName = prefName;
968 *aID = id;
969 *aSubjectName = subjectName;
970 *aGrantedList = granted;
971 *aDeniedList = denied;
973 return NS_OK;
976 PR_STATIC_CALLBACK(nsresult)
977 ReadAnnotationEntry(nsIObjectInputStream* aStream, nsHashKey** aKey,
978 void** aData)
980 nsresult rv;
981 nsCStringKey* key = new nsCStringKey(aStream, &rv);
982 if (NS_FAILED(rv)) {
983 return rv;
986 PRUint32 value;
987 rv = aStream->Read32(&value);
988 if (NS_FAILED(rv)) {
989 delete key;
990 return rv;
993 *aKey = key;
994 *aData = (void*) value;
995 return NS_OK;
998 PR_STATIC_CALLBACK(void)
999 FreeAnnotationEntry(nsIObjectInputStream* aStream, nsHashKey* aKey,
1000 void* aData)
1002 delete aKey;
1005 NS_IMETHODIMP
1006 nsPrincipal::Read(nsIObjectInputStream* aStream)
1008 PRBool hasCapabilities;
1009 nsresult rv = aStream->ReadBoolean(&hasCapabilities);
1010 if (NS_SUCCEEDED(rv) && hasCapabilities) {
1011 mCapabilities = new nsHashtable(aStream, ReadAnnotationEntry,
1012 FreeAnnotationEntry, &rv);
1013 NS_ENSURE_TRUE(mCapabilities, NS_ERROR_OUT_OF_MEMORY);
1016 if (NS_FAILED(rv)) {
1017 return rv;
1020 rv = NS_ReadOptionalCString(aStream, mPrefName);
1021 if (NS_FAILED(rv)) {
1022 return rv;
1025 const char* ordinalBegin = PL_strpbrk(mPrefName.get(), "1234567890");
1026 if (ordinalBegin) {
1027 PRIntn n = atoi(ordinalBegin);
1028 if (sCapabilitiesOrdinal <= n) {
1029 sCapabilitiesOrdinal = n + 1;
1033 PRBool haveCert;
1034 rv = aStream->ReadBoolean(&haveCert);
1035 if (NS_FAILED(rv)) {
1036 return rv;
1039 nsCString fingerprint;
1040 nsCString subjectName;
1041 nsCString prettyName;
1042 nsCOMPtr<nsISupports> cert;
1043 if (haveCert) {
1044 rv = NS_ReadOptionalCString(aStream, fingerprint);
1045 if (NS_FAILED(rv)) {
1046 return rv;
1049 rv = NS_ReadOptionalCString(aStream, subjectName);
1050 if (NS_FAILED(rv)) {
1051 return rv;
1054 rv = NS_ReadOptionalCString(aStream, prettyName);
1055 if (NS_FAILED(rv)) {
1056 return rv;
1059 rv = aStream->ReadObject(PR_TRUE, getter_AddRefs(cert));
1060 if (NS_FAILED(rv)) {
1061 return rv;
1065 nsCOMPtr<nsIURI> codebase;
1066 rv = NS_ReadOptionalObject(aStream, PR_TRUE, getter_AddRefs(codebase));
1067 if (NS_FAILED(rv)) {
1068 return rv;
1071 rv = Init(fingerprint, subjectName, prettyName, cert, codebase);
1072 NS_ENSURE_SUCCESS(rv, rv);
1074 nsCOMPtr<nsIURI> domain;
1075 rv = NS_ReadOptionalObject(aStream, PR_TRUE, getter_AddRefs(domain));
1076 if (NS_FAILED(rv)) {
1077 return rv;
1080 SetDomain(domain);
1082 rv = aStream->Read8(&mTrusted);
1083 if (NS_FAILED(rv)) {
1084 return rv;
1087 return NS_OK;
1090 PR_STATIC_CALLBACK(nsresult)
1091 WriteScalarValue(nsIObjectOutputStream* aStream, void* aData)
1093 PRUint32 value = NS_PTR_TO_INT32(aData);
1095 return aStream->Write32(value);
1098 NS_IMETHODIMP
1099 nsPrincipal::Write(nsIObjectOutputStream* aStream)
1101 NS_ENSURE_STATE(mCert || mCodebase);
1103 // mAnnotations is transient data associated to specific JS stack frames. We
1104 // don't want to serialize that.
1106 PRBool hasCapabilities = (mCapabilities && mCapabilities->Count() > 0);
1107 nsresult rv = aStream->WriteBoolean(hasCapabilities);
1108 if (NS_SUCCEEDED(rv) && hasCapabilities) {
1109 rv = mCapabilities->Write(aStream, WriteScalarValue);
1112 if (NS_FAILED(rv)) {
1113 return rv;
1116 rv = NS_WriteOptionalStringZ(aStream, mPrefName.get());
1117 if (NS_FAILED(rv)) {
1118 return rv;
1121 rv = aStream->WriteBoolean(mCert != nsnull);
1122 if (NS_FAILED(rv)) {
1123 return rv;
1126 if (mCert) {
1127 NS_ENSURE_STATE(mCert->cert);
1129 rv = NS_WriteOptionalStringZ(aStream, mCert->fingerprint.get());
1130 if (NS_FAILED(rv)) {
1131 return rv;
1134 rv = NS_WriteOptionalStringZ(aStream, mCert->subjectName.get());
1135 if (NS_FAILED(rv)) {
1136 return rv;
1139 rv = NS_WriteOptionalStringZ(aStream, mCert->prettyName.get());
1140 if (NS_FAILED(rv)) {
1141 return rv;
1144 rv = aStream->WriteCompoundObject(mCert->cert, NS_GET_IID(nsISupports),
1145 PR_TRUE);
1146 if (NS_FAILED(rv)) {
1147 return rv;
1151 // mSecurityPolicy is an optimization; it'll get looked up again as needed.
1152 // Don't bother saving and restoring it, esp. since it might change if
1153 // preferences change.
1155 rv = NS_WriteOptionalCompoundObject(aStream, mCodebase, NS_GET_IID(nsIURI),
1156 PR_TRUE);
1157 if (NS_FAILED(rv)) {
1158 return rv;
1161 rv = NS_WriteOptionalCompoundObject(aStream, mDomain, NS_GET_IID(nsIURI),
1162 PR_TRUE);
1163 if (NS_FAILED(rv)) {
1164 return rv;
1167 rv = aStream->Write8(mTrusted);
1168 if (NS_FAILED(rv)) {
1169 return rv;
1172 // mCodebaseImmutable and mDomainImmutable will be recomputed based
1173 // on the deserialized URIs in Read().
1175 return NS_OK;