Evince
Evince is a document viewer capable of displaying multiple and single page document formats like PDF and Postscript.
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ev-file-helpers.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2002 Jorn Baayen
3  * Copyright © 2009 Christian Persch
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19 
20 #include <config.h>
21 
22 #include <stdlib.h>
23 #include <sys/types.h>
24 #include <unistd.h>
25 #include <string.h>
26 #include <errno.h>
27 
28 #include <glib.h>
29 #include <glib/gstdio.h>
30 #include <glib/gi18n-lib.h>
31 
32 #include "ev-file-helpers.h"
33 
34 static gchar *tmp_dir = NULL;
35 
36 /*
37  * ev_dir_ensure_exists:
38  * @dir: the directory name
39  * @mode: permissions to use when creating the directory
40  * @error: a location to store a #GError
41  *
42  * Create @dir recursively with permissions @mode.
43  *
44  * Returns: %TRUE on success, or %FALSE on error with @error filled in
45  */
46 static gboolean
47 _ev_dir_ensure_exists (const gchar *dir,
48  int mode,
49  GError **error)
50 {
51  int errsv;
52  char *display_name;
53 
54  g_return_val_if_fail (dir != NULL, FALSE);
55 
56  errno = 0;
57  if (g_mkdir_with_parents (dir, mode) == 0)
58  return TRUE;
59 
60  errsv = errno;
61  if (errsv == EEXIST && g_file_test (dir, G_FILE_TEST_IS_DIR))
62  return TRUE;
63 
64  display_name = g_filename_display_name (dir);
65  g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
66  "Failed to create directory '%s': %s",
67  display_name, g_strerror (errsv));
68  g_free (display_name);
69 
70  return FALSE;
71 }
72 
73 /*
74  * _ev_tmp_dir:
75  * @error: a location to store a #GError
76  *
77  * Returns the tmp directory.
78  *
79  * Returns: the tmp directory, or %NULL with @error filled in if the
80  * directory could not be created
81  */
82 static const char *
83 _ev_tmp_dir (GError **error)
84 {
85 
86  if (tmp_dir == NULL) {
87  gchar *dirname;
88  const gchar *prgname;
89 
90  prgname = g_get_prgname ();
91  dirname = g_strdup_printf ("%s-%u", prgname ? prgname : "unknown", getpid ());
92  tmp_dir = g_build_filename (g_get_tmp_dir (), dirname, NULL);
93  g_free (dirname);
94  }
95 
96  if (!_ev_dir_ensure_exists (tmp_dir, 0700, error))
97  return NULL;
98 
99  return tmp_dir;
100 }
101 
102 void
104 {
105 }
106 
107 void
109 {
110  if (tmp_dir != NULL)
111  g_rmdir (tmp_dir);
112 
113  g_free (tmp_dir);
114  tmp_dir = NULL;
115 }
116 
128 int
129 ev_mkstemp (const char *tmpl,
130  char **file_name,
131  GError **error)
132 {
133  const char *tmp;
134  char *name;
135  int fd;
136 
137  if ((tmp = _ev_tmp_dir (error)) == NULL)
138  return -1;
139 
140  name = g_build_filename (tmp, tmpl, NULL);
141  fd = g_mkstemp (name);
142 
143  if (fd == -1) {
144  int errsv = errno;
145 
146  g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
147  _("Failed to create a temporary file: %s"),
148  g_strerror (errsv));
149 
150  g_free (name);
151  return -1;
152  }
153 
154  if (file_name)
155  *file_name = name;
156 
157  return fd;
158 }
159 
160 static void
161 close_fd_cb (gpointer fdptr)
162 {
163  int fd = GPOINTER_TO_INT (fdptr);
164 
165  close (fd);
166 }
167 
178 GFile *
179 ev_mkstemp_file (const char *tmpl,
180  GError **error)
181 {
182  char *file_name;
183  int fd;
184  GFile *file;
185 
186  fd = ev_mkstemp (tmpl, &file_name, error);
187  if (fd == -1)
188  return NULL;
189 
190  file = g_file_new_for_path (file_name);
191  g_free (file_name);
192 
193  g_object_set_data_full (G_OBJECT (file), "ev-mkstemp-fd",
194  GINT_TO_POINTER (fd), (GDestroyNotify) close_fd_cb);
195 
196  return file;
197 }
198 
199 /*
200  * This function is copied from
201  * http://bugzilla.gnome.org/show_bug.cgi?id=524831
202  * and renamed from g_mkdtemp to _ev_g_mkdtemp.
203  *
204  * If/when this function gets added to glib, it can be removed from
205  * evince' sources.
206  */
227 static gchar *
228 _ev_g_mkdtemp (gchar *tmpl)
229 {
230  static const char letters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
231  static const int NLETTERS = sizeof (letters) - 1;
232  static int counter = 0;
233  char *XXXXXX;
234  GTimeVal tv;
235  glong value;
236  int count;
237 
238  /* find the last occurrence of "XXXXXX" */
239  XXXXXX = g_strrstr (tmpl, "XXXXXX");
240 
241  if (!XXXXXX || strncmp (XXXXXX, "XXXXXX", 6))
242  {
243  errno = EINVAL;
244  return NULL;
245  }
246 
247  /* Get some more or less random data. */
248  g_get_current_time (&tv);
249  value = (tv.tv_usec ^ tv.tv_sec) + counter++;
250 
251  for (count = 0; count < 100; value += 7777, ++count)
252  {
253  glong v = value;
254 
255  /* Fill in the random bits. */
256  XXXXXX[0] = letters[v % NLETTERS];
257  v /= NLETTERS;
258  XXXXXX[1] = letters[v % NLETTERS];
259  v /= NLETTERS;
260  XXXXXX[2] = letters[v % NLETTERS];
261  v /= NLETTERS;
262  XXXXXX[3] = letters[v % NLETTERS];
263  v /= NLETTERS;
264  XXXXXX[4] = letters[v % NLETTERS];
265  v /= NLETTERS;
266  XXXXXX[5] = letters[v % NLETTERS];
267 
268  /* tmpl is in UTF-8 on Windows, thus use g_mkdir() */
269  if (g_mkdir (tmpl, 0700) == 0)
270  return tmpl;
271 
272  if (errno != EEXIST)
273  /* Any other error will apply also to other names we might
274  * try, and there are 2^32 or so of them, so give up now.
275  */
276  return NULL;
277  }
278 
279  /* We got out of the loop because we ran out of combinations to try. */
280  errno = EEXIST;
281  return NULL;
282 }
283 
294 gchar *
295 ev_mkdtemp (const char *tmpl,
296  GError **error)
297 {
298  const char *tmp;
299  char *name;
300 
301  if ((tmp = _ev_tmp_dir (error)) == NULL)
302  return NULL;
303 
304  name = g_build_filename (tmp, tmpl, NULL);
305  if (_ev_g_mkdtemp (name) == NULL) {
306  int errsv = errno;
307 
308  g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
309  _("Failed to create a temporary directory: %s"),
310  g_strerror (errsv));
311 
312  g_free (name);
313  return NULL;
314  }
315 
316  return name;
317 }
318 
319 /* Remove a local temp file created by evince */
320 void
321 ev_tmp_filename_unlink (const gchar *filename)
322 {
323  if (!filename)
324  return;
325 
326  if (!tmp_dir)
327  return;
328 
329  if (g_str_has_prefix (filename, tmp_dir)) {
330  g_unlink (filename);
331  }
332 }
333 
334 void
335 ev_tmp_file_unlink (GFile *file)
336 {
337  gboolean res;
338  GError *error = NULL;
339 
340  if (!file)
341  return;
342 
343  res = g_file_delete (file, NULL, &error);
344  if (!res) {
345  char *uri;
346 
347  uri = g_file_get_uri (file);
348  g_warning ("Unable to delete temp file %s: %s\n", uri, error->message);
349  g_free (uri);
350  g_error_free (error);
351  }
352 }
353 
354 void
355 ev_tmp_uri_unlink (const gchar *uri)
356 {
357  GFile *file;
358 
359  if (!uri)
360  return;
361 
362  file = g_file_new_for_uri (uri);
363  if (!g_file_is_native (file)) {
364  g_warning ("Attempting to delete non native uri: %s\n", uri);
365  g_object_unref (file);
366  return;
367  }
368 
369  ev_tmp_file_unlink (file);
370  g_object_unref (file);
371 }
372 
373 gboolean
374 ev_file_is_temp (GFile *file)
375 {
376  gchar *path;
377  gboolean retval;
378 
379  if (!g_file_is_native (file))
380  return FALSE;
381 
382  path = g_file_get_path (file);
383  if (!path)
384  return FALSE;
385 
386  retval = g_str_has_prefix (path, g_get_tmp_dir ());
387  g_free (path);
388 
389  return retval;
390 }
391 
402 gboolean
403 ev_xfer_uri_simple (const char *from,
404  const char *to,
405  GError **error)
406 {
407  GFile *source_file;
408  GFile *target_file;
409  gboolean result;
410 
411  if (!from)
412  return TRUE;
413 
414  g_return_val_if_fail (to != NULL, TRUE);
415 
416  source_file = g_file_new_for_uri (from);
417  target_file = g_file_new_for_uri (to);
418 
419  result = g_file_copy (source_file, target_file,
420  G_FILE_COPY_TARGET_DEFAULT_PERMS |
421  G_FILE_COPY_OVERWRITE,
422  NULL, NULL, NULL, error);
423 
424  g_object_unref (target_file);
425  g_object_unref (source_file);
426 
427  return result;
428 }
429 
443 gboolean
444 ev_file_copy_metadata (const char *from,
445  const char *to,
446  GError **error)
447 {
448  GFile *source_file;
449  GFile *target_file;
450  gboolean result;
451 
452  g_return_val_if_fail (from != NULL, FALSE);
453  g_return_val_if_fail (to != NULL, FALSE);
454 
455  source_file = g_file_new_for_uri (from);
456  target_file = g_file_new_for_uri (to);
457 
458  result = g_file_copy_attributes (source_file, target_file,
459  G_FILE_COPY_ALL_METADATA |
460  G_FILE_COPY_TARGET_DEFAULT_PERMS,
461  NULL, error);
462 
463  g_object_unref (target_file);
464  g_object_unref (source_file);
465 
466  return result;
467 }
468 
469 static gchar *
470 get_mime_type_from_uri (const gchar *uri, GError **error)
471 {
472  GFile *file;
473  GFileInfo *file_info;
474  const gchar *content_type;
475  gchar *mime_type = NULL;
476 
477  file = g_file_new_for_uri (uri);
478  file_info = g_file_query_info (file,
479  G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
480  0, NULL, error);
481  g_object_unref (file);
482 
483  if (file_info == NULL)
484  return NULL;
485 
486  content_type = g_file_info_get_content_type (file_info);
487  if (content_type != NULL) {
488  mime_type = g_content_type_get_mime_type (content_type);
489  }
490  if (mime_type == NULL) {
491  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
492  _("Unknown MIME Type"));
493  }
494 
495  g_object_unref (file_info);
496  return mime_type;
497 }
498 
499 static gchar *
500 get_mime_type_from_data (const gchar *uri, GError **error)
501 {
502  GFile *file;
503  GFileInputStream *input_stream;
504  gssize size_read;
505  guchar buffer[1024];
506  gboolean retval;
507  gchar *content_type;
508  gchar *mime_type = NULL;
509 
510  file = g_file_new_for_uri (uri);
511 
512  input_stream = g_file_read (file, NULL, error);
513  if (!input_stream) {
514  g_object_unref (file);
515  return NULL;
516  }
517 
518  size_read = g_input_stream_read (G_INPUT_STREAM (input_stream),
519  buffer, sizeof (buffer), NULL, error);
520  if (size_read == -1) {
521  g_object_unref (input_stream);
522  g_object_unref (file);
523  return NULL;
524  }
525 
526  retval = g_input_stream_close (G_INPUT_STREAM (input_stream), NULL, error);
527 
528  g_object_unref (input_stream);
529  g_object_unref (file);
530  if (!retval)
531  return NULL;
532 
533  content_type = g_content_type_guess (NULL, /* no filename */
534  buffer, size_read,
535  NULL);
536  if (content_type == NULL) {
537  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
538  _("Unknown MIME Type"));
539  return NULL;
540  }
541 
542 #ifndef G_OS_WIN32
543  /* On Windows, the implementation of g_content_type_guess() is
544  * sometimes too limited, so we do use get_mime_type_from_uri()
545  * as a fallback */
546  if (strcmp (content_type, "*") == 0) {
547  g_free (content_type);
548  return get_mime_type_from_uri (uri, error);
549  }
550 #endif /* G_OS_WIN32 */
551 
552  mime_type = g_content_type_get_mime_type (content_type);
553  g_free (content_type);
554 
555  if (mime_type == NULL) {
556  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
557  _("Unknown MIME Type"));
558  }
559 
560  return mime_type;
561 }
562 
572 gchar *
573 ev_file_get_mime_type (const gchar *uri,
574  gboolean fast,
575  GError **error)
576 {
577  return fast ? get_mime_type_from_uri (uri, error) : get_mime_type_from_data (uri, error);
578 }
579 
580 /* Compressed files support */
581 
582 static const char *compressor_cmds[] = {
583  NULL,
584  "bzip2",
585  "gzip",
586  "xz"
587 };
588 
589 #define N_ARGS 4
590 #define BUFFER_SIZE 1024
591 
592 static gchar *
593 compression_run (const gchar *uri,
594  EvCompressionType type,
595  gboolean compress,
596  GError **error)
597 {
598  gchar *argv[N_ARGS];
599  gchar *uri_dst = NULL;
600  gchar *filename, *filename_dst = NULL;
601  gchar *cmd;
602  gint fd, pout;
603  GError *err = NULL;
604 
605  if (type == EV_COMPRESSION_NONE)
606  return NULL;
607 
608  cmd = g_find_program_in_path (compressor_cmds[type]);
609  if (!cmd) {
610  /* FIXME: better error codes! */
611  /* FIXME: i18n later */
612  g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
613  "Failed to find the \"%s\" command in the search path.",
614  compressor_cmds[type]);
615  return NULL;
616  }
617 
618  filename = g_filename_from_uri (uri, NULL, error);
619  if (!filename) {
620  g_free (cmd);
621  return NULL;
622  }
623 
624  fd = ev_mkstemp ("comp.XXXXXX", &filename_dst, error);
625  if (fd == -1) {
626  g_free (cmd);
627  g_free (filename);
628 
629  return NULL;
630  }
631 
632  argv[0] = cmd;
633  argv[1] = compress ? (char *) "-c" : (char *) "-cd";
634  argv[2] = filename;
635  argv[3] = NULL;
636 
637  if (g_spawn_async_with_pipes (NULL, argv, NULL,
638  G_SPAWN_STDERR_TO_DEV_NULL,
639  NULL, NULL, NULL,
640  NULL, &pout, NULL, &err)) {
641  GIOChannel *in, *out;
642  gchar buf[BUFFER_SIZE];
643  GIOStatus read_st, write_st;
644  gsize bytes_read, bytes_written;
645 
646  in = g_io_channel_unix_new (pout);
647  g_io_channel_set_encoding (in, NULL, NULL);
648  out = g_io_channel_unix_new (fd);
649  g_io_channel_set_encoding (out, NULL, NULL);
650 
651  do {
652  read_st = g_io_channel_read_chars (in, buf,
653  BUFFER_SIZE,
654  &bytes_read,
655  error);
656  if (read_st == G_IO_STATUS_NORMAL) {
657  write_st = g_io_channel_write_chars (out, buf,
658  bytes_read,
659  &bytes_written,
660  error);
661  if (write_st == G_IO_STATUS_ERROR)
662  break;
663  } else if (read_st == G_IO_STATUS_ERROR) {
664  break;
665  }
666  } while (bytes_read > 0);
667 
668  g_io_channel_unref (in);
669  g_io_channel_unref (out);
670  }
671 
672  close (fd);
673 
674  if (err) {
675  g_propagate_error (error, err);
676  } else {
677  uri_dst = g_filename_to_uri (filename_dst, NULL, error);
678  }
679 
680  g_free (cmd);
681  g_free (filename);
682  g_free (filename_dst);
683 
684  return uri_dst;
685 }
686 
705 gchar *
706 ev_file_uncompress (const gchar *uri,
707  EvCompressionType type,
708  GError **error)
709 {
710  g_return_val_if_fail (uri != NULL, NULL);
711 
712  return compression_run (uri, type, FALSE, error);
713 }
714 
734 gchar *
735 ev_file_compress (const gchar *uri,
736  EvCompressionType type,
737  GError **error)
738 {
739  g_return_val_if_fail (uri != NULL, NULL);
740 
741  return compression_run (uri, type, TRUE, error);
742 }