Back to index

lightning-sunbird  0.9+nobinonly
nsLocalStore.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; 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) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or 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 /*
00040 
00041   Implementation for the local store
00042 
00043  */
00044 
00045 #include "nsNetUtil.h"
00046 #include "nsIURI.h"
00047 #include "nsIIOService.h"
00048 #include "nsIOutputStream.h"
00049 #include "nsIComponentManager.h"
00050 #include "nsILocalStore.h"
00051 #include "nsIRDFDataSource.h"
00052 #include "nsIRDFRemoteDataSource.h"
00053 #include "nsIRDFService.h"
00054 #include "nsIServiceManager.h"
00055 #include "nsRDFCID.h"
00056 #include "nsXPIDLString.h"
00057 #include "plstr.h"
00058 #include "rdf.h"
00059 #include "nsCOMPtr.h"
00060 #include "nsWeakPtr.h"
00061 #include "nsAppDirectoryServiceDefs.h"
00062 #include "nsIObserver.h"
00063 #include "nsIObserverService.h"
00064 #include "nsWeakReference.h"
00065 #include "nsCRT.h"
00066 #include "stdio.h"
00067 
00069 
00070 class LocalStoreImpl : public nsILocalStore,
00071                        public nsIRDFDataSource,
00072                        public nsIRDFRemoteDataSource,
00073                        public nsIObserver,
00074                        public nsSupportsWeakReference
00075 {
00076 protected:
00077     nsCOMPtr<nsIRDFDataSource> mInner;
00078 
00079     LocalStoreImpl();
00080     virtual ~LocalStoreImpl();
00081     nsresult Init();
00082     nsresult CreateLocalStore(nsIFile* aFile);
00083     nsresult LoadData();
00084 
00085     friend NS_IMETHODIMP
00086     NS_NewLocalStore(nsISupports* aOuter, REFNSIID aIID, void** aResult);
00087 
00088     nsCOMPtr<nsISupportsArray> mObservers;
00089 
00090     static nsWeakPtr gRDF;
00091 
00092 public:
00093     // nsISupports interface
00094     NS_DECL_ISUPPORTS
00095 
00096     // nsILocalStore interface
00097 
00098     // nsIRDFDataSource interface. Most of these are just delegated to
00099     // the inner, in-memory datasource.
00100     NS_IMETHOD GetURI(char* *aURI);
00101 
00102     NS_IMETHOD GetSource(nsIRDFResource* aProperty,
00103                          nsIRDFNode* aTarget,
00104                          PRBool aTruthValue,
00105                          nsIRDFResource** aSource) {
00106         return mInner->GetSource(aProperty, aTarget, aTruthValue, aSource);
00107     }
00108 
00109     NS_IMETHOD GetSources(nsIRDFResource* aProperty,
00110                           nsIRDFNode* aTarget,
00111                           PRBool aTruthValue,
00112                           nsISimpleEnumerator** aSources) {
00113         return mInner->GetSources(aProperty, aTarget, aTruthValue, aSources);
00114     }
00115 
00116     NS_IMETHOD GetTarget(nsIRDFResource* aSource,
00117                          nsIRDFResource* aProperty,
00118                          PRBool aTruthValue,
00119                          nsIRDFNode** aTarget) {
00120         return mInner->GetTarget(aSource, aProperty, aTruthValue, aTarget);
00121     }
00122 
00123     NS_IMETHOD GetTargets(nsIRDFResource* aSource,
00124                           nsIRDFResource* aProperty,
00125                           PRBool aTruthValue,
00126                           nsISimpleEnumerator** aTargets) {
00127         return mInner->GetTargets(aSource, aProperty, aTruthValue, aTargets);
00128     }
00129 
00130     NS_IMETHOD Assert(nsIRDFResource* aSource, 
00131                       nsIRDFResource* aProperty, 
00132                       nsIRDFNode* aTarget,
00133                       PRBool aTruthValue) {
00134         return mInner->Assert(aSource, aProperty, aTarget, aTruthValue);
00135     }
00136 
00137     NS_IMETHOD Unassert(nsIRDFResource* aSource,
00138                         nsIRDFResource* aProperty,
00139                         nsIRDFNode* aTarget) {
00140         return mInner->Unassert(aSource, aProperty, aTarget);
00141     }
00142 
00143     NS_IMETHOD Change(nsIRDFResource* aSource,
00144                       nsIRDFResource* aProperty,
00145                       nsIRDFNode* aOldTarget,
00146                       nsIRDFNode* aNewTarget) {
00147         return mInner->Change(aSource, aProperty, aOldTarget, aNewTarget);
00148     }
00149 
00150     NS_IMETHOD Move(nsIRDFResource* aOldSource,
00151                     nsIRDFResource* aNewSource,
00152                     nsIRDFResource* aProperty,
00153                     nsIRDFNode* aTarget) {
00154         return mInner->Move(aOldSource, aNewSource, aProperty, aTarget);
00155     }
00156 
00157     NS_IMETHOD HasAssertion(nsIRDFResource* aSource,
00158                             nsIRDFResource* aProperty,
00159                             nsIRDFNode* aTarget,
00160                             PRBool aTruthValue,
00161                             PRBool* hasAssertion) {
00162         return mInner->HasAssertion(aSource, aProperty, aTarget, aTruthValue, hasAssertion);
00163     }
00164 
00165     NS_IMETHOD AddObserver(nsIRDFObserver* aObserver) {
00166         // Observers are _never_ notified, but we still have to play
00167         // nice.
00168         if (! mObservers) {
00169             nsresult rv;
00170             rv = NS_NewISupportsArray(getter_AddRefs(mObservers));
00171             if (NS_FAILED(rv)) return rv;
00172         }
00173 
00174         mObservers->AppendElement(aObserver);
00175         return NS_OK;
00176     }
00177 
00178     NS_IMETHOD RemoveObserver(nsIRDFObserver* aObserver) {
00179         if (mObservers) {
00180             mObservers->RemoveElement(aObserver);
00181         }
00182         return NS_OK;
00183     }
00184 
00185     NS_IMETHOD HasArcIn(nsIRDFNode *aNode, nsIRDFResource *aArc, PRBool *_retval) {
00186         return mInner->HasArcIn(aNode, aArc, _retval);
00187     }
00188 
00189     NS_IMETHOD HasArcOut(nsIRDFResource *aSource, nsIRDFResource *aArc, PRBool *_retval) {
00190         return mInner->HasArcOut(aSource, aArc, _retval);
00191     }
00192 
00193     NS_IMETHOD ArcLabelsIn(nsIRDFNode* aNode,
00194                            nsISimpleEnumerator** aLabels) {
00195         return mInner->ArcLabelsIn(aNode, aLabels);
00196     }
00197 
00198     NS_IMETHOD ArcLabelsOut(nsIRDFResource* aSource,
00199                             nsISimpleEnumerator** aLabels) {
00200         return mInner->ArcLabelsOut(aSource, aLabels);
00201     }
00202 
00203     NS_IMETHOD GetAllResources(nsISimpleEnumerator** aResult) {
00204         return mInner->GetAllResources(aResult);
00205     }
00206 
00207     NS_IMETHOD GetAllCmds(nsIRDFResource* aSource,
00208                               nsISimpleEnumerator/*<nsIRDFResource>*/** aCommands);
00209 
00210     NS_IMETHOD IsCommandEnabled(nsISupportsArray/*<nsIRDFResource>*/* aSources,
00211                                 nsIRDFResource*   aCommand,
00212                                 nsISupportsArray/*<nsIRDFResource>*/* aArguments,
00213                                 PRBool* aResult);
00214 
00215     NS_IMETHOD DoCommand(nsISupportsArray/*<nsIRDFResource>*/* aSources,
00216                          nsIRDFResource*   aCommand,
00217                          nsISupportsArray/*<nsIRDFResource>*/* aArguments);
00218 
00219     NS_IMETHOD BeginUpdateBatch() {
00220         return mInner->BeginUpdateBatch();
00221     }
00222                                                                                 
00223     NS_IMETHOD EndUpdateBatch() {
00224         return mInner->EndUpdateBatch();
00225     }
00226 
00227     NS_IMETHOD GetLoaded(PRBool* _result);
00228        NS_IMETHOD Init(const char *uri);
00229        NS_IMETHOD Flush();
00230        NS_IMETHOD FlushTo(const char *aURI);
00231        NS_IMETHOD Refresh(PRBool sync);
00232        
00233        // nsIObserver
00234        NS_DECL_NSIOBSERVER
00235 };
00236 
00237 nsWeakPtr LocalStoreImpl::gRDF;
00238 
00239 
00241 
00242 
00243 LocalStoreImpl::LocalStoreImpl(void)
00244 {
00245 }
00246 
00247 LocalStoreImpl::~LocalStoreImpl(void)
00248 {
00249     nsCOMPtr<nsIRDFService> rdf = do_QueryReferent(gRDF);
00250     if (rdf)
00251         rdf->UnregisterDataSource(this);
00252 }
00253 
00254 
00255 NS_IMETHODIMP
00256 NS_NewLocalStore(nsISupports* aOuter, REFNSIID aIID, void** aResult)
00257 {
00258     NS_PRECONDITION(aOuter == nsnull, "no aggregation");
00259     if (aOuter)
00260         return NS_ERROR_NO_AGGREGATION;
00261 
00262     NS_PRECONDITION(aResult != nsnull, "null ptr");
00263     if (! aResult)
00264         return NS_ERROR_NULL_POINTER;
00265 
00266     LocalStoreImpl* impl = new LocalStoreImpl();
00267     if (! impl)
00268         return NS_ERROR_OUT_OF_MEMORY;
00269 
00270     NS_ADDREF(impl);
00271 
00272     nsresult rv;
00273     rv = impl->Init();
00274     if (NS_SUCCEEDED(rv)) {
00275         // Set up the result pointer
00276         rv = impl->QueryInterface(aIID, aResult);
00277     }
00278 
00279     NS_RELEASE(impl);
00280     return rv;
00281 }
00282 
00283 
00284 // nsISupports interface
00285 
00286 NS_IMPL_ADDREF(LocalStoreImpl)
00287 NS_IMPL_RELEASE(LocalStoreImpl)
00288 
00289 NS_IMETHODIMP
00290 LocalStoreImpl::QueryInterface(REFNSIID aIID, void** aResult)
00291 {
00292 static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
00293 
00294     NS_PRECONDITION(aResult != nsnull, "null ptr");
00295     if (! aResult)
00296         return NS_ERROR_NULL_POINTER;
00297 
00298     if (aIID.Equals(kISupportsIID) ||
00299         aIID.Equals(NS_GET_IID(nsILocalStore))) {
00300         *aResult = NS_STATIC_CAST(nsILocalStore*, this);
00301     }
00302     else if (aIID.Equals(NS_GET_IID(nsIRDFDataSource))) {
00303         *aResult = NS_STATIC_CAST(nsIRDFDataSource *, this);
00304     }
00305     else if (aIID.Equals(NS_GET_IID(nsIRDFRemoteDataSource))) {
00306         *aResult = NS_STATIC_CAST(nsIRDFRemoteDataSource *, this);
00307     }
00308     else if (aIID.Equals(NS_GET_IID(nsIObserver))) {
00309         *aResult = NS_STATIC_CAST(nsIObserver *, this);
00310     }
00311     else if (aIID.Equals(NS_GET_IID(nsISupportsWeakReference))) {
00312         *aResult = NS_STATIC_CAST(nsISupportsWeakReference *, this);
00313     }
00314     else {
00315         *aResult = nsnull;
00316         return NS_NOINTERFACE;
00317     }
00318 
00319     NS_ADDREF(this);
00320     return NS_OK;
00321 }
00322 
00323 
00324 // nsILocalStore interface
00325 
00326 
00327 
00328 // nsIRDFDataSource interface
00329 
00330 NS_IMETHODIMP
00331 LocalStoreImpl::GetLoaded(PRBool* _result)
00332 {
00333        nsCOMPtr<nsIRDFRemoteDataSource> remote = do_QueryInterface(mInner);
00334     NS_ASSERTION(remote != nsnull, "not an nsIRDFRemoteDataSource");
00335        if (! remote)
00336         return NS_ERROR_UNEXPECTED;
00337 
00338     return remote->GetLoaded(_result);
00339 }
00340 
00341 
00342 NS_IMETHODIMP
00343 LocalStoreImpl::Init(const char *uri)
00344 {
00345        return(NS_OK);
00346 }
00347 
00348 NS_IMETHODIMP
00349 LocalStoreImpl::Flush()
00350 {
00351        nsCOMPtr<nsIRDFRemoteDataSource> remote = do_QueryInterface(mInner);
00352     NS_ASSERTION(remote != nsnull, "not an nsIRDFRemoteDataSource");
00353        if (! remote)
00354         return NS_ERROR_UNEXPECTED;
00355 
00356     return remote->Flush();
00357 }
00358 
00359 NS_IMETHODIMP
00360 LocalStoreImpl::FlushTo(const char *aURI)
00361 {
00362   // Do not ever implement this (security)
00363   return NS_ERROR_NOT_IMPLEMENTED;
00364 }
00365 
00366 NS_IMETHODIMP
00367 LocalStoreImpl::Refresh(PRBool sync)
00368 {
00369        nsCOMPtr<nsIRDFRemoteDataSource> remote = do_QueryInterface(mInner);
00370     NS_ASSERTION(remote != nsnull, "not an nsIRDFRemoteDataSource");
00371        if (! remote)
00372         return NS_ERROR_UNEXPECTED;
00373 
00374     return remote->Refresh(sync);
00375 }
00376 
00377 nsresult
00378 LocalStoreImpl::Init()
00379 {
00380     nsresult rv;
00381 
00382     rv = LoadData();
00383     if (NS_FAILED(rv)) return rv;
00384 
00385     // register this as a named data source with the RDF service
00386     nsCOMPtr<nsIRDFService> rdf = do_GetService(NS_RDF_CONTRACTID "/rdf-service;1", &rv);
00387     if (NS_FAILED(rv)) return rv;
00388 
00389     // for later
00390     if (!gRDF)
00391         gRDF = do_GetWeakReference(rdf);
00392 
00393     rdf->RegisterDataSource(this, PR_FALSE);
00394 
00395     // Register as an observer of profile changes
00396     nsCOMPtr<nsIObserverService> obs =
00397         do_GetService("@mozilla.org/observer-service;1");
00398 
00399     if (obs) {
00400         obs->AddObserver(this, "profile-before-change", PR_TRUE);
00401         obs->AddObserver(this, "profile-do-change", PR_TRUE);
00402     }
00403 
00404     return NS_OK;
00405 }
00406 
00407 nsresult
00408 LocalStoreImpl::CreateLocalStore(nsIFile* aFile)
00409 {
00410     nsresult rv;
00411 
00412     rv = aFile->Create(nsIFile::NORMAL_FILE_TYPE, 0666);
00413     if (NS_FAILED(rv)) return rv;
00414 
00415     nsCOMPtr<nsIOutputStream> outStream;
00416     rv = NS_NewLocalFileOutputStream(getter_AddRefs(outStream), aFile);
00417     if (NS_FAILED(rv)) return rv;
00418 
00419     const char defaultRDF[] = 
00420         "<?xml version=\"1.0\"?>\n" \
00421         "<RDF:RDF xmlns:RDF=\"" RDF_NAMESPACE_URI "\"\n" \
00422         "         xmlns:NC=\""  NC_NAMESPACE_URI "\">\n" \
00423         "  <!-- Empty -->\n" \
00424         "</RDF:RDF>\n";
00425 
00426     PRUint32 count;
00427     rv = outStream->Write(defaultRDF, sizeof(defaultRDF)-1, &count);
00428     if (NS_FAILED(rv)) return rv;
00429 
00430     if (count != sizeof(defaultRDF)-1)
00431         return NS_ERROR_UNEXPECTED;
00432 
00433     // Okay, now see if the file exists _for real_. If it's still
00434     // not there, it could be that the profile service gave us
00435     // back a read-only directory. Whatever.
00436     PRBool fileExistsFlag = PR_FALSE;
00437     aFile->Exists(&fileExistsFlag);
00438     if (!fileExistsFlag)
00439         return NS_ERROR_UNEXPECTED;
00440 
00441     return NS_OK;
00442 }
00443 
00444 nsresult
00445 LocalStoreImpl::LoadData()
00446 {
00447     nsresult rv;
00448 
00449     // Look for localstore.rdf in the current profile
00450     // directory. Bomb if we can't find it.
00451 
00452     nsCOMPtr<nsIFile> aFile;
00453     rv = NS_GetSpecialDirectory(NS_APP_LOCALSTORE_50_FILE, getter_AddRefs(aFile));
00454     if (NS_FAILED(rv)) return rv;
00455 
00456     PRBool fileExistsFlag = PR_FALSE;
00457     (void)aFile->Exists(&fileExistsFlag);
00458     if (!fileExistsFlag) {
00459         // if file doesn't exist, create it
00460         rv = CreateLocalStore(aFile);
00461         if (NS_FAILED(rv)) return rv;
00462     }
00463 
00464     mInner = do_CreateInstance(NS_RDF_DATASOURCE_CONTRACTID_PREFIX "xml-datasource", &rv);
00465     if (NS_FAILED(rv)) return rv;
00466 
00467     nsCOMPtr<nsIRDFRemoteDataSource> remote = do_QueryInterface(mInner, &rv);
00468     if (NS_FAILED(rv)) return rv;
00469 
00470     nsCOMPtr<nsIURI> aURI;
00471     rv = NS_NewFileURI(getter_AddRefs(aURI), aFile);
00472     if (NS_FAILED(rv)) return rv;
00473 
00474     nsCAutoString spec;
00475     rv = aURI->GetSpec(spec);
00476     if (NS_FAILED(rv)) return rv;
00477 
00478     rv = remote->Init(spec.get());
00479     if (NS_FAILED(rv)) return rv;
00480 
00481     // Read the datasource synchronously.
00482     rv = remote->Refresh(PR_TRUE);
00483     
00484     if (NS_FAILED(rv)) {
00485         // Load failed, delete and recreate a fresh localstore
00486         aFile->Remove(PR_TRUE);
00487         rv = CreateLocalStore(aFile);
00488         if (NS_FAILED(rv)) return rv;
00489         
00490         rv = remote->Refresh(PR_TRUE);
00491     }
00492 
00493     return rv;
00494 }
00495 
00496 
00497 NS_IMETHODIMP
00498 LocalStoreImpl::GetURI(char* *aURI)
00499 {
00500     NS_PRECONDITION(aURI != nsnull, "null ptr");
00501     if (! aURI)
00502         return NS_ERROR_NULL_POINTER;
00503 
00504     *aURI = nsCRT::strdup("rdf:local-store");
00505     if (! *aURI)
00506         return NS_ERROR_OUT_OF_MEMORY;
00507 
00508     return NS_OK;
00509 }
00510 
00511 
00512 NS_IMETHODIMP
00513 LocalStoreImpl::GetAllCmds(nsIRDFResource* aSource,
00514                                nsISimpleEnumerator/*<nsIRDFResource>*/** aCommands)
00515 {
00516        return(NS_NewEmptyEnumerator(aCommands));
00517 }
00518 
00519 NS_IMETHODIMP
00520 LocalStoreImpl::IsCommandEnabled(nsISupportsArray/*<nsIRDFResource>*/* aSources,
00521                                  nsIRDFResource*   aCommand,
00522                                  nsISupportsArray/*<nsIRDFResource>*/* aArguments,
00523                                  PRBool* aResult)
00524 {
00525     *aResult = PR_TRUE;
00526     return NS_OK;
00527 }
00528 
00529 NS_IMETHODIMP
00530 LocalStoreImpl::DoCommand(nsISupportsArray* aSources,
00531                           nsIRDFResource*   aCommand,
00532                           nsISupportsArray* aArguments)
00533 {
00534     // no-op
00535     return NS_OK;
00536 }
00537 
00538 NS_IMETHODIMP
00539 LocalStoreImpl::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *someData)
00540 {
00541     nsresult rv = NS_OK;
00542 
00543     if (!nsCRT::strcmp(aTopic, "profile-before-change")) {
00544         // Write out the old datasource's contents.
00545         if (mInner) {
00546             nsCOMPtr<nsIRDFRemoteDataSource> remote = do_QueryInterface(mInner);
00547             if (remote)
00548                 remote->Flush();
00549         }
00550 
00551         // Create an in-memory datasource for use while we're
00552         // profile-less.
00553         mInner = do_CreateInstance(NS_RDF_DATASOURCE_CONTRACTID_PREFIX "in-memory-datasource");
00554 
00555         if (!nsCRT::strcmp(NS_ConvertUCS2toUTF8(someData).get(), "shutdown-cleanse")) {
00556             nsCOMPtr<nsIFile> aFile;
00557             rv = NS_GetSpecialDirectory(NS_APP_LOCALSTORE_50_FILE, getter_AddRefs(aFile));
00558             if (NS_SUCCEEDED(rv))
00559                 rv = aFile->Remove(PR_FALSE);
00560         }
00561     }
00562     else if (!nsCRT::strcmp(aTopic, "profile-do-change")) {
00563         rv = LoadData();
00564     }
00565     return rv;
00566 }