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