michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/DebugOnly.h" michael@0: michael@0: #include "mozilla/dom/ContentParent.h" michael@0: #include "mozilla/dom/ContentChild.h" michael@0: #include "mozilla/unused.h" michael@0: #include "nsPermissionManager.h" michael@0: #include "nsPermission.h" michael@0: #include "nsCRT.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsCOMArray.h" michael@0: #include "nsArrayEnumerator.h" michael@0: #include "nsTArray.h" michael@0: #include "nsReadableUtils.h" michael@0: #include "nsILineInputStream.h" michael@0: #include "nsIIDNService.h" michael@0: #include "nsAppDirectoryServiceDefs.h" michael@0: #include "prprf.h" michael@0: #include "mozilla/storage.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "nsXULAppAPI.h" michael@0: #include "nsIPrincipal.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsIScriptSecurityManager.h" michael@0: #include "nsIAppsService.h" michael@0: #include "mozIApplication.h" michael@0: #include "nsIEffectiveTLDService.h" michael@0: #include "nsPIDOMWindow.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsIPrefService.h" michael@0: #include "nsIPrefBranch.h" michael@0: #include "nsIPrefBranch2.h" michael@0: #include "mozilla/net/NeckoMessageUtils.h" michael@0: michael@0: static nsPermissionManager *gPermissionManager = nullptr; michael@0: michael@0: using mozilla::dom::ContentParent; michael@0: using mozilla::dom::ContentChild; michael@0: using mozilla::unused; // ha! michael@0: michael@0: static bool michael@0: IsChildProcess() michael@0: { michael@0: return XRE_GetProcessType() == GeckoProcessType_Content; michael@0: } michael@0: michael@0: /** michael@0: * @returns The child process object, or if we are not in the child michael@0: * process, nullptr. michael@0: */ michael@0: static ContentChild* michael@0: ChildProcess() michael@0: { michael@0: if (IsChildProcess()) { michael@0: ContentChild* cpc = ContentChild::GetSingleton(); michael@0: if (!cpc) michael@0: NS_RUNTIMEABORT("Content Process is nullptr!"); michael@0: return cpc; michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: michael@0: #define ENSURE_NOT_CHILD_PROCESS_(onError) \ michael@0: PR_BEGIN_MACRO \ michael@0: if (IsChildProcess()) { \ michael@0: NS_ERROR("Cannot perform action in content process!"); \ michael@0: onError \ michael@0: } \ michael@0: PR_END_MACRO michael@0: michael@0: #define ENSURE_NOT_CHILD_PROCESS \ michael@0: ENSURE_NOT_CHILD_PROCESS_({ return NS_ERROR_NOT_AVAILABLE; }) michael@0: michael@0: #define ENSURE_NOT_CHILD_PROCESS_NORET \ michael@0: ENSURE_NOT_CHILD_PROCESS_(;) michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: namespace { michael@0: michael@0: nsresult michael@0: GetPrincipal(const nsACString& aHost, uint32_t aAppId, bool aIsInBrowserElement, michael@0: nsIPrincipal** aPrincipal) michael@0: { michael@0: nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); michael@0: NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE); michael@0: michael@0: nsCOMPtr uri; michael@0: nsresult rv = NS_NewURI(getter_AddRefs(uri), aHost); michael@0: if (NS_FAILED(rv)) { michael@0: // NOTE: most callers will end up here because we don't append "http://" for michael@0: // hosts. It's fine to arbitrary use "http://" because, for those entries, michael@0: // we will actually just use the host. If we end up here, but the host looks michael@0: // like an email address, we use mailto: instead. michael@0: nsCString scheme; michael@0: if (aHost.FindChar('@') == -1) michael@0: scheme = NS_LITERAL_CSTRING("http://"); michael@0: else michael@0: scheme = NS_LITERAL_CSTRING("mailto:"); michael@0: rv = NS_NewURI(getter_AddRefs(uri), scheme + aHost); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return secMan->GetAppCodebasePrincipal(uri, aAppId, aIsInBrowserElement, aPrincipal); michael@0: } michael@0: michael@0: nsresult michael@0: GetPrincipal(nsIURI* aURI, nsIPrincipal** aPrincipal) michael@0: { michael@0: nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); michael@0: NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE); michael@0: michael@0: return secMan->GetNoAppCodebasePrincipal(aURI, aPrincipal); michael@0: } michael@0: michael@0: nsresult michael@0: GetPrincipal(const nsACString& aHost, nsIPrincipal** aPrincipal) michael@0: { michael@0: return GetPrincipal(aHost, nsIScriptSecurityManager::NO_APP_ID, false, aPrincipal); michael@0: } michael@0: michael@0: nsresult michael@0: GetHostForPrincipal(nsIPrincipal* aPrincipal, nsACString& aHost) michael@0: { michael@0: nsCOMPtr uri; michael@0: nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: uri = NS_GetInnermostURI(uri); michael@0: NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE); michael@0: michael@0: rv = uri->GetAsciiHost(aHost); michael@0: if (NS_SUCCEEDED(rv) && !aHost.IsEmpty()) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // For the mailto scheme, we use the path of the URI. We have to chop off the michael@0: // query part if one exists, so we eliminate everything after a ?. michael@0: bool isMailTo = false; michael@0: if (NS_SUCCEEDED(uri->SchemeIs("mailto", &isMailTo)) && isMailTo) { michael@0: rv = uri->GetPath(aHost); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: int32_t spart = aHost.FindChar('?', 0); michael@0: if (spart >= 0) { michael@0: aHost.Cut(spart, aHost.Length() - spart); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Some entries like "file://" uses the origin. michael@0: rv = aPrincipal->GetOrigin(getter_Copies(aHost)); michael@0: if (NS_SUCCEEDED(rv) && !aHost.IsEmpty()) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: nsCString michael@0: GetNextSubDomainForHost(const nsACString& aHost) michael@0: { michael@0: nsCOMPtr tldService = michael@0: do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID); michael@0: if (!tldService) { michael@0: NS_ERROR("Should have a tld service!"); michael@0: return EmptyCString(); michael@0: } michael@0: michael@0: nsCString subDomain; michael@0: nsresult rv = tldService->GetNextSubDomain(aHost, subDomain); michael@0: // We can fail if there is no more subdomain or if the host can't have a michael@0: // subdomain. michael@0: if (NS_FAILED(rv)) { michael@0: return EmptyCString(); michael@0: } michael@0: michael@0: return subDomain; michael@0: } michael@0: michael@0: class AppClearDataObserver MOZ_FINAL : public nsIObserver { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: michael@0: // nsIObserver implementation. michael@0: NS_IMETHODIMP michael@0: Observe(nsISupports *aSubject, const char *aTopic, const char16_t *data) michael@0: { michael@0: MOZ_ASSERT(!nsCRT::strcmp(aTopic, "webapps-clear-data")); michael@0: michael@0: nsCOMPtr params = michael@0: do_QueryInterface(aSubject); michael@0: if (!params) { michael@0: NS_ERROR("'webapps-clear-data' notification's subject should be a mozIApplicationClearPrivateDataParams"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: uint32_t appId; michael@0: nsresult rv = params->GetAppId(&appId); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: bool browserOnly; michael@0: rv = params->GetBrowserOnly(&browserOnly); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr permManager = do_GetService("@mozilla.org/permissionmanager;1"); michael@0: return permManager->RemovePermissionsForApp(appId, browserOnly); michael@0: } michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(AppClearDataObserver, nsIObserver) michael@0: michael@0: static bool michael@0: IsExpandedPrincipal(nsIPrincipal* aPrincipal) michael@0: { michael@0: nsCOMPtr ep = do_QueryInterface(aPrincipal); michael@0: return !!ep; michael@0: } michael@0: michael@0: } // anonymous namespace michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: nsPermissionManager::PermissionKey::PermissionKey(nsIPrincipal* aPrincipal) michael@0: { michael@0: MOZ_ALWAYS_TRUE(NS_SUCCEEDED(GetHostForPrincipal(aPrincipal, mHost))); michael@0: MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aPrincipal->GetAppId(&mAppId))); michael@0: MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aPrincipal->GetIsInBrowserElement(&mIsInBrowserElement))); michael@0: } michael@0: michael@0: /** michael@0: * Simple callback used by |AsyncClose| to trigger a treatment once michael@0: * the database is closed. michael@0: * michael@0: * Note: Beware that, if you hold onto a |CloseDatabaseListener| from a michael@0: * |nsPermissionManager|, this will create a cycle. michael@0: * michael@0: * Note: Once the callback has been called this DeleteFromMozHostListener cannot michael@0: * be reused. michael@0: */ michael@0: class CloseDatabaseListener MOZ_FINAL : public mozIStorageCompletionCallback michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_MOZISTORAGECOMPLETIONCALLBACK michael@0: /** michael@0: * @param aManager The owning manager. michael@0: * @param aRebuildOnSuccess If |true|, reinitialize the database once michael@0: * it has been closed. Otherwise, do nothing such. michael@0: */ michael@0: CloseDatabaseListener(nsPermissionManager* aManager, michael@0: bool aRebuildOnSuccess); michael@0: michael@0: protected: michael@0: nsRefPtr mManager; michael@0: bool mRebuildOnSuccess; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(CloseDatabaseListener, mozIStorageCompletionCallback) michael@0: michael@0: CloseDatabaseListener::CloseDatabaseListener(nsPermissionManager* aManager, michael@0: bool aRebuildOnSuccess) michael@0: : mManager(aManager) michael@0: , mRebuildOnSuccess(aRebuildOnSuccess) michael@0: { michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: CloseDatabaseListener::Complete(nsresult, nsISupports*) michael@0: { michael@0: // Help breaking cycles michael@0: nsRefPtr manager = mManager.forget(); michael@0: if (mRebuildOnSuccess && !manager->mIsShuttingDown) { michael@0: return manager->InitDB(true); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: /** michael@0: * Simple callback used by |RemoveAllInternal| to trigger closing michael@0: * the database and reinitializing it. michael@0: * michael@0: * Note: Beware that, if you hold onto a |DeleteFromMozHostListener| from a michael@0: * |nsPermissionManager|, this will create a cycle. michael@0: * michael@0: * Note: Once the callback has been called this DeleteFromMozHostListener cannot michael@0: * be reused. michael@0: */ michael@0: class DeleteFromMozHostListener MOZ_FINAL : public mozIStorageStatementCallback michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_MOZISTORAGESTATEMENTCALLBACK michael@0: michael@0: /** michael@0: * @param aManager The owning manager. michael@0: */ michael@0: DeleteFromMozHostListener(nsPermissionManager* aManager); michael@0: michael@0: protected: michael@0: nsRefPtr mManager; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(DeleteFromMozHostListener, mozIStorageStatementCallback) michael@0: michael@0: DeleteFromMozHostListener:: michael@0: DeleteFromMozHostListener(nsPermissionManager* aManager) michael@0: : mManager(aManager) michael@0: { michael@0: } michael@0: michael@0: NS_IMETHODIMP DeleteFromMozHostListener::HandleResult(mozIStorageResultSet *) michael@0: { michael@0: MOZ_CRASH("Should not get any results"); michael@0: } michael@0: michael@0: NS_IMETHODIMP DeleteFromMozHostListener::HandleError(mozIStorageError *) michael@0: { michael@0: // Errors are handled in |HandleCompletion| michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP DeleteFromMozHostListener::HandleCompletion(uint16_t aReason) michael@0: { michael@0: // Help breaking cycles michael@0: nsRefPtr manager = mManager.forget(); michael@0: michael@0: if (aReason == REASON_ERROR) { michael@0: manager->CloseDB(true); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* static */ void michael@0: nsPermissionManager::AppClearDataObserverInit() michael@0: { michael@0: nsCOMPtr observerService = do_GetService("@mozilla.org/observer-service;1"); michael@0: observerService->AddObserver(new AppClearDataObserver(), "webapps-clear-data", /* holdsWeak= */ false); michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // nsPermissionManager Implementation michael@0: michael@0: static const char kPermissionsFileName[] = "permissions.sqlite"; michael@0: #define HOSTS_SCHEMA_VERSION 3 michael@0: michael@0: static const char kHostpermFileName[] = "hostperm.1"; michael@0: michael@0: static const char kPermissionChangeNotification[] = PERM_CHANGE_NOTIFICATION; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsPermissionManager, nsIPermissionManager, nsIObserver, nsISupportsWeakReference) michael@0: michael@0: nsPermissionManager::nsPermissionManager() michael@0: : mLargestID(0) michael@0: , mIsShuttingDown(false) michael@0: { michael@0: } michael@0: michael@0: nsPermissionManager::~nsPermissionManager() michael@0: { michael@0: RemoveAllFromMemory(); michael@0: gPermissionManager = nullptr; michael@0: } michael@0: michael@0: // static michael@0: nsIPermissionManager* michael@0: nsPermissionManager::GetXPCOMSingleton() michael@0: { michael@0: if (gPermissionManager) { michael@0: NS_ADDREF(gPermissionManager); michael@0: return gPermissionManager; michael@0: } michael@0: michael@0: // Create a new singleton nsPermissionManager. michael@0: // We AddRef only once since XPCOM has rules about the ordering of module michael@0: // teardowns - by the time our module destructor is called, it's too late to michael@0: // Release our members, since GC cycles have already been completed and michael@0: // would result in serious leaks. michael@0: // See bug 209571. michael@0: gPermissionManager = new nsPermissionManager(); michael@0: if (gPermissionManager) { michael@0: NS_ADDREF(gPermissionManager); michael@0: if (NS_FAILED(gPermissionManager->Init())) { michael@0: NS_RELEASE(gPermissionManager); michael@0: } michael@0: } michael@0: michael@0: return gPermissionManager; michael@0: } michael@0: michael@0: nsresult michael@0: nsPermissionManager::Init() michael@0: { michael@0: nsresult rv; michael@0: michael@0: mObserverService = do_GetService("@mozilla.org/observer-service;1", &rv); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mObserverService->AddObserver(this, "profile-before-change", true); michael@0: mObserverService->AddObserver(this, "profile-do-change", true); michael@0: } michael@0: michael@0: michael@0: nsCOMPtr pbi = do_GetService(NS_PREFSERVICE_CONTRACTID); michael@0: if (pbi) { michael@0: pbi->AddObserver("permissions.", this, PR_FALSE); michael@0: } michael@0: michael@0: if (IsChildProcess()) { michael@0: // Get the permissions from the parent process michael@0: InfallibleTArray perms; michael@0: ChildProcess()->SendReadPermissions(&perms); michael@0: michael@0: for (uint32_t i = 0; i < perms.Length(); i++) { michael@0: const IPC::Permission &perm = perms[i]; michael@0: michael@0: nsCOMPtr principal; michael@0: rv = GetPrincipal(perm.host, perm.appId, perm.isInBrowserElement, getter_AddRefs(principal)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: AddInternal(principal, perm.type, perm.capability, 0, perm.expireType, michael@0: perm.expireTime, eNotify, eNoDBOperation); michael@0: } michael@0: michael@0: // Stop here; we don't need the DB in the child process michael@0: return NS_OK; michael@0: } michael@0: michael@0: // ignore failure here, since it's non-fatal (we can run fine without michael@0: // persistent storage - e.g. if there's no profile). michael@0: // XXX should we tell the user about this? michael@0: InitDB(false); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsPermissionManager::InitDB(bool aRemoveFile) michael@0: { michael@0: nsCOMPtr permissionsFile; michael@0: nsresult rv = NS_GetSpecialDirectory(NS_APP_PERMISSION_PARENT_DIR, getter_AddRefs(permissionsFile)); michael@0: if (NS_FAILED(rv)) { michael@0: rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(permissionsFile)); michael@0: } michael@0: NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED); michael@0: michael@0: rv = permissionsFile->AppendNative(NS_LITERAL_CSTRING(kPermissionsFileName)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (aRemoveFile) { michael@0: bool exists = false; michael@0: rv = permissionsFile->Exists(&exists); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: if (exists) { michael@0: rv = permissionsFile->Remove(false); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: } michael@0: michael@0: nsCOMPtr storage = do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID); michael@0: if (!storage) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: bool memory_db = false; michael@0: nsCOMPtr prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); michael@0: if (prefs) { michael@0: prefs->GetBoolPref("permissions.memory_only", &memory_db); michael@0: } michael@0: michael@0: // cache a connection to the hosts database michael@0: if (memory_db) { michael@0: rv = storage->OpenSpecialDatabase("memory", getter_AddRefs(mDBConn)); michael@0: } else { michael@0: rv = storage->OpenDatabase(permissionsFile, getter_AddRefs(mDBConn)); michael@0: } michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: bool ready; michael@0: mDBConn->GetConnectionReady(&ready); michael@0: if (!ready) { michael@0: // delete and try again michael@0: rv = permissionsFile->Remove(false); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (memory_db) { michael@0: rv = storage->OpenSpecialDatabase("memory", getter_AddRefs(mDBConn)); michael@0: } else { michael@0: rv = storage->OpenDatabase(permissionsFile, getter_AddRefs(mDBConn)); michael@0: } michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: mDBConn->GetConnectionReady(&ready); michael@0: if (!ready) michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: bool tableExists = false; michael@0: mDBConn->TableExists(NS_LITERAL_CSTRING("moz_hosts"), &tableExists); michael@0: if (!tableExists) { michael@0: rv = CreateTable(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: } else { michael@0: // table already exists; check the schema version before reading michael@0: int32_t dbSchemaVersion; michael@0: rv = mDBConn->GetSchemaVersion(&dbSchemaVersion); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: switch (dbSchemaVersion) { michael@0: // upgrading. michael@0: // every time you increment the database schema, you need to implement michael@0: // the upgrading code from the previous version to the new one. michael@0: // fall through to current version michael@0: michael@0: case 1: michael@0: { michael@0: // previous non-expiry version of database. Upgrade it by adding the michael@0: // expiration columns michael@0: rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( michael@0: "ALTER TABLE moz_hosts ADD expireType INTEGER")); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( michael@0: "ALTER TABLE moz_hosts ADD expireTime INTEGER")); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: // fall through to the next upgrade michael@0: michael@0: // TODO: we want to make default version as version 2 in order to fix bug 784875. michael@0: case 0: michael@0: case 2: michael@0: { michael@0: // Add appId/isInBrowserElement fields. michael@0: rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( michael@0: "ALTER TABLE moz_hosts ADD appId INTEGER")); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( michael@0: "ALTER TABLE moz_hosts ADD isInBrowserElement INTEGER")); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = mDBConn->SetSchemaVersion(HOSTS_SCHEMA_VERSION); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: // fall through to the next upgrade michael@0: michael@0: // current version. michael@0: case HOSTS_SCHEMA_VERSION: michael@0: break; michael@0: michael@0: // downgrading. michael@0: // if columns have been added to the table, we can still use the ones we michael@0: // understand safely. if columns have been deleted or altered, just michael@0: // blow away the table and start from scratch! if you change the way michael@0: // a column is interpreted, make sure you also change its name so this michael@0: // check will catch it. michael@0: default: michael@0: { michael@0: // check if all the expected columns exist michael@0: nsCOMPtr stmt; michael@0: rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( michael@0: "SELECT host, type, permission, expireType, expireTime, appId, isInBrowserElement FROM moz_hosts"), michael@0: getter_AddRefs(stmt)); michael@0: if (NS_SUCCEEDED(rv)) michael@0: break; michael@0: michael@0: // our columns aren't there - drop the table! michael@0: rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DROP TABLE moz_hosts")); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = CreateTable(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: michael@0: // make operations on the table asynchronous, for performance michael@0: mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("PRAGMA synchronous = OFF")); michael@0: michael@0: // cache frequently used statements (for insertion, deletion, and updating) michael@0: rv = mDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING( michael@0: "INSERT INTO moz_hosts " michael@0: "(id, host, type, permission, expireType, expireTime, appId, isInBrowserElement) " michael@0: "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)"), getter_AddRefs(mStmtInsert)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = mDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING( michael@0: "DELETE FROM moz_hosts " michael@0: "WHERE id = ?1"), getter_AddRefs(mStmtDelete)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = mDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING( michael@0: "UPDATE moz_hosts " michael@0: "SET permission = ?2, expireType= ?3, expireTime = ?4 WHERE id = ?1"), michael@0: getter_AddRefs(mStmtUpdate)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // check whether to import or just read in the db michael@0: if (tableExists) michael@0: return Read(); michael@0: michael@0: return Import(); michael@0: } michael@0: michael@0: // sets the schema version and creates the moz_hosts table. michael@0: nsresult michael@0: nsPermissionManager::CreateTable() michael@0: { michael@0: // set the schema version, before creating the table michael@0: nsresult rv = mDBConn->SetSchemaVersion(HOSTS_SCHEMA_VERSION); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // create the table michael@0: // SQL also lives in automation.py.in. If you change this SQL change that michael@0: // one too. michael@0: return mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( michael@0: "CREATE TABLE moz_hosts (" michael@0: " id INTEGER PRIMARY KEY" michael@0: ",host TEXT" michael@0: ",type TEXT" michael@0: ",permission INTEGER" michael@0: ",expireType INTEGER" michael@0: ",expireTime INTEGER" michael@0: ",appId INTEGER" michael@0: ",isInBrowserElement INTEGER" michael@0: ")")); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPermissionManager::Add(nsIURI *aURI, michael@0: const char *aType, michael@0: uint32_t aPermission, michael@0: uint32_t aExpireType, michael@0: int64_t aExpireTime) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aURI); michael@0: michael@0: nsCOMPtr principal; michael@0: nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return AddFromPrincipal(principal, aType, aPermission, aExpireType, aExpireTime); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPermissionManager::AddFromPrincipal(nsIPrincipal* aPrincipal, michael@0: const char* aType, uint32_t aPermission, michael@0: uint32_t aExpireType, int64_t aExpireTime) michael@0: { michael@0: ENSURE_NOT_CHILD_PROCESS; michael@0: NS_ENSURE_ARG_POINTER(aPrincipal); michael@0: NS_ENSURE_ARG_POINTER(aType); michael@0: NS_ENSURE_TRUE(aExpireType == nsIPermissionManager::EXPIRE_NEVER || michael@0: aExpireType == nsIPermissionManager::EXPIRE_TIME || michael@0: aExpireType == nsIPermissionManager::EXPIRE_SESSION, michael@0: NS_ERROR_INVALID_ARG); michael@0: michael@0: // Skip addition if the permission is already expired. Note that EXPIRE_SESSION only michael@0: // honors expireTime if it is nonzero. michael@0: if ((aExpireType == nsIPermissionManager::EXPIRE_TIME || michael@0: (aExpireType == nsIPermissionManager::EXPIRE_SESSION && aExpireTime != 0)) && michael@0: aExpireTime <= (PR_Now() / 1000)) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // We don't add the system principal because it actually has no URI and we michael@0: // always allow action for them. michael@0: if (nsContentUtils::IsSystemPrincipal(aPrincipal)) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Permissions may not be added to expanded principals. michael@0: if (IsExpandedPrincipal(aPrincipal)) { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: return AddInternal(aPrincipal, nsDependentCString(aType), aPermission, 0, michael@0: aExpireType, aExpireTime, eNotify, eWriteToDB); michael@0: } michael@0: michael@0: nsresult michael@0: nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal, michael@0: const nsAFlatCString &aType, michael@0: uint32_t aPermission, michael@0: int64_t aID, michael@0: uint32_t aExpireType, michael@0: int64_t aExpireTime, michael@0: NotifyOperationType aNotifyOperation, michael@0: DBOperationType aDBOperation) michael@0: { michael@0: nsAutoCString host; michael@0: nsresult rv = GetHostForPrincipal(aPrincipal, host); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (!IsChildProcess()) { michael@0: uint32_t appId; michael@0: rv = aPrincipal->GetAppId(&appId); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: bool isInBrowserElement; michael@0: rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: IPC::Permission permission(host, appId, isInBrowserElement, aType, michael@0: aPermission, aExpireType, aExpireTime); michael@0: michael@0: nsTArray cplist; michael@0: ContentParent::GetAll(cplist); michael@0: for (uint32_t i = 0; i < cplist.Length(); ++i) { michael@0: ContentParent* cp = cplist[i]; michael@0: if (cp->NeedsPermissionsUpdate()) michael@0: unused << cp->SendAddPermission(permission); michael@0: } michael@0: } michael@0: michael@0: // look up the type index michael@0: int32_t typeIndex = GetTypeIndex(aType.get(), true); michael@0: NS_ENSURE_TRUE(typeIndex != -1, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: // When an entry already exists, PutEntry will return that, instead michael@0: // of adding a new one michael@0: nsRefPtr key = new PermissionKey(aPrincipal); michael@0: PermissionHashKey* entry = mPermissionTable.PutEntry(key); michael@0: if (!entry) return NS_ERROR_FAILURE; michael@0: if (!entry->GetKey()) { michael@0: mPermissionTable.RawRemoveEntry(entry); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: // figure out the transaction type, and get any existing permission value michael@0: OperationType op; michael@0: int32_t index = entry->GetPermissionIndex(typeIndex); michael@0: if (index == -1) { michael@0: if (aPermission == nsIPermissionManager::UNKNOWN_ACTION) michael@0: op = eOperationNone; michael@0: else michael@0: op = eOperationAdding; michael@0: michael@0: } else { michael@0: PermissionEntry oldPermissionEntry = entry->GetPermissions()[index]; michael@0: michael@0: // remove the permission if the permission is UNKNOWN, update the michael@0: // permission if its value or expire type have changed OR if the time has michael@0: // changed and the expire type is time, otherwise, don't modify. There's michael@0: // no need to modify a permission that doesn't expire with time when the michael@0: // only thing changed is the expire time. michael@0: if (aPermission == oldPermissionEntry.mPermission && michael@0: aExpireType == oldPermissionEntry.mExpireType && michael@0: (aExpireType == nsIPermissionManager::EXPIRE_NEVER || michael@0: aExpireTime == oldPermissionEntry.mExpireTime)) michael@0: op = eOperationNone; michael@0: else if (aPermission == nsIPermissionManager::UNKNOWN_ACTION) michael@0: op = eOperationRemoving; michael@0: else michael@0: op = eOperationChanging; michael@0: } michael@0: michael@0: // do the work for adding, deleting, or changing a permission: michael@0: // update the in-memory list, write to the db, and notify consumers. michael@0: int64_t id; michael@0: switch (op) { michael@0: case eOperationNone: michael@0: { michael@0: // nothing to do michael@0: return NS_OK; michael@0: } michael@0: michael@0: case eOperationAdding: michael@0: { michael@0: if (aDBOperation == eWriteToDB) { michael@0: // we'll be writing to the database - generate a known unique id michael@0: id = ++mLargestID; michael@0: } else { michael@0: // we're reading from the database - use the id already assigned michael@0: id = aID; michael@0: } michael@0: michael@0: entry->GetPermissions().AppendElement(PermissionEntry(id, typeIndex, aPermission, aExpireType, aExpireTime)); michael@0: michael@0: if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION) { michael@0: uint32_t appId; michael@0: rv = aPrincipal->GetAppId(&appId); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: bool isInBrowserElement; michael@0: rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: UpdateDB(op, mStmtInsert, id, host, aType, aPermission, aExpireType, aExpireTime, appId, isInBrowserElement); michael@0: } michael@0: michael@0: if (aNotifyOperation == eNotify) { michael@0: NotifyObserversWithPermission(host, michael@0: entry->GetKey()->mAppId, michael@0: entry->GetKey()->mIsInBrowserElement, michael@0: mTypeArray[typeIndex], michael@0: aPermission, michael@0: aExpireType, michael@0: aExpireTime, michael@0: MOZ_UTF16("added")); michael@0: } michael@0: michael@0: break; michael@0: } michael@0: michael@0: case eOperationRemoving: michael@0: { michael@0: PermissionEntry oldPermissionEntry = entry->GetPermissions()[index]; michael@0: id = oldPermissionEntry.mID; michael@0: entry->GetPermissions().RemoveElementAt(index); michael@0: michael@0: if (aDBOperation == eWriteToDB) michael@0: // We care only about the id here so we pass dummy values for all other michael@0: // parameters. michael@0: UpdateDB(op, mStmtDelete, id, EmptyCString(), EmptyCString(), 0, michael@0: nsIPermissionManager::EXPIRE_NEVER, 0, 0, false); michael@0: michael@0: if (aNotifyOperation == eNotify) { michael@0: NotifyObserversWithPermission(host, michael@0: entry->GetKey()->mAppId, michael@0: entry->GetKey()->mIsInBrowserElement, michael@0: mTypeArray[typeIndex], michael@0: oldPermissionEntry.mPermission, michael@0: oldPermissionEntry.mExpireType, michael@0: oldPermissionEntry.mExpireTime, michael@0: MOZ_UTF16("deleted")); michael@0: } michael@0: michael@0: // If there are no more permissions stored for that entry, clear it. michael@0: if (entry->GetPermissions().IsEmpty()) { michael@0: mPermissionTable.RawRemoveEntry(entry); michael@0: } michael@0: michael@0: break; michael@0: } michael@0: michael@0: case eOperationChanging: michael@0: { michael@0: id = entry->GetPermissions()[index].mID; michael@0: michael@0: // If the new expireType is EXPIRE_SESSION, then we have to keep a michael@0: // copy of the previous permission/expireType values. This cached value will be michael@0: // used when restoring the permissions of an app. michael@0: if (entry->GetPermissions()[index].mExpireType != nsIPermissionManager::EXPIRE_SESSION && michael@0: aExpireType == nsIPermissionManager::EXPIRE_SESSION) { michael@0: entry->GetPermissions()[index].mNonSessionPermission = entry->GetPermissions()[index].mPermission; michael@0: entry->GetPermissions()[index].mNonSessionExpireType = entry->GetPermissions()[index].mExpireType; michael@0: entry->GetPermissions()[index].mNonSessionExpireTime = entry->GetPermissions()[index].mExpireTime; michael@0: } else if (aExpireType != nsIPermissionManager::EXPIRE_SESSION) { michael@0: entry->GetPermissions()[index].mNonSessionPermission = aPermission; michael@0: entry->GetPermissions()[index].mNonSessionExpireType = aExpireType; michael@0: entry->GetPermissions()[index].mNonSessionExpireTime = aExpireTime; michael@0: } michael@0: michael@0: entry->GetPermissions()[index].mPermission = aPermission; michael@0: entry->GetPermissions()[index].mExpireType = aExpireType; michael@0: entry->GetPermissions()[index].mExpireTime = aExpireTime; michael@0: michael@0: if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION) michael@0: // We care only about the id, the permission and expireType/expireTime here. michael@0: // We pass dummy values for all other parameters. michael@0: UpdateDB(op, mStmtUpdate, id, EmptyCString(), EmptyCString(), michael@0: aPermission, aExpireType, aExpireTime, 0, false); michael@0: michael@0: if (aNotifyOperation == eNotify) { michael@0: NotifyObserversWithPermission(host, michael@0: entry->GetKey()->mAppId, michael@0: entry->GetKey()->mIsInBrowserElement, michael@0: mTypeArray[typeIndex], michael@0: aPermission, michael@0: aExpireType, michael@0: aExpireTime, michael@0: MOZ_UTF16("changed")); michael@0: } michael@0: michael@0: break; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPermissionManager::Remove(const nsACString &aHost, michael@0: const char *aType) michael@0: { michael@0: nsCOMPtr principal; michael@0: nsresult rv = GetPrincipal(aHost, getter_AddRefs(principal)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return RemoveFromPrincipal(principal, aType); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPermissionManager::RemoveFromPrincipal(nsIPrincipal* aPrincipal, michael@0: const char* aType) michael@0: { michael@0: ENSURE_NOT_CHILD_PROCESS; michael@0: NS_ENSURE_ARG_POINTER(aPrincipal); michael@0: NS_ENSURE_ARG_POINTER(aType); michael@0: michael@0: // System principals are never added to the database, no need to remove them. michael@0: if (nsContentUtils::IsSystemPrincipal(aPrincipal)) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Permissions may not be added to expanded principals. michael@0: if (IsExpandedPrincipal(aPrincipal)) { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: // AddInternal() handles removal, just let it do the work michael@0: return AddInternal(aPrincipal, michael@0: nsDependentCString(aType), michael@0: nsIPermissionManager::UNKNOWN_ACTION, michael@0: 0, michael@0: nsIPermissionManager::EXPIRE_NEVER, michael@0: 0, michael@0: eNotify, michael@0: eWriteToDB); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPermissionManager::RemoveAll() michael@0: { michael@0: ENSURE_NOT_CHILD_PROCESS; michael@0: return RemoveAllInternal(true); michael@0: } michael@0: michael@0: void michael@0: nsPermissionManager::CloseDB(bool aRebuildOnSuccess) michael@0: { michael@0: // Null the statements, this will finalize them. michael@0: mStmtInsert = nullptr; michael@0: mStmtDelete = nullptr; michael@0: mStmtUpdate = nullptr; michael@0: if (mDBConn) { michael@0: mozIStorageCompletionCallback* cb = new CloseDatabaseListener(this, michael@0: aRebuildOnSuccess); michael@0: mozilla::DebugOnly rv = mDBConn->AsyncClose(cb); michael@0: MOZ_ASSERT(NS_SUCCEEDED(rv)); michael@0: mDBConn = nullptr; // Avoid race conditions michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsPermissionManager::RemoveAllInternal(bool aNotifyObservers) michael@0: { michael@0: // Remove from memory and notify immediately. Since the in-memory michael@0: // database is authoritative, we do not need confirmation from the michael@0: // on-disk database to notify observers. michael@0: RemoveAllFromMemory(); michael@0: if (aNotifyObservers) { michael@0: NotifyObservers(nullptr, MOZ_UTF16("cleared")); michael@0: } michael@0: michael@0: // clear the db michael@0: if (mDBConn) { michael@0: nsCOMPtr removeStmt; michael@0: nsresult rv = mDBConn-> michael@0: CreateAsyncStatement(NS_LITERAL_CSTRING( michael@0: "DELETE FROM moz_hosts" michael@0: ), getter_AddRefs(removeStmt)); michael@0: MOZ_ASSERT(NS_SUCCEEDED(rv)); michael@0: if (!removeStmt) { michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: nsCOMPtr pending; michael@0: mozIStorageStatementCallback* cb = new DeleteFromMozHostListener(this); michael@0: rv = removeStmt->ExecuteAsync(cb, getter_AddRefs(pending)); michael@0: MOZ_ASSERT(NS_SUCCEEDED(rv)); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPermissionManager::TestExactPermission(nsIURI *aURI, michael@0: const char *aType, michael@0: uint32_t *aPermission) michael@0: { michael@0: nsCOMPtr principal; michael@0: nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return TestExactPermissionFromPrincipal(principal, aType, aPermission); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPermissionManager::TestExactPermissionFromPrincipal(nsIPrincipal* aPrincipal, michael@0: const char* aType, michael@0: uint32_t* aPermission) michael@0: { michael@0: return CommonTestPermission(aPrincipal, aType, aPermission, true, true); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPermissionManager::TestExactPermanentPermission(nsIPrincipal* aPrincipal, michael@0: const char* aType, michael@0: uint32_t* aPermission) michael@0: { michael@0: return CommonTestPermission(aPrincipal, aType, aPermission, true, false); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPermissionManager::TestPermission(nsIURI *aURI, michael@0: const char *aType, michael@0: uint32_t *aPermission) michael@0: { michael@0: nsCOMPtr principal; michael@0: nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return TestPermissionFromPrincipal(principal, aType, aPermission); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPermissionManager::TestPermissionFromWindow(nsIDOMWindow* aWindow, michael@0: const char* aType, michael@0: uint32_t* aPermission) michael@0: { michael@0: nsCOMPtr window = do_QueryInterface(aWindow); michael@0: NS_ENSURE_TRUE(window, NS_NOINTERFACE); michael@0: michael@0: nsPIDOMWindow* innerWindow = window->IsInnerWindow() ? michael@0: window.get() : michael@0: window->GetCurrentInnerWindow(); michael@0: michael@0: // Get the document for security check michael@0: nsCOMPtr document = innerWindow->GetExtantDoc(); michael@0: NS_ENSURE_TRUE(document, NS_NOINTERFACE); michael@0: michael@0: nsCOMPtr principal = document->NodePrincipal(); michael@0: return TestPermissionFromPrincipal(principal, aType, aPermission); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPermissionManager::TestPermissionFromPrincipal(nsIPrincipal* aPrincipal, michael@0: const char* aType, michael@0: uint32_t* aPermission) michael@0: { michael@0: return CommonTestPermission(aPrincipal, aType, aPermission, false, true); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPermissionManager::GetPermissionObject(nsIPrincipal* aPrincipal, michael@0: const char* aType, michael@0: bool aExactHostMatch, michael@0: nsIPermission** aResult) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aPrincipal); michael@0: NS_ENSURE_ARG_POINTER(aType); michael@0: michael@0: *aResult = nullptr; michael@0: michael@0: if (nsContentUtils::IsSystemPrincipal(aPrincipal)) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Querying the permission object of an nsEP is non-sensical. michael@0: if (IsExpandedPrincipal(aPrincipal)) { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: nsAutoCString host; michael@0: nsresult rv = GetHostForPrincipal(aPrincipal, host); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: int32_t typeIndex = GetTypeIndex(aType, false); michael@0: // If type == -1, the type isn't known, michael@0: // so just return NS_OK michael@0: if (typeIndex == -1) return NS_OK; michael@0: michael@0: uint32_t appId; michael@0: rv = aPrincipal->GetAppId(&appId); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: bool isInBrowserElement; michael@0: rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: PermissionHashKey* entry = GetPermissionHashKey(host, appId, isInBrowserElement, michael@0: typeIndex, aExactHostMatch); michael@0: if (!entry) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // We don't call GetPermission(typeIndex) because that returns a fake michael@0: // UNKNOWN_ACTION entry if there is no match. michael@0: int32_t idx = entry->GetPermissionIndex(typeIndex); michael@0: if (-1 == idx) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: PermissionEntry& perm = entry->GetPermissions()[idx]; michael@0: nsCOMPtr r = new nsPermission(entry->GetKey()->mHost, michael@0: entry->GetKey()->mAppId, michael@0: entry->GetKey()->mIsInBrowserElement, michael@0: mTypeArray.ElementAt(perm.mType), michael@0: perm.mPermission, michael@0: perm.mExpireType, michael@0: perm.mExpireTime); michael@0: r.forget(aResult); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsPermissionManager::CommonTestPermission(nsIPrincipal* aPrincipal, michael@0: const char *aType, michael@0: uint32_t *aPermission, michael@0: bool aExactHostMatch, michael@0: bool aIncludingSession) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aPrincipal); michael@0: NS_ENSURE_ARG_POINTER(aType); michael@0: michael@0: if (nsContentUtils::IsSystemPrincipal(aPrincipal)) { michael@0: *aPermission = nsIPermissionManager::ALLOW_ACTION; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Set the default. michael@0: *aPermission = nsIPermissionManager::UNKNOWN_ACTION; michael@0: michael@0: // For expanded principals, we want to iterate over the whitelist and see michael@0: // if the permission is granted for any of them. michael@0: nsCOMPtr ep = do_QueryInterface(aPrincipal); michael@0: if (ep) { michael@0: nsTArray>* whitelist; michael@0: nsresult rv = ep->GetWhiteList(&whitelist); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: for (size_t i = 0; i < whitelist->Length(); ++i) { michael@0: uint32_t perm; michael@0: rv = CommonTestPermission(whitelist->ElementAt(i), aType, &perm, aExactHostMatch, michael@0: aIncludingSession); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: if (perm == nsIPermissionManager::ALLOW_ACTION) { michael@0: *aPermission = perm; michael@0: return NS_OK; michael@0: } else if (perm == nsIPermissionManager::PROMPT_ACTION) { michael@0: // Store it, but keep going to see if we can do better. michael@0: *aPermission = perm; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsAutoCString host; michael@0: nsresult rv = GetHostForPrincipal(aPrincipal, host); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: int32_t typeIndex = GetTypeIndex(aType, false); michael@0: // If type == -1, the type isn't known, michael@0: // so just return NS_OK michael@0: if (typeIndex == -1) return NS_OK; michael@0: michael@0: uint32_t appId; michael@0: rv = aPrincipal->GetAppId(&appId); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: bool isInBrowserElement; michael@0: rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: PermissionHashKey* entry = GetPermissionHashKey(host, appId, isInBrowserElement, michael@0: typeIndex, aExactHostMatch); michael@0: if (!entry || michael@0: (!aIncludingSession && michael@0: entry->GetPermission(typeIndex).mNonSessionExpireType == michael@0: nsIPermissionManager::EXPIRE_SESSION)) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: *aPermission = aIncludingSession michael@0: ? entry->GetPermission(typeIndex).mPermission michael@0: : entry->GetPermission(typeIndex).mNonSessionPermission; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Returns PermissionHashKey for a given { host, appId, isInBrowserElement } tuple. michael@0: // This is not simply using PermissionKey because we will walk-up domains in michael@0: // case of |host| contains sub-domains. michael@0: // Returns null if nothing found. michael@0: // Also accepts host on the format "". This will perform an exact match michael@0: // lookup as the string doesn't contain any dots. michael@0: nsPermissionManager::PermissionHashKey* michael@0: nsPermissionManager::GetPermissionHashKey(const nsACString& aHost, michael@0: uint32_t aAppId, michael@0: bool aIsInBrowserElement, michael@0: uint32_t aType, michael@0: bool aExactHostMatch) michael@0: { michael@0: PermissionHashKey* entry = nullptr; michael@0: michael@0: nsRefPtr key = new PermissionKey(aHost, aAppId, aIsInBrowserElement); michael@0: entry = mPermissionTable.GetEntry(key); michael@0: michael@0: if (entry) { michael@0: PermissionEntry permEntry = entry->GetPermission(aType); michael@0: michael@0: // if the entry is expired, remove and keep looking for others. michael@0: // Note that EXPIRE_SESSION only honors expireTime if it is nonzero. michael@0: if ((permEntry.mExpireType == nsIPermissionManager::EXPIRE_TIME || michael@0: (permEntry.mExpireType == nsIPermissionManager::EXPIRE_SESSION && michael@0: permEntry.mExpireTime != 0)) && michael@0: permEntry.mExpireTime <= (PR_Now() / 1000)) { michael@0: nsCOMPtr principal; michael@0: if (NS_FAILED(GetPrincipal(aHost, aAppId, aIsInBrowserElement, getter_AddRefs(principal)))) { michael@0: return nullptr; michael@0: } michael@0: michael@0: entry = nullptr; michael@0: RemoveFromPrincipal(principal, mTypeArray[aType].get()); michael@0: } else if (permEntry.mPermission == nsIPermissionManager::UNKNOWN_ACTION) { michael@0: entry = nullptr; michael@0: } michael@0: } michael@0: michael@0: if (entry) { michael@0: return entry; michael@0: } michael@0: michael@0: // If we haven't found an entry, depending on the host, we could try a bit michael@0: // harder. michael@0: // If this is a file:// URI, we can check for the presence of the magic entry michael@0: // which gives permission to all file://. This hack might disappear, michael@0: // see bug 817007. Note that we don't require aExactHostMatch to be true for michael@0: // that to keep retro-compatibility. michael@0: // If this is not a file:// URI, and that aExactHostMatch wasn't true, we can michael@0: // check if the base domain has a permission entry. michael@0: michael@0: if (StringBeginsWith(aHost, NS_LITERAL_CSTRING("file://"))) { michael@0: return GetPermissionHashKey(NS_LITERAL_CSTRING(""), aAppId, aIsInBrowserElement, aType, true); michael@0: } michael@0: michael@0: if (!aExactHostMatch) { michael@0: nsCString domain = GetNextSubDomainForHost(aHost); michael@0: if (!domain.IsEmpty()) { michael@0: return GetPermissionHashKey(domain, aAppId, aIsInBrowserElement, aType, aExactHostMatch); michael@0: } michael@0: } michael@0: michael@0: // No entry, really... michael@0: return nullptr; michael@0: } michael@0: michael@0: // helper struct for passing arguments into hash enumeration callback. michael@0: struct nsGetEnumeratorData michael@0: { michael@0: nsGetEnumeratorData(nsCOMArray *aArray, const nsTArray *aTypes) michael@0: : array(aArray) michael@0: , types(aTypes) {} michael@0: michael@0: nsCOMArray *array; michael@0: const nsTArray *types; michael@0: }; michael@0: michael@0: static PLDHashOperator michael@0: AddPermissionsToList(nsPermissionManager::PermissionHashKey* entry, void *arg) michael@0: { michael@0: nsGetEnumeratorData *data = static_cast(arg); michael@0: michael@0: for (uint32_t i = 0; i < entry->GetPermissions().Length(); ++i) { michael@0: nsPermissionManager::PermissionEntry& permEntry = entry->GetPermissions()[i]; michael@0: michael@0: nsPermission *perm = new nsPermission(entry->GetKey()->mHost, michael@0: entry->GetKey()->mAppId, michael@0: entry->GetKey()->mIsInBrowserElement, michael@0: data->types->ElementAt(permEntry.mType), michael@0: permEntry.mPermission, michael@0: permEntry.mExpireType, michael@0: permEntry.mExpireTime); michael@0: michael@0: data->array->AppendObject(perm); michael@0: } michael@0: michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsPermissionManager::GetEnumerator(nsISimpleEnumerator **aEnum) michael@0: { michael@0: // roll an nsCOMArray of all our permissions, then hand out an enumerator michael@0: nsCOMArray array; michael@0: nsGetEnumeratorData data(&array, &mTypeArray); michael@0: michael@0: mPermissionTable.EnumerateEntries(AddPermissionsToList, &data); michael@0: michael@0: return NS_NewArrayEnumerator(aEnum, array); michael@0: } michael@0: michael@0: NS_IMETHODIMP nsPermissionManager::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *someData) michael@0: { michael@0: ENSURE_NOT_CHILD_PROCESS; michael@0: michael@0: if (nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) { michael@0: if (!nsCRT::strcmp(someData, NS_LITERAL_STRING("permissions.memory_only").get())) { michael@0: // XXX: Should we remove the file? Probably not.. michael@0: InitDB(PR_FALSE); michael@0: } michael@0: } else if (!nsCRT::strcmp(aTopic, "profile-before-change")) { michael@0: // The profile is about to change, michael@0: // or is going away because the application is shutting down. michael@0: mIsShuttingDown = true; michael@0: if (!nsCRT::strcmp(someData, MOZ_UTF16("shutdown-cleanse"))) { michael@0: // Clear the permissions file and close the db asynchronously michael@0: RemoveAllInternal(false); michael@0: } else { michael@0: RemoveAllFromMemory(); michael@0: CloseDB(false); michael@0: } michael@0: } michael@0: else if (!nsCRT::strcmp(aTopic, "profile-do-change")) { michael@0: // the profile has already changed; init the db from the new location michael@0: InitDB(false); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: PLDHashOperator michael@0: nsPermissionManager::GetPermissionsForApp(nsPermissionManager::PermissionHashKey* entry, void* arg) michael@0: { michael@0: GetPermissionsForAppStruct* data = static_cast(arg); michael@0: michael@0: for (uint32_t i = 0; i < entry->GetPermissions().Length(); ++i) { michael@0: nsPermissionManager::PermissionEntry& permEntry = entry->GetPermissions()[i]; michael@0: michael@0: if (entry->GetKey()->mAppId != data->appId || michael@0: (data->browserOnly && !entry->GetKey()->mIsInBrowserElement)) { michael@0: continue; michael@0: } michael@0: michael@0: data->permissions.AppendObject(new nsPermission(entry->GetKey()->mHost, michael@0: entry->GetKey()->mAppId, michael@0: entry->GetKey()->mIsInBrowserElement, michael@0: gPermissionManager->mTypeArray.ElementAt(permEntry.mType), michael@0: permEntry.mPermission, michael@0: permEntry.mExpireType, michael@0: permEntry.mExpireTime)); michael@0: } michael@0: michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPermissionManager::RemovePermissionsForApp(uint32_t aAppId, bool aBrowserOnly) michael@0: { michael@0: ENSURE_NOT_CHILD_PROCESS; michael@0: NS_ENSURE_ARG(aAppId != nsIScriptSecurityManager::NO_APP_ID); michael@0: michael@0: // We begin by removing all the permissions from the DB. michael@0: // After clearing the DB, we call AddInternal() to make sure that all michael@0: // processes are aware of this change and the representation of the DB in michael@0: // memory is updated. michael@0: // We have to get all permissions associated with an application and then michael@0: // remove those because doing so in EnumerateEntries() would fail because michael@0: // we might happen to actually delete entries from the list. michael@0: michael@0: nsAutoCString sql; michael@0: sql.AppendLiteral("DELETE FROM moz_hosts WHERE appId="); michael@0: sql.AppendInt(aAppId); michael@0: michael@0: if (aBrowserOnly) { michael@0: sql.AppendLiteral(" AND isInBrowserElement=1"); michael@0: } michael@0: michael@0: nsCOMPtr removeStmt; michael@0: nsresult rv = mDBConn->CreateAsyncStatement(sql, getter_AddRefs(removeStmt)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr pending; michael@0: rv = removeStmt->ExecuteAsync(nullptr, getter_AddRefs(pending)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: GetPermissionsForAppStruct data(aAppId, aBrowserOnly); michael@0: mPermissionTable.EnumerateEntries(GetPermissionsForApp, &data); michael@0: michael@0: for (int32_t i=0; iGetHost(host); michael@0: data.permissions[i]->GetIsInBrowserElement(&isInBrowserElement); michael@0: data.permissions[i]->GetType(type); michael@0: michael@0: nsCOMPtr principal; michael@0: if (NS_FAILED(GetPrincipal(host, aAppId, isInBrowserElement, michael@0: getter_AddRefs(principal)))) { michael@0: NS_ERROR("GetPrincipal() failed!"); michael@0: continue; michael@0: } michael@0: michael@0: AddInternal(principal, michael@0: type, michael@0: nsIPermissionManager::UNKNOWN_ACTION, michael@0: 0, michael@0: nsIPermissionManager::EXPIRE_NEVER, michael@0: 0, michael@0: nsPermissionManager::eNotify, michael@0: nsPermissionManager::eNoDBOperation); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: PLDHashOperator michael@0: nsPermissionManager::RemoveExpiredPermissionsForAppEnumerator( michael@0: nsPermissionManager::PermissionHashKey* entry, void* arg) michael@0: { michael@0: uint32_t* appId = static_cast(arg); michael@0: michael@0: for (uint32_t i = 0; i < entry->GetPermissions().Length(); ++i) { michael@0: if (entry->GetKey()->mAppId != *appId) { michael@0: continue; michael@0: } michael@0: michael@0: nsPermissionManager::PermissionEntry& permEntry = entry->GetPermissions()[i]; michael@0: if (permEntry.mExpireType != nsIPermissionManager::EXPIRE_SESSION) { michael@0: continue; michael@0: } michael@0: michael@0: if (permEntry.mNonSessionExpireType == nsIPermissionManager::EXPIRE_SESSION) { michael@0: PermissionEntry oldPermissionEntry = entry->GetPermissions()[i]; michael@0: michael@0: entry->GetPermissions().RemoveElementAt(i); michael@0: michael@0: gPermissionManager->NotifyObserversWithPermission(entry->GetKey()->mHost, michael@0: entry->GetKey()->mAppId, michael@0: entry->GetKey()->mIsInBrowserElement, michael@0: gPermissionManager->mTypeArray.ElementAt(oldPermissionEntry.mType), michael@0: oldPermissionEntry.mPermission, michael@0: oldPermissionEntry.mExpireType, michael@0: oldPermissionEntry.mExpireTime, michael@0: MOZ_UTF16("deleted")); michael@0: --i; michael@0: continue; michael@0: } michael@0: michael@0: permEntry.mPermission = permEntry.mNonSessionPermission; michael@0: permEntry.mExpireType = permEntry.mNonSessionExpireType; michael@0: permEntry.mExpireTime = permEntry.mNonSessionExpireTime; michael@0: michael@0: gPermissionManager->NotifyObserversWithPermission(entry->GetKey()->mHost, michael@0: entry->GetKey()->mAppId, michael@0: entry->GetKey()->mIsInBrowserElement, michael@0: gPermissionManager->mTypeArray.ElementAt(permEntry.mType), michael@0: permEntry.mPermission, michael@0: permEntry.mExpireType, michael@0: permEntry.mExpireTime, michael@0: MOZ_UTF16("changed")); michael@0: } michael@0: michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: nsresult michael@0: nsPermissionManager::RemoveExpiredPermissionsForApp(uint32_t aAppId) michael@0: { michael@0: ENSURE_NOT_CHILD_PROCESS; michael@0: michael@0: if (aAppId != nsIScriptSecurityManager::NO_APP_ID) { michael@0: mPermissionTable.EnumerateEntries(RemoveExpiredPermissionsForAppEnumerator, &aAppId); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //***************************************************************************** michael@0: //*** nsPermissionManager private methods michael@0: //***************************************************************************** michael@0: michael@0: nsresult michael@0: nsPermissionManager::RemoveAllFromMemory() michael@0: { michael@0: mLargestID = 0; michael@0: mTypeArray.Clear(); michael@0: mPermissionTable.Clear(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Returns -1 on failure michael@0: int32_t michael@0: nsPermissionManager::GetTypeIndex(const char *aType, michael@0: bool aAdd) michael@0: { michael@0: for (uint32_t i = 0; i < mTypeArray.Length(); ++i) michael@0: if (mTypeArray[i].Equals(aType)) michael@0: return i; michael@0: michael@0: if (!aAdd) { michael@0: // Not found, but that is ok - we were just looking. michael@0: return -1; michael@0: } michael@0: michael@0: // This type was not registered before. michael@0: // append it to the array, without copy-constructing the string michael@0: nsCString *elem = mTypeArray.AppendElement(); michael@0: if (!elem) michael@0: return -1; michael@0: michael@0: elem->Assign(aType); michael@0: return mTypeArray.Length() - 1; michael@0: } michael@0: michael@0: // wrapper function for mangling (host,type,perm,expireType,expireTime) michael@0: // set into an nsIPermission. michael@0: void michael@0: nsPermissionManager::NotifyObserversWithPermission(const nsACString &aHost, michael@0: uint32_t aAppId, michael@0: bool aIsInBrowserElement, michael@0: const nsCString &aType, michael@0: uint32_t aPermission, michael@0: uint32_t aExpireType, michael@0: int64_t aExpireTime, michael@0: const char16_t *aData) michael@0: { michael@0: nsCOMPtr permission = michael@0: new nsPermission(aHost, aAppId, aIsInBrowserElement, aType, aPermission, michael@0: aExpireType, aExpireTime); michael@0: if (permission) michael@0: NotifyObservers(permission, aData); michael@0: } michael@0: michael@0: // notify observers that the permission list changed. there are four possible michael@0: // values for aData: michael@0: // "deleted" means a permission was deleted. aPermission is the deleted permission. michael@0: // "added" means a permission was added. aPermission is the added permission. michael@0: // "changed" means a permission was altered. aPermission is the new permission. michael@0: // "cleared" means the entire permission list was cleared. aPermission is null. michael@0: void michael@0: nsPermissionManager::NotifyObservers(nsIPermission *aPermission, michael@0: const char16_t *aData) michael@0: { michael@0: if (mObserverService) michael@0: mObserverService->NotifyObservers(aPermission, michael@0: kPermissionChangeNotification, michael@0: aData); michael@0: } michael@0: michael@0: nsresult michael@0: nsPermissionManager::Read() michael@0: { michael@0: ENSURE_NOT_CHILD_PROCESS; michael@0: michael@0: nsresult rv; michael@0: michael@0: // delete expired permissions before we read in the db michael@0: { michael@0: // this deletion has its own scope so the write lock is released when done. michael@0: nsCOMPtr stmtDeleteExpired; michael@0: rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( michael@0: "DELETE FROM moz_hosts WHERE expireType = ?1 AND expireTime <= ?2"), michael@0: getter_AddRefs(stmtDeleteExpired)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = stmtDeleteExpired->BindInt32ByIndex(0, nsIPermissionManager::EXPIRE_TIME); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = stmtDeleteExpired->BindInt64ByIndex(1, PR_Now() / 1000); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: bool hasResult; michael@0: rv = stmtDeleteExpired->ExecuteStep(&hasResult); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: nsCOMPtr stmt; michael@0: rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( michael@0: "SELECT id, host, type, permission, expireType, expireTime, appId, isInBrowserElement " michael@0: "FROM moz_hosts"), getter_AddRefs(stmt)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: int64_t id; michael@0: nsAutoCString host, type; michael@0: uint32_t permission; michael@0: uint32_t expireType; michael@0: int64_t expireTime; michael@0: uint32_t appId; michael@0: bool isInBrowserElement; michael@0: bool hasResult; michael@0: bool readError = false; michael@0: michael@0: while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) { michael@0: // explicitly set our entry id counter for use in AddInternal(), michael@0: // and keep track of the largest id so we know where to pick up. michael@0: id = stmt->AsInt64(0); michael@0: if (id > mLargestID) michael@0: mLargestID = id; michael@0: michael@0: rv = stmt->GetUTF8String(1, host); michael@0: if (NS_FAILED(rv)) { michael@0: readError = true; michael@0: continue; michael@0: } michael@0: michael@0: rv = stmt->GetUTF8String(2, type); michael@0: if (NS_FAILED(rv)) { michael@0: readError = true; michael@0: continue; michael@0: } michael@0: michael@0: permission = stmt->AsInt32(3); michael@0: expireType = stmt->AsInt32(4); michael@0: michael@0: // convert into int64_t value (milliseconds) michael@0: expireTime = stmt->AsInt64(5); michael@0: michael@0: if (stmt->AsInt64(6) < 0) { michael@0: readError = true; michael@0: continue; michael@0: } michael@0: appId = static_cast(stmt->AsInt64(6)); michael@0: isInBrowserElement = static_cast(stmt->AsInt32(7)); michael@0: michael@0: nsCOMPtr principal; michael@0: nsresult rv = GetPrincipal(host, appId, isInBrowserElement, getter_AddRefs(principal)); michael@0: if (NS_FAILED(rv)) { michael@0: readError = true; michael@0: continue; michael@0: } michael@0: michael@0: rv = AddInternal(principal, type, permission, id, expireType, expireTime, michael@0: eDontNotify, eNoDBOperation); michael@0: if (NS_FAILED(rv)) { michael@0: readError = true; michael@0: continue; michael@0: } michael@0: } michael@0: michael@0: if (readError) { michael@0: NS_ERROR("Error occured while reading the permissions database!"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: static const char kMatchTypeHost[] = "host"; michael@0: michael@0: nsresult michael@0: nsPermissionManager::Import() michael@0: { michael@0: ENSURE_NOT_CHILD_PROCESS; michael@0: michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr permissionsFile; michael@0: rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(permissionsFile)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = permissionsFile->AppendNative(NS_LITERAL_CSTRING(kHostpermFileName)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr fileInputStream; michael@0: rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream), michael@0: permissionsFile); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsCOMPtr lineInputStream = do_QueryInterface(fileInputStream, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // start a transaction on the storage db, to optimize insertions. michael@0: // transaction will automically commit on completion michael@0: mozStorageTransaction transaction(mDBConn, true); michael@0: michael@0: /* format is: michael@0: * matchtype \t type \t permission \t host michael@0: * Only "host" is supported for matchtype michael@0: * type is a string that identifies the type of permission (e.g. "cookie") michael@0: * permission is an integer between 1 and 15 michael@0: */ michael@0: michael@0: nsAutoCString buffer; michael@0: bool isMore = true; michael@0: while (isMore && NS_SUCCEEDED(lineInputStream->ReadLine(buffer, &isMore))) { michael@0: if (buffer.IsEmpty() || buffer.First() == '#') { michael@0: continue; michael@0: } michael@0: michael@0: nsTArray lineArray; michael@0: michael@0: // Split the line at tabs michael@0: ParseString(buffer, '\t', lineArray); michael@0: michael@0: if (lineArray[0].EqualsLiteral(kMatchTypeHost) && michael@0: lineArray.Length() == 4) { michael@0: michael@0: nsresult error; michael@0: uint32_t permission = lineArray[2].ToInteger(&error); michael@0: if (NS_FAILED(error)) michael@0: continue; michael@0: michael@0: // hosts might be encoded in UTF8; switch them to ACE to be consistent michael@0: if (!IsASCII(lineArray[3])) { michael@0: rv = NormalizeToACE(lineArray[3]); michael@0: if (NS_FAILED(rv)) michael@0: continue; michael@0: } michael@0: michael@0: nsCOMPtr principal; michael@0: nsresult rv = GetPrincipal(lineArray[3], getter_AddRefs(principal)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = AddInternal(principal, lineArray[1], permission, 0, michael@0: nsIPermissionManager::EXPIRE_NEVER, 0, eDontNotify, eWriteToDB); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: } michael@0: michael@0: // we're done importing - delete the old file michael@0: permissionsFile->Remove(false); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsPermissionManager::NormalizeToACE(nsCString &aHost) michael@0: { michael@0: // lazily init the IDN service michael@0: if (!mIDNService) { michael@0: nsresult rv; michael@0: mIDNService = do_GetService(NS_IDNSERVICE_CONTRACTID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return mIDNService->ConvertUTF8toACE(aHost, aHost); michael@0: } michael@0: michael@0: void michael@0: nsPermissionManager::UpdateDB(OperationType aOp, michael@0: mozIStorageAsyncStatement* aStmt, michael@0: int64_t aID, michael@0: const nsACString &aHost, michael@0: const nsACString &aType, michael@0: uint32_t aPermission, michael@0: uint32_t aExpireType, michael@0: int64_t aExpireTime, michael@0: uint32_t aAppId, michael@0: bool aIsInBrowserElement) michael@0: { michael@0: ENSURE_NOT_CHILD_PROCESS_NORET; michael@0: michael@0: nsresult rv; michael@0: michael@0: // no statement is ok - just means we don't have a profile michael@0: if (!aStmt) michael@0: return; michael@0: michael@0: switch (aOp) { michael@0: case eOperationAdding: michael@0: { michael@0: rv = aStmt->BindInt64ByIndex(0, aID); michael@0: if (NS_FAILED(rv)) break; michael@0: michael@0: rv = aStmt->BindUTF8StringByIndex(1, aHost); michael@0: if (NS_FAILED(rv)) break; michael@0: michael@0: rv = aStmt->BindUTF8StringByIndex(2, aType); michael@0: if (NS_FAILED(rv)) break; michael@0: michael@0: rv = aStmt->BindInt32ByIndex(3, aPermission); michael@0: if (NS_FAILED(rv)) break; michael@0: michael@0: rv = aStmt->BindInt32ByIndex(4, aExpireType); michael@0: if (NS_FAILED(rv)) break; michael@0: michael@0: rv = aStmt->BindInt64ByIndex(5, aExpireTime); michael@0: if (NS_FAILED(rv)) break; michael@0: michael@0: rv = aStmt->BindInt64ByIndex(6, aAppId); michael@0: if (NS_FAILED(rv)) break; michael@0: michael@0: rv = aStmt->BindInt64ByIndex(7, aIsInBrowserElement); michael@0: break; michael@0: } michael@0: michael@0: case eOperationRemoving: michael@0: { michael@0: rv = aStmt->BindInt64ByIndex(0, aID); michael@0: break; michael@0: } michael@0: michael@0: case eOperationChanging: michael@0: { michael@0: rv = aStmt->BindInt64ByIndex(0, aID); michael@0: if (NS_FAILED(rv)) break; michael@0: michael@0: rv = aStmt->BindInt32ByIndex(1, aPermission); michael@0: if (NS_FAILED(rv)) break; michael@0: michael@0: rv = aStmt->BindInt32ByIndex(2, aExpireType); michael@0: if (NS_FAILED(rv)) break; michael@0: michael@0: rv = aStmt->BindInt64ByIndex(3, aExpireTime); michael@0: break; michael@0: } michael@0: michael@0: default: michael@0: { michael@0: NS_NOTREACHED("need a valid operation in UpdateDB()!"); michael@0: rv = NS_ERROR_UNEXPECTED; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: NS_WARNING("db change failed!"); michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr pending; michael@0: rv = aStmt->ExecuteAsync(nullptr, getter_AddRefs(pending)); michael@0: MOZ_ASSERT(NS_SUCCEEDED(rv)); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPermissionManager::AddrefAppId(uint32_t aAppId) michael@0: { michael@0: if (aAppId == nsIScriptSecurityManager::NO_APP_ID) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool found = false; michael@0: for (uint32_t i = 0; i < mAppIdRefcounts.Length(); ++i) { michael@0: if (mAppIdRefcounts[i].mAppId == aAppId) { michael@0: ++mAppIdRefcounts[i].mCounter; michael@0: found = true; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (!found) { michael@0: ApplicationCounter app = { aAppId, 1 }; michael@0: mAppIdRefcounts.AppendElement(app); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPermissionManager::ReleaseAppId(uint32_t aAppId) michael@0: { michael@0: // An app has been released, maybe we have to reset its session. michael@0: michael@0: if (aAppId == nsIScriptSecurityManager::NO_APP_ID) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: for (uint32_t i = 0; i < mAppIdRefcounts.Length(); ++i) { michael@0: if (mAppIdRefcounts[i].mAppId == aAppId) { michael@0: --mAppIdRefcounts[i].mCounter; michael@0: michael@0: if (!mAppIdRefcounts[i].mCounter) { michael@0: mAppIdRefcounts.RemoveElementAt(i); michael@0: return RemoveExpiredPermissionsForApp(aAppId); michael@0: } michael@0: michael@0: break; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPermissionManager::UpdateExpireTime(nsIPrincipal* aPrincipal, michael@0: const char* aType, michael@0: bool aExactHostMatch, michael@0: uint64_t aSessionExpireTime, michael@0: uint64_t aPersistentExpireTime) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aPrincipal); michael@0: NS_ENSURE_ARG_POINTER(aType); michael@0: michael@0: uint64_t nowms = PR_Now() / 1000; michael@0: if (aSessionExpireTime < nowms || aPersistentExpireTime < nowms) { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: if (nsContentUtils::IsSystemPrincipal(aPrincipal)) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Setting the expire time of an nsEP is non-sensical. michael@0: if (IsExpandedPrincipal(aPrincipal)) { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: nsAutoCString host; michael@0: nsresult rv = GetHostForPrincipal(aPrincipal, host); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: int32_t typeIndex = GetTypeIndex(aType, false); michael@0: // If type == -1, the type isn't known, michael@0: // so just return NS_OK michael@0: if (typeIndex == -1) return NS_OK; michael@0: michael@0: uint32_t appId; michael@0: rv = aPrincipal->GetAppId(&appId); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: bool isInBrowserElement; michael@0: rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: PermissionHashKey* entry = GetPermissionHashKey(host, appId, isInBrowserElement, michael@0: typeIndex, aExactHostMatch); michael@0: if (!entry) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: int32_t idx = entry->GetPermissionIndex(typeIndex); michael@0: if (-1 == idx) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: PermissionEntry& perm = entry->GetPermissions()[idx]; michael@0: if (perm.mExpireType == EXPIRE_TIME) { michael@0: perm.mExpireTime = aPersistentExpireTime; michael@0: } else if (perm.mExpireType == EXPIRE_SESSION && perm.mExpireTime != 0) { michael@0: perm.mExpireTime = aSessionExpireTime; michael@0: } michael@0: return NS_OK; michael@0: }