1 #include "libfmqtglobals.h"
2 #include "archiver.h"
3
4 #include <cstring>
5 #include <glib.h>
6 #include <gio/gdesktopappinfo.h>
7
8 #include <string>
9
10 namespace Fm {
11
12 Archiver* Archiver::defaultArchiver_ = nullptr; // static
13 std::vector<std::unique_ptr<Archiver>> Archiver::allArchivers_; // static
14
Archiver()15 Archiver::Archiver() {
16 }
17
isMimeTypeSupported(const char * type)18 bool Archiver::isMimeTypeSupported(const char* type) {
19 char** p;
20 if(G_UNLIKELY(!type)) {
21 return false;
22 }
23 for(p = mimeTypes_.get(); *p; ++p) {
24 if(strcmp(*p, type) == 0) {
25 return true;
26 }
27 }
28 return false;
29 }
30
launchProgram(GAppLaunchContext * ctx,const char * cmd,const FilePathList & files,const FilePath & dir)31 bool Archiver::launchProgram(GAppLaunchContext* ctx, const char* cmd, const FilePathList& files, const FilePath& dir) {
32 char* _cmd = nullptr;
33 const char* dir_place_holder;
34 GKeyFile* dummy;
35
36 if(dir.isValid() && (dir_place_holder = strstr(cmd, "%d"))) {
37 CStrPtr dir_str;
38 int len;
39 if(strstr(cmd, "%U") || strstr(cmd, "%u")) { /* supports URI */
40 dir_str = dir.uri();
41 }
42 else {
43 dir_str = dir.localPath();
44 }
45
46 // FIXME: remove libfm dependency here
47 /* replace all % with %% so encoded URI can be handled correctly when parsing Exec key. */
48 std::string percentEscapedDir;
49 for(auto p = dir_str.get(); *p; ++p) {
50 percentEscapedDir += *p;
51 if(*p == '%') {
52 percentEscapedDir += '%';
53 }
54 }
55
56 /* quote the path or URI */
57 dir_str = CStrPtr{g_shell_quote(percentEscapedDir.c_str())};
58
59 len = strlen(cmd) - 2 + strlen(dir_str.get()) + 1;
60 _cmd = (char*)g_malloc(len);
61 len = (dir_place_holder - cmd);
62 strncpy(_cmd, cmd, len);
63 strcpy(_cmd + len, dir_str.get());
64 strcat(_cmd, dir_place_holder + 2);
65 cmd = _cmd;
66 }
67
68 /* create a fake key file to cheat GDesktopAppInfo */
69 dummy = g_key_file_new();
70 g_key_file_set_string(dummy, G_KEY_FILE_DESKTOP_GROUP, "Type", "Application");
71 g_key_file_set_string(dummy, G_KEY_FILE_DESKTOP_GROUP, "Name", program_.get());
72
73 /* replace all % with %% so encoded URI can be handled correctly when parsing Exec key. */
74 g_key_file_set_string(dummy, G_KEY_FILE_DESKTOP_GROUP, "Exec", cmd);
75 GAppInfoPtr app{reinterpret_cast<GAppInfo*>(g_desktop_app_info_new_from_keyfile(dummy)), false};
76
77 g_key_file_free(dummy);
78 g_debug("cmd = %s", cmd);
79 if(app) {
80 GList* uris = nullptr;
81 for(auto& file: files) {
82 uris = g_list_prepend(uris, g_strdup(file.uri().get()));
83 }
84 g_app_info_launch_uris(app.get(), uris, ctx, nullptr);
85 g_list_free_full(uris, g_free);
86 }
87 g_free(_cmd);
88 return true;
89 }
90
createArchive(GAppLaunchContext * ctx,const FilePathList & files)91 bool Archiver::createArchive(GAppLaunchContext* ctx, const FilePathList& files) {
92 if(createCmd_ && !files.empty()) {
93 launchProgram(ctx, createCmd_.get(), files, FilePath{});
94 }
95 return false;
96 }
97
extractArchives(GAppLaunchContext * ctx,const FilePathList & files)98 bool Archiver::extractArchives(GAppLaunchContext* ctx, const FilePathList& files) {
99 if(extractCmd_ && !files.empty()) {
100 launchProgram(ctx, extractCmd_.get(), files, FilePath{});
101 }
102 return false;
103 }
104
extractArchivesTo(GAppLaunchContext * ctx,const FilePathList & files,const FilePath & dest_dir)105 bool Archiver::extractArchivesTo(GAppLaunchContext* ctx, const FilePathList& files, const FilePath& dest_dir) {
106 if(extractToCmd_ && !files.empty()) {
107 launchProgram(ctx, extractToCmd_.get(), files, dest_dir);
108 }
109 return false;
110 }
111
112 // static
defaultArchiver()113 Archiver* Archiver::defaultArchiver() {
114 allArchivers(); // to have a preliminary default archiver
115 return defaultArchiver_;
116 }
117
setDefaultArchiverByName(const char * name)118 void Archiver::setDefaultArchiverByName(const char *name) {
119 if(name) {
120 auto& all = allArchivers();
121 for(auto& archiver: all) {
122 if(archiver->program_ && strcmp(archiver->program_.get(), name) == 0) {
123 defaultArchiver_ = archiver.get();
124 break;
125 }
126 }
127 }
128 }
129
130 // static
setDefaultArchiver(Archiver * archiver)131 void Archiver::setDefaultArchiver(Archiver* archiver) {
132 if(archiver) {
133 defaultArchiver_ = archiver;
134 }
135 }
136
137 // static
allArchivers()138 const std::vector<std::unique_ptr<Archiver> >& Archiver::allArchivers() {
139 // load all archivers on demand
140 if(allArchivers_.empty()) {
141 GKeyFile* kf = g_key_file_new();
142 if(g_key_file_load_from_file(kf, LIBFM_QT_DATA_DIR "/archivers.list", G_KEY_FILE_NONE, nullptr)) {
143 gsize n_archivers;
144 CStrArrayPtr programs{g_key_file_get_groups(kf, &n_archivers)};
145 if(programs) {
146 gsize i;
147 for(i = 0; i < n_archivers; ++i) {
148 auto program = programs[i];
149 std::unique_ptr<Archiver> archiver{new Archiver{}};
150 archiver->createCmd_ = CStrPtr{g_key_file_get_string(kf, program, "create", nullptr)};
151 archiver->extractCmd_ = CStrPtr{g_key_file_get_string(kf, program, "extract", nullptr)};
152 archiver->extractToCmd_ = CStrPtr{g_key_file_get_string(kf, program, "extract_to", nullptr)};
153 archiver->mimeTypes_ = CStrArrayPtr{g_key_file_get_string_list(kf, program, "mime_types", nullptr, nullptr)};
154 archiver->program_ = CStrPtr{g_strdup(program)};
155
156 // if default archiver is not set, find the first program existing in the current system.
157 if(!defaultArchiver_) {
158 CStrPtr fullPath{g_find_program_in_path(program)};
159 if(fullPath) {
160 defaultArchiver_ = archiver.get();
161 }
162 }
163
164 allArchivers_.emplace_back(std::move(archiver));
165 }
166 }
167 }
168 g_key_file_free(kf);
169 }
170 return allArchivers_;
171 }
172
173 } // namespace Fm
174