]> www.fi.muni.cz Git - evince.git/blob - libdocument/ev-file-helpers.c
Remove attachments from EvDocument interface and use EvDocumentAttachments instead
[evince.git] / libdocument / ev-file-helpers.c
1 /*
2  *  Copyright (C) 2002 Jorn Baayen
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2, or (at your option)
7  *  any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  *
18  *  $Id$
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <stdlib.h>
26 #include <sys/types.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <glib.h>
30 #include <glib/gstdio.h>
31 #include <errno.h>
32
33 #include "ev-file-helpers.h"
34
35 static gchar *tmp_dir = NULL;
36 static gint   count = 0;
37
38 gboolean
39 ev_dir_ensure_exists (const gchar *dir,
40                       int          mode)
41 {
42         if (g_mkdir_with_parents (dir, mode) == 0)
43                 return TRUE;
44
45         if (errno == EEXIST)
46                 return g_file_test (dir, G_FILE_TEST_IS_DIR);
47         
48         g_warning ("Failed to create directory %s: %s", dir, g_strerror (errno));
49         return FALSE;
50 }
51
52 const gchar *
53 ev_tmp_dir (void)
54 {
55         if (tmp_dir == NULL) {
56                 gboolean exists;
57                 gchar   *dirname, *prgname;
58
59                 prgname = g_get_prgname ();
60                 dirname = g_strdup_printf ("%s-%u", prgname ? prgname : "unknown", getpid ());
61                 tmp_dir = g_build_filename (g_get_tmp_dir (),
62                                             dirname,
63                                             NULL);
64                 g_free (dirname);
65
66                 exists = ev_dir_ensure_exists (tmp_dir, 0700);
67                 g_assert (exists);
68         }
69
70         return tmp_dir;
71 }
72
73 void
74 _ev_file_helpers_init (void)
75 {
76 }
77
78 void
79 _ev_file_helpers_shutdown (void)
80 {       
81         if (tmp_dir != NULL)    
82                 g_rmdir (tmp_dir);
83
84         g_free (tmp_dir);
85         tmp_dir = NULL;
86 }
87
88 GFile *
89 ev_tmp_file_get (const gchar *prefix)
90 {
91         gchar *path;
92         GFile *file;
93
94         path = ev_tmp_filename (prefix);
95         file = g_file_new_for_path (path);
96         
97         g_free (path);
98         
99         return file;
100 }
101
102 gchar * 
103 ev_tmp_filename (const gchar *prefix)
104 {
105         gchar *basename;
106         gchar *filename = NULL;
107
108         do {
109                 if (filename != NULL)
110                         g_free (filename);
111                         
112                 basename = g_strdup_printf ("%s-%d",
113                                             prefix ? prefix : "document",
114                                             count ++);
115                 
116                 filename = g_build_filename (ev_tmp_dir (),
117                                              basename, NULL);
118                 
119                 g_free (basename);
120         } while (g_file_test (filename, G_FILE_TEST_EXISTS));
121                         
122         return filename;
123 }
124
125 gchar * 
126 ev_tmp_directory (const gchar *prefix) 
127 {
128         return ev_tmp_filename (prefix ? prefix : "directory");
129 }
130
131 /* Remove a local temp file created by evince */
132 void
133 ev_tmp_filename_unlink (const gchar *filename)
134 {
135         const gchar *tempdir;
136         
137         if (!filename)
138                 return;
139
140         tempdir = g_get_tmp_dir ();
141         if (g_ascii_strncasecmp (filename, tempdir, strlen (tempdir)) == 0) {
142                 g_unlink (filename);
143         }
144 }
145
146 void
147 ev_tmp_file_unlink (GFile *file)
148 {
149         gboolean res;
150         GError  *error = NULL;
151
152         if (!file)
153                 return;
154         
155         res = g_file_delete (file, NULL, &error);
156         if (!res) {
157                 char *uri;
158                 
159                 uri = g_file_get_uri (file);
160                 g_warning ("Unable to delete temp file %s: %s\n", uri, error->message);
161                 g_free (uri);
162                 g_error_free (error);
163         }
164 }
165
166 void
167 ev_tmp_uri_unlink (const gchar *uri)
168 {
169         GFile *file;
170         
171         if (!uri)
172                 return;
173         
174         file = g_file_new_for_uri (uri);
175         if (!g_file_is_native (file)) {
176                 g_warning ("Attempting to delete non native uri: %s\n", uri);
177                 g_object_unref (file);
178                 return;
179         }
180         
181         ev_tmp_file_unlink (file);
182         g_object_unref (file);
183 }
184
185 /**
186  * ev_xfer_uri_simple:
187  * @from: the source URI
188  * @to: the target URI
189  * @error: a #GError location to store an error, or %NULL
190  *
191  * Performs a g_file_copy() from @from to @to.
192  *
193  * Returns: %TRUE on success, or %FALSE on error with @error filled in
194  */
195 gboolean
196 ev_xfer_uri_simple (const char *from,
197                     const char *to,
198                     GError     **error)
199 {
200         GFile *source_file;
201         GFile *target_file;
202         gboolean result;
203         
204         if (!from)
205                 return TRUE;
206
207         g_return_val_if_fail (to != NULL, TRUE);
208
209         source_file = g_file_new_for_uri (from);
210         target_file = g_file_new_for_uri (to);
211         
212         result = g_file_copy (source_file, target_file,
213 #if GLIB_CHECK_VERSION(2,19,0)
214                               G_FILE_COPY_TARGET_DEFAULT_PERMS |
215 #endif
216                               G_FILE_COPY_OVERWRITE,
217                               NULL, NULL, NULL, error);
218
219         g_object_unref (target_file);
220         g_object_unref (source_file);
221     
222         return result;
223 }
224
225 static gchar *
226 get_mime_type_from_uri (const gchar *uri, GError **error)
227 {
228         GFile       *file;
229         GFileInfo   *file_info;
230         const gchar *content_type;
231         gchar       *mime_type = NULL;
232
233         file = g_file_new_for_uri (uri);
234         file_info = g_file_query_info (file,
235                                        G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
236                                        0, NULL, error);
237         g_object_unref (file);
238
239         if (file_info == NULL)
240                 return NULL;
241
242         content_type = g_file_info_get_content_type (file_info);
243         if (content_type) {
244                 mime_type = g_content_type_get_mime_type (content_type);
245         }
246
247         g_object_unref (file_info);
248         return mime_type;
249 }
250
251 static gchar *
252 get_mime_type_from_data (const gchar *uri, GError **error)
253 {
254         GFile            *file;
255         GFileInputStream *input_stream;
256         gssize            size_read;
257         guchar            buffer[1024];
258         gboolean          retval;
259         gchar            *content_type, *mime_type;
260
261         file = g_file_new_for_uri (uri);
262         
263         input_stream = g_file_read (file, NULL, error);
264         if (!input_stream) {
265                 g_object_unref (file);
266                 return NULL;
267         }
268
269         size_read = g_input_stream_read (G_INPUT_STREAM (input_stream),
270                                          buffer, sizeof (buffer), NULL, error);
271         if (size_read == -1) {
272                 g_object_unref (input_stream);
273                 g_object_unref (file);
274                 return NULL;
275         }
276
277         retval = g_input_stream_close (G_INPUT_STREAM (input_stream), NULL, error);
278
279         g_object_unref (input_stream);
280         g_object_unref (file);
281         if (!retval)
282                 return NULL;
283
284         content_type = g_content_type_guess (NULL, /* no filename */
285                                              buffer, size_read,
286                                              NULL);
287         if (!content_type)
288                 return NULL;
289
290         mime_type = g_content_type_get_mime_type (content_type);
291         g_free (content_type);
292         return mime_type;
293 }
294
295 /**
296  * ev_file_get_mime_type:
297  * @uri: the URI
298  * @fast: whether to use fast MIME type detection
299  * @error: a #GError location to store an error, or %NULL
300  *
301  * Note: on unknown MIME types, this may return NULL without @error
302  * being filled in.
303  * 
304  * Returns: a newly allocated string with the MIME type of the file at
305  *   @uri, or %NULL on error or if the MIME type could not be determined
306  */
307 gchar *
308 ev_file_get_mime_type (const gchar *uri,
309                        gboolean     fast,
310                        GError     **error)
311 {
312         return fast ? get_mime_type_from_uri (uri, error) : get_mime_type_from_data (uri, error);
313 }
314
315 /* Compressed files support */
316 #define BZIPCOMMAND "bzip2"
317 #define GZIPCOMMAND "gzip"
318 #define N_ARGS      4
319 #define BUFFER_SIZE 1024
320
321 static gchar *
322 compression_run (const gchar       *uri,
323                  EvCompressionType  type,
324                  gboolean           compress, 
325                  GError           **error)
326 {
327         gchar *argv[N_ARGS];
328         gchar *uri_dst = NULL;
329         gchar *filename, *filename_dst;
330         gchar *cmd;
331         gint   fd, pout;
332         GError *err = NULL;
333
334         if (type == EV_COMPRESSION_NONE)
335                 return NULL;
336
337         cmd = g_find_program_in_path ((type == EV_COMPRESSION_BZIP2) ? BZIPCOMMAND : GZIPCOMMAND);
338         if (!cmd) {
339                 /* FIXME: better error codes! */
340                 /* FIXME: i18n later */
341                 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
342                              "Failed to find the \"%s\" command in the search path.",
343                              type == EV_COMPRESSION_BZIP2 ? BZIPCOMMAND : GZIPCOMMAND);
344                 return NULL;
345         }
346
347         filename = g_filename_from_uri (uri, NULL, error);
348         if (!filename) {
349                 g_free (cmd);
350                 return NULL;
351         }
352         
353         filename_dst = g_build_filename (ev_tmp_dir (), "evinceXXXXXX", NULL);
354         fd = g_mkstemp (filename_dst);
355         if (fd < 0) {
356                 int errsv = errno;
357
358                 g_free (cmd);
359                 g_free (filename);
360                 g_free (filename_dst);
361
362                 g_set_error (error, G_IO_ERROR,
363                              g_io_error_from_errno (errsv),
364                              "Error creating a temporary file: %s",
365                              g_strerror (errsv));
366                 return NULL;
367         }
368
369         argv[0] = cmd;
370         argv[1] = compress ? "-c" : "-cd";
371         argv[2] = filename;
372         argv[3] = NULL;
373
374         if (g_spawn_async_with_pipes (NULL, argv, NULL,
375                                       G_SPAWN_STDERR_TO_DEV_NULL,
376                                       NULL, NULL, NULL,
377                                       NULL, &pout, NULL, &err)) {
378                 GIOChannel *in, *out;
379                 gchar buf[BUFFER_SIZE];
380                 GIOStatus read_st, write_st;
381                 gsize bytes_read, bytes_written;
382
383                 in = g_io_channel_unix_new (pout);
384                 g_io_channel_set_encoding (in, NULL, NULL);
385                 out = g_io_channel_unix_new (fd);
386                 g_io_channel_set_encoding (out, NULL, NULL);
387
388                 do {
389                         read_st = g_io_channel_read_chars (in, buf,
390                                                            BUFFER_SIZE,
391                                                            &bytes_read,
392                                                            error);
393                         if (read_st == G_IO_STATUS_NORMAL) {
394                                 write_st = g_io_channel_write_chars (out, buf,
395                                                                      bytes_read,
396                                                                      &bytes_written,
397                                                                      error);
398                                 if (write_st == G_IO_STATUS_ERROR)
399                                         break;
400                         } else if (read_st == G_IO_STATUS_ERROR) {
401                                 break;
402                         }
403                 } while (bytes_read > 0);
404
405                 g_io_channel_unref (in);
406                 g_io_channel_unref (out);
407         }
408
409         close (fd);
410
411         if (err) {
412                 g_propagate_error (error, err);
413         } else {
414                 uri_dst = g_filename_to_uri (filename_dst, NULL, error);
415         }
416
417         g_free (cmd);
418         g_free (filename);
419         g_free (filename_dst);
420
421         return uri_dst;
422 }
423
424 /**
425  * ev_file_uncompress:
426  * @uri: a file URI
427  * @type: the compression type
428  * @error: a #GError location to store an error, or %NULL
429  *
430  * Uncompresses the file at @uri.
431  *
432  * If @type is %EV_COMPRESSION_NONE, it does nothing and returns %NULL.
433  *
434  * Otherwise, it returns the filename of a
435  * temporary file containing the decompressed data from the file at @uri.
436  * On error it returns %NULL and fills in @error.
437  *
438  * It is the caller's responsibility to unlink the temp file after use.
439  *
440  * Returns: a newly allocated string URI, or %NULL on error
441  */
442 gchar *
443 ev_file_uncompress (const gchar       *uri,
444                     EvCompressionType  type,
445                     GError           **error)
446 {
447         g_return_val_if_fail (uri != NULL, NULL);
448
449         return compression_run (uri, type, FALSE, error);
450 }
451
452 /**
453  * ev_file_compress:
454  * @uri: a file URI
455  * @type: the compression type
456  * @error: a #GError location to store an error, or %NULL
457  *
458  * Compresses the file at @uri.
459  
460  * If @type is %EV_COMPRESSION_NONE, it does nothing and returns %NULL.
461  *
462  * Otherwise, it returns the filename of a
463  * temporary file containing the compressed data from the file at @uri.
464  *
465  * On error it returns %NULL and fills in @error.
466  *
467  * It is the caller's responsibility to unlink the temp file after use.
468  *
469  * Returns: a newly allocated string URI, or %NULL on error
470  */
471 gchar *
472 ev_file_compress (const gchar       *uri,
473                   EvCompressionType  type,
474                   GError           **error)
475 {
476         g_return_val_if_fail (uri != NULL, NULL);
477
478         return compression_run (uri, type, TRUE, error);
479 }