AppDirs.vala 12.9 KB
Newer Older
1
/* Copyright 2016 Software Freedom Conservancy Inc.
2 3
 *
 * This software is licensed under the GNU LGPL (version 2.1 or later).
4
 * See the COPYING file in this distribution.
5 6 7
 */

class AppDirs {
8
    private const string DEFAULT_DATA_DIR = "shotwell";
9 10 11
    
    private static File exec_dir;
    private static File data_dir = null;
12
    private static File tmp_dir = null;
13
    private static File libexec_dir = null;
14
    
15
    // Because this is called prior to Debug.init(), this function cannot do any logging calls
16
    public static void init(string arg0) {
17
        File exec_file = File.new_for_path(Posix.realpath(Environment.find_program_in_path(arg0)));
18
        exec_dir = exec_file.get_parent();
19 20
    }
    
21 22
    // Because this *may* be called prior to Debug.init(), this function cannot do any logging
    // calls
23 24 25
    public static void terminate() {
    }
    
26 27 28
    public static File get_home_dir() {
        return File.new_for_path(Environment.get_home_dir());
    }
29 30

    public static File get_cache_dir() {
31 32 33
        return ((data_dir == null) ?
            File.new_for_path(Environment.get_user_cache_dir()).get_child(DEFAULT_DATA_DIR) :
            data_dir);
34
    }
35
    
36
    public static void try_migrate_data() {
37 38 39 40 41 42 43 44
        // Migrate the user plugin dir from .gnome2 to .local
        File user_plugin_dir = get_user_plugins_dir();
        File old_dir =
            get_home_dir().get_child(".gnome2").get_child("shotwell").get_child("plugins");

        if (old_dir.query_exists() && !user_plugin_dir.get_parent().query_exists()) {
            try {
              user_plugin_dir.get_parent().make_directory_with_parents(null);
45
            } catch (Error err) { }
46 47 48 49
        }

        try {
            old_dir.move(user_plugin_dir, FileCopyFlags.NONE);
50
        } catch (Error err) { }
51 52


53
        File new_dir = get_data_dir();
54
        old_dir = get_home_dir().get_child(".shotwell");
55 56 57 58
        if (new_dir.query_exists() || !old_dir.query_exists())
            return;

        File cache_dir = get_cache_dir();
59
        Posix.mode_t mask = Posix.umask(0077);
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
        if (!cache_dir.query_exists()) {
            try {
                cache_dir.make_directory_with_parents(null);
            } catch (Error err) {
                AppWindow.panic(_("Unable to create cache directory %s: %s").printf(cache_dir.get_path(),
                                                                                    err.message));
            }
        }
        GLib.FileUtils.rename(old_dir.get_child("thumbs").get_path(), cache_dir.get_child("thumbs").get_path());

        if (!new_dir.get_parent().query_exists()) {
            try {
              new_dir.get_parent().make_directory_with_parents(null);
            } catch (Error err) {
                AppWindow.panic(_("Unable to create data directory %s: %s").printf(new_dir.get_parent().get_path(),
                                                                                   err.message));
            }
        }
        GLib.FileUtils.rename(old_dir.get_path(), new_dir.get_path());
        GLib.FileUtils.chmod(new_dir.get_path(), 0700);

        Posix.umask(mask);
    }

84
    // This can only be called once, and it better be called at startup
85
    public static void set_data_dir(string user_data_dir) requires (!is_string_empty(user_data_dir)) {
86
        assert(data_dir == null);
87 88 89 90
        
        // fix up to absolute path
        string path = strip_pretty_path(user_data_dir);
        if (!Path.is_absolute(path))
91
            data_dir = get_home_dir().get_child(path);
92 93 94 95
        else
            data_dir = File.new_for_path(path);
        
        message("Setting private data directory to %s", data_dir.get_path());
96 97 98
    }
    
    public static void verify_data_dir() {
99 100
        File data_dir = get_data_dir();
        try {
101 102
            if (!data_dir.query_exists(null))
                data_dir.make_directory_with_parents(null);
103
        } catch (Error err) {
104 105
            AppWindow.panic(_("Unable to create data directory %s: %s").printf(data_dir.get_path(),
                err.message));
106 107 108
        }
    }
    
109 110 111 112 113 114 115 116 117 118
    public static void verify_cache_dir() {
        File cache_dir = get_cache_dir();
        try {
            if (!cache_dir.query_exists(null))
                cache_dir.make_directory_with_parents(null);
        } catch (Error err) {
            AppWindow.panic(_("Unable to create cache directory %s: %s").printf(cache_dir.get_path(),
                err.message));
        }
    }
119 120 121 122 123 124 125 126 127 128 129
    
    /**
     * @brief Returns the build directory if not installed yet, or a path
     * to where any helper applets we need will live if installed.
     */
    public static File get_libexec_dir() {
        if (libexec_dir == null) {
            if (get_install_dir() == null) {
                // not installed yet - use wherever we were run from
                libexec_dir = get_exec_dir();
            } else {
130
                libexec_dir = File.new_for_path(Resources.LIBEXECDIR);
131 132 133 134 135
            }
        }

        return libexec_dir;
    }
136

137 138 139 140 141 142
    // Return the directory in which Shotwell is installed, or null if uninstalled.
    public static File? get_install_dir() {
        return get_sys_install_dir(exec_dir);
    }
    
    public static File get_data_dir() {
143
        return (data_dir == null) ? File.new_for_path(Environment.get_user_data_dir()).get_child(DEFAULT_DATA_DIR) : data_dir;
144 145
    }
    
146 147
    // The "import directory" is the same as the library directory, and are often used
    // interchangeably throughout the code.
148
    public static File get_import_dir() {
Lucas Beeler's avatar
Lucas Beeler committed
149
        string path = Config.Facade.get_instance().get_import_dir();
150 151 152 153 154 155
        if (!is_string_empty(path)) {
            // tilde -> home directory
            path = strip_pretty_path(path);
            
            // if non-empty and relative, make it relative to the user's home directory
            if (!Path.is_absolute(path)) 
156
                return get_home_dir().get_child(path);
157 158
            
            // non-empty and absolute, it's golden
159
            return File.new_for_path(path);
160 161 162
        }
        
        // Empty path, use XDG Pictures directory
163
        path = Environment.get_user_special_dir(UserDirectory.PICTURES);
164
        if (!is_string_empty(path))
165 166
            return File.new_for_path(path);
        
167
        // If XDG yarfed, use ~/Pictures
168
        return get_home_dir().get_child(_("Pictures"));
169
    }
170
    
171
    // Library folder + photo folder, based on user's preferred directory pattern.
172
    public static File get_baked_import_dir(time_t tm) {
Lucas Beeler's avatar
Lucas Beeler committed
173
        string? pattern = Config.Facade.get_instance().get_directory_pattern();
174
        if (is_string_empty(pattern))
Lucas Beeler's avatar
Lucas Beeler committed
175
            pattern = Config.Facade.get_instance().get_directory_pattern_custom();
176 177 178
        if (is_string_empty(pattern))
            pattern = "%Y" + Path.DIR_SEPARATOR_S + "%m" + Path.DIR_SEPARATOR_S + "%d"; // default
            
179
        DateTime date = new DateTime.from_unix_local(tm);
180 181 182
        return File.new_for_path(get_import_dir().get_path() + Path.DIR_SEPARATOR_S + date.format(pattern));
    }
    
183 184 185 186 187 188
    // Returns true if the File is in or is equal to the library/import directory.
    public static bool is_in_import_dir(File file) {
        File import_dir = get_import_dir();
        
        return file.has_prefix(import_dir) || file.equal(import_dir);
    }
189

190
    public static void set_import_dir(string path) {
Lucas Beeler's avatar
Lucas Beeler committed
191
        Config.Facade.get_instance().set_import_dir(path);
192
    }
193 194 195 196 197 198
    
    public static File get_exec_dir() {
        return exec_dir;
    }
    
    public static File get_temp_dir() {
199 200 201 202 203 204 205 206 207 208
        if (tmp_dir == null) {
            tmp_dir = File.new_for_path(DirUtils.mkdtemp (Environment.get_tmp_dir() + "/shotwell-XXXXXX"));
            
            try {
                if (!tmp_dir.query_exists(null))
                    tmp_dir.make_directory_with_parents(null);
            } catch (Error err) {
                AppWindow.panic(_("Unable to create temporary directory %s: %s").printf(
                    tmp_dir.get_path(), err.message));
            }
209 210 211 212
        }
        
        return tmp_dir;
    }
NMA's avatar
NMA committed
213

214 215 216
    public static File get_data_subdir(string name, string? subname = null) {
        File subdir = get_data_dir().get_child(name);
        if (subname != null)
217 218 219 220 221 222 223 224 225 226 227 228 229
            subdir = subdir.get_child(subname);

        try {
            if (!subdir.query_exists(null))
                subdir.make_directory_with_parents(null);
        } catch (Error err) {
            AppWindow.panic(_("Unable to create data subdirectory %s: %s").printf(subdir.get_path(),
                err.message));
        }
        
        return subdir;
    }

230
    public static void ensure_writable(File dir) {
231 232 233 234 235
        if (dir.query_exists(null)) {
            try {
                FileInfo info = dir.query_info(FileAttribute.UNIX_MODE, FileQueryInfoFlags.NONE);
                uint32 mode = info.get_attribute_uint32(FileAttribute.UNIX_MODE) | 0700;
                if (!dir.set_attribute_uint32(FileAttribute.UNIX_MODE, mode, FileQueryInfoFlags.NONE)) {
236
                    AppWindow.panic(_("Could not make directory %s writable").printf(dir.get_path()));
237 238
                }
            } catch (Error err) {
239
                AppWindow.panic(_("Could not make directory %s writable: %s").printf(dir.get_path(), err.message));
240 241 242 243
            }
        }
    }

244 245
    public static File get_cache_subdir(string name, string? subname = null) {
        File subdir = get_cache_dir().get_child(name);
246
        ensure_writable(subdir);
247
        if (subname != null)
248 249 250
            subdir = subdir.get_child(subname);

        try {
251 252
            if (!subdir.query_exists(null))
                subdir.make_directory_with_parents(null);
253
        } catch (Error err) {
254 255
            AppWindow.panic(_("Unable to create data subdirectory %s: %s").printf(subdir.get_path(),
                err.message));
256
        }
257
        ensure_writable(subdir);
258 259 260
        return subdir;
    }
    
261 262 263 264 265 266 267 268 269
#if ENABLE_FACES
    public static File get_resources_dir() {
        File? install_dir = get_install_dir();
        
        return (install_dir != null) ? install_dir.get_child("share").get_child("shotwell")
            : get_exec_dir();
    }
#endif
    
270 271
    public static File get_lib_dir() {
        File? install_dir = get_install_dir();
272 273 274 275 276 277 278 279 280 281

        // Running from source tree
        if (install_dir == null) {
            // Meson build
            if (get_exec_dir().get_path().has_suffix("src")) {
                return get_exec_dir().get_parent();
            }

            return get_exec_dir();
        }
282
        
283
        return install_dir.get_child(Resources.LIB).get_child("shotwell");
284 285 286 287 288 289 290
    }
    
    public static File get_system_plugins_dir() {
        return get_lib_dir().get_child("plugins");
    }
    
    public static File get_user_plugins_dir() {
291
        return get_data_dir().get_child("plugins");
292 293
    }
    
294 295 296 297 298 299 300 301 302 303 304 305
    public static File? get_log_file() {
        if (Environment.get_variable("SHOTWELL_LOG_FILE") != null) {
            if (Environment.get_variable("SHOTWELL_LOG_FILE") == ":console:") {
                return null;
            } else {
                return File.new_for_path(Environment.get_variable("SHOTWELL_LOG_FILE"));
            }
        } else {
            return File.new_for_path(Environment.get_user_cache_dir()).
                get_child("shotwell").get_child("shotwell.log");
        }
    }
306 307 308
    
    public static File get_thumbnailer_bin() {
        const string filename = "shotwell-video-thumbnailer";
309
        File f = AppDirs.get_libexec_dir().get_child("thumbnailer").get_child (filename);
310 311
        if (!f.query_exists()) {
            // If we're running installed.
312
            f = AppDirs.get_libexec_dir () .get_child ("shotwell").get_child (filename);
313
        }
314 315 316 317 318

        if (!f.query_exists()) {
            f = AppDirs.get_libexec_dir().get_parent().get_child("thumbnailer").get_child(filename);
        }

319 320
        return f;
    }
321 322 323

    public static File get_settings_migrator_bin() {
        const string filename = "shotwell-settings-migrator";
324
        File f = AppDirs.get_libexec_dir().get_child ("settings-migrator").get_child (filename);
325 326
        if (!f.query_exists()) {
            // If we're running installed.
327
            f = AppDirs.get_libexec_dir () .get_child ("shotwell").get_child (filename);
328 329 330
        }
        return f;
    }
331 332 333 334 335 336 337 338 339 340

#if ENABLE_FACES
    public static File get_facedetect_bin() {
        const string filename = "shotwell-facedetect";
        File f = AppDirs.get_libexec_dir().get_parent().get_child("facedetect").get_child (filename);
        if (!f.query_exists()) {
            f = AppDirs.get_libexec_dir().get_child("shotwell").get_child(filename);
        }
        return f;
    }
NMA's avatar
NMA committed
341

342 343 344 345 346 347 348
    public static File get_haarcascade_file() {
        File f = File.new_for_path(AppDirs.get_exec_dir().get_parent().get_parent().get_child("facedetect").get_child("facedetect-haarcascade.xml").get_path());
        if (f.query_exists()) {//testing meson builddir
            return f;
        }
        return get_resources_dir().get_child("facedetect-haarcascade.xml");
    }
NMA's avatar
NMA committed
349 350 351 352

    public static File get_openface_dnn_dir() {
        return get_data_subdir("data"); //get_child("openface.nn4.small2.v1.t7");
    }
353 354
#endif

355 356
}