1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "nsAppFileLocationProvider.h"
8 #include "nsAppDirectoryServiceDefs.h"
9 #include "nsDirectoryServiceDefs.h"
10 #include "nsEnumeratorUtils.h"
11 #include "nsAtom.h"
12 #include "nsIFile.h"
13 #include "nsString.h"
14 #include "nsISimpleEnumerator.h"
15 #include "prenv.h"
16 #include "nsCRT.h"
17 #if defined(MOZ_WIDGET_COCOA)
18 #include <Carbon/Carbon.h>
19 #include "nsILocalFileMac.h"
20 #elif defined(XP_WIN)
21 #include <windows.h>
22 #include <shlobj.h>
23 #elif defined(XP_UNIX)
24 #include <unistd.h>
25 #include <stdlib.h>
26 #include <sys/param.h>
27 #endif
28
29 // WARNING: These hard coded names need to go away. They need to
30 // come from localizable resources
31
32 #if defined(MOZ_WIDGET_COCOA)
33 #define APP_REGISTRY_NAME NS_LITERAL_CSTRING("Application Registry")
34 #define ESSENTIAL_FILES NS_LITERAL_CSTRING("Essential Files")
35 #elif defined(XP_WIN)
36 #define APP_REGISTRY_NAME NS_LITERAL_CSTRING("registry.dat")
37 #else
38 #define APP_REGISTRY_NAME NS_LITERAL_CSTRING("appreg")
39 #endif
40
41 // define default product directory
42 #define DEFAULT_PRODUCT_DIR NS_LITERAL_CSTRING(MOZ_USER_DIR)
43
44 // Locally defined keys used by nsAppDirectoryEnumerator
45 #define NS_ENV_PLUGINS_DIR "EnvPlugins" // env var MOZ_PLUGIN_PATH
46 #define NS_USER_PLUGINS_DIR "UserPlugins"
47
48 #ifdef MOZ_WIDGET_COCOA
49 #define NS_MACOSX_USER_PLUGIN_DIR "OSXUserPlugins"
50 #define NS_MACOSX_LOCAL_PLUGIN_DIR "OSXLocalPlugins"
51 #elif XP_UNIX
52 #define NS_SYSTEM_PLUGINS_DIR "SysPlugins"
53 #endif
54
55 #define DEFAULTS_DIR_NAME NS_LITERAL_CSTRING("defaults")
56 #define DEFAULTS_PREF_DIR_NAME NS_LITERAL_CSTRING("pref")
57 #define RES_DIR_NAME NS_LITERAL_CSTRING("res")
58 #define CHROME_DIR_NAME NS_LITERAL_CSTRING("chrome")
59 #define PLUGINS_DIR_NAME NS_LITERAL_CSTRING("plugins")
60
61 //*****************************************************************************
62 // nsAppFileLocationProvider::Constructor/Destructor
63 //*****************************************************************************
64
nsAppFileLocationProvider()65 nsAppFileLocationProvider::nsAppFileLocationProvider() {}
66
67 //*****************************************************************************
68 // nsAppFileLocationProvider::nsISupports
69 //*****************************************************************************
70
NS_IMPL_ISUPPORTS(nsAppFileLocationProvider,nsIDirectoryServiceProvider,nsIDirectoryServiceProvider2)71 NS_IMPL_ISUPPORTS(nsAppFileLocationProvider, nsIDirectoryServiceProvider,
72 nsIDirectoryServiceProvider2)
73
74 //*****************************************************************************
75 // nsAppFileLocationProvider::nsIDirectoryServiceProvider
76 //*****************************************************************************
77
78 NS_IMETHODIMP
79 nsAppFileLocationProvider::GetFile(const char* aProp, bool* aPersistent,
80 nsIFile** aResult) {
81 if (NS_WARN_IF(!aProp)) {
82 return NS_ERROR_INVALID_ARG;
83 }
84
85 nsCOMPtr<nsIFile> localFile;
86 nsresult rv = NS_ERROR_FAILURE;
87
88 *aResult = nullptr;
89 *aPersistent = true;
90
91 #ifdef MOZ_WIDGET_COCOA
92 FSRef fileRef;
93 nsCOMPtr<nsILocalFileMac> macFile;
94 #endif
95
96 if (nsCRT::strcmp(aProp, NS_APP_APPLICATION_REGISTRY_DIR) == 0) {
97 rv = GetProductDirectory(getter_AddRefs(localFile));
98 } else if (nsCRT::strcmp(aProp, NS_APP_APPLICATION_REGISTRY_FILE) == 0) {
99 rv = GetProductDirectory(getter_AddRefs(localFile));
100 if (NS_SUCCEEDED(rv)) {
101 rv = localFile->AppendNative(APP_REGISTRY_NAME);
102 }
103 } else if (nsCRT::strcmp(aProp, NS_APP_DEFAULTS_50_DIR) == 0) {
104 rv = CloneMozBinDirectory(getter_AddRefs(localFile));
105 if (NS_SUCCEEDED(rv)) {
106 rv = localFile->AppendRelativeNativePath(DEFAULTS_DIR_NAME);
107 }
108 } else if (nsCRT::strcmp(aProp, NS_APP_PREF_DEFAULTS_50_DIR) == 0) {
109 rv = CloneMozBinDirectory(getter_AddRefs(localFile));
110 if (NS_SUCCEEDED(rv)) {
111 rv = localFile->AppendRelativeNativePath(DEFAULTS_DIR_NAME);
112 if (NS_SUCCEEDED(rv)) {
113 rv = localFile->AppendRelativeNativePath(DEFAULTS_PREF_DIR_NAME);
114 }
115 }
116 } else if (nsCRT::strcmp(aProp, NS_APP_USER_PROFILES_ROOT_DIR) == 0) {
117 rv = GetDefaultUserProfileRoot(getter_AddRefs(localFile));
118 } else if (nsCRT::strcmp(aProp, NS_APP_USER_PROFILES_LOCAL_ROOT_DIR) == 0) {
119 rv = GetDefaultUserProfileRoot(getter_AddRefs(localFile), true);
120 } else if (nsCRT::strcmp(aProp, NS_APP_RES_DIR) == 0) {
121 rv = CloneMozBinDirectory(getter_AddRefs(localFile));
122 if (NS_SUCCEEDED(rv)) {
123 rv = localFile->AppendRelativeNativePath(RES_DIR_NAME);
124 }
125 } else if (nsCRT::strcmp(aProp, NS_APP_CHROME_DIR) == 0) {
126 rv = CloneMozBinDirectory(getter_AddRefs(localFile));
127 if (NS_SUCCEEDED(rv)) {
128 rv = localFile->AppendRelativeNativePath(CHROME_DIR_NAME);
129 }
130 } else if (nsCRT::strcmp(aProp, NS_APP_PLUGINS_DIR) == 0) {
131 rv = CloneMozBinDirectory(getter_AddRefs(localFile));
132 if (NS_SUCCEEDED(rv)) {
133 rv = localFile->AppendRelativeNativePath(PLUGINS_DIR_NAME);
134 }
135 }
136 #ifdef MOZ_WIDGET_COCOA
137 else if (nsCRT::strcmp(aProp, NS_MACOSX_USER_PLUGIN_DIR) == 0) {
138 if (::FSFindFolder(kUserDomain, kInternetPlugInFolderType, false,
139 &fileRef) == noErr) {
140 rv = NS_NewLocalFileWithFSRef(&fileRef, true, getter_AddRefs(macFile));
141 if (NS_SUCCEEDED(rv)) {
142 localFile = macFile;
143 }
144 }
145 } else if (nsCRT::strcmp(aProp, NS_MACOSX_LOCAL_PLUGIN_DIR) == 0) {
146 if (::FSFindFolder(kLocalDomain, kInternetPlugInFolderType, false,
147 &fileRef) == noErr) {
148 rv = NS_NewLocalFileWithFSRef(&fileRef, true, getter_AddRefs(macFile));
149 if (NS_SUCCEEDED(rv)) {
150 localFile = macFile;
151 }
152 }
153 }
154 #else
155 else if (nsCRT::strcmp(aProp, NS_ENV_PLUGINS_DIR) == 0) {
156 NS_ERROR(
157 "Don't use nsAppFileLocationProvider::GetFile(NS_ENV_PLUGINS_DIR, "
158 "...). "
159 "Use nsAppFileLocationProvider::GetFiles(...).");
160 const char* pathVar = PR_GetEnv("MOZ_PLUGIN_PATH");
161 if (pathVar && *pathVar)
162 rv = NS_NewNativeLocalFile(nsDependentCString(pathVar), true,
163 getter_AddRefs(localFile));
164 } else if (nsCRT::strcmp(aProp, NS_USER_PLUGINS_DIR) == 0) {
165 #ifdef ENABLE_SYSTEM_EXTENSION_DIRS
166 rv = GetProductDirectory(getter_AddRefs(localFile));
167 if (NS_SUCCEEDED(rv)) {
168 rv = localFile->AppendRelativeNativePath(PLUGINS_DIR_NAME);
169 }
170 #else
171 rv = NS_ERROR_FAILURE;
172 #endif
173 }
174 #ifdef XP_UNIX
175 else if (nsCRT::strcmp(aProp, NS_SYSTEM_PLUGINS_DIR) == 0) {
176 #ifdef ENABLE_SYSTEM_EXTENSION_DIRS
177 static const char* const sysLPlgDir =
178 #if defined(HAVE_USR_LIB64_DIR) && defined(__LP64__)
179 "/usr/lib64/mozilla/plugins";
180 #elif defined(__OpenBSD__) || defined(__FreeBSD__)
181 "/usr/local/lib/mozilla/plugins";
182 #else
183 "/usr/lib/mozilla/plugins";
184 #endif
185 rv = NS_NewNativeLocalFile(nsDependentCString(sysLPlgDir), false,
186 getter_AddRefs(localFile));
187 #else
188 rv = NS_ERROR_FAILURE;
189 #endif
190 }
191 #endif
192 #endif
193 else if (nsCRT::strcmp(aProp, NS_APP_INSTALL_CLEANUP_DIR) == 0) {
194 // This is cloned so that embeddors will have a hook to override
195 // with their own cleanup dir. See bugzilla bug #105087
196 rv = CloneMozBinDirectory(getter_AddRefs(localFile));
197 }
198
199 if (localFile && NS_SUCCEEDED(rv)) {
200 localFile.forget(aResult);
201 return NS_OK;
202 }
203
204 return rv;
205 }
206
CloneMozBinDirectory(nsIFile ** aLocalFile)207 nsresult nsAppFileLocationProvider::CloneMozBinDirectory(nsIFile** aLocalFile) {
208 if (NS_WARN_IF(!aLocalFile)) {
209 return NS_ERROR_INVALID_ARG;
210 }
211 nsresult rv;
212
213 if (!mMozBinDirectory) {
214 // Get the mozilla bin directory
215 // 1. Check the directory service first for NS_XPCOM_CURRENT_PROCESS_DIR
216 // This will be set if a directory was passed to NS_InitXPCOM
217 // 2. If that doesn't work, set it to be the current process directory
218 nsCOMPtr<nsIProperties> directoryService(
219 do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv));
220 if (NS_FAILED(rv)) {
221 return rv;
222 }
223
224 rv =
225 directoryService->Get(NS_XPCOM_CURRENT_PROCESS_DIR, NS_GET_IID(nsIFile),
226 getter_AddRefs(mMozBinDirectory));
227 if (NS_FAILED(rv)) {
228 rv = directoryService->Get(NS_OS_CURRENT_PROCESS_DIR, NS_GET_IID(nsIFile),
229 getter_AddRefs(mMozBinDirectory));
230 if (NS_FAILED(rv)) {
231 return rv;
232 }
233 }
234 }
235
236 nsCOMPtr<nsIFile> aFile;
237 rv = mMozBinDirectory->Clone(getter_AddRefs(aFile));
238 if (NS_FAILED(rv)) {
239 return rv;
240 }
241
242 NS_IF_ADDREF(*aLocalFile = aFile);
243 return NS_OK;
244 }
245
246 //----------------------------------------------------------------------------------------
247 // GetProductDirectory - Gets the directory which contains the application data
248 // folder
249 //
250 // UNIX : ~/.mozilla/
251 // WIN : <Application Data folder on user's machine>\Mozilla
252 // Mac : :Documents:Mozilla:
253 //----------------------------------------------------------------------------------------
GetProductDirectory(nsIFile ** aLocalFile,bool aLocal)254 nsresult nsAppFileLocationProvider::GetProductDirectory(nsIFile** aLocalFile,
255 bool aLocal) {
256 if (NS_WARN_IF(!aLocalFile)) {
257 return NS_ERROR_INVALID_ARG;
258 }
259
260 nsresult rv;
261 bool exists;
262 nsCOMPtr<nsIFile> localDir;
263
264 #if defined(MOZ_WIDGET_COCOA)
265 FSRef fsRef;
266 OSType folderType =
267 aLocal ? (OSType)kCachedDataFolderType : (OSType)kDomainLibraryFolderType;
268 OSErr err = ::FSFindFolder(kUserDomain, folderType, kCreateFolder, &fsRef);
269 if (err) {
270 return NS_ERROR_FAILURE;
271 }
272 NS_NewLocalFile(EmptyString(), true, getter_AddRefs(localDir));
273 if (!localDir) {
274 return NS_ERROR_FAILURE;
275 }
276 nsCOMPtr<nsILocalFileMac> localDirMac(do_QueryInterface(localDir));
277 rv = localDirMac->InitWithFSRef(&fsRef);
278 if (NS_FAILED(rv)) {
279 return rv;
280 }
281 #elif defined(XP_WIN)
282 nsCOMPtr<nsIProperties> directoryService =
283 do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
284 if (NS_FAILED(rv)) {
285 return rv;
286 }
287 const char* prop = aLocal ? NS_WIN_LOCAL_APPDATA_DIR : NS_WIN_APPDATA_DIR;
288 rv = directoryService->Get(prop, NS_GET_IID(nsIFile),
289 getter_AddRefs(localDir));
290 if (NS_FAILED(rv)) {
291 return rv;
292 }
293 #elif defined(XP_UNIX)
294 rv = NS_NewNativeLocalFile(nsDependentCString(PR_GetEnv("HOME")), true,
295 getter_AddRefs(localDir));
296 if (NS_FAILED(rv)) {
297 return rv;
298 }
299 #else
300 #error dont_know_how_to_get_product_dir_on_your_platform
301 #endif
302
303 rv = localDir->AppendRelativeNativePath(DEFAULT_PRODUCT_DIR);
304 if (NS_FAILED(rv)) {
305 return rv;
306 }
307 rv = localDir->Exists(&exists);
308
309 if (NS_SUCCEEDED(rv) && !exists) {
310 rv = localDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
311 }
312
313 if (NS_FAILED(rv)) {
314 return rv;
315 }
316
317 localDir.forget(aLocalFile);
318
319 return rv;
320 }
321
322 //----------------------------------------------------------------------------------------
323 // GetDefaultUserProfileRoot - Gets the directory which contains each user
324 // profile dir
325 //
326 // UNIX : ~/.mozilla/
327 // WIN : <Application Data folder on user's machine>\Mozilla\Profiles
328 // Mac : :Documents:Mozilla:Profiles:
329 //----------------------------------------------------------------------------------------
GetDefaultUserProfileRoot(nsIFile ** aLocalFile,bool aLocal)330 nsresult nsAppFileLocationProvider::GetDefaultUserProfileRoot(
331 nsIFile** aLocalFile, bool aLocal) {
332 if (NS_WARN_IF(!aLocalFile)) {
333 return NS_ERROR_INVALID_ARG;
334 }
335
336 nsresult rv;
337 nsCOMPtr<nsIFile> localDir;
338
339 rv = GetProductDirectory(getter_AddRefs(localDir), aLocal);
340 if (NS_FAILED(rv)) {
341 return rv;
342 }
343
344 #if defined(MOZ_WIDGET_COCOA) || defined(XP_WIN)
345 // These 3 platforms share this part of the path - do them as one
346 rv = localDir->AppendRelativeNativePath(NS_LITERAL_CSTRING("Profiles"));
347 if (NS_FAILED(rv)) {
348 return rv;
349 }
350
351 bool exists;
352 rv = localDir->Exists(&exists);
353 if (NS_SUCCEEDED(rv) && !exists) {
354 rv = localDir->Create(nsIFile::DIRECTORY_TYPE, 0775);
355 }
356 if (NS_FAILED(rv)) {
357 return rv;
358 }
359 #endif
360
361 localDir.forget(aLocalFile);
362
363 return rv;
364 }
365
366 //*****************************************************************************
367 // nsAppFileLocationProvider::nsIDirectoryServiceProvider2
368 //*****************************************************************************
369
370 class nsAppDirectoryEnumerator : public nsISimpleEnumerator {
371 public:
372 NS_DECL_ISUPPORTS
373
374 /**
375 * aKeyList is a null-terminated list of properties which are provided by
376 * aProvider They do not need to be publicly defined keys.
377 */
nsAppDirectoryEnumerator(nsIDirectoryServiceProvider * aProvider,const char * aKeyList[])378 nsAppDirectoryEnumerator(nsIDirectoryServiceProvider* aProvider,
379 const char* aKeyList[])
380 : mProvider(aProvider), mCurrentKey(aKeyList) {}
381
HasMoreElements(bool * aResult)382 NS_IMETHOD HasMoreElements(bool* aResult) override {
383 while (!mNext && *mCurrentKey) {
384 bool dontCare;
385 nsCOMPtr<nsIFile> testFile;
386 (void)mProvider->GetFile(*mCurrentKey++, &dontCare,
387 getter_AddRefs(testFile));
388 // Don't return a file which does not exist.
389 bool exists;
390 if (testFile && NS_SUCCEEDED(testFile->Exists(&exists)) && exists) {
391 mNext = testFile;
392 }
393 }
394 *aResult = mNext != nullptr;
395 return NS_OK;
396 }
397
GetNext(nsISupports ** aResult)398 NS_IMETHOD GetNext(nsISupports** aResult) override {
399 if (NS_WARN_IF(!aResult)) {
400 return NS_ERROR_INVALID_ARG;
401 }
402 *aResult = nullptr;
403
404 bool hasMore;
405 HasMoreElements(&hasMore);
406 if (!hasMore) {
407 return NS_ERROR_FAILURE;
408 }
409
410 *aResult = mNext;
411 NS_IF_ADDREF(*aResult);
412 mNext = nullptr;
413
414 return *aResult ? NS_OK : NS_ERROR_FAILURE;
415 }
416
417 protected:
418 nsCOMPtr<nsIDirectoryServiceProvider> mProvider;
419 const char** mCurrentKey;
420 nsCOMPtr<nsIFile> mNext;
421
422 // Virtual destructor since subclass nsPathsDirectoryEnumerator
423 // does not re-implement Release()
~nsAppDirectoryEnumerator()424 virtual ~nsAppDirectoryEnumerator() {}
425 };
426
427 NS_IMPL_ISUPPORTS(nsAppDirectoryEnumerator, nsISimpleEnumerator)
428
429 /* nsPathsDirectoryEnumerator and PATH_SEPARATOR
430 * are not used on MacOS/X. */
431
432 #if defined(XP_WIN) /* Win32 */
433 #define PATH_SEPARATOR ';'
434 #else
435 #define PATH_SEPARATOR ':'
436 #endif
437
438 class nsPathsDirectoryEnumerator final : public nsAppDirectoryEnumerator {
~nsPathsDirectoryEnumerator()439 ~nsPathsDirectoryEnumerator() {}
440
441 public:
442 /**
443 * aKeyList is a null-terminated list.
444 * The first element is a path list.
445 * The remainder are properties provided by aProvider.
446 * They do not need to be publicly defined keys.
447 */
nsPathsDirectoryEnumerator(nsIDirectoryServiceProvider * aProvider,const char * aKeyList[])448 nsPathsDirectoryEnumerator(nsIDirectoryServiceProvider* aProvider,
449 const char* aKeyList[])
450 : nsAppDirectoryEnumerator(aProvider, aKeyList + 1),
451 mEndPath(aKeyList[0]) {}
452
HasMoreElements(bool * aResult)453 NS_IMETHOD HasMoreElements(bool* aResult) override {
454 if (mEndPath)
455 while (!mNext && *mEndPath) {
456 const char* pathVar = mEndPath;
457
458 // skip PATH_SEPARATORs at the begining of the mEndPath
459 while (*pathVar == PATH_SEPARATOR) {
460 ++pathVar;
461 }
462
463 do {
464 ++mEndPath;
465 } while (*mEndPath && *mEndPath != PATH_SEPARATOR);
466
467 nsCOMPtr<nsIFile> localFile;
468 NS_NewNativeLocalFile(Substring(pathVar, mEndPath), true,
469 getter_AddRefs(localFile));
470 if (*mEndPath == PATH_SEPARATOR) {
471 ++mEndPath;
472 }
473 // Don't return a "file" (directory) which does not exist.
474 bool exists;
475 if (localFile && NS_SUCCEEDED(localFile->Exists(&exists)) && exists) {
476 mNext = localFile;
477 }
478 }
479 if (mNext) {
480 *aResult = true;
481 } else {
482 nsAppDirectoryEnumerator::HasMoreElements(aResult);
483 }
484
485 return NS_OK;
486 }
487
488 protected:
489 const char* mEndPath;
490 };
491
492 NS_IMETHODIMP
GetFiles(const char * aProp,nsISimpleEnumerator ** aResult)493 nsAppFileLocationProvider::GetFiles(const char* aProp,
494 nsISimpleEnumerator** aResult) {
495 if (NS_WARN_IF(!aResult)) {
496 return NS_ERROR_INVALID_ARG;
497 }
498 *aResult = nullptr;
499 nsresult rv = NS_ERROR_FAILURE;
500
501 if (!nsCRT::strcmp(aProp, NS_APP_PLUGINS_DIR_LIST)) {
502 #ifdef MOZ_WIDGET_COCOA
503 static const char* keys[] = {NS_APP_PLUGINS_DIR, NS_MACOSX_USER_PLUGIN_DIR,
504 NS_MACOSX_LOCAL_PLUGIN_DIR, nullptr};
505 *aResult = new nsAppDirectoryEnumerator(this, keys);
506 #else
507 #ifdef XP_UNIX
508 static const char* keys[] = {nullptr, NS_USER_PLUGINS_DIR,
509 NS_APP_PLUGINS_DIR, NS_SYSTEM_PLUGINS_DIR,
510 nullptr};
511 #else
512 static const char* keys[] = {nullptr, NS_USER_PLUGINS_DIR,
513 NS_APP_PLUGINS_DIR, nullptr};
514 #endif
515 if (!keys[0] && !(keys[0] = PR_GetEnv("MOZ_PLUGIN_PATH"))) {
516 static const char nullstr = 0;
517 keys[0] = &nullstr;
518 }
519 *aResult = new nsPathsDirectoryEnumerator(this, keys);
520 #endif
521 NS_ADDREF(*aResult);
522 rv = NS_OK;
523 }
524 if (!strcmp(aProp, NS_APP_DISTRIBUTION_SEARCH_DIR_LIST)) {
525 return NS_NewEmptyEnumerator(aResult);
526 }
527 return rv;
528 }
529