Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "mozilla/ArrayUtils.h"
7 : #include "mozilla/UniquePtr.h"
8 :
9 : #include <stdio.h>
10 : #include <stdlib.h>
11 : #include <prprf.h>
12 : #include <prtime.h>
13 : #include "nsProfileLock.h"
14 :
15 : #ifdef XP_WIN
16 : #include <windows.h>
17 : #include <shlobj.h>
18 : #endif
19 : #ifdef XP_UNIX
20 : #include <unistd.h>
21 : #endif
22 :
23 : #include "nsIToolkitProfileService.h"
24 : #include "nsIToolkitProfile.h"
25 : #include "nsIFactory.h"
26 : #include "nsIFile.h"
27 : #include "nsISimpleEnumerator.h"
28 :
29 : #ifdef XP_MACOSX
30 : #include <CoreFoundation/CoreFoundation.h>
31 : #include "nsILocalFileMac.h"
32 : #endif
33 :
34 : #include "nsAppDirectoryServiceDefs.h"
35 : #include "nsXULAppAPI.h"
36 :
37 : #include "nsINIParser.h"
38 : #include "nsXREDirProvider.h"
39 : #include "nsAppRunner.h"
40 : #include "nsString.h"
41 : #include "nsReadableUtils.h"
42 : #include "nsNativeCharsetUtils.h"
43 : #include "mozilla/Attributes.h"
44 : #include "mozilla/Sprintf.h"
45 :
46 : using namespace mozilla;
47 :
48 : class nsToolkitProfile final : public nsIToolkitProfile
49 : {
50 : public:
51 : NS_DECL_ISUPPORTS
52 : NS_DECL_NSITOOLKITPROFILE
53 :
54 : friend class nsToolkitProfileService;
55 : RefPtr<nsToolkitProfile> mNext;
56 : nsToolkitProfile *mPrev;
57 :
58 : private:
59 0 : ~nsToolkitProfile() { }
60 :
61 : nsToolkitProfile(const nsACString& aName,
62 : nsIFile* aRootDir,
63 : nsIFile* aLocalDir,
64 : nsToolkitProfile* aPrev,
65 : bool aForExternalApp);
66 :
67 : friend class nsToolkitProfileLock;
68 :
69 : nsCString mName;
70 : nsCOMPtr<nsIFile> mRootDir;
71 : nsCOMPtr<nsIFile> mLocalDir;
72 : nsIProfileLock* mLock;
73 : bool mForExternalApp;
74 : };
75 :
76 : class nsToolkitProfileLock final : public nsIProfileLock
77 : {
78 : public:
79 : NS_DECL_ISUPPORTS
80 : NS_DECL_NSIPROFILELOCK
81 :
82 : nsresult Init(nsToolkitProfile* aProfile, nsIProfileUnlocker* *aUnlocker);
83 : nsresult Init(nsIFile* aDirectory, nsIFile* aLocalDirectory,
84 : nsIProfileUnlocker* *aUnlocker);
85 :
86 1 : nsToolkitProfileLock() { }
87 :
88 : private:
89 : ~nsToolkitProfileLock();
90 :
91 : RefPtr<nsToolkitProfile> mProfile;
92 : nsCOMPtr<nsIFile> mDirectory;
93 : nsCOMPtr<nsIFile> mLocalDirectory;
94 :
95 : nsProfileLock mLock;
96 : };
97 :
98 1 : class nsToolkitProfileFactory final : public nsIFactory
99 : {
100 0 : ~nsToolkitProfileFactory() {}
101 : public:
102 : NS_DECL_ISUPPORTS
103 : NS_DECL_NSIFACTORY
104 : };
105 :
106 : class nsToolkitProfileService final : public nsIToolkitProfileService
107 : {
108 : public:
109 : NS_DECL_ISUPPORTS
110 : NS_DECL_NSITOOLKITPROFILESERVICE
111 :
112 : private:
113 : friend class nsToolkitProfile;
114 : friend class nsToolkitProfileFactory;
115 : friend nsresult NS_NewToolkitProfileService(nsIToolkitProfileService**);
116 :
117 1 : nsToolkitProfileService() :
118 : mDirty(false),
119 : mStartWithLast(true),
120 1 : mStartOffline(false)
121 : {
122 1 : gService = this;
123 1 : }
124 0 : ~nsToolkitProfileService()
125 0 : {
126 0 : gService = nullptr;
127 0 : }
128 :
129 : nsresult Init();
130 :
131 : nsresult CreateTimesInternal(nsIFile *profileDir);
132 :
133 : nsresult CreateProfileInternal(nsIFile* aRootDir,
134 : const nsACString& aName,
135 : const nsACString* aProfileName,
136 : const nsACString* aAppName,
137 : const nsACString* aVendorName,
138 : bool aForExternalApp,
139 : nsIToolkitProfile** aResult);
140 :
141 : RefPtr<nsToolkitProfile> mFirst;
142 : nsCOMPtr<nsIToolkitProfile> mChosen;
143 : nsCOMPtr<nsIToolkitProfile> mDefault;
144 : nsCOMPtr<nsIFile> mAppData;
145 : nsCOMPtr<nsIFile> mTempData;
146 : nsCOMPtr<nsIFile> mListFile;
147 : bool mDirty;
148 : bool mStartWithLast;
149 : bool mStartOffline;
150 :
151 : static nsToolkitProfileService *gService;
152 :
153 : class ProfileEnumerator final : public nsISimpleEnumerator
154 : {
155 : public:
156 : NS_DECL_ISUPPORTS
157 : NS_DECL_NSISIMPLEENUMERATOR
158 :
159 1 : explicit ProfileEnumerator(nsToolkitProfile *first)
160 1 : { mCurrent = first; }
161 : private:
162 1 : ~ProfileEnumerator() { }
163 : RefPtr<nsToolkitProfile> mCurrent;
164 : };
165 : };
166 :
167 1 : nsToolkitProfile::nsToolkitProfile(const nsACString& aName,
168 : nsIFile* aRootDir,
169 : nsIFile* aLocalDir,
170 : nsToolkitProfile* aPrev,
171 1 : bool aForExternalApp) :
172 : mPrev(aPrev),
173 : mName(aName),
174 : mRootDir(aRootDir),
175 : mLocalDir(aLocalDir),
176 : mLock(nullptr),
177 1 : mForExternalApp(aForExternalApp)
178 : {
179 1 : NS_ASSERTION(aRootDir, "No file!");
180 :
181 1 : if (!aForExternalApp) {
182 1 : if (aPrev) {
183 0 : aPrev->mNext = this;
184 : } else {
185 1 : nsToolkitProfileService::gService->mFirst = this;
186 : }
187 : }
188 1 : }
189 :
190 9 : NS_IMPL_ISUPPORTS(nsToolkitProfile, nsIToolkitProfile)
191 :
192 : NS_IMETHODIMP
193 0 : nsToolkitProfile::GetRootDir(nsIFile* *aResult)
194 : {
195 0 : NS_ADDREF(*aResult = mRootDir);
196 0 : return NS_OK;
197 : }
198 :
199 : NS_IMETHODIMP
200 0 : nsToolkitProfile::GetLocalDir(nsIFile* *aResult)
201 : {
202 0 : NS_ADDREF(*aResult = mLocalDir);
203 0 : return NS_OK;
204 : }
205 :
206 : NS_IMETHODIMP
207 0 : nsToolkitProfile::GetName(nsACString& aResult)
208 : {
209 0 : aResult = mName;
210 0 : return NS_OK;
211 : }
212 :
213 : NS_IMETHODIMP
214 0 : nsToolkitProfile::SetName(const nsACString& aName)
215 : {
216 0 : NS_ASSERTION(nsToolkitProfileService::gService,
217 : "Where did my service go?");
218 0 : NS_ENSURE_TRUE(!mForExternalApp, NS_ERROR_NOT_IMPLEMENTED);
219 :
220 0 : mName = aName;
221 0 : nsToolkitProfileService::gService->mDirty = true;
222 :
223 0 : return NS_OK;
224 : }
225 :
226 : NS_IMETHODIMP
227 0 : nsToolkitProfile::Remove(bool removeFiles)
228 : {
229 0 : NS_ASSERTION(nsToolkitProfileService::gService,
230 : "Whoa, my service is gone.");
231 :
232 0 : NS_ENSURE_TRUE(!mForExternalApp, NS_ERROR_NOT_IMPLEMENTED);
233 :
234 0 : if (mLock)
235 0 : return NS_ERROR_FILE_IS_LOCKED;
236 :
237 0 : if (!mPrev && !mNext && nsToolkitProfileService::gService->mFirst != this)
238 0 : return NS_ERROR_NOT_INITIALIZED;
239 :
240 0 : if (removeFiles) {
241 : bool equals;
242 0 : nsresult rv = mRootDir->Equals(mLocalDir, &equals);
243 0 : if (NS_FAILED(rv))
244 0 : return rv;
245 :
246 : // The root dir might contain the temp dir, so remove
247 : // the temp dir first.
248 0 : if (!equals)
249 0 : mLocalDir->Remove(true);
250 :
251 0 : mRootDir->Remove(true);
252 : }
253 :
254 0 : if (mPrev)
255 0 : mPrev->mNext = mNext;
256 : else
257 0 : nsToolkitProfileService::gService->mFirst = mNext;
258 :
259 0 : if (mNext)
260 0 : mNext->mPrev = mPrev;
261 :
262 0 : mPrev = nullptr;
263 0 : mNext = nullptr;
264 :
265 0 : if (nsToolkitProfileService::gService->mChosen == this)
266 0 : nsToolkitProfileService::gService->mChosen = nullptr;
267 :
268 0 : nsToolkitProfileService::gService->mDirty = true;
269 :
270 0 : return NS_OK;
271 : }
272 :
273 : NS_IMETHODIMP
274 0 : nsToolkitProfile::Lock(nsIProfileUnlocker* *aUnlocker, nsIProfileLock* *aResult)
275 : {
276 0 : if (mLock) {
277 0 : NS_ADDREF(*aResult = mLock);
278 0 : return NS_OK;
279 : }
280 :
281 0 : RefPtr<nsToolkitProfileLock> lock = new nsToolkitProfileLock();
282 0 : if (!lock) return NS_ERROR_OUT_OF_MEMORY;
283 :
284 0 : nsresult rv = lock->Init(this, aUnlocker);
285 0 : if (NS_FAILED(rv)) return rv;
286 :
287 0 : NS_ADDREF(*aResult = lock);
288 0 : return NS_OK;
289 : }
290 :
291 4 : NS_IMPL_ISUPPORTS(nsToolkitProfileLock, nsIProfileLock)
292 :
293 : nsresult
294 0 : nsToolkitProfileLock::Init(nsToolkitProfile* aProfile, nsIProfileUnlocker* *aUnlocker)
295 : {
296 : nsresult rv;
297 0 : rv = Init(aProfile->mRootDir, aProfile->mLocalDir, aUnlocker);
298 0 : if (NS_SUCCEEDED(rv))
299 0 : mProfile = aProfile;
300 :
301 0 : return rv;
302 : }
303 :
304 : nsresult
305 1 : nsToolkitProfileLock::Init(nsIFile* aDirectory, nsIFile* aLocalDirectory,
306 : nsIProfileUnlocker* *aUnlocker)
307 : {
308 : nsresult rv;
309 :
310 1 : rv = mLock.Lock(aDirectory, aUnlocker);
311 :
312 1 : if (NS_SUCCEEDED(rv)) {
313 1 : mDirectory = aDirectory;
314 1 : mLocalDirectory = aLocalDirectory;
315 : }
316 :
317 1 : return rv;
318 : }
319 :
320 : NS_IMETHODIMP
321 1 : nsToolkitProfileLock::GetDirectory(nsIFile* *aResult)
322 : {
323 1 : if (!mDirectory) {
324 0 : NS_ERROR("Not initialized, or unlocked!");
325 0 : return NS_ERROR_NOT_INITIALIZED;
326 : }
327 :
328 1 : NS_ADDREF(*aResult = mDirectory);
329 1 : return NS_OK;
330 : }
331 :
332 : NS_IMETHODIMP
333 1 : nsToolkitProfileLock::GetLocalDirectory(nsIFile* *aResult)
334 : {
335 1 : if (!mLocalDirectory) {
336 0 : NS_ERROR("Not initialized, or unlocked!");
337 0 : return NS_ERROR_NOT_INITIALIZED;
338 : }
339 :
340 1 : NS_ADDREF(*aResult = mLocalDirectory);
341 1 : return NS_OK;
342 : }
343 :
344 : NS_IMETHODIMP
345 0 : nsToolkitProfileLock::Unlock()
346 : {
347 0 : if (!mDirectory) {
348 0 : NS_ERROR("Unlocking a never-locked nsToolkitProfileLock!");
349 0 : return NS_ERROR_UNEXPECTED;
350 : }
351 :
352 0 : mLock.Unlock();
353 :
354 0 : if (mProfile) {
355 0 : mProfile->mLock = nullptr;
356 0 : mProfile = nullptr;
357 : }
358 0 : mDirectory = nullptr;
359 0 : mLocalDirectory = nullptr;
360 :
361 0 : return NS_OK;
362 : }
363 :
364 : NS_IMETHODIMP
365 2 : nsToolkitProfileLock::GetReplacedLockTime(PRTime *aResult)
366 : {
367 2 : mLock.GetReplacedLockTime(aResult);
368 2 : return NS_OK;
369 : }
370 :
371 0 : nsToolkitProfileLock::~nsToolkitProfileLock()
372 : {
373 0 : if (mDirectory) {
374 0 : Unlock();
375 : }
376 0 : }
377 :
378 : nsToolkitProfileService*
379 : nsToolkitProfileService::gService = nullptr;
380 :
381 19 : NS_IMPL_ISUPPORTS(nsToolkitProfileService,
382 : nsIToolkitProfileService)
383 :
384 : nsresult
385 1 : nsToolkitProfileService::Init()
386 : {
387 1 : NS_ASSERTION(gDirServiceProvider, "No dirserviceprovider!");
388 : nsresult rv;
389 :
390 1 : rv = gDirServiceProvider->GetUserAppDataDirectory(getter_AddRefs(mAppData));
391 1 : NS_ENSURE_SUCCESS(rv, rv);
392 :
393 1 : rv = gDirServiceProvider->GetUserLocalDataDirectory(getter_AddRefs(mTempData));
394 1 : NS_ENSURE_SUCCESS(rv, rv);
395 :
396 1 : rv = mAppData->Clone(getter_AddRefs(mListFile));
397 1 : NS_ENSURE_SUCCESS(rv, rv);
398 :
399 1 : rv = mListFile->AppendNative(NS_LITERAL_CSTRING("profiles.ini"));
400 1 : NS_ENSURE_SUCCESS(rv, rv);
401 :
402 : bool exists;
403 1 : rv = mListFile->IsFile(&exists);
404 1 : if (NS_FAILED(rv) || !exists) {
405 0 : return NS_OK;
406 : }
407 :
408 : int64_t size;
409 1 : rv = mListFile->GetFileSize(&size);
410 1 : if (NS_FAILED(rv) || !size) {
411 0 : return NS_OK;
412 : }
413 :
414 2 : nsINIParser parser;
415 1 : rv = parser.Init(mListFile);
416 : // Init does not fail on parsing errors, only on OOM/really unexpected
417 : // conditions.
418 1 : if (NS_FAILED(rv))
419 0 : return rv;
420 :
421 2 : nsAutoCString buffer;
422 1 : rv = parser.GetString("General", "StartWithLastProfile", buffer);
423 1 : if (NS_SUCCEEDED(rv) && buffer.EqualsLiteral("0"))
424 1 : mStartWithLast = false;
425 :
426 1 : nsToolkitProfile* currentProfile = nullptr;
427 :
428 : #ifdef MOZ_DEV_EDITION
429 : nsCOMPtr<nsIFile> ignoreSeparateProfile;
430 : rv = mAppData->Clone(getter_AddRefs(ignoreSeparateProfile));
431 : if (NS_FAILED(rv))
432 : return rv;
433 :
434 : rv = ignoreSeparateProfile->AppendNative(NS_LITERAL_CSTRING("ignore-dev-edition-profile"));
435 : if (NS_FAILED(rv))
436 : return rv;
437 :
438 : bool shouldIgnoreSeparateProfile;
439 : rv = ignoreSeparateProfile->Exists(&shouldIgnoreSeparateProfile);
440 : if (NS_FAILED(rv))
441 : return rv;
442 : #endif
443 :
444 1 : unsigned int c = 0;
445 1 : bool foundAuroraDefault = false;
446 2 : for (c = 0; true; ++c) {
447 3 : nsAutoCString profileID("Profile");
448 2 : profileID.AppendInt(c);
449 :
450 2 : rv = parser.GetString(profileID.get(), "IsRelative", buffer);
451 2 : if (NS_FAILED(rv)) break;
452 :
453 1 : bool isRelative = buffer.EqualsLiteral("1");
454 :
455 2 : nsAutoCString filePath;
456 :
457 1 : rv = parser.GetString(profileID.get(), "Path", filePath);
458 1 : if (NS_FAILED(rv)) {
459 0 : NS_ERROR("Malformed profiles.ini: Path= not found");
460 0 : continue;
461 : }
462 :
463 2 : nsAutoCString name;
464 :
465 1 : rv = parser.GetString(profileID.get(), "Name", name);
466 1 : if (NS_FAILED(rv)) {
467 0 : NS_ERROR("Malformed profiles.ini: Name= not found");
468 0 : continue;
469 : }
470 :
471 2 : nsCOMPtr<nsIFile> rootDir;
472 1 : rv = NS_NewNativeLocalFile(EmptyCString(), true,
473 2 : getter_AddRefs(rootDir));
474 1 : NS_ENSURE_SUCCESS(rv, rv);
475 :
476 1 : if (isRelative) {
477 1 : rv = rootDir->SetRelativeDescriptor(mAppData, filePath);
478 : } else {
479 0 : rv = rootDir->SetPersistentDescriptor(filePath);
480 : }
481 1 : if (NS_FAILED(rv)) continue;
482 :
483 2 : nsCOMPtr<nsIFile> localDir;
484 1 : if (isRelative) {
485 1 : rv = NS_NewNativeLocalFile(EmptyCString(), true,
486 2 : getter_AddRefs(localDir));
487 1 : NS_ENSURE_SUCCESS(rv, rv);
488 :
489 1 : rv = localDir->SetRelativeDescriptor(mTempData, filePath);
490 : } else {
491 0 : localDir = rootDir;
492 : }
493 :
494 1 : currentProfile = new nsToolkitProfile(name,
495 : rootDir, localDir,
496 2 : currentProfile, false);
497 1 : NS_ENSURE_TRUE(currentProfile, NS_ERROR_OUT_OF_MEMORY);
498 :
499 1 : rv = parser.GetString(profileID.get(), "Default", buffer);
500 1 : if (NS_SUCCEEDED(rv) && buffer.EqualsLiteral("1") && !foundAuroraDefault) {
501 0 : mChosen = currentProfile;
502 0 : this->SetDefaultProfile(currentProfile);
503 : }
504 : #ifdef MOZ_DEV_EDITION
505 : // Use the dev-edition-default profile if this is an Aurora build and
506 : // ignore-dev-edition-profile is not present.
507 : if (name.EqualsLiteral("dev-edition-default") && !shouldIgnoreSeparateProfile) {
508 : mChosen = currentProfile;
509 : foundAuroraDefault = true;
510 : }
511 : #endif
512 1 : }
513 :
514 : #ifdef MOZ_DEV_EDITION
515 : if (!foundAuroraDefault && !shouldIgnoreSeparateProfile) {
516 : // If a single profile exists, it may not be already marked as default.
517 : // Do it now to avoid problems when we create the dev-edition-default profile.
518 : if (!mChosen && mFirst && !mFirst->mNext)
519 : this->SetDefaultProfile(mFirst);
520 :
521 : // Create a default profile for aurora, if none was found.
522 : nsCOMPtr<nsIToolkitProfile> profile;
523 : rv = CreateProfile(nullptr,
524 : NS_LITERAL_CSTRING("dev-edition-default"),
525 : getter_AddRefs(profile));
526 : if (NS_FAILED(rv)) return rv;
527 : mChosen = profile;
528 : rv = Flush();
529 : if (NS_FAILED(rv)) return rv;
530 : }
531 : #endif
532 :
533 2 : if (!mChosen && mFirst && !mFirst->mNext) // only one profile
534 1 : mChosen = mFirst;
535 1 : return NS_OK;
536 : }
537 :
538 : NS_IMETHODIMP
539 0 : nsToolkitProfileService::SetStartWithLastProfile(bool aValue)
540 : {
541 0 : if (mStartWithLast != aValue) {
542 0 : mStartWithLast = aValue;
543 0 : mDirty = true;
544 : }
545 0 : return NS_OK;
546 : }
547 :
548 : NS_IMETHODIMP
549 0 : nsToolkitProfileService::GetStartWithLastProfile(bool *aResult)
550 : {
551 0 : *aResult = mStartWithLast;
552 0 : return NS_OK;
553 : }
554 :
555 : NS_IMETHODIMP
556 0 : nsToolkitProfileService::GetStartOffline(bool *aResult)
557 : {
558 0 : *aResult = mStartOffline;
559 0 : return NS_OK;
560 : }
561 :
562 : NS_IMETHODIMP
563 0 : nsToolkitProfileService::SetStartOffline(bool aValue)
564 : {
565 0 : mStartOffline = aValue;
566 0 : return NS_OK;
567 : }
568 :
569 : NS_IMETHODIMP
570 1 : nsToolkitProfileService::GetProfiles(nsISimpleEnumerator* *aResult)
571 : {
572 2 : *aResult = new ProfileEnumerator(this->mFirst);
573 1 : if (!*aResult)
574 0 : return NS_ERROR_OUT_OF_MEMORY;
575 :
576 1 : NS_ADDREF(*aResult);
577 1 : return NS_OK;
578 : }
579 :
580 6 : NS_IMPL_ISUPPORTS(nsToolkitProfileService::ProfileEnumerator,
581 : nsISimpleEnumerator)
582 :
583 : NS_IMETHODIMP
584 0 : nsToolkitProfileService::ProfileEnumerator::HasMoreElements(bool* aResult)
585 : {
586 0 : *aResult = mCurrent ? true : false;
587 0 : return NS_OK;
588 : }
589 :
590 : NS_IMETHODIMP
591 2 : nsToolkitProfileService::ProfileEnumerator::GetNext(nsISupports* *aResult)
592 : {
593 2 : if (!mCurrent) return NS_ERROR_FAILURE;
594 :
595 1 : NS_ADDREF(*aResult = mCurrent);
596 :
597 1 : mCurrent = mCurrent->mNext;
598 1 : return NS_OK;
599 : }
600 :
601 : NS_IMETHODIMP
602 0 : nsToolkitProfileService::GetSelectedProfile(nsIToolkitProfile* *aResult)
603 : {
604 0 : if (!mChosen && mFirst && !mFirst->mNext) // only one profile
605 0 : mChosen = mFirst;
606 :
607 0 : if (!mChosen) return NS_ERROR_FAILURE;
608 :
609 0 : NS_ADDREF(*aResult = mChosen);
610 0 : return NS_OK;
611 : }
612 :
613 : NS_IMETHODIMP
614 0 : nsToolkitProfileService::SetSelectedProfile(nsIToolkitProfile* aProfile)
615 : {
616 0 : if (mChosen != aProfile) {
617 0 : mChosen = aProfile;
618 0 : mDirty = true;
619 : }
620 0 : return NS_OK;
621 : }
622 :
623 : NS_IMETHODIMP
624 0 : nsToolkitProfileService::GetDefaultProfile(nsIToolkitProfile* *aResult)
625 : {
626 0 : if (!mDefault) return NS_ERROR_FAILURE;
627 :
628 0 : NS_ADDREF(*aResult = mDefault);
629 0 : return NS_OK;
630 : }
631 :
632 : NS_IMETHODIMP
633 0 : nsToolkitProfileService::SetDefaultProfile(nsIToolkitProfile* aProfile)
634 : {
635 0 : if (mDefault != aProfile) {
636 0 : mDefault = aProfile;
637 0 : mDirty = true;
638 : }
639 0 : return NS_OK;
640 : }
641 :
642 : NS_IMETHODIMP
643 0 : nsToolkitProfileService::GetProfileByName(const nsACString& aName,
644 : nsIToolkitProfile* *aResult)
645 : {
646 0 : nsToolkitProfile* curP = mFirst;
647 0 : while (curP) {
648 0 : if (curP->mName.Equals(aName)) {
649 0 : NS_ADDREF(*aResult = curP);
650 0 : return NS_OK;
651 : }
652 0 : curP = curP->mNext;
653 : }
654 :
655 0 : return NS_ERROR_FAILURE;
656 : }
657 :
658 : NS_IMETHODIMP
659 0 : nsToolkitProfileService::LockProfilePath(nsIFile* aDirectory,
660 : nsIFile* aLocalDirectory,
661 : nsIProfileLock* *aResult)
662 : {
663 0 : return NS_LockProfilePath(aDirectory, aLocalDirectory, nullptr, aResult);
664 : }
665 :
666 : nsresult
667 1 : NS_LockProfilePath(nsIFile* aPath, nsIFile* aTempPath,
668 : nsIProfileUnlocker* *aUnlocker, nsIProfileLock* *aResult)
669 : {
670 2 : RefPtr<nsToolkitProfileLock> lock = new nsToolkitProfileLock();
671 1 : if (!lock) return NS_ERROR_OUT_OF_MEMORY;
672 :
673 1 : nsresult rv = lock->Init(aPath, aTempPath, aUnlocker);
674 1 : if (NS_FAILED(rv)) return rv;
675 :
676 1 : lock.forget(aResult);
677 1 : return NS_OK;
678 : }
679 :
680 : static const char kTable[] =
681 : { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
682 : 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
683 : '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' };
684 :
685 0 : static void SaltProfileName(nsACString& aName)
686 : {
687 0 : double fpTime = double(PR_Now());
688 :
689 : // use 1e-6, granularity of PR_Now() on the mac is seconds
690 0 : srand((unsigned int)(fpTime * 1e-6 + 0.5));
691 :
692 : char salt[9];
693 :
694 : int i;
695 0 : for (i = 0; i < 8; ++i)
696 0 : salt[i] = kTable[rand() % ArrayLength(kTable)];
697 :
698 0 : salt[8] = '.';
699 :
700 0 : aName.Insert(salt, 0, 9);
701 0 : }
702 :
703 : NS_IMETHODIMP
704 0 : nsToolkitProfileService::CreateDefaultProfileForApp(const nsACString& aProfileName,
705 : const nsACString& aAppName,
706 : const nsACString& aVendorName,
707 : nsIToolkitProfile** aResult)
708 : {
709 0 : NS_ENSURE_STATE(!aProfileName.IsEmpty() || !aAppName.IsEmpty());
710 0 : nsCOMPtr<nsIFile> appData;
711 : nsresult rv =
712 0 : gDirServiceProvider->GetUserDataDirectory(getter_AddRefs(appData),
713 : false,
714 : &aProfileName,
715 : &aAppName,
716 0 : &aVendorName);
717 0 : NS_ENSURE_SUCCESS(rv, rv);
718 :
719 0 : nsCOMPtr<nsIFile> profilesini;
720 0 : appData->Clone(getter_AddRefs(profilesini));
721 0 : rv = profilesini->AppendNative(NS_LITERAL_CSTRING("profiles.ini"));
722 0 : NS_ENSURE_SUCCESS(rv, rv);
723 :
724 0 : bool exists = false;
725 0 : profilesini->Exists(&exists);
726 0 : NS_ENSURE_FALSE(exists, NS_ERROR_ALREADY_INITIALIZED);
727 :
728 0 : rv = CreateProfileInternal(nullptr,
729 0 : NS_LITERAL_CSTRING("default"),
730 : &aProfileName, &aAppName, &aVendorName,
731 0 : true, aResult);
732 0 : NS_ENSURE_SUCCESS(rv, rv);
733 0 : NS_ENSURE_STATE(*aResult);
734 :
735 0 : nsCOMPtr<nsIFile> rootDir;
736 0 : (*aResult)->GetRootDir(getter_AddRefs(rootDir));
737 0 : NS_ENSURE_SUCCESS(rv, rv);
738 :
739 0 : nsAutoCString profileDir;
740 0 : rv = rootDir->GetRelativeDescriptor(appData, profileDir);
741 0 : NS_ENSURE_SUCCESS(rv, rv);
742 :
743 0 : nsCString ini;
744 0 : ini.SetCapacity(512);
745 0 : ini.AppendLiteral("[General]\n");
746 0 : ini.AppendLiteral("StartWithLastProfile=1\n\n");
747 :
748 0 : ini.AppendLiteral("[Profile0]\n");
749 0 : ini.AppendLiteral("Name=default\n");
750 0 : ini.AppendLiteral("IsRelative=1\n");
751 0 : ini.AppendLiteral("Path=");
752 0 : ini.Append(profileDir);
753 0 : ini.Append('\n');
754 0 : ini.AppendLiteral("Default=1\n\n");
755 :
756 : FILE* writeFile;
757 0 : rv = profilesini->OpenANSIFileDesc("w", &writeFile);
758 0 : NS_ENSURE_SUCCESS(rv, rv);
759 :
760 0 : if (fwrite(ini.get(), sizeof(char), ini.Length(), writeFile) !=
761 0 : ini.Length()) {
762 0 : rv = NS_ERROR_UNEXPECTED;
763 : }
764 0 : fclose(writeFile);
765 0 : return rv;
766 : }
767 :
768 : NS_IMETHODIMP
769 0 : nsToolkitProfileService::CreateProfile(nsIFile* aRootDir,
770 : const nsACString& aName,
771 : nsIToolkitProfile** aResult)
772 : {
773 : return CreateProfileInternal(aRootDir, aName,
774 0 : nullptr, nullptr, nullptr, false, aResult);
775 : }
776 :
777 : nsresult
778 0 : nsToolkitProfileService::CreateProfileInternal(nsIFile* aRootDir,
779 : const nsACString& aName,
780 : const nsACString* aProfileName,
781 : const nsACString* aAppName,
782 : const nsACString* aVendorName,
783 : bool aForExternalApp,
784 : nsIToolkitProfile** aResult)
785 : {
786 0 : nsresult rv = NS_ERROR_FAILURE;
787 :
788 0 : if (!aForExternalApp) {
789 0 : rv = GetProfileByName(aName, aResult);
790 0 : if (NS_SUCCEEDED(rv)) {
791 0 : return rv;
792 : }
793 : }
794 :
795 0 : nsCOMPtr<nsIFile> rootDir (aRootDir);
796 :
797 0 : nsAutoCString dirName;
798 0 : if (!rootDir) {
799 0 : rv = gDirServiceProvider->GetUserProfilesRootDir(getter_AddRefs(rootDir),
800 : aProfileName, aAppName,
801 0 : aVendorName);
802 0 : NS_ENSURE_SUCCESS(rv, rv);
803 :
804 0 : dirName = aName;
805 0 : SaltProfileName(dirName);
806 :
807 0 : if (NS_IsNativeUTF8()) {
808 0 : rootDir->AppendNative(dirName);
809 : } else {
810 0 : rootDir->Append(NS_ConvertUTF8toUTF16(dirName));
811 : }
812 : }
813 :
814 0 : nsCOMPtr<nsIFile> localDir;
815 :
816 : bool isRelative;
817 0 : rv = mAppData->Contains(rootDir, &isRelative);
818 0 : if (NS_SUCCEEDED(rv) && isRelative) {
819 0 : nsAutoCString path;
820 0 : rv = rootDir->GetRelativeDescriptor(mAppData, path);
821 0 : NS_ENSURE_SUCCESS(rv, rv);
822 :
823 0 : rv = NS_NewNativeLocalFile(EmptyCString(), true,
824 0 : getter_AddRefs(localDir));
825 0 : NS_ENSURE_SUCCESS(rv, rv);
826 :
827 0 : rv = localDir->SetRelativeDescriptor(mTempData, path);
828 : } else {
829 0 : localDir = rootDir;
830 : }
831 :
832 : bool exists;
833 0 : rv = rootDir->Exists(&exists);
834 0 : NS_ENSURE_SUCCESS(rv, rv);
835 :
836 0 : if (exists) {
837 0 : rv = rootDir->IsDirectory(&exists);
838 0 : NS_ENSURE_SUCCESS(rv, rv);
839 :
840 0 : if (!exists)
841 0 : return NS_ERROR_FILE_NOT_DIRECTORY;
842 : }
843 : else {
844 0 : nsCOMPtr<nsIFile> profileDirParent;
845 0 : nsAutoString profileDirName;
846 :
847 0 : rv = rootDir->GetParent(getter_AddRefs(profileDirParent));
848 0 : NS_ENSURE_SUCCESS(rv, rv);
849 :
850 0 : rv = rootDir->GetLeafName(profileDirName);
851 0 : NS_ENSURE_SUCCESS(rv, rv);
852 :
853 : // let's ensure that the profile directory exists.
854 0 : rv = rootDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
855 0 : NS_ENSURE_SUCCESS(rv, rv);
856 0 : rv = rootDir->SetPermissions(0700);
857 : #ifndef ANDROID
858 : // If the profile is on the sdcard, this will fail but its non-fatal
859 0 : NS_ENSURE_SUCCESS(rv, rv);
860 : #endif
861 : }
862 :
863 0 : rv = localDir->Exists(&exists);
864 0 : NS_ENSURE_SUCCESS(rv, rv);
865 :
866 0 : if (!exists) {
867 0 : rv = localDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
868 0 : NS_ENSURE_SUCCESS(rv, rv);
869 : }
870 :
871 : // We created a new profile dir. Let's store a creation timestamp.
872 : // Note that this code path does not apply if the profile dir was
873 : // created prior to launching.
874 0 : rv = CreateTimesInternal(rootDir);
875 0 : NS_ENSURE_SUCCESS(rv, rv);
876 :
877 0 : nsToolkitProfile* last = aForExternalApp ? nullptr : mFirst.get();
878 0 : if (last) {
879 0 : while (last->mNext)
880 0 : last = last->mNext;
881 : }
882 :
883 : nsCOMPtr<nsIToolkitProfile> profile =
884 0 : new nsToolkitProfile(aName, rootDir, localDir, last, aForExternalApp);
885 0 : if (!profile) return NS_ERROR_OUT_OF_MEMORY;
886 :
887 0 : profile.forget(aResult);
888 0 : return NS_OK;
889 : }
890 :
891 : nsresult
892 0 : nsToolkitProfileService::CreateTimesInternal(nsIFile* aProfileDir)
893 : {
894 0 : nsresult rv = NS_ERROR_FAILURE;
895 0 : nsCOMPtr<nsIFile> creationLog;
896 0 : rv = aProfileDir->Clone(getter_AddRefs(creationLog));
897 0 : NS_ENSURE_SUCCESS(rv, rv);
898 :
899 0 : rv = creationLog->AppendNative(NS_LITERAL_CSTRING("times.json"));
900 0 : NS_ENSURE_SUCCESS(rv, rv);
901 :
902 0 : bool exists = false;
903 0 : creationLog->Exists(&exists);
904 0 : if (exists) {
905 0 : return NS_OK;
906 : }
907 :
908 0 : rv = creationLog->Create(nsIFile::NORMAL_FILE_TYPE, 0700);
909 0 : NS_ENSURE_SUCCESS(rv, rv);
910 :
911 : // We don't care about microsecond resolution.
912 0 : int64_t msec = PR_Now() / PR_USEC_PER_MSEC;
913 :
914 : // Write it out.
915 : PRFileDesc *writeFile;
916 0 : rv = creationLog->OpenNSPRFileDesc(PR_WRONLY, 0700, &writeFile);
917 0 : NS_ENSURE_SUCCESS(rv, rv);
918 :
919 0 : PR_fprintf(writeFile, "{\n\"created\": %lld\n}\n", msec);
920 0 : PR_Close(writeFile);
921 0 : return NS_OK;
922 : }
923 :
924 : NS_IMETHODIMP
925 0 : nsToolkitProfileService::GetProfileCount(uint32_t *aResult)
926 : {
927 0 : if (!mFirst)
928 0 : *aResult = 0;
929 0 : else if (! mFirst->mNext)
930 0 : *aResult = 1;
931 : else
932 0 : *aResult = 2;
933 :
934 0 : return NS_OK;
935 : }
936 :
937 : NS_IMETHODIMP
938 0 : nsToolkitProfileService::Flush()
939 : {
940 : // Errors during writing might cause unhappy semi-written files.
941 : // To avoid this, write the entire thing to a buffer, then write
942 : // that buffer to disk.
943 :
944 : nsresult rv;
945 0 : uint32_t pCount = 0;
946 : nsToolkitProfile *cur;
947 :
948 0 : for (cur = mFirst; cur != nullptr; cur = cur->mNext)
949 0 : ++pCount;
950 :
951 : uint32_t length;
952 0 : const int bufsize = 100+MAXPATHLEN*pCount;
953 0 : auto buffer = MakeUnique<char[]>(bufsize);
954 :
955 0 : char *pos = buffer.get();
956 0 : char *end = pos + bufsize;
957 :
958 0 : pos += snprintf(pos, end - pos,
959 : "[General]\n"
960 : "StartWithLastProfile=%s\n\n",
961 0 : mStartWithLast ? "1" : "0");
962 :
963 0 : nsAutoCString path;
964 0 : cur = mFirst;
965 0 : pCount = 0;
966 :
967 0 : while (cur) {
968 : // if the profile dir is relative to appdir...
969 : bool isRelative;
970 0 : rv = mAppData->Contains(cur->mRootDir, &isRelative);
971 0 : if (NS_SUCCEEDED(rv) && isRelative) {
972 : // we use a relative descriptor
973 0 : rv = cur->mRootDir->GetRelativeDescriptor(mAppData, path);
974 : } else {
975 : // otherwise, a persistent descriptor
976 0 : rv = cur->mRootDir->GetPersistentDescriptor(path);
977 0 : NS_ENSURE_SUCCESS(rv, rv);
978 : }
979 :
980 0 : pos += snprintf(pos, end - pos,
981 : "[Profile%u]\n"
982 : "Name=%s\n"
983 : "IsRelative=%s\n"
984 : "Path=%s\n",
985 : pCount, cur->mName.get(),
986 0 : isRelative ? "1" : "0", path.get());
987 :
988 0 : nsCOMPtr<nsIToolkitProfile> profile;
989 0 : rv = this->GetDefaultProfile(getter_AddRefs(profile));
990 0 : if (NS_SUCCEEDED(rv) && profile == cur) {
991 0 : pos += snprintf(pos, end - pos, "Default=1\n");
992 : }
993 :
994 0 : pos += snprintf(pos, end - pos, "\n");
995 :
996 0 : cur = cur->mNext;
997 0 : ++pCount;
998 : }
999 :
1000 : FILE* writeFile;
1001 0 : rv = mListFile->OpenANSIFileDesc("w", &writeFile);
1002 0 : NS_ENSURE_SUCCESS(rv, rv);
1003 :
1004 0 : length = pos - buffer.get();
1005 :
1006 0 : if (fwrite(buffer.get(), sizeof(char), length, writeFile) != length) {
1007 0 : fclose(writeFile);
1008 0 : return NS_ERROR_UNEXPECTED;
1009 : }
1010 :
1011 0 : fclose(writeFile);
1012 0 : return NS_OK;
1013 : }
1014 :
1015 12 : NS_IMPL_ISUPPORTS(nsToolkitProfileFactory, nsIFactory)
1016 :
1017 : NS_IMETHODIMP
1018 1 : nsToolkitProfileFactory::CreateInstance(nsISupports* aOuter, const nsID& aIID,
1019 : void** aResult)
1020 : {
1021 1 : if (aOuter)
1022 0 : return NS_ERROR_NO_AGGREGATION;
1023 :
1024 : nsCOMPtr<nsIToolkitProfileService> profileService =
1025 2 : nsToolkitProfileService::gService;
1026 1 : if (!profileService) {
1027 0 : nsresult rv = NS_NewToolkitProfileService(getter_AddRefs(profileService));
1028 0 : if (NS_FAILED(rv))
1029 0 : return rv;
1030 : }
1031 1 : return profileService->QueryInterface(aIID, aResult);
1032 : }
1033 :
1034 : NS_IMETHODIMP
1035 0 : nsToolkitProfileFactory::LockFactory(bool aVal)
1036 : {
1037 0 : return NS_OK;
1038 : }
1039 :
1040 : nsresult
1041 1 : NS_NewToolkitProfileFactory(nsIFactory* *aResult)
1042 : {
1043 1 : *aResult = new nsToolkitProfileFactory();
1044 1 : if (!*aResult)
1045 0 : return NS_ERROR_OUT_OF_MEMORY;
1046 :
1047 1 : NS_ADDREF(*aResult);
1048 1 : return NS_OK;
1049 : }
1050 :
1051 : nsresult
1052 1 : NS_NewToolkitProfileService(nsIToolkitProfileService* *aResult)
1053 : {
1054 1 : nsToolkitProfileService* profileService = new nsToolkitProfileService();
1055 1 : if (!profileService)
1056 0 : return NS_ERROR_OUT_OF_MEMORY;
1057 1 : nsresult rv = profileService->Init();
1058 1 : if (NS_FAILED(rv)) {
1059 0 : NS_ERROR("nsToolkitProfileService::Init failed!");
1060 0 : delete profileService;
1061 0 : return rv;
1062 : }
1063 :
1064 1 : NS_ADDREF(*aResult = profileService);
1065 1 : return NS_OK;
1066 : }
1067 :
1068 : nsresult
1069 3 : XRE_GetFileFromPath(const char *aPath, nsIFile* *aResult)
1070 : {
1071 : #if defined(XP_MACOSX)
1072 : int32_t pathLen = strlen(aPath);
1073 : if (pathLen > MAXPATHLEN)
1074 : return NS_ERROR_INVALID_ARG;
1075 :
1076 : CFURLRef fullPath =
1077 : CFURLCreateFromFileSystemRepresentation(nullptr, (const UInt8 *) aPath,
1078 : pathLen, true);
1079 : if (!fullPath)
1080 : return NS_ERROR_FAILURE;
1081 :
1082 : nsCOMPtr<nsIFile> lf;
1083 : nsresult rv = NS_NewNativeLocalFile(EmptyCString(), true,
1084 : getter_AddRefs(lf));
1085 : if (NS_SUCCEEDED(rv)) {
1086 : nsCOMPtr<nsILocalFileMac> lfMac = do_QueryInterface(lf, &rv);
1087 : if (NS_SUCCEEDED(rv)) {
1088 : rv = lfMac->InitWithCFURL(fullPath);
1089 : if (NS_SUCCEEDED(rv)) {
1090 : lf.forget(aResult);
1091 : }
1092 : }
1093 : }
1094 : CFRelease(fullPath);
1095 : return rv;
1096 :
1097 : #elif defined(XP_UNIX)
1098 : char fullPath[MAXPATHLEN];
1099 :
1100 3 : if (!realpath(aPath, fullPath))
1101 0 : return NS_ERROR_FAILURE;
1102 :
1103 6 : return NS_NewNativeLocalFile(nsDependentCString(fullPath), true,
1104 3 : aResult);
1105 : #elif defined(XP_WIN)
1106 : WCHAR fullPath[MAXPATHLEN];
1107 :
1108 : if (!_wfullpath(fullPath, NS_ConvertUTF8toUTF16(aPath).get(), MAXPATHLEN))
1109 : return NS_ERROR_FAILURE;
1110 :
1111 : return NS_NewLocalFile(nsDependentString(fullPath), true,
1112 : aResult);
1113 :
1114 : #else
1115 : #error Platform-specific logic needed here.
1116 : #endif
1117 : }
|