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