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