Back to index

lightning-sunbird  0.9+nobinonly
nsPrincipal.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* vim: set ts=2 sw=2 et tw=80: */
00003 /* ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 2003
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Christopher A. Aillon <christopher@aillon.com>
00025  *   Giorgio Maone <g.maone@informaction.com>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either the GNU General Public License Version 2 or later (the "GPL"), or
00029  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #include "nscore.h"
00042 #include "nsScriptSecurityManager.h"
00043 #include "nsString.h"
00044 #include "nsReadableUtils.h"
00045 #include "plstr.h"
00046 #include "nsCRT.h"
00047 #include "nsIURI.h"
00048 #include "nsIJARURI.h"
00049 #include "nsNetUtil.h"
00050 #include "nsJSPrincipals.h"
00051 #include "nsVoidArray.h"
00052 #include "nsHashtable.h"
00053 #include "nsIObjectInputStream.h"
00054 #include "nsIObjectOutputStream.h"
00055 #include "nsIPrefBranch.h"
00056 #include "nsIPrefService.h"
00057 
00058 #include "nsPrincipal.h"
00059 
00060 
00061 // Static member variables
00062 PRInt32 nsPrincipal::sCapabilitiesOrdinal = 0;
00063 const char nsPrincipal::sInvalid[] = "Invalid";
00064 
00065 
00066 NS_IMPL_QUERY_INTERFACE2_CI(nsPrincipal,
00067                             nsIPrincipal,
00068                             nsISerializable)
00069 NS_IMPL_CI_INTERFACE_GETTER2(nsPrincipal,
00070                              nsIPrincipal,
00071                              nsISerializable)
00072 
00073 NS_IMETHODIMP_(nsrefcnt)
00074 nsPrincipal::AddRef()
00075 {
00076   NS_PRECONDITION(PRInt32(mJSPrincipals.refcount) >= 0, "illegal refcnt");
00077   // XXXcaa does this need to be threadsafe?  See bug 143559.
00078   nsrefcnt count = PR_AtomicIncrement((PRInt32 *)&mJSPrincipals.refcount);
00079   NS_LOG_ADDREF(this, count, "nsPrincipal", sizeof(*this));
00080   return count;
00081 }
00082 
00083 NS_IMETHODIMP_(nsrefcnt)
00084 nsPrincipal::Release()
00085 {
00086   NS_PRECONDITION(0 != mJSPrincipals.refcount, "dup release");
00087   nsrefcnt count = PR_AtomicDecrement((PRInt32 *)&mJSPrincipals.refcount);
00088   NS_LOG_RELEASE(this, count, "nsPrincipal");
00089   if (count == 0) {
00090     NS_DELETEXPCOM(this);
00091   }
00092 
00093   return count;
00094 }
00095 
00096 nsPrincipal::nsPrincipal()
00097   : mCapabilities(7),
00098     mSecurityPolicy(nsnull),
00099     mTrusted(PR_FALSE),
00100     mInitialized(PR_FALSE)
00101 {
00102 }
00103 
00104 nsresult
00105 nsPrincipal::Init(const nsACString& aCertFingerprint,
00106                   const nsACString& aSubjectName,
00107                   const nsACString& aPrettyName,
00108                   nsISupports* aCert,
00109                   nsIURI *aCodebase)
00110 {
00111   NS_ENSURE_STATE(!mInitialized);
00112   NS_ENSURE_ARG(!aCertFingerprint.IsEmpty() || aCodebase); // better have one of these.
00113 
00114   mInitialized = PR_TRUE;
00115 
00116   mCodebase = aCodebase;
00117 
00118   nsresult rv;
00119   if (!aCertFingerprint.IsEmpty()) {
00120     rv = SetCertificate(aCertFingerprint, aSubjectName, aPrettyName, aCert);
00121     if (NS_SUCCEEDED(rv)) {
00122       rv = mJSPrincipals.Init(this, mCert->fingerprint.get());
00123     }
00124   }
00125   else {
00126     nsCAutoString spec;
00127     rv = mCodebase->GetSpec(spec);
00128     if (NS_SUCCEEDED(rv)) {
00129       rv = mJSPrincipals.Init(this, spec.get());
00130     }
00131   }
00132 
00133   NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "nsPrincipal::Init() failed");
00134 
00135   return rv;
00136 }
00137 
00138 
00139 PR_STATIC_CALLBACK(PRBool)
00140 deleteElement(void* aElement, void *aData)
00141 {
00142   nsHashtable *ht = (nsHashtable *) aElement;
00143   delete ht;
00144   return PR_TRUE;
00145 }
00146 
00147 nsPrincipal::~nsPrincipal(void)
00148 {
00149   mAnnotations.EnumerateForwards(deleteElement, nsnull);
00150   SetSecurityPolicy(nsnull); 
00151 }
00152 
00153 NS_IMETHODIMP
00154 nsPrincipal::GetJSPrincipals(JSContext *cx, JSPrincipals **jsprin)
00155 {
00156   NS_PRECONDITION(mJSPrincipals.nsIPrincipalPtr, "mJSPrincipals is uninitalized!");
00157 
00158   JSPRINCIPALS_HOLD(cx, &mJSPrincipals);
00159   *jsprin = &mJSPrincipals;
00160   return NS_OK;
00161 }
00162 
00163 NS_IMETHODIMP
00164 nsPrincipal::GetOrigin(char **aOrigin)
00165 {
00166   *aOrigin = nsnull;
00167 
00168   nsCOMPtr<nsIURI> origin = mCodebase;
00169   if (mCodebase) {
00170     // If uri is a jar URI, get the base URI
00171     nsCOMPtr<nsIJARURI> jarURI;
00172     while((jarURI = do_QueryInterface(origin)))
00173     {
00174       jarURI->GetJARFile(getter_AddRefs(origin));
00175     }
00176   }
00177       
00178   if (!origin) {
00179       NS_ASSERTION(mCert, "No Domain or Codebase for a non-cert principal");
00180       return NS_ERROR_FAILURE;
00181   }
00182 
00183   nsCAutoString hostPort;
00184 
00185   // chrome: URLs don't have a meaningful origin, so make
00186   // sure we just get the full spec for them.
00187   PRBool isChrome;
00188   nsresult rv = origin->SchemeIs("chrome", &isChrome);
00189   if (NS_SUCCEEDED(rv) && !isChrome) {
00190     rv = origin->GetHostPort(hostPort);
00191   }
00192 
00193   if (NS_SUCCEEDED(rv) && !isChrome) {
00194     nsCAutoString scheme;
00195     rv = origin->GetScheme(scheme);
00196     NS_ENSURE_SUCCESS(rv, rv);
00197     *aOrigin = ToNewCString(scheme + NS_LITERAL_CSTRING("://") + hostPort);
00198   }
00199   else {
00200     // Some URIs (e.g., nsSimpleURI) don't support host. Just
00201     // get the full spec.
00202     nsCAutoString spec;
00203     rv = origin->GetSpec(spec);
00204     NS_ENSURE_SUCCESS(rv, rv);
00205     *aOrigin = ToNewCString(spec);
00206   }
00207 
00208   return *aOrigin ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
00209 }
00210 
00211 NS_IMETHODIMP
00212 nsPrincipal::GetSecurityPolicy(void** aSecurityPolicy)
00213 {
00214   if (mSecurityPolicy && mSecurityPolicy->IsInvalid()) 
00215     SetSecurityPolicy(nsnull);
00216   
00217   *aSecurityPolicy = (void *) mSecurityPolicy;
00218   return NS_OK;
00219 }
00220 
00221 NS_IMETHODIMP
00222 nsPrincipal::SetSecurityPolicy(void* aSecurityPolicy)
00223 {
00224   DomainPolicy *newPolicy = NS_REINTERPRET_CAST(
00225                               DomainPolicy *, aSecurityPolicy);
00226   if (newPolicy)
00227     newPolicy->Hold();
00228  
00229   if (mSecurityPolicy)
00230     mSecurityPolicy->Drop();
00231   
00232   mSecurityPolicy = newPolicy;
00233   return NS_OK;
00234 }
00235 
00236 NS_IMETHODIMP
00237 nsPrincipal::Equals(nsIPrincipal *aOther, PRBool *aResult)
00238 {
00239   *aResult = PR_FALSE;
00240 
00241   if (!aOther) {
00242     NS_WARNING("Need a principal to compare this to!");
00243     return NS_OK;
00244   }
00245 
00246   if (this != aOther) {
00247     PRBool otherHasCert;
00248     aOther->GetHasCertificate(&otherHasCert);
00249     if (otherHasCert != (mCert != nsnull)) {
00250       // One has a cert while the other doesn't.  Not equal.
00251       return NS_OK;
00252     }
00253 
00254     if (mCert) {
00255       nsCAutoString str;
00256       aOther->GetFingerprint(str);
00257       *aResult = str.Equals(mCert->fingerprint);
00258 
00259       // If either subject name is empty, just let the result stand (so that
00260       // nsScriptSecurityManager::SetCanEnableCapability works), but if they're
00261       // both non-empty, only claim equality if they're equal.
00262       if (*aResult && !mCert->subjectName.IsEmpty()) {
00263         // Check the other principal's subject name
00264         aOther->GetSubjectName(str);
00265         *aResult = str.Equals(mCert->subjectName) || str.IsEmpty();
00266       }
00267         
00268       return NS_OK;
00269     }
00270 
00271     // Codebases are equal if they have the same origin.
00272     *aResult =
00273       NS_SUCCEEDED(nsScriptSecurityManager::GetScriptSecurityManager()
00274                    ->CheckSameOriginPrincipal(this, aOther, PR_FALSE));
00275     return NS_OK;
00276   }
00277 
00278   *aResult = PR_TRUE;
00279   return NS_OK;
00280 }
00281 
00282 NS_IMETHODIMP
00283 nsPrincipal::Subsumes(nsIPrincipal *aOther, PRBool *aResult)
00284 {
00285   return Equals(aOther, aResult);
00286 }
00287 
00288 NS_IMETHODIMP
00289 nsPrincipal::CanEnableCapability(const char *capability, PRInt16 *result)
00290 {
00291   // If this principal is marked invalid, can't enable any capabilities
00292   nsCStringKey invalidKey(sInvalid);
00293   if (mCapabilities.Exists(&invalidKey)) {
00294     *result = nsIPrincipal::ENABLE_DENIED;
00295 
00296     return NS_OK;
00297   }
00298 
00299   if (!mCert && !mTrusted) {
00300     NS_ASSERTION(mInitialized, "Trying to enable a capability on an "
00301                                "uninitialized principal");
00302 
00303     // If we are a non-trusted codebase principal, capabilities can not
00304     // be enabled if the user has not set the pref allowing scripts to
00305     // request enhanced capabilities; however, the file: and resource:
00306     // schemes are special and may be able to get extra capabilities
00307     // even with the pref disabled.
00308 
00309     static const char pref[] = "signed.applets.codebase_principal_support";
00310     nsCOMPtr<nsIPrefBranch> prefBranch =
00311       do_GetService(NS_PREFSERVICE_CONTRACTID);
00312     if (prefBranch) {
00313       PRBool mightEnable;
00314       nsresult rv = prefBranch->GetBoolPref(pref, &mightEnable);
00315       if (NS_FAILED(rv) || !mightEnable) {
00316         rv = mCodebase->SchemeIs("file", &mightEnable);
00317         if (NS_FAILED(rv) || !mightEnable) {
00318           rv = mCodebase->SchemeIs("resource", &mightEnable);
00319           if (NS_FAILED(rv) || !mightEnable) {
00320             *result = nsIPrincipal::ENABLE_DENIED;
00321 
00322             return NS_OK;
00323           }
00324         }
00325       }
00326     }
00327   }
00328 
00329   const char *start = capability;
00330   *result = nsIPrincipal::ENABLE_GRANTED;
00331   for(;;) {
00332     const char *space = PL_strchr(start, ' ');
00333     PRInt32 len = space ? space - start : strlen(start);
00334     nsCAutoString capString(start, len);
00335     nsCStringKey key(capString);
00336     PRInt16 value = (PRInt16)NS_PTR_TO_INT32(mCapabilities.Get(&key));
00337     if (value == 0 || value == nsIPrincipal::ENABLE_UNKNOWN) {
00338       // We don't know whether we can enable this capability,
00339       // so we should ask the user.
00340       value = nsIPrincipal::ENABLE_WITH_USER_PERMISSION;
00341     }
00342 
00343     if (value < *result) {
00344       *result = value;
00345     }
00346 
00347     if (!space) {
00348       break;
00349     }
00350 
00351     start = space + 1;
00352   }
00353 
00354   return NS_OK;
00355 }
00356 
00357 NS_IMETHODIMP
00358 nsPrincipal::SetCanEnableCapability(const char *capability,
00359                                     PRInt16 canEnable)
00360 {
00361   // If this principal is marked invalid, can't enable any capabilities
00362 
00363   nsCStringKey invalidKey(sInvalid);
00364   if (mCapabilities.Exists(&invalidKey)) {
00365     return NS_OK;
00366   }
00367 
00368   if (PL_strcmp(capability, sInvalid) == 0) {
00369     mCapabilities.Reset();
00370   }
00371 
00372   const char *start = capability;
00373   for(;;) {
00374     const char *space = PL_strchr(start, ' ');
00375     int len = space ? space - start : strlen(start);
00376     nsCAutoString capString(start, len);
00377     nsCStringKey key(capString);
00378     mCapabilities.Put(&key, NS_INT32_TO_PTR(canEnable));
00379     if (!space) {
00380       break;
00381     }
00382 
00383     start = space + 1;
00384   }
00385 
00386   return NS_OK;
00387 }
00388 
00389 NS_IMETHODIMP
00390 nsPrincipal::IsCapabilityEnabled(const char *capability, void *annotation,
00391                                  PRBool *result)
00392 {
00393   *result = PR_FALSE;
00394   nsHashtable *ht = (nsHashtable *) annotation;
00395   if (!ht) {
00396     return NS_OK;
00397   }
00398   const char *start = capability;
00399   for(;;) {
00400     const char *space = PL_strchr(start, ' ');
00401     int len = space ? space - start : strlen(start);
00402     nsCAutoString capString(start, len);
00403     nsCStringKey key(capString);
00404     *result = (ht->Get(&key) == (void *) AnnotationEnabled);
00405     if (!*result) {
00406       // If any single capability is not enabled, then return false.
00407       return NS_OK;
00408     }
00409 
00410     if (!space) {
00411       return NS_OK;
00412     }
00413 
00414     start = space + 1;
00415   }
00416 
00417   return NS_OK;
00418 }
00419 
00420 NS_IMETHODIMP
00421 nsPrincipal::EnableCapability(const char *capability, void **annotation)
00422 {
00423   return SetCapability(capability, annotation, AnnotationEnabled);
00424 }
00425 
00426 NS_IMETHODIMP
00427 nsPrincipal::DisableCapability(const char *capability, void **annotation)
00428 {
00429   return SetCapability(capability, annotation, AnnotationDisabled);
00430 }
00431 
00432 NS_IMETHODIMP
00433 nsPrincipal::RevertCapability(const char *capability, void **annotation)
00434 {
00435   if (*annotation) {
00436     nsHashtable *ht = (nsHashtable *) *annotation;
00437     const char *start = capability;
00438     for(;;) {
00439       const char *space = PL_strchr(start, ' ');
00440       int len = space ? space - start : strlen(start);
00441       nsCAutoString capString(start, len);
00442       nsCStringKey key(capString);
00443       ht->Remove(&key);
00444       if (!space) {
00445         return NS_OK;
00446       }
00447 
00448       start = space + 1;
00449     }
00450   }
00451   return NS_OK;
00452 }
00453 
00454 nsresult
00455 nsPrincipal::SetCapability(const char *capability, void **annotation,
00456                            AnnotationValue value)
00457 {
00458   if (*annotation == nsnull) {
00459     *annotation = new nsHashtable(5);
00460     if (!*annotation) {
00461        return NS_ERROR_OUT_OF_MEMORY;
00462      }
00463 
00464     // This object owns its annotations. Save them so we can release
00465     // them when we destroy this object.
00466     mAnnotations.AppendElement(*annotation);
00467   }
00468 
00469   const char *start = capability;
00470   for(;;) {
00471     const char *space = PL_strchr(start, ' ');
00472     int len = space ? space - start : strlen(start);
00473     nsCAutoString capString(start, len);
00474     nsCStringKey key(capString);
00475     nsHashtable *ht = (nsHashtable *) *annotation;
00476     ht->Put(&key, (void *) value);
00477     if (!space) {
00478       break;
00479     }
00480 
00481     start = space + 1;
00482   }
00483 
00484   return NS_OK;
00485 }
00486 
00487 NS_IMETHODIMP
00488 nsPrincipal::GetHasCertificate(PRBool* aResult)
00489 {
00490   *aResult = (mCert != nsnull);
00491 
00492   return NS_OK;
00493 }
00494 
00495 NS_IMETHODIMP
00496 nsPrincipal::GetURI(nsIURI** aURI)
00497 {
00498   NS_IF_ADDREF(*aURI = mCodebase);
00499 
00500   return NS_OK;
00501 }
00502 
00503 void
00504 nsPrincipal::SetURI(nsIURI* aURI)
00505 {
00506   mCodebase = aURI;
00507 }
00508 
00509 
00510 nsresult
00511 nsPrincipal::SetCertificate(const nsACString& aFingerprint,
00512                             const nsACString& aSubjectName,
00513                             const nsACString& aPrettyName,
00514                             nsISupports* aCert)
00515 {
00516   NS_ENSURE_STATE(!mCert);
00517 
00518   if (aFingerprint.IsEmpty()) {
00519     return NS_ERROR_INVALID_ARG;
00520   }
00521 
00522   mCert = new Certificate(aFingerprint, aSubjectName, aPrettyName, aCert);
00523   if (!mCert) {
00524     return NS_ERROR_OUT_OF_MEMORY;
00525   }
00526 
00527   return NS_OK;
00528 }
00529 
00530 NS_IMETHODIMP
00531 nsPrincipal::GetFingerprint(nsACString& aFingerprint)
00532 {
00533   NS_ENSURE_STATE(mCert);
00534 
00535   aFingerprint = mCert->fingerprint;
00536 
00537   return NS_OK;
00538 }
00539 
00540 NS_IMETHODIMP
00541 nsPrincipal::GetPrettyName(nsACString& aName)
00542 {
00543   NS_ENSURE_STATE(mCert);
00544 
00545   aName = mCert->prettyName;
00546 
00547   return NS_OK;
00548 }
00549 
00550 NS_IMETHODIMP
00551 nsPrincipal::GetSubjectName(nsACString& aName)
00552 {
00553   NS_ENSURE_STATE(mCert);
00554 
00555   aName = mCert->subjectName;
00556 
00557   return NS_OK;
00558 }
00559 
00560 NS_IMETHODIMP
00561 nsPrincipal::GetCertificate(nsISupports** aCertificate)
00562 {
00563   if (mCert) {
00564     NS_IF_ADDREF(*aCertificate = mCert->cert);
00565   }
00566   else {
00567     *aCertificate = nsnull;
00568   }
00569   return NS_OK;
00570 }
00571 
00572 NS_IMETHODIMP
00573 nsPrincipal::GetHashValue(PRUint32* aValue)
00574 {
00575   NS_PRECONDITION(mCert || mCodebase, "Need a cert or codebase");
00576 
00577   // If there is a certificate, it takes precendence over the codebase.
00578   if (mCert) {
00579     *aValue = nsCRT::HashCode(mCert->fingerprint.get(), nsnull);
00580   }
00581   else {
00582     nsCAutoString str;
00583     mCodebase->GetSpec(str);
00584     *aValue = nsCRT::HashCode(str.get(), nsnull);
00585   }
00586 
00587   return NS_OK;
00588 }
00589 
00590 NS_IMETHODIMP
00591 nsPrincipal::GetDomain(nsIURI** aDomain)
00592 {
00593   NS_IF_ADDREF(*aDomain = mDomain);
00594 
00595   return NS_OK;
00596 }
00597 
00598 NS_IMETHODIMP
00599 nsPrincipal::SetDomain(nsIURI* aDomain)
00600 {
00601   mDomain = aDomain;
00602   // Domain has changed, forget cached security policy
00603   SetSecurityPolicy(nsnull);
00604 
00605   return NS_OK;
00606 }
00607 
00608 nsresult
00609 nsPrincipal::InitFromPersistent(const char* aPrefName,
00610                                 const nsCString& aToken,
00611                                 const nsCString& aSubjectName,
00612                                 const nsACString& aPrettyName,
00613                                 const char* aGrantedList,
00614                                 const char* aDeniedList,
00615                                 nsISupports* aCert,
00616                                 PRBool aIsCert,
00617                                 PRBool aTrusted)
00618 {
00619   NS_PRECONDITION(mCapabilities.Count() == 0,
00620                   "mCapabilities was already initialized?");
00621   NS_PRECONDITION(mAnnotations.Count() == 0,
00622                   "mAnnotations was already initialized?");
00623   NS_PRECONDITION(!mInitialized, "We were already initialized?");
00624 
00625   mInitialized = PR_TRUE;
00626 
00627   nsresult rv;
00628   if (aIsCert) {
00629     rv = SetCertificate(aToken, aSubjectName, aPrettyName, aCert);
00630     
00631     if (NS_FAILED(rv)) {
00632       return rv;
00633     }
00634   }
00635   else {
00636     rv = NS_NewURI(getter_AddRefs(mCodebase), aToken, nsnull);
00637     if (NS_FAILED(rv)) {
00638       NS_ERROR("Malformed URI in capability.principal preference.");
00639       return rv;
00640     }
00641 
00642     mTrusted = aTrusted;
00643   }
00644 
00645   rv = mJSPrincipals.Init(this, aToken.get());
00646   NS_ENSURE_SUCCESS(rv, rv);
00647 
00648   //-- Save the preference name
00649   mPrefName = aPrefName;
00650 
00651   const char* ordinalBegin = PL_strpbrk(aPrefName, "1234567890");
00652   if (ordinalBegin) {
00653     PRIntn n = atoi(ordinalBegin);
00654     if (sCapabilitiesOrdinal <= n) {
00655       sCapabilitiesOrdinal = n + 1;
00656     }
00657   }
00658 
00659   //-- Store the capabilities
00660   rv = NS_OK;
00661   if (aGrantedList) {
00662     rv = SetCanEnableCapability(aGrantedList, nsIPrincipal::ENABLE_GRANTED);
00663   }
00664 
00665   if (NS_SUCCEEDED(rv) && aDeniedList) {
00666     rv = SetCanEnableCapability(aDeniedList, nsIPrincipal::ENABLE_DENIED);
00667   }
00668 
00669   return rv;
00670 }
00671 
00672 nsresult
00673 nsPrincipal::EnsureCertData(const nsACString& aSubjectName,
00674                             const nsACString& aPrettyName,
00675                             nsISupports* aCert)
00676 {
00677   NS_ENSURE_STATE(mCert);
00678 
00679   if (!mCert->subjectName.IsEmpty() &&
00680       !mCert->subjectName.Equals(aSubjectName)) {
00681     return NS_ERROR_INVALID_ARG;
00682   }
00683 
00684   mCert->subjectName = aSubjectName;
00685   mCert->prettyName = aPrettyName;
00686   mCert->cert = aCert;
00687   return NS_OK;
00688 }
00689 
00690 struct CapabilityList
00691 {
00692   nsCString* granted;
00693   nsCString* denied;
00694 };
00695 
00696 PR_STATIC_CALLBACK(PRBool)
00697 AppendCapability(nsHashKey *aKey, void *aData, void *capListPtr)
00698 {
00699   CapabilityList* capList = (CapabilityList*)capListPtr;
00700   PRInt16 value = (PRInt16)NS_PTR_TO_INT32(aData);
00701   nsCStringKey* key = (nsCStringKey *)aKey;
00702   if (value == nsIPrincipal::ENABLE_GRANTED) {
00703     capList->granted->Append(key->GetString(), key->GetStringLength());
00704     capList->granted->Append(' ');
00705   }
00706   else if (value == nsIPrincipal::ENABLE_DENIED) {
00707     capList->denied->Append(key->GetString(), key->GetStringLength());
00708     capList->denied->Append(' ');
00709   }
00710 
00711   return PR_TRUE;
00712 }
00713 
00714 NS_IMETHODIMP
00715 nsPrincipal::GetPreferences(char** aPrefName, char** aID,
00716                             char** aSubjectName,
00717                             char** aGrantedList, char** aDeniedList)
00718 {
00719   if (mPrefName.IsEmpty()) {
00720     if (mCert) {
00721       mPrefName.Assign("capability.principal.certificate.p");
00722     }
00723     else {
00724       mPrefName.Assign("capability.principal.codebase.p");
00725     }
00726 
00727     mPrefName.AppendInt(sCapabilitiesOrdinal++);
00728     mPrefName.Append(".id");
00729   }
00730 
00731   *aPrefName = nsnull;
00732   *aID = nsnull;
00733   *aSubjectName = nsnull;
00734   *aGrantedList = nsnull;
00735   *aDeniedList = nsnull;
00736 
00737   char *prefName = nsnull;
00738   char *id = nsnull;
00739   char *subjectName = nsnull;
00740   char *granted = nsnull;
00741   char *denied = nsnull;
00742 
00743   //-- Preference name
00744   prefName = ToNewCString(mPrefName);
00745   if (!prefName) {
00746     return NS_ERROR_OUT_OF_MEMORY;
00747   }
00748 
00749   //-- ID
00750   nsresult rv = NS_OK;
00751   if (mCert) {
00752     id = ToNewCString(mCert->fingerprint);
00753     if (!id) {
00754       rv = NS_ERROR_OUT_OF_MEMORY;
00755     }
00756   }
00757   else {
00758     rv = GetOrigin(&id);
00759   }
00760 
00761   if (NS_FAILED(rv)) {
00762     nsMemory::Free(prefName);
00763     return rv;
00764   }
00765 
00766   if (mCert) {
00767     subjectName = ToNewCString(mCert->subjectName);
00768   } else {
00769     subjectName = ToNewCString(EmptyCString());
00770   }
00771 
00772   if (!subjectName) {
00773     nsMemory::Free(prefName);
00774     nsMemory::Free(id);
00775     return NS_ERROR_OUT_OF_MEMORY;
00776   }
00777 
00778   //-- Capabilities
00779   nsCAutoString grantedListStr, deniedListStr;
00780   CapabilityList capList = CapabilityList();
00781   capList.granted = &grantedListStr;
00782   capList.denied = &deniedListStr;
00783   mCapabilities.Enumerate(AppendCapability, (void*)&capList);
00784 
00785   if (!grantedListStr.IsEmpty()) {
00786     grantedListStr.Truncate(grantedListStr.Length() - 1);
00787     granted = ToNewCString(grantedListStr);
00788     if (!granted) {
00789       nsMemory::Free(prefName);
00790       nsMemory::Free(id);
00791       nsMemory::Free(subjectName);
00792       return NS_ERROR_OUT_OF_MEMORY;
00793     }
00794   }
00795 
00796   if (!deniedListStr.IsEmpty()) {
00797     deniedListStr.Truncate(deniedListStr.Length() - 1);
00798     denied = ToNewCString(deniedListStr);
00799     if (!denied) {
00800       nsMemory::Free(prefName);
00801       nsMemory::Free(id);
00802       nsMemory::Free(subjectName);
00803       if (granted) {
00804         nsMemory::Free(granted);
00805       }
00806       return NS_ERROR_OUT_OF_MEMORY;
00807     }
00808   }
00809 
00810   *aPrefName = prefName;
00811   *aID = id;
00812   *aSubjectName = subjectName;
00813   *aGrantedList = granted;
00814   *aDeniedList = denied;
00815 
00816   return NS_OK;
00817 }
00818 
00819 PR_STATIC_CALLBACK(nsresult)
00820 ReadAnnotationEntry(nsIObjectInputStream* aStream, nsHashKey** aKey,
00821                     void** aData)
00822 {
00823   nsresult rv;
00824   nsCStringKey* key = new nsCStringKey(aStream, &rv);
00825   if (NS_FAILED(rv)) {
00826     return rv;
00827   }
00828 
00829   PRUint32 value;
00830   rv = aStream->Read32(&value);
00831   if (NS_FAILED(rv)) {
00832     delete key;
00833     return rv;
00834   }
00835 
00836   *aKey = key;
00837   *aData = (void*) value;
00838   return NS_OK;
00839 }
00840 
00841 PR_STATIC_CALLBACK(void)
00842 FreeAnnotationEntry(nsIObjectInputStream* aStream, nsHashKey* aKey,
00843                     void* aData)
00844 {
00845   delete aKey;
00846 }
00847 
00848 NS_IMETHODIMP
00849 nsPrincipal::Read(nsIObjectInputStream* aStream)
00850 {
00851   PRUint32 annotationCount;
00852   nsresult rv = aStream->Read32(&annotationCount);
00853   if (NS_FAILED(rv)) {
00854     return rv;
00855   }
00856 
00857   for (PRInt32 i = 0, n = PRInt32(annotationCount); i < n; i++) {
00858     nsHashtable *ht = new nsHashtable(aStream,
00859                                       ReadAnnotationEntry,
00860                                       FreeAnnotationEntry,
00861                                       &rv);
00862     if (!ht) {
00863       return NS_ERROR_OUT_OF_MEMORY;
00864     }
00865 
00866     if (NS_FAILED(rv)) {
00867       delete ht;
00868       return rv;
00869     }
00870 
00871     if (!mAnnotations.InsertElementAt(NS_REINTERPRET_CAST(void*, ht), i)) {
00872       delete ht;
00873       return NS_ERROR_OUT_OF_MEMORY;
00874     }
00875   }
00876 
00877   PRBool hasCapabilities;
00878   rv = aStream->ReadBoolean(&hasCapabilities);
00879   if (NS_SUCCEEDED(rv) && hasCapabilities) {
00880     mCapabilities = nsHashtable(aStream,
00881                                 ReadAnnotationEntry,
00882                                 FreeAnnotationEntry,
00883                                 &rv);
00884   }
00885 
00886   if (NS_FAILED(rv)) {
00887     return rv;
00888   }
00889 
00890   rv = NS_ReadOptionalCString(aStream, mPrefName);
00891   if (NS_FAILED(rv)) {
00892     return rv;
00893   }
00894 
00895   return NS_OK;
00896 }
00897 
00898 PR_STATIC_CALLBACK(nsresult)
00899 WriteScalarValue(nsIObjectOutputStream* aStream, void* aData)
00900 {
00901   PRUint32 value = NS_PTR_TO_INT32(aData);
00902 
00903   return aStream->Write32(value);
00904 }
00905 
00906 NS_IMETHODIMP
00907 nsPrincipal::Write(nsIObjectOutputStream* aStream)
00908 {
00909   PRUint32 annotationCount = PRUint32(mAnnotations.Count());
00910   nsresult rv = aStream->Write32(annotationCount);
00911   if (NS_FAILED(rv)) {
00912     return rv;
00913   }
00914 
00915   for (PRInt32 i = 0, n = PRInt32(annotationCount); i < n; i++) {
00916     nsHashtable *ht = NS_REINTERPRET_CAST(nsHashtable *, mAnnotations[i]);
00917     rv = ht->Write(aStream, WriteScalarValue);
00918     if (NS_FAILED(rv)) {
00919       return rv;
00920     }
00921   }
00922 
00923   PRBool hasCapabilities = (mCapabilities.Count() > 0);
00924   rv = aStream->WriteBoolean(hasCapabilities);
00925   if (NS_SUCCEEDED(rv) && hasCapabilities) {
00926     rv = mCapabilities.Write(aStream, WriteScalarValue);
00927   }
00928 
00929   if (NS_FAILED(rv)) {
00930     return rv;
00931   }
00932 
00933   rv = NS_WriteOptionalStringZ(aStream, mPrefName.get());
00934   if (NS_FAILED(rv)) {
00935     return rv;
00936   }
00937 
00938   return NS_OK;
00939 }