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/. */
7 #include "SpecialSystemDirectory.h"
9 #include "nsDependentString.h"
10 #include "nsAutoPtr.h"
21 #include <knownfolders.h>
23 #include "mozilla/WindowsVersion.h"
25 using mozilla::IsWin7OrLater
;
27 #elif defined(XP_UNIX)
32 #include <sys/param.h>
43 #define MAXPATHLEN PATH_MAX
44 #elif defined(MAX_PATH)
45 #define MAXPATHLEN MAX_PATH
46 #elif defined(_MAX_PATH)
47 #define MAXPATHLEN _MAX_PATH
48 #elif defined(CCHMAXPATH)
49 #define MAXPATHLEN CCHMAXPATH
51 #define MAXPATHLEN 1024
56 typedef HRESULT (WINAPI
* nsGetKnownFolderPath
)(GUID
& rfid
,
61 static nsGetKnownFolderPath gGetKnownFolderPath
= nullptr;
65 StartupSpecialSystemDirectory()
68 // SHGetKnownFolderPath is only available on Windows Vista
69 // so that we need to use GetProcAddress to get the pointer.
70 HMODULE hShell32DLLInst
= GetModuleHandleW(L
"shell32.dll");
71 if (hShell32DLLInst
) {
72 gGetKnownFolderPath
= (nsGetKnownFolderPath
)
73 GetProcAddress(hShell32DLLInst
, "SHGetKnownFolderPath");
81 GetKnownFolder(GUID
* aGuid
, nsIFile
** aFile
)
83 if (!aGuid
|| !gGetKnownFolderPath
) {
84 return NS_ERROR_FAILURE
;
88 gGetKnownFolderPath(*aGuid
, 0, nullptr, &path
);
91 return NS_ERROR_FAILURE
;
94 nsresult rv
= NS_NewLocalFile(nsDependentString(path
),
103 GetWindowsFolder(int aFolder
, nsIFile
** aFile
)
105 WCHAR path_orig
[MAX_PATH
+ 3];
106 WCHAR
* path
= path_orig
+ 1;
107 HRESULT result
= SHGetSpecialFolderPathW(nullptr, path
, aFolder
, true);
109 if (!SUCCEEDED(result
)) {
110 return NS_ERROR_FAILURE
;
113 // Append the trailing slash
114 int len
= wcslen(path
);
115 if (len
> 1 && path
[len
- 1] != L
'\\') {
120 return NS_NewLocalFile(nsDependentString(path
, len
), true, aFile
);
124 SHLoadLibraryFromKnownFolder(REFKNOWNFOLDERID aFolderId
, DWORD aMode
,
125 REFIID riid
, void** ppv
)
129 HRESULT hr
= CoCreateInstance(CLSID_ShellLibrary
, nullptr,
130 CLSCTX_INPROC_SERVER
,
131 IID_PPV_ARGS(&plib
));
133 hr
= plib
->LoadLibraryFromKnownFolder(aFolderId
, aMode
);
135 hr
= plib
->QueryInterface(riid
, ppv
);
143 * Check to see if we're on Win7 and up, and if so, returns the default
144 * save-to location for the Windows Library passed in through aFolderId.
145 * Otherwise falls back on pre-win7 GetWindowsFolder.
148 GetLibrarySaveToPath(int aFallbackFolderId
, REFKNOWNFOLDERID aFolderId
,
151 // Skip off checking for library support if the os is Vista or lower.
152 if (!IsWin7OrLater()) {
153 return GetWindowsFolder(aFallbackFolderId
, aFile
);
156 nsRefPtr
<IShellLibrary
> shellLib
;
157 nsRefPtr
<IShellItem
> savePath
;
159 SHLoadLibraryFromKnownFolder(aFolderId
, STGM_READ
,
160 IID_IShellLibrary
, getter_AddRefs(shellLib
));
163 SUCCEEDED(shellLib
->GetDefaultSaveFolder(DSFT_DETECT
, IID_IShellItem
,
164 getter_AddRefs(savePath
)))) {
165 wchar_t* str
= nullptr;
166 if (SUCCEEDED(savePath
->GetDisplayName(SIGDN_FILESYSPATH
, &str
))) {
171 NS_NewLocalFile(path
, false, aFile
);
177 return GetWindowsFolder(aFallbackFolderId
, aFile
);
181 * Provides a fallback for getting the path to APPDATA or LOCALAPPDATA by
182 * querying the registry when the call to SHGetSpecialFolderPathW is unable to
183 * provide these paths (Bug 513958).
186 GetRegWindowsAppDataFolder(bool aLocal
, nsIFile
** aFile
)
189 NS_NAMED_LITERAL_STRING(keyName
,
190 "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders");
191 DWORD res
= ::RegOpenKeyExW(HKEY_CURRENT_USER
, keyName
.get(), 0, KEY_READ
,
193 if (res
!= ERROR_SUCCESS
) {
194 return NS_ERROR_FAILURE
;
197 WCHAR path
[MAX_PATH
+ 2];
199 res
= RegQueryValueExW(key
, (aLocal
? L
"Local AppData" : L
"AppData"),
200 nullptr, &type
, (LPBYTE
)&path
, &size
);
202 // The call to RegQueryValueExW must succeed, the type must be REG_SZ, the
203 // buffer size must not equal 0, and the buffer size be a multiple of 2.
204 if (res
!= ERROR_SUCCESS
|| type
!= REG_SZ
|| size
== 0 || size
% 2 != 0) {
205 return NS_ERROR_FAILURE
;
208 // Append the trailing slash
209 int len
= wcslen(path
);
210 if (len
> 1 && path
[len
- 1] != L
'\\') {
215 return NS_NewLocalFile(nsDependentString(path
, len
), true, aFile
);
222 GetUnixHomeDir(nsIFile
** aFile
)
226 pHome
= getenv("HOME");
228 return NS_NewNativeLocalFile(nsDependentCString(pHome
),
232 return NS_NewNativeLocalFile(nsDependentCString(decc$
translate_vms(pHome
)),
236 #elif defined(ANDROID)
237 // XXX no home dir on android; maybe we should return the sdcard if present?
238 return NS_ERROR_FAILURE
;
240 return NS_NewNativeLocalFile(nsDependentCString(PR_GetEnv("HOME")),
246 The following license applies to the xdg_user_dir_lookup function:
248 Copyright (c) 2007 Red Hat, Inc.
250 Permission is hereby granted, free of charge, to any person
251 obtaining a copy of this software and associated documentation files
252 (the "Software"), to deal in the Software without restriction,
253 including without limitation the rights to use, copy, modify, merge,
254 publish, distribute, sublicense, and/or sell copies of the Software,
255 and to permit persons to whom the Software is furnished to do so,
256 subject to the following conditions:
258 The above copyright notice and this permission notice shall be
259 included in all copies or substantial portions of the Software.
261 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
262 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
263 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
264 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
265 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
266 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
267 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
272 xdg_user_dir_lookup(const char* aType
)
285 home_dir
= getenv("HOME");
291 config_home
= getenv("XDG_CONFIG_HOME");
292 if (!config_home
|| config_home
[0] == 0) {
293 config_file
= (char*)malloc(strlen(home_dir
) +
294 strlen("/.config/user-dirs.dirs") + 1);
299 strcpy(config_file
, home_dir
);
300 strcat(config_file
, "/.config/user-dirs.dirs");
302 config_file
= (char*)malloc(strlen(config_home
) +
303 strlen("/user-dirs.dirs") + 1);
308 strcpy(config_file
, config_home
);
309 strcat(config_file
, "/user-dirs.dirs");
312 file
= fopen(config_file
, "r");
319 while (fgets(buffer
, sizeof(buffer
), file
)) {
320 /* Remove newline at end */
321 len
= strlen(buffer
);
322 if (len
> 0 && buffer
[len
- 1] == '\n') {
327 while (*p
== ' ' || *p
== '\t') {
331 if (strncmp(p
, "XDG_", 4) != 0) {
335 if (strncmp(p
, aType
, strlen(aType
)) != 0) {
339 if (strncmp(p
, "_DIR", 4) != 0) {
344 while (*p
== ' ' || *p
== '\t') {
353 while (*p
== ' ' || *p
== '\t') {
363 if (strncmp(p
, "$HOME/", 6) == 0) {
366 } else if (*p
!= '/') {
371 user_dir
= (char*)malloc(strlen(home_dir
) + 1 + strlen(p
) + 1);
376 strcpy(user_dir
, home_dir
);
377 strcat(user_dir
, "/");
379 user_dir
= (char*)malloc(strlen(p
) + 1);
387 d
= user_dir
+ strlen(user_dir
);
388 while (*p
&& *p
!= '"') {
389 if ((*p
== '\\') && (*(p
+ 1) != 0)) {
407 static const char xdg_user_dirs
[] =
417 static const uint8_t xdg_user_dir_offsets
[] = {
429 GetUnixXDGUserDirectory(SystemDirectories aSystemDirectory
,
432 char* dir
= xdg_user_dir_lookup(
433 xdg_user_dirs
+ xdg_user_dir_offsets
[aSystemDirectory
- Unix_XDG_Desktop
]);
436 nsCOMPtr
<nsIFile
> file
;
438 rv
= NS_NewNativeLocalFile(nsDependentCString(dir
), true,
439 getter_AddRefs(file
));
441 } else if (Unix_XDG_Desktop
== aSystemDirectory
) {
442 // for the XDG desktop dir, fall back to HOME/Desktop
443 // (for historical compatibility)
444 rv
= GetUnixHomeDir(getter_AddRefs(file
));
449 rv
= file
->AppendNative(NS_LITERAL_CSTRING("Desktop"));
451 // no fallback for the other XDG dirs
452 rv
= NS_ERROR_FAILURE
;
460 rv
= file
->Exists(&exists
);
465 rv
= file
->Create(nsIFile::DIRECTORY_TYPE
, 0755);
479 GetSpecialSystemDirectory(SystemDirectories aSystemSystemDirectory
,
483 WCHAR path
[MAX_PATH
];
485 char path
[MAXPATHLEN
];
488 switch (aSystemSystemDirectory
) {
489 case OS_CurrentWorkingDirectory
:
491 if (!_wgetcwd(path
, MAX_PATH
)) {
492 return NS_ERROR_FAILURE
;
494 return NS_NewLocalFile(nsDependentString(path
),
498 if (!getcwd(path
, MAXPATHLEN
)) {
499 return NS_ERROR_FAILURE
;
504 return NS_NewNativeLocalFile(nsDependentCString(path
),
509 case OS_DriveDirectory
:
512 int32_t len
= ::GetWindowsDirectoryW(path
, MAX_PATH
);
516 if (path
[1] == char16_t(':') && path
[2] == char16_t('\\')) {
520 return NS_NewLocalFile(nsDependentString(path
),
525 return NS_NewNativeLocalFile(nsDependentCString("/"),
531 case OS_TemporaryDirectory
:
534 DWORD len
= ::GetTempPathW(MAX_PATH
, path
);
538 return NS_NewLocalFile(nsDependentString(path
, len
),
542 #elif defined(MOZ_WIDGET_COCOA)
544 return GetOSXFolderType(kUserDomain
, kTemporaryFolderType
, aFile
);
547 #elif defined(XP_UNIX)
549 static const char* tPath
= nullptr;
551 tPath
= PR_GetEnv("TMPDIR");
552 if (!tPath
|| !*tPath
) {
553 tPath
= PR_GetEnv("TMP");
554 if (!tPath
|| !*tPath
) {
555 tPath
= PR_GetEnv("TEMP");
556 if (!tPath
|| !*tPath
) {
562 return NS_NewNativeLocalFile(nsDependentCString(tPath
),
570 case Win_SystemDirectory
: {
571 int32_t len
= ::GetSystemDirectoryW(path
, MAX_PATH
);
573 // Need enough space to add the trailing backslash
574 if (!len
|| len
> MAX_PATH
- 2) {
580 return NS_NewLocalFile(nsDependentString(path
, len
),
585 case Win_WindowsDirectory
: {
586 int32_t len
= ::GetWindowsDirectoryW(path
, MAX_PATH
);
588 // Need enough space to add the trailing backslash
589 if (!len
|| len
> MAX_PATH
- 2) {
596 return NS_NewLocalFile(nsDependentString(path
, len
),
601 case Win_ProgramFiles
: {
602 return GetWindowsFolder(CSIDL_PROGRAM_FILES
, aFile
);
605 case Win_HomeDirectory
: {
606 nsresult rv
= GetWindowsFolder(CSIDL_PROFILE
, aFile
);
607 if (NS_SUCCEEDED(rv
)) {
612 if ((len
= ::GetEnvironmentVariableW(L
"HOME", path
, MAX_PATH
)) > 0) {
613 // Need enough space to add the trailing backslash
614 if (len
> MAX_PATH
- 2) {
621 rv
= NS_NewLocalFile(nsDependentString(path
, len
),
624 if (NS_SUCCEEDED(rv
)) {
629 len
= ::GetEnvironmentVariableW(L
"HOMEDRIVE", path
, MAX_PATH
);
630 if (0 < len
&& len
< MAX_PATH
) {
631 WCHAR temp
[MAX_PATH
];
632 DWORD len2
= ::GetEnvironmentVariableW(L
"HOMEPATH", temp
, MAX_PATH
);
633 if (0 < len2
&& len
+ len2
< MAX_PATH
) {
634 wcsncat(path
, temp
, len2
);
639 // Need enough space to add the trailing backslash
640 if (len
> MAX_PATH
- 2) {
647 return NS_NewLocalFile(nsDependentString(path
, len
),
653 return GetWindowsFolder(CSIDL_DESKTOP
, aFile
);
656 return GetWindowsFolder(CSIDL_PROGRAMS
, aFile
);
659 case Win_Downloads
: {
660 // Defined in KnownFolders.h.
661 GUID folderid_downloads
= {
662 0x374de290, 0x123f, 0x4565,
663 { 0x91, 0x64, 0x39, 0xc4, 0x92, 0x5e, 0x46, 0x7b }
665 nsresult rv
= GetKnownFolder(&folderid_downloads
, aFile
);
666 // On WinXP, there is no downloads folder, default
668 if (NS_ERROR_FAILURE
== rv
) {
669 rv
= GetWindowsFolder(CSIDL_DESKTOP
, aFile
);
675 return GetWindowsFolder(CSIDL_CONTROLS
, aFile
);
678 return GetWindowsFolder(CSIDL_PRINTERS
, aFile
);
681 return GetWindowsFolder(CSIDL_PERSONAL
, aFile
);
683 case Win_Favorites
: {
684 return GetWindowsFolder(CSIDL_FAVORITES
, aFile
);
687 return GetWindowsFolder(CSIDL_STARTUP
, aFile
);
690 return GetWindowsFolder(CSIDL_RECENT
, aFile
);
693 return GetWindowsFolder(CSIDL_SENDTO
, aFile
);
695 case Win_Bitbucket
: {
696 return GetWindowsFolder(CSIDL_BITBUCKET
, aFile
);
698 case Win_Startmenu
: {
699 return GetWindowsFolder(CSIDL_STARTMENU
, aFile
);
701 case Win_Desktopdirectory
: {
702 return GetWindowsFolder(CSIDL_DESKTOPDIRECTORY
, aFile
);
705 return GetWindowsFolder(CSIDL_DRIVES
, aFile
);
708 return GetWindowsFolder(CSIDL_NETWORK
, aFile
);
711 return GetWindowsFolder(CSIDL_NETHOOD
, aFile
);
714 return GetWindowsFolder(CSIDL_FONTS
, aFile
);
716 case Win_Templates
: {
717 return GetWindowsFolder(CSIDL_TEMPLATES
, aFile
);
719 case Win_Common_Startmenu
: {
720 return GetWindowsFolder(CSIDL_COMMON_STARTMENU
, aFile
);
722 case Win_Common_Programs
: {
723 return GetWindowsFolder(CSIDL_COMMON_PROGRAMS
, aFile
);
725 case Win_Common_Startup
: {
726 return GetWindowsFolder(CSIDL_COMMON_STARTUP
, aFile
);
728 case Win_Common_Desktopdirectory
: {
729 return GetWindowsFolder(CSIDL_COMMON_DESKTOPDIRECTORY
, aFile
);
731 case Win_Common_AppData
: {
732 return GetWindowsFolder(CSIDL_COMMON_APPDATA
, aFile
);
734 case Win_Printhood
: {
735 return GetWindowsFolder(CSIDL_PRINTHOOD
, aFile
);
738 return GetWindowsFolder(CSIDL_COOKIES
, aFile
);
741 nsresult rv
= GetWindowsFolder(CSIDL_APPDATA
, aFile
);
743 rv
= GetRegWindowsAppDataFolder(false, aFile
);
747 case Win_LocalAppdata
: {
748 nsresult rv
= GetWindowsFolder(CSIDL_LOCAL_APPDATA
, aFile
);
750 rv
= GetRegWindowsAppDataFolder(true, aFile
);
754 #if defined(MOZ_CONTENT_SANDBOX)
755 case Win_LocalAppdataLow
: {
756 // This should only really fail on versions pre-Vista, in which case this
757 // shouldn't have been used in the first place.
758 GUID localAppDataLowGuid
= FOLDERID_LocalAppDataLow
;
759 return GetKnownFolder(&localAppDataLowGuid
, aFile
);
762 case Win_Documents
: {
763 return GetLibrarySaveToPath(CSIDL_MYDOCUMENTS
,
764 FOLDERID_DocumentsLibrary
,
768 return GetLibrarySaveToPath(CSIDL_MYPICTURES
,
769 FOLDERID_PicturesLibrary
,
773 return GetLibrarySaveToPath(CSIDL_MYMUSIC
,
774 FOLDERID_MusicLibrary
,
778 return GetLibrarySaveToPath(CSIDL_MYVIDEO
,
779 FOLDERID_VideosLibrary
,
785 case Unix_LocalDirectory
:
786 return NS_NewNativeLocalFile(nsDependentCString("/usr/local/netscape/"),
789 case Unix_LibDirectory
:
790 return NS_NewNativeLocalFile(nsDependentCString("/usr/local/lib/netscape/"),
794 case Unix_HomeDirectory
:
795 return GetUnixHomeDir(aFile
);
797 case Unix_XDG_Desktop
:
798 case Unix_XDG_Documents
:
799 case Unix_XDG_Download
:
801 case Unix_XDG_Pictures
:
802 case Unix_XDG_PublicShare
:
803 case Unix_XDG_Templates
:
804 case Unix_XDG_Videos
:
805 return GetUnixXDGUserDirectory(aSystemSystemDirectory
, aFile
);
811 return NS_ERROR_NOT_AVAILABLE
;
814 #if defined (MOZ_WIDGET_COCOA)
816 GetOSXFolderType(short aDomain
, OSType aFolderType
, nsIFile
** aLocalFile
)
820 nsresult rv
= NS_ERROR_FAILURE
;
822 err
= ::FSFindFolder(aDomain
, aFolderType
, kCreateFolder
, &fsRef
);
824 NS_NewLocalFile(EmptyString(), true, aLocalFile
);
825 nsCOMPtr
<nsILocalFileMac
> localMacFile(do_QueryInterface(*aLocalFile
));
827 rv
= localMacFile
->InitWithFSRef(&fsRef
);