1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 
3 /*
4  *  File-Roller
5  *
6  *  Copyright (C) 2001, 2003 Free Software Foundation, Inc.
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program 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
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include <config.h>
23 #include <pwd.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <ctype.h>
28 #include <time.h>
29 #include <unistd.h>
30 #include <sys/param.h>
31 #include <sys/stat.h>
32 #include <sys/time.h>
33 #include <sys/types.h>
34 #include <dirent.h>
35 #include <glib.h>
36 #include <gio/gio.h>
37 #include "file-utils.h"
38 #include "glib-utils.h"
39 #include "fr-init.h"
40 
41 
42 #ifndef HAVE_MKDTEMP
43 #include "mkdtemp.h"
44 #endif
45 
46 #define BUF_SIZE 4096
47 #define FILE_PREFIX    "file://"
48 #define FILE_PREFIX_L  7
49 #define IS_SPECIAL_DIR(x) ((strcmp ((x), "..") == 0) || (strcmp ((x), ".") == 0))
50 
51 
52 /* path */
53 
54 
55 static const char *try_folder[] = { "cache", "~", "tmp", NULL };
56 
57 
58 static const char *
get_nth_temp_folder_to_try(int n)59 get_nth_temp_folder_to_try (int n)
60 {
61         const char *folder;
62 
63         folder = try_folder[n];
64         if (strcmp (folder, "cache") == 0)
65                 folder = g_get_user_cache_dir ();
66         else if (strcmp (folder, "~") == 0)
67                 folder = g_get_home_dir ();
68         else if (strcmp (folder, "tmp") == 0)
69                 folder = g_get_tmp_dir ();
70 
71         return folder;
72 }
73 
74 
75 char *
_g_path_get_temp_work_dir(const char * parent_folder)76 _g_path_get_temp_work_dir (const char *parent_folder)
77 {
78         guint64  max_size = 0;
79         char    *best_folder = NULL;
80         int      i;
81         char    *template;
82         char    *result = NULL;
83 
84         if (parent_folder == NULL) {
85                 /* find the folder with more free space. */
86 
87                 for (i = 0; try_folder[i] != NULL; i++) {
88                         const char *folder;
89                         GFile      *file;
90                         guint64     size;
91 
92                         folder = get_nth_temp_folder_to_try (i);
93                         file = g_file_new_for_path (folder);
94                         size = _g_file_get_free_space (file);
95                         g_object_unref (file);
96 
97                         if (max_size < size) {
98                                 max_size = size;
99                                 g_free (best_folder);
100                                 best_folder = g_strdup (folder);
101                         }
102                 }
103         }
104         else
105                 best_folder = g_strdup (parent_folder);
106 
107         if (best_folder == NULL)
108                 return NULL;
109 
110         template = g_strconcat (best_folder, "/.fr-XXXXXX", NULL);
111         result = mkdtemp (template);
112         g_free (best_folder);
113 
114         if ((result == NULL) || (*result == '\0')) {
115                 g_free (template);
116                 result = NULL;
117         }
118 
119         return result;
120 }
121 
122 
123 /* GFile */
124 
125 
126 static gboolean
_g_file_is_filetype(GFile * file,GFileType file_type)127 _g_file_is_filetype (GFile     *file,
128 		     GFileType  file_type)
129 {
130 	gboolean   result = FALSE;
131 	GFileInfo *info;
132 
133 	if (! g_file_query_exists (file, NULL))
134 		return FALSE;
135 
136 	info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_TYPE, 0, NULL, NULL);
137 	if (info != NULL) {
138 		result = (g_file_info_get_file_type (info) == file_type);
139 		g_object_unref (info);
140 	}
141 
142 	return result;
143 }
144 
145 
146 gboolean
_g_file_query_is_file(GFile * file)147 _g_file_query_is_file (GFile *file)
148 {
149 	return _g_file_is_filetype (file, G_FILE_TYPE_REGULAR);
150 }
151 
152 
153 gboolean
_g_file_query_is_dir(GFile * file)154 _g_file_query_is_dir (GFile *file)
155 {
156 	return _g_file_is_filetype (file, G_FILE_TYPE_DIRECTORY);
157 }
158 
159 
160 static time_t
_g_file_get_file_time_type(GFile * file,const char * type)161 _g_file_get_file_time_type (GFile      *file,
162 			    const char *type)
163 {
164 	time_t     result = 0;
165 	GFileInfo *info;
166 	GError    *err = NULL;
167 
168 	if (file == NULL)
169  		return 0;
170 
171 	info = g_file_query_info (file, type, 0, NULL, &err);
172 	if (err == NULL) {
173 		result = (time_t) g_file_info_get_attribute_uint64 (info, type);
174 	}
175 	else {
176 		g_warning ("Failed to get %s: %s", type, err->message);
177 		g_error_free (err);
178 		result = 0;
179 	}
180 
181 	g_object_unref (info);
182 
183 	return result;
184 }
185 
186 
187 goffset
_g_file_get_file_size(GFile * file)188 _g_file_get_file_size (GFile *file)
189 {
190 	goffset    size = 0;
191 	GFileInfo *info;
192 	GError    *error = NULL;
193 
194 	if (file == NULL)
195 		return 0;
196 
197 	info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_SIZE, 0, NULL, &error);
198 	if (info != NULL) {
199 		size = g_file_info_get_size (info);
200 		g_object_unref (info);
201 	}
202 	else {
203 		g_warning ("%s", error->message);
204 		g_error_free (error);
205 	}
206 
207 	return size;
208 }
209 
210 
211 time_t
_g_file_get_file_mtime(GFile * file)212 _g_file_get_file_mtime (GFile *file)
213 {
214 	return _g_file_get_file_time_type (file, G_FILE_ATTRIBUTE_TIME_MODIFIED);
215 }
216 
217 
218 time_t
_g_file_get_file_ctime(GFile * file)219 _g_file_get_file_ctime (GFile *file)
220 {
221 	return _g_file_get_file_time_type (file, G_FILE_ATTRIBUTE_TIME_CREATED);
222 }
223 
224 
225 const char *
_g_file_get_mime_type(GFile * file,gboolean fast_file_type)226 _g_file_get_mime_type (GFile    *file,
227                        gboolean  fast_file_type)
228 {
229 	GFileInfo  *info;
230 	GError     *error = NULL;
231  	const char *result = NULL;
232 
233  	info = g_file_query_info (file,
234 				  (fast_file_type ?
235 				   G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE :
236 				   G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE),
237 				  0,
238 				  NULL,
239 				  &error);
240 	if (info == NULL) {
241 		g_warning ("%s", error->message);
242 		g_clear_error (&error);
243 	}
244 	else {
245 		result = _g_str_get_static (g_file_info_get_content_type (info));
246 		g_object_unref (info);
247 	}
248 
249 	return result;
250 }
251 
252 
253 gboolean
_g_file_is_temp_dir(GFile * file)254 _g_file_is_temp_dir (GFile *file)
255 {
256 	gboolean  result = FALSE;
257 	char     *path;
258 
259 	path = g_file_get_path (file);
260 	if (path == NULL)
261 		result = TRUE;
262 	else if (strcmp (g_get_tmp_dir (), path) == 0)
263 		result = TRUE;
264 	else if (_g_path_is_parent_of (g_get_tmp_dir (), path))
265 		result = TRUE;
266 	else
267 		result = _g_file_is_temp_work_dir (file);
268 
269 	g_free (path);
270 
271 	return result;
272 }
273 
274 
275 GFile *
_g_file_create_alternative(GFile * folder,const char * name)276 _g_file_create_alternative (GFile      *folder,
277 			    const char *name)
278 {
279 	GFile *file = NULL;
280 	int    n = 1;
281 
282 	do {
283 		char *new_name;
284 
285 		_g_object_unref (file);
286 
287 		if (n == 1)
288 			new_name = g_strdup (name);
289 		else
290 			new_name = g_strdup_printf ("%s (%d)", name, n);
291 		n++;
292 
293 		file = g_file_get_child (folder, new_name);
294 
295 		g_free (new_name);
296 	}
297 	while (g_file_query_exists (file, NULL));
298 
299 	return file;
300 }
301 
302 
303 GFile *
_g_file_create_alternative_for_file(GFile * file)304 _g_file_create_alternative_for_file (GFile *file)
305 {
306 	GFile *parent;
307 	char  *name;
308 	GFile *new_file;
309 
310 	parent = g_file_get_parent (file);
311 	name = g_file_get_basename (file);
312 	new_file = _g_file_create_alternative (parent, name);
313 
314 	g_free (name);
315 	g_object_unref (parent);
316 
317 	return new_file;
318 }
319 
320 
321 gboolean
_g_file_check_permissions(GFile * file,int mode)322 _g_file_check_permissions (GFile *file,
323 			   int    mode)
324 {
325 	gboolean   result = TRUE;
326 	GFileInfo *info;
327 	GError    *err = NULL;
328 	gboolean   default_permission_when_unknown = TRUE;
329 
330 	info = g_file_query_info (file, "access::*", 0, NULL, &err);
331 	if (err != NULL) {
332 		g_clear_error (&err);
333 		result = FALSE;
334 	}
335 	else {
336 		if ((mode & R_OK) == R_OK) {
337 			if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ))
338 				result = (result && g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ));
339 			else
340 				result = (result && default_permission_when_unknown);
341 		}
342 
343 		if ((mode & W_OK) == W_OK) {
344 			if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE))
345 				result = (result && g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE));
346 			else
347 				result = (result && default_permission_when_unknown);
348 		}
349 
350 		if ((mode & X_OK) == X_OK) {
351 			if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE))
352 				result = (result && g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE));
353 			else
354 				result = (result && default_permission_when_unknown);
355 		}
356 
357 		g_object_unref (info);
358 	}
359 
360 	return result;
361 }
362 
363 
364 gboolean
_g_file_make_directory_tree(GFile * dir,mode_t mode,GError ** error)365 _g_file_make_directory_tree (GFile    *dir,
366 			     mode_t    mode,
367 			     GError  **error)
368 {
369 	gboolean  success = TRUE;
370 	GFile    *parent;
371 
372 	if ((dir == NULL) || g_file_query_exists (dir, NULL))
373 		return TRUE;
374 
375 	parent = g_file_get_parent (dir);
376 	if (parent != NULL) {
377 		success = _g_file_make_directory_tree (parent, mode, error);
378 		g_object_unref (parent);
379 		if (! success)
380 			return FALSE;
381 	}
382 
383 	success = g_file_make_directory (dir, NULL, error);
384 	if ((error != NULL) && (*error != NULL) && g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_EXISTS)) {
385 		g_clear_error (error);
386 		success = TRUE;
387 	}
388 
389 	if (success)
390 		g_file_set_attribute_uint32 (dir,
391 					     G_FILE_ATTRIBUTE_UNIX_MODE,
392 					     mode,
393 					     0,
394 					     NULL,
395 					     NULL);
396 
397 	return success;
398 }
399 
400 
401 gboolean
_g_file_remove_directory(GFile * directory,GCancellable * cancellable,GError ** error)402 _g_file_remove_directory (GFile         *directory,
403 			  GCancellable  *cancellable,
404 			  GError       **error)
405 {
406 	GFileEnumerator *enumerator;
407 	GFileInfo       *info;
408 	gboolean         error_occurred = FALSE;
409 
410 	if (directory == NULL)
411 		return TRUE;
412 
413 	enumerator = g_file_enumerate_children (directory,
414 					        G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_TYPE,
415 						G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
416 					        cancellable,
417 					        error);
418 
419 	while (! error_occurred && (info = g_file_enumerator_next_file (enumerator, cancellable, error)) != NULL) {
420 		GFile *child;
421 
422 		child = g_file_get_child (directory, g_file_info_get_name (info));
423 		switch (g_file_info_get_file_type (info)) {
424 		case G_FILE_TYPE_DIRECTORY:
425 			if (! _g_file_remove_directory (child, cancellable, error))
426 				error_occurred = TRUE;
427 			break;
428 		default:
429 			if (! g_file_delete (child, cancellable, error))
430 				error_occurred = TRUE;
431 			break;
432 		}
433 
434 		g_object_unref (child);
435 		g_object_unref (info);
436 	}
437 
438 	if (! error_occurred && ! g_file_delete (directory, cancellable, error))
439  		error_occurred = TRUE;
440 
441 	g_object_unref (enumerator);
442 
443 	return ! error_occurred;
444 }
445 
446 
447 GFile *
_g_file_new_user_config_subdir(const char * child_name,gboolean create_child)448 _g_file_new_user_config_subdir (const char *child_name,
449 			        gboolean    create_child)
450 {
451 	char   *full_path;
452 	GFile  *file;
453 	GError *error = NULL;
454 
455 	full_path = g_strconcat (g_get_user_config_dir (), "/", child_name, NULL);
456 	file = g_file_new_for_path (full_path);
457 	g_free (full_path);
458 
459 	if  (create_child && ! _g_file_make_directory_tree (file, 0700, &error)) {
460 		g_warning ("%s", error->message);
461 		g_error_free (error);
462 		g_object_unref (file);
463 		file = NULL;
464 	}
465 
466 	return file;
467 }
468 
469 
470 GFile *
_g_file_get_dir_content_if_unique(GFile * file)471 _g_file_get_dir_content_if_unique (GFile *file)
472 {
473 	GFileEnumerator *enumarator;
474 	GFileInfo       *info;
475 	GError          *error = NULL;
476 	GFile           *content = NULL;
477 
478 	if (! g_file_query_exists (file, NULL)) {
479 		g_object_unref (file);
480 		return NULL;
481 	}
482 
483 	enumarator = g_file_enumerate_children (file, G_FILE_ATTRIBUTE_STANDARD_NAME, 0, NULL, &error);
484 	if (error != NULL) {
485 		g_warning ("%s", error->message);
486 		g_error_free (error);
487 		return NULL;
488 	}
489 
490 	while ((info = g_file_enumerator_next_file (enumarator, NULL, &error)) != NULL) {
491 		const char *name;
492 
493 		if (error != NULL) {
494 			g_warning ("Failed to get info while enumerating children: %s", error->message);
495 			g_clear_error (&error);
496 			g_object_unref (info);
497 			continue;
498 		}
499 
500 		name = g_file_info_get_name (info);
501 		if ((strcmp (name, ".") == 0) || (strcmp (name, "..") == 0)) {
502 			g_object_unref (info);
503 			continue;
504 		}
505 
506 		if (content != NULL) {
507 			g_object_unref (content);
508 			g_object_unref (info);
509 			content = NULL;
510 			break;
511 		}
512 
513 		content = g_file_get_child (file, name);
514 
515 		g_object_unref (info);
516 	}
517 
518 	if (error != NULL) {
519 		g_warning ("Failed to get info after enumerating children: %s", error->message);
520 		g_clear_error (&error);
521 	}
522 
523 	g_object_unref (enumarator);
524 
525 	return content;
526 }
527 
528 
529 guint64
_g_file_get_free_space(GFile * file)530 _g_file_get_free_space (GFile *file)
531 {
532 	GFileInfo *info;
533 	guint64    freespace = 0;
534 	GError    *error = NULL;
535 
536 	info = g_file_query_filesystem_info (file, G_FILE_ATTRIBUTE_FILESYSTEM_FREE, NULL, &error);
537 	if (info != NULL) {
538 		freespace = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE);
539 		g_object_unref (info);
540 	}
541 	else {
542 		g_warning ("%s", error->message);
543 		g_error_free (error);
544 	}
545 
546 	return freespace;
547 }
548 
549 
550 GFile *
_g_file_get_temp_work_dir(GFile * parent_folder)551 _g_file_get_temp_work_dir (GFile *parent_folder)
552 {
553 	char  *parent_path;
554 	char  *tmp;
555 	GFile *file;
556 
557 	parent_path = (parent_folder != NULL) ? g_file_get_path (parent_folder) : NULL;
558 	tmp = _g_path_get_temp_work_dir (parent_path);
559 	file = g_file_new_for_path (tmp);
560 
561 	g_free (tmp);
562 	g_free (parent_path);
563 
564 	return file;
565 }
566 
567 
568 gboolean
_g_file_is_temp_work_dir(GFile * file)569 _g_file_is_temp_work_dir (GFile *file)
570 {
571 	gboolean  result = FALSE;
572 	char     *path;
573 	int       i;
574 
575 	path = g_file_get_path (file);
576 	if (path[0] != '/') {
577 		g_free (path);
578 		return FALSE;
579 	}
580 
581 	for (i = 0; try_folder[i] != NULL; i++) {
582 		const char *folder;
583 
584 		folder = get_nth_temp_folder_to_try (i);
585 		if (strncmp (path, folder, strlen (folder)) == 0) {
586 			if (strncmp (path + strlen (folder), "/.fr-", 5) == 0) {
587 				result = TRUE;
588 				break;
589 			}
590 		}
591 	}
592 
593 	g_free (path);
594 
595 	return result;
596 }
597 
598 
599 gboolean
_g_file_query_dir_is_empty(GFile * file)600 _g_file_query_dir_is_empty (GFile *file)
601 {
602 	GFileEnumerator *enumerator;
603 	GFileInfo       *info;
604 	GError          *error = NULL;
605 	int              n = 0;
606 
607 	if (! g_file_query_exists (file, NULL))
608 		return TRUE;
609 
610 	enumerator = g_file_enumerate_children (file, G_FILE_ATTRIBUTE_STANDARD_NAME, 0, NULL, &error);
611 	if (error != NULL) {
612 		g_warning ("%s", error->message);
613 		g_error_free (error);
614 		g_object_unref (enumerator);
615 		return TRUE;
616 	}
617 
618 	while ((n == 0) && ((info = g_file_enumerator_next_file (enumerator, NULL, &error)) != NULL)) {
619 		if (error != NULL) {
620 			g_warning ("%s", error->message);
621 			g_error_free (error);
622 		}
623 		else if (! IS_SPECIAL_DIR (g_file_info_get_name (info)))
624 			n++;
625 		g_object_unref (info);
626 	}
627 
628 	g_object_unref (enumerator);
629 
630 	return (n == 0);
631 }
632 
633 
634 gboolean
_g_file_dir_contains_one_object(GFile * file)635 _g_file_dir_contains_one_object (GFile *file)
636 {
637 	GFileEnumerator *enumerator;
638 	GFileInfo       *info;
639 	GError          *error = NULL;
640 	int              n = 0;
641 
642 	if (! g_file_query_exists (file, NULL))
643 		return FALSE;
644 
645 	enumerator = g_file_enumerate_children (file, G_FILE_ATTRIBUTE_STANDARD_NAME, 0, NULL, &error);
646 	if (error != NULL) {
647 		g_warning ("%s", error->message);
648 		g_error_free (error);
649 		g_object_unref (enumerator);
650 		return FALSE;
651 	}
652 
653 	while ((info = g_file_enumerator_next_file (enumerator, NULL, &error)) != NULL) {
654 		const char *name;
655 
656 		if (error != NULL) {
657 			g_warning ("%s", error->message);
658 			g_error_free (error);
659 			g_object_unref (info);
660 			continue;
661 		}
662 
663 		name = g_file_info_get_name (info);
664 		if (strcmp (name, ".") == 0 || strcmp (name, "..") == 0) {
665 			g_object_unref (info);
666  			continue;
667 		}
668 
669 		g_object_unref (info);
670 
671 		if (++n > 1)
672 			break;
673 	}
674 
675 	g_object_unref (enumerator);
676 
677 	return (n == 1);
678 }
679 
680 
681 /* program */
682 
683 
684 gboolean
_g_program_is_in_path(const char * filename)685 _g_program_is_in_path (const char *filename)
686 {
687 	char *str;
688 	char *value;
689 	int   result = FALSE;
690 
691 	value = g_hash_table_lookup (ProgramsCache, filename);
692 	if (value != NULL) {
693 		result = (strcmp (value, "1") == 0);
694 		return result;
695 	}
696 
697 	str = g_find_program_in_path (filename);
698 	if (str != NULL) {
699 		g_free (str);
700 		result = TRUE;
701 	}
702 
703 	g_hash_table_insert (ProgramsCache,
704 			     g_strdup (filename),
705 			     result ? "1" : "0");
706 
707 	return result;
708 }
709 
710 
711 gboolean
_g_program_is_available(const char * filename,gboolean check)712 _g_program_is_available (const char *filename,
713 		         gboolean    check)
714 {
715 	return ! check || _g_program_is_in_path (filename);
716 }
717 
718 
719 /* GKeyFile */
720 
721 
722 void
_g_key_file_save(GKeyFile * key_file,GFile * file)723 _g_key_file_save (GKeyFile *key_file,
724 	          GFile    *file)
725 {
726 	char   *file_data;
727 	gsize   size;
728 	GError *error = NULL;
729 
730 	file_data = g_key_file_to_data (key_file, &size, &error);
731 	if (error != NULL) {
732 		g_warning ("Could not save options: %s\n", error->message);
733 		g_clear_error (&error);
734 	}
735 	else {
736 		GFileOutputStream *stream;
737 
738 		stream = g_file_replace (file, NULL, FALSE, 0, NULL, &error);
739 		if (stream == NULL) {
740 			g_warning ("Could not save options: %s\n", error->message);
741 			g_clear_error (&error);
742 		}
743 		else if (! g_output_stream_write_all (G_OUTPUT_STREAM (stream), file_data, size, NULL, NULL, &error)) {
744 			g_warning ("Could not save options: %s\n", error->message);
745 			g_clear_error (&error);
746 		}
747 		else if (! g_output_stream_close (G_OUTPUT_STREAM (stream), NULL, &error)) {
748 			g_warning ("Could not save options: %s\n", error->message);
749 			g_clear_error (&error);
750 		}
751 
752 		g_object_unref (stream);
753 	}
754 
755 	g_free (file_data);
756 }
757