/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is Mozilla Communicator client code. * * The Initial Developer of the Original Code is Netscape Communications * Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * Alec Flett * Brian Nesse */ #include "nsPrefBranch.h" #include "nsILocalFile.h" #include "nsIObserverService.h" #include "nsISupportsPrimitives.h" #include "nsString.h" #include "nsXPIDLString.h" #include "nsScriptSecurityManager.h" // Danger -- this includes nsIPref.h #include "nsIStringBundle.h" #include "prefapi.h" // Must be included after nsScriptSecurityManager #include "prmem.h" #include "nsIFileSpec.h" // this should be removed eventually #include "prefapi_private_data.h" // Definitions struct EnumerateData { const char *parent; nsVoidArray *pref_list; }; struct PrefCallbackData { nsIPrefBranch *pBranch; nsIObserver *pObserver; }; static NS_DEFINE_CID(kSecurityManagerCID, NS_SCRIPTSECURITYMANAGER_CID); static NS_DEFINE_CID(kStringBundleServiceCID, NS_STRINGBUNDLESERVICE_CID); // Prototypes extern "C" PrefResult pref_UnlockPref(const char *key); PR_STATIC_CALLBACK(PRIntn) pref_enumChild(PLHashEntry *he, int i, void *arg); static int PR_CALLBACK NotifyObserver(const char *newpref, void *data); // this needs to be removed! static nsresult _convertRes(int res) //--------------------------------------------------------------------------- { switch (res) { case PREF_OUT_OF_MEMORY: return NS_ERROR_OUT_OF_MEMORY; case PREF_NOT_INITIALIZED: return NS_ERROR_NOT_INITIALIZED; case PREF_BAD_PARAMETER: return NS_ERROR_INVALID_ARG; case PREF_TYPE_CHANGE_ERR: case PREF_ERROR: case PREF_BAD_LOCKFILE: case PREF_DEFAULT_VALUE_NOT_INITIALIZED: return NS_ERROR_UNEXPECTED; case PREF_VALUECHANGED: return 1; } NS_ASSERTION((res >= PREF_DEFAULT_VALUE_NOT_INITIALIZED) && (res <= PREF_PROFILE_UPGRADE), "you added a new error code to prefapi.h and didn't update _convertRes"); return NS_OK; } /* * Constructor/Destructor */ nsPrefBranch::nsPrefBranch(const char *aPrefRoot, PRBool aDefaultBranch) : mObservers(nsnull) { NS_INIT_ISUPPORTS(); mPrefRoot = aPrefRoot; mPrefRootLength = mPrefRoot.Length(); mIsDefault = aDefaultBranch; } nsPrefBranch::~nsPrefBranch() { PrefCallbackData *pCallback; if (mObservers) { // unregister the observers PRInt32 count; count = mObservers->Count(); if (count > 0) { PRInt32 i; nsCString domain; for (i = 0; i < count; i++) { pCallback = (PrefCallbackData *)mObservers->ElementAt(i); if (pCallback) { mObserverDomains.CStringAt(i, domain); PREF_UnregisterCallback(domain, NotifyObserver, pCallback); NS_RELEASE(pCallback->pObserver); } nsMemory::Free(pCallback); } // now empty the observer arrays in bulk mObservers->Clear(); mObserverDomains.Clear(); } delete mObservers; } } /* * nsISupports Implementation */ NS_IMPL_THREADSAFE_ADDREF(nsPrefBranch) NS_IMPL_THREADSAFE_RELEASE(nsPrefBranch) NS_INTERFACE_MAP_BEGIN(nsPrefBranch) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIPrefBranch) NS_INTERFACE_MAP_ENTRY(nsIPrefBranch) NS_INTERFACE_MAP_ENTRY(nsIPrefBranchInternal) NS_INTERFACE_MAP_ENTRY(nsISecurityPref) NS_INTERFACE_MAP_END /* * nsIPrefBranch Implementation */ NS_IMETHODIMP nsPrefBranch::GetRoot(char * *aRoot) { NS_ENSURE_ARG_POINTER(aRoot); mPrefRoot.Truncate(mPrefRootLength); *aRoot = mPrefRoot.ToNewCString(); return NS_OK; } NS_IMETHODIMP nsPrefBranch::GetPrefType(const char *aPrefName, PRInt32 *_retval) { *_retval = PREF_GetPrefType(getPrefName(aPrefName)); return NS_OK; } NS_IMETHODIMP nsPrefBranch::GetBoolPref(const char *aPrefName, PRBool *_retval) { const char *pref = getPrefName(aPrefName); nsresult rv; rv = QueryObserver(pref); if (NS_SUCCEEDED(rv)) { rv = _convertRes(PREF_GetBoolPref(pref, _retval, mIsDefault)); } return rv; } NS_IMETHODIMP nsPrefBranch::SetBoolPref(const char *aPrefName, PRInt32 aValue) { const char *pref = getPrefName(aPrefName); nsresult rv; rv = QueryObserver(pref); if (NS_SUCCEEDED(rv)) { if (PR_FALSE == mIsDefault) { rv = _convertRes(PREF_SetBoolPref(pref, aValue)); } else { rv = _convertRes(PREF_SetDefaultBoolPref(pref, aValue)); } } return rv; } NS_IMETHODIMP nsPrefBranch::GetCharPref(const char *aPrefName, char **_retval) { const char *pref = getPrefName(aPrefName); nsresult rv; rv = QueryObserver(pref); if (NS_SUCCEEDED(rv)) { rv = _convertRes(PREF_CopyCharPref(pref, _retval, mIsDefault)); } return rv; } NS_IMETHODIMP nsPrefBranch::SetCharPref(const char *aPrefName, const char *aValue) { const char *pref = getPrefName(aPrefName); nsresult rv; rv = QueryObserver(pref); if (NS_SUCCEEDED(rv)) { if (PR_FALSE == mIsDefault) { rv = _convertRes(PREF_SetCharPref(pref, aValue)); } else { rv = _convertRes(PREF_SetDefaultCharPref(pref, aValue)); } } return rv; } NS_IMETHODIMP nsPrefBranch::GetIntPref(const char *aPrefName, PRInt32 *_retval) { const char *pref = getPrefName(aPrefName); nsresult rv; rv = QueryObserver(pref); if (NS_SUCCEEDED(rv)) { rv = _convertRes(PREF_GetIntPref(pref, _retval, mIsDefault)); } return rv; } NS_IMETHODIMP nsPrefBranch::SetIntPref(const char *aPrefName, PRInt32 aValue) { const char *pref = getPrefName(aPrefName); nsresult rv; rv = QueryObserver(pref); if (NS_SUCCEEDED(rv)) { if (PR_FALSE == mIsDefault) { rv = _convertRes(PREF_SetIntPref(pref, aValue)); } else { rv = _convertRes(PREF_SetDefaultIntPref(pref, aValue)); } } return rv; } NS_IMETHODIMP nsPrefBranch::GetComplexValue(const char *aPrefName, const nsIID & aType, void * *_retval) { nsresult rv; nsXPIDLCString utf8String; // we have to do this one first because it's different than all the rest if (aType.Equals(NS_GET_IID(nsIPrefLocalizedString))) { nsCOMPtr theString(do_CreateInstance(NS_PREFLOCALIZEDSTRING_CONTRACTID, &rv)); if (NS_SUCCEEDED(rv)) { const char *pref = getPrefName(aPrefName); PRBool bNeedDefault = PR_FALSE; if (mIsDefault) { bNeedDefault = PR_TRUE; } else { // if there is no user (or locked) value if (!PREF_HasUserPref(pref) && !PREF_PrefIsLocked(pref)) { bNeedDefault = PR_TRUE; } } // if we need to fetch the default value, do that instead, otherwise use the // value we pulled in at the top of this function if (bNeedDefault) { nsXPIDLString utf16String; rv = GetDefaultFromPropertiesFile(pref, getter_Copies(utf16String)); if (NS_SUCCEEDED(rv)) { rv = theString->SetData(utf16String.get()); } } else { rv = GetCharPref(aPrefName, getter_Copies(utf8String)); if (NS_SUCCEEDED(rv)) { rv = theString->SetData(NS_ConvertASCIItoUCS2(utf8String).get()); } } if (NS_SUCCEEDED(rv)) { nsIPrefLocalizedString *temp = theString; NS_ADDREF(temp); *_retval = (void *)temp; } } return rv; } // if we can't get the pref, there's no point in being here rv = GetCharPref(aPrefName, getter_Copies(utf8String)); if (NS_FAILED(rv)) { return rv; } if (aType.Equals(NS_GET_IID(nsILocalFile))) { nsCOMPtr file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv)); if (NS_SUCCEEDED(rv)) { rv = file->SetPersistentDescriptor(utf8String); if (NS_SUCCEEDED(rv)) { nsILocalFile *temp = file; NS_ADDREF(temp); *_retval = (void *)temp; return NS_OK; } } return rv; } if (aType.Equals(NS_GET_IID(nsISupportsWString))) { nsCOMPtr theString(do_CreateInstance(NS_SUPPORTS_WSTRING_CONTRACTID, &rv)); if (NS_SUCCEEDED(rv)) { rv = theString->SetData(NS_ConvertASCIItoUCS2(utf8String).get()); if (NS_SUCCEEDED(rv)) { nsISupportsWString *temp = theString; NS_ADDREF(temp); *_retval = (void *)temp; return NS_OK; } } return rv; } // This is depricated and you should not be using it if (aType.Equals(NS_GET_IID(nsIFileSpec))) { nsCOMPtr file(do_CreateInstance(NS_FILESPEC_CONTRACTID, &rv)); if (NS_SUCCEEDED(rv)) { nsIFileSpec *temp = file; PRBool valid; file->SetPersistentDescriptorString(utf8String); // only returns NS_OK file->IsValid(&valid); if (!valid) { /* if the string wasn't a valid persistent descriptor, it might be a valid native path */ file->SetNativePath(utf8String); } NS_ADDREF(temp); *_retval = (void *)temp; return NS_OK; } return rv; } NS_WARNING("nsPrefBranch::GetComplexValue - Unsupported interface type"); return NS_NOINTERFACE; } NS_IMETHODIMP nsPrefBranch::SetComplexValue(const char *aPrefName, const nsIID & aType, nsISupports *aValue) { nsresult rv; if (aType.Equals(NS_GET_IID(nsILocalFile))) { nsCOMPtr file = do_QueryInterface(aValue); nsXPIDLCString descriptorString; rv = file->GetPersistentDescriptor(getter_Copies(descriptorString)); if (NS_SUCCEEDED(rv)) { rv = SetCharPref(aPrefName, descriptorString); } return rv; } if (aType.Equals(NS_GET_IID(nsISupportsWString))) { nsCOMPtr theString = do_QueryInterface(aValue); if (theString) { nsXPIDLString wideString; rv = theString->GetData(getter_Copies(wideString)); if (NS_SUCCEEDED(rv)) { rv = SetCharPref(aPrefName, NS_ConvertUCS2toUTF8(wideString).get()); } } return rv; } if (aType.Equals(NS_GET_IID(nsIPrefLocalizedString))) { nsCOMPtr theString = do_QueryInterface(aValue); if (theString) { nsXPIDLString wideString; rv = theString->GetData(getter_Copies(wideString)); if (NS_SUCCEEDED(rv)) { rv = SetCharPref(aPrefName, NS_ConvertUCS2toUTF8(wideString).get()); } } return rv; } // This is depricated and you should not be using it if (aType.Equals(NS_GET_IID(nsIFileSpec))) { nsCOMPtr file = do_QueryInterface(aValue); nsXPIDLCString descriptorString; rv = file->GetPersistentDescriptorString(getter_Copies(descriptorString)); if (NS_SUCCEEDED(rv)) { rv = SetCharPref(aPrefName, descriptorString); } return rv; } NS_WARNING("nsPrefBranch::SetComplexValue - Unsupported interface type"); return NS_NOINTERFACE; } NS_IMETHODIMP nsPrefBranch::ClearUserPref(const char *aPrefName) { const char *pref = getPrefName(aPrefName); nsresult rv; rv = QueryObserver(pref); if (NS_SUCCEEDED(rv)) { rv = _convertRes(PREF_ClearUserPref(pref)); } return rv; } NS_IMETHODIMP nsPrefBranch::LockPref(const char *aPrefName) { const char *pref = getPrefName(aPrefName); nsresult rv; rv = QueryObserver(pref); if (NS_SUCCEEDED(rv)) { rv = _convertRes(PREF_LockPref(pref)); } return rv; } NS_IMETHODIMP nsPrefBranch::PrefIsLocked(const char *aPrefName, PRBool *_retval) { const char *pref = getPrefName(aPrefName); nsresult rv; NS_ENSURE_ARG_POINTER(_retval); rv = QueryObserver(pref); if (NS_SUCCEEDED(rv)) { *_retval = PREF_PrefIsLocked(pref); } return rv; } NS_IMETHODIMP nsPrefBranch::UnlockPref(const char *aPrefName) { const char *pref = getPrefName(aPrefName); nsresult rv; rv = QueryObserver(pref); if (NS_SUCCEEDED(rv)) { rv = _convertRes(pref_UnlockPref(pref)); } return rv; } /* void resetBranch (in string startingAt); */ NS_IMETHODIMP nsPrefBranch::ResetBranch(const char *aStartingAt) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsPrefBranch::DeleteBranch(const char *aStartingAt) { return _convertRes(PREF_DeleteBranch(getPrefName(aStartingAt))); } NS_IMETHODIMP nsPrefBranch::GetChildList(const char *aStartingAt, PRUint32 *aCount, char ***aChildArray) { char** outArray; char* theElement; PRInt32 numPrefs; PRInt32 dwIndex; nsresult rv = NS_OK; EnumerateData ed; nsAutoVoidArray prefArray; NS_ENSURE_ARG_POINTER(aCount); NS_ENSURE_ARG_POINTER(aChildArray); // this will contain a list of all the pref name strings // allocate on the stack for speed ed.parent = getPrefName(aStartingAt); ed.pref_list = &prefArray; PL_HashTableEnumerateEntries(gHashTable, pref_enumChild, &ed); // now that we've built up the list, run the callback on // all the matching elements numPrefs = prefArray.Count(); outArray = (char **)nsMemory::Alloc(numPrefs * sizeof(char *)); if (!outArray) return NS_ERROR_OUT_OF_MEMORY; for (dwIndex = 0; dwIndex < numPrefs; ++dwIndex) { theElement = (char *)prefArray.ElementAt(dwIndex); outArray[dwIndex] = (char *)nsMemory::Clone(theElement, strlen(theElement) + 1); if (!outArray[dwIndex]) { // we ran out of memory... this is annoying NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(dwIndex, outArray); return NS_ERROR_OUT_OF_MEMORY; } } *aCount = numPrefs; *aChildArray = outArray; return NS_OK; } NS_IMETHODIMP nsPrefBranch::AddObserver(const char *aDomain, nsIObserver *aObserver) { PrefCallbackData *pCallback; NS_ENSURE_ARG_POINTER(aDomain); NS_ENSURE_ARG_POINTER(aObserver); if (!mObservers) { nsresult rv = NS_OK; mObservers = new nsAutoVoidArray(); if (nsnull == mObservers) return NS_ERROR_OUT_OF_MEMORY; } pCallback = (PrefCallbackData *)nsMemory::Alloc(sizeof(PrefCallbackData)); if (nsnull == pCallback) return NS_ERROR_OUT_OF_MEMORY; pCallback->pBranch = NS_STATIC_CAST(nsIPrefBranch *, this); NS_ADDREF(aObserver); pCallback->pObserver = aObserver; mObservers->AppendElement(pCallback); mObserverDomains.AppendCString(nsCString(aDomain)); PREF_RegisterCallback(aDomain, NotifyObserver, pCallback); return NS_OK; } NS_IMETHODIMP nsPrefBranch::RemoveObserver(const char *aDomain, nsIObserver *aObserver) { PrefCallbackData *pCallback; PRInt32 count; PRInt32 i; nsresult rv; nsCString domain; NS_ENSURE_ARG_POINTER(aDomain); NS_ENSURE_ARG_POINTER(aObserver); if (!mObservers) return NS_OK; // need to find the index of observer, so we can remove it from the domain list too count = mObservers->Count(); if (count == 0) return NS_OK; for (i = 0; i < count; i++) { pCallback = (PrefCallbackData *)mObservers->ElementAt(i); if (pCallback && (pCallback->pObserver == aObserver)) { mObserverDomains.CStringAt(i, domain); if (domain.Equals(aDomain)) break; } } if (i == count) // not found, just return return NS_OK; rv = _convertRes(PREF_UnregisterCallback(aDomain, NotifyObserver, pCallback)); if (NS_SUCCEEDED(rv)) { NS_RELEASE(pCallback->pObserver); nsMemory::Free(pCallback); mObservers->RemoveElementAt(i); mObserverDomains.RemoveCStringAt(i); } return rv; } static int PR_CALLBACK NotifyObserver(const char *newpref, void *data) { PrefCallbackData *pData = (PrefCallbackData *)data; nsCOMPtr observer = NS_STATIC_CAST(nsIObserver *, pData->pObserver); observer->Observe(pData->pBranch, NS_LITERAL_STRING("nsPref:changed").get(), NS_ConvertASCIItoUCS2(newpref).get()); return 0; } nsresult nsPrefBranch::GetDefaultFromPropertiesFile(const char *aPrefName, PRUnichar **return_buf) { nsresult rv; // the default value contains a URL to a .properties file nsXPIDLCString propertyFileURL; rv = _convertRes(PREF_CopyCharPref(aPrefName, getter_Copies(propertyFileURL), PR_TRUE)); if (NS_FAILED(rv)) return rv; nsCOMPtr bundleService = do_GetService(kStringBundleServiceCID, &rv); if (NS_FAILED(rv)) return rv; nsCOMPtr bundle; rv = bundleService->CreateBundle(propertyFileURL, getter_AddRefs(bundle)); if (NS_FAILED(rv)) return rv; // string names are in unicdoe nsAutoString stringId; stringId.AssignWithConversion(aPrefName); return bundle->GetStringFromName(stringId.GetUnicode(), return_buf); } const char *nsPrefBranch::getPrefName(const char *aPrefName) { // for speed, avoid strcpy if we can: if (mPrefRoot.IsEmpty()) return aPrefName; // isn't there a better way to do this? this is really kind of gross. mPrefRoot.Truncate(mPrefRootLength); // only append if anything to append if ((nsnull != aPrefName) && (aPrefName != "")) mPrefRoot.Append(aPrefName); return mPrefRoot.get(); } PR_STATIC_CALLBACK(PRIntn) pref_enumChild(PLHashEntry *he, int i, void *arg) { EnumerateData *d = (EnumerateData *) arg; if (PL_strncmp((char *)he->key, d->parent, PL_strlen(d->parent)) == 0) { d->pref_list->AppendElement((void *)he->key); } return HT_ENUMERATE_NEXT; } /* * This function is currently named badly because the security stuff was going * be done through observers. There ended up being issues, and thus we are * temporarily reverting to using the previous implementation. */ nsresult nsPrefBranch::QueryObserver(const char *aPrefName) { static const char capabilityPrefix[] = "capability."; if ((aPrefName[0] == 'c' || aPrefName[0] == 'C') && PL_strncasecmp(aPrefName, capabilityPrefix, sizeof(capabilityPrefix)-1) == 0) { nsresult rv; NS_WITH_SERVICE(nsIScriptSecurityManager, secMan, kSecurityManagerCID, &rv); if (NS_FAILED(rv)) return NS_ERROR_FAILURE; PRBool enabled; rv = secMan->IsCapabilityEnabled("CapabilityPreferencesAccess", &enabled); if (NS_FAILED(rv) || !enabled) return NS_ERROR_FAILURE; } return NS_OK; } /* * Pref access without security check - these are here * to support nsScriptSecurityManager. * These functions are part of nsISecurityPref, not nsIPref. * **PLEASE** do not call these functions from elsewhere */ NS_IMETHODIMP nsPrefBranch::SecurityGetBoolPref(const char *pref, PRBool * return_val) { return _convertRes(PREF_GetBoolPref(getPrefName(pref), return_val, PR_FALSE)); } NS_IMETHODIMP nsPrefBranch::SecuritySetBoolPref(const char *pref, PRBool value) { return _convertRes(PREF_SetBoolPref(getPrefName(pref), value)); } NS_IMETHODIMP nsPrefBranch::SecurityGetCharPref(const char *pref, char ** return_buf) { return _convertRes(PREF_CopyCharPref(getPrefName(pref), return_buf, PR_FALSE)); } NS_IMETHODIMP nsPrefBranch::SecuritySetCharPref(const char *pref, const char* value) { return _convertRes(PREF_SetCharPref(getPrefName(pref), value)); } NS_IMETHODIMP nsPrefBranch::SecurityGetIntPref(const char *pref, PRInt32 * return_val) { return _convertRes(PREF_GetIntPref(getPrefName(pref), return_val, PR_FALSE)); } NS_IMETHODIMP nsPrefBranch::SecuritySetIntPref(const char *pref, PRInt32 value) { return _convertRes(PREF_SetIntPref(getPrefName(pref), value)); } NS_IMETHODIMP nsPrefBranch::SecurityClearUserPref(const char *pref_name) { return _convertRes(PREF_ClearUserPref(getPrefName(pref_name))); } nsPrefLocalizedString::nsPrefLocalizedString() : mUnicodeString(nsnull) { nsresult rv; NS_INIT_REFCNT(); mUnicodeString = do_CreateInstance(NS_SUPPORTS_WSTRING_CONTRACTID, &rv); } nsPrefLocalizedString::~nsPrefLocalizedString() { } /* * nsISupports Implementation */ NS_IMPL_THREADSAFE_ADDREF(nsPrefLocalizedString) NS_IMPL_THREADSAFE_RELEASE(nsPrefLocalizedString) NS_INTERFACE_MAP_BEGIN(nsPrefLocalizedString) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIPrefLocalizedString) NS_INTERFACE_MAP_ENTRY(nsIPrefLocalizedString) NS_INTERFACE_MAP_ENTRY(nsISupportsWString) NS_INTERFACE_MAP_END nsresult nsPrefLocalizedString::Init() { nsresult rv; mUnicodeString = do_CreateInstance(NS_SUPPORTS_WSTRING_CONTRACTID, &rv); return rv; }