extensions/cookie/nsPermissionManager.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "mozilla/Attributes.h"
michael@0 7 #include "mozilla/DebugOnly.h"
michael@0 8
michael@0 9 #include "mozilla/dom/ContentParent.h"
michael@0 10 #include "mozilla/dom/ContentChild.h"
michael@0 11 #include "mozilla/unused.h"
michael@0 12 #include "nsPermissionManager.h"
michael@0 13 #include "nsPermission.h"
michael@0 14 #include "nsCRT.h"
michael@0 15 #include "nsNetUtil.h"
michael@0 16 #include "nsCOMArray.h"
michael@0 17 #include "nsArrayEnumerator.h"
michael@0 18 #include "nsTArray.h"
michael@0 19 #include "nsReadableUtils.h"
michael@0 20 #include "nsILineInputStream.h"
michael@0 21 #include "nsIIDNService.h"
michael@0 22 #include "nsAppDirectoryServiceDefs.h"
michael@0 23 #include "prprf.h"
michael@0 24 #include "mozilla/storage.h"
michael@0 25 #include "mozilla/Attributes.h"
michael@0 26 #include "nsXULAppAPI.h"
michael@0 27 #include "nsIPrincipal.h"
michael@0 28 #include "nsContentUtils.h"
michael@0 29 #include "nsIScriptSecurityManager.h"
michael@0 30 #include "nsIAppsService.h"
michael@0 31 #include "mozIApplication.h"
michael@0 32 #include "nsIEffectiveTLDService.h"
michael@0 33 #include "nsPIDOMWindow.h"
michael@0 34 #include "nsIDocument.h"
michael@0 35 #include "nsCOMPtr.h"
michael@0 36 #include "nsIPrefService.h"
michael@0 37 #include "nsIPrefBranch.h"
michael@0 38 #include "nsIPrefBranch2.h"
michael@0 39 #include "mozilla/net/NeckoMessageUtils.h"
michael@0 40
michael@0 41 static nsPermissionManager *gPermissionManager = nullptr;
michael@0 42
michael@0 43 using mozilla::dom::ContentParent;
michael@0 44 using mozilla::dom::ContentChild;
michael@0 45 using mozilla::unused; // ha!
michael@0 46
michael@0 47 static bool
michael@0 48 IsChildProcess()
michael@0 49 {
michael@0 50 return XRE_GetProcessType() == GeckoProcessType_Content;
michael@0 51 }
michael@0 52
michael@0 53 /**
michael@0 54 * @returns The child process object, or if we are not in the child
michael@0 55 * process, nullptr.
michael@0 56 */
michael@0 57 static ContentChild*
michael@0 58 ChildProcess()
michael@0 59 {
michael@0 60 if (IsChildProcess()) {
michael@0 61 ContentChild* cpc = ContentChild::GetSingleton();
michael@0 62 if (!cpc)
michael@0 63 NS_RUNTIMEABORT("Content Process is nullptr!");
michael@0 64 return cpc;
michael@0 65 }
michael@0 66
michael@0 67 return nullptr;
michael@0 68 }
michael@0 69
michael@0 70
michael@0 71 #define ENSURE_NOT_CHILD_PROCESS_(onError) \
michael@0 72 PR_BEGIN_MACRO \
michael@0 73 if (IsChildProcess()) { \
michael@0 74 NS_ERROR("Cannot perform action in content process!"); \
michael@0 75 onError \
michael@0 76 } \
michael@0 77 PR_END_MACRO
michael@0 78
michael@0 79 #define ENSURE_NOT_CHILD_PROCESS \
michael@0 80 ENSURE_NOT_CHILD_PROCESS_({ return NS_ERROR_NOT_AVAILABLE; })
michael@0 81
michael@0 82 #define ENSURE_NOT_CHILD_PROCESS_NORET \
michael@0 83 ENSURE_NOT_CHILD_PROCESS_(;)
michael@0 84
michael@0 85 ////////////////////////////////////////////////////////////////////////////////
michael@0 86
michael@0 87 namespace {
michael@0 88
michael@0 89 nsresult
michael@0 90 GetPrincipal(const nsACString& aHost, uint32_t aAppId, bool aIsInBrowserElement,
michael@0 91 nsIPrincipal** aPrincipal)
michael@0 92 {
michael@0 93 nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
michael@0 94 NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
michael@0 95
michael@0 96 nsCOMPtr<nsIURI> uri;
michael@0 97 nsresult rv = NS_NewURI(getter_AddRefs(uri), aHost);
michael@0 98 if (NS_FAILED(rv)) {
michael@0 99 // NOTE: most callers will end up here because we don't append "http://" for
michael@0 100 // hosts. It's fine to arbitrary use "http://" because, for those entries,
michael@0 101 // we will actually just use the host. If we end up here, but the host looks
michael@0 102 // like an email address, we use mailto: instead.
michael@0 103 nsCString scheme;
michael@0 104 if (aHost.FindChar('@') == -1)
michael@0 105 scheme = NS_LITERAL_CSTRING("http://");
michael@0 106 else
michael@0 107 scheme = NS_LITERAL_CSTRING("mailto:");
michael@0 108 rv = NS_NewURI(getter_AddRefs(uri), scheme + aHost);
michael@0 109 NS_ENSURE_SUCCESS(rv, rv);
michael@0 110 }
michael@0 111
michael@0 112 return secMan->GetAppCodebasePrincipal(uri, aAppId, aIsInBrowserElement, aPrincipal);
michael@0 113 }
michael@0 114
michael@0 115 nsresult
michael@0 116 GetPrincipal(nsIURI* aURI, nsIPrincipal** aPrincipal)
michael@0 117 {
michael@0 118 nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
michael@0 119 NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
michael@0 120
michael@0 121 return secMan->GetNoAppCodebasePrincipal(aURI, aPrincipal);
michael@0 122 }
michael@0 123
michael@0 124 nsresult
michael@0 125 GetPrincipal(const nsACString& aHost, nsIPrincipal** aPrincipal)
michael@0 126 {
michael@0 127 return GetPrincipal(aHost, nsIScriptSecurityManager::NO_APP_ID, false, aPrincipal);
michael@0 128 }
michael@0 129
michael@0 130 nsresult
michael@0 131 GetHostForPrincipal(nsIPrincipal* aPrincipal, nsACString& aHost)
michael@0 132 {
michael@0 133 nsCOMPtr<nsIURI> uri;
michael@0 134 nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
michael@0 135 NS_ENSURE_SUCCESS(rv, rv);
michael@0 136
michael@0 137 uri = NS_GetInnermostURI(uri);
michael@0 138 NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
michael@0 139
michael@0 140 rv = uri->GetAsciiHost(aHost);
michael@0 141 if (NS_SUCCEEDED(rv) && !aHost.IsEmpty()) {
michael@0 142 return NS_OK;
michael@0 143 }
michael@0 144
michael@0 145 // For the mailto scheme, we use the path of the URI. We have to chop off the
michael@0 146 // query part if one exists, so we eliminate everything after a ?.
michael@0 147 bool isMailTo = false;
michael@0 148 if (NS_SUCCEEDED(uri->SchemeIs("mailto", &isMailTo)) && isMailTo) {
michael@0 149 rv = uri->GetPath(aHost);
michael@0 150 NS_ENSURE_SUCCESS(rv, rv);
michael@0 151
michael@0 152 int32_t spart = aHost.FindChar('?', 0);
michael@0 153 if (spart >= 0) {
michael@0 154 aHost.Cut(spart, aHost.Length() - spart);
michael@0 155 }
michael@0 156 return NS_OK;
michael@0 157 }
michael@0 158
michael@0 159 // Some entries like "file://" uses the origin.
michael@0 160 rv = aPrincipal->GetOrigin(getter_Copies(aHost));
michael@0 161 if (NS_SUCCEEDED(rv) && !aHost.IsEmpty()) {
michael@0 162 return NS_OK;
michael@0 163 }
michael@0 164
michael@0 165 return NS_ERROR_UNEXPECTED;
michael@0 166 }
michael@0 167
michael@0 168 nsCString
michael@0 169 GetNextSubDomainForHost(const nsACString& aHost)
michael@0 170 {
michael@0 171 nsCOMPtr<nsIEffectiveTLDService> tldService =
michael@0 172 do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
michael@0 173 if (!tldService) {
michael@0 174 NS_ERROR("Should have a tld service!");
michael@0 175 return EmptyCString();
michael@0 176 }
michael@0 177
michael@0 178 nsCString subDomain;
michael@0 179 nsresult rv = tldService->GetNextSubDomain(aHost, subDomain);
michael@0 180 // We can fail if there is no more subdomain or if the host can't have a
michael@0 181 // subdomain.
michael@0 182 if (NS_FAILED(rv)) {
michael@0 183 return EmptyCString();
michael@0 184 }
michael@0 185
michael@0 186 return subDomain;
michael@0 187 }
michael@0 188
michael@0 189 class AppClearDataObserver MOZ_FINAL : public nsIObserver {
michael@0 190 public:
michael@0 191 NS_DECL_ISUPPORTS
michael@0 192
michael@0 193 // nsIObserver implementation.
michael@0 194 NS_IMETHODIMP
michael@0 195 Observe(nsISupports *aSubject, const char *aTopic, const char16_t *data)
michael@0 196 {
michael@0 197 MOZ_ASSERT(!nsCRT::strcmp(aTopic, "webapps-clear-data"));
michael@0 198
michael@0 199 nsCOMPtr<mozIApplicationClearPrivateDataParams> params =
michael@0 200 do_QueryInterface(aSubject);
michael@0 201 if (!params) {
michael@0 202 NS_ERROR("'webapps-clear-data' notification's subject should be a mozIApplicationClearPrivateDataParams");
michael@0 203 return NS_ERROR_UNEXPECTED;
michael@0 204 }
michael@0 205
michael@0 206 uint32_t appId;
michael@0 207 nsresult rv = params->GetAppId(&appId);
michael@0 208 NS_ENSURE_SUCCESS(rv, rv);
michael@0 209
michael@0 210 bool browserOnly;
michael@0 211 rv = params->GetBrowserOnly(&browserOnly);
michael@0 212 NS_ENSURE_SUCCESS(rv, rv);
michael@0 213
michael@0 214 nsCOMPtr<nsIPermissionManager> permManager = do_GetService("@mozilla.org/permissionmanager;1");
michael@0 215 return permManager->RemovePermissionsForApp(appId, browserOnly);
michael@0 216 }
michael@0 217 };
michael@0 218
michael@0 219 NS_IMPL_ISUPPORTS(AppClearDataObserver, nsIObserver)
michael@0 220
michael@0 221 static bool
michael@0 222 IsExpandedPrincipal(nsIPrincipal* aPrincipal)
michael@0 223 {
michael@0 224 nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal);
michael@0 225 return !!ep;
michael@0 226 }
michael@0 227
michael@0 228 } // anonymous namespace
michael@0 229
michael@0 230 ////////////////////////////////////////////////////////////////////////////////
michael@0 231
michael@0 232 nsPermissionManager::PermissionKey::PermissionKey(nsIPrincipal* aPrincipal)
michael@0 233 {
michael@0 234 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(GetHostForPrincipal(aPrincipal, mHost)));
michael@0 235 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aPrincipal->GetAppId(&mAppId)));
michael@0 236 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aPrincipal->GetIsInBrowserElement(&mIsInBrowserElement)));
michael@0 237 }
michael@0 238
michael@0 239 /**
michael@0 240 * Simple callback used by |AsyncClose| to trigger a treatment once
michael@0 241 * the database is closed.
michael@0 242 *
michael@0 243 * Note: Beware that, if you hold onto a |CloseDatabaseListener| from a
michael@0 244 * |nsPermissionManager|, this will create a cycle.
michael@0 245 *
michael@0 246 * Note: Once the callback has been called this DeleteFromMozHostListener cannot
michael@0 247 * be reused.
michael@0 248 */
michael@0 249 class CloseDatabaseListener MOZ_FINAL : public mozIStorageCompletionCallback
michael@0 250 {
michael@0 251 public:
michael@0 252 NS_DECL_ISUPPORTS
michael@0 253 NS_DECL_MOZISTORAGECOMPLETIONCALLBACK
michael@0 254 /**
michael@0 255 * @param aManager The owning manager.
michael@0 256 * @param aRebuildOnSuccess If |true|, reinitialize the database once
michael@0 257 * it has been closed. Otherwise, do nothing such.
michael@0 258 */
michael@0 259 CloseDatabaseListener(nsPermissionManager* aManager,
michael@0 260 bool aRebuildOnSuccess);
michael@0 261
michael@0 262 protected:
michael@0 263 nsRefPtr<nsPermissionManager> mManager;
michael@0 264 bool mRebuildOnSuccess;
michael@0 265 };
michael@0 266
michael@0 267 NS_IMPL_ISUPPORTS(CloseDatabaseListener, mozIStorageCompletionCallback)
michael@0 268
michael@0 269 CloseDatabaseListener::CloseDatabaseListener(nsPermissionManager* aManager,
michael@0 270 bool aRebuildOnSuccess)
michael@0 271 : mManager(aManager)
michael@0 272 , mRebuildOnSuccess(aRebuildOnSuccess)
michael@0 273 {
michael@0 274 }
michael@0 275
michael@0 276 NS_IMETHODIMP
michael@0 277 CloseDatabaseListener::Complete(nsresult, nsISupports*)
michael@0 278 {
michael@0 279 // Help breaking cycles
michael@0 280 nsRefPtr<nsPermissionManager> manager = mManager.forget();
michael@0 281 if (mRebuildOnSuccess && !manager->mIsShuttingDown) {
michael@0 282 return manager->InitDB(true);
michael@0 283 }
michael@0 284 return NS_OK;
michael@0 285 }
michael@0 286
michael@0 287
michael@0 288 /**
michael@0 289 * Simple callback used by |RemoveAllInternal| to trigger closing
michael@0 290 * the database and reinitializing it.
michael@0 291 *
michael@0 292 * Note: Beware that, if you hold onto a |DeleteFromMozHostListener| from a
michael@0 293 * |nsPermissionManager|, this will create a cycle.
michael@0 294 *
michael@0 295 * Note: Once the callback has been called this DeleteFromMozHostListener cannot
michael@0 296 * be reused.
michael@0 297 */
michael@0 298 class DeleteFromMozHostListener MOZ_FINAL : public mozIStorageStatementCallback
michael@0 299 {
michael@0 300 public:
michael@0 301 NS_DECL_ISUPPORTS
michael@0 302 NS_DECL_MOZISTORAGESTATEMENTCALLBACK
michael@0 303
michael@0 304 /**
michael@0 305 * @param aManager The owning manager.
michael@0 306 */
michael@0 307 DeleteFromMozHostListener(nsPermissionManager* aManager);
michael@0 308
michael@0 309 protected:
michael@0 310 nsRefPtr<nsPermissionManager> mManager;
michael@0 311 };
michael@0 312
michael@0 313 NS_IMPL_ISUPPORTS(DeleteFromMozHostListener, mozIStorageStatementCallback)
michael@0 314
michael@0 315 DeleteFromMozHostListener::
michael@0 316 DeleteFromMozHostListener(nsPermissionManager* aManager)
michael@0 317 : mManager(aManager)
michael@0 318 {
michael@0 319 }
michael@0 320
michael@0 321 NS_IMETHODIMP DeleteFromMozHostListener::HandleResult(mozIStorageResultSet *)
michael@0 322 {
michael@0 323 MOZ_CRASH("Should not get any results");
michael@0 324 }
michael@0 325
michael@0 326 NS_IMETHODIMP DeleteFromMozHostListener::HandleError(mozIStorageError *)
michael@0 327 {
michael@0 328 // Errors are handled in |HandleCompletion|
michael@0 329 return NS_OK;
michael@0 330 }
michael@0 331
michael@0 332 NS_IMETHODIMP DeleteFromMozHostListener::HandleCompletion(uint16_t aReason)
michael@0 333 {
michael@0 334 // Help breaking cycles
michael@0 335 nsRefPtr<nsPermissionManager> manager = mManager.forget();
michael@0 336
michael@0 337 if (aReason == REASON_ERROR) {
michael@0 338 manager->CloseDB(true);
michael@0 339 }
michael@0 340
michael@0 341 return NS_OK;
michael@0 342 }
michael@0 343
michael@0 344 /* static */ void
michael@0 345 nsPermissionManager::AppClearDataObserverInit()
michael@0 346 {
michael@0 347 nsCOMPtr<nsIObserverService> observerService = do_GetService("@mozilla.org/observer-service;1");
michael@0 348 observerService->AddObserver(new AppClearDataObserver(), "webapps-clear-data", /* holdsWeak= */ false);
michael@0 349 }
michael@0 350
michael@0 351 ////////////////////////////////////////////////////////////////////////////////
michael@0 352 // nsPermissionManager Implementation
michael@0 353
michael@0 354 static const char kPermissionsFileName[] = "permissions.sqlite";
michael@0 355 #define HOSTS_SCHEMA_VERSION 3
michael@0 356
michael@0 357 static const char kHostpermFileName[] = "hostperm.1";
michael@0 358
michael@0 359 static const char kPermissionChangeNotification[] = PERM_CHANGE_NOTIFICATION;
michael@0 360
michael@0 361 NS_IMPL_ISUPPORTS(nsPermissionManager, nsIPermissionManager, nsIObserver, nsISupportsWeakReference)
michael@0 362
michael@0 363 nsPermissionManager::nsPermissionManager()
michael@0 364 : mLargestID(0)
michael@0 365 , mIsShuttingDown(false)
michael@0 366 {
michael@0 367 }
michael@0 368
michael@0 369 nsPermissionManager::~nsPermissionManager()
michael@0 370 {
michael@0 371 RemoveAllFromMemory();
michael@0 372 gPermissionManager = nullptr;
michael@0 373 }
michael@0 374
michael@0 375 // static
michael@0 376 nsIPermissionManager*
michael@0 377 nsPermissionManager::GetXPCOMSingleton()
michael@0 378 {
michael@0 379 if (gPermissionManager) {
michael@0 380 NS_ADDREF(gPermissionManager);
michael@0 381 return gPermissionManager;
michael@0 382 }
michael@0 383
michael@0 384 // Create a new singleton nsPermissionManager.
michael@0 385 // We AddRef only once since XPCOM has rules about the ordering of module
michael@0 386 // teardowns - by the time our module destructor is called, it's too late to
michael@0 387 // Release our members, since GC cycles have already been completed and
michael@0 388 // would result in serious leaks.
michael@0 389 // See bug 209571.
michael@0 390 gPermissionManager = new nsPermissionManager();
michael@0 391 if (gPermissionManager) {
michael@0 392 NS_ADDREF(gPermissionManager);
michael@0 393 if (NS_FAILED(gPermissionManager->Init())) {
michael@0 394 NS_RELEASE(gPermissionManager);
michael@0 395 }
michael@0 396 }
michael@0 397
michael@0 398 return gPermissionManager;
michael@0 399 }
michael@0 400
michael@0 401 nsresult
michael@0 402 nsPermissionManager::Init()
michael@0 403 {
michael@0 404 nsresult rv;
michael@0 405
michael@0 406 mObserverService = do_GetService("@mozilla.org/observer-service;1", &rv);
michael@0 407 if (NS_SUCCEEDED(rv)) {
michael@0 408 mObserverService->AddObserver(this, "profile-before-change", true);
michael@0 409 mObserverService->AddObserver(this, "profile-do-change", true);
michael@0 410 }
michael@0 411
michael@0 412
michael@0 413 nsCOMPtr<nsIPrefBranch2> pbi = do_GetService(NS_PREFSERVICE_CONTRACTID);
michael@0 414 if (pbi) {
michael@0 415 pbi->AddObserver("permissions.", this, PR_FALSE);
michael@0 416 }
michael@0 417
michael@0 418 if (IsChildProcess()) {
michael@0 419 // Get the permissions from the parent process
michael@0 420 InfallibleTArray<IPC::Permission> perms;
michael@0 421 ChildProcess()->SendReadPermissions(&perms);
michael@0 422
michael@0 423 for (uint32_t i = 0; i < perms.Length(); i++) {
michael@0 424 const IPC::Permission &perm = perms[i];
michael@0 425
michael@0 426 nsCOMPtr<nsIPrincipal> principal;
michael@0 427 rv = GetPrincipal(perm.host, perm.appId, perm.isInBrowserElement, getter_AddRefs(principal));
michael@0 428 NS_ENSURE_SUCCESS(rv, rv);
michael@0 429
michael@0 430 AddInternal(principal, perm.type, perm.capability, 0, perm.expireType,
michael@0 431 perm.expireTime, eNotify, eNoDBOperation);
michael@0 432 }
michael@0 433
michael@0 434 // Stop here; we don't need the DB in the child process
michael@0 435 return NS_OK;
michael@0 436 }
michael@0 437
michael@0 438 // ignore failure here, since it's non-fatal (we can run fine without
michael@0 439 // persistent storage - e.g. if there's no profile).
michael@0 440 // XXX should we tell the user about this?
michael@0 441 InitDB(false);
michael@0 442
michael@0 443 return NS_OK;
michael@0 444 }
michael@0 445
michael@0 446 nsresult
michael@0 447 nsPermissionManager::InitDB(bool aRemoveFile)
michael@0 448 {
michael@0 449 nsCOMPtr<nsIFile> permissionsFile;
michael@0 450 nsresult rv = NS_GetSpecialDirectory(NS_APP_PERMISSION_PARENT_DIR, getter_AddRefs(permissionsFile));
michael@0 451 if (NS_FAILED(rv)) {
michael@0 452 rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(permissionsFile));
michael@0 453 }
michael@0 454 NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
michael@0 455
michael@0 456 rv = permissionsFile->AppendNative(NS_LITERAL_CSTRING(kPermissionsFileName));
michael@0 457 NS_ENSURE_SUCCESS(rv, rv);
michael@0 458
michael@0 459 if (aRemoveFile) {
michael@0 460 bool exists = false;
michael@0 461 rv = permissionsFile->Exists(&exists);
michael@0 462 NS_ENSURE_SUCCESS(rv, rv);
michael@0 463 if (exists) {
michael@0 464 rv = permissionsFile->Remove(false);
michael@0 465 NS_ENSURE_SUCCESS(rv, rv);
michael@0 466 }
michael@0 467 }
michael@0 468
michael@0 469 nsCOMPtr<mozIStorageService> storage = do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
michael@0 470 if (!storage)
michael@0 471 return NS_ERROR_UNEXPECTED;
michael@0 472
michael@0 473 bool memory_db = false;
michael@0 474 nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
michael@0 475 if (prefs) {
michael@0 476 prefs->GetBoolPref("permissions.memory_only", &memory_db);
michael@0 477 }
michael@0 478
michael@0 479 // cache a connection to the hosts database
michael@0 480 if (memory_db) {
michael@0 481 rv = storage->OpenSpecialDatabase("memory", getter_AddRefs(mDBConn));
michael@0 482 } else {
michael@0 483 rv = storage->OpenDatabase(permissionsFile, getter_AddRefs(mDBConn));
michael@0 484 }
michael@0 485 NS_ENSURE_SUCCESS(rv, rv);
michael@0 486
michael@0 487 bool ready;
michael@0 488 mDBConn->GetConnectionReady(&ready);
michael@0 489 if (!ready) {
michael@0 490 // delete and try again
michael@0 491 rv = permissionsFile->Remove(false);
michael@0 492 NS_ENSURE_SUCCESS(rv, rv);
michael@0 493
michael@0 494 if (memory_db) {
michael@0 495 rv = storage->OpenSpecialDatabase("memory", getter_AddRefs(mDBConn));
michael@0 496 } else {
michael@0 497 rv = storage->OpenDatabase(permissionsFile, getter_AddRefs(mDBConn));
michael@0 498 }
michael@0 499 NS_ENSURE_SUCCESS(rv, rv);
michael@0 500
michael@0 501 mDBConn->GetConnectionReady(&ready);
michael@0 502 if (!ready)
michael@0 503 return NS_ERROR_UNEXPECTED;
michael@0 504 }
michael@0 505
michael@0 506 bool tableExists = false;
michael@0 507 mDBConn->TableExists(NS_LITERAL_CSTRING("moz_hosts"), &tableExists);
michael@0 508 if (!tableExists) {
michael@0 509 rv = CreateTable();
michael@0 510 NS_ENSURE_SUCCESS(rv, rv);
michael@0 511
michael@0 512 } else {
michael@0 513 // table already exists; check the schema version before reading
michael@0 514 int32_t dbSchemaVersion;
michael@0 515 rv = mDBConn->GetSchemaVersion(&dbSchemaVersion);
michael@0 516 NS_ENSURE_SUCCESS(rv, rv);
michael@0 517
michael@0 518 switch (dbSchemaVersion) {
michael@0 519 // upgrading.
michael@0 520 // every time you increment the database schema, you need to implement
michael@0 521 // the upgrading code from the previous version to the new one.
michael@0 522 // fall through to current version
michael@0 523
michael@0 524 case 1:
michael@0 525 {
michael@0 526 // previous non-expiry version of database. Upgrade it by adding the
michael@0 527 // expiration columns
michael@0 528 rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
michael@0 529 "ALTER TABLE moz_hosts ADD expireType INTEGER"));
michael@0 530 NS_ENSURE_SUCCESS(rv, rv);
michael@0 531
michael@0 532 rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
michael@0 533 "ALTER TABLE moz_hosts ADD expireTime INTEGER"));
michael@0 534 NS_ENSURE_SUCCESS(rv, rv);
michael@0 535 }
michael@0 536
michael@0 537 // fall through to the next upgrade
michael@0 538
michael@0 539 // TODO: we want to make default version as version 2 in order to fix bug 784875.
michael@0 540 case 0:
michael@0 541 case 2:
michael@0 542 {
michael@0 543 // Add appId/isInBrowserElement fields.
michael@0 544 rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
michael@0 545 "ALTER TABLE moz_hosts ADD appId INTEGER"));
michael@0 546 NS_ENSURE_SUCCESS(rv, rv);
michael@0 547
michael@0 548 rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
michael@0 549 "ALTER TABLE moz_hosts ADD isInBrowserElement INTEGER"));
michael@0 550 NS_ENSURE_SUCCESS(rv, rv);
michael@0 551
michael@0 552 rv = mDBConn->SetSchemaVersion(HOSTS_SCHEMA_VERSION);
michael@0 553 NS_ENSURE_SUCCESS(rv, rv);
michael@0 554 }
michael@0 555
michael@0 556 // fall through to the next upgrade
michael@0 557
michael@0 558 // current version.
michael@0 559 case HOSTS_SCHEMA_VERSION:
michael@0 560 break;
michael@0 561
michael@0 562 // downgrading.
michael@0 563 // if columns have been added to the table, we can still use the ones we
michael@0 564 // understand safely. if columns have been deleted or altered, just
michael@0 565 // blow away the table and start from scratch! if you change the way
michael@0 566 // a column is interpreted, make sure you also change its name so this
michael@0 567 // check will catch it.
michael@0 568 default:
michael@0 569 {
michael@0 570 // check if all the expected columns exist
michael@0 571 nsCOMPtr<mozIStorageStatement> stmt;
michael@0 572 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
michael@0 573 "SELECT host, type, permission, expireType, expireTime, appId, isInBrowserElement FROM moz_hosts"),
michael@0 574 getter_AddRefs(stmt));
michael@0 575 if (NS_SUCCEEDED(rv))
michael@0 576 break;
michael@0 577
michael@0 578 // our columns aren't there - drop the table!
michael@0 579 rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DROP TABLE moz_hosts"));
michael@0 580 NS_ENSURE_SUCCESS(rv, rv);
michael@0 581
michael@0 582 rv = CreateTable();
michael@0 583 NS_ENSURE_SUCCESS(rv, rv);
michael@0 584 }
michael@0 585 break;
michael@0 586 }
michael@0 587 }
michael@0 588
michael@0 589 // make operations on the table asynchronous, for performance
michael@0 590 mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("PRAGMA synchronous = OFF"));
michael@0 591
michael@0 592 // cache frequently used statements (for insertion, deletion, and updating)
michael@0 593 rv = mDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
michael@0 594 "INSERT INTO moz_hosts "
michael@0 595 "(id, host, type, permission, expireType, expireTime, appId, isInBrowserElement) "
michael@0 596 "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)"), getter_AddRefs(mStmtInsert));
michael@0 597 NS_ENSURE_SUCCESS(rv, rv);
michael@0 598
michael@0 599 rv = mDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
michael@0 600 "DELETE FROM moz_hosts "
michael@0 601 "WHERE id = ?1"), getter_AddRefs(mStmtDelete));
michael@0 602 NS_ENSURE_SUCCESS(rv, rv);
michael@0 603
michael@0 604 rv = mDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
michael@0 605 "UPDATE moz_hosts "
michael@0 606 "SET permission = ?2, expireType= ?3, expireTime = ?4 WHERE id = ?1"),
michael@0 607 getter_AddRefs(mStmtUpdate));
michael@0 608 NS_ENSURE_SUCCESS(rv, rv);
michael@0 609
michael@0 610 // check whether to import or just read in the db
michael@0 611 if (tableExists)
michael@0 612 return Read();
michael@0 613
michael@0 614 return Import();
michael@0 615 }
michael@0 616
michael@0 617 // sets the schema version and creates the moz_hosts table.
michael@0 618 nsresult
michael@0 619 nsPermissionManager::CreateTable()
michael@0 620 {
michael@0 621 // set the schema version, before creating the table
michael@0 622 nsresult rv = mDBConn->SetSchemaVersion(HOSTS_SCHEMA_VERSION);
michael@0 623 if (NS_FAILED(rv)) return rv;
michael@0 624
michael@0 625 // create the table
michael@0 626 // SQL also lives in automation.py.in. If you change this SQL change that
michael@0 627 // one too.
michael@0 628 return mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
michael@0 629 "CREATE TABLE moz_hosts ("
michael@0 630 " id INTEGER PRIMARY KEY"
michael@0 631 ",host TEXT"
michael@0 632 ",type TEXT"
michael@0 633 ",permission INTEGER"
michael@0 634 ",expireType INTEGER"
michael@0 635 ",expireTime INTEGER"
michael@0 636 ",appId INTEGER"
michael@0 637 ",isInBrowserElement INTEGER"
michael@0 638 ")"));
michael@0 639 }
michael@0 640
michael@0 641 NS_IMETHODIMP
michael@0 642 nsPermissionManager::Add(nsIURI *aURI,
michael@0 643 const char *aType,
michael@0 644 uint32_t aPermission,
michael@0 645 uint32_t aExpireType,
michael@0 646 int64_t aExpireTime)
michael@0 647 {
michael@0 648 NS_ENSURE_ARG_POINTER(aURI);
michael@0 649
michael@0 650 nsCOMPtr<nsIPrincipal> principal;
michael@0 651 nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal));
michael@0 652 NS_ENSURE_SUCCESS(rv, rv);
michael@0 653
michael@0 654 return AddFromPrincipal(principal, aType, aPermission, aExpireType, aExpireTime);
michael@0 655 }
michael@0 656
michael@0 657 NS_IMETHODIMP
michael@0 658 nsPermissionManager::AddFromPrincipal(nsIPrincipal* aPrincipal,
michael@0 659 const char* aType, uint32_t aPermission,
michael@0 660 uint32_t aExpireType, int64_t aExpireTime)
michael@0 661 {
michael@0 662 ENSURE_NOT_CHILD_PROCESS;
michael@0 663 NS_ENSURE_ARG_POINTER(aPrincipal);
michael@0 664 NS_ENSURE_ARG_POINTER(aType);
michael@0 665 NS_ENSURE_TRUE(aExpireType == nsIPermissionManager::EXPIRE_NEVER ||
michael@0 666 aExpireType == nsIPermissionManager::EXPIRE_TIME ||
michael@0 667 aExpireType == nsIPermissionManager::EXPIRE_SESSION,
michael@0 668 NS_ERROR_INVALID_ARG);
michael@0 669
michael@0 670 // Skip addition if the permission is already expired. Note that EXPIRE_SESSION only
michael@0 671 // honors expireTime if it is nonzero.
michael@0 672 if ((aExpireType == nsIPermissionManager::EXPIRE_TIME ||
michael@0 673 (aExpireType == nsIPermissionManager::EXPIRE_SESSION && aExpireTime != 0)) &&
michael@0 674 aExpireTime <= (PR_Now() / 1000)) {
michael@0 675 return NS_OK;
michael@0 676 }
michael@0 677
michael@0 678 // We don't add the system principal because it actually has no URI and we
michael@0 679 // always allow action for them.
michael@0 680 if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
michael@0 681 return NS_OK;
michael@0 682 }
michael@0 683
michael@0 684 // Permissions may not be added to expanded principals.
michael@0 685 if (IsExpandedPrincipal(aPrincipal)) {
michael@0 686 return NS_ERROR_INVALID_ARG;
michael@0 687 }
michael@0 688
michael@0 689 return AddInternal(aPrincipal, nsDependentCString(aType), aPermission, 0,
michael@0 690 aExpireType, aExpireTime, eNotify, eWriteToDB);
michael@0 691 }
michael@0 692
michael@0 693 nsresult
michael@0 694 nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal,
michael@0 695 const nsAFlatCString &aType,
michael@0 696 uint32_t aPermission,
michael@0 697 int64_t aID,
michael@0 698 uint32_t aExpireType,
michael@0 699 int64_t aExpireTime,
michael@0 700 NotifyOperationType aNotifyOperation,
michael@0 701 DBOperationType aDBOperation)
michael@0 702 {
michael@0 703 nsAutoCString host;
michael@0 704 nsresult rv = GetHostForPrincipal(aPrincipal, host);
michael@0 705 NS_ENSURE_SUCCESS(rv, rv);
michael@0 706
michael@0 707 if (!IsChildProcess()) {
michael@0 708 uint32_t appId;
michael@0 709 rv = aPrincipal->GetAppId(&appId);
michael@0 710 NS_ENSURE_SUCCESS(rv, rv);
michael@0 711
michael@0 712 bool isInBrowserElement;
michael@0 713 rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
michael@0 714 NS_ENSURE_SUCCESS(rv, rv);
michael@0 715
michael@0 716 IPC::Permission permission(host, appId, isInBrowserElement, aType,
michael@0 717 aPermission, aExpireType, aExpireTime);
michael@0 718
michael@0 719 nsTArray<ContentParent*> cplist;
michael@0 720 ContentParent::GetAll(cplist);
michael@0 721 for (uint32_t i = 0; i < cplist.Length(); ++i) {
michael@0 722 ContentParent* cp = cplist[i];
michael@0 723 if (cp->NeedsPermissionsUpdate())
michael@0 724 unused << cp->SendAddPermission(permission);
michael@0 725 }
michael@0 726 }
michael@0 727
michael@0 728 // look up the type index
michael@0 729 int32_t typeIndex = GetTypeIndex(aType.get(), true);
michael@0 730 NS_ENSURE_TRUE(typeIndex != -1, NS_ERROR_OUT_OF_MEMORY);
michael@0 731
michael@0 732 // When an entry already exists, PutEntry will return that, instead
michael@0 733 // of adding a new one
michael@0 734 nsRefPtr<PermissionKey> key = new PermissionKey(aPrincipal);
michael@0 735 PermissionHashKey* entry = mPermissionTable.PutEntry(key);
michael@0 736 if (!entry) return NS_ERROR_FAILURE;
michael@0 737 if (!entry->GetKey()) {
michael@0 738 mPermissionTable.RawRemoveEntry(entry);
michael@0 739 return NS_ERROR_OUT_OF_MEMORY;
michael@0 740 }
michael@0 741
michael@0 742 // figure out the transaction type, and get any existing permission value
michael@0 743 OperationType op;
michael@0 744 int32_t index = entry->GetPermissionIndex(typeIndex);
michael@0 745 if (index == -1) {
michael@0 746 if (aPermission == nsIPermissionManager::UNKNOWN_ACTION)
michael@0 747 op = eOperationNone;
michael@0 748 else
michael@0 749 op = eOperationAdding;
michael@0 750
michael@0 751 } else {
michael@0 752 PermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
michael@0 753
michael@0 754 // remove the permission if the permission is UNKNOWN, update the
michael@0 755 // permission if its value or expire type have changed OR if the time has
michael@0 756 // changed and the expire type is time, otherwise, don't modify. There's
michael@0 757 // no need to modify a permission that doesn't expire with time when the
michael@0 758 // only thing changed is the expire time.
michael@0 759 if (aPermission == oldPermissionEntry.mPermission &&
michael@0 760 aExpireType == oldPermissionEntry.mExpireType &&
michael@0 761 (aExpireType == nsIPermissionManager::EXPIRE_NEVER ||
michael@0 762 aExpireTime == oldPermissionEntry.mExpireTime))
michael@0 763 op = eOperationNone;
michael@0 764 else if (aPermission == nsIPermissionManager::UNKNOWN_ACTION)
michael@0 765 op = eOperationRemoving;
michael@0 766 else
michael@0 767 op = eOperationChanging;
michael@0 768 }
michael@0 769
michael@0 770 // do the work for adding, deleting, or changing a permission:
michael@0 771 // update the in-memory list, write to the db, and notify consumers.
michael@0 772 int64_t id;
michael@0 773 switch (op) {
michael@0 774 case eOperationNone:
michael@0 775 {
michael@0 776 // nothing to do
michael@0 777 return NS_OK;
michael@0 778 }
michael@0 779
michael@0 780 case eOperationAdding:
michael@0 781 {
michael@0 782 if (aDBOperation == eWriteToDB) {
michael@0 783 // we'll be writing to the database - generate a known unique id
michael@0 784 id = ++mLargestID;
michael@0 785 } else {
michael@0 786 // we're reading from the database - use the id already assigned
michael@0 787 id = aID;
michael@0 788 }
michael@0 789
michael@0 790 entry->GetPermissions().AppendElement(PermissionEntry(id, typeIndex, aPermission, aExpireType, aExpireTime));
michael@0 791
michael@0 792 if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION) {
michael@0 793 uint32_t appId;
michael@0 794 rv = aPrincipal->GetAppId(&appId);
michael@0 795 NS_ENSURE_SUCCESS(rv, rv);
michael@0 796
michael@0 797 bool isInBrowserElement;
michael@0 798 rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
michael@0 799 NS_ENSURE_SUCCESS(rv, rv);
michael@0 800
michael@0 801 UpdateDB(op, mStmtInsert, id, host, aType, aPermission, aExpireType, aExpireTime, appId, isInBrowserElement);
michael@0 802 }
michael@0 803
michael@0 804 if (aNotifyOperation == eNotify) {
michael@0 805 NotifyObserversWithPermission(host,
michael@0 806 entry->GetKey()->mAppId,
michael@0 807 entry->GetKey()->mIsInBrowserElement,
michael@0 808 mTypeArray[typeIndex],
michael@0 809 aPermission,
michael@0 810 aExpireType,
michael@0 811 aExpireTime,
michael@0 812 MOZ_UTF16("added"));
michael@0 813 }
michael@0 814
michael@0 815 break;
michael@0 816 }
michael@0 817
michael@0 818 case eOperationRemoving:
michael@0 819 {
michael@0 820 PermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
michael@0 821 id = oldPermissionEntry.mID;
michael@0 822 entry->GetPermissions().RemoveElementAt(index);
michael@0 823
michael@0 824 if (aDBOperation == eWriteToDB)
michael@0 825 // We care only about the id here so we pass dummy values for all other
michael@0 826 // parameters.
michael@0 827 UpdateDB(op, mStmtDelete, id, EmptyCString(), EmptyCString(), 0,
michael@0 828 nsIPermissionManager::EXPIRE_NEVER, 0, 0, false);
michael@0 829
michael@0 830 if (aNotifyOperation == eNotify) {
michael@0 831 NotifyObserversWithPermission(host,
michael@0 832 entry->GetKey()->mAppId,
michael@0 833 entry->GetKey()->mIsInBrowserElement,
michael@0 834 mTypeArray[typeIndex],
michael@0 835 oldPermissionEntry.mPermission,
michael@0 836 oldPermissionEntry.mExpireType,
michael@0 837 oldPermissionEntry.mExpireTime,
michael@0 838 MOZ_UTF16("deleted"));
michael@0 839 }
michael@0 840
michael@0 841 // If there are no more permissions stored for that entry, clear it.
michael@0 842 if (entry->GetPermissions().IsEmpty()) {
michael@0 843 mPermissionTable.RawRemoveEntry(entry);
michael@0 844 }
michael@0 845
michael@0 846 break;
michael@0 847 }
michael@0 848
michael@0 849 case eOperationChanging:
michael@0 850 {
michael@0 851 id = entry->GetPermissions()[index].mID;
michael@0 852
michael@0 853 // If the new expireType is EXPIRE_SESSION, then we have to keep a
michael@0 854 // copy of the previous permission/expireType values. This cached value will be
michael@0 855 // used when restoring the permissions of an app.
michael@0 856 if (entry->GetPermissions()[index].mExpireType != nsIPermissionManager::EXPIRE_SESSION &&
michael@0 857 aExpireType == nsIPermissionManager::EXPIRE_SESSION) {
michael@0 858 entry->GetPermissions()[index].mNonSessionPermission = entry->GetPermissions()[index].mPermission;
michael@0 859 entry->GetPermissions()[index].mNonSessionExpireType = entry->GetPermissions()[index].mExpireType;
michael@0 860 entry->GetPermissions()[index].mNonSessionExpireTime = entry->GetPermissions()[index].mExpireTime;
michael@0 861 } else if (aExpireType != nsIPermissionManager::EXPIRE_SESSION) {
michael@0 862 entry->GetPermissions()[index].mNonSessionPermission = aPermission;
michael@0 863 entry->GetPermissions()[index].mNonSessionExpireType = aExpireType;
michael@0 864 entry->GetPermissions()[index].mNonSessionExpireTime = aExpireTime;
michael@0 865 }
michael@0 866
michael@0 867 entry->GetPermissions()[index].mPermission = aPermission;
michael@0 868 entry->GetPermissions()[index].mExpireType = aExpireType;
michael@0 869 entry->GetPermissions()[index].mExpireTime = aExpireTime;
michael@0 870
michael@0 871 if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION)
michael@0 872 // We care only about the id, the permission and expireType/expireTime here.
michael@0 873 // We pass dummy values for all other parameters.
michael@0 874 UpdateDB(op, mStmtUpdate, id, EmptyCString(), EmptyCString(),
michael@0 875 aPermission, aExpireType, aExpireTime, 0, false);
michael@0 876
michael@0 877 if (aNotifyOperation == eNotify) {
michael@0 878 NotifyObserversWithPermission(host,
michael@0 879 entry->GetKey()->mAppId,
michael@0 880 entry->GetKey()->mIsInBrowserElement,
michael@0 881 mTypeArray[typeIndex],
michael@0 882 aPermission,
michael@0 883 aExpireType,
michael@0 884 aExpireTime,
michael@0 885 MOZ_UTF16("changed"));
michael@0 886 }
michael@0 887
michael@0 888 break;
michael@0 889 }
michael@0 890 }
michael@0 891
michael@0 892 return NS_OK;
michael@0 893 }
michael@0 894
michael@0 895 NS_IMETHODIMP
michael@0 896 nsPermissionManager::Remove(const nsACString &aHost,
michael@0 897 const char *aType)
michael@0 898 {
michael@0 899 nsCOMPtr<nsIPrincipal> principal;
michael@0 900 nsresult rv = GetPrincipal(aHost, getter_AddRefs(principal));
michael@0 901 NS_ENSURE_SUCCESS(rv, rv);
michael@0 902
michael@0 903 return RemoveFromPrincipal(principal, aType);
michael@0 904 }
michael@0 905
michael@0 906 NS_IMETHODIMP
michael@0 907 nsPermissionManager::RemoveFromPrincipal(nsIPrincipal* aPrincipal,
michael@0 908 const char* aType)
michael@0 909 {
michael@0 910 ENSURE_NOT_CHILD_PROCESS;
michael@0 911 NS_ENSURE_ARG_POINTER(aPrincipal);
michael@0 912 NS_ENSURE_ARG_POINTER(aType);
michael@0 913
michael@0 914 // System principals are never added to the database, no need to remove them.
michael@0 915 if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
michael@0 916 return NS_OK;
michael@0 917 }
michael@0 918
michael@0 919 // Permissions may not be added to expanded principals.
michael@0 920 if (IsExpandedPrincipal(aPrincipal)) {
michael@0 921 return NS_ERROR_INVALID_ARG;
michael@0 922 }
michael@0 923
michael@0 924 // AddInternal() handles removal, just let it do the work
michael@0 925 return AddInternal(aPrincipal,
michael@0 926 nsDependentCString(aType),
michael@0 927 nsIPermissionManager::UNKNOWN_ACTION,
michael@0 928 0,
michael@0 929 nsIPermissionManager::EXPIRE_NEVER,
michael@0 930 0,
michael@0 931 eNotify,
michael@0 932 eWriteToDB);
michael@0 933 }
michael@0 934
michael@0 935 NS_IMETHODIMP
michael@0 936 nsPermissionManager::RemoveAll()
michael@0 937 {
michael@0 938 ENSURE_NOT_CHILD_PROCESS;
michael@0 939 return RemoveAllInternal(true);
michael@0 940 }
michael@0 941
michael@0 942 void
michael@0 943 nsPermissionManager::CloseDB(bool aRebuildOnSuccess)
michael@0 944 {
michael@0 945 // Null the statements, this will finalize them.
michael@0 946 mStmtInsert = nullptr;
michael@0 947 mStmtDelete = nullptr;
michael@0 948 mStmtUpdate = nullptr;
michael@0 949 if (mDBConn) {
michael@0 950 mozIStorageCompletionCallback* cb = new CloseDatabaseListener(this,
michael@0 951 aRebuildOnSuccess);
michael@0 952 mozilla::DebugOnly<nsresult> rv = mDBConn->AsyncClose(cb);
michael@0 953 MOZ_ASSERT(NS_SUCCEEDED(rv));
michael@0 954 mDBConn = nullptr; // Avoid race conditions
michael@0 955 }
michael@0 956 }
michael@0 957
michael@0 958 nsresult
michael@0 959 nsPermissionManager::RemoveAllInternal(bool aNotifyObservers)
michael@0 960 {
michael@0 961 // Remove from memory and notify immediately. Since the in-memory
michael@0 962 // database is authoritative, we do not need confirmation from the
michael@0 963 // on-disk database to notify observers.
michael@0 964 RemoveAllFromMemory();
michael@0 965 if (aNotifyObservers) {
michael@0 966 NotifyObservers(nullptr, MOZ_UTF16("cleared"));
michael@0 967 }
michael@0 968
michael@0 969 // clear the db
michael@0 970 if (mDBConn) {
michael@0 971 nsCOMPtr<mozIStorageAsyncStatement> removeStmt;
michael@0 972 nsresult rv = mDBConn->
michael@0 973 CreateAsyncStatement(NS_LITERAL_CSTRING(
michael@0 974 "DELETE FROM moz_hosts"
michael@0 975 ), getter_AddRefs(removeStmt));
michael@0 976 MOZ_ASSERT(NS_SUCCEEDED(rv));
michael@0 977 if (!removeStmt) {
michael@0 978 return NS_ERROR_UNEXPECTED;
michael@0 979 }
michael@0 980 nsCOMPtr<mozIStoragePendingStatement> pending;
michael@0 981 mozIStorageStatementCallback* cb = new DeleteFromMozHostListener(this);
michael@0 982 rv = removeStmt->ExecuteAsync(cb, getter_AddRefs(pending));
michael@0 983 MOZ_ASSERT(NS_SUCCEEDED(rv));
michael@0 984
michael@0 985 return rv;
michael@0 986 }
michael@0 987
michael@0 988 return NS_OK;
michael@0 989 }
michael@0 990
michael@0 991 NS_IMETHODIMP
michael@0 992 nsPermissionManager::TestExactPermission(nsIURI *aURI,
michael@0 993 const char *aType,
michael@0 994 uint32_t *aPermission)
michael@0 995 {
michael@0 996 nsCOMPtr<nsIPrincipal> principal;
michael@0 997 nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal));
michael@0 998 NS_ENSURE_SUCCESS(rv, rv);
michael@0 999
michael@0 1000 return TestExactPermissionFromPrincipal(principal, aType, aPermission);
michael@0 1001 }
michael@0 1002
michael@0 1003 NS_IMETHODIMP
michael@0 1004 nsPermissionManager::TestExactPermissionFromPrincipal(nsIPrincipal* aPrincipal,
michael@0 1005 const char* aType,
michael@0 1006 uint32_t* aPermission)
michael@0 1007 {
michael@0 1008 return CommonTestPermission(aPrincipal, aType, aPermission, true, true);
michael@0 1009 }
michael@0 1010
michael@0 1011 NS_IMETHODIMP
michael@0 1012 nsPermissionManager::TestExactPermanentPermission(nsIPrincipal* aPrincipal,
michael@0 1013 const char* aType,
michael@0 1014 uint32_t* aPermission)
michael@0 1015 {
michael@0 1016 return CommonTestPermission(aPrincipal, aType, aPermission, true, false);
michael@0 1017 }
michael@0 1018
michael@0 1019 NS_IMETHODIMP
michael@0 1020 nsPermissionManager::TestPermission(nsIURI *aURI,
michael@0 1021 const char *aType,
michael@0 1022 uint32_t *aPermission)
michael@0 1023 {
michael@0 1024 nsCOMPtr<nsIPrincipal> principal;
michael@0 1025 nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal));
michael@0 1026 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1027
michael@0 1028 return TestPermissionFromPrincipal(principal, aType, aPermission);
michael@0 1029 }
michael@0 1030
michael@0 1031 NS_IMETHODIMP
michael@0 1032 nsPermissionManager::TestPermissionFromWindow(nsIDOMWindow* aWindow,
michael@0 1033 const char* aType,
michael@0 1034 uint32_t* aPermission)
michael@0 1035 {
michael@0 1036 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
michael@0 1037 NS_ENSURE_TRUE(window, NS_NOINTERFACE);
michael@0 1038
michael@0 1039 nsPIDOMWindow* innerWindow = window->IsInnerWindow() ?
michael@0 1040 window.get() :
michael@0 1041 window->GetCurrentInnerWindow();
michael@0 1042
michael@0 1043 // Get the document for security check
michael@0 1044 nsCOMPtr<nsIDocument> document = innerWindow->GetExtantDoc();
michael@0 1045 NS_ENSURE_TRUE(document, NS_NOINTERFACE);
michael@0 1046
michael@0 1047 nsCOMPtr<nsIPrincipal> principal = document->NodePrincipal();
michael@0 1048 return TestPermissionFromPrincipal(principal, aType, aPermission);
michael@0 1049 }
michael@0 1050
michael@0 1051 NS_IMETHODIMP
michael@0 1052 nsPermissionManager::TestPermissionFromPrincipal(nsIPrincipal* aPrincipal,
michael@0 1053 const char* aType,
michael@0 1054 uint32_t* aPermission)
michael@0 1055 {
michael@0 1056 return CommonTestPermission(aPrincipal, aType, aPermission, false, true);
michael@0 1057 }
michael@0 1058
michael@0 1059 NS_IMETHODIMP
michael@0 1060 nsPermissionManager::GetPermissionObject(nsIPrincipal* aPrincipal,
michael@0 1061 const char* aType,
michael@0 1062 bool aExactHostMatch,
michael@0 1063 nsIPermission** aResult)
michael@0 1064 {
michael@0 1065 NS_ENSURE_ARG_POINTER(aPrincipal);
michael@0 1066 NS_ENSURE_ARG_POINTER(aType);
michael@0 1067
michael@0 1068 *aResult = nullptr;
michael@0 1069
michael@0 1070 if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
michael@0 1071 return NS_OK;
michael@0 1072 }
michael@0 1073
michael@0 1074 // Querying the permission object of an nsEP is non-sensical.
michael@0 1075 if (IsExpandedPrincipal(aPrincipal)) {
michael@0 1076 return NS_ERROR_INVALID_ARG;
michael@0 1077 }
michael@0 1078
michael@0 1079 nsAutoCString host;
michael@0 1080 nsresult rv = GetHostForPrincipal(aPrincipal, host);
michael@0 1081 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1082
michael@0 1083 int32_t typeIndex = GetTypeIndex(aType, false);
michael@0 1084 // If type == -1, the type isn't known,
michael@0 1085 // so just return NS_OK
michael@0 1086 if (typeIndex == -1) return NS_OK;
michael@0 1087
michael@0 1088 uint32_t appId;
michael@0 1089 rv = aPrincipal->GetAppId(&appId);
michael@0 1090 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1091
michael@0 1092 bool isInBrowserElement;
michael@0 1093 rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
michael@0 1094 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1095
michael@0 1096 PermissionHashKey* entry = GetPermissionHashKey(host, appId, isInBrowserElement,
michael@0 1097 typeIndex, aExactHostMatch);
michael@0 1098 if (!entry) {
michael@0 1099 return NS_OK;
michael@0 1100 }
michael@0 1101
michael@0 1102 // We don't call GetPermission(typeIndex) because that returns a fake
michael@0 1103 // UNKNOWN_ACTION entry if there is no match.
michael@0 1104 int32_t idx = entry->GetPermissionIndex(typeIndex);
michael@0 1105 if (-1 == idx) {
michael@0 1106 return NS_OK;
michael@0 1107 }
michael@0 1108
michael@0 1109 PermissionEntry& perm = entry->GetPermissions()[idx];
michael@0 1110 nsCOMPtr<nsIPermission> r = new nsPermission(entry->GetKey()->mHost,
michael@0 1111 entry->GetKey()->mAppId,
michael@0 1112 entry->GetKey()->mIsInBrowserElement,
michael@0 1113 mTypeArray.ElementAt(perm.mType),
michael@0 1114 perm.mPermission,
michael@0 1115 perm.mExpireType,
michael@0 1116 perm.mExpireTime);
michael@0 1117 r.forget(aResult);
michael@0 1118 return NS_OK;
michael@0 1119 }
michael@0 1120
michael@0 1121 nsresult
michael@0 1122 nsPermissionManager::CommonTestPermission(nsIPrincipal* aPrincipal,
michael@0 1123 const char *aType,
michael@0 1124 uint32_t *aPermission,
michael@0 1125 bool aExactHostMatch,
michael@0 1126 bool aIncludingSession)
michael@0 1127 {
michael@0 1128 NS_ENSURE_ARG_POINTER(aPrincipal);
michael@0 1129 NS_ENSURE_ARG_POINTER(aType);
michael@0 1130
michael@0 1131 if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
michael@0 1132 *aPermission = nsIPermissionManager::ALLOW_ACTION;
michael@0 1133 return NS_OK;
michael@0 1134 }
michael@0 1135
michael@0 1136 // Set the default.
michael@0 1137 *aPermission = nsIPermissionManager::UNKNOWN_ACTION;
michael@0 1138
michael@0 1139 // For expanded principals, we want to iterate over the whitelist and see
michael@0 1140 // if the permission is granted for any of them.
michael@0 1141 nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal);
michael@0 1142 if (ep) {
michael@0 1143 nsTArray<nsCOMPtr<nsIPrincipal>>* whitelist;
michael@0 1144 nsresult rv = ep->GetWhiteList(&whitelist);
michael@0 1145 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1146
michael@0 1147 for (size_t i = 0; i < whitelist->Length(); ++i) {
michael@0 1148 uint32_t perm;
michael@0 1149 rv = CommonTestPermission(whitelist->ElementAt(i), aType, &perm, aExactHostMatch,
michael@0 1150 aIncludingSession);
michael@0 1151 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1152 if (perm == nsIPermissionManager::ALLOW_ACTION) {
michael@0 1153 *aPermission = perm;
michael@0 1154 return NS_OK;
michael@0 1155 } else if (perm == nsIPermissionManager::PROMPT_ACTION) {
michael@0 1156 // Store it, but keep going to see if we can do better.
michael@0 1157 *aPermission = perm;
michael@0 1158 }
michael@0 1159 }
michael@0 1160
michael@0 1161 return NS_OK;
michael@0 1162 }
michael@0 1163
michael@0 1164 nsAutoCString host;
michael@0 1165 nsresult rv = GetHostForPrincipal(aPrincipal, host);
michael@0 1166 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1167
michael@0 1168 int32_t typeIndex = GetTypeIndex(aType, false);
michael@0 1169 // If type == -1, the type isn't known,
michael@0 1170 // so just return NS_OK
michael@0 1171 if (typeIndex == -1) return NS_OK;
michael@0 1172
michael@0 1173 uint32_t appId;
michael@0 1174 rv = aPrincipal->GetAppId(&appId);
michael@0 1175 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1176
michael@0 1177 bool isInBrowserElement;
michael@0 1178 rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
michael@0 1179 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1180
michael@0 1181 PermissionHashKey* entry = GetPermissionHashKey(host, appId, isInBrowserElement,
michael@0 1182 typeIndex, aExactHostMatch);
michael@0 1183 if (!entry ||
michael@0 1184 (!aIncludingSession &&
michael@0 1185 entry->GetPermission(typeIndex).mNonSessionExpireType ==
michael@0 1186 nsIPermissionManager::EXPIRE_SESSION)) {
michael@0 1187 return NS_OK;
michael@0 1188 }
michael@0 1189
michael@0 1190 *aPermission = aIncludingSession
michael@0 1191 ? entry->GetPermission(typeIndex).mPermission
michael@0 1192 : entry->GetPermission(typeIndex).mNonSessionPermission;
michael@0 1193
michael@0 1194 return NS_OK;
michael@0 1195 }
michael@0 1196
michael@0 1197 // Returns PermissionHashKey for a given { host, appId, isInBrowserElement } tuple.
michael@0 1198 // This is not simply using PermissionKey because we will walk-up domains in
michael@0 1199 // case of |host| contains sub-domains.
michael@0 1200 // Returns null if nothing found.
michael@0 1201 // Also accepts host on the format "<foo>". This will perform an exact match
michael@0 1202 // lookup as the string doesn't contain any dots.
michael@0 1203 nsPermissionManager::PermissionHashKey*
michael@0 1204 nsPermissionManager::GetPermissionHashKey(const nsACString& aHost,
michael@0 1205 uint32_t aAppId,
michael@0 1206 bool aIsInBrowserElement,
michael@0 1207 uint32_t aType,
michael@0 1208 bool aExactHostMatch)
michael@0 1209 {
michael@0 1210 PermissionHashKey* entry = nullptr;
michael@0 1211
michael@0 1212 nsRefPtr<PermissionKey> key = new PermissionKey(aHost, aAppId, aIsInBrowserElement);
michael@0 1213 entry = mPermissionTable.GetEntry(key);
michael@0 1214
michael@0 1215 if (entry) {
michael@0 1216 PermissionEntry permEntry = entry->GetPermission(aType);
michael@0 1217
michael@0 1218 // if the entry is expired, remove and keep looking for others.
michael@0 1219 // Note that EXPIRE_SESSION only honors expireTime if it is nonzero.
michael@0 1220 if ((permEntry.mExpireType == nsIPermissionManager::EXPIRE_TIME ||
michael@0 1221 (permEntry.mExpireType == nsIPermissionManager::EXPIRE_SESSION &&
michael@0 1222 permEntry.mExpireTime != 0)) &&
michael@0 1223 permEntry.mExpireTime <= (PR_Now() / 1000)) {
michael@0 1224 nsCOMPtr<nsIPrincipal> principal;
michael@0 1225 if (NS_FAILED(GetPrincipal(aHost, aAppId, aIsInBrowserElement, getter_AddRefs(principal)))) {
michael@0 1226 return nullptr;
michael@0 1227 }
michael@0 1228
michael@0 1229 entry = nullptr;
michael@0 1230 RemoveFromPrincipal(principal, mTypeArray[aType].get());
michael@0 1231 } else if (permEntry.mPermission == nsIPermissionManager::UNKNOWN_ACTION) {
michael@0 1232 entry = nullptr;
michael@0 1233 }
michael@0 1234 }
michael@0 1235
michael@0 1236 if (entry) {
michael@0 1237 return entry;
michael@0 1238 }
michael@0 1239
michael@0 1240 // If we haven't found an entry, depending on the host, we could try a bit
michael@0 1241 // harder.
michael@0 1242 // If this is a file:// URI, we can check for the presence of the magic entry
michael@0 1243 // <file> which gives permission to all file://. This hack might disappear,
michael@0 1244 // see bug 817007. Note that we don't require aExactHostMatch to be true for
michael@0 1245 // that to keep retro-compatibility.
michael@0 1246 // If this is not a file:// URI, and that aExactHostMatch wasn't true, we can
michael@0 1247 // check if the base domain has a permission entry.
michael@0 1248
michael@0 1249 if (StringBeginsWith(aHost, NS_LITERAL_CSTRING("file://"))) {
michael@0 1250 return GetPermissionHashKey(NS_LITERAL_CSTRING("<file>"), aAppId, aIsInBrowserElement, aType, true);
michael@0 1251 }
michael@0 1252
michael@0 1253 if (!aExactHostMatch) {
michael@0 1254 nsCString domain = GetNextSubDomainForHost(aHost);
michael@0 1255 if (!domain.IsEmpty()) {
michael@0 1256 return GetPermissionHashKey(domain, aAppId, aIsInBrowserElement, aType, aExactHostMatch);
michael@0 1257 }
michael@0 1258 }
michael@0 1259
michael@0 1260 // No entry, really...
michael@0 1261 return nullptr;
michael@0 1262 }
michael@0 1263
michael@0 1264 // helper struct for passing arguments into hash enumeration callback.
michael@0 1265 struct nsGetEnumeratorData
michael@0 1266 {
michael@0 1267 nsGetEnumeratorData(nsCOMArray<nsIPermission> *aArray, const nsTArray<nsCString> *aTypes)
michael@0 1268 : array(aArray)
michael@0 1269 , types(aTypes) {}
michael@0 1270
michael@0 1271 nsCOMArray<nsIPermission> *array;
michael@0 1272 const nsTArray<nsCString> *types;
michael@0 1273 };
michael@0 1274
michael@0 1275 static PLDHashOperator
michael@0 1276 AddPermissionsToList(nsPermissionManager::PermissionHashKey* entry, void *arg)
michael@0 1277 {
michael@0 1278 nsGetEnumeratorData *data = static_cast<nsGetEnumeratorData *>(arg);
michael@0 1279
michael@0 1280 for (uint32_t i = 0; i < entry->GetPermissions().Length(); ++i) {
michael@0 1281 nsPermissionManager::PermissionEntry& permEntry = entry->GetPermissions()[i];
michael@0 1282
michael@0 1283 nsPermission *perm = new nsPermission(entry->GetKey()->mHost,
michael@0 1284 entry->GetKey()->mAppId,
michael@0 1285 entry->GetKey()->mIsInBrowserElement,
michael@0 1286 data->types->ElementAt(permEntry.mType),
michael@0 1287 permEntry.mPermission,
michael@0 1288 permEntry.mExpireType,
michael@0 1289 permEntry.mExpireTime);
michael@0 1290
michael@0 1291 data->array->AppendObject(perm);
michael@0 1292 }
michael@0 1293
michael@0 1294 return PL_DHASH_NEXT;
michael@0 1295 }
michael@0 1296
michael@0 1297 NS_IMETHODIMP nsPermissionManager::GetEnumerator(nsISimpleEnumerator **aEnum)
michael@0 1298 {
michael@0 1299 // roll an nsCOMArray of all our permissions, then hand out an enumerator
michael@0 1300 nsCOMArray<nsIPermission> array;
michael@0 1301 nsGetEnumeratorData data(&array, &mTypeArray);
michael@0 1302
michael@0 1303 mPermissionTable.EnumerateEntries(AddPermissionsToList, &data);
michael@0 1304
michael@0 1305 return NS_NewArrayEnumerator(aEnum, array);
michael@0 1306 }
michael@0 1307
michael@0 1308 NS_IMETHODIMP nsPermissionManager::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *someData)
michael@0 1309 {
michael@0 1310 ENSURE_NOT_CHILD_PROCESS;
michael@0 1311
michael@0 1312 if (nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) {
michael@0 1313 if (!nsCRT::strcmp(someData, NS_LITERAL_STRING("permissions.memory_only").get())) {
michael@0 1314 // XXX: Should we remove the file? Probably not..
michael@0 1315 InitDB(PR_FALSE);
michael@0 1316 }
michael@0 1317 } else if (!nsCRT::strcmp(aTopic, "profile-before-change")) {
michael@0 1318 // The profile is about to change,
michael@0 1319 // or is going away because the application is shutting down.
michael@0 1320 mIsShuttingDown = true;
michael@0 1321 if (!nsCRT::strcmp(someData, MOZ_UTF16("shutdown-cleanse"))) {
michael@0 1322 // Clear the permissions file and close the db asynchronously
michael@0 1323 RemoveAllInternal(false);
michael@0 1324 } else {
michael@0 1325 RemoveAllFromMemory();
michael@0 1326 CloseDB(false);
michael@0 1327 }
michael@0 1328 }
michael@0 1329 else if (!nsCRT::strcmp(aTopic, "profile-do-change")) {
michael@0 1330 // the profile has already changed; init the db from the new location
michael@0 1331 InitDB(false);
michael@0 1332 }
michael@0 1333
michael@0 1334 return NS_OK;
michael@0 1335 }
michael@0 1336
michael@0 1337 PLDHashOperator
michael@0 1338 nsPermissionManager::GetPermissionsForApp(nsPermissionManager::PermissionHashKey* entry, void* arg)
michael@0 1339 {
michael@0 1340 GetPermissionsForAppStruct* data = static_cast<GetPermissionsForAppStruct*>(arg);
michael@0 1341
michael@0 1342 for (uint32_t i = 0; i < entry->GetPermissions().Length(); ++i) {
michael@0 1343 nsPermissionManager::PermissionEntry& permEntry = entry->GetPermissions()[i];
michael@0 1344
michael@0 1345 if (entry->GetKey()->mAppId != data->appId ||
michael@0 1346 (data->browserOnly && !entry->GetKey()->mIsInBrowserElement)) {
michael@0 1347 continue;
michael@0 1348 }
michael@0 1349
michael@0 1350 data->permissions.AppendObject(new nsPermission(entry->GetKey()->mHost,
michael@0 1351 entry->GetKey()->mAppId,
michael@0 1352 entry->GetKey()->mIsInBrowserElement,
michael@0 1353 gPermissionManager->mTypeArray.ElementAt(permEntry.mType),
michael@0 1354 permEntry.mPermission,
michael@0 1355 permEntry.mExpireType,
michael@0 1356 permEntry.mExpireTime));
michael@0 1357 }
michael@0 1358
michael@0 1359 return PL_DHASH_NEXT;
michael@0 1360 }
michael@0 1361
michael@0 1362 NS_IMETHODIMP
michael@0 1363 nsPermissionManager::RemovePermissionsForApp(uint32_t aAppId, bool aBrowserOnly)
michael@0 1364 {
michael@0 1365 ENSURE_NOT_CHILD_PROCESS;
michael@0 1366 NS_ENSURE_ARG(aAppId != nsIScriptSecurityManager::NO_APP_ID);
michael@0 1367
michael@0 1368 // We begin by removing all the permissions from the DB.
michael@0 1369 // After clearing the DB, we call AddInternal() to make sure that all
michael@0 1370 // processes are aware of this change and the representation of the DB in
michael@0 1371 // memory is updated.
michael@0 1372 // We have to get all permissions associated with an application and then
michael@0 1373 // remove those because doing so in EnumerateEntries() would fail because
michael@0 1374 // we might happen to actually delete entries from the list.
michael@0 1375
michael@0 1376 nsAutoCString sql;
michael@0 1377 sql.AppendLiteral("DELETE FROM moz_hosts WHERE appId=");
michael@0 1378 sql.AppendInt(aAppId);
michael@0 1379
michael@0 1380 if (aBrowserOnly) {
michael@0 1381 sql.AppendLiteral(" AND isInBrowserElement=1");
michael@0 1382 }
michael@0 1383
michael@0 1384 nsCOMPtr<mozIStorageAsyncStatement> removeStmt;
michael@0 1385 nsresult rv = mDBConn->CreateAsyncStatement(sql, getter_AddRefs(removeStmt));
michael@0 1386 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1387
michael@0 1388 nsCOMPtr<mozIStoragePendingStatement> pending;
michael@0 1389 rv = removeStmt->ExecuteAsync(nullptr, getter_AddRefs(pending));
michael@0 1390 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1391
michael@0 1392 GetPermissionsForAppStruct data(aAppId, aBrowserOnly);
michael@0 1393 mPermissionTable.EnumerateEntries(GetPermissionsForApp, &data);
michael@0 1394
michael@0 1395 for (int32_t i=0; i<data.permissions.Count(); ++i) {
michael@0 1396 nsAutoCString host;
michael@0 1397 bool isInBrowserElement;
michael@0 1398 nsAutoCString type;
michael@0 1399
michael@0 1400 data.permissions[i]->GetHost(host);
michael@0 1401 data.permissions[i]->GetIsInBrowserElement(&isInBrowserElement);
michael@0 1402 data.permissions[i]->GetType(type);
michael@0 1403
michael@0 1404 nsCOMPtr<nsIPrincipal> principal;
michael@0 1405 if (NS_FAILED(GetPrincipal(host, aAppId, isInBrowserElement,
michael@0 1406 getter_AddRefs(principal)))) {
michael@0 1407 NS_ERROR("GetPrincipal() failed!");
michael@0 1408 continue;
michael@0 1409 }
michael@0 1410
michael@0 1411 AddInternal(principal,
michael@0 1412 type,
michael@0 1413 nsIPermissionManager::UNKNOWN_ACTION,
michael@0 1414 0,
michael@0 1415 nsIPermissionManager::EXPIRE_NEVER,
michael@0 1416 0,
michael@0 1417 nsPermissionManager::eNotify,
michael@0 1418 nsPermissionManager::eNoDBOperation);
michael@0 1419 }
michael@0 1420
michael@0 1421 return NS_OK;
michael@0 1422 }
michael@0 1423
michael@0 1424 PLDHashOperator
michael@0 1425 nsPermissionManager::RemoveExpiredPermissionsForAppEnumerator(
michael@0 1426 nsPermissionManager::PermissionHashKey* entry, void* arg)
michael@0 1427 {
michael@0 1428 uint32_t* appId = static_cast<uint32_t*>(arg);
michael@0 1429
michael@0 1430 for (uint32_t i = 0; i < entry->GetPermissions().Length(); ++i) {
michael@0 1431 if (entry->GetKey()->mAppId != *appId) {
michael@0 1432 continue;
michael@0 1433 }
michael@0 1434
michael@0 1435 nsPermissionManager::PermissionEntry& permEntry = entry->GetPermissions()[i];
michael@0 1436 if (permEntry.mExpireType != nsIPermissionManager::EXPIRE_SESSION) {
michael@0 1437 continue;
michael@0 1438 }
michael@0 1439
michael@0 1440 if (permEntry.mNonSessionExpireType == nsIPermissionManager::EXPIRE_SESSION) {
michael@0 1441 PermissionEntry oldPermissionEntry = entry->GetPermissions()[i];
michael@0 1442
michael@0 1443 entry->GetPermissions().RemoveElementAt(i);
michael@0 1444
michael@0 1445 gPermissionManager->NotifyObserversWithPermission(entry->GetKey()->mHost,
michael@0 1446 entry->GetKey()->mAppId,
michael@0 1447 entry->GetKey()->mIsInBrowserElement,
michael@0 1448 gPermissionManager->mTypeArray.ElementAt(oldPermissionEntry.mType),
michael@0 1449 oldPermissionEntry.mPermission,
michael@0 1450 oldPermissionEntry.mExpireType,
michael@0 1451 oldPermissionEntry.mExpireTime,
michael@0 1452 MOZ_UTF16("deleted"));
michael@0 1453 --i;
michael@0 1454 continue;
michael@0 1455 }
michael@0 1456
michael@0 1457 permEntry.mPermission = permEntry.mNonSessionPermission;
michael@0 1458 permEntry.mExpireType = permEntry.mNonSessionExpireType;
michael@0 1459 permEntry.mExpireTime = permEntry.mNonSessionExpireTime;
michael@0 1460
michael@0 1461 gPermissionManager->NotifyObserversWithPermission(entry->GetKey()->mHost,
michael@0 1462 entry->GetKey()->mAppId,
michael@0 1463 entry->GetKey()->mIsInBrowserElement,
michael@0 1464 gPermissionManager->mTypeArray.ElementAt(permEntry.mType),
michael@0 1465 permEntry.mPermission,
michael@0 1466 permEntry.mExpireType,
michael@0 1467 permEntry.mExpireTime,
michael@0 1468 MOZ_UTF16("changed"));
michael@0 1469 }
michael@0 1470
michael@0 1471 return PL_DHASH_NEXT;
michael@0 1472 }
michael@0 1473
michael@0 1474 nsresult
michael@0 1475 nsPermissionManager::RemoveExpiredPermissionsForApp(uint32_t aAppId)
michael@0 1476 {
michael@0 1477 ENSURE_NOT_CHILD_PROCESS;
michael@0 1478
michael@0 1479 if (aAppId != nsIScriptSecurityManager::NO_APP_ID) {
michael@0 1480 mPermissionTable.EnumerateEntries(RemoveExpiredPermissionsForAppEnumerator, &aAppId);
michael@0 1481 }
michael@0 1482
michael@0 1483 return NS_OK;
michael@0 1484 }
michael@0 1485
michael@0 1486 //*****************************************************************************
michael@0 1487 //*** nsPermissionManager private methods
michael@0 1488 //*****************************************************************************
michael@0 1489
michael@0 1490 nsresult
michael@0 1491 nsPermissionManager::RemoveAllFromMemory()
michael@0 1492 {
michael@0 1493 mLargestID = 0;
michael@0 1494 mTypeArray.Clear();
michael@0 1495 mPermissionTable.Clear();
michael@0 1496
michael@0 1497 return NS_OK;
michael@0 1498 }
michael@0 1499
michael@0 1500 // Returns -1 on failure
michael@0 1501 int32_t
michael@0 1502 nsPermissionManager::GetTypeIndex(const char *aType,
michael@0 1503 bool aAdd)
michael@0 1504 {
michael@0 1505 for (uint32_t i = 0; i < mTypeArray.Length(); ++i)
michael@0 1506 if (mTypeArray[i].Equals(aType))
michael@0 1507 return i;
michael@0 1508
michael@0 1509 if (!aAdd) {
michael@0 1510 // Not found, but that is ok - we were just looking.
michael@0 1511 return -1;
michael@0 1512 }
michael@0 1513
michael@0 1514 // This type was not registered before.
michael@0 1515 // append it to the array, without copy-constructing the string
michael@0 1516 nsCString *elem = mTypeArray.AppendElement();
michael@0 1517 if (!elem)
michael@0 1518 return -1;
michael@0 1519
michael@0 1520 elem->Assign(aType);
michael@0 1521 return mTypeArray.Length() - 1;
michael@0 1522 }
michael@0 1523
michael@0 1524 // wrapper function for mangling (host,type,perm,expireType,expireTime)
michael@0 1525 // set into an nsIPermission.
michael@0 1526 void
michael@0 1527 nsPermissionManager::NotifyObserversWithPermission(const nsACString &aHost,
michael@0 1528 uint32_t aAppId,
michael@0 1529 bool aIsInBrowserElement,
michael@0 1530 const nsCString &aType,
michael@0 1531 uint32_t aPermission,
michael@0 1532 uint32_t aExpireType,
michael@0 1533 int64_t aExpireTime,
michael@0 1534 const char16_t *aData)
michael@0 1535 {
michael@0 1536 nsCOMPtr<nsIPermission> permission =
michael@0 1537 new nsPermission(aHost, aAppId, aIsInBrowserElement, aType, aPermission,
michael@0 1538 aExpireType, aExpireTime);
michael@0 1539 if (permission)
michael@0 1540 NotifyObservers(permission, aData);
michael@0 1541 }
michael@0 1542
michael@0 1543 // notify observers that the permission list changed. there are four possible
michael@0 1544 // values for aData:
michael@0 1545 // "deleted" means a permission was deleted. aPermission is the deleted permission.
michael@0 1546 // "added" means a permission was added. aPermission is the added permission.
michael@0 1547 // "changed" means a permission was altered. aPermission is the new permission.
michael@0 1548 // "cleared" means the entire permission list was cleared. aPermission is null.
michael@0 1549 void
michael@0 1550 nsPermissionManager::NotifyObservers(nsIPermission *aPermission,
michael@0 1551 const char16_t *aData)
michael@0 1552 {
michael@0 1553 if (mObserverService)
michael@0 1554 mObserverService->NotifyObservers(aPermission,
michael@0 1555 kPermissionChangeNotification,
michael@0 1556 aData);
michael@0 1557 }
michael@0 1558
michael@0 1559 nsresult
michael@0 1560 nsPermissionManager::Read()
michael@0 1561 {
michael@0 1562 ENSURE_NOT_CHILD_PROCESS;
michael@0 1563
michael@0 1564 nsresult rv;
michael@0 1565
michael@0 1566 // delete expired permissions before we read in the db
michael@0 1567 {
michael@0 1568 // this deletion has its own scope so the write lock is released when done.
michael@0 1569 nsCOMPtr<mozIStorageStatement> stmtDeleteExpired;
michael@0 1570 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
michael@0 1571 "DELETE FROM moz_hosts WHERE expireType = ?1 AND expireTime <= ?2"),
michael@0 1572 getter_AddRefs(stmtDeleteExpired));
michael@0 1573 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1574
michael@0 1575 rv = stmtDeleteExpired->BindInt32ByIndex(0, nsIPermissionManager::EXPIRE_TIME);
michael@0 1576 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1577
michael@0 1578 rv = stmtDeleteExpired->BindInt64ByIndex(1, PR_Now() / 1000);
michael@0 1579 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1580
michael@0 1581 bool hasResult;
michael@0 1582 rv = stmtDeleteExpired->ExecuteStep(&hasResult);
michael@0 1583 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1584 }
michael@0 1585
michael@0 1586 nsCOMPtr<mozIStorageStatement> stmt;
michael@0 1587 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
michael@0 1588 "SELECT id, host, type, permission, expireType, expireTime, appId, isInBrowserElement "
michael@0 1589 "FROM moz_hosts"), getter_AddRefs(stmt));
michael@0 1590 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1591
michael@0 1592 int64_t id;
michael@0 1593 nsAutoCString host, type;
michael@0 1594 uint32_t permission;
michael@0 1595 uint32_t expireType;
michael@0 1596 int64_t expireTime;
michael@0 1597 uint32_t appId;
michael@0 1598 bool isInBrowserElement;
michael@0 1599 bool hasResult;
michael@0 1600 bool readError = false;
michael@0 1601
michael@0 1602 while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
michael@0 1603 // explicitly set our entry id counter for use in AddInternal(),
michael@0 1604 // and keep track of the largest id so we know where to pick up.
michael@0 1605 id = stmt->AsInt64(0);
michael@0 1606 if (id > mLargestID)
michael@0 1607 mLargestID = id;
michael@0 1608
michael@0 1609 rv = stmt->GetUTF8String(1, host);
michael@0 1610 if (NS_FAILED(rv)) {
michael@0 1611 readError = true;
michael@0 1612 continue;
michael@0 1613 }
michael@0 1614
michael@0 1615 rv = stmt->GetUTF8String(2, type);
michael@0 1616 if (NS_FAILED(rv)) {
michael@0 1617 readError = true;
michael@0 1618 continue;
michael@0 1619 }
michael@0 1620
michael@0 1621 permission = stmt->AsInt32(3);
michael@0 1622 expireType = stmt->AsInt32(4);
michael@0 1623
michael@0 1624 // convert into int64_t value (milliseconds)
michael@0 1625 expireTime = stmt->AsInt64(5);
michael@0 1626
michael@0 1627 if (stmt->AsInt64(6) < 0) {
michael@0 1628 readError = true;
michael@0 1629 continue;
michael@0 1630 }
michael@0 1631 appId = static_cast<uint32_t>(stmt->AsInt64(6));
michael@0 1632 isInBrowserElement = static_cast<bool>(stmt->AsInt32(7));
michael@0 1633
michael@0 1634 nsCOMPtr<nsIPrincipal> principal;
michael@0 1635 nsresult rv = GetPrincipal(host, appId, isInBrowserElement, getter_AddRefs(principal));
michael@0 1636 if (NS_FAILED(rv)) {
michael@0 1637 readError = true;
michael@0 1638 continue;
michael@0 1639 }
michael@0 1640
michael@0 1641 rv = AddInternal(principal, type, permission, id, expireType, expireTime,
michael@0 1642 eDontNotify, eNoDBOperation);
michael@0 1643 if (NS_FAILED(rv)) {
michael@0 1644 readError = true;
michael@0 1645 continue;
michael@0 1646 }
michael@0 1647 }
michael@0 1648
michael@0 1649 if (readError) {
michael@0 1650 NS_ERROR("Error occured while reading the permissions database!");
michael@0 1651 return NS_ERROR_FAILURE;
michael@0 1652 }
michael@0 1653
michael@0 1654 return NS_OK;
michael@0 1655 }
michael@0 1656
michael@0 1657 static const char kMatchTypeHost[] = "host";
michael@0 1658
michael@0 1659 nsresult
michael@0 1660 nsPermissionManager::Import()
michael@0 1661 {
michael@0 1662 ENSURE_NOT_CHILD_PROCESS;
michael@0 1663
michael@0 1664 nsresult rv;
michael@0 1665
michael@0 1666 nsCOMPtr<nsIFile> permissionsFile;
michael@0 1667 rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(permissionsFile));
michael@0 1668 if (NS_FAILED(rv)) return rv;
michael@0 1669
michael@0 1670 rv = permissionsFile->AppendNative(NS_LITERAL_CSTRING(kHostpermFileName));
michael@0 1671 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1672
michael@0 1673 nsCOMPtr<nsIInputStream> fileInputStream;
michael@0 1674 rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream),
michael@0 1675 permissionsFile);
michael@0 1676 if (NS_FAILED(rv)) return rv;
michael@0 1677
michael@0 1678 nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(fileInputStream, &rv);
michael@0 1679 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1680
michael@0 1681 // start a transaction on the storage db, to optimize insertions.
michael@0 1682 // transaction will automically commit on completion
michael@0 1683 mozStorageTransaction transaction(mDBConn, true);
michael@0 1684
michael@0 1685 /* format is:
michael@0 1686 * matchtype \t type \t permission \t host
michael@0 1687 * Only "host" is supported for matchtype
michael@0 1688 * type is a string that identifies the type of permission (e.g. "cookie")
michael@0 1689 * permission is an integer between 1 and 15
michael@0 1690 */
michael@0 1691
michael@0 1692 nsAutoCString buffer;
michael@0 1693 bool isMore = true;
michael@0 1694 while (isMore && NS_SUCCEEDED(lineInputStream->ReadLine(buffer, &isMore))) {
michael@0 1695 if (buffer.IsEmpty() || buffer.First() == '#') {
michael@0 1696 continue;
michael@0 1697 }
michael@0 1698
michael@0 1699 nsTArray<nsCString> lineArray;
michael@0 1700
michael@0 1701 // Split the line at tabs
michael@0 1702 ParseString(buffer, '\t', lineArray);
michael@0 1703
michael@0 1704 if (lineArray[0].EqualsLiteral(kMatchTypeHost) &&
michael@0 1705 lineArray.Length() == 4) {
michael@0 1706
michael@0 1707 nsresult error;
michael@0 1708 uint32_t permission = lineArray[2].ToInteger(&error);
michael@0 1709 if (NS_FAILED(error))
michael@0 1710 continue;
michael@0 1711
michael@0 1712 // hosts might be encoded in UTF8; switch them to ACE to be consistent
michael@0 1713 if (!IsASCII(lineArray[3])) {
michael@0 1714 rv = NormalizeToACE(lineArray[3]);
michael@0 1715 if (NS_FAILED(rv))
michael@0 1716 continue;
michael@0 1717 }
michael@0 1718
michael@0 1719 nsCOMPtr<nsIPrincipal> principal;
michael@0 1720 nsresult rv = GetPrincipal(lineArray[3], getter_AddRefs(principal));
michael@0 1721 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1722
michael@0 1723 rv = AddInternal(principal, lineArray[1], permission, 0,
michael@0 1724 nsIPermissionManager::EXPIRE_NEVER, 0, eDontNotify, eWriteToDB);
michael@0 1725 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1726 }
michael@0 1727 }
michael@0 1728
michael@0 1729 // we're done importing - delete the old file
michael@0 1730 permissionsFile->Remove(false);
michael@0 1731
michael@0 1732 return NS_OK;
michael@0 1733 }
michael@0 1734
michael@0 1735 nsresult
michael@0 1736 nsPermissionManager::NormalizeToACE(nsCString &aHost)
michael@0 1737 {
michael@0 1738 // lazily init the IDN service
michael@0 1739 if (!mIDNService) {
michael@0 1740 nsresult rv;
michael@0 1741 mIDNService = do_GetService(NS_IDNSERVICE_CONTRACTID, &rv);
michael@0 1742 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1743 }
michael@0 1744
michael@0 1745 return mIDNService->ConvertUTF8toACE(aHost, aHost);
michael@0 1746 }
michael@0 1747
michael@0 1748 void
michael@0 1749 nsPermissionManager::UpdateDB(OperationType aOp,
michael@0 1750 mozIStorageAsyncStatement* aStmt,
michael@0 1751 int64_t aID,
michael@0 1752 const nsACString &aHost,
michael@0 1753 const nsACString &aType,
michael@0 1754 uint32_t aPermission,
michael@0 1755 uint32_t aExpireType,
michael@0 1756 int64_t aExpireTime,
michael@0 1757 uint32_t aAppId,
michael@0 1758 bool aIsInBrowserElement)
michael@0 1759 {
michael@0 1760 ENSURE_NOT_CHILD_PROCESS_NORET;
michael@0 1761
michael@0 1762 nsresult rv;
michael@0 1763
michael@0 1764 // no statement is ok - just means we don't have a profile
michael@0 1765 if (!aStmt)
michael@0 1766 return;
michael@0 1767
michael@0 1768 switch (aOp) {
michael@0 1769 case eOperationAdding:
michael@0 1770 {
michael@0 1771 rv = aStmt->BindInt64ByIndex(0, aID);
michael@0 1772 if (NS_FAILED(rv)) break;
michael@0 1773
michael@0 1774 rv = aStmt->BindUTF8StringByIndex(1, aHost);
michael@0 1775 if (NS_FAILED(rv)) break;
michael@0 1776
michael@0 1777 rv = aStmt->BindUTF8StringByIndex(2, aType);
michael@0 1778 if (NS_FAILED(rv)) break;
michael@0 1779
michael@0 1780 rv = aStmt->BindInt32ByIndex(3, aPermission);
michael@0 1781 if (NS_FAILED(rv)) break;
michael@0 1782
michael@0 1783 rv = aStmt->BindInt32ByIndex(4, aExpireType);
michael@0 1784 if (NS_FAILED(rv)) break;
michael@0 1785
michael@0 1786 rv = aStmt->BindInt64ByIndex(5, aExpireTime);
michael@0 1787 if (NS_FAILED(rv)) break;
michael@0 1788
michael@0 1789 rv = aStmt->BindInt64ByIndex(6, aAppId);
michael@0 1790 if (NS_FAILED(rv)) break;
michael@0 1791
michael@0 1792 rv = aStmt->BindInt64ByIndex(7, aIsInBrowserElement);
michael@0 1793 break;
michael@0 1794 }
michael@0 1795
michael@0 1796 case eOperationRemoving:
michael@0 1797 {
michael@0 1798 rv = aStmt->BindInt64ByIndex(0, aID);
michael@0 1799 break;
michael@0 1800 }
michael@0 1801
michael@0 1802 case eOperationChanging:
michael@0 1803 {
michael@0 1804 rv = aStmt->BindInt64ByIndex(0, aID);
michael@0 1805 if (NS_FAILED(rv)) break;
michael@0 1806
michael@0 1807 rv = aStmt->BindInt32ByIndex(1, aPermission);
michael@0 1808 if (NS_FAILED(rv)) break;
michael@0 1809
michael@0 1810 rv = aStmt->BindInt32ByIndex(2, aExpireType);
michael@0 1811 if (NS_FAILED(rv)) break;
michael@0 1812
michael@0 1813 rv = aStmt->BindInt64ByIndex(3, aExpireTime);
michael@0 1814 break;
michael@0 1815 }
michael@0 1816
michael@0 1817 default:
michael@0 1818 {
michael@0 1819 NS_NOTREACHED("need a valid operation in UpdateDB()!");
michael@0 1820 rv = NS_ERROR_UNEXPECTED;
michael@0 1821 break;
michael@0 1822 }
michael@0 1823 }
michael@0 1824
michael@0 1825 if (NS_FAILED(rv)) {
michael@0 1826 NS_WARNING("db change failed!");
michael@0 1827 return;
michael@0 1828 }
michael@0 1829
michael@0 1830 nsCOMPtr<mozIStoragePendingStatement> pending;
michael@0 1831 rv = aStmt->ExecuteAsync(nullptr, getter_AddRefs(pending));
michael@0 1832 MOZ_ASSERT(NS_SUCCEEDED(rv));
michael@0 1833 }
michael@0 1834
michael@0 1835 NS_IMETHODIMP
michael@0 1836 nsPermissionManager::AddrefAppId(uint32_t aAppId)
michael@0 1837 {
michael@0 1838 if (aAppId == nsIScriptSecurityManager::NO_APP_ID) {
michael@0 1839 return NS_OK;
michael@0 1840 }
michael@0 1841
michael@0 1842 bool found = false;
michael@0 1843 for (uint32_t i = 0; i < mAppIdRefcounts.Length(); ++i) {
michael@0 1844 if (mAppIdRefcounts[i].mAppId == aAppId) {
michael@0 1845 ++mAppIdRefcounts[i].mCounter;
michael@0 1846 found = true;
michael@0 1847 break;
michael@0 1848 }
michael@0 1849 }
michael@0 1850
michael@0 1851 if (!found) {
michael@0 1852 ApplicationCounter app = { aAppId, 1 };
michael@0 1853 mAppIdRefcounts.AppendElement(app);
michael@0 1854 }
michael@0 1855
michael@0 1856 return NS_OK;
michael@0 1857 }
michael@0 1858
michael@0 1859 NS_IMETHODIMP
michael@0 1860 nsPermissionManager::ReleaseAppId(uint32_t aAppId)
michael@0 1861 {
michael@0 1862 // An app has been released, maybe we have to reset its session.
michael@0 1863
michael@0 1864 if (aAppId == nsIScriptSecurityManager::NO_APP_ID) {
michael@0 1865 return NS_OK;
michael@0 1866 }
michael@0 1867
michael@0 1868 for (uint32_t i = 0; i < mAppIdRefcounts.Length(); ++i) {
michael@0 1869 if (mAppIdRefcounts[i].mAppId == aAppId) {
michael@0 1870 --mAppIdRefcounts[i].mCounter;
michael@0 1871
michael@0 1872 if (!mAppIdRefcounts[i].mCounter) {
michael@0 1873 mAppIdRefcounts.RemoveElementAt(i);
michael@0 1874 return RemoveExpiredPermissionsForApp(aAppId);
michael@0 1875 }
michael@0 1876
michael@0 1877 break;
michael@0 1878 }
michael@0 1879 }
michael@0 1880
michael@0 1881 return NS_OK;
michael@0 1882 }
michael@0 1883
michael@0 1884 NS_IMETHODIMP
michael@0 1885 nsPermissionManager::UpdateExpireTime(nsIPrincipal* aPrincipal,
michael@0 1886 const char* aType,
michael@0 1887 bool aExactHostMatch,
michael@0 1888 uint64_t aSessionExpireTime,
michael@0 1889 uint64_t aPersistentExpireTime)
michael@0 1890 {
michael@0 1891 NS_ENSURE_ARG_POINTER(aPrincipal);
michael@0 1892 NS_ENSURE_ARG_POINTER(aType);
michael@0 1893
michael@0 1894 uint64_t nowms = PR_Now() / 1000;
michael@0 1895 if (aSessionExpireTime < nowms || aPersistentExpireTime < nowms) {
michael@0 1896 return NS_ERROR_INVALID_ARG;
michael@0 1897 }
michael@0 1898
michael@0 1899 if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
michael@0 1900 return NS_OK;
michael@0 1901 }
michael@0 1902
michael@0 1903 // Setting the expire time of an nsEP is non-sensical.
michael@0 1904 if (IsExpandedPrincipal(aPrincipal)) {
michael@0 1905 return NS_ERROR_INVALID_ARG;
michael@0 1906 }
michael@0 1907
michael@0 1908 nsAutoCString host;
michael@0 1909 nsresult rv = GetHostForPrincipal(aPrincipal, host);
michael@0 1910 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1911
michael@0 1912 int32_t typeIndex = GetTypeIndex(aType, false);
michael@0 1913 // If type == -1, the type isn't known,
michael@0 1914 // so just return NS_OK
michael@0 1915 if (typeIndex == -1) return NS_OK;
michael@0 1916
michael@0 1917 uint32_t appId;
michael@0 1918 rv = aPrincipal->GetAppId(&appId);
michael@0 1919 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1920
michael@0 1921 bool isInBrowserElement;
michael@0 1922 rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
michael@0 1923 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1924
michael@0 1925 PermissionHashKey* entry = GetPermissionHashKey(host, appId, isInBrowserElement,
michael@0 1926 typeIndex, aExactHostMatch);
michael@0 1927 if (!entry) {
michael@0 1928 return NS_OK;
michael@0 1929 }
michael@0 1930
michael@0 1931 int32_t idx = entry->GetPermissionIndex(typeIndex);
michael@0 1932 if (-1 == idx) {
michael@0 1933 return NS_OK;
michael@0 1934 }
michael@0 1935
michael@0 1936 PermissionEntry& perm = entry->GetPermissions()[idx];
michael@0 1937 if (perm.mExpireType == EXPIRE_TIME) {
michael@0 1938 perm.mExpireTime = aPersistentExpireTime;
michael@0 1939 } else if (perm.mExpireType == EXPIRE_SESSION && perm.mExpireTime != 0) {
michael@0 1940 perm.mExpireTime = aSessionExpireTime;
michael@0 1941 }
michael@0 1942 return NS_OK;
michael@0 1943 }

mercurial