Backout 30bfb150da06 (bug 449315) due to unit test timeouts.
[wine-gecko.git] / extensions / cookie / nsPermissionManager.cpp
blob8e92fdc95dc44462101e47ad07a17282d8dfa879
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
13 * License.
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.
22 * Contributor(s):
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 ***** */
40 #include "nsPermissionManager.h"
41 #include "nsPermission.h"
42 #include "nsCRT.h"
43 #include "nsNetUtil.h"
44 #include "nsCOMArray.h"
45 #include "nsArrayEnumerator.h"
46 #include "nsILineInputStream.h"
47 #include "nsIIDNService.h"
48 #include "nsAppDirectoryServiceDefs.h"
49 #include "prprf.h"
50 #include "mozIStorageService.h"
51 #include "mozIStorageStatement.h"
52 #include "mozIStorageConnection.h"
53 #include "mozStorageHelper.h"
54 #include "mozStorageCID.h"
56 ////////////////////////////////////////////////////////////////////////////////
58 #define PL_ARENA_CONST_ALIGN_MASK 3
59 #include "plarena.h"
61 static PLArenaPool *gHostArena = nsnull;
63 // making sHostArena 512b for nice allocation
64 // growing is quite cheap
65 #define HOST_ARENA_SIZE 512
67 // equivalent to strdup() - does no error checking,
68 // we're assuming we're only called with a valid pointer
69 static char *
70 ArenaStrDup(const char* str, PLArenaPool* aArena)
72 void* mem;
73 const PRUint32 size = strlen(str) + 1;
74 PL_ARENA_ALLOCATE(mem, aArena, size);
75 if (mem)
76 memcpy(mem, str, size);
77 return static_cast<char*>(mem);
80 nsHostEntry::nsHostEntry(const char* aHost)
82 mHost = ArenaStrDup(aHost, gHostArena);
85 // XXX this can fail on OOM
86 nsHostEntry::nsHostEntry(const nsHostEntry& toCopy)
87 : mHost(toCopy.mHost)
88 , mPermissions(toCopy.mPermissions)
92 ////////////////////////////////////////////////////////////////////////////////
93 // nsPermissionManager Implementation
95 static const char kPermissionsFileName[] = "permissions.sqlite";
96 #define HOSTS_SCHEMA_VERSION 1
98 static const char kHostpermFileName[] = "hostperm.1";
100 static const char kPermissionChangeNotification[] = PERM_CHANGE_NOTIFICATION;
102 NS_IMPL_ISUPPORTS3(nsPermissionManager, nsIPermissionManager, nsIObserver, nsISupportsWeakReference)
104 nsPermissionManager::nsPermissionManager()
105 : mLargestID(0)
109 nsPermissionManager::~nsPermissionManager()
111 RemoveAllFromMemory();
114 nsresult
115 nsPermissionManager::Init()
117 nsresult rv;
119 if (!mHostTable.Init()) {
120 return NS_ERROR_OUT_OF_MEMORY;
123 // ignore failure here, since it's non-fatal (we can run fine without
124 // persistent storage - e.g. if there's no profile).
125 // XXX should we tell the user about this?
126 InitDB();
128 mObserverService = do_GetService("@mozilla.org/observer-service;1", &rv);
129 if (NS_SUCCEEDED(rv)) {
130 mObserverService->AddObserver(this, "profile-before-change", PR_TRUE);
131 mObserverService->AddObserver(this, "profile-do-change", PR_TRUE);
134 return NS_OK;
137 nsresult
138 nsPermissionManager::InitDB()
140 nsCOMPtr<nsIFile> permissionsFile;
141 NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(permissionsFile));
142 if (!permissionsFile)
143 return NS_ERROR_UNEXPECTED;
145 nsresult rv = permissionsFile->AppendNative(NS_LITERAL_CSTRING(kPermissionsFileName));
146 NS_ENSURE_SUCCESS(rv, rv);
148 nsCOMPtr<mozIStorageService> storage = do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
149 if (!storage)
150 return NS_ERROR_UNEXPECTED;
152 // cache a connection to the hosts database
153 rv = storage->OpenDatabase(permissionsFile, getter_AddRefs(mDBConn));
154 NS_ENSURE_SUCCESS(rv, rv);
156 PRBool ready;
157 mDBConn->GetConnectionReady(&ready);
158 if (!ready) {
159 // delete and try again
160 rv = permissionsFile->Remove(PR_FALSE);
161 NS_ENSURE_SUCCESS(rv, rv);
163 rv = storage->OpenDatabase(permissionsFile, getter_AddRefs(mDBConn));
164 NS_ENSURE_SUCCESS(rv, rv);
166 mDBConn->GetConnectionReady(&ready);
167 if (!ready)
168 return NS_ERROR_UNEXPECTED;
171 PRBool tableExists = PR_FALSE;
172 mDBConn->TableExists(NS_LITERAL_CSTRING("moz_hosts"), &tableExists);
173 if (!tableExists) {
174 rv = CreateTable();
175 NS_ENSURE_SUCCESS(rv, rv);
177 } else {
178 // table already exists; check the schema version before reading
179 PRInt32 dbSchemaVersion;
180 rv = mDBConn->GetSchemaVersion(&dbSchemaVersion);
181 NS_ENSURE_SUCCESS(rv, rv);
183 switch (dbSchemaVersion) {
184 // upgrading.
185 // every time you increment the database schema, you need to implement
186 // the upgrading code from the previous version to the new one.
187 // fall through to current version
189 // current version.
190 case HOSTS_SCHEMA_VERSION:
191 break;
193 case 0:
195 NS_WARNING("couldn't get schema version!");
197 // the table may be usable; someone might've just clobbered the schema
198 // version. we can treat this case like a downgrade using the codepath
199 // below, by verifying the columns we care about are all there. for now,
200 // re-set the schema version in the db, in case the checks succeed (if
201 // they don't, we're dropping the table anyway).
202 rv = mDBConn->SetSchemaVersion(HOSTS_SCHEMA_VERSION);
203 NS_ENSURE_SUCCESS(rv, rv);
205 // fall through to downgrade check
207 // downgrading.
208 // if columns have been added to the table, we can still use the ones we
209 // understand safely. if columns have been deleted or altered, just
210 // blow away the table and start from scratch! if you change the way
211 // a column is interpreted, make sure you also change its name so this
212 // check will catch it.
213 default:
215 // check if all the expected columns exist
216 nsCOMPtr<mozIStorageStatement> stmt;
217 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
218 "SELECT host, type, permission FROM moz_hosts"), getter_AddRefs(stmt));
219 if (NS_SUCCEEDED(rv))
220 break;
222 // our columns aren't there - drop the table!
223 rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DROP TABLE moz_hosts"));
224 NS_ENSURE_SUCCESS(rv, rv);
226 rv = CreateTable();
227 NS_ENSURE_SUCCESS(rv, rv);
229 break;
233 // make operations on the table asynchronous, for performance
234 mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("PRAGMA synchronous = OFF"));
236 // cache frequently used statements (for insertion, deletion, and updating)
237 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
238 "INSERT INTO moz_hosts "
239 "(id, host, type, permission) "
240 "VALUES (?1, ?2, ?3, ?4)"), getter_AddRefs(mStmtInsert));
241 NS_ENSURE_SUCCESS(rv, rv);
243 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
244 "DELETE FROM moz_hosts "
245 "WHERE id = ?1"), getter_AddRefs(mStmtDelete));
246 NS_ENSURE_SUCCESS(rv, rv);
248 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
249 "UPDATE moz_hosts "
250 "SET permission = ?2 WHERE id = ?1"), getter_AddRefs(mStmtUpdate));
251 NS_ENSURE_SUCCESS(rv, rv);
253 // check whether to import or just read in the db
254 if (tableExists)
255 return Read();
257 return Import();
260 // sets the schema version and creates the moz_hosts table.
261 nsresult
262 nsPermissionManager::CreateTable()
264 // set the schema version, before creating the table
265 nsresult rv = mDBConn->SetSchemaVersion(HOSTS_SCHEMA_VERSION);
266 if (NS_FAILED(rv)) return rv;
268 // create the table
269 return mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
270 "CREATE TABLE moz_hosts ("
271 " id INTEGER PRIMARY KEY"
272 ",host TEXT"
273 ",type TEXT"
274 ",permission INTEGER"
275 ")"));
278 NS_IMETHODIMP
279 nsPermissionManager::Add(nsIURI *aURI,
280 const char *aType,
281 PRUint32 aPermission)
283 NS_ENSURE_ARG_POINTER(aURI);
284 NS_ENSURE_ARG_POINTER(aType);
286 nsresult rv;
288 nsCAutoString host;
289 rv = GetHost(aURI, host);
290 NS_ENSURE_SUCCESS(rv, rv);
292 return AddInternal(host, nsDependentCString(aType), aPermission, 0, eNotify, eWriteToDB);
295 nsresult
296 nsPermissionManager::AddInternal(const nsAFlatCString &aHost,
297 const nsAFlatCString &aType,
298 PRUint32 aPermission,
299 PRInt64 aID,
300 NotifyOperationType aNotifyOperation,
301 DBOperationType aDBOperation)
303 if (!gHostArena) {
304 gHostArena = new PLArenaPool;
305 if (!gHostArena)
306 return NS_ERROR_OUT_OF_MEMORY;
307 PL_INIT_ARENA_POOL(gHostArena, "PermissionHostArena", HOST_ARENA_SIZE);
310 // look up the type index
311 PRInt32 typeIndex = GetTypeIndex(aType.get(), PR_TRUE);
312 NS_ENSURE_TRUE(typeIndex != -1, NS_ERROR_OUT_OF_MEMORY);
314 // When an entry already exists, PutEntry will return that, instead
315 // of adding a new one
316 nsHostEntry *entry = mHostTable.PutEntry(aHost.get());
317 if (!entry) return NS_ERROR_FAILURE;
318 if (!entry->GetKey()) {
319 mHostTable.RawRemoveEntry(entry);
320 return NS_ERROR_OUT_OF_MEMORY;
323 // figure out the transaction type, and get any existing permission value
324 OperationType op;
325 PRInt32 index = entry->GetPermissionIndex(typeIndex);
326 PRUint32 oldPermission;
327 if (index == -1) {
328 if (aPermission == nsIPermissionManager::UNKNOWN_ACTION)
329 op = eOperationNone;
330 else
331 op = eOperationAdding;
333 } else {
334 oldPermission = entry->GetPermissions()[index].mPermission;
336 if (aPermission == oldPermission)
337 op = eOperationNone;
338 else if (aPermission == nsIPermissionManager::UNKNOWN_ACTION)
339 op = eOperationRemoving;
340 else
341 op = eOperationChanging;
344 // do the work for adding, deleting, or changing a permission:
345 // update the in-memory list, write to the db, and notify consumers.
346 PRInt64 id;
347 switch (op) {
348 case eOperationNone:
350 // nothing to do
351 return NS_OK;
354 case eOperationAdding:
356 if (aDBOperation == eWriteToDB) {
357 // we'll be writing to the database - generate a known unique id
358 id = ++mLargestID;
359 } else {
360 // we're reading from the database - use the id already assigned
361 id = aID;
364 entry->GetPermissions().AppendElement(nsPermissionEntry(typeIndex, aPermission, id));
366 if (aDBOperation == eWriteToDB)
367 UpdateDB(op, mStmtInsert, id, aHost, aType, aPermission);
369 if (aNotifyOperation == eNotify) {
370 NotifyObserversWithPermission(aHost,
371 mTypeArray[typeIndex],
372 aPermission,
373 NS_LITERAL_STRING("added").get());
376 break;
379 case eOperationRemoving:
381 id = entry->GetPermissions()[index].mID;
382 entry->GetPermissions().RemoveElementAt(index);
384 // If no more types are present, remove the entry
385 if (entry->GetPermissions().IsEmpty())
386 mHostTable.RawRemoveEntry(entry);
388 if (aDBOperation == eWriteToDB)
389 UpdateDB(op, mStmtDelete, id, EmptyCString(), EmptyCString(), 0);
391 if (aNotifyOperation == eNotify) {
392 NotifyObserversWithPermission(aHost,
393 mTypeArray[typeIndex],
394 oldPermission,
395 NS_LITERAL_STRING("deleted").get());
398 break;
401 case eOperationChanging:
403 id = entry->GetPermissions()[index].mID;
404 entry->GetPermissions()[index].mPermission = aPermission;
406 if (aDBOperation == eWriteToDB)
407 UpdateDB(op, mStmtUpdate, id, EmptyCString(), EmptyCString(), aPermission);
409 if (aNotifyOperation == eNotify) {
410 NotifyObserversWithPermission(aHost,
411 mTypeArray[typeIndex],
412 aPermission,
413 NS_LITERAL_STRING("changed").get());
416 break;
420 return NS_OK;
423 NS_IMETHODIMP
424 nsPermissionManager::Remove(const nsACString &aHost,
425 const char *aType)
427 NS_ENSURE_ARG_POINTER(aType);
429 // AddInternal() handles removal, just let it do the work
430 return AddInternal(PromiseFlatCString(aHost),
431 nsDependentCString(aType),
432 nsIPermissionManager::UNKNOWN_ACTION,
434 eNotify,
435 eWriteToDB);
438 NS_IMETHODIMP
439 nsPermissionManager::RemoveAll()
441 nsresult rv = RemoveAllInternal();
442 NotifyObservers(nsnull, NS_LITERAL_STRING("cleared").get());
443 return rv;
446 nsresult
447 nsPermissionManager::RemoveAllInternal()
449 RemoveAllFromMemory();
451 // clear the db
452 if (mDBConn) {
453 nsresult rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DELETE FROM moz_hosts"));
454 if (NS_FAILED(rv)) {
455 NS_WARNING("db delete failed");
456 return rv;
460 return NS_OK;
463 NS_IMETHODIMP
464 nsPermissionManager::TestExactPermission(nsIURI *aURI,
465 const char *aType,
466 PRUint32 *aPermission)
468 return CommonTestPermission(aURI, aType, aPermission, PR_TRUE);
471 NS_IMETHODIMP
472 nsPermissionManager::TestPermission(nsIURI *aURI,
473 const char *aType,
474 PRUint32 *aPermission)
476 return CommonTestPermission(aURI, aType, aPermission, PR_FALSE);
479 nsresult
480 nsPermissionManager::CommonTestPermission(nsIURI *aURI,
481 const char *aType,
482 PRUint32 *aPermission,
483 PRBool aExactHostMatch)
485 NS_ENSURE_ARG_POINTER(aURI);
486 NS_ENSURE_ARG_POINTER(aType);
488 // set the default
489 *aPermission = nsIPermissionManager::UNKNOWN_ACTION;
491 nsCAutoString host;
492 nsresult rv = GetHost(aURI, host);
493 // no host doesn't mean an error. just return the default
494 if (NS_FAILED(rv)) return NS_OK;
496 PRInt32 typeIndex = GetTypeIndex(aType, PR_FALSE);
497 // If type == -1, the type isn't known,
498 // so just return NS_OK
499 if (typeIndex == -1) return NS_OK;
501 nsHostEntry *entry = GetHostEntry(host, typeIndex, aExactHostMatch);
502 if (entry)
503 *aPermission = entry->GetPermission(typeIndex);
505 return NS_OK;
508 // Get hostentry for given host string and permission type.
509 // walk up the domain if needed.
510 // return null if nothing found.
511 nsHostEntry *
512 nsPermissionManager::GetHostEntry(const nsAFlatCString &aHost,
513 PRUint32 aType,
514 PRBool aExactHostMatch)
516 PRUint32 offset = 0;
517 nsHostEntry *entry;
518 do {
519 entry = mHostTable.GetEntry(aHost.get() + offset);
520 if (entry) {
521 if (entry->GetPermission(aType) != nsIPermissionManager::UNKNOWN_ACTION)
522 break;
524 // reset entry, to be able to return null on failure
525 entry = nsnull;
527 if (aExactHostMatch)
528 break; // do not try super domains
530 offset = aHost.FindChar('.', offset) + 1;
532 // walk up the domaintree (we stop as soon as we find a match,
533 // which will be the most specific domain we have an entry for).
534 } while (offset > 0);
535 return entry;
538 // helper struct for passing arguments into hash enumeration callback.
539 struct nsGetEnumeratorData
541 nsGetEnumeratorData(nsCOMArray<nsIPermission> *aArray, const nsTArray<nsCString> *aTypes)
542 : array(aArray)
543 , types(aTypes) {}
545 nsCOMArray<nsIPermission> *array;
546 const nsTArray<nsCString> *types;
549 static PLDHashOperator
550 AddPermissionsToList(nsHostEntry *entry, void *arg)
552 nsGetEnumeratorData *data = static_cast<nsGetEnumeratorData *>(arg);
554 for (PRUint32 i = 0; i < entry->GetPermissions().Length(); ++i) {
555 nsPermissionEntry &permEntry = entry->GetPermissions()[i];
557 nsPermission *perm = new nsPermission(entry->GetHost(),
558 data->types->ElementAt(permEntry.mType),
559 permEntry.mPermission);
561 data->array->AppendObject(perm);
564 return PL_DHASH_NEXT;
567 NS_IMETHODIMP nsPermissionManager::GetEnumerator(nsISimpleEnumerator **aEnum)
569 // roll an nsCOMArray of all our permissions, then hand out an enumerator
570 nsCOMArray<nsIPermission> array;
571 nsGetEnumeratorData data(&array, &mTypeArray);
573 mHostTable.EnumerateEntries(AddPermissionsToList, &data);
575 return NS_NewArrayEnumerator(aEnum, array);
578 NS_IMETHODIMP nsPermissionManager::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *someData)
580 if (!nsCRT::strcmp(aTopic, "profile-before-change")) {
581 // The profile is about to change,
582 // or is going away because the application is shutting down.
583 if (!nsCRT::strcmp(someData, NS_LITERAL_STRING("shutdown-cleanse").get())) {
584 // clear the permissions file
585 RemoveAllInternal();
586 } else {
587 RemoveAllFromMemory();
590 else if (!nsCRT::strcmp(aTopic, "profile-do-change")) {
591 // the profile has already changed; init the db from the new location
592 InitDB();
595 return NS_OK;
598 //*****************************************************************************
599 //*** nsPermissionManager private methods
600 //*****************************************************************************
602 nsresult
603 nsPermissionManager::RemoveAllFromMemory()
605 mLargestID = 0;
606 mTypeArray.Clear();
607 mHostTable.Clear();
608 if (gHostArena) {
609 PL_FinishArenaPool(gHostArena);
610 delete gHostArena;
612 gHostArena = nsnull;
613 return NS_OK;
616 // Returns -1 on failure
617 PRInt32
618 nsPermissionManager::GetTypeIndex(const char *aType,
619 PRBool aAdd)
621 for (PRUint32 i = 0; i < mTypeArray.Length(); ++i)
622 if (mTypeArray[i].Equals(aType))
623 return i;
625 if (!aAdd) {
626 // Not found, but that is ok - we were just looking.
627 return -1;
630 // This type was not registered before.
631 // append it to the array, without copy-constructing the string
632 nsCString *elem = mTypeArray.AppendElement();
633 if (!elem)
634 return -1;
636 elem->Assign(aType);
637 return mTypeArray.Length() - 1;
640 // wrapper function for mangling (host,type,perm) triplet into an nsIPermission.
641 void
642 nsPermissionManager::NotifyObserversWithPermission(const nsACString &aHost,
643 const nsCString &aType,
644 PRUint32 aPermission,
645 const PRUnichar *aData)
647 nsCOMPtr<nsIPermission> permission =
648 new nsPermission(aHost, aType, aPermission);
649 if (permission)
650 NotifyObservers(permission, aData);
653 // notify observers that the permission list changed. there are four possible
654 // values for aData:
655 // "deleted" means a permission was deleted. aPermission is the deleted permission.
656 // "added" means a permission was added. aPermission is the added permission.
657 // "changed" means a permission was altered. aPermission is the new permission.
658 // "cleared" means the entire permission list was cleared. aPermission is null.
659 void
660 nsPermissionManager::NotifyObservers(nsIPermission *aPermission,
661 const PRUnichar *aData)
663 if (mObserverService)
664 mObserverService->NotifyObservers(aPermission,
665 kPermissionChangeNotification,
666 aData);
669 nsresult
670 nsPermissionManager::Read()
672 nsresult rv;
674 nsCOMPtr<mozIStorageStatement> stmt;
675 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
676 "SELECT id, host, type, permission "
677 "FROM moz_hosts"), getter_AddRefs(stmt));
678 NS_ENSURE_SUCCESS(rv, rv);
680 PRInt64 id;
681 nsCAutoString host, type;
682 PRUint32 permission;
683 PRBool hasResult;
684 while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
685 // explicitly set our entry id counter for use in AddInternal(),
686 // and keep track of the largest id so we know where to pick up.
687 id = stmt->AsInt64(0);
688 if (id > mLargestID)
689 mLargestID = id;
691 rv = stmt->GetUTF8String(1, host);
692 NS_ENSURE_SUCCESS(rv, rv);
694 rv = stmt->GetUTF8String(2, type);
695 NS_ENSURE_SUCCESS(rv, rv);
697 permission = stmt->AsInt32(3);
699 rv = AddInternal(host, type, permission, id, eDontNotify, eNoDBOperation);
700 NS_ENSURE_SUCCESS(rv, rv);
703 return NS_OK;
706 static const char kMatchTypeHost[] = "host";
708 nsresult
709 nsPermissionManager::Import()
711 nsresult rv;
713 nsCOMPtr<nsIFile> permissionsFile;
714 rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(permissionsFile));
715 if (NS_FAILED(rv)) return rv;
717 rv = permissionsFile->AppendNative(NS_LITERAL_CSTRING(kHostpermFileName));
718 NS_ENSURE_SUCCESS(rv, rv);
720 nsCOMPtr<nsIInputStream> fileInputStream;
721 rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream),
722 permissionsFile);
723 if (NS_FAILED(rv)) return rv;
725 nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(fileInputStream, &rv);
726 NS_ENSURE_SUCCESS(rv, rv);
728 // start a transaction on the storage db, to optimize insertions.
729 // transaction will automically commit on completion
730 mozStorageTransaction transaction(mDBConn, PR_TRUE);
732 /* format is:
733 * matchtype \t type \t permission \t host
734 * Only "host" is supported for matchtype
735 * type is a string that identifies the type of permission (e.g. "cookie")
736 * permission is an integer between 1 and 15
739 nsCAutoString buffer;
740 PRBool isMore = PR_TRUE;
741 while (isMore && NS_SUCCEEDED(lineInputStream->ReadLine(buffer, &isMore))) {
742 if (buffer.IsEmpty() || buffer.First() == '#') {
743 continue;
746 nsCStringArray lineArray;
748 // Split the line at tabs
749 lineArray.ParseString(buffer.get(), "\t");
751 if (lineArray[0]->EqualsLiteral(kMatchTypeHost) &&
752 lineArray.Count() == 4) {
754 PRInt32 error;
755 PRUint32 permission = lineArray[2]->ToInteger(&error);
756 if (error)
757 continue;
759 // hosts might be encoded in UTF8; switch them to ACE to be consistent
760 if (!IsASCII(*lineArray[3])) {
761 rv = NormalizeToACE(*lineArray[3]);
762 if (NS_FAILED(rv))
763 continue;
766 rv = AddInternal(*lineArray[3], *lineArray[1], permission, 0, eDontNotify, eWriteToDB);
767 NS_ENSURE_SUCCESS(rv, rv);
771 // we're done importing - delete the old file
772 permissionsFile->Remove(PR_FALSE);
774 return NS_OK;
777 nsresult
778 nsPermissionManager::NormalizeToACE(nsCString &aHost)
780 // lazily init the IDN service
781 if (!mIDNService) {
782 nsresult rv;
783 mIDNService = do_GetService(NS_IDNSERVICE_CONTRACTID, &rv);
784 NS_ENSURE_SUCCESS(rv, rv);
787 return mIDNService->ConvertUTF8toACE(aHost, aHost);
790 nsresult
791 nsPermissionManager::GetHost(nsIURI *aURI, nsACString &aResult)
793 nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(aURI);
794 if (!innerURI) return NS_ERROR_FAILURE;
796 nsresult rv = innerURI->GetAsciiHost(aResult);
798 if (NS_FAILED(rv) || aResult.IsEmpty())
799 return NS_ERROR_UNEXPECTED;
801 return NS_OK;
804 void
805 nsPermissionManager::UpdateDB(OperationType aOp,
806 mozIStorageStatement* aStmt,
807 PRInt64 aID,
808 const nsACString &aHost,
809 const nsACString &aType,
810 PRUint32 aPermission)
812 nsresult rv;
814 // no statement is ok - just means we don't have a profile
815 if (!aStmt)
816 return;
818 switch (aOp) {
819 case eOperationAdding:
821 rv = aStmt->BindInt64Parameter(0, aID);
822 if (NS_FAILED(rv)) break;
824 rv = aStmt->BindUTF8StringParameter(1, aHost);
825 if (NS_FAILED(rv)) break;
827 rv = aStmt->BindUTF8StringParameter(2, aType);
828 if (NS_FAILED(rv)) break;
830 rv = aStmt->BindInt32Parameter(3, aPermission);
831 break;
834 case eOperationRemoving:
836 rv = aStmt->BindInt64Parameter(0, aID);
837 break;
840 case eOperationChanging:
842 rv = aStmt->BindInt64Parameter(0, aID);
843 if (NS_FAILED(rv)) break;
845 rv = aStmt->BindInt32Parameter(1, aPermission);
846 break;
849 default:
851 NS_NOTREACHED("need a valid operation in UpdateDB()!");
852 rv = NS_ERROR_UNEXPECTED;
853 break;
857 if (NS_SUCCEEDED(rv)) {
858 PRBool hasResult;
859 rv = aStmt->ExecuteStep(&hasResult);
860 aStmt->Reset();
863 if (NS_FAILED(rv))
864 NS_WARNING("db change failed!");