/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 | | } |