Coverage Report

Created: 2018-09-25 14:53

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