1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2020 - Raw Material Software Limited
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 #if defined(__FreeBSD__) || defined(__DragonFly__)
24 extern char **environ;
25 #endif
26 
27 namespace juce
28 {
29 
30 enum
31 {
32     U_ISOFS_SUPER_MAGIC = 0x9660,   // linux/iso_fs.h
33     U_MSDOS_SUPER_MAGIC = 0x4d44,   // linux/msdos_fs.h
34     U_NFS_SUPER_MAGIC = 0x6969,     // linux/nfs_fs.h
35     U_SMB_SUPER_MAGIC = 0x517B      // linux/smb_fs.h
36 };
37 
isOnCDRomDrive() const38 bool File::isOnCDRomDrive() const
39 {
40     struct statfs buf;
41 
42     return statfs (getFullPathName().toUTF8(), &buf) == 0
43              && buf.f_type == (short) U_ISOFS_SUPER_MAGIC;
44 }
45 
isOnHardDisk() const46 bool File::isOnHardDisk() const
47 {
48     struct statfs buf;
49 
50     if (statfs (getFullPathName().toUTF8(), &buf) == 0)
51     {
52         switch (buf.f_type)
53         {
54             case U_ISOFS_SUPER_MAGIC:   // CD-ROM
55             case U_MSDOS_SUPER_MAGIC:   // Probably floppy (but could be mounted FAT filesystem)
56             case U_NFS_SUPER_MAGIC:     // Network NFS
57             case U_SMB_SUPER_MAGIC:     // Network Samba
58                 return false;
59 
60             default: break;
61         }
62     }
63 
64     // Assume so if this fails for some reason
65     return true;
66 }
67 
isOnRemovableDrive() const68 bool File::isOnRemovableDrive() const
69 {
70     jassertfalse; // xxx not implemented for linux!
71     return false;
72 }
73 
getVersion() const74 String File::getVersion() const
75 {
76     return {}; // xxx not yet implemented
77 }
78 
79 //==============================================================================
resolveXDGFolder(const char * const type,const char * const fallbackFolder)80 static File resolveXDGFolder (const char* const type, const char* const fallbackFolder)
81 {
82     StringArray confLines;
83     File ("~/.config/user-dirs.dirs").readLines (confLines);
84 
85     for (int i = 0; i < confLines.size(); ++i)
86     {
87         const String line (confLines[i].trimStart());
88 
89         if (line.startsWith (type))
90         {
91             // eg. resolve XDG_MUSIC_DIR="$HOME/Music" to /home/user/Music
92             const File f (line.replace ("$HOME", File ("~").getFullPathName())
93                               .fromFirstOccurrenceOf ("=", false, false)
94                               .trim().unquoted());
95 
96             if (f.isDirectory())
97                 return f;
98         }
99     }
100 
101     return File (fallbackFolder);
102 }
103 
104 const char* const* juce_argv = nullptr;
105 int juce_argc = 0;
106 
getSpecialLocation(const SpecialLocationType type)107 File File::getSpecialLocation (const SpecialLocationType type)
108 {
109     switch (type)
110     {
111         case userHomeDirectory:
112         {
113             if (const char* homeDir = getenv ("HOME"))
114                 return File (CharPointer_UTF8 (homeDir));
115 
116             if (auto* pw = getpwuid (getuid()))
117                 return File (CharPointer_UTF8 (pw->pw_dir));
118 
119             return {};
120         }
121 
122         case userDocumentsDirectory:          return resolveXDGFolder ("XDG_DOCUMENTS_DIR", "~/Documents");
123         case userMusicDirectory:              return resolveXDGFolder ("XDG_MUSIC_DIR",     "~/Music");
124         case userMoviesDirectory:             return resolveXDGFolder ("XDG_VIDEOS_DIR",    "~/Videos");
125         case userPicturesDirectory:           return resolveXDGFolder ("XDG_PICTURES_DIR",  "~/Pictures");
126         case userDesktopDirectory:            return resolveXDGFolder ("XDG_DESKTOP_DIR",   "~/Desktop");
127         case userApplicationDataDirectory:    return resolveXDGFolder ("XDG_CONFIG_HOME",   "~/.config");
128         case commonDocumentsDirectory:
129         case commonApplicationDataDirectory:  return File ("/opt");
130         case globalApplicationsDirectory:     return File ("/usr");
131 
132         case tempDirectory:
133         {
134             if (const char* tmpDir = getenv ("TMPDIR"))
135                 return File (CharPointer_UTF8 (tmpDir));
136 
137             return File ("/tmp");
138         }
139 
140         case invokedExecutableFile:
141             if (juce_argv != nullptr && juce_argc > 0)
142                 return File (CharPointer_UTF8 (juce_argv[0]));
143             // Falls through
144             JUCE_FALLTHROUGH
145 
146         case currentExecutableFile:
147         case currentApplicationFile:
148            #if ! JUCE_STANDALONE_APPLICATION
149             return juce_getExecutableFile();
150            #endif
151             // deliberate fall-through if this is not a shared-library
152             JUCE_FALLTHROUGH
153 
154         case hostApplicationPath:
155         {
156             const File f ("/proc/self/exe");
157             return f.isSymbolicLink() ? f.getLinkedTarget() : juce_getExecutableFile();
158         }
159 
160         default:
161             jassertfalse; // unknown type?
162             break;
163     }
164 
165     return {};
166 }
167 
168 //==============================================================================
moveToTrash() const169 bool File::moveToTrash() const
170 {
171     if (! exists())
172         return true;
173 
174     File trashCan ("~/.Trash");
175 
176     if (! trashCan.isDirectory())
177         trashCan = "~/.local/share/Trash/files";
178 
179     if (! trashCan.isDirectory())
180         return false;
181 
182     return moveFileTo (trashCan.getNonexistentChildFile (getFileNameWithoutExtension(),
183                                                          getFileExtension()));
184 }
185 
186 //==============================================================================
isFileExecutable(const String & filename)187 static bool isFileExecutable (const String& filename)
188 {
189     juce_statStruct info;
190 
191     return juce_stat (filename, info)
192             && S_ISREG (info.st_mode)
193             && access (filename.toUTF8(), X_OK) == 0;
194 }
195 
openDocument(const String & fileName,const String & parameters)196 bool Process::openDocument (const String& fileName, const String& parameters)
197 {
198     auto cmdString = fileName.replace (" ", "\\ ", false);
199     cmdString << " " << parameters;
200 
201     if (cmdString.startsWithIgnoreCase ("file:")
202          || File::createFileWithoutCheckingPath (fileName).isDirectory()
203          || ! isFileExecutable (fileName))
204     {
205         StringArray cmdLines;
206 
207         for (auto browserName : { "xdg-open", "/etc/alternatives/x-www-browser", "firefox", "mozilla",
208                                   "google-chrome", "chromium-browser", "opera", "konqueror" })
209         {
210             cmdLines.add (String (browserName) + " " + cmdString.trim().quoted());
211         }
212 
213         cmdString = cmdLines.joinIntoString (" || ");
214     }
215 
216     const char* const argv[4] = { "/bin/sh", "-c", cmdString.toUTF8(), nullptr };
217 
218     auto cpid = fork();
219 
220     if (cpid == 0)
221     {
222         setsid();
223 
224         // Child process
225         execve (argv[0], (char**) argv, environ);
226         exit (0);
227     }
228 
229     return cpid >= 0;
230 }
231 
revealToUser() const232 void File::revealToUser() const
233 {
234     if (isDirectory())
235         startAsProcess();
236     else if (getParentDirectory().exists())
237         getParentDirectory().startAsProcess();
238 }
239 
240 } // namespace juce
241