1 /*
2 * fm-archiver.c
3 *
4 * Copyright 2010 PCMan <pcman.tw@gmail.com>
5 *
6 * This file is a part of the Libfm library.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23 /**
24 * SECTION:fm-archiver
25 * @short_description: Support for packing and unpacking archiver utilities.
26 * @title: FmArchiver
27 *
28 * @include: libfm/fm.h
29 *
30 * The #FmArchiver represents support for utilities which can pack files
31 * into archive and/or extract them.
32 */
33
34 #ifdef HAVE_CONFIG_H
35 #include <config.h>
36 #endif
37
38 #include "fm-config.h"
39 #include "fm-archiver.h"
40 #include "fm-app-info.h"
41 #include "fm-utils.h"
42 #include <gio/gdesktopappinfo.h>
43 #include <string.h>
44
45 static GList* archivers = NULL;
46 static FmArchiver* default_archiver = NULL;
47
fm_archiver_free(FmArchiver * archiver)48 static void fm_archiver_free(FmArchiver* archiver)
49 {
50 g_free(archiver->program);
51 g_free(archiver->create_cmd);
52 g_free(archiver->extract_cmd);
53 g_free(archiver->extract_to_cmd);
54 g_strfreev(archiver->mime_types);
55 g_slice_free(FmArchiver, archiver);
56 }
57
fm_archiver_is_mime_type_supported(FmArchiver * archiver,const char * type)58 gboolean fm_archiver_is_mime_type_supported(FmArchiver* archiver, const char* type)
59 {
60 char** p;
61 if(G_UNLIKELY(!type))
62 return FALSE;
63 for(p=archiver->mime_types; *p; ++p)
64 {
65 if(strcmp(*p, type) == 0)
66 return TRUE;
67 }
68 return FALSE;
69 }
70
71 /* FIXME: error handling */
launch_program(FmArchiver * archiver,GAppLaunchContext * ctx,const char * cmd,FmPathList * files,FmPath * dir)72 static gboolean launch_program(FmArchiver* archiver, GAppLaunchContext* ctx, const char* cmd, FmPathList* files, FmPath* dir)
73 {
74 GAppInfo* app;
75 char* _cmd = NULL;
76 const char* dir_place_holder;
77 GKeyFile* dummy;
78 char* tmp;
79
80 if(dir && (dir_place_holder = strstr(cmd, "%d")))
81 {
82 char* dir_str;
83 int len;
84 if(strstr(cmd, "%U") || strstr(cmd, "%u")) /* supports URI */
85 dir_str = fm_path_to_uri(dir);
86 else
87 {
88 GFile* gf = fm_path_to_gfile(dir);
89 /* FIXME: convert dir to fuse-based local path if needed. */
90 dir_str = g_file_get_path(gf);
91 g_object_unref(gf);
92 }
93
94 /* replace all % with %% so encoded URI can be handled correctly when parsing Exec key. */
95 tmp = fm_strdup_replace(dir_str, "%", "%%");
96 g_free(dir_str);
97 dir_str = tmp;
98
99 /* quote the path or URI */
100 tmp = g_shell_quote(dir_str);
101 g_free(dir_str);
102 dir_str = tmp;
103
104 len = strlen(cmd) - 2 + strlen(dir_str) + 1;
105 _cmd = g_malloc(len);
106 len = (dir_place_holder - cmd);
107 strncpy(_cmd, cmd, len);
108 strcpy(_cmd + len, dir_str);
109 strcat(_cmd, dir_place_holder + 2);
110 g_free(dir_str);
111 cmd = _cmd;
112 }
113
114 /* create a fake key file to cheat GDesktopAppInfo */
115 dummy = g_key_file_new();
116 g_key_file_set_string(dummy, G_KEY_FILE_DESKTOP_GROUP, "Type", "Application");
117 g_key_file_set_string(dummy, G_KEY_FILE_DESKTOP_GROUP, "Name", archiver->program);
118
119 /* replace all % with %% so encoded URI can be handled correctly when parsing Exec key. */
120 g_key_file_set_string(dummy, G_KEY_FILE_DESKTOP_GROUP, "Exec", cmd);
121 app = (GAppInfo*)g_desktop_app_info_new_from_keyfile(dummy);
122
123 g_key_file_free(dummy);
124 g_debug("cmd = %s", cmd);
125 if(app)
126 {
127 GList* uris = NULL, *l;
128 for(l = fm_path_list_peek_head_link(files); l; l=l->next)
129 {
130 FmPath* path = FM_PATH(l->data);
131 uris = g_list_prepend(uris, fm_path_to_uri(path));
132 }
133 fm_app_info_launch_uris(app, uris, ctx, NULL);
134 g_list_foreach(uris, (GFunc)g_free, NULL);
135 g_list_free(uris);
136 g_object_unref(app);
137 }
138 g_free(_cmd);
139 return TRUE;
140 }
141
142 /**
143 * fm_archiver_create_archive
144 * @archiver: the archiver descriptor
145 * @ctx: (allow-none): a launch context
146 * @files: files to pack into archive
147 *
148 * Creates an archive for @files.
149 *
150 * Returns: %FALSE.
151 *
152 * Since: 0.1.9
153 */
fm_archiver_create_archive(FmArchiver * archiver,GAppLaunchContext * ctx,FmPathList * files)154 gboolean fm_archiver_create_archive(FmArchiver* archiver, GAppLaunchContext* ctx, FmPathList* files)
155 {
156 if(archiver->create_cmd && files)
157 launch_program(archiver, ctx, archiver->create_cmd, files, NULL);
158 return FALSE;
159 }
160
161 /**
162 * fm_archiver_extract_archives
163 * @archiver: the archiver descriptor
164 * @ctx: (allow-none): a launch context
165 * @files: archives to unpack
166 *
167 * Extracts files from archives.
168 *
169 * Returns: %FALSE.
170 *
171 * Since: 0.1.9
172 */
fm_archiver_extract_archives(FmArchiver * archiver,GAppLaunchContext * ctx,FmPathList * files)173 gboolean fm_archiver_extract_archives(FmArchiver* archiver, GAppLaunchContext* ctx, FmPathList* files)
174 {
175 if(archiver->extract_cmd && files)
176 launch_program(archiver, ctx, archiver->extract_cmd, files, NULL);
177 return FALSE;
178 }
179
180 /**
181 * fm_archiver_extract_archives_to
182 * @archiver: archiver descriptor
183 * @ctx: (allow-none): a launch context
184 * @files: archives to unpack
185 * @dest_dir: directory where files should be extracted to
186 *
187 * Extracts files from archives into @dest_dir.
188 *
189 * Returns: %FALSE.
190 *
191 * Since: 0.1.9
192 */
fm_archiver_extract_archives_to(FmArchiver * archiver,GAppLaunchContext * ctx,FmPathList * files,FmPath * dest_dir)193 gboolean fm_archiver_extract_archives_to(FmArchiver* archiver, GAppLaunchContext* ctx, FmPathList* files, FmPath* dest_dir)
194 {
195 if(archiver->extract_to_cmd && files)
196 launch_program(archiver, ctx, archiver->extract_to_cmd, files, dest_dir);
197 return FALSE;
198 }
199
200 /**
201 * fm_archiver_get_default
202 *
203 * Retrieves default GUI archiver used by libfm.
204 *
205 * This API is not thread-safe and should be used only in default context.
206 *
207 * Returns: archiver descriptor.
208 *
209 * Since: 0.1.9
210 */
fm_archiver_get_default(void)211 FmArchiver* fm_archiver_get_default(void)
212 {
213 if(!default_archiver)
214 {
215 GList* l;
216 if(fm_config->archiver)
217 {
218 for(l = archivers; l; l=l->next)
219 {
220 FmArchiver* archiver = (FmArchiver*)l->data;
221 if( g_strcmp0(fm_config->archiver, archiver->program) == 0 )
222 {
223 default_archiver = archiver;
224 break;
225 }
226 }
227 }
228 else if(archivers)
229 {
230 for(l = archivers; l; l=l->next)
231 {
232 FmArchiver* archiver = (FmArchiver*)l->data;
233 char* tmp = g_find_program_in_path(archiver->program);
234 if( tmp )
235 {
236 g_free(tmp);
237 default_archiver = archiver;
238 g_free(fm_config->archiver);
239 fm_config->archiver = g_strdup(archiver->program);
240 break;
241 }
242 }
243 }
244 }
245 return default_archiver;
246 }
247
248 /**
249 * fm_archiver_set_default
250 * @archiver: archiver descriptor
251 *
252 * Sets default GUI archiver used by libfm.
253 *
254 * This API is not thread-safe and should be used only in default context.
255 *
256 * Since: 0.1.9
257 */
fm_archiver_set_default(FmArchiver * archiver)258 void fm_archiver_set_default(FmArchiver* archiver)
259 {
260 if(archiver)
261 default_archiver = archiver;
262 }
263
264 /**
265 * fm_archiver_get_all
266 *
267 * Retrieves a list of #FmArchiver of all GUI archivers known to libfm.
268 *
269 * This API is not thread-safe and should be used only in default context.
270 *
271 * Returns: list of archivers.
272 *
273 * Since: 0.1.9
274 */
fm_archiver_get_all(void)275 const GList* fm_archiver_get_all(void)
276 {
277 return archivers;
278 }
279
_fm_archiver_init()280 void _fm_archiver_init()
281 {
282 GKeyFile *kf = g_key_file_new();
283 if(g_key_file_load_from_file(kf, PACKAGE_DATA_DIR "/archivers.list", 0, NULL))
284 {
285 gsize n_archivers;
286 gchar** programs = g_key_file_get_groups(kf, &n_archivers);
287 if(programs)
288 {
289 gsize i;
290 for(i = 0; i < n_archivers; ++i)
291 {
292 FmArchiver* archiver = g_slice_new0(FmArchiver);
293 archiver->program = programs[i];
294 archiver->create_cmd = g_key_file_get_string(kf, programs[i], "create", NULL);
295 archiver->extract_cmd = g_key_file_get_string(kf, programs[i], "extract", NULL);
296 archiver->extract_to_cmd = g_key_file_get_string(kf, programs[i], "extract_to", NULL);
297 archiver->mime_types = g_key_file_get_string_list(kf, programs[i], "mime_types", NULL, NULL);
298 archivers = g_list_append(archivers, archiver);
299 }
300 g_free(programs); /* strings in the vector are stolen by FmArchiver. */
301 }
302 }
303 g_key_file_free(kf);
304 }
305
_fm_archiver_finalize()306 void _fm_archiver_finalize()
307 {
308 g_list_foreach(archivers, (GFunc)fm_archiver_free, NULL);
309 g_list_free(archivers);
310 archivers = NULL;
311 default_archiver = NULL;
312 }
313