Back to index

lightning-sunbird  0.9+nobinonly
nsSharedPrefHandler.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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) 2003
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Conrad Carlen <ccarlen@netscape.com>
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 "nsSharedPrefHandler.h"
00040 #include "nsPrefService.h"
00041 
00042 #include "nsServiceManagerUtils.h"
00043 #include "nsILocalFile.h"
00044 #include "nsAppDirectoryServiceDefs.h"
00045 #include "nsReadableUtils.h"
00046 #include "ipcMessageReader.h"
00047 #include "ipcMessageWriter.h"
00048 
00049 
00050 #if defined(PR_LOGGING)
00051 // set NSPR_LOG_MODULES=nsSharedPrefHandler:5
00052 PRLogModuleInfo *gPrefsTransactionObserverLog = nsnull;
00053 #define LOG(args) PR_LOG(PrefsTransactionObserver, PR_LOG_DEBUG, args)
00054 #else
00055 #define LOG(args)
00056 #endif
00057 
00058 nsSharedPrefHandler *gSharedPrefHandler = nsnull;
00059 
00060 // Constants
00061 #define kPrefsTSQueueName NS_LITERAL_CSTRING("prefs")
00062 
00063 #define kExceptionListFileName NS_LITERAL_CSTRING("nonshared.txt")
00064 const char kExceptionListCommentChar = '#';
00065 
00066 const PRUint32 kCurrentPrefsTransactionDataVersion = 1;
00067 
00068 // Static function prototypes
00069 static PRBool PR_CALLBACK enumFind(void* aElement, void *aData);
00070 static PRBool PR_CALLBACK enumFree(void* aElement, void *aData);
00071 static PRInt32 ReadLine(FILE* inStm, nsACString& destString);
00072 
00073 
00074 //****************************************************************************
00075 // Transaction Format for kCurrentPrefsTransactionDataVersion
00076 //****************************************************************************
00077 
00078 /*
00079   NOTE: All string data is null terminated.
00080   The data length includes the null character.
00081   
00082   PRUint32 dataVersion
00083   PRUint32 action SET or CLEAR
00084   PRUint32 prefNameLen
00085   char prefName[prefNameLen]
00086   PRUint32 prefValueKind
00087   PRUint32 prefValueLen
00088   char prefValue[prefValueLen]
00089 */
00090 
00091 //*****************************************************************************
00092 // nsSharedPrefHandler
00093 //*****************************************************************************   
00094 
00095 nsSharedPrefHandler::nsSharedPrefHandler() :
00096   mPrefService(nsnull), mPrefsTSQueueName("prefs"),
00097   mSessionActive(PR_FALSE),
00098   mReadingUserPrefs(PR_FALSE),
00099   mProcessingTransaction(PR_FALSE)
00100 {
00101 #if defined(PR_LOGGING)
00102   if (!gPrefsTransactionObserverLog)
00103     gPrefsTransactionObserverLog = PR_NewLogModule("nsSharedPrefHandler");
00104 #endif
00105 }
00106 
00107 nsSharedPrefHandler::~nsSharedPrefHandler()
00108 {
00109   mExceptionList.EnumerateForwards(enumFree, nsnull);
00110 }
00111         
00112 nsresult nsSharedPrefHandler::OnSessionBegin()
00113 {
00114   nsresult rv = EnsureTransactionService();
00115   NS_ENSURE_SUCCESS(rv, rv);
00116 
00117   // When the reply to this comes (synchronously), we'll actually
00118   // read in our data. The transaction service holds a lock on
00119   // our data file during our reply.
00120   rv = mTransService->Attach(kPrefsTSQueueName, this, PR_TRUE);
00121   NS_ASSERTION(NS_SUCCEEDED(rv), "ipcITransactionService::Attach() failed");
00122   
00123   if (NS_SUCCEEDED(rv))
00124     mSessionActive = PR_TRUE;
00125     
00126   return rv;
00127 }
00128 
00129 nsresult nsSharedPrefHandler::OnSessionEnd()
00130 {
00131   nsresult rv = EnsureTransactionService();
00132   NS_ENSURE_SUCCESS(rv, rv);
00133   
00134   rv = mTransService->Detach(kPrefsTSQueueName);
00135   NS_ASSERTION(NS_SUCCEEDED(rv), "ipcITransactionService::Detach() failed");
00136 
00137   mSessionActive = PR_FALSE;
00138 
00139   return rv;
00140 }
00141 
00142 nsresult nsSharedPrefHandler::OnSavePrefs()
00143 {
00144   nsresult rv = EnsureTransactionService();
00145   NS_ENSURE_SUCCESS(rv, rv);
00146   
00147   // When the reply to this comes (synchronously), we'll actually
00148   // write out our data. The transaction service holds a lock on
00149   // our data file during our reply.
00150   rv = mTransService->Flush(kPrefsTSQueueName, PR_TRUE);
00151   NS_ASSERTION(NS_SUCCEEDED(rv), "ipcITransactionService::Flush() failed");
00152 
00153   return NS_OK;
00154 }
00155     
00156 nsresult nsSharedPrefHandler::OnPrefChanged(PRBool defaultPref,
00157                                             PrefHashEntry* pref,
00158                                             PrefValue newValue)
00159 {
00160   if (!mSessionActive 
00161     || defaultPref
00162     || !IsPrefShared(pref->key)
00163     || mReadingUserPrefs
00164     || mProcessingTransaction)
00165     return NS_OK;
00166     
00167   nsresult rv = EnsureTransactionService();
00168   NS_ENSURE_SUCCESS(rv, rv);
00169 
00170   PRUint32 valueLen, prefNameLen = strlen(pref->key);
00171   
00172   ipcMessageWriter outMsg(256);
00173   outMsg.PutInt32(kCurrentPrefsTransactionDataVersion);
00174   outMsg.PutInt32(defaultPref); // XXX: Is not used?
00175   outMsg.PutInt32(prefNameLen + 1);
00176   outMsg.PutBytes(pref->key, prefNameLen + 1);
00177 
00178   switch (pref->flags & PREF_VALUETYPE_MASK) {
00179     case PREF_STRING:
00180       outMsg.PutInt32(PREF_STRING);
00181       valueLen = strlen(newValue.stringVal) + 1;
00182       outMsg.PutInt32(valueLen);
00183       outMsg.PutBytes(newValue.stringVal, valueLen);
00184       break;
00185     case PREF_INT:
00186       outMsg.PutInt32(PREF_INT);
00187       outMsg.PutInt32(sizeof(PRInt32));
00188       outMsg.PutInt32(newValue.intVal);
00189       break;
00190     case PREF_BOOL:
00191       outMsg.PutInt32(PREF_BOOL);
00192       outMsg.PutInt32(sizeof(PRInt32));
00193       outMsg.PutInt32(newValue.boolVal);
00194       break;
00195     default:
00196       return NS_ERROR_UNEXPECTED;
00197   }
00198   
00199   rv = outMsg.HasError() ? NS_ERROR_FAILURE : NS_OK;
00200   NS_ASSERTION(NS_SUCCEEDED(rv), "OnPrefChanged: outMsg failed");
00201   if (NS_SUCCEEDED(rv)) {
00202     rv = mTransService->PostTransaction(kPrefsTSQueueName, outMsg.GetBuffer(), outMsg.GetSize());
00203     NS_ASSERTION(NS_SUCCEEDED(rv), "ipcITransactionService::PostTransaction() failed");
00204   }
00205   return rv;
00206 }
00207 
00208 PRBool nsSharedPrefHandler::IsPrefShared(const char* prefName)
00209 {
00210   if (!mExceptionList.Count()) // quick check for empty list
00211     return PR_TRUE;
00212     
00213   // returns PR_TRUE if we reached the end without finding it.
00214   return mExceptionList.EnumerateForwards(enumFind, NS_CONST_CAST(char*, prefName));
00215 }
00216 
00217 //*****************************************************************************
00218 // nsSharedPrefHandler Protected Methods
00219 //*****************************************************************************   
00220 
00221 nsresult nsSharedPrefHandler::Init(nsPrefService* aOwner)
00222 {
00223   NS_ENSURE_ARG(aOwner);
00224   mPrefService = aOwner;
00225   (void)ReadExceptionFile(); 
00226   
00227   return NS_OK;
00228 }
00229 
00230 nsresult nsSharedPrefHandler::ReadExceptionFile()
00231 {
00232   nsresult rv;
00233   
00234   nsCOMPtr<nsIProperties> directoryService =
00235       do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
00236   if (NS_SUCCEEDED(rv)) {
00237     nsCOMPtr<nsILocalFile> exceptionFile;
00238     rv = directoryService->Get(NS_APP_PREF_DEFAULTS_50_DIR, NS_GET_IID(nsILocalFile),
00239                               getter_AddRefs(exceptionFile));
00240     if (NS_SUCCEEDED(rv)) {
00241       rv = exceptionFile->AppendNative(kExceptionListFileName);
00242       if (NS_SUCCEEDED(rv)) {
00243         FILE *inStm;
00244         rv = exceptionFile->OpenANSIFileDesc("r", &inStm);
00245         if (NS_SUCCEEDED(rv)) {
00246           nsCAutoString lineStr;
00247           while (ReadLine(inStm, lineStr) != EOF) {
00248             lineStr.CompressWhitespace();
00249             if (lineStr.IsEmpty() || lineStr.CharAt(0) == kExceptionListCommentChar)
00250               continue;
00251               
00252             char *rawStr = ToNewCString(lineStr);
00253             if (!rawStr) {
00254               rv = NS_ERROR_OUT_OF_MEMORY;
00255               break;
00256             }
00257             mExceptionList.AppendElement(rawStr);
00258           }
00259           fclose(inStm);
00260         }
00261       }
00262     }
00263   }
00264   return rv;
00265 }
00266 
00267 nsresult nsSharedPrefHandler::EnsureTransactionService()
00268 {
00269   if (mTransService)
00270     return NS_OK;
00271   nsresult rv;
00272   mTransService = do_GetService(IPC_TRANSACTIONSERVICE_CONTRACTID, &rv);
00273   return rv;
00274 }
00275 
00276 //*****************************************************************************
00277 // nsSharedPrefHandler::nsISupports
00278 //*****************************************************************************   
00279 
00280 NS_IMPL_ISUPPORTS1(nsSharedPrefHandler, ipcITransactionObserver)
00281 
00282 //*****************************************************************************
00283 // nsSharedPrefHandler::ipcITransactionObserver
00284 //*****************************************************************************   
00285 
00286 NS_IMETHODIMP nsSharedPrefHandler::OnTransactionAvailable(PRUint32 aQueueID, const PRUint8 *aData, PRUint32 aDataLen)
00287 {
00288     LOG(("nsSharedPrefHandler::OnTransactionAvailable [%s]\n", aData));
00289 
00290     ipcMessageReader inMsg(aData, aDataLen);
00291 
00292     PRUint32 dataVersion, prefAction, dataLen, prefKind, tempInt32;
00293     const char *stringStart;
00294     
00295     dataVersion = inMsg.GetInt32();
00296     NS_ENSURE_TRUE(dataVersion == kCurrentPrefsTransactionDataVersion, NS_ERROR_INVALID_ARG);
00297     prefAction = inMsg.GetInt32();  //XXX: Is not used?
00298     dataLen = inMsg.GetInt32(); // includes terminating null
00299     stringStart = (const char *)inMsg.GetPtr();
00300     nsDependentCString prefNameStr(stringStart);
00301     inMsg.AdvancePtr(dataLen);
00302     prefKind = inMsg.GetInt32();
00303     dataLen = inMsg.GetInt32();
00304     
00305     mProcessingTransaction = PR_TRUE; // Don't generate transactions for these
00306     switch (prefKind) {
00307       case PREF_STRING:
00308         {
00309         stringStart = (const char *)inMsg.GetPtr();
00310         nsDependentCString prefStrValueStr(stringStart);
00311         inMsg.AdvancePtr(dataLen);
00312         NS_ASSERTION(!inMsg.HasError(), "error in reading transaction");
00313         if (!inMsg.HasError())
00314           PREF_SetCharPref(prefNameStr.get(), prefStrValueStr.get());
00315         }
00316         break;
00317       case PREF_INT:
00318         tempInt32 = inMsg.GetInt32();
00319         NS_ASSERTION(!inMsg.HasError(), "error in reading transaction");
00320         if (!inMsg.HasError())
00321           PREF_SetIntPref(prefNameStr.get(), tempInt32);
00322         break;
00323       case PREF_BOOL:
00324         tempInt32 = inMsg.GetInt32();
00325         NS_ASSERTION(!inMsg.HasError(), "error in reading transaction");
00326         if (!inMsg.HasError())
00327           PREF_SetBoolPref(prefNameStr.get(), tempInt32);
00328         break;
00329     }
00330     mProcessingTransaction = PR_FALSE;
00331     
00332     return NS_OK;
00333 }
00334 
00335 NS_IMETHODIMP nsSharedPrefHandler::OnAttachReply(PRUint32 aQueueID, PRUint32 aStatus)
00336 {
00337     LOG(("nsSharedPrefHandler::OnAttachReply [%d]\n", aStatus));
00338 
00339     // The transaction service holds a lock on the file during this call.
00340     mPrefService->ResetUserPrefs();
00341     mPrefService->ReadUserPrefs(nsnull);
00342     
00343     return NS_OK;
00344 }
00345 
00346 NS_IMETHODIMP nsSharedPrefHandler::OnDetachReply(PRUint32 aQueueID, PRUint32 aStatus)
00347 {
00348     LOG(("tmModuleTest: nsSharedPrefHandler::OnDetachReply [%d]\n", aStatus));
00349     return NS_OK;
00350 }
00351 
00352 NS_IMETHODIMP nsSharedPrefHandler::OnFlushReply(PRUint32 aQueueID, PRUint32 aStatus)
00353 {
00354     LOG(("tmModuleTest: nsSharedPrefHandler::OnFlushReply [%d]\n", aStatus));
00355     
00356     // Call the internal method to write immediately
00357     // The transaction service holds a lock on the file during this call.
00358     mPrefService->SavePrefFileInternal(nsnull);
00359     return NS_OK;
00360 }
00361 
00362 //*****************************************************************************
00363 // Static functions
00364 //*****************************************************************************   
00365 
00366 static PRBool PR_CALLBACK enumFind(void* aElement, void *aData)
00367 {
00368   char *elemStr = NS_STATIC_CAST(char*, aElement);
00369   char *searchStr = NS_STATIC_CAST(char*, aData);
00370   // return PR_FALSE for a match and to stop search
00371   return (strncmp(elemStr, searchStr, strlen(elemStr)) != 0);
00372 }
00373 
00374 static PRBool PR_CALLBACK enumFree(void* aElement, void *aData)
00375 {
00376   if (aElement)
00377     nsMemory::Free(aElement);
00378   return PR_TRUE;
00379 }
00380 
00381 static PRInt32 ReadLine(FILE* inStm, nsACString& destString)
00382 {
00383   char stackBuf[512];
00384   PRUint32 charsInBuf = 0;
00385   destString.Truncate();
00386   int c;
00387   
00388   while (1) {
00389     c = getc(inStm);
00390     if (c == EOF)
00391       break;
00392     else if (c == '\r') {
00393       c = getc(inStm);
00394       if (c != '\n')
00395         ungetc(c, inStm);
00396       break;
00397     }
00398     else if (c == '\n')
00399       break;
00400     else {
00401       if (charsInBuf >= sizeof(stackBuf)) {
00402         destString.Append(stackBuf, charsInBuf);
00403         charsInBuf = 0;
00404       }
00405       stackBuf[charsInBuf++] = c;
00406     }
00407   }
00408   if (charsInBuf)
00409     destString.Append(stackBuf, charsInBuf);
00410   return (c == EOF && destString.IsEmpty()) ? EOF : 1;
00411 }
00412 
00413 //*****************************************************************************
00414 // NS_CreateSharedPrefHandler
00415 //*****************************************************************************   
00416 
00417 nsresult NS_CreateSharedPrefHandler(nsPrefService *aOwner)
00418 {
00419   nsSharedPrefHandler *local = new nsSharedPrefHandler;
00420   if (!local)
00421     return NS_ERROR_OUT_OF_MEMORY;
00422   nsresult rv = local->Init(aOwner);
00423   if (NS_FAILED(rv)) {
00424     delete local;
00425     return rv;
00426   }
00427   NS_ADDREF(gSharedPrefHandler = local);
00428   return NS_OK;
00429 }
00430