1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
23 * Michiel van Leeuwen (mvl@exedo.nl)
24 * Daniel Witte (dwitte@stanford.edu)
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
41 #include "mozilla/dom/ContentParent.h"
42 #include "mozilla/dom/ContentChild.h"
44 #include "nsPermissionManager.h"
45 #include "nsPermission.h"
47 #include "nsNetUtil.h"
48 #include "nsCOMArray.h"
49 #include "nsArrayEnumerator.h"
51 #include "nsReadableUtils.h"
52 #include "nsILineInputStream.h"
53 #include "nsIIDNService.h"
54 #include "nsAppDirectoryServiceDefs.h"
56 #include "mozIStorageService.h"
57 #include "mozIStorageStatement.h"
58 #include "mozIStorageConnection.h"
59 #include "mozStorageHelper.h"
60 #include "mozStorageCID.h"
61 #include "nsXULAppAPI.h"
63 static nsPermissionManager
*gPermissionManager
= nsnull
;
66 using mozilla::dom::ContentParent
;
67 using mozilla::dom::ContentChild
;
72 return XRE_GetProcessType() == GeckoProcessType_Content
;
76 * @returns The child process object, or if we are not in the child
82 if (IsChildProcess()) {
83 ContentChild
* cpc
= ContentChild::GetSingleton();
85 NS_RUNTIMEABORT("Content Process is NULL!");
94 * @returns The parent process object, or if we are not in the parent
100 if (!IsChildProcess()) {
101 ContentParent
* cpc
= ContentParent::GetSingleton();
103 NS_RUNTIMEABORT("Content Process is NULL!");
111 #define ENSURE_NOT_CHILD_PROCESS_(onError) \
113 if (IsChildProcess()) { \
114 NS_ERROR("Cannot perform action in content process!"); \
119 #define ENSURE_NOT_CHILD_PROCESS \
120 ENSURE_NOT_CHILD_PROCESS_({ return NS_ERROR_NOT_AVAILABLE; })
122 #define ENSURE_NOT_CHILD_PROCESS_NORET \
123 ENSURE_NOT_CHILD_PROCESS_()
125 ////////////////////////////////////////////////////////////////////////////////
127 #define PL_ARENA_CONST_ALIGN_MASK 3
130 static PLArenaPool
*gHostArena
= nsnull
;
132 // making sHostArena 512b for nice allocation
133 // growing is quite cheap
134 #define HOST_ARENA_SIZE 512
136 // equivalent to strdup() - does no error checking,
137 // we're assuming we're only called with a valid pointer
139 ArenaStrDup(const char* str
, PLArenaPool
* aArena
)
142 const PRUint32 size
= strlen(str
) + 1;
143 PL_ARENA_ALLOCATE(mem
, aArena
, size
);
145 memcpy(mem
, str
, size
);
146 return static_cast<char*>(mem
);
149 nsHostEntry::nsHostEntry(const char* aHost
)
151 mHost
= ArenaStrDup(aHost
, gHostArena
);
154 // XXX this can fail on OOM
155 nsHostEntry::nsHostEntry(const nsHostEntry
& toCopy
)
156 : mHost(toCopy
.mHost
)
157 , mPermissions(toCopy
.mPermissions
)
161 ////////////////////////////////////////////////////////////////////////////////
162 // nsPermissionManager Implementation
164 static const char kPermissionsFileName
[] = "permissions.sqlite";
165 #define HOSTS_SCHEMA_VERSION 2
167 static const char kHostpermFileName
[] = "hostperm.1";
169 static const char kPermissionChangeNotification
[] = PERM_CHANGE_NOTIFICATION
;
171 NS_IMPL_ISUPPORTS3(nsPermissionManager
, nsIPermissionManager
, nsIObserver
, nsISupportsWeakReference
)
173 nsPermissionManager::nsPermissionManager()
176 , mUpdateChildProcess(PR_FALSE
)
181 nsPermissionManager::~nsPermissionManager()
183 RemoveAllFromMemory();
187 nsIPermissionManager
*
188 nsPermissionManager::GetXPCOMSingleton()
190 return GetSingleton().get();
194 already_AddRefed
<nsPermissionManager
>
195 nsPermissionManager::GetSingleton()
197 if (gPermissionManager
) {
198 NS_ADDREF(gPermissionManager
);
199 return gPermissionManager
;
202 // Create a new singleton nsPermissionManager.
203 // We AddRef only once since XPCOM has rules about the ordering of module
204 // teardowns - by the time our module destructor is called, it's too late to
205 // Release our members, since GC cycles have already been completed and
206 // would result in serious leaks.
208 gPermissionManager
= new nsPermissionManager();
209 if (gPermissionManager
) {
210 NS_ADDREF(gPermissionManager
);
211 if (NS_FAILED(gPermissionManager
->Init())) {
212 NS_RELEASE(gPermissionManager
);
216 return gPermissionManager
;
220 nsPermissionManager::Init()
224 if (!mHostTable
.Init()) {
225 return NS_ERROR_OUT_OF_MEMORY
;
229 if (IsChildProcess()) {
230 // Get the permissions from the parent process
231 InfallibleTArray
<IPC::Permission
> perms
;
232 ChildProcess()->SendReadPermissions(&perms
);
234 for (int i
= 0; i
< perms
.Length(); i
++) {
235 const IPC::Permission
&perm
= perms
[i
];
236 AddInternal(perm
.host
, perm
.type
, perm
.capability
, 0, perm
.expireType
,
237 perm
.expireTime
, eNotify
, eNoDBOperation
);
240 // Stop here; we don't need the DB in the child process
245 // ignore failure here, since it's non-fatal (we can run fine without
246 // persistent storage - e.g. if there's no profile).
247 // XXX should we tell the user about this?
250 mObserverService
= do_GetService("@mozilla.org/observer-service;1", &rv
);
251 if (NS_SUCCEEDED(rv
)) {
252 mObserverService
->AddObserver(this, "profile-before-change", PR_TRUE
);
253 mObserverService
->AddObserver(this, "profile-do-change", PR_TRUE
);
260 nsPermissionManager::InitDB(PRBool aRemoveFile
)
262 nsCOMPtr
<nsIFile
> permissionsFile
;
263 NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR
, getter_AddRefs(permissionsFile
));
264 if (!permissionsFile
)
265 return NS_ERROR_UNEXPECTED
;
267 nsresult rv
= permissionsFile
->AppendNative(NS_LITERAL_CSTRING(kPermissionsFileName
));
268 NS_ENSURE_SUCCESS(rv
, rv
);
271 PRBool exists
= PR_FALSE
;
272 rv
= permissionsFile
->Exists(&exists
);
273 NS_ENSURE_SUCCESS(rv
, rv
);
275 rv
= permissionsFile
->Remove(PR_FALSE
);
276 NS_ENSURE_SUCCESS(rv
, rv
);
280 nsCOMPtr
<mozIStorageService
> storage
= do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID
);
282 return NS_ERROR_UNEXPECTED
;
284 // cache a connection to the hosts database
285 rv
= storage
->OpenDatabase(permissionsFile
, getter_AddRefs(mDBConn
));
286 NS_ENSURE_SUCCESS(rv
, rv
);
289 mDBConn
->GetConnectionReady(&ready
);
291 // delete and try again
292 rv
= permissionsFile
->Remove(PR_FALSE
);
293 NS_ENSURE_SUCCESS(rv
, rv
);
295 rv
= storage
->OpenDatabase(permissionsFile
, getter_AddRefs(mDBConn
));
296 NS_ENSURE_SUCCESS(rv
, rv
);
298 mDBConn
->GetConnectionReady(&ready
);
300 return NS_ERROR_UNEXPECTED
;
303 PRBool tableExists
= PR_FALSE
;
304 mDBConn
->TableExists(NS_LITERAL_CSTRING("moz_hosts"), &tableExists
);
307 NS_ENSURE_SUCCESS(rv
, rv
);
310 // table already exists; check the schema version before reading
311 PRInt32 dbSchemaVersion
;
312 rv
= mDBConn
->GetSchemaVersion(&dbSchemaVersion
);
313 NS_ENSURE_SUCCESS(rv
, rv
);
315 switch (dbSchemaVersion
) {
317 // every time you increment the database schema, you need to implement
318 // the upgrading code from the previous version to the new one.
319 // fall through to current version
323 // previous non-expiry version of database. Upgrade it by adding the
324 // expiration columns
325 rv
= mDBConn
->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
326 "ALTER TABLE moz_hosts ADD expireType INTEGER"));
327 NS_ENSURE_SUCCESS(rv
, rv
);
329 rv
= mDBConn
->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
330 "ALTER TABLE moz_hosts ADD expireTime INTEGER"));
331 NS_ENSURE_SUCCESS(rv
, rv
);
333 rv
= mDBConn
->SetSchemaVersion(HOSTS_SCHEMA_VERSION
);
334 NS_ENSURE_SUCCESS(rv
, rv
);
337 // fall through to the next upgrade
340 case HOSTS_SCHEMA_VERSION
:
345 NS_WARNING("couldn't get schema version!");
347 // the table may be usable; someone might've just clobbered the schema
348 // version. we can treat this case like a downgrade using the codepath
349 // below, by verifying the columns we care about are all there. for now,
350 // re-set the schema version in the db, in case the checks succeed (if
351 // they don't, we're dropping the table anyway).
352 rv
= mDBConn
->SetSchemaVersion(HOSTS_SCHEMA_VERSION
);
353 NS_ENSURE_SUCCESS(rv
, rv
);
355 // fall through to downgrade check
358 // if columns have been added to the table, we can still use the ones we
359 // understand safely. if columns have been deleted or altered, just
360 // blow away the table and start from scratch! if you change the way
361 // a column is interpreted, make sure you also change its name so this
362 // check will catch it.
365 // check if all the expected columns exist
366 nsCOMPtr
<mozIStorageStatement
> stmt
;
367 rv
= mDBConn
->CreateStatement(NS_LITERAL_CSTRING(
368 "SELECT host, type, permission, expireType, expireTime FROM moz_hosts"),
369 getter_AddRefs(stmt
));
370 if (NS_SUCCEEDED(rv
))
373 // our columns aren't there - drop the table!
374 rv
= mDBConn
->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DROP TABLE moz_hosts"));
375 NS_ENSURE_SUCCESS(rv
, rv
);
378 NS_ENSURE_SUCCESS(rv
, rv
);
384 // make operations on the table asynchronous, for performance
385 mDBConn
->ExecuteSimpleSQL(NS_LITERAL_CSTRING("PRAGMA synchronous = OFF"));
387 // cache frequently used statements (for insertion, deletion, and updating)
388 rv
= mDBConn
->CreateStatement(NS_LITERAL_CSTRING(
389 "INSERT INTO moz_hosts "
390 "(id, host, type, permission, expireType, expireTime) "
391 "VALUES (?1, ?2, ?3, ?4, ?5, ?6)"), getter_AddRefs(mStmtInsert
));
392 NS_ENSURE_SUCCESS(rv
, rv
);
394 rv
= mDBConn
->CreateStatement(NS_LITERAL_CSTRING(
395 "DELETE FROM moz_hosts "
396 "WHERE id = ?1"), getter_AddRefs(mStmtDelete
));
397 NS_ENSURE_SUCCESS(rv
, rv
);
399 rv
= mDBConn
->CreateStatement(NS_LITERAL_CSTRING(
401 "SET permission = ?2, expireType= ?3, expireTime = ?4 WHERE id = ?1"),
402 getter_AddRefs(mStmtUpdate
));
403 NS_ENSURE_SUCCESS(rv
, rv
);
405 // check whether to import or just read in the db
412 // sets the schema version and creates the moz_hosts table.
414 nsPermissionManager::CreateTable()
416 // set the schema version, before creating the table
417 nsresult rv
= mDBConn
->SetSchemaVersion(HOSTS_SCHEMA_VERSION
);
418 if (NS_FAILED(rv
)) return rv
;
421 // SQL also lives in automation.py.in. If you change this SQL change that
423 return mDBConn
->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
424 "CREATE TABLE moz_hosts ("
425 " id INTEGER PRIMARY KEY"
428 ",permission INTEGER"
429 ",expireType INTEGER"
430 ",expireTime INTEGER"
435 nsPermissionManager::Add(nsIURI
*aURI
,
437 PRUint32 aPermission
,
438 PRUint32 aExpireType
,
442 ENSURE_NOT_CHILD_PROCESS
;
445 NS_ENSURE_ARG_POINTER(aURI
);
446 NS_ENSURE_ARG_POINTER(aType
);
447 NS_ENSURE_TRUE(aExpireType
== nsIPermissionManager::EXPIRE_NEVER
||
448 aExpireType
== nsIPermissionManager::EXPIRE_TIME
||
449 aExpireType
== nsIPermissionManager::EXPIRE_SESSION
,
450 NS_ERROR_INVALID_ARG
);
454 // Skip addition if the permission is already expired.
455 if (aExpireType
== nsIPermissionManager::EXPIRE_TIME
&&
456 aExpireTime
<= PR_Now() / 1000)
460 rv
= GetHost(aURI
, host
);
461 NS_ENSURE_SUCCESS(rv
, rv
);
463 return AddInternal(host
, nsDependentCString(aType
), aPermission
, 0,
464 aExpireType
, aExpireTime
, eNotify
, eWriteToDB
);
468 nsPermissionManager::AddInternal(const nsAFlatCString
&aHost
,
469 const nsAFlatCString
&aType
,
470 PRUint32 aPermission
,
472 PRUint32 aExpireType
,
474 NotifyOperationType aNotifyOperation
,
475 DBOperationType aDBOperation
)
478 if (!IsChildProcess()) {
479 // In the parent, send the update now, if the child is ready
480 if (mUpdateChildProcess
) {
481 IPC::Permission
permission((aHost
),
483 aPermission
, aExpireType
, aExpireTime
);
484 ParentProcess()->SendAddPermission(permission
);
490 gHostArena
= new PLArenaPool
;
492 return NS_ERROR_OUT_OF_MEMORY
;
493 PL_INIT_ARENA_POOL(gHostArena
, "PermissionHostArena", HOST_ARENA_SIZE
);
496 // look up the type index
497 PRInt32 typeIndex
= GetTypeIndex(aType
.get(), PR_TRUE
);
498 NS_ENSURE_TRUE(typeIndex
!= -1, NS_ERROR_OUT_OF_MEMORY
);
500 // When an entry already exists, PutEntry will return that, instead
501 // of adding a new one
502 nsHostEntry
*entry
= mHostTable
.PutEntry(aHost
.get());
503 if (!entry
) return NS_ERROR_FAILURE
;
504 if (!entry
->GetKey()) {
505 mHostTable
.RawRemoveEntry(entry
);
506 return NS_ERROR_OUT_OF_MEMORY
;
509 // figure out the transaction type, and get any existing permission value
511 PRInt32 index
= entry
->GetPermissionIndex(typeIndex
);
513 if (aPermission
== nsIPermissionManager::UNKNOWN_ACTION
)
516 op
= eOperationAdding
;
519 nsPermissionEntry oldPermissionEntry
= entry
->GetPermissions()[index
];
521 // remove the permission if the permission is UNKNOWN, update the
522 // permission if its value or expire type have changed OR if the time has
523 // changed and the expire type is time, otherwise, don't modify. There's
524 // no need to modify a permission that doesn't expire with time when the
525 // only thing changed is the expire time.
526 if (aPermission
== oldPermissionEntry
.mPermission
&&
527 aExpireType
== oldPermissionEntry
.mExpireType
&&
528 (aExpireType
!= nsIPermissionManager::EXPIRE_TIME
||
529 aExpireTime
== oldPermissionEntry
.mExpireTime
))
531 else if (aPermission
== nsIPermissionManager::UNKNOWN_ACTION
)
532 op
= eOperationRemoving
;
534 op
= eOperationChanging
;
537 // do the work for adding, deleting, or changing a permission:
538 // update the in-memory list, write to the db, and notify consumers.
547 case eOperationAdding
:
549 if (aDBOperation
== eWriteToDB
) {
550 // we'll be writing to the database - generate a known unique id
553 // we're reading from the database - use the id already assigned
557 entry
->GetPermissions().AppendElement(nsPermissionEntry(typeIndex
, aPermission
, id
, aExpireType
, aExpireTime
));
559 if (aDBOperation
== eWriteToDB
&& aExpireType
!= nsIPermissionManager::EXPIRE_SESSION
)
560 UpdateDB(op
, mStmtInsert
, id
, aHost
, aType
, aPermission
, aExpireType
, aExpireTime
);
562 if (aNotifyOperation
== eNotify
) {
563 NotifyObserversWithPermission(aHost
,
564 mTypeArray
[typeIndex
],
568 NS_LITERAL_STRING("added").get());
574 case eOperationRemoving
:
576 nsPermissionEntry oldPermissionEntry
= entry
->GetPermissions()[index
];
577 id
= oldPermissionEntry
.mID
;
578 entry
->GetPermissions().RemoveElementAt(index
);
580 // If no more types are present, remove the entry
581 if (entry
->GetPermissions().IsEmpty())
582 mHostTable
.RawRemoveEntry(entry
);
584 if (aDBOperation
== eWriteToDB
)
585 UpdateDB(op
, mStmtDelete
, id
, EmptyCString(), EmptyCString(), 0,
586 nsIPermissionManager::EXPIRE_NEVER
, 0);
588 if (aNotifyOperation
== eNotify
) {
589 NotifyObserversWithPermission(aHost
,
590 mTypeArray
[typeIndex
],
591 oldPermissionEntry
.mPermission
,
592 oldPermissionEntry
.mExpireType
,
593 oldPermissionEntry
.mExpireTime
,
594 NS_LITERAL_STRING("deleted").get());
600 case eOperationChanging
:
602 id
= entry
->GetPermissions()[index
].mID
;
603 entry
->GetPermissions()[index
].mPermission
= aPermission
;
605 if (aDBOperation
== eWriteToDB
&& aExpireType
!= nsIPermissionManager::EXPIRE_SESSION
)
606 UpdateDB(op
, mStmtUpdate
, id
, EmptyCString(), EmptyCString(), aPermission
, aExpireType
, aExpireTime
);
608 if (aNotifyOperation
== eNotify
) {
609 NotifyObserversWithPermission(aHost
,
610 mTypeArray
[typeIndex
],
614 NS_LITERAL_STRING("changed").get());
625 nsPermissionManager::Remove(const nsACString
&aHost
,
629 ENSURE_NOT_CHILD_PROCESS
;
632 NS_ENSURE_ARG_POINTER(aType
);
634 // AddInternal() handles removal, just let it do the work
635 return AddInternal(PromiseFlatCString(aHost
),
636 nsDependentCString(aType
),
637 nsIPermissionManager::UNKNOWN_ACTION
,
639 nsIPermissionManager::EXPIRE_NEVER
,
646 nsPermissionManager::RemoveAll()
649 ENSURE_NOT_CHILD_PROCESS
;
652 nsresult rv
= RemoveAllInternal();
653 NotifyObservers(nsnull
, NS_LITERAL_STRING("cleared").get());
658 nsPermissionManager::RemoveAllInternal()
660 RemoveAllFromMemory();
664 nsresult rv
= mDBConn
->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DELETE FROM moz_hosts"));
666 mStmtInsert
= nsnull
;
667 mStmtDelete
= nsnull
;
668 mStmtUpdate
= nsnull
;
670 rv
= InitDB(PR_TRUE
);
679 nsPermissionManager::TestExactPermission(nsIURI
*aURI
,
681 PRUint32
*aPermission
)
683 return CommonTestPermission(aURI
, aType
, aPermission
, PR_TRUE
);
687 nsPermissionManager::TestPermission(nsIURI
*aURI
,
689 PRUint32
*aPermission
)
691 return CommonTestPermission(aURI
, aType
, aPermission
, PR_FALSE
);
695 nsPermissionManager::CommonTestPermission(nsIURI
*aURI
,
697 PRUint32
*aPermission
,
698 PRBool aExactHostMatch
)
700 NS_ENSURE_ARG_POINTER(aURI
);
701 NS_ENSURE_ARG_POINTER(aType
);
704 *aPermission
= nsIPermissionManager::UNKNOWN_ACTION
;
707 nsresult rv
= GetHost(aURI
, host
);
708 // No host doesn't mean an error. Just return the default. Unless this is
709 // a file uri. In that case use a magic host.
712 rv
= aURI
->SchemeIs("file", &isFile
);
713 NS_ENSURE_SUCCESS(rv
, rv
);
715 host
.AssignLiteral("<file>");
722 PRInt32 typeIndex
= GetTypeIndex(aType
, PR_FALSE
);
723 // If type == -1, the type isn't known,
724 // so just return NS_OK
725 if (typeIndex
== -1) return NS_OK
;
727 nsHostEntry
*entry
= GetHostEntry(host
, typeIndex
, aExactHostMatch
);
729 *aPermission
= entry
->GetPermission(typeIndex
).mPermission
;
734 // Get hostentry for given host string and permission type.
735 // walk up the domain if needed.
736 // return null if nothing found.
737 // Also accepts host on the format "<foo>". This will perform an exact match
738 // lookup as the string doesn't contain any dots.
740 nsPermissionManager::GetHostEntry(const nsAFlatCString
&aHost
,
742 PRBool aExactHostMatch
)
746 PRInt64 now
= PR_Now() / 1000;
749 entry
= mHostTable
.GetEntry(aHost
.get() + offset
);
751 nsPermissionEntry permEntry
= entry
->GetPermission(aType
);
753 // if the entry is expired, remove and keep looking for others.
754 if (permEntry
.mExpireType
== nsIPermissionManager::EXPIRE_TIME
&&
755 permEntry
.mExpireTime
<= now
)
756 Remove(aHost
, mTypeArray
[aType
].get());
757 else if (permEntry
.mPermission
!= nsIPermissionManager::UNKNOWN_ACTION
)
760 // reset entry, to be able to return null on failure
764 break; // do not try super domains
766 offset
= aHost
.FindChar('.', offset
) + 1;
768 // walk up the domaintree (we stop as soon as we find a match,
769 // which will be the most specific domain we have an entry for).
770 } while (offset
> 0);
774 // helper struct for passing arguments into hash enumeration callback.
775 struct nsGetEnumeratorData
777 nsGetEnumeratorData(nsCOMArray
<nsIPermission
> *aArray
, const nsTArray
<nsCString
> *aTypes
)
781 nsCOMArray
<nsIPermission
> *array
;
782 const nsTArray
<nsCString
> *types
;
785 static PLDHashOperator
786 AddPermissionsToList(nsHostEntry
*entry
, void *arg
)
788 nsGetEnumeratorData
*data
= static_cast<nsGetEnumeratorData
*>(arg
);
790 for (PRUint32 i
= 0; i
< entry
->GetPermissions().Length(); ++i
) {
791 nsPermissionEntry
&permEntry
= entry
->GetPermissions()[i
];
793 nsPermission
*perm
= new nsPermission(entry
->GetHost(),
794 data
->types
->ElementAt(permEntry
.mType
),
795 permEntry
.mPermission
,
796 permEntry
.mExpireType
,
797 permEntry
.mExpireTime
);
799 data
->array
->AppendObject(perm
);
802 return PL_DHASH_NEXT
;
805 NS_IMETHODIMP
nsPermissionManager::GetEnumerator(nsISimpleEnumerator
**aEnum
)
808 ENSURE_NOT_CHILD_PROCESS
;
811 // roll an nsCOMArray of all our permissions, then hand out an enumerator
812 nsCOMArray
<nsIPermission
> array
;
813 nsGetEnumeratorData
data(&array
, &mTypeArray
);
815 mHostTable
.EnumerateEntries(AddPermissionsToList
, &data
);
817 return NS_NewArrayEnumerator(aEnum
, array
);
820 NS_IMETHODIMP
nsPermissionManager::Observe(nsISupports
*aSubject
, const char *aTopic
, const PRUnichar
*someData
)
823 ENSURE_NOT_CHILD_PROCESS
;
826 if (!nsCRT::strcmp(aTopic
, "profile-before-change")) {
827 // The profile is about to change,
828 // or is going away because the application is shutting down.
829 if (!nsCRT::strcmp(someData
, NS_LITERAL_STRING("shutdown-cleanse").get())) {
830 // clear the permissions file
833 RemoveAllFromMemory();
836 else if (!nsCRT::strcmp(aTopic
, "profile-do-change")) {
837 // the profile has already changed; init the db from the new location
844 //*****************************************************************************
845 //*** nsPermissionManager private methods
846 //*****************************************************************************
849 nsPermissionManager::RemoveAllFromMemory()
855 PL_FinishArenaPool(gHostArena
);
862 // Returns -1 on failure
864 nsPermissionManager::GetTypeIndex(const char *aType
,
867 for (PRUint32 i
= 0; i
< mTypeArray
.Length(); ++i
)
868 if (mTypeArray
[i
].Equals(aType
))
872 // Not found, but that is ok - we were just looking.
876 // This type was not registered before.
877 // append it to the array, without copy-constructing the string
878 nsCString
*elem
= mTypeArray
.AppendElement();
883 return mTypeArray
.Length() - 1;
886 // wrapper function for mangling (host,type,perm,expireType,expireTime)
887 // set into an nsIPermission.
889 nsPermissionManager::NotifyObserversWithPermission(const nsACString
&aHost
,
890 const nsCString
&aType
,
891 PRUint32 aPermission
,
892 PRUint32 aExpireType
,
894 const PRUnichar
*aData
)
896 nsCOMPtr
<nsIPermission
> permission
=
897 new nsPermission(aHost
, aType
, aPermission
, aExpireType
, aExpireTime
);
899 NotifyObservers(permission
, aData
);
902 // notify observers that the permission list changed. there are four possible
904 // "deleted" means a permission was deleted. aPermission is the deleted permission.
905 // "added" means a permission was added. aPermission is the added permission.
906 // "changed" means a permission was altered. aPermission is the new permission.
907 // "cleared" means the entire permission list was cleared. aPermission is null.
909 nsPermissionManager::NotifyObservers(nsIPermission
*aPermission
,
910 const PRUnichar
*aData
)
912 if (mObserverService
)
913 mObserverService
->NotifyObservers(aPermission
,
914 kPermissionChangeNotification
,
919 nsPermissionManager::Read()
922 ENSURE_NOT_CHILD_PROCESS
;
927 // delete expired permissions before we read in the db
929 // this deletion has its own scope so the write lock is released when done.
930 nsCOMPtr
<mozIStorageStatement
> stmtDeleteExpired
;
931 rv
= mDBConn
->CreateStatement(NS_LITERAL_CSTRING(
932 "DELETE FROM moz_hosts WHERE expireType = ?1 AND expireTime <= ?2"),
933 getter_AddRefs(stmtDeleteExpired
));
934 NS_ENSURE_SUCCESS(rv
, rv
);
936 rv
= stmtDeleteExpired
->BindInt32Parameter(0, nsIPermissionManager::EXPIRE_TIME
);
937 NS_ENSURE_SUCCESS(rv
, rv
);
939 rv
= stmtDeleteExpired
->BindInt64Parameter(1, PR_Now() / 1000);
940 NS_ENSURE_SUCCESS(rv
, rv
);
943 rv
= stmtDeleteExpired
->ExecuteStep(&hasResult
);
944 NS_ENSURE_SUCCESS(rv
, rv
);
947 nsCOMPtr
<mozIStorageStatement
> stmt
;
948 rv
= mDBConn
->CreateStatement(NS_LITERAL_CSTRING(
949 "SELECT id, host, type, permission, expireType, expireTime "
950 "FROM moz_hosts"), getter_AddRefs(stmt
));
951 NS_ENSURE_SUCCESS(rv
, rv
);
954 nsCAutoString host
, type
;
959 while (NS_SUCCEEDED(stmt
->ExecuteStep(&hasResult
)) && hasResult
) {
960 // explicitly set our entry id counter for use in AddInternal(),
961 // and keep track of the largest id so we know where to pick up.
962 id
= stmt
->AsInt64(0);
966 rv
= stmt
->GetUTF8String(1, host
);
967 NS_ENSURE_SUCCESS(rv
, rv
);
969 rv
= stmt
->GetUTF8String(2, type
);
970 NS_ENSURE_SUCCESS(rv
, rv
);
972 permission
= stmt
->AsInt32(3);
973 expireType
= stmt
->AsInt32(4);
975 // convert into PRInt64 value (milliseconds)
976 expireTime
= stmt
->AsInt64(5);
978 rv
= AddInternal(host
, type
, permission
, id
, expireType
, expireTime
,
979 eDontNotify
, eNoDBOperation
);
980 NS_ENSURE_SUCCESS(rv
, rv
);
986 static const char kMatchTypeHost
[] = "host";
989 nsPermissionManager::Import()
992 ENSURE_NOT_CHILD_PROCESS
;
997 nsCOMPtr
<nsIFile
> permissionsFile
;
998 rv
= NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR
, getter_AddRefs(permissionsFile
));
999 if (NS_FAILED(rv
)) return rv
;
1001 rv
= permissionsFile
->AppendNative(NS_LITERAL_CSTRING(kHostpermFileName
));
1002 NS_ENSURE_SUCCESS(rv
, rv
);
1004 nsCOMPtr
<nsIInputStream
> fileInputStream
;
1005 rv
= NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream
),
1007 if (NS_FAILED(rv
)) return rv
;
1009 nsCOMPtr
<nsILineInputStream
> lineInputStream
= do_QueryInterface(fileInputStream
, &rv
);
1010 NS_ENSURE_SUCCESS(rv
, rv
);
1012 // start a transaction on the storage db, to optimize insertions.
1013 // transaction will automically commit on completion
1014 mozStorageTransaction
transaction(mDBConn
, PR_TRUE
);
1017 * matchtype \t type \t permission \t host
1018 * Only "host" is supported for matchtype
1019 * type is a string that identifies the type of permission (e.g. "cookie")
1020 * permission is an integer between 1 and 15
1023 nsCAutoString buffer
;
1024 PRBool isMore
= PR_TRUE
;
1025 while (isMore
&& NS_SUCCEEDED(lineInputStream
->ReadLine(buffer
, &isMore
))) {
1026 if (buffer
.IsEmpty() || buffer
.First() == '#') {
1030 nsTArray
<nsCString
> lineArray
;
1032 // Split the line at tabs
1033 ParseString(buffer
, '\t', lineArray
);
1035 if (lineArray
[0].EqualsLiteral(kMatchTypeHost
) &&
1036 lineArray
.Length() == 4) {
1039 PRUint32 permission
= lineArray
[2].ToInteger(&error
);
1043 // hosts might be encoded in UTF8; switch them to ACE to be consistent
1044 if (!IsASCII(lineArray
[3])) {
1045 rv
= NormalizeToACE(lineArray
[3]);
1050 rv
= AddInternal(lineArray
[3], lineArray
[1], permission
, 0,
1051 nsIPermissionManager::EXPIRE_NEVER
, 0, eDontNotify
, eWriteToDB
);
1052 NS_ENSURE_SUCCESS(rv
, rv
);
1056 // we're done importing - delete the old file
1057 permissionsFile
->Remove(PR_FALSE
);
1063 nsPermissionManager::NormalizeToACE(nsCString
&aHost
)
1065 // lazily init the IDN service
1068 mIDNService
= do_GetService(NS_IDNSERVICE_CONTRACTID
, &rv
);
1069 NS_ENSURE_SUCCESS(rv
, rv
);
1072 return mIDNService
->ConvertUTF8toACE(aHost
, aHost
);
1076 nsPermissionManager::GetHost(nsIURI
*aURI
, nsACString
&aResult
)
1078 nsCOMPtr
<nsIURI
> innerURI
= NS_GetInnermostURI(aURI
);
1079 if (!innerURI
) return NS_ERROR_FAILURE
;
1081 nsresult rv
= innerURI
->GetAsciiHost(aResult
);
1083 if (NS_FAILED(rv
) || aResult
.IsEmpty())
1084 return NS_ERROR_UNEXPECTED
;
1090 nsPermissionManager::UpdateDB(OperationType aOp
,
1091 mozIStorageStatement
* aStmt
,
1093 const nsACString
&aHost
,
1094 const nsACString
&aType
,
1095 PRUint32 aPermission
,
1096 PRUint32 aExpireType
,
1097 PRInt64 aExpireTime
)
1100 ENSURE_NOT_CHILD_PROCESS_NORET
;
1105 // no statement is ok - just means we don't have a profile
1110 case eOperationAdding
:
1112 rv
= aStmt
->BindInt64Parameter(0, aID
);
1113 if (NS_FAILED(rv
)) break;
1115 rv
= aStmt
->BindUTF8StringParameter(1, aHost
);
1116 if (NS_FAILED(rv
)) break;
1118 rv
= aStmt
->BindUTF8StringParameter(2, aType
);
1119 if (NS_FAILED(rv
)) break;
1121 rv
= aStmt
->BindInt32Parameter(3, aPermission
);
1122 if (NS_FAILED(rv
)) break;
1124 rv
= aStmt
->BindInt32Parameter(4, aExpireType
);
1125 if (NS_FAILED(rv
)) break;
1127 rv
= aStmt
->BindInt64Parameter(5, aExpireTime
);
1131 case eOperationRemoving
:
1133 rv
= aStmt
->BindInt64Parameter(0, aID
);
1137 case eOperationChanging
:
1139 rv
= aStmt
->BindInt64Parameter(0, aID
);
1140 if (NS_FAILED(rv
)) break;
1142 rv
= aStmt
->BindInt32Parameter(1, aPermission
);
1143 if (NS_FAILED(rv
)) break;
1145 rv
= aStmt
->BindInt32Parameter(2, aExpireType
);
1146 if (NS_FAILED(rv
)) break;
1148 rv
= aStmt
->BindInt64Parameter(3, aExpireTime
);
1154 NS_NOTREACHED("need a valid operation in UpdateDB()!");
1155 rv
= NS_ERROR_UNEXPECTED
;
1160 if (NS_SUCCEEDED(rv
)) {
1162 rv
= aStmt
->ExecuteStep(&hasResult
);
1167 NS_WARNING("db change failed!");