1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* vim: set cindent tabstop=4 expandtab shiftwidth=4: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is mozilla.org code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 1998
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Pierre Phaneuf <pp@ludusdesign.com>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or 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.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : /*
41 :
42 : Implementation for the local store
43 :
44 : */
45 :
46 : #include "nsNetUtil.h"
47 : #include "nsIURI.h"
48 : #include "nsIIOService.h"
49 : #include "nsIOutputStream.h"
50 : #include "nsIComponentManager.h"
51 : #include "nsILocalStore.h"
52 : #include "nsIRDFDataSource.h"
53 : #include "nsIRDFRemoteDataSource.h"
54 : #include "nsIRDFService.h"
55 : #include "nsIServiceManager.h"
56 : #include "nsRDFCID.h"
57 : #include "nsXPIDLString.h"
58 : #include "plstr.h"
59 : #include "rdf.h"
60 : #include "nsCOMPtr.h"
61 : #include "nsWeakPtr.h"
62 : #include "nsAppDirectoryServiceDefs.h"
63 : #include "nsIObserver.h"
64 : #include "nsIObserverService.h"
65 : #include "nsWeakReference.h"
66 : #include "nsCRTGlue.h"
67 : #include "nsCRT.h"
68 : #include "nsEnumeratorUtils.h"
69 : #include "nsCycleCollectionParticipant.h"
70 :
71 : ////////////////////////////////////////////////////////////////////////
72 :
73 : class LocalStoreImpl : public nsILocalStore,
74 : public nsIRDFDataSource,
75 : public nsIRDFRemoteDataSource,
76 : public nsIObserver,
77 : public nsSupportsWeakReference
78 : {
79 : protected:
80 : nsCOMPtr<nsIRDFDataSource> mInner;
81 :
82 : LocalStoreImpl();
83 : virtual ~LocalStoreImpl();
84 : nsresult Init();
85 : nsresult CreateLocalStore(nsIFile* aFile);
86 : nsresult LoadData();
87 :
88 : friend nsresult
89 : NS_NewLocalStore(nsISupports* aOuter, REFNSIID aIID, void** aResult);
90 :
91 : nsCOMPtr<nsIRDFService> mRDFService;
92 :
93 : public:
94 : // nsISupports interface
95 0 : NS_DECL_CYCLE_COLLECTING_ISUPPORTS
96 1464 : NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(LocalStoreImpl, nsILocalStore)
97 :
98 : // nsILocalStore interface
99 :
100 : // nsIRDFDataSource interface. Most of these are just delegated to
101 : // the inner, in-memory datasource.
102 : NS_IMETHOD GetURI(char* *aURI);
103 :
104 0 : NS_IMETHOD GetSource(nsIRDFResource* aProperty,
105 : nsIRDFNode* aTarget,
106 : bool aTruthValue,
107 : nsIRDFResource** aSource) {
108 0 : return mInner->GetSource(aProperty, aTarget, aTruthValue, aSource);
109 : }
110 :
111 0 : NS_IMETHOD GetSources(nsIRDFResource* aProperty,
112 : nsIRDFNode* aTarget,
113 : bool aTruthValue,
114 : nsISimpleEnumerator** aSources) {
115 0 : return mInner->GetSources(aProperty, aTarget, aTruthValue, aSources);
116 : }
117 :
118 0 : NS_IMETHOD GetTarget(nsIRDFResource* aSource,
119 : nsIRDFResource* aProperty,
120 : bool aTruthValue,
121 : nsIRDFNode** aTarget) {
122 0 : return mInner->GetTarget(aSource, aProperty, aTruthValue, aTarget);
123 : }
124 :
125 0 : NS_IMETHOD GetTargets(nsIRDFResource* aSource,
126 : nsIRDFResource* aProperty,
127 : bool aTruthValue,
128 : nsISimpleEnumerator** aTargets) {
129 0 : return mInner->GetTargets(aSource, aProperty, aTruthValue, aTargets);
130 : }
131 :
132 0 : NS_IMETHOD Assert(nsIRDFResource* aSource,
133 : nsIRDFResource* aProperty,
134 : nsIRDFNode* aTarget,
135 : bool aTruthValue) {
136 0 : return mInner->Assert(aSource, aProperty, aTarget, aTruthValue);
137 : }
138 :
139 0 : NS_IMETHOD Unassert(nsIRDFResource* aSource,
140 : nsIRDFResource* aProperty,
141 : nsIRDFNode* aTarget) {
142 0 : return mInner->Unassert(aSource, aProperty, aTarget);
143 : }
144 :
145 0 : NS_IMETHOD Change(nsIRDFResource* aSource,
146 : nsIRDFResource* aProperty,
147 : nsIRDFNode* aOldTarget,
148 : nsIRDFNode* aNewTarget) {
149 0 : return mInner->Change(aSource, aProperty, aOldTarget, aNewTarget);
150 : }
151 :
152 0 : NS_IMETHOD Move(nsIRDFResource* aOldSource,
153 : nsIRDFResource* aNewSource,
154 : nsIRDFResource* aProperty,
155 : nsIRDFNode* aTarget) {
156 0 : return mInner->Move(aOldSource, aNewSource, aProperty, aTarget);
157 : }
158 :
159 0 : NS_IMETHOD HasAssertion(nsIRDFResource* aSource,
160 : nsIRDFResource* aProperty,
161 : nsIRDFNode* aTarget,
162 : bool aTruthValue,
163 : bool* hasAssertion) {
164 0 : return mInner->HasAssertion(aSource, aProperty, aTarget, aTruthValue, hasAssertion);
165 : }
166 :
167 0 : NS_IMETHOD AddObserver(nsIRDFObserver* aObserver) {
168 0 : return NS_ERROR_NOT_IMPLEMENTED;
169 : }
170 :
171 0 : NS_IMETHOD RemoveObserver(nsIRDFObserver* aObserver) {
172 0 : return NS_ERROR_NOT_IMPLEMENTED;
173 : }
174 :
175 0 : NS_IMETHOD HasArcIn(nsIRDFNode *aNode, nsIRDFResource *aArc, bool *_retval) {
176 0 : return mInner->HasArcIn(aNode, aArc, _retval);
177 : }
178 :
179 0 : NS_IMETHOD HasArcOut(nsIRDFResource *aSource, nsIRDFResource *aArc, bool *_retval) {
180 0 : return mInner->HasArcOut(aSource, aArc, _retval);
181 : }
182 :
183 0 : NS_IMETHOD ArcLabelsIn(nsIRDFNode* aNode,
184 : nsISimpleEnumerator** aLabels) {
185 0 : return mInner->ArcLabelsIn(aNode, aLabels);
186 : }
187 :
188 0 : NS_IMETHOD ArcLabelsOut(nsIRDFResource* aSource,
189 : nsISimpleEnumerator** aLabels) {
190 0 : return mInner->ArcLabelsOut(aSource, aLabels);
191 : }
192 :
193 0 : NS_IMETHOD GetAllResources(nsISimpleEnumerator** aResult) {
194 0 : return mInner->GetAllResources(aResult);
195 : }
196 :
197 : NS_IMETHOD GetAllCmds(nsIRDFResource* aSource,
198 : nsISimpleEnumerator/*<nsIRDFResource>*/** aCommands);
199 :
200 : NS_IMETHOD IsCommandEnabled(nsISupportsArray/*<nsIRDFResource>*/* aSources,
201 : nsIRDFResource* aCommand,
202 : nsISupportsArray/*<nsIRDFResource>*/* aArguments,
203 : bool* aResult);
204 :
205 : NS_IMETHOD DoCommand(nsISupportsArray/*<nsIRDFResource>*/* aSources,
206 : nsIRDFResource* aCommand,
207 : nsISupportsArray/*<nsIRDFResource>*/* aArguments);
208 :
209 0 : NS_IMETHOD BeginUpdateBatch() {
210 0 : return mInner->BeginUpdateBatch();
211 : }
212 :
213 0 : NS_IMETHOD EndUpdateBatch() {
214 0 : return mInner->EndUpdateBatch();
215 : }
216 :
217 : NS_IMETHOD GetLoaded(bool* _result);
218 : NS_IMETHOD Init(const char *uri);
219 : NS_IMETHOD Flush();
220 : NS_IMETHOD FlushTo(const char *aURI);
221 : NS_IMETHOD Refresh(bool sync);
222 :
223 : // nsIObserver
224 : NS_DECL_NSIOBSERVER
225 : };
226 :
227 : ////////////////////////////////////////////////////////////////////////
228 :
229 :
230 0 : LocalStoreImpl::LocalStoreImpl(void)
231 : {
232 0 : }
233 :
234 0 : LocalStoreImpl::~LocalStoreImpl(void)
235 : {
236 0 : if (mRDFService)
237 0 : mRDFService->UnregisterDataSource(this);
238 0 : }
239 :
240 :
241 : nsresult
242 0 : NS_NewLocalStore(nsISupports* aOuter, REFNSIID aIID, void** aResult)
243 : {
244 0 : NS_PRECONDITION(aOuter == nsnull, "no aggregation");
245 0 : if (aOuter)
246 0 : return NS_ERROR_NO_AGGREGATION;
247 :
248 0 : NS_PRECONDITION(aResult != nsnull, "null ptr");
249 0 : if (! aResult)
250 0 : return NS_ERROR_NULL_POINTER;
251 :
252 0 : LocalStoreImpl* impl = new LocalStoreImpl();
253 0 : if (! impl)
254 0 : return NS_ERROR_OUT_OF_MEMORY;
255 :
256 0 : NS_ADDREF(impl);
257 :
258 : nsresult rv;
259 0 : rv = impl->Init();
260 0 : if (NS_SUCCEEDED(rv)) {
261 : // Set up the result pointer
262 0 : rv = impl->QueryInterface(aIID, aResult);
263 : }
264 :
265 0 : NS_RELEASE(impl);
266 0 : return rv;
267 : }
268 :
269 1464 : NS_IMPL_CYCLE_COLLECTION_1(LocalStoreImpl, mInner)
270 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(LocalStoreImpl)
271 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(LocalStoreImpl)
272 :
273 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(LocalStoreImpl)
274 0 : NS_INTERFACE_MAP_ENTRY(nsILocalStore)
275 0 : NS_INTERFACE_MAP_ENTRY(nsIRDFDataSource)
276 0 : NS_INTERFACE_MAP_ENTRY(nsIRDFRemoteDataSource)
277 0 : NS_INTERFACE_MAP_ENTRY(nsIObserver)
278 0 : NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
279 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsILocalStore)
280 0 : NS_INTERFACE_MAP_END
281 :
282 : // nsILocalStore interface
283 :
284 : // nsIRDFDataSource interface
285 :
286 : NS_IMETHODIMP
287 0 : LocalStoreImpl::GetLoaded(bool* _result)
288 : {
289 0 : nsCOMPtr<nsIRDFRemoteDataSource> remote = do_QueryInterface(mInner);
290 0 : NS_ASSERTION(remote != nsnull, "not an nsIRDFRemoteDataSource");
291 0 : if (! remote)
292 0 : return NS_ERROR_UNEXPECTED;
293 :
294 0 : return remote->GetLoaded(_result);
295 : }
296 :
297 :
298 : NS_IMETHODIMP
299 0 : LocalStoreImpl::Init(const char *uri)
300 : {
301 0 : return(NS_OK);
302 : }
303 :
304 : NS_IMETHODIMP
305 0 : LocalStoreImpl::Flush()
306 : {
307 0 : nsCOMPtr<nsIRDFRemoteDataSource> remote = do_QueryInterface(mInner);
308 : // FIXME Bug 340242: Temporarily make this a warning rather than an
309 : // assertion until we sort out the ordering of how we write
310 : // everything to the localstore, flush it, and disconnect it when
311 : // we're getting profile-change notifications.
312 0 : NS_WARN_IF_FALSE(remote != nsnull, "not an nsIRDFRemoteDataSource");
313 0 : if (! remote)
314 0 : return NS_ERROR_UNEXPECTED;
315 :
316 0 : return remote->Flush();
317 : }
318 :
319 : NS_IMETHODIMP
320 0 : LocalStoreImpl::FlushTo(const char *aURI)
321 : {
322 : // Do not ever implement this (security)
323 0 : return NS_ERROR_NOT_IMPLEMENTED;
324 : }
325 :
326 : NS_IMETHODIMP
327 0 : LocalStoreImpl::Refresh(bool sync)
328 : {
329 0 : nsCOMPtr<nsIRDFRemoteDataSource> remote = do_QueryInterface(mInner);
330 0 : NS_ASSERTION(remote != nsnull, "not an nsIRDFRemoteDataSource");
331 0 : if (! remote)
332 0 : return NS_ERROR_UNEXPECTED;
333 :
334 0 : return remote->Refresh(sync);
335 : }
336 :
337 : nsresult
338 0 : LocalStoreImpl::Init()
339 : {
340 : nsresult rv;
341 :
342 0 : rv = LoadData();
343 0 : if (NS_FAILED(rv)) return rv;
344 :
345 : // register this as a named data source with the RDF service
346 0 : mRDFService = do_GetService(NS_RDF_CONTRACTID "/rdf-service;1", &rv);
347 0 : if (NS_FAILED(rv)) return rv;
348 :
349 0 : mRDFService->RegisterDataSource(this, false);
350 :
351 : // Register as an observer of profile changes
352 : nsCOMPtr<nsIObserverService> obs =
353 0 : do_GetService("@mozilla.org/observer-service;1");
354 :
355 0 : if (obs) {
356 0 : obs->AddObserver(this, "profile-before-change", true);
357 0 : obs->AddObserver(this, "profile-do-change", true);
358 : }
359 :
360 0 : return NS_OK;
361 : }
362 :
363 : nsresult
364 0 : LocalStoreImpl::CreateLocalStore(nsIFile* aFile)
365 : {
366 : nsresult rv;
367 :
368 0 : rv = aFile->Create(nsIFile::NORMAL_FILE_TYPE, 0666);
369 0 : if (NS_FAILED(rv)) return rv;
370 :
371 0 : nsCOMPtr<nsIOutputStream> outStream;
372 0 : rv = NS_NewLocalFileOutputStream(getter_AddRefs(outStream), aFile);
373 0 : if (NS_FAILED(rv)) return rv;
374 :
375 : const char defaultRDF[] =
376 : "<?xml version=\"1.0\"?>\n" \
377 : "<RDF:RDF xmlns:RDF=\"" RDF_NAMESPACE_URI "\"\n" \
378 : " xmlns:NC=\"" NC_NAMESPACE_URI "\">\n" \
379 : " <!-- Empty -->\n" \
380 0 : "</RDF:RDF>\n";
381 :
382 : PRUint32 count;
383 0 : rv = outStream->Write(defaultRDF, sizeof(defaultRDF)-1, &count);
384 0 : if (NS_FAILED(rv)) return rv;
385 :
386 0 : if (count != sizeof(defaultRDF)-1)
387 0 : return NS_ERROR_UNEXPECTED;
388 :
389 : // Okay, now see if the file exists _for real_. If it's still
390 : // not there, it could be that the profile service gave us
391 : // back a read-only directory. Whatever.
392 0 : bool fileExistsFlag = false;
393 0 : aFile->Exists(&fileExistsFlag);
394 0 : if (!fileExistsFlag)
395 0 : return NS_ERROR_UNEXPECTED;
396 :
397 0 : return NS_OK;
398 : }
399 :
400 : nsresult
401 0 : LocalStoreImpl::LoadData()
402 : {
403 : nsresult rv;
404 :
405 : // Look for localstore.rdf in the current profile
406 : // directory. Bomb if we can't find it.
407 :
408 0 : nsCOMPtr<nsIFile> aFile;
409 0 : rv = NS_GetSpecialDirectory(NS_APP_LOCALSTORE_50_FILE, getter_AddRefs(aFile));
410 0 : if (NS_FAILED(rv)) return rv;
411 :
412 0 : bool fileExistsFlag = false;
413 0 : (void)aFile->Exists(&fileExistsFlag);
414 0 : if (!fileExistsFlag) {
415 : // if file doesn't exist, create it
416 0 : rv = CreateLocalStore(aFile);
417 0 : if (NS_FAILED(rv)) return rv;
418 : }
419 :
420 0 : mInner = do_CreateInstance(NS_RDF_DATASOURCE_CONTRACTID_PREFIX "xml-datasource", &rv);
421 0 : if (NS_FAILED(rv)) return rv;
422 :
423 0 : nsCOMPtr<nsIRDFRemoteDataSource> remote = do_QueryInterface(mInner, &rv);
424 0 : if (NS_FAILED(rv)) return rv;
425 :
426 0 : nsCOMPtr<nsIURI> aURI;
427 0 : rv = NS_NewFileURI(getter_AddRefs(aURI), aFile);
428 0 : if (NS_FAILED(rv)) return rv;
429 :
430 0 : nsCAutoString spec;
431 0 : rv = aURI->GetSpec(spec);
432 0 : if (NS_FAILED(rv)) return rv;
433 :
434 0 : rv = remote->Init(spec.get());
435 0 : if (NS_FAILED(rv)) return rv;
436 :
437 : // Read the datasource synchronously.
438 0 : rv = remote->Refresh(true);
439 :
440 0 : if (NS_FAILED(rv)) {
441 : // Load failed, delete and recreate a fresh localstore
442 0 : aFile->Remove(true);
443 0 : rv = CreateLocalStore(aFile);
444 0 : if (NS_FAILED(rv)) return rv;
445 :
446 0 : rv = remote->Refresh(true);
447 : }
448 :
449 0 : return rv;
450 : }
451 :
452 :
453 : NS_IMETHODIMP
454 0 : LocalStoreImpl::GetURI(char* *aURI)
455 : {
456 0 : NS_PRECONDITION(aURI != nsnull, "null ptr");
457 0 : if (! aURI)
458 0 : return NS_ERROR_NULL_POINTER;
459 :
460 0 : *aURI = NS_strdup("rdf:local-store");
461 0 : if (! *aURI)
462 0 : return NS_ERROR_OUT_OF_MEMORY;
463 :
464 0 : return NS_OK;
465 : }
466 :
467 :
468 : NS_IMETHODIMP
469 0 : LocalStoreImpl::GetAllCmds(nsIRDFResource* aSource,
470 : nsISimpleEnumerator/*<nsIRDFResource>*/** aCommands)
471 : {
472 0 : return(NS_NewEmptyEnumerator(aCommands));
473 : }
474 :
475 : NS_IMETHODIMP
476 0 : LocalStoreImpl::IsCommandEnabled(nsISupportsArray/*<nsIRDFResource>*/* aSources,
477 : nsIRDFResource* aCommand,
478 : nsISupportsArray/*<nsIRDFResource>*/* aArguments,
479 : bool* aResult)
480 : {
481 0 : *aResult = true;
482 0 : return NS_OK;
483 : }
484 :
485 : NS_IMETHODIMP
486 0 : LocalStoreImpl::DoCommand(nsISupportsArray* aSources,
487 : nsIRDFResource* aCommand,
488 : nsISupportsArray* aArguments)
489 : {
490 : // no-op
491 0 : return NS_OK;
492 : }
493 :
494 : NS_IMETHODIMP
495 0 : LocalStoreImpl::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *someData)
496 : {
497 0 : nsresult rv = NS_OK;
498 :
499 0 : if (!nsCRT::strcmp(aTopic, "profile-before-change")) {
500 : // Write out the old datasource's contents.
501 0 : if (mInner) {
502 0 : nsCOMPtr<nsIRDFRemoteDataSource> remote = do_QueryInterface(mInner);
503 0 : if (remote)
504 0 : remote->Flush();
505 : }
506 :
507 : // Create an in-memory datasource for use while we're
508 : // profile-less.
509 0 : mInner = do_CreateInstance(NS_RDF_DATASOURCE_CONTRACTID_PREFIX "in-memory-datasource");
510 :
511 0 : if (!nsCRT::strcmp(NS_ConvertUTF16toUTF8(someData).get(), "shutdown-cleanse")) {
512 0 : nsCOMPtr<nsIFile> aFile;
513 0 : rv = NS_GetSpecialDirectory(NS_APP_LOCALSTORE_50_FILE, getter_AddRefs(aFile));
514 0 : if (NS_SUCCEEDED(rv))
515 0 : rv = aFile->Remove(false);
516 : }
517 : }
518 0 : else if (!nsCRT::strcmp(aTopic, "profile-do-change")) {
519 0 : rv = LoadData();
520 : }
521 0 : return rv;
522 4392 : }
|