root/trunk/components/library/localdatabase/src/sbLocalDatabaseLibraryLoader.cpp

Revision 8949, 33.7 kB (checked in by steven, 1 month ago)

Bug 8326 - Defaulting to saving device library sync preferences useing the nsIPrefService, this works well for devices like mtp, and allows each library to have its own sync preferences. Devices that need to store there sync preferences differently should override the sbIDeviceLibrary component.

  • Property svn:eol-style set to LF
Line 
1 /*
2 //
3 // BEGIN SONGBIRD GPL
4 //
5 // This file is part of the Songbird web player.
6 //
7 // Copyright(c) 2005-2008 POTI, Inc.
8 // http://songbirdnest.com
9 //
10 // This file may be licensed under the terms of of the
11 // GNU General Public License Version 2 (the "GPL").
12 //
13 // Software distributed under the License is distributed
14 // on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
15 // express or implied. See the GPL for the specific language
16 // governing rights and limitations.
17 //
18 // You should have received a copy of the GPL along with this
19 // program. If not, go to http://www.gnu.org/licenses/gpl.html
20 // or write to the Free Software Foundation, Inc.,
21 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 //
23 // END SONGBIRD GPL
24 //
25 */
26
27 #include "sbLocalDatabaseLibraryLoader.h"
28
29 #include <nsICategoryManager.h>
30 #include <nsIAppStartup.h>
31 #include <nsIFile.h>
32 #include <nsIGenericFactory.h>
33 #include <nsILocalFile.h>
34 #include <nsIObserverService.h>
35 #include <nsIPrefBranch.h>
36 #include <nsIPrefService.h>
37 #include <nsIPromptService.h>
38 #include <nsIProperties.h>
39 #include <nsIPropertyBag2.h>
40 #include <nsIStringBundle.h>
41 #include <nsISupportsPrimitives.h>
42 #include <sbILibrary.h>
43 #include <sbIMediaList.h>
44 #include <sbIMetrics.h>
45
46 #include <nsAutoPtr.h>
47 #include <nsComponentManagerUtils.h>
48 #include <nsServiceManagerUtils.h>
49 #include <nsMemory.h>
50 #include <nsServiceManagerUtils.h>
51 #include <nsTHashtable.h>
52 #include <nsXPCOMCID.h>
53 #include <nsXPFEComponentsCID.h>
54 #include <prlog.h>
55 #include <sbLibraryManager.h>
56 #include <sbMemoryUtils.h>
57 #include "sbLocalDatabaseCID.h"
58 #include "sbLocalDatabaseLibraryFactory.h"
59
60 #include <sbIPropertyManager.h>
61 #include <sbPropertiesCID.h>
62 #include <sbStandardProperties.h>
63
64 #define PROPERTY_KEY_DATABASEFILE "databaseFile"
65
66 /**
67  * To log this module, set the following environment variable:
68  *   NSPR_LOG_MODULES=sbLocalDatabaseLibraryLoader:5
69  */
70 #ifdef PR_LOGGING
71 static PRLogModuleInfo* sLibraryLoaderLog = nsnull;
72 #define TRACE(args) PR_LOG(sLibraryLoaderLog, PR_LOG_DEBUG, args)
73 #define LOG(args)   PR_LOG(sLibraryLoaderLog, PR_LOG_WARN, args)
74 #else
75 #define TRACE(args) /* nothing */
76 #define LOG(args)   /* nothing */
77 #endif
78
79 #define NS_APPSTARTUP_CATEGORY         "app-startup"
80 #define NS_FINAL_UI_STARTUP_CATEGORY   "final-ui-startup"
81
82 #define PREFBRANCH_LOADER SB_PREFBRANCH_LIBRARY "loader."
83
84 // These are on PREFBRANCH_LOADER.[index]
85 #define PREF_DATABASE_GUID     "databaseGUID"
86 #define PREF_DATABASE_LOCATION "databaseLocation"
87 #define PREF_LOAD_AT_STARTUP   "loadAtStartup"
88 #define PREF_RESOURCE_GUID     "resourceGUID"
89
90 #define MINIMUM_LIBRARY_COUNT 2
91 #define LOADERINFO_VALUE_COUNT 4
92
93 #define DBENGINE_GUID_MAIN_LIBRARY     "main@library.songbirdnest.com"
94 #define DBENGINE_GUID_WEB_LIBRARY      "web@library.songbirdnest.com"
95
96 // XXXben These should be renamed and standardized somehow.
97 #define SB_NAMEKEY_MAIN_LIBRARY                            \
98   "&chrome://songbird/locale/songbird.properties#servicesource.library"
99 #define SB_NAMEKEY_WEB_LIBRARY                             \
100   "&chrome://songbird/locale/songbird.properties#device.weblibrary"
101
102 #define SB_CUSTOMTYPE_MAIN_LIBRARY                            \
103   "local"
104 #define SB_CUSTOMTYPE_WEB_LIBRARY                             \
105   "web"
106
107 #define DEFAULT_COLUMNSPEC_WEB_LIBRARY \
108   "http://songbirdnest.com/data/1.0#trackName 264 http://songbirdnest.com/data/1.0#duration 56 http://songbirdnest.com/data/1.0#artistName 209 http://songbirdnest.com/data/1.0#originPageImage 44 http://songbirdnest.com/data/1.0#created 119 d http://songbirdnest.com/data/1.0#downloadButton 83"
109
110
111 NS_IMPL_ISUPPORTS2(sbLocalDatabaseLibraryLoader, sbILibraryLoader, nsIObserver)
112 sbLocalDatabaseLibraryLoader::sbLocalDatabaseLibraryLoader()
113 : m_DetectedCorruptLibrary(PR_FALSE)
114 , m_DeleteLibrariesAtShutdown(PR_FALSE)
115 {
116 #ifdef PR_LOGGING
117   if (!sLibraryLoaderLog)
118     sLibraryLoaderLog = PR_NewLogModule("sbLocalDatabaseLibraryLoader");
119 #endif
120   TRACE(("sbLocalDatabaseLibraryLoader[0x%x] - Created", this));
121 }
122
123 sbLocalDatabaseLibraryLoader::~sbLocalDatabaseLibraryLoader()
124 {
125   TRACE(("sbLocalDatabaseLibraryLoader[0x%x] - Destroyed", this));
126 }
127
128 /**
129  * \brief
130  */
131 nsresult
132 sbLocalDatabaseLibraryLoader::Init()
133 {
134   TRACE(("sbLocalDatabaseLibraryLoader[0x%x] - Init", this));
135
136   nsresult rv;
137
138   nsCOMPtr<nsIObserverService> observerService =
139     do_GetService("@mozilla.org/observer-service;1", &rv);
140   if (NS_SUCCEEDED(rv)) {
141     rv = observerService->AddObserver(this, NS_FINAL_UI_STARTUP_CATEGORY,
142                                       PR_FALSE);
143     NS_ENSURE_SUCCESS(rv, rv);
144     rv = observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
145                                       PR_FALSE);
146     NS_ENSURE_SUCCESS(rv, rv);
147   }
148
149   nsCOMPtr<nsIPrefService> prefService =
150     do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
151   NS_ENSURE_SUCCESS(rv, rv);
152
153   mRootBranch = do_QueryInterface(prefService, &rv);
154   NS_ENSURE_SUCCESS(rv, rv);
155
156   PRUint32 libraryKeysCount;
157   char** libraryKeys;
158
159   rv = mRootBranch->GetChildList(PREFBRANCH_LOADER, &libraryKeysCount,
160                                  &libraryKeys);
161   NS_ENSURE_SUCCESS(rv, rv);
162
163   sbAutoFreeXPCOMArray<char**> autoFree(libraryKeysCount, libraryKeys);
164
165   PRBool success =
166     mLibraryInfoTable.Init(PR_MAX(MINIMUM_LIBRARY_COUNT,
167                                   libraryKeysCount / LOADERINFO_VALUE_COUNT));
168   NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
169
170   for (PRUint32 index = 0; index < libraryKeysCount; index++) {
171     // Should be something like "songbird.library.loader.2.loadAtStartup".
172     nsCAutoString pref(libraryKeys[index]);
173     NS_ASSERTION(StringBeginsWith(pref, NS_LITERAL_CSTRING(PREFBRANCH_LOADER)),
174                  "Bad pref string!");
175
176     PRUint32 branchLength = NS_LITERAL_CSTRING(PREFBRANCH_LOADER).Length();
177
178     PRInt32 firstDotIndex = pref.FindChar('.', branchLength);
179     NS_ASSERTION(firstDotIndex != -1, "Bad pref string!");
180
181     PRUint32 keyLength = firstDotIndex - branchLength;
182     NS_ASSERTION(keyLength > 0, "Bad pref string!");
183
184     // Should be something like "1".
185     nsCAutoString keyString(Substring(pref, branchLength, keyLength));
186     PRUint32 libraryKey = keyString.ToInteger(&rv);
187     NS_ENSURE_SUCCESS(rv, rv);
188
189     // Should be something like "songbird.library.loader.13.".
190     nsCAutoString branchString(Substring(pref, 0, branchLength + keyLength + 1));
191     NS_ASSERTION(StringEndsWith(branchString, NS_LITERAL_CSTRING(".")),
192                  "Bad pref string!");
193
194     if (!mLibraryInfoTable.Get(libraryKey, nsnull)) {
195       nsAutoPtr<sbLibraryLoaderInfo> newLibraryInfo(new sbLibraryLoaderInfo());
196       NS_ENSURE_TRUE(newLibraryInfo, NS_ERROR_OUT_OF_MEMORY);
197
198       rv = newLibraryInfo->Init(branchString);
199       NS_ENSURE_SUCCESS(rv, rv);
200
201       success = mLibraryInfoTable.Put(libraryKey, newLibraryInfo);
202       NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
203
204       newLibraryInfo.forget();
205     }
206   }
207
208   mLibraryInfoTable.Enumerate(VerifyEntriesCallback, nsnull);
209
210   return NS_OK;
211 }
212
213 nsresult
214 sbLocalDatabaseLibraryLoader::EnsureDefaultLibraries()
215 {
216   PRBool databasesOkay = PR_TRUE;
217   nsresult retval = NS_OK;
218  
219   nsresult rv =
220     EnsureDefaultLibrary(NS_LITERAL_CSTRING(SB_PREF_MAIN_LIBRARY),
221                          NS_LITERAL_STRING(DBENGINE_GUID_MAIN_LIBRARY),
222                          NS_LITERAL_STRING(SB_NAMEKEY_MAIN_LIBRARY),
223                          NS_LITERAL_STRING(SB_CUSTOMTYPE_MAIN_LIBRARY),
224                          EmptyString());
225   if (NS_FAILED(rv)) {
226     databasesOkay = PR_FALSE;
227     retval = rv;
228   }
229
230   rv = EnsureDefaultLibrary(NS_LITERAL_CSTRING(SB_PREF_WEB_LIBRARY),
231                             NS_LITERAL_STRING(DBENGINE_GUID_WEB_LIBRARY),
232                             NS_LITERAL_STRING(SB_NAMEKEY_WEB_LIBRARY),
233                             NS_LITERAL_STRING(SB_CUSTOMTYPE_WEB_LIBRARY),
234                             NS_LITERAL_STRING(DEFAULT_COLUMNSPEC_WEB_LIBRARY));
235   if (NS_FAILED(rv)) {
236     databasesOkay = PR_FALSE;
237     retval = rv;
238   }
239
240   if (! databasesOkay) {
241     // Bad database problem.  Later we'll prompt the user.  Now is so early
242     // that they would see the prompt again after a silent restart, like
243     // the ones that happen on firstrun - component registration, etc.
244     // (see Observe() for the prompt call.)
245     m_DetectedCorruptLibrary = PR_TRUE;
246
247     // metric: corrupt database at startup
248     nsCOMPtr<sbIMetrics> metrics =
249       do_CreateInstance("@songbirdnest.com/Songbird/Metrics;1", &rv);
250     NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to get metrics service");
251
252     nsString metricsCategory = NS_LITERAL_STRING("app");
253     nsString metricsId = NS_LITERAL_STRING("library.error");
254     rv = metrics->MetricsInc(metricsCategory, metricsId, EmptyString());
255     NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to post metric");
256   }
257  
258   return retval;
259 }
260
261 nsresult
262 sbLocalDatabaseLibraryLoader::EnsureDefaultLibrary(const nsACString& aLibraryGUIDPref,
263                                                    const nsAString& aDefaultDatabaseGUID,
264                                                    const nsAString& aLibraryNameKey,
265                                                    const nsAString& aCustomType,
266                                                    const nsAString& aDefaultColumnSpec)
267 {
268   nsCAutoString resourceGUIDPrefKey(aLibraryGUIDPref);
269
270   // Figure out the GUID for this library.
271   nsAutoString resourceGUID;
272
273   PRInt32 libraryInfoIndex = -1;
274
275   // The prefs here should point to a library resourceGUID.
276   nsCOMPtr<nsISupportsString> supportsString;
277   nsresult rv = mRootBranch->GetComplexValue(resourceGUIDPrefKey.get(),
278                                              NS_GET_IID(nsISupportsString),
279                                              getter_AddRefs(supportsString));
280   if (NS_SUCCEEDED(rv)) {
281     // Use the value stored in the prefs.
282     rv = supportsString->GetData(resourceGUID);
283     NS_ENSURE_SUCCESS(rv, rv);
284
285     NS_ASSERTION(!resourceGUID.IsEmpty(), "Should have a resource GUID here!");
286
287     // See if this library already exists in the hashtable.
288     sbLibraryExistsInfo existsInfo(resourceGUID);
289     mLibraryInfoTable.EnumerateRead(LibraryExistsCallback, &existsInfo);
290    
291     libraryInfoIndex = existsInfo.index;
292   }
293
294   sbLibraryLoaderInfo* libraryInfo;
295   if ((libraryInfoIndex == -1) ||
296       (!mLibraryInfoTable.Get(libraryInfoIndex, &libraryInfo))) {
297     // The library wasn't in our hashtable, so make a new sbLibraryLoaderInfo
298     // object. That will take care of setting the prefs up correctly.
299     PRUint32 index = GetNextLibraryIndex();
300
301     nsCAutoString prefKey(PREFBRANCH_LOADER);
302     prefKey.AppendInt(index);
303     prefKey.AppendLiteral(".");
304
305     nsAutoPtr<sbLibraryLoaderInfo>
306       newLibraryInfo(CreateDefaultLibraryInfo(prefKey, aDefaultDatabaseGUID,
307                                               nsnull, aLibraryNameKey));
308     if (!newLibraryInfo || !mLibraryInfoTable.Put(index, newLibraryInfo)) {
309       return NS_ERROR_FAILURE;
310     }
311
312     // Set the resource GUID into the prefs.
313     newLibraryInfo->GetResourceGUID(resourceGUID);
314     NS_ENSURE_FALSE(resourceGUID.IsEmpty(), NS_ERROR_UNEXPECTED);
315
316     supportsString = do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
317     NS_ENSURE_SUCCESS(rv, rv);
318
319     rv = supportsString->SetData(resourceGUID);
320     NS_ENSURE_SUCCESS(rv, rv);
321
322     rv = mRootBranch->SetComplexValue(resourceGUIDPrefKey.get(),
323                                       NS_GET_IID(nsISupportsString),
324                                       supportsString);
325     NS_ENSURE_SUCCESS(rv, rv);
326
327     libraryInfo = newLibraryInfo.forget();
328   }
329
330 #ifdef DEBUG
331   nsAutoString guid;
332   libraryInfo->GetDatabaseGUID(guid);
333   NS_ASSERTION(!guid.IsEmpty(), "Empty GUID!");
334 #endif
335
336   // Make sure this library loads at startup.
337   if (!libraryInfo->GetLoadAtStartup()) {
338     rv = libraryInfo->SetLoadAtStartup(PR_TRUE);
339     NS_ENSURE_SUCCESS(rv, rv);
340   }
341
342   // And make sure that the database file actually exists and is accessible.
343   nsCOMPtr<nsILocalFile> location = libraryInfo->GetDatabaseLocation();
344   NS_ENSURE_TRUE(location, NS_ERROR_UNEXPECTED);
345
346   nsRefPtr<sbLocalDatabaseLibraryFactory> libraryFactory =
347     dont_AddRef(sbLocalDatabaseLibraryFactory::GetInstance());
348   NS_ENSURE_TRUE(libraryFactory, NS_ERROR_OUT_OF_MEMORY);
349
350   nsCOMPtr<sbILibrary> library;
351   rv = libraryFactory->CreateLibraryFromDatabase(location,
352                                                  getter_AddRefs(library));
353   if (NS_FAILED(rv)) {
354     // We can't access this required database file. For now we're going to
355     // simply make a new blank database in the default location and switch
356     // the preferences to use it.
357     location = libraryFactory->GetFileForGUID(aDefaultDatabaseGUID);
358     NS_ENSURE_TRUE(location, NS_ERROR_FAILURE);
359
360     // Make sure we can access this one.
361     rv = libraryFactory->CreateLibraryFromDatabase(location,
362                                                    getter_AddRefs(library));
363     NS_ENSURE_SUCCESS(rv, rv);
364
365     // Set the name.
366     nsCOMPtr<sbIMediaList> mediaList = do_QueryInterface(library, &rv);
367     NS_ENSURE_SUCCESS(rv, rv);
368
369     rv = mediaList->SetName(aLibraryNameKey);
370     NS_ENSURE_SUCCESS(rv, rv);
371
372     // And update the libraryInfo object.
373     rv = libraryInfo->SetDatabaseGUID(aDefaultDatabaseGUID);
374     NS_ENSURE_SUCCESS(rv, rv);
375
376     rv = libraryInfo->SetDatabaseLocation(location);
377     NS_ENSURE_SUCCESS(rv, rv);
378
379     rv = library->GetGuid(resourceGUID);
380     NS_ENSURE_SUCCESS(rv, rv);
381
382     rv = libraryInfo->SetResourceGUID(resourceGUID);
383     NS_ENSURE_SUCCESS(rv, rv);
384   }
385
386   rv = library->SetProperty(NS_LITERAL_STRING(SB_PROPERTY_CUSTOMTYPE),
387                             aCustomType);
388   NS_ENSURE_SUCCESS(rv, rv);
389
390   rv = library->SetProperty(NS_LITERAL_STRING(SB_PROPERTY_ISSORTABLE),
391                             NS_LITERAL_STRING("1"));
392   NS_ENSURE_SUCCESS(rv, rv);
393
394   if (!aDefaultColumnSpec.IsEmpty()) {
395     rv = library->SetProperty(NS_LITERAL_STRING(SB_PROPERTY_DEFAULTCOLUMNSPEC),
396                               aDefaultColumnSpec);
397     NS_ENSURE_SUCCESS(rv, rv);
398   }
399
400   return NS_OK;
401 }
402
403 /**
404  * \brief
405  */
406 sbLibraryLoaderInfo*
407 sbLocalDatabaseLibraryLoader::CreateDefaultLibraryInfo(const nsACString& aPrefKey,
408                                                        const nsAString& aDatabaseGUID,
409                                                        nsILocalFile* aDatabaseFile,
410                                                        const nsAString& aLibraryNameKey)
411 {
412   nsAutoPtr<sbLibraryLoaderInfo> newLibraryInfo(new sbLibraryLoaderInfo());
413   NS_ENSURE_TRUE(newLibraryInfo, nsnull);
414
415   nsresult rv = newLibraryInfo->Init(aPrefKey);
416   NS_ENSURE_SUCCESS(rv, nsnull);
417
418   nsRefPtr<sbLocalDatabaseLibraryFactory> libraryFactory =
419     dont_AddRef(sbLocalDatabaseLibraryFactory::GetInstance());
420   NS_ENSURE_TRUE(libraryFactory, nsnull);
421  
422   nsAutoString databaseGUID;
423
424   if (!aDatabaseGUID.IsEmpty()) {
425     databaseGUID.Assign(aDatabaseGUID);
426   }
427   else {
428     NS_ASSERTION(aDatabaseFile, "You must supply either the GUID or file!");
429
430     // Figure out the GUID from the filename.
431     libraryFactory->GetGUIDFromFile(aDatabaseFile, databaseGUID);
432     NS_ENSURE_FALSE(databaseGUID.IsEmpty(), nsnull);
433   }
434
435   rv = newLibraryInfo->SetDatabaseGUID(databaseGUID);
436   NS_ENSURE_SUCCESS(rv, nsnull);
437
438   nsCOMPtr<nsILocalFile> location;
439
440   if (aDatabaseFile) {
441     location = aDatabaseFile;
442   }
443   else {
444     NS_ASSERTION(!aDatabaseGUID.IsEmpty(),
445                  "You must specify either the GUID or file!");
446
447     // Figure out the file from the GUID.
448     location = libraryFactory->GetFileForGUID(aDatabaseGUID);
449     NS_ENSURE_TRUE(location, nsnull);
450   }
451
452   rv = newLibraryInfo->SetDatabaseLocation(location);
453   NS_ENSURE_SUCCESS(rv, nsnull);
454
455   rv = newLibraryInfo->SetLoadAtStartup(PR_TRUE);
456   NS_ENSURE_SUCCESS(rv, nsnull);
457
458   // The resource GUID is unknown at this point. Load the library and get it.
459   nsCOMPtr<sbILibrary> library;
460   rv = libraryFactory->CreateLibraryFromDatabase(location,
461                                                  getter_AddRefs(library));
462   NS_ENSURE_SUCCESS(rv, nsnull);
463
464   if (!aLibraryNameKey.IsEmpty()) {
465     nsCOMPtr<sbIMediaList> mediaList = do_QueryInterface(library, &rv);
466     NS_ENSURE_SUCCESS(rv, nsnull);
467
468     // Set the name.
469     rv = mediaList->SetName(aLibraryNameKey);
470     NS_ENSURE_SUCCESS(rv, nsnull);
471   }
472
473   nsAutoString resourceGUID;
474   rv = library->GetGuid(resourceGUID);
475   NS_ENSURE_SUCCESS(rv, nsnull);
476
477   rv = newLibraryInfo->SetResourceGUID(resourceGUID);
478   NS_ENSURE_SUCCESS(rv, nsnull);
479
480   nsCOMPtr<nsIPrefService> prefService = do_QueryInterface(mRootBranch, &rv);
481   NS_ENSURE_SUCCESS(rv, nsnull);
482
483   rv = prefService->SavePrefFile(nsnull);
484   NS_ENSURE_SUCCESS(rv, nsnull);
485
486   return newLibraryInfo.forget();
487 }
488
489
490 /*
491  * Something bad has happened to the user's database(s).  Prompt to
492  * tell them that we need to delete their libraries.  If the user says
493  * OK, set a flag and shut down the app; the actual deletion is done
494  * in the Observe() method at shutdown time.
495  */
496 nsresult
497 sbLocalDatabaseLibraryLoader::PromptToDeleteLibraries()
498 {
499   nsresult rv;
500
501   nsCOMPtr<nsIPromptService> promptService =
502     do_GetService("@mozilla.org/embedcomp/prompt-service;1", &rv);
503   NS_ENSURE_SUCCESS(rv, rv);
504
505   PRUint32 buttons = nsIPromptService::BUTTON_POS_0 * nsIPromptService::BUTTON_TITLE_IS_STRING +
506                      nsIPromptService::BUTTON_POS_1 * nsIPromptService::BUTTON_TITLE_IS_STRING +
507                      nsIPromptService::BUTTON_POS_1_DEFAULT;
508
509   PRInt32 promptResult;
510
511   nsCOMPtr<nsIStringBundleService> stringBundleService =
512     do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
513   NS_ENSURE_SUCCESS(rv, rv);
514
515   // get dialog strings
516   nsCOMPtr<nsIStringBundle> bundle;
517   rv = stringBundleService->CreateBundle("chrome://songbird/locale/songbird.properties",
518                                          getter_AddRefs(bundle));
519   NS_ENSURE_SUCCESS(rv, rv);
520
521   nsAutoString dialogTitle;
522   rv = bundle->GetStringFromName(NS_LITERAL_STRING("corruptdatabase.dialog.title").get(),
523                                  getter_Copies(dialogTitle));
524   NS_ENSURE_SUCCESS(rv, rv);
525
526   nsAutoString dialogText;
527   rv = bundle->GetStringFromName(NS_LITERAL_STRING("corruptdatabase.dialog.text").get(),
528                                  getter_Copies(dialogText));
529   NS_ENSURE_SUCCESS(rv, rv);
530
531   nsAutoString deleteText;
532   rv = bundle->GetStringFromName(NS_LITERAL_STRING("corruptdatabase.dialog.buttons.delete").get(),
533                                  getter_Copies(deleteText));
534   NS_ENSURE_SUCCESS(rv, rv);
535
536   nsAutoString continueText;
537   rv = bundle->GetStringFromName(NS_LITERAL_STRING("corruptdatabase.dialog.buttons.cancel").get(),
538                                  getter_Copies(continueText));
539   NS_ENSURE_SUCCESS(rv, rv);
540
541
542   // prompt.
543   rv = promptService->ConfirmEx(nsnull,
544                                 dialogTitle.BeginReading(),
545                                 dialogText.BeginReading(),
546                                 buttons,           
547                                 deleteText.BeginReading(),   // button 0
548                                 continueText.BeginReading(), // button 1
549                                 nsnull,                      // button 2
550                                 nsnull,                      // no checkbox
551                                 nsnull,                      // no check value
552                                 &promptResult);     
553   NS_ENSURE_SUCCESS(rv, rv);
554
555   // "Delete" means delete & restart.  "Continue" means let the app
556   // start anyway.
557   if (promptResult == 0) {
558     m_DeleteLibrariesAtShutdown = PR_TRUE;
559
560      // metric: user chose to delete corrupt library
561      nsCOMPtr<sbIMetrics> metrics =
562        do_CreateInstance("@songbirdnest.com/Songbird/Metrics;1", &rv);
563      NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to get metrics service");
564      
565      nsString metricsCategory = NS_LITERAL_STRING("app");
566      nsString metricsId = NS_LITERAL_STRING("library.error.reset");
567      rv = metrics->MetricsInc(metricsCategory, metricsId, EmptyString());
568      NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to post metric");
569
570      // now attempt to quit/restart.
571      nsCOMPtr<nsIAppStartup> appStartup =
572        (do_GetService(NS_APPSTARTUP_CONTRACTID, &rv));
573      NS_ENSURE_SUCCESS(rv, rv);
574  
575      appStartup->Quit(nsIAppStartup::eForceQuit | nsIAppStartup::eRestart);
576   }
577
578   return NS_OK;
579 }
580
581
582 /* static */ void
583 sbLocalDatabaseLibraryLoader::RemovePrefBranch(const nsACString& aPrefBranch)
584 {
585   nsresult rv;
586   nsCOMPtr<nsIPrefService> prefService =
587     do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
588   NS_ENSURE_SUCCESS(rv,);
589
590   nsCAutoString prefBranch(aPrefBranch);
591
592   nsCOMPtr<nsIPrefBranch> doomedBranch;
593   rv = prefService->GetBranch(prefBranch.get(), getter_AddRefs(doomedBranch));
594   NS_ENSURE_SUCCESS(rv,);
595
596   rv = doomedBranch->DeleteBranch("");
597   NS_ENSURE_SUCCESS(rv,);
598
599   rv = prefService->SavePrefFile(nsnull);
600   NS_ENSURE_SUCCESS(rv,);
601 }
602
603 PRUint32
604 sbLocalDatabaseLibraryLoader::GetNextLibraryIndex()
605 {
606   for (PRUint32 index = 0; ; index++) {
607     if (!mLibraryInfoTable.Get(index, nsnull)) {
608       return index;
609     }
610   }
611   NS_NOTREACHED("Shouldn't be here!");
612   return 0;
613 }
614
615 /* static */ PLDHashOperator PR_CALLBACK
616 sbLocalDatabaseLibraryLoader::LoadLibrariesCallback(nsUint32HashKey::KeyType aKey,
617                                                     sbLibraryLoaderInfo* aEntry,
618                                                     void* aUserData)
619 {
620   sbLoaderInfo* loaderInfo = static_cast<sbLoaderInfo*>(aUserData);
621   NS_ASSERTION(loaderInfo, "Doh, how did this happen?!");
622
623   if (!aEntry->GetLoadAtStartup()) {
624     return PL_DHASH_NEXT;
625   }
626
627   nsCOMPtr<nsILocalFile> databaseFile = aEntry->GetDatabaseLocation();
628   NS_ASSERTION(databaseFile, "Must have a file here!");
629
630   nsCOMPtr<sbILibrary> library;
631   nsresult rv =
632     loaderInfo->libraryFactory->CreateLibraryFromDatabase(databaseFile,
633                                                           getter_AddRefs(library));
634   NS_ENSURE_SUCCESS(rv, PL_DHASH_NEXT);
635  
636   rv = loaderInfo->libraryManager->RegisterLibrary(library, PR_TRUE);
637   NS_ENSURE_SUCCESS(rv, PL_DHASH_NEXT);
638
639   return PL_DHASH_NEXT;
640 }
641
642 /* static */ PLDHashOperator PR_CALLBACK
643 sbLocalDatabaseLibraryLoader::LibraryExistsCallback(nsUint32HashKey::KeyType aKey,
644                                                     sbLibraryLoaderInfo* aEntry,
645                                                     void* aUserData)
646 {
647   sbLibraryExistsInfo* existsInfo =
648     static_cast<sbLibraryExistsInfo*>(aUserData);
649   NS_ASSERTION(existsInfo, "Doh, how did this happen?!");
650
651   nsAutoString resourceGUID;
652   aEntry->GetResourceGUID(resourceGUID);
653   NS_ASSERTION(!resourceGUID.IsEmpty(), "GUID can't be empty here!");
654
655   if (resourceGUID.Equals(existsInfo->resourceGUID)) {
656     existsInfo->index = (PRInt32)aKey;
657     return PL_DHASH_STOP;
658   }
659
660   return PL_DHASH_NEXT;
661 }
662
663 /* static */ PLDHashOperator PR_CALLBACK
664 sbLocalDatabaseLibraryLoader::VerifyEntriesCallback(nsUint32HashKey::KeyType aKey,
665                                                     nsAutoPtr<sbLibraryLoaderInfo>& aEntry,
666                                                     void* aUserData)
667 {
668   nsCAutoString prefBranch;
669   aEntry->GetPrefBranch(prefBranch);
670   NS_ASSERTION(!prefBranch.IsEmpty(), "This can't be empty!");
671
672   nsAutoString databaseGUID;
673   aEntry->GetDatabaseGUID(databaseGUID);
674   if (databaseGUID.IsEmpty()) {
675     NS_WARNING("One of the libraries was missing a database GUID and will be removed.");
676     RemovePrefBranch(prefBranch);
677     return PL_DHASH_REMOVE;
678   }
679
680   nsAutoString resourceGUID;
681   aEntry->GetResourceGUID(resourceGUID);
682   if (resourceGUID.IsEmpty()) {
683     NS_WARNING("One of the libraries was missing a resource GUID and will be removed.");
684     RemovePrefBranch(prefBranch);
685     return PL_DHASH_REMOVE;
686   }
687
688   nsCOMPtr<nsILocalFile> location = aEntry->GetDatabaseLocation();
689   if (!location) {
690     NS_WARNING("One of the libraries had no location and will be removed.");
691     RemovePrefBranch(prefBranch);
692     return PL_DHASH_REMOVE;
693   }
694
695   return PL_DHASH_NEXT;
696 }
697
698 /**
699  * \brief Load all of the libraries we need for startup.
700  */
701 NS_IMETHODIMP
702 sbLocalDatabaseLibraryLoader::OnRegisterStartupLibraries(sbILibraryManager* aLibraryManager)
703 {
704   TRACE(("sbLocalDatabaseLibraryLoader[0x%x] - LoadLibraries", this));
705
706   nsresult rv = Init();
707   NS_ENSURE_SUCCESS(rv, rv);
708
709   rv = EnsureDefaultLibraries();
710   NS_ENSURE_SUCCESS(rv, rv);
711
712   nsRefPtr<sbLocalDatabaseLibraryFactory> libraryFactory =
713     dont_AddRef(sbLocalDatabaseLibraryFactory::GetInstance());
714   NS_ENSURE_TRUE(libraryFactory, NS_ERROR_OUT_OF_MEMORY);
715  
716   sbLoaderInfo info(aLibraryManager, libraryFactory);
717
718   PRUint32 enumeratedLibraries =
719     mLibraryInfoTable.EnumerateRead(LoadLibrariesCallback, &info);
720   NS_ASSERTION(enumeratedLibraries >= MINIMUM_LIBRARY_COUNT, "Too few libraries enumerated!");
721
722   return NS_OK;
723 }
724
725 NS_IMETHODIMP
726 sbLocalDatabaseLibraryLoader::OnLibraryStartupModified(sbILibrary* aLibrary,
727                                                        PRBool aLoadAtStartup)
728 {
729   NS_ENSURE_ARG_POINTER(aLibrary);
730
731   // See if we support this library type.
732   nsCOMPtr<sbILibraryFactory> factory;
733   nsresult rv = aLibrary->GetFactory(getter_AddRefs(factory));
734   NS_ENSURE_SUCCESS(rv, rv);
735
736   nsAutoString factoryType;
737   rv = factory->GetType(factoryType);
738   NS_ENSURE_SUCCESS(rv, rv);
739
740   NS_ENSURE_TRUE(factoryType.EqualsLiteral(SB_LOCALDATABASE_LIBRARYFACTORY_TYPE),
741                  NS_ERROR_NOT_AVAILABLE);
742
743   // See if this library already exists in the hashtable.
744   nsAutoString resourceGUID;
745   rv = aLibrary->GetGuid(resourceGUID);
746   NS_ENSURE_SUCCESS(rv, rv);
747
748   sbLibraryExistsInfo existsInfo(resourceGUID);
749   mLibraryInfoTable.EnumerateRead(LibraryExistsCallback, &existsInfo);
750
751   sbLibraryLoaderInfo* libraryInfo;
752   if ((existsInfo.index == -1) ||
753       (!mLibraryInfoTable.Get(existsInfo.index, &libraryInfo))) {
754
755     // The library wasn't in the hashtable so make sure that we can recreate
756     // it.
757     nsCOMPtr<nsIPropertyBag2> creationParameters;
758     rv = aLibrary->GetCreationParameters(getter_AddRefs(creationParameters));
759     NS_ENSURE_SUCCESS(rv, rv);
760
761     NS_NAMED_LITERAL_STRING(fileKey, PROPERTY_KEY_DATABASEFILE);
762     nsCOMPtr<nsILocalFile> databaseFile;
763     rv = creationParameters->GetPropertyAsInterface(fileKey,
764                                                     NS_GET_IID(nsILocalFile),
765                                                     getter_AddRefs(databaseFile));
766     NS_ENSURE_SUCCESS(rv, rv);
767
768     NS_ASSERTION(databaseFile, "This can't be null!");
769
770     // Make a new sbLibraryLoaderInfo object.
771     PRUint32 index = GetNextLibraryIndex();
772
773     nsCAutoString prefKey(PREFBRANCH_LOADER);
774     prefKey.AppendInt(index);
775     prefKey.AppendLiteral(".");
776
777     nsAutoPtr<sbLibraryLoaderInfo>
778       newLibraryInfo(CreateDefaultLibraryInfo(prefKey, EmptyString(),
779                                               databaseFile));
780     if (!newLibraryInfo || !mLibraryInfoTable.Put(index, newLibraryInfo)) {
781       return NS_ERROR_FAILURE;
782     }
783
784     rv = newLibraryInfo->SetDatabaseLocation(databaseFile);
785     NS_ENSURE_SUCCESS(rv, rv);
786
787     libraryInfo = newLibraryInfo.forget();
788   }
789
790   rv = libraryInfo->SetLoadAtStartup(aLoadAtStartup);
791   NS_ENSURE_SUCCESS(rv, rv);
792
793   return NS_OK;
794 }
795
796
797 NS_IMETHODIMP
798 sbLocalDatabaseLibraryLoader::Observe(nsISupports *aSubject,
799                                       const char *aTopic,
800                                       const PRUnichar *aData)
801 {
802   nsresult rv;
803
804   if (strcmp(aTopic, NS_FINAL_UI_STARTUP_CATEGORY) == 0) {
805     if (m_DetectedCorruptLibrary) {
806       rv = PromptToDeleteLibraries();
807       NS_ENSURE_SUCCESS(rv, rv);
808     }
809   } else if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
810     // By now, databases should all be closed so it is safe to delete
811     // the db directory.
812     if (m_DeleteLibrariesAtShutdown) {
813       // get profile directory
814       nsCOMPtr<nsIProperties> directoryService(
815         do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv));
816       NS_ENSURE_SUCCESS(rv, rv);
817      
818       nsCOMPtr<nsIFile> siteDBDir;
819       rv = directoryService->Get("ProfD", NS_GET_IID(nsIFile),
820                                  getter_AddRefs(siteDBDir));
821       NS_ENSURE_SUCCESS(rv, rv);
822      
823       // append the database dir name
824       siteDBDir->Append(NS_LITERAL_STRING("db"));
825      
826
827       // Delete all the databases but the metrics DB. (which hopefully
828       // is not corrupt)
829       nsCOMPtr<nsISimpleEnumerator> dirEnumerator;
830       rv = siteDBDir->GetDirectoryEntries(getter_AddRefs(dirEnumerator));
831       NS_ENSURE_SUCCESS(rv, rv);
832
833       nsString metricsdb = NS_LITERAL_STRING("metrics.db");
834
835       PRBool hasMore;
836       dirEnumerator->HasMoreElements(&hasMore);
837       while (hasMore && NS_SUCCEEDED(rv)) {
838         nsCOMPtr<nsISupports> aSupport;
839         rv = dirEnumerator->GetNext(getter_AddRefs(aSupport));
840         if (NS_FAILED(rv)) break;
841         nsCOMPtr<nsIFile> curFile(do_QueryInterface(aSupport, &rv));
842         if (NS_FAILED(rv)) break;
843
844         nsString leafName;
845         rv = curFile->GetLeafName(leafName);
846         if (NS_FAILED(rv)) break;
847        
848         if (leafName.Compare(metricsdb) != 0) {
849           rv = curFile->Remove(false);
850           NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Unable to delete file");
851         }
852        
853         dirEnumerator->HasMoreElements(&hasMore);
854       }
855
856       // We want to prompt the user to rescan on restart.
857       nsCAutoString scancompleteBranch("songbird.firstrun.scancomplete");
858       sbLocalDatabaseLibraryLoader::RemovePrefBranch(scancompleteBranch);
859
860       // And delete all the library prefs, so they get recreated on
861       // startup.  (It would be nice to not need to do this, so that
862       // users could delete their db directory by hand and restart, but
863       // bad things happen due to prefs and it's not worth putting time
864       // into.)
865       nsCAutoString prefBranch(PREFBRANCH_LOADER);
866       sbLocalDatabaseLibraryLoader::RemovePrefBranch(prefBranch);
867     }
868   }
869
870    return NS_OK;
871 }
872
873
874
875 /**
876  * sbLibraryLoaderInfo implementation
877  */
878 nsresult
879 sbLibraryLoaderInfo::Init(const nsACString& aPrefKey)
880 {
881   nsresult rv;
882   nsCOMPtr<nsIPrefService> prefService =
883     do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
884   NS_ENSURE_SUCCESS(rv, rv);
885
886   nsCAutoString prefBranchString(aPrefKey);
887   rv = prefService->GetBranch(prefBranchString.get(),
888                               getter_AddRefs(mPrefBranch));
889   NS_ENSURE_SUCCESS(rv, rv);
890
891   mDatabaseGUIDKey.Assign(PREF_DATABASE_GUID);
892   mLocationKey.Assign(PREF_DATABASE_LOCATION);
893   mStartupKey.Assign(PREF_LOAD_AT_STARTUP);
894   mResourceGUIDKey.Assign(PREF_RESOURCE_GUID);
895
896   // Now ensure that the key exists.
897   PRBool exists;
898   rv = mPrefBranch->PrefHasUserValue(mStartupKey.get(), &exists);
899   NS_ENSURE_SUCCESS(rv, rv);
900
901   if (!exists) {
902     rv = SetLoadAtStartup(PR_FALSE);
903     NS_ENSURE_SUCCESS(rv, rv);
904   }
905
906   return NS_OK;
907 }
908
909 nsresult
910 sbLibraryLoaderInfo::SetDatabaseGUID(const nsAString& aGUID)
911 {
912   NS_ENSURE_FALSE(aGUID.IsEmpty(), NS_ERROR_INVALID_ARG);
913
914   nsresult rv;
915   nsCOMPtr<nsISupportsString> supportsString =
916     do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
917   NS_ENSURE_SUCCESS(rv, rv);
918
919   rv = supportsString->SetData(aGUID);
920   NS_ENSURE_SUCCESS(rv, rv);
921
922   rv = mPrefBranch->SetComplexValue(mDatabaseGUIDKey.get(),
923                                     NS_GET_IID(nsISupportsString),
924                                     supportsString);
925
926   return NS_OK;
927 }
928
929 void
930 sbLibraryLoaderInfo::GetDatabaseGUID(nsAString& _retval)
931 {
932   _retval.Truncate();
933
934   nsCOMPtr<nsISupportsString> supportsString;
935   nsresult rv = mPrefBranch->GetComplexValue(mDatabaseGUIDKey.get(),
936                                              NS_GET_IID(nsISupportsString),
937                                              getter_AddRefs(supportsString));
938   NS_ENSURE_SUCCESS(rv,);
939
940   rv = supportsString->GetData(_retval);
941   NS_ENSURE_SUCCESS(rv,);
942 }
943
944 nsresult
945 sbLibraryLoaderInfo::SetDatabaseLocation(nsILocalFile* aLocation)
946 {
947   NS_ENSURE_ARG_POINTER(aLocation);
948
949   nsresult rv;
950   nsCOMPtr<nsIFile> file = do_QueryInterface(aLocation, &rv);
951   NS_ENSURE_SUCCESS(rv, rv);
952
953   nsCAutoString filePath;
954   rv = file->GetNativePath(filePath);
955   NS_ENSURE_SUCCESS(rv, rv);
956
957   rv = mPrefBranch->SetCharPref(mLocationKey.get(), filePath.get());
958   NS_ENSURE_SUCCESS(rv, rv);
959
960   return NS_OK;
961 }
962
963 already_AddRefed<nsILocalFile>
964 sbLibraryLoaderInfo::GetDatabaseLocation()
965 {
966   nsresult rv;
967   nsCOMPtr<nsILocalFile> location = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
968   NS_ENSURE_SUCCESS(rv, nsnull);
969
970   nsCAutoString filePath;
971   rv = mPrefBranch->GetCharPref(mLocationKey.get(), getter_Copies(filePath));
972   NS_ENSURE_SUCCESS(rv, nsnull);
973
974   rv = location->InitWithNativePath(filePath);
975   NS_ENSURE_SUCCESS(rv, nsnull);
976
977   nsILocalFile* _retval;
978   NS_ADDREF(_retval = location);
979   return _retval;
980 }
981
982 nsresult
983 sbLibraryLoaderInfo::SetLoadAtStartup(PRBool aLoadAtStartup)
984 {
985   nsresult rv = mPrefBranch->SetBoolPref(mStartupKey.get(), aLoadAtStartup);
986   NS_ENSURE_SUCCESS(rv, rv);
987
988   return NS_OK;
989 }
990
991 PRBool
992 sbLibraryLoaderInfo::GetLoadAtStartup()
993 {
994   PRBool loadAtStartup;
995   nsresult rv = mPrefBranch->GetBoolPref(mStartupKey.get(), &loadAtStartup);
996   NS_ENSURE_SUCCESS(rv, PR_FALSE);
997
998   return loadAtStartup;
999 }
1000
1001 nsresult
1002 sbLibraryLoaderInfo::SetResourceGUID(const nsAString& aGUID)
1003 {
1004   NS_ENSURE_FALSE(aGUID.IsEmpty(), NS_ERROR_INVALID_ARG);
1005
1006   nsresult rv;
1007   nsCOMPtr<nsISupportsString> supportsString =
1008     do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
1009   NS_ENSURE_SUCCESS(rv, rv);
1010
1011   rv = supportsString->SetData(aGUID);
1012   NS_ENSURE_SUCCESS(rv, rv);
1013
1014   rv = mPrefBranch->SetComplexValue(mResourceGUIDKey.get(),
1015                                     NS_GET_IID(nsISupportsString),
1016                                     supportsString);
1017
1018   return NS_OK;
1019 }
1020
1021 void
1022 sbLibraryLoaderInfo::GetResourceGUID(nsAString& _retval)
1023 {
1024   _retval.Truncate();
1025
1026   nsCOMPtr<nsISupportsString> supportsString;
1027   nsresult rv = mPrefBranch->GetComplexValue(mResourceGUIDKey.get(),
1028                                              NS_GET_IID(nsISupportsString),
1029                                              getter_AddRefs(supportsString));
1030   NS_ENSURE_SUCCESS(rv,);
1031
1032   rv = supportsString->GetData(_retval);
1033   NS_ENSURE_SUCCESS(rv,);
1034 }
1035
1036 void
1037 sbLibraryLoaderInfo::GetPrefBranch(nsACString& _retval)
1038 {
1039   _retval.Truncate();
1040
1041   nsCAutoString prefBranch;
1042   nsresult rv = mPrefBranch->GetRoot(getter_Copies(prefBranch));
1043   NS_ENSURE_SUCCESS(rv,);
1044
1045   _retval.Assign(prefBranch);
1046 }
Note: See TracBrowser for help on using the browser.

© 2005-2008 POTI, Inc. Mozilla and Firefox are registered trademarks of the Mozilla Foundation.