extensions/cookie/nsPermissionManager.cpp

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

mercurial