Back to index

lightning-sunbird  0.9+nobinonly
nsPermissionManager.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Michiel van Leeuwen (mvl@exedo.nl)
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsPermissionManager.h"
00040 #include "nsPermission.h"
00041 #include "nsCRT.h"
00042 #include "nsNetUtil.h"
00043 #include "nsILineInputStream.h"
00044 #include "nsAppDirectoryServiceDefs.h"
00045 #include "nsVoidArray.h"
00046 #include "prprf.h"
00047 
00049 
00050 #define PL_ARENA_CONST_ALIGN_MASK 3
00051 #include "plarena.h"
00052 
00053 static PLArenaPool *gHostArena = nsnull;
00054 
00055 // making sHostArena 512b for nice allocation
00056 // growing is quite cheap
00057 #define HOST_ARENA_SIZE 512
00058 
00059 // equivalent to strdup() - does no error checking,
00060 // we're assuming we're only called with a valid pointer
00061 static char *
00062 ArenaStrDup(const char* str, PLArenaPool* aArena)
00063 {
00064   void* mem;
00065   const PRUint32 size = strlen(str) + 1;
00066   PL_ARENA_ALLOCATE(mem, aArena, size);
00067   if (mem)
00068     memcpy(mem, str, size);
00069   return NS_STATIC_CAST(char*, mem);
00070 }
00071 
00072 nsHostEntry::nsHostEntry(const char* aHost)
00073 {
00074   mHost = ArenaStrDup(aHost, gHostArena);
00075   mPermissions[0] = mPermissions[1] = 0;
00076 }
00077 
00078 nsHostEntry::nsHostEntry(const nsHostEntry& toCopy)
00079 {
00080   // nsTHashtable shouldn't allow us to end up here, since we
00081   // set ALLOW_MEMMOVE to true.
00082   NS_NOTREACHED("nsHostEntry copy constructor is forbidden!");
00083 }
00084 
00086 
00087 class nsPermissionEnumerator : public nsISimpleEnumerator
00088 {
00089   public:
00090     NS_DECL_ISUPPORTS
00091  
00092     nsPermissionEnumerator(const nsTHashtable<nsHostEntry> *aHostTable,
00093                            const char*   *aHostList,
00094                            const PRUint32 aHostCount,
00095                            const char*   *aTypeArray)
00096       : mHostCount(aHostCount),
00097         mHostIndex(0),
00098         mTypeIndex(0),
00099         mHostTable(aHostTable),
00100         mHostList(aHostList),
00101         mTypeArray(aTypeArray)
00102     {
00103       Prefetch();
00104     }
00105     
00106     NS_IMETHOD HasMoreElements(PRBool *aResult) 
00107     {
00108       *aResult = (mNextPermission != nsnull);
00109       return NS_OK;
00110     }
00111 
00112     NS_IMETHOD GetNext(nsISupports **aResult) 
00113     {
00114       *aResult = mNextPermission;
00115       if (!mNextPermission)
00116         return NS_ERROR_FAILURE;
00117 
00118       NS_ADDREF(*aResult);
00119       
00120       Prefetch();
00121 
00122       return NS_OK;
00123     }
00124 
00125     virtual ~nsPermissionEnumerator() 
00126     {
00127       delete[] mHostList;
00128     }
00129 
00130   protected:
00131     void Prefetch();
00132 
00133     PRInt32 mHostCount;
00134     PRInt32 mHostIndex;
00135     PRInt32 mTypeIndex;
00136     
00137     const nsTHashtable<nsHostEntry> *mHostTable;
00138     const char*                     *mHostList;
00139     nsCOMPtr<nsIPermission>          mNextPermission;
00140     const char*                     *mTypeArray;
00141 };
00142 
00143 NS_IMPL_ISUPPORTS1(nsPermissionEnumerator, nsISimpleEnumerator)
00144 
00145 // Sets mNextPermission to a new nsIPermission on success,
00146 // to nsnull when no new permissions are present.
00147 void
00148 nsPermissionEnumerator::Prefetch() 
00149 {
00150   // init to null, so we know when we've prefetched something
00151   mNextPermission = nsnull;
00152 
00153   // check we have something more to get
00154   PRUint32 permission;
00155   while (mHostIndex < mHostCount && !mNextPermission) {
00156     // loop over the types to find it
00157     nsHostEntry *entry = mHostTable->GetEntry(mHostList[mHostIndex]);
00158     if (entry) {
00159       // see if we've found it
00160       permission = entry->GetPermission(mTypeIndex);
00161       if (permission != nsIPermissionManager::UNKNOWN_ACTION && mTypeArray[mTypeIndex]) {
00162         mNextPermission = new nsPermission(entry->GetHost(), 
00163                                            nsDependentCString(mTypeArray[mTypeIndex]),
00164                                            permission);
00165       }
00166     }
00167 
00168     // increment mTypeIndex/mHostIndex as required
00169     ++mTypeIndex;
00170     if (mTypeIndex == NUMBER_OF_TYPES) {
00171       mTypeIndex = 0;
00172       ++mHostIndex;
00173     }
00174   }
00175 }
00176 
00178 // nsPermissionManager Implementation
00179 
00180 static const char kPermissionsFileName[] = "hostperm.1";
00181 static const char kOldPermissionsFileName[] = "cookperm.txt";
00182 static const char kPermissionChangeNotification[] = PERM_CHANGE_NOTIFICATION;
00183 
00184 static const PRUint32 kLazyWriteTimeout = 2000; //msec
00185 
00186 NS_IMPL_ISUPPORTS3(nsPermissionManager, nsIPermissionManager, nsIObserver, nsISupportsWeakReference)
00187 
00188 nsPermissionManager::nsPermissionManager()
00189  : mHostCount(0),
00190    mChangedList(PR_FALSE)
00191 {
00192 }
00193 
00194 nsPermissionManager::~nsPermissionManager()
00195 {
00196   if (mWriteTimer)
00197     mWriteTimer->Cancel();
00198 
00199   RemoveTypeStrings();
00200   RemoveAllFromMemory();
00201 }
00202 
00203 nsresult nsPermissionManager::Init()
00204 {
00205   nsresult rv;
00206 
00207   if (!mHostTable.Init()) {
00208     return NS_ERROR_OUT_OF_MEMORY;
00209   }
00210 
00211   // Cache the permissions file
00212   rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mPermissionsFile));
00213   if (NS_SUCCEEDED(rv)) {
00214     rv = mPermissionsFile->AppendNative(NS_LITERAL_CSTRING(kPermissionsFileName));
00215   }
00216 
00217   // Clear the array of type strings
00218   memset(mTypeArray, nsnull, sizeof(mTypeArray));
00219 
00220   // Ignore an error. That is not a problem. No cookperm.txt usually.
00221   Read();
00222 
00223   mObserverService = do_GetService("@mozilla.org/observer-service;1", &rv);
00224   if (NS_SUCCEEDED(rv)) {
00225     mObserverService->AddObserver(this, "profile-before-change", PR_TRUE);
00226     mObserverService->AddObserver(this, "profile-do-change", PR_TRUE);
00227   }
00228 
00229   return NS_OK;
00230 }
00231 
00232 NS_IMETHODIMP
00233 nsPermissionManager::Add(nsIURI     *aURI,
00234                          const char *aType,
00235                          PRUint32    aPermission)
00236 {
00237   NS_ENSURE_ARG_POINTER(aURI);
00238   NS_ENSURE_ARG_POINTER(aType);
00239 
00240   nsresult rv;
00241 
00242   nsCAutoString host;
00243   rv = GetHost(aURI, host);
00244   // no host doesn't mean an error. just return the default
00245   if (NS_FAILED(rv)) return NS_OK;
00246 
00247   PRInt32 typeIndex = GetTypeIndex(aType, PR_TRUE);
00248   if (typeIndex == -1 || aPermission >= NUMBER_OF_PERMISSIONS)
00249     return NS_ERROR_FAILURE;
00250 
00251   rv = AddInternal(host, typeIndex, aPermission, PR_TRUE);
00252   if (NS_FAILED(rv)) return rv;
00253 
00254   mChangedList = PR_TRUE;
00255   LazyWrite();
00256 
00257   return NS_OK;
00258 }
00259 
00260 // This only adds a permission to memory (it doesn't save to disk), and doesn't
00261 // bounds check aTypeIndex or aPermission. These are up to the caller.
00262 nsresult
00263 nsPermissionManager::AddInternal(const nsAFlatCString &aHost,
00264                                  PRInt32               aTypeIndex,
00265                                  PRUint32              aPermission,
00266                                  PRBool                aNotify)
00267 {
00268   if (!gHostArena) {
00269     gHostArena = new PLArenaPool;
00270     if (!gHostArena)
00271       return NS_ERROR_OUT_OF_MEMORY;    
00272     PL_INIT_ARENA_POOL(gHostArena, "PermissionHostArena", HOST_ARENA_SIZE);
00273   }
00274 
00275   // When an entry already exists, PutEntry will return that, instead
00276   // of adding a new one
00277   nsHostEntry *entry = mHostTable.PutEntry(aHost.get());
00278   if (!entry) return NS_ERROR_FAILURE;
00279   if (!entry->GetKey()) {
00280     mHostTable.RawRemoveEntry(entry);
00281     return NS_ERROR_OUT_OF_MEMORY;
00282   }
00283 
00284   if (entry->PermissionsAreEmpty()) {
00285     ++mHostCount;
00286   }
00287 
00288   PRUint32 oldPermission = entry->GetPermission(aTypeIndex);
00289   entry->SetPermission(aTypeIndex, aPermission);
00290 
00291   // If no more types are present, remove the entry
00292   // Can happen if this add() is resetting the permission to default.
00293   if (entry->PermissionsAreEmpty()) {
00294     mHostTable.RawRemoveEntry(entry);
00295     --mHostCount;
00296   }
00297 
00298   // check whether we are deleting, adding, or changing a permission,
00299   // so we can notify observers. this would be neater to do in Add(),
00300   // but we need to do it here because we only know what type of notification
00301   // to send (removal, addition, or change) after we've done the hash
00302   // lookup.
00303   if (aNotify) {
00304     if (aPermission == nsIPermissionManager::UNKNOWN_ACTION) {
00305       if (oldPermission != nsIPermissionManager::UNKNOWN_ACTION)
00306         // deleting
00307         NotifyObserversWithPermission(aHost,
00308                                       mTypeArray[aTypeIndex],
00309                                       oldPermission,
00310                                       NS_LITERAL_STRING("deleted").get());
00311     } else {
00312       if (oldPermission == nsIPermissionManager::UNKNOWN_ACTION)
00313         // adding
00314         NotifyObserversWithPermission(aHost,
00315                                       mTypeArray[aTypeIndex],
00316                                       aPermission,
00317                                       NS_LITERAL_STRING("added").get());
00318       else
00319         // changing
00320         NotifyObserversWithPermission(aHost,
00321                                       mTypeArray[aTypeIndex],
00322                                       aPermission,
00323                                       NS_LITERAL_STRING("changed").get());
00324     }
00325   }
00326 
00327   return NS_OK;
00328 }
00329 
00330 NS_IMETHODIMP
00331 nsPermissionManager::Remove(const nsACString &aHost,
00332                             const char       *aType)
00333 {
00334   NS_ENSURE_ARG_POINTER(aType);
00335   PRInt32 typeIndex = GetTypeIndex(aType, PR_FALSE);
00336   // If type == -1, the type isn't known,
00337   // so just return NS_OK
00338   if (typeIndex == -1) return NS_OK;
00339 
00340   nsHostEntry *entry = GetHostEntry(PromiseFlatCString(aHost), typeIndex);
00341   if (entry) {
00342     // cache the old permission before we delete it, to notify observers
00343     PRUint32 oldPermission = entry->GetPermission(typeIndex);
00344 
00345     entry->SetPermission(typeIndex, nsIPermissionManager::UNKNOWN_ACTION);
00346 
00347     // If no more types are present, remove the entry
00348     if (entry->PermissionsAreEmpty()) {
00349       mHostTable.RawRemoveEntry(entry);
00350       --mHostCount;
00351     }
00352     mChangedList = PR_TRUE;
00353     LazyWrite();
00354 
00355     // Notify Observers
00356     if (oldPermission != nsIPermissionManager::UNKNOWN_ACTION)
00357       NotifyObserversWithPermission(PromiseFlatCString(aHost),
00358                                     aType,
00359                                     oldPermission,
00360                                     NS_LITERAL_STRING("deleted").get());
00361   }
00362   return NS_OK;
00363 }
00364 
00365 NS_IMETHODIMP
00366 nsPermissionManager::RemoveAll()
00367 {
00368   RemoveAllFromMemory();
00369   NotifyObservers(nsnull, NS_LITERAL_STRING("cleared").get());
00370   LazyWrite();
00371   return NS_OK;
00372 }
00373 
00374 NS_IMETHODIMP
00375 nsPermissionManager::TestPermission(nsIURI     *aURI,
00376                                     const char *aType,
00377                                     PRUint32   *aPermission)
00378 {
00379   NS_ENSURE_ARG_POINTER(aURI);
00380   NS_ENSURE_ARG_POINTER(aType);
00381 
00382   // set the default
00383   *aPermission = nsIPermissionManager::UNKNOWN_ACTION;
00384 
00385   nsCAutoString host;
00386   nsresult rv = GetHost(aURI, host);
00387   // no host doesn't mean an error. just return the default
00388   if (NS_FAILED(rv)) return NS_OK;
00389   
00390   PRInt32 typeIndex = GetTypeIndex(aType, PR_FALSE);
00391   // If type == -1, the type isn't known,
00392   // so just return NS_OK
00393   if (typeIndex == -1) return NS_OK;
00394 
00395   nsHostEntry *entry = GetHostEntry(host, typeIndex);
00396   if (entry)
00397     *aPermission = entry->GetPermission(typeIndex);
00398 
00399   return NS_OK;
00400 }
00401 
00402 // Get hostentry for given host string and permission type.
00403 // walk up the domain if needed.
00404 // return null if nothing found.
00405 nsHostEntry *
00406 nsPermissionManager::GetHostEntry(const nsAFlatCString &aHost,
00407                                   PRUint32              aType)
00408 {
00409   PRUint32 offset = 0;
00410   nsHostEntry *entry;
00411   do {
00412     entry = mHostTable.GetEntry(aHost.get() + offset);
00413     if (entry) {
00414       if (entry->GetPermission(aType) != nsIPermissionManager::UNKNOWN_ACTION)
00415         break;
00416 
00417       // reset entry, to be able to return null on failure
00418       entry = nsnull;
00419     }
00420     offset = aHost.FindChar('.', offset) + 1;
00421 
00422   // walk up the domaintree (we stop as soon as we find a match,
00423   // which will be the most specific domain we have an entry for).
00424   } while (offset > 0);
00425   return entry;
00426 }
00427 
00428 // A little helper function to add the hostname to the list.
00429 // The hostname comes from an arena, and so it won't go away
00430 PR_STATIC_CALLBACK(PLDHashOperator)
00431 AddHostToList(nsHostEntry *entry, void *arg)
00432 {
00433   // arg is a double-ptr to an string entry in the hostList array,
00434   // so we dereference it twice to assign the |const char*| string
00435   // and once so we can increment the |const char**| array location
00436   // ready for the next assignment.
00437   const char*** elementPtr = NS_STATIC_CAST(const char***, arg);
00438   **elementPtr = entry->GetKey();
00439   ++(*elementPtr);
00440   return PL_DHASH_NEXT;
00441 }
00442 
00443 NS_IMETHODIMP nsPermissionManager::GetEnumerator(nsISimpleEnumerator **aEnum)
00444 {
00445   *aEnum = nsnull;
00446   // get the host list, to hand to the enumerator.
00447   // the enumerator takes ownership of the list.
00448 
00449   // create a new host list. enumerator takes ownership of it.
00450   const char* *hostList = new const char*[mHostCount];
00451   if (!hostList) return NS_ERROR_OUT_OF_MEMORY;
00452 
00453   // Make a copy of the pointer, so we can increase it without loosing
00454   // the original pointer
00455   const char** hostListCopy = hostList;
00456   mHostTable.EnumerateEntries(AddHostToList, &hostListCopy);
00457 
00458   nsPermissionEnumerator* permissionEnum = new nsPermissionEnumerator(&mHostTable, hostList, mHostCount, NS_CONST_CAST(const char**, mTypeArray));
00459   if (!permissionEnum) {
00460     delete[] hostList;
00461     return NS_ERROR_OUT_OF_MEMORY;
00462   }
00463   NS_ADDREF(permissionEnum);
00464   *aEnum = permissionEnum;
00465   return NS_OK;
00466 }
00467 
00468 NS_IMETHODIMP nsPermissionManager::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *someData)
00469 {
00470   nsresult rv = NS_OK;
00471 
00472   if (!nsCRT::strcmp(aTopic, "profile-before-change")) {
00473     // The profile is about to change.
00474 
00475     if (mWriteTimer) {
00476       mWriteTimer->Cancel();
00477       mWriteTimer = 0;
00478     }
00479     
00480     // Dump current permission.  This will be done by calling 
00481     // RemoveAllFromMemory which clears the memory-resident
00482     // permission table.  The reason the permission file does not
00483     // need to be updated is because the file was updated every time
00484     // the memory-resident table changed (i.e., whenever a new permission
00485     // was accepted).  If this condition ever changes, the permission
00486     // file would need to be updated here.
00487 
00488     if (!nsCRT::strcmp(someData, NS_LITERAL_STRING("shutdown-cleanse").get())) {
00489       if (mPermissionsFile) {
00490         mPermissionsFile->Remove(PR_FALSE);
00491       }
00492     } else {
00493       Write();
00494     }
00495     RemoveTypeStrings();
00496     RemoveAllFromMemory();
00497   }  
00498   else if (!nsCRT::strcmp(aTopic, "profile-do-change")) {
00499     // The profile has aleady changed.    
00500     // Now just read them from the new profile location.
00501 
00502     // Re-get the permissions file
00503     rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mPermissionsFile));
00504     if (NS_SUCCEEDED(rv)) {
00505       rv = mPermissionsFile->AppendNative(NS_LITERAL_CSTRING(kPermissionsFileName));
00506     }
00507     Read();
00508   }
00509 
00510   return rv;
00511 }
00512 
00513 //*****************************************************************************
00514 //*** nsPermissionManager private methods
00515 //*****************************************************************************
00516 
00517 nsresult
00518 nsPermissionManager::RemoveAllFromMemory()
00519 {
00520   mHostTable.Clear();
00521   mHostCount = 0;
00522   if (gHostArena) {
00523     PL_FinishArenaPool(gHostArena);
00524     delete gHostArena;
00525   }
00526   gHostArena = nsnull;
00527   mChangedList = PR_TRUE;
00528   return NS_OK;
00529 }
00530 
00531 void
00532 nsPermissionManager::RemoveTypeStrings()
00533 {
00534   for (PRUint32 i = NUMBER_OF_TYPES; i--; ) {
00535     if (mTypeArray[i]) {
00536       PL_strfree(mTypeArray[i]);
00537       mTypeArray[i] = nsnull;
00538     }
00539   }
00540 }
00541 
00542 // Returns -1 on failure
00543 PRInt32
00544 nsPermissionManager::GetTypeIndex(const char *aType,
00545                                   PRBool      aAdd)
00546 {
00547   PRInt32 firstEmpty = -1;
00548 
00549   for (PRUint32 i = 0; i < NUMBER_OF_TYPES; ++i) {
00550     if (!mTypeArray[i]) {
00551       if (firstEmpty == -1)
00552         // Don't break, the type might be later in the array
00553         firstEmpty = i;
00554     } else if (!strcmp(aType, mTypeArray[i])) {
00555       return i;
00556     }
00557   }
00558 
00559   if (!aAdd || firstEmpty == -1)
00560     // Not found, but that is ok - we were just looking.
00561     // Or, no free spots left - error.
00562     return -1;
00563 
00564   // This type was not registered before.
00565   // Can't use ToNewCString here, other strings in the array are allocated
00566   // with PL_strdup too, and they all need to be freed the same way
00567   mTypeArray[firstEmpty] = PL_strdup(aType);
00568   if (!mTypeArray[firstEmpty])
00569     return -1;
00570 
00571   return firstEmpty;
00572 }
00573 
00574 // wrapper function for mangling (host,type,perm) triplet into an nsIPermission.
00575 void
00576 nsPermissionManager::NotifyObserversWithPermission(const nsACString &aHost,
00577                                                    const char       *aType,
00578                                                    PRUint32          aPermission,
00579                                                    const PRUnichar  *aData)
00580 {
00581   nsCOMPtr<nsIPermission> permission =
00582     new nsPermission(aHost, nsDependentCString(aType), aPermission);
00583   if (permission)
00584     NotifyObservers(permission, aData);
00585 }
00586 
00587 // notify observers that the permission list changed. there are four possible
00588 // values for aData:
00589 // "deleted" means a permission was deleted. aPermission is the deleted permission.
00590 // "added"   means a permission was added. aPermission is the added permission.
00591 // "changed" means a permission was altered. aPermission is the new permission.
00592 // "cleared" means the entire permission list was cleared. aPermission is null.
00593 void
00594 nsPermissionManager::NotifyObservers(nsIPermission   *aPermission,
00595                                      const PRUnichar *aData)
00596 {
00597   if (mObserverService)
00598     mObserverService->NotifyObservers(aPermission,
00599                                       kPermissionChangeNotification,
00600                                       aData);
00601 }
00602 
00603 static const char kTab = '\t';
00604 static const char kNew = '\n';
00605 static const char kTrue = 'T';
00606 static const char kFalse = 'F';
00607 static const char kFirstLetter = 'a';
00608 static const char kTypeSign = '%';
00609 
00610 static const char kMatchTypeHost[] = "host";
00611 
00612 nsresult
00613 nsPermissionManager::Read()
00614 {
00615   nsresult rv;
00616   
00617   PRBool readingOldFile = PR_FALSE;
00618 
00619   nsCOMPtr<nsIInputStream> fileInputStream;
00620   rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream), mPermissionsFile);
00621   if (rv == NS_ERROR_FILE_NOT_FOUND) {
00622     // Try finding the old-style file
00623 
00624     nsCOMPtr<nsIFile> oldPermissionsFile;
00625     rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(oldPermissionsFile));
00626     NS_ENSURE_SUCCESS(rv, rv);
00627 
00628     rv = oldPermissionsFile->AppendNative(NS_LITERAL_CSTRING(kOldPermissionsFileName));
00629     NS_ENSURE_SUCCESS(rv, rv);
00630 
00631     rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream), oldPermissionsFile);
00632     NS_ENSURE_SUCCESS(rv, rv);
00633 
00634     readingOldFile = PR_TRUE;
00635 
00636     /* old format is:
00637      * host \t number permission \t number permission ... \n
00638      * if this format isn't respected we move onto the next line in the file.
00639      * permission is T or F for accept or deny, otherwise a lowercase letter,
00640      * with a=0, b=1 etc
00641      */
00642   }
00643 
00644   nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(fileInputStream, &rv);
00645   NS_ENSURE_SUCCESS(rv, rv);
00646 
00647   /* format is:
00648    * matchtype \t type \t permission \t host
00649    * Only "host" is supported for matchtype
00650    * type is a string that identifies the type of permission (e.g. "cookie")
00651    * permission is an integer between 1 and 15
00652    */
00653 
00654   mHasUnknownTypes = PR_FALSE;
00655 
00656   nsCAutoString buffer;
00657   PRBool isMore = PR_TRUE;
00658   while (isMore && NS_SUCCEEDED(lineInputStream->ReadLine(buffer, &isMore))) {
00659     if (buffer.IsEmpty() || buffer.First() == '#') {
00660       continue;
00661     }
00662 
00663     if (!readingOldFile) {
00664       nsCStringArray lineArray;
00665       
00666       // Split the line at tabs
00667       lineArray.ParseString(buffer.get(), "\t");
00668       
00669       if (lineArray[0]->EqualsLiteral(kMatchTypeHost) &&
00670           lineArray.Count() == 4) {
00671         
00672         PRInt32 error;
00673         PRUint32 permission = lineArray[2]->ToInteger(&error);
00674         if (error)
00675           continue;
00676         PRInt32 type = GetTypeIndex(lineArray[1]->get(), PR_TRUE);
00677         if (type < 0)
00678           continue;
00679 
00680         rv = AddInternal(*lineArray[3], type, permission, PR_FALSE);
00681         NS_ENSURE_SUCCESS(rv, rv);
00682       } else {
00683         mHasUnknownTypes = PR_TRUE;
00684       }
00685 
00686     } else {
00687       // Begin backwards compatibility code
00688       nsASingleFragmentCString::char_iterator iter;
00689       if (buffer.First() == kTypeSign) {
00690         // A permission string line
00691 
00692         PRInt32 stringIndex;
00693 
00694         if ((stringIndex = buffer.FindChar(kTab) + 1) == 0) {
00695           continue;      
00696         }
00697 
00698         PRUint32 type;
00699         if (PR_sscanf(buffer.get() + 1, "%u", &type) != 1 || type >= NUMBER_OF_TYPES) {
00700           continue;
00701         }
00702 
00703         // Older versions of mozilla can't parse the permission string lines.
00704         // They will put them back like '%0 0F' instead of '%0 cookie'
00705         // Ignore those lines, and revert to the defaults later.
00706         // XXX This means that when the user has additional types besides the
00707         // default, those will be lost after using an old version with the
00708         // new profile, and then going back to a new version.
00709         if (!strcmp(buffer.get() + stringIndex, "0F"))
00710           continue;
00711 
00712         NS_ASSERTION(GetTypeIndex(buffer.get() + stringIndex, PR_FALSE) == -1, "Corrupt cookperm.txt file");
00713         mTypeArray[type] = PL_strdup(buffer.get() + stringIndex);
00714 
00715         continue;
00716       }
00717 
00718       PRInt32 hostIndex, permissionIndex;
00719       PRUint32 nextPermissionIndex = 0;
00720       hostIndex = 0;
00721 
00722       if ((permissionIndex = buffer.FindChar('\t', hostIndex) + 1) == 0)
00723         continue;      
00724 
00725       // ignore leading periods in host name
00726       while (hostIndex < permissionIndex && (buffer.CharAt(hostIndex) == '.'))
00727         ++hostIndex;
00728 
00729       // nullstomp the trailing tab, to get a flat string
00730       buffer.BeginWriting(iter);
00731       *(iter += permissionIndex - 1) = char(0);
00732       nsDependentCString host(buffer.get() + hostIndex, iter);
00733 
00734       for (;;) {
00735         if (nextPermissionIndex == buffer.Length()+1)
00736           break;
00737 
00738         if ((nextPermissionIndex = buffer.FindChar(kTab, permissionIndex) + 1) == 0)
00739           nextPermissionIndex = buffer.Length()+1;
00740 
00741         const nsASingleFragmentCString &permissionString = Substring(buffer, permissionIndex, nextPermissionIndex - permissionIndex - 1);
00742         permissionIndex = nextPermissionIndex;
00743 
00744         PRInt32 type = 0;
00745         PRUint32 index = 0;
00746 
00747         if (permissionString.IsEmpty())
00748           continue; // empty permission entry -- should never happen
00749 
00750         // Parse "2T"
00751         char c = permissionString.CharAt(index);
00752         while (index < permissionString.Length() && c >= '0' && c <= '9') {
00753           type = 10*type + (c-'0');
00754           c = permissionString.CharAt(++index);
00755         }
00756         if (index >= permissionString.Length())
00757           continue; // bad format for this permission entry
00758 
00759         PRUint32 permission;
00760         if (permissionString.CharAt(index) == kTrue)
00761           permission = nsIPermissionManager::ALLOW_ACTION;
00762         else if (permissionString.CharAt(index) == kFalse)
00763           permission = nsIPermissionManager::DENY_ACTION;
00764         else
00765           permission = permissionString.CharAt(index) - kFirstLetter;
00766 
00767         // |permission| is unsigned, so no need to check for < 0
00768         if (permission >= NUMBER_OF_PERMISSIONS)
00769           continue;
00770 
00771         // Ignore @@@ as host. Old style checkbox status
00772         if (!permissionString.IsEmpty() && !host.EqualsLiteral("@@@@")) {
00773           rv = AddInternal(host, type, permission, PR_FALSE);
00774           if (NS_FAILED(rv)) return rv;
00775         }
00776 
00777       }
00778       // Old files, without the part that defines the types, assume 0 is cookie
00779       // etc. So add those types, but make sure not to overwrite types from new
00780       // style files.
00781       GetTypeIndex("cookie", PR_TRUE);
00782       GetTypeIndex("image", PR_TRUE);
00783       GetTypeIndex("popup", PR_TRUE);
00784 
00785       // End backwards compatibility code
00786     }
00787 
00788   }
00789 
00790 
00791   mChangedList = PR_FALSE;
00792 
00793   return NS_OK;
00794 }
00795 
00796 // A helper function that adds the pointer to the entry to the list.
00797 // This is not threadsafe, and only safe if the consumer does not 
00798 // modify the list. It is only used in Write() where we are sure
00799 // that nothing else changes the list while we are saving.
00800 PR_STATIC_CALLBACK(PLDHashOperator)
00801 AddEntryToList(nsHostEntry *entry, void *arg)
00802 {
00803   // arg is a double-ptr to an entry in the hostList array,
00804   // so we dereference it twice to assign the |nsHostEntry*|
00805   // and once so we can increment the |nsHostEntry**| array location
00806   // ready for the next assignment.
00807   nsHostEntry*** elementPtr = NS_STATIC_CAST(nsHostEntry***, arg);
00808   **elementPtr = entry;
00809   ++(*elementPtr);
00810   return PL_DHASH_NEXT;
00811 }
00812 
00813 void
00814 nsPermissionManager::LazyWrite()
00815 {
00816   if (mWriteTimer) {
00817     mWriteTimer->SetDelay(kLazyWriteTimeout);
00818   } else {
00819     mWriteTimer = do_CreateInstance("@mozilla.org/timer;1");
00820     if (mWriteTimer) {
00821       mWriteTimer->InitWithFuncCallback(DoLazyWrite, this, kLazyWriteTimeout,
00822                                         nsITimer::TYPE_ONE_SHOT);
00823     }
00824   }
00825 }
00826 
00827 void
00828 nsPermissionManager::DoLazyWrite(nsITimer *aTimer,
00829                                  void     *aClosure)
00830 {
00831   nsPermissionManager *service = NS_REINTERPRET_CAST(nsPermissionManager*, aClosure);
00832   service->Write();
00833   service->mWriteTimer = 0;
00834 }
00835 
00836 nsresult
00837 nsPermissionManager::Write()
00838 {
00839   nsresult rv;
00840 
00841   if (!mChangedList) {
00842     return NS_OK;
00843   }
00844 
00845   if (!mPermissionsFile) {
00846     return NS_ERROR_FAILURE;
00847   }
00848 
00849   // Start with reading the old file, and remember any data that
00850   // wasn't parsed, to put it right back into the new file.
00851   // But only do that if we know there is unknown stuff
00852   nsCStringArray rememberList;
00853   if (mHasUnknownTypes) {
00854     nsCOMPtr<nsIInputStream> fileInputStream;
00855     rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream), mPermissionsFile);
00856     if (NS_SUCCEEDED(rv)) {
00857       nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(fileInputStream, &rv);
00858       if (NS_SUCCEEDED(rv)) {
00859         nsCAutoString buffer;
00860         PRBool isMore = PR_TRUE;
00861         while (isMore && NS_SUCCEEDED(lineInputStream->ReadLine(buffer, &isMore))) {
00862           if (buffer.IsEmpty() || buffer.First() == '#' ||
00863               StringBeginsWith(buffer, NS_LITERAL_CSTRING(kMatchTypeHost)))
00864             continue;
00865 
00866           rememberList.AppendCString(buffer);
00867         }
00868       }
00869     }
00870   }
00871 
00872   nsCOMPtr<nsIOutputStream> fileOutputStream;
00873   rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(fileOutputStream),
00874                                        mPermissionsFile,
00875                                        -1,
00876                                        0600);
00877   NS_ENSURE_SUCCESS(rv, rv);
00878 
00879   // get a buffered output stream 4096 bytes big, to optimize writes
00880   nsCOMPtr<nsIOutputStream> bufferedOutputStream;
00881   rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream), fileOutputStream, 4096);
00882   NS_ENSURE_SUCCESS(rv, rv);
00883 
00884   static const char kHeader[] = 
00885     "# Permission File\n"
00886     "# This is a generated file! Do not edit.\n\n";
00887 
00888   bufferedOutputStream->Write(kHeader, sizeof(kHeader) - 1, &rv);
00889 
00890   /* format is:
00891    * matchtype \t type \t permission \t host \t
00892    */
00893 
00894   // Write out the list of strings we remembered from the old file
00895   PRUint32 i;
00896   if (mHasUnknownTypes) {
00897     for (i = 0 ; i < rememberList.Count() ; ++i) {
00898       bufferedOutputStream->Write(rememberList[i]->get(), 
00899                                   rememberList[i]->Length(), &rv);
00900       bufferedOutputStream->Write(&kNew, 1, &rv);
00901     }
00902   }
00903 
00904   // create a new host list
00905   nsHostEntry* *hostList = new nsHostEntry*[mHostCount];
00906   if (!hostList) return NS_ERROR_OUT_OF_MEMORY;
00907 
00908   // Make a copy of the pointer, so we can increase it without losing
00909   // the original pointer
00910   nsHostEntry** hostListCopy = hostList;
00911   mHostTable.EnumerateEntries(AddEntryToList, &hostListCopy);
00912 
00913   for (i = 0; i < mHostCount; ++i) {
00914     nsHostEntry *entry = NS_STATIC_CAST(nsHostEntry*, hostList[i]);
00915     NS_ASSERTION(entry, "corrupt permission list");
00916 
00917     for (PRInt32 type = 0; type < NUMBER_OF_TYPES; ++type) {
00918     
00919       PRUint32 permission = entry->GetPermission(type);
00920       if (permission && mTypeArray[type]) {
00921         // Write matchtype
00922         // only "host" is possible at the moment.
00923         bufferedOutputStream->Write(kMatchTypeHost, sizeof(kMatchTypeHost) - 1, &rv);
00924 
00925         // Write type
00926         bufferedOutputStream->Write(&kTab, 1, &rv);
00927         bufferedOutputStream->Write(mTypeArray[type], strlen(mTypeArray[type]), &rv);
00928 
00929         // Write permission
00930         bufferedOutputStream->Write(&kTab, 1, &rv);
00931         char permissionString[5];
00932         PRUint32 len = PR_snprintf(permissionString, sizeof(permissionString) - 1, "%u", permission);
00933         bufferedOutputStream->Write(permissionString, len, &rv);
00934 
00935         // Write host
00936         bufferedOutputStream->Write(&kTab, 1, &rv);
00937         bufferedOutputStream->Write(entry->GetHost().get(), entry->GetHost().Length(), &rv);
00938         
00939         // Write newline
00940         bufferedOutputStream->Write(&kNew, 1, &rv);
00941       }
00942     }
00943   }
00944 
00945   delete[] hostList;
00946 
00947   // All went ok. Maybe except for problems in Write(), but the stream detects
00948   // that for us
00949   nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(bufferedOutputStream);
00950   NS_ASSERTION(safeStream, "expected a safe output stream!");
00951   if (safeStream) {
00952     rv = safeStream->Finish();
00953     if (NS_FAILED(rv)) {
00954       NS_WARNING("failed to save permissions file! possible dataloss");
00955       return rv;
00956     }
00957   }
00958 
00959   mChangedList = PR_FALSE;
00960   return NS_OK;
00961 }
00962 
00963 nsresult
00964 nsPermissionManager::GetHost(nsIURI *aURI, nsACString &aResult)
00965 {
00966   aURI->GetHost(aResult);
00967 
00968   // If there is no host, use the scheme, and prepend "scheme:",
00969   // to make sure it isn't a host or something.
00970   if (aResult.IsEmpty()) {
00971     aURI->GetScheme(aResult);
00972     if (aResult.IsEmpty()) {
00973       // still empty. Return error.
00974       return NS_ERROR_FAILURE;
00975     }
00976     aResult = NS_LITERAL_CSTRING("scheme:") + aResult;
00977   }
00978 
00979   return NS_OK;
00980 }