summaryrefslogtreecommitdiff
path: root/src/LibraryFiles.vala
blob: a49b77b6b0b0730de84719df8338e13f9fc9ade9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
/* Copyright 2016 Software Freedom Conservancy Inc.
 *
 * This software is licensed under the GNU Lesser General Public License
 * (version 2.1 or later).  See the COPYING file in this distribution.
 */

namespace LibraryFiles {

// This method uses global::generate_unique_file_at in order to "claim" a file in the filesystem.
// Thus, when the method returns success a file may exist already, and should be overwritten.
//
// This function is thread safe.
public File? generate_unique_file(string basename, MediaMetadata? metadata, time_t ts, out bool collision)
    throws Error {
    // use exposure timestamp over the supplied one (which probably comes from the file's
    // modified time, or is simply time()), unless it's zero, in which case use current time
    
    time_t timestamp = ts;
    if (metadata != null) {
        MetadataDateTime? date_time = metadata.get_creation_date_time();
        if (date_time != null)
            timestamp = date_time.get_timestamp();
        else if (timestamp == 0)
            timestamp = time_t();
    }
    
    // build a directory tree inside the library
    File dir = AppDirs.get_baked_import_dir(timestamp);
    try {
        dir.make_directory_with_parents(null);
    } catch (Error err) {
        if (!(err is IOError.EXISTS))
            throw err;
        
        // silently ignore not creating a directory that already exists
    }
    
    // Optionally convert to lower-case.
    string newbasename = convert_basename(basename);
    
    return global::generate_unique_file(dir, newbasename, out collision);
}

// Create the basename for files in the library.
// Depending on the setting USE_LOWERCASE_FILENAMES the basename will be converted to lower case or not
public string convert_basename(string basename) {
    if (Config.Facade.get_instance().get_use_lowercase_filenames()) {
        return basename.down();
    } else {
        return basename;
    }

}


// This function is thread-safe.
private File duplicate(File src, FileProgressCallback? progress_callback, bool blacklist) throws Error {
    time_t timestamp = 0;
    try {
        timestamp = query_file_modified(src);
    } catch (Error err) {
        critical("Unable to access file modification for %s: %s", src.get_path(), err.message);
    }
       
    MediaMetadata? metadata = null;
    if (VideoReader.is_supported_video_file(src)) {
        VideoReader reader = new VideoReader(src);
        try {
            metadata = reader.read_metadata();
        } catch (Error err) {
            // ignored, leave metadata as null
        }
    } else {
        PhotoFileReader reader = PhotoFileFormat.get_by_file_extension(src).create_reader(
            src.get_path());
        try {
            metadata = reader.read_metadata();
        } catch (Error err) {
            // ignored, leave metadata as null
        }
    }
    
    bool collision;
    File? dest = generate_unique_file(src.get_basename(), metadata, timestamp, out collision);
    if (dest == null)
        throw new FileError.FAILED("Unable to generate unique pathname for destination");
    
    if (blacklist)
        LibraryMonitor.blacklist_file(dest, "LibraryFiles.duplicate");
    
    try {
        src.copy(dest, FileCopyFlags.ALL_METADATA | FileCopyFlags.OVERWRITE, null, progress_callback);
        if (blacklist)
            LibraryMonitor.unblacklist_file(dest);
    } catch (Error err) {
        message("There was a problem copying %s: %s", src.get_path(), err.message);
        if (blacklist && (md5_file(src) != md5_file(dest)))
            LibraryMonitor.unblacklist_file(dest);
    }
    
    // Make file writable by getting current Unix mode and or it with 600 (user read/write)
    try {
        FileInfo info = dest.query_info(FileAttribute.UNIX_MODE, FileQueryInfoFlags.NONE);
        uint32 mode = info.get_attribute_uint32(FileAttribute.UNIX_MODE) | 0600;
        if (!dest.set_attribute_uint32(FileAttribute.UNIX_MODE, mode, FileQueryInfoFlags.NONE)) {
            warning("Could not make file writable");
        }
    } catch (Error err) {
        warning("Could not make file writable: %s", err.message);
    }
    
    return dest;
}
}