1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2017 - ROLI Ltd.
6 
7    JUCE is an open source library subject to commercial or open-source
8    licensing.
9 
10    The code included in this file is provided under the terms of the ISC license
11    http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12    To use, copy, modify, and/or distribute this software for any purpose with or
13    without fee is hereby granted provided that the above copyright notice and
14    this permission notice appear in all copies.
15 
16    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18    DISCLAIMED.
19 
20   ==============================================================================
21 */
22 
23 namespace juce
24 {
25 
26 enum
27 {
28     U_ISOFS_SUPER_MAGIC = 0x9660,   // linux/iso_fs.h
29     U_MSDOS_SUPER_MAGIC = 0x4d44,   // linux/msdos_fs.h
30     U_NFS_SUPER_MAGIC = 0x6969,     // linux/nfs_fs.h
31     U_SMB_SUPER_MAGIC = 0x517B      // linux/smb_fs.h
32 };
33 
isOnCDRomDrive() const34 bool File::isOnCDRomDrive() const
35 {
36     struct statfs buf;
37 
38 #if JUCE_BSD
39 	if (statfs(getFullPathName().toUTF8(), &buf) == 0) {
40 		String s(buf.f_fstypename);
41 		return s.compare("cd9660") == 0 || s.compare("udf") == 0;
42 	}
43 	return false;
44 #else
45     return statfs (getFullPathName().toUTF8(), &buf) == 0
46              && buf.f_type == (short) U_ISOFS_SUPER_MAGIC;
47 #endif
48 }
49 
isOnHardDisk() const50 bool File::isOnHardDisk() const
51 {
52     struct statfs buf;
53 
54     if (statfs (getFullPathName().toUTF8(), &buf) == 0)
55     {
56 #if JUCE_BSD
57 		String s(buf.f_fstypename);
58 		if (s.compare("cd9660") == 0 || s.compare("udf") == 0 ||
59 			s.compare("nfs") == 0 || s.compare("smbfs") == 0) {
60 			return false;
61 		}
62 #else
63         switch (buf.f_type)
64         {
65             case U_ISOFS_SUPER_MAGIC:   // CD-ROM
66             case U_MSDOS_SUPER_MAGIC:   // Probably floppy (but could be mounted FAT filesystem)
67             case U_NFS_SUPER_MAGIC:     // Network NFS
68             case U_SMB_SUPER_MAGIC:     // Network Samba
69                 return false;
70 
71             default: break;
72         }
73 #endif
74     }
75 
76     // Assume so if this fails for some reason
77     return true;
78 }
79 
isOnRemovableDrive() const80 bool File::isOnRemovableDrive() const
81 {
82     jassertfalse; // xxx not implemented for linux!
83     return false;
84 }
85 
getVersion() const86 String File::getVersion() const
87 {
88     return {}; // xxx not yet implemented
89 }
90 
91 //==============================================================================
resolveXDGFolder(const char * const type,const char * const fallbackFolder)92 static File resolveXDGFolder (const char* const type, const char* const fallbackFolder)
93 {
94     StringArray confLines;
95     File ("~/.config/user-dirs.dirs").readLines (confLines);
96 
97     for (int i = 0; i < confLines.size(); ++i)
98     {
99         const String line (confLines[i].trimStart());
100 
101         if (line.startsWith (type))
102         {
103             // eg. resolve XDG_MUSIC_DIR="$HOME/Music" to /home/user/Music
104             const File f (line.replace ("$HOME", File ("~").getFullPathName())
105                               .fromFirstOccurrenceOf ("=", false, false)
106                               .trim().unquoted());
107 
108             if (f.isDirectory())
109                 return f;
110         }
111     }
112 
113     return File (fallbackFolder);
114 }
115 
116 const char* const* juce_argv = nullptr;
117 int juce_argc = 0;
118 
getSpecialLocation(const SpecialLocationType type)119 File File::getSpecialLocation (const SpecialLocationType type)
120 {
121     switch (type)
122     {
123         case userHomeDirectory:
124         {
125             if (const char* homeDir = getenv ("HOME"))
126                 return File (CharPointer_UTF8 (homeDir));
127 
128             if (auto* pw = getpwuid (getuid()))
129                 return File (CharPointer_UTF8 (pw->pw_dir));
130 
131             return {};
132         }
133 
134         case userDocumentsDirectory:          return resolveXDGFolder ("XDG_DOCUMENTS_DIR", "~/Documents");
135         case userMusicDirectory:              return resolveXDGFolder ("XDG_MUSIC_DIR",     "~/Music");
136         case userMoviesDirectory:             return resolveXDGFolder ("XDG_VIDEOS_DIR",    "~/Videos");
137         case userPicturesDirectory:           return resolveXDGFolder ("XDG_PICTURES_DIR",  "~/Pictures");
138         case userDesktopDirectory:            return resolveXDGFolder ("XDG_DESKTOP_DIR",   "~/Desktop");
139         case userApplicationDataDirectory:    return resolveXDGFolder ("XDG_CONFIG_HOME",   "~/.config");
140         case commonDocumentsDirectory:
141         case commonApplicationDataDirectory:  return File ("/opt");
142         case globalApplicationsDirectory:     return File ("/usr");
143 
144         case tempDirectory:
145         {
146             if (const char* tmpDir = getenv ("TMPDIR"))
147                 return File (CharPointer_UTF8 (tmpDir));
148 
149             return File ("/tmp");
150         }
151 
152         case invokedExecutableFile:
153             if (juce_argv != nullptr && juce_argc > 0)
154                 return File (CharPointer_UTF8 (juce_argv[0]));
155             // deliberate fall-through...
156 
157         case currentExecutableFile:
158         case currentApplicationFile:
159            #if ! JUCE_STANDALONE_APPLICATION
160             return juce_getExecutableFile();
161            #endif
162             // deliberate fall-through if this is not a shared-library
163 
164         case hostApplicationPath:
165         {
166             const File f ("/proc/self/exe");
167             return f.isSymbolicLink() ? f.getLinkedTarget() : juce_getExecutableFile();
168         }
169 
170         default:
171             jassertfalse; // unknown type?
172             break;
173     }
174 
175     return {};
176 }
177 
178 //==============================================================================
moveToTrash() const179 bool File::moveToTrash() const
180 {
181     if (! exists())
182         return true;
183 
184     File trashCan ("~/.Trash");
185 
186     if (! trashCan.isDirectory())
187         trashCan = "~/.local/share/Trash/files";
188 
189     if (! trashCan.isDirectory())
190         return false;
191 
192     return moveFileTo (trashCan.getNonexistentChildFile (getFileNameWithoutExtension(),
193                                                          getFileExtension()));
194 }
195 
196 //==============================================================================
isFileExecutable(const String & filename)197 static bool isFileExecutable (const String& filename)
198 {
199     juce_statStruct info;
200 
201     return juce_stat (filename, info)
202             && S_ISREG (info.st_mode)
203             && access (filename.toUTF8(), X_OK) == 0;
204 }
205 
openDocument(const String & fileName,const String & parameters)206 bool Process::openDocument (const String& fileName, const String& parameters)
207 {
208     auto cmdString = fileName.replace (" ", "\\ ", false);
209     cmdString << " " << parameters;
210 
211     if (cmdString.startsWithIgnoreCase ("file:")
212          || File::createFileWithoutCheckingPath (fileName).isDirectory()
213          || ! isFileExecutable (fileName))
214     {
215         StringArray cmdLines;
216 
217         for (auto browserName : { "xdg-open", "/etc/alternatives/x-www-browser", "firefox", "mozilla",
218                                   "google-chrome", "chromium-browser", "opera", "konqueror" })
219         {
220             cmdLines.add (String (browserName) + " " + cmdString.trim());
221         }
222 
223         cmdString = cmdLines.joinIntoString (" || ");
224     }
225 
226     const char* const argv[4] = { "/bin/sh", "-c", cmdString.toUTF8(), nullptr };
227 
228     auto cpid = fork();
229 
230     if (cpid == 0)
231     {
232         setsid();
233 
234         // Child process
235         execv (argv[0], (char**) argv);
236         exit (0);
237     }
238 
239     return cpid >= 0;
240 }
241 
revealToUser() const242 void File::revealToUser() const
243 {
244     if (isDirectory())
245         startAsProcess();
246     else if (getParentDirectory().exists())
247         getParentDirectory().startAsProcess();
248 }
249 
250 } // namespace juce
251