~elementary-os/ubuntu-package-imports/evince-vivid

1 by RabbitBot
Initial import, version 3.14.2-0ubuntu2
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
103
_ev_file_helpers_init (void)
104
{
105
}
106
107
void
108
_ev_file_helpers_shutdown (void)
109
{	
110
	if (tmp_dir != NULL)	
111
		g_rmdir (tmp_dir);
112
113
	g_free (tmp_dir);
114
	tmp_dir = NULL;
115
}
116
117
/**
118
 * ev_mkstemp:
119
 * @tmpl: a template string; must contain 'XXXXXX', but not necessarily as a suffix
120
 * @file_name: a location to store the filename of the temp file
121
 * @error: a location to store a #GError
122
 *
123
 * Creates a temp file in the evince temp directory.
124
 *
125
 * Returns: a file descriptor to the newly created temp file name, or %-1
126
 *   on error with @error filled in
127
 */
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
168
/**
169
 * ev_mkstemp_file:
170
 * @tmpl: a template string; must contain 'XXXXXX', but not necessarily as a suffix
171
 * @error: a location to store a #GError
172
 *
173
 * Creates a temp #GFile in the evince temp directory. See ev_mkstemp() for more information.
174
 *
175
 * Returns: (transfer full): a newly allocated #GFile for the newly created temp file name, or %NULL
176
 *   on error with @error filled in
177
 */
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
 */
207
/**
208
 * g_mkdtemp:
209
 * @tmpl: template directory name
210
 *
211
 * Creates a temporary directory. See the mkdtemp() documentation
212
 * on most UNIX-like systems.
213
 *
214
 * The parameter is a string that should follow the rules for
215
 * mkdtemp() templates, i.e. contain the string "XXXXXX".  g_mkdtemp()
216
 * is slightly more flexible than mkdtemp() in that the sequence does
217
 * not have to occur at the very end of the template. The X string
218
 * will be modified to form the name of a directory that didn't
219
 * already exist.  The string should be in the GLib file name
220
 * encoding. Most importantly, on Windows it should be in UTF-8.
221
 *
222
 * Return value: If a temporary directory was successfully created,
223
 * @tmpl will be returned with the XXXXXX string modified in such a
224
 * way as to make the path unique.  In case of errors, %NULL is
225
 * returned.
226
 */
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
284
/**
285
 * ev_mkdtemp:
286
 * @tmpl: a template string; must end in 'XXXXXX'
287
 * @error: a location to store a #GError
288
 *
289
 * Creates a temp directory in the evince temp directory.
290
 *
291
 * Returns: a newly allocated string with the temp directory name, or %NULL
292
 *   on error with @error filled in
293
 */
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
392
/**
393
 * ev_xfer_uri_simple:
394
 * @from: the source URI
395
 * @to: the target URI
396
 * @error: a #GError location to store an error, or %NULL
397
 *
398
 * Performs a g_file_copy() from @from to @to.
399
 *
400
 * Returns: %TRUE on success, or %FALSE on error with @error filled in
401
 */
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
430
/**
431
 * ev_file_copy_metadata:
432
 * @from: the source URI
433
 * @to: the target URI
434
 * @error: a #GError location to store an error, or %NULL
435
 *
436
 * Performs a g_file_copy_attributes() with %G_FILE_COPY_ALL_METADATA
437
 * from @from to @to.
438
 *
439
 * Returns: %TRUE if the attributes were copied successfully, %FALSE otherwise.
440
 *
441
 * Since: 3.4
442
 */
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
                                         NULL, error);
461
462
        g_object_unref (target_file);
463
        g_object_unref (source_file);
464
465
        return result;
466
}
467
468
static gchar *
469
get_mime_type_from_uri (const gchar *uri, GError **error)
470
{
471
	GFile       *file;
472
	GFileInfo   *file_info;
473
	const gchar *content_type;
474
        gchar       *mime_type = NULL;
475
476
	file = g_file_new_for_uri (uri);
477
	file_info = g_file_query_info (file,
478
				       G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
479
				       0, NULL, error);
480
	g_object_unref (file);
481
482
	if (file_info == NULL)
483
		return NULL;
484
485
	content_type = g_file_info_get_content_type (file_info);
486
	if (content_type != NULL) {
487
                mime_type = g_content_type_get_mime_type (content_type);
488
        }
489
        if (mime_type == NULL) {
490
                g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
491
                                     _("Unknown MIME Type"));
492
        }
493
494
	g_object_unref (file_info);
495
	return mime_type;
496
}
497
498
static gchar *
499
get_mime_type_from_data (const gchar *uri, GError **error)
500
{
501
	GFile            *file;
502
	GFileInputStream *input_stream;
503
	gssize            size_read;
504
	guchar            buffer[1024];
505
	gboolean          retval;
506
	gchar            *content_type;
507
        gchar            *mime_type = NULL;
508
509
	file = g_file_new_for_uri (uri);
510
	
511
	input_stream = g_file_read (file, NULL, error);
512
	if (!input_stream) {
513
		g_object_unref (file);
514
		return NULL;
515
	}
516
517
	size_read = g_input_stream_read (G_INPUT_STREAM (input_stream),
518
					 buffer, sizeof (buffer), NULL, error);
519
	if (size_read == -1) {
520
		g_object_unref (input_stream);
521
		g_object_unref (file);
522
		return NULL;
523
	}
524
525
	retval = g_input_stream_close (G_INPUT_STREAM (input_stream), NULL, error);
526
527
	g_object_unref (input_stream);
528
	g_object_unref (file);
529
	if (!retval)
530
		return NULL;
531
532
	content_type = g_content_type_guess (NULL, /* no filename */
533
					     buffer, size_read,
534
					     NULL);
535
        if (content_type == NULL) {
536
                g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
537
                                     _("Unknown MIME Type"));
538
                return NULL;
539
        }
540
541
#ifndef G_OS_WIN32
542
       /* On Windows, the implementation of g_content_type_guess() is
543
        * sometimes too limited, so we do use get_mime_type_from_uri()
544
        * as a fallback */
545
       if (strcmp (content_type, "*") == 0) {
546
               g_free (content_type);
547
               return get_mime_type_from_uri (uri, error);
548
       }
549
#endif /* G_OS_WIN32 */
550
551
        mime_type = g_content_type_get_mime_type (content_type);
552
        g_free (content_type);
553
554
        if (mime_type == NULL) {
555
                g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
556
                                     _("Unknown MIME Type"));
557
        }
558
559
	return mime_type;
560
}
561
562
/**
563
 * ev_file_get_mime_type:
564
 * @uri: the URI
565
 * @fast: whether to use fast MIME type detection
566
 * @error: a #GError location to store an error, or %NULL
567
 *
568
 * Returns: a newly allocated string with the MIME type of the file at
569
 *   @uri, or %NULL on error or if the MIME type could not be determined
570
 */
571
gchar *
572
ev_file_get_mime_type (const gchar *uri,
573
		       gboolean     fast,
574
		       GError     **error)
575
{
576
	return fast ? get_mime_type_from_uri (uri, error) : get_mime_type_from_data (uri, error);
577
}
578
579
/* Compressed files support */
580
581
static const char *compressor_cmds[] = {
582
  NULL,
583
  "bzip2",
584
  "gzip",
585
  "xz"
586
};
587
588
#define N_ARGS      4
589
#define BUFFER_SIZE 1024
590
591
static gchar *
592
compression_run (const gchar       *uri,
593
		 EvCompressionType  type,
594
		 gboolean           compress, 
595
		 GError           **error)
596
{
597
	gchar *argv[N_ARGS];
598
	gchar *uri_dst = NULL;
599
	gchar *filename, *filename_dst = NULL;
600
	gchar *cmd;
601
	gint   fd, pout;
602
	GError *err = NULL;
603
604
	if (type == EV_COMPRESSION_NONE)
605
		return NULL;
606
607
	cmd = g_find_program_in_path (compressor_cmds[type]);
608
	if (!cmd) {
609
		/* FIXME: better error codes! */
610
		/* FIXME: i18n later */
611
		g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
612
			     "Failed to find the \"%s\" command in the search path.",
613
                             compressor_cmds[type]);
614
		return NULL;
615
	}
616
617
	filename = g_filename_from_uri (uri, NULL, error);
618
	if (!filename) {
619
		g_free (cmd);
620
		return NULL;
621
	}
622
623
        fd = ev_mkstemp ("comp.XXXXXX", &filename_dst, error);
624
	if (fd == -1) {
625
		g_free (cmd);
626
		g_free (filename);
627
628
		return NULL;
629
	}
630
631
	argv[0] = cmd;
632
	argv[1] = compress ? (char *) "-c" : (char *) "-cd";
633
	argv[2] = filename;
634
	argv[3] = NULL;
635
636
	if (g_spawn_async_with_pipes (NULL, argv, NULL,
637
				      G_SPAWN_STDERR_TO_DEV_NULL,
638
				      NULL, NULL, NULL,
639
				      NULL, &pout, NULL, &err)) {
640
		GIOChannel *in, *out;
641
		gchar buf[BUFFER_SIZE];
642
		GIOStatus read_st, write_st;
643
		gsize bytes_read, bytes_written;
644
645
		in = g_io_channel_unix_new (pout);
646
		g_io_channel_set_encoding (in, NULL, NULL);
647
		out = g_io_channel_unix_new (fd);
648
		g_io_channel_set_encoding (out, NULL, NULL);
649
650
		do {
651
			read_st = g_io_channel_read_chars (in, buf,
652
							   BUFFER_SIZE,
653
							   &bytes_read,
654
							   error);
655
			if (read_st == G_IO_STATUS_NORMAL) {
656
				write_st = g_io_channel_write_chars (out, buf,
657
								     bytes_read,
658
								     &bytes_written,
659
								     error);
660
				if (write_st == G_IO_STATUS_ERROR)
661
					break;
662
			} else if (read_st == G_IO_STATUS_ERROR) {
663
				break;
664
			}
665
		} while (bytes_read > 0);
666
667
		g_io_channel_unref (in);
668
		g_io_channel_unref (out);
669
	}
670
671
	close (fd);
672
673
	if (err) {
674
		g_propagate_error (error, err);
675
	} else {
676
		uri_dst = g_filename_to_uri (filename_dst, NULL, error);
677
	}
678
679
	g_free (cmd);
680
	g_free (filename);
681
	g_free (filename_dst);
682
683
	return uri_dst;
684
}
685
686
/**
687
 * ev_file_uncompress:
688
 * @uri: a file URI
689
 * @type: the compression type
690
 * @error: a #GError location to store an error, or %NULL
691
 *
692
 * Uncompresses the file at @uri.
693
 *
694
 * If @type is %EV_COMPRESSION_NONE, it does nothing and returns %NULL.
695
 *
696
 * Otherwise, it returns the filename of a
697
 * temporary file containing the decompressed data from the file at @uri.
698
 * On error it returns %NULL and fills in @error.
699
 *
700
 * It is the caller's responsibility to unlink the temp file after use.
701
 *
702
 * Returns: a newly allocated string URI, or %NULL on error
703
 */
704
gchar *
705
ev_file_uncompress (const gchar       *uri,
706
		    EvCompressionType  type,
707
		    GError           **error)
708
{
709
	g_return_val_if_fail (uri != NULL, NULL);
710
711
	return compression_run (uri, type, FALSE, error);
712
}
713
714
/**
715
 * ev_file_compress:
716
 * @uri: a file URI
717
 * @type: the compression type
718
 * @error: a #GError location to store an error, or %NULL
719
 *
720
 * Compresses the file at @uri.
721
 
722
 * If @type is %EV_COMPRESSION_NONE, it does nothing and returns %NULL.
723
 *
724
 * Otherwise, it returns the filename of a
725
 * temporary file containing the compressed data from the file at @uri.
726
 *
727
 * On error it returns %NULL and fills in @error.
728
 *
729
 * It is the caller's responsibility to unlink the temp file after use.
730
 *
731
 * Returns: a newly allocated string URI, or %NULL on error
732
 */
733
gchar *
734
ev_file_compress (const gchar       *uri,
735
		  EvCompressionType  type,
736
		  GError           **error)
737
{
738
	g_return_val_if_fail (uri != NULL, NULL);
739
740
	return compression_run (uri, type, TRUE, error);
741
}