1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
2 
3    caja-file.c: Caja file model.
4 
5    Copyright (C) 1999, 2000, 2001 Eazel, Inc.
6 
7    This program is free software; you can redistribute it and/or
8    modify it under the terms of the GNU General Public License as
9    published by the Free Software Foundation; either version 2 of the
10    License, or (at your option) any later version.
11 
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    General Public License for more details.
16 
17    You should have received a copy of the GNU General Public
18    License along with this program; if not, write to the
19    Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20    Boston, MA 02110-1301, USA.
21 
22    Author: Darin Adler <darin@bentspoon.com>
23 */
24 
25 #include <config.h>
26 #include <grp.h>
27 #include <gtk/gtk.h>
28 #include <glib/gi18n.h>
29 #include <glib/gstdio.h>
30 #include <gio/gio.h>
31 #include <glib.h>
32 #include <libxml/parser.h>
33 #include <pwd.h>
34 #include <stdlib.h>
35 #include <ctype.h>
36 #include <sys/time.h>
37 #include <time.h>
38 #include <unistd.h>
39 #include <sys/stat.h>
40 
41 #include <eel/eel-debug.h>
42 #include <eel/eel-glib-extensions.h>
43 #include <eel/eel-gtk-extensions.h>
44 #include <eel/eel-vfs-extensions.h>
45 #include <eel/eel-gtk-macros.h>
46 #include <eel/eel-string.h>
47 
48 #include <libcaja-extension/caja-file-info.h>
49 #include <libcaja-extension/caja-extension-private.h>
50 #include <libcaja-private/caja-extensions.h>
51 
52 #include "caja-file.h"
53 #include "caja-directory-notify.h"
54 #include "caja-directory-private.h"
55 #include "caja-signaller.h"
56 #include "caja-desktop-directory.h"
57 #include "caja-desktop-directory-file.h"
58 #include "caja-desktop-icon-file.h"
59 #include "caja-file-attributes.h"
60 #include "caja-file-private.h"
61 #include "caja-file-operations.h"
62 #include "caja-file-utilities.h"
63 #include "caja-global-preferences.h"
64 #include "caja-lib-self-check-functions.h"
65 #include "caja-link.h"
66 #include "caja-metadata.h"
67 #include "caja-module.h"
68 #include "caja-search-directory.h"
69 #include "caja-search-directory-file.h"
70 #include "caja-thumbnails.h"
71 #include "caja-ui-utilities.h"
72 #include "caja-vfs-file.h"
73 #include "caja-saved-search-file.h"
74 
75 #ifdef HAVE_SELINUX
76 #include <selinux/selinux.h>
77 #endif
78 
79 #define ICON_NAME_THUMBNAIL_LOADING   "image-loading"
80 
81 #undef CAJA_FILE_DEBUG_REF
82 #undef CAJA_FILE_DEBUG_REF_VALGRIND
83 
84 #ifdef CAJA_FILE_DEBUG_REF_VALGRIND
85 #include <valgrind/valgrind.h>
86 #define DEBUG_REF_PRINTF VALGRIND_PRINTF_BACKTRACE
87 #else
88 #define DEBUG_REF_PRINTF printf
89 #endif
90 
91 /* Files that start with these characters sort after files that don't. */
92 #define SORT_LAST_CHAR1 '.'
93 #define SORT_LAST_CHAR2 '#'
94 
95 #define METADATA_ID_IS_LIST_MASK (1U<<31)
96 
97 #define SORT_BY_EXTENSION_FOLLOWING_MAX_LENGTH 3
98 #define SORT_BY_EXTENSION_MAX_SEGMENTS 3
99 
100 typedef enum {
101 	SHOW_HIDDEN = 1 << 0,
102 	SHOW_BACKUP = 1 << 1,
103 } FilterOptions;
104 
105 typedef void (* ModifyListFunction) (GList **list, CajaFile *file);
106 
107 enum {
108 	CHANGED,
109 	UPDATED_DEEP_COUNT_IN_PROGRESS,
110 	LAST_SIGNAL
111 };
112 
113 static int date_format_pref;
114 
115 static guint signals[LAST_SIGNAL] = { 0 };
116 
117 static GHashTable *symbolic_links;
118 
119 static GQuark attribute_name_q,
120 	attribute_size_q,
121 	attribute_size_on_disk_q,
122 	attribute_type_q,
123 	attribute_creation_date_q,
124 	attribute_date_created_q,
125 	attribute_modification_date_q,
126 	attribute_date_modified_q,
127 	attribute_accessed_date_q,
128 	attribute_date_accessed_q,
129 	attribute_emblems_q,
130 	attribute_extension_q,
131 	attribute_mime_type_q,
132 	attribute_size_detail_q,
133 	attribute_size_on_disk_detail_q,
134 	attribute_deep_size_q,
135 	attribute_deep_size_on_disk_q,
136 	attribute_deep_file_count_q,
137 	attribute_deep_directory_count_q,
138 	attribute_deep_total_count_q,
139 	attribute_date_changed_q,
140 	attribute_trashed_on_q,
141 	attribute_trash_orig_path_q,
142 	attribute_date_permissions_q,
143 	attribute_permissions_q,
144 	attribute_selinux_context_q,
145 	attribute_octal_permissions_q,
146 	attribute_owner_q,
147 	attribute_group_q,
148 	attribute_uri_q,
149 	attribute_where_q,
150 	attribute_link_target_q,
151 	attribute_volume_q,
152 	attribute_free_space_q;
153 
154 static void     caja_file_info_iface_init                (CajaFileInfoIface *iface);
155 static char *   caja_file_get_owner_as_string            (CajaFile          *file,
156 							      gboolean               include_real_name);
157 static char *   caja_file_get_type_as_string             (CajaFile          *file);
158 static gboolean update_info_and_name                         (CajaFile          *file,
159 							      GFileInfo             *info);
160 static const char * caja_file_peek_display_name (CajaFile *file);
161 static const char * caja_file_peek_display_name_collation_key (CajaFile *file);
162 static void file_mount_unmounted (GMount *mount,  gpointer data);
163 static void metadata_hash_free (GHashTable *hash);
164 
165 G_DEFINE_TYPE_WITH_CODE (CajaFile, caja_file, G_TYPE_OBJECT,
166                          G_ADD_PRIVATE (CajaFile)
167 			 G_IMPLEMENT_INTERFACE (CAJA_TYPE_FILE_INFO,
168 						caja_file_info_iface_init));
169 
170 static void
caja_file_init(CajaFile * file)171 caja_file_init (CajaFile *file)
172 {
173 	file->details = caja_file_get_instance_private (file);
174 
175 	caja_file_clear_info (file);
176 	caja_file_invalidate_extension_info_internal (file);
177 }
178 
179 static GObject*
caja_file_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_params)180 caja_file_constructor (GType                  type,
181 			   guint                  n_construct_properties,
182 			   GObjectConstructParam *construct_params)
183 {
184   GObject *object;
185   CajaFile *file;
186 
187   object = (* G_OBJECT_CLASS (caja_file_parent_class)->constructor) (type,
188 									 n_construct_properties,
189 									 construct_params);
190 
191   file = CAJA_FILE (object);
192 
193   /* Set to default type after full construction */
194   if (CAJA_FILE_GET_CLASS (file)->default_file_type != G_FILE_TYPE_UNKNOWN) {
195 	  file->details->type = CAJA_FILE_GET_CLASS (file)->default_file_type;
196   }
197 
198   return object;
199 }
200 
201 gboolean
caja_file_set_display_name(CajaFile * file,const char * display_name,const char * edit_name,gboolean custom)202 caja_file_set_display_name (CajaFile *file,
203 				const char *display_name,
204 				const char *edit_name,
205 				gboolean custom)
206 {
207 	gboolean changed;
208 
209 	if (custom && display_name == NULL) {
210 		/* We're re-setting a custom display name, invalidate it if
211 		   we already set it so that the old one is re-read */
212 		if (file->details->got_custom_display_name) {
213 			file->details->got_custom_display_name = FALSE;
214 			caja_file_invalidate_attributes (file,
215 							     CAJA_FILE_ATTRIBUTE_INFO);
216 		}
217 		return FALSE;
218 	}
219 
220 	if (display_name == NULL || *display_name == 0) {
221 		return FALSE;
222 	}
223 
224 	if (!custom && file->details->got_custom_display_name) {
225 		return FALSE;
226 	}
227 
228 	if (edit_name == NULL) {
229 		edit_name = display_name;
230 	}
231 
232 	changed = FALSE;
233 
234 	if (eel_strcmp (file->details->display_name, display_name) != 0) {
235 		changed = TRUE;
236 
237 		g_clear_pointer (&file->details->display_name, g_ref_string_release);
238 
239 		if (eel_strcmp (file->details->name, display_name) == 0) {
240 			file->details->display_name = g_ref_string_acquire (file->details->name);
241 		} else {
242 			file->details->display_name = g_ref_string_new (display_name);
243 		}
244 
245 		g_free (file->details->display_name_collation_key);
246 		file->details->display_name_collation_key = g_utf8_collate_key_for_filename (display_name, -1);
247 	}
248 
249 	if (eel_strcmp (file->details->edit_name, edit_name) != 0) {
250 		changed = TRUE;
251 
252 		g_clear_pointer (&file->details->edit_name, g_ref_string_release);
253 		if (eel_strcmp (file->details->display_name, edit_name) == 0) {
254 			file->details->edit_name = g_ref_string_acquire (file->details->display_name);
255 		} else {
256 			file->details->edit_name = g_ref_string_new (edit_name);
257 		}
258 	}
259 
260 	file->details->got_custom_display_name = custom;
261 	return changed;
262 }
263 
264 static void
caja_file_clear_display_name(CajaFile * file)265 caja_file_clear_display_name (CajaFile *file)
266 {
267 	g_clear_pointer (&file->details->display_name, g_ref_string_release);
268 	file->details->display_name = NULL;
269 	g_free (file->details->display_name_collation_key);
270 	file->details->display_name_collation_key = NULL;
271 	g_clear_pointer (&file->details->edit_name, g_ref_string_release);
272 	file->details->edit_name = NULL;
273 }
274 
275 static gboolean
foreach_metadata_free(gpointer key,gpointer value,gpointer user_data)276 foreach_metadata_free (gpointer  key,
277 		       gpointer  value,
278 		       gpointer  user_data)
279 {
280 	guint id;
281 
282 	id = GPOINTER_TO_UINT (key);
283 
284 	if (id & METADATA_ID_IS_LIST_MASK) {
285 		g_strfreev ((char **)value);
286 	} else {
287 		g_free ((char *)value);
288 	}
289 	return TRUE;
290 }
291 
292 
293 static void
metadata_hash_free(GHashTable * hash)294 metadata_hash_free (GHashTable *hash)
295 {
296 	g_hash_table_foreach_remove (hash,
297 				     foreach_metadata_free,
298 				     NULL);
299 	g_hash_table_destroy (hash);
300 }
301 
302 static gboolean
metadata_hash_equal(GHashTable * hash1,GHashTable * hash2)303 metadata_hash_equal (GHashTable *hash1,
304 		     GHashTable *hash2)
305 {
306 	GHashTableIter iter;
307 	gpointer key1, value1, value2;
308 	guint id;
309 
310 	if (hash1 == NULL && hash2 == NULL) {
311 		return TRUE;
312 	}
313 
314 	if (hash1 == NULL || hash2 == NULL) {
315 		return FALSE;
316 	}
317 
318 	if (g_hash_table_size (hash1) !=
319 	    g_hash_table_size (hash2)) {
320 		return FALSE;
321 	}
322 
323 	g_hash_table_iter_init (&iter, hash1);
324 	while (g_hash_table_iter_next (&iter, &key1, &value1)) {
325 		value2 = g_hash_table_lookup (hash2, key1);
326 		if (value2 == NULL) {
327 			return FALSE;
328 		}
329 		id = GPOINTER_TO_UINT (key1);
330 		if (id & METADATA_ID_IS_LIST_MASK) {
331 			if (!eel_g_strv_equal ((char **)value1, (char **)value2)) {
332 				return FALSE;
333 			}
334 		} else {
335 			if (strcmp ((char *)value1, (char *)value2) != 0) {
336 				return FALSE;
337 			}
338 		}
339 	}
340 
341 	return TRUE;
342 }
343 
344 static void
clear_metadata(CajaFile * file)345 clear_metadata (CajaFile *file)
346 {
347 	if (file->details->metadata) {
348 		metadata_hash_free (file->details->metadata);
349 		file->details->metadata = NULL;
350 	}
351 }
352 
353 static GHashTable *
get_metadata_from_info(GFileInfo * info)354 get_metadata_from_info (GFileInfo *info)
355 {
356 	GHashTable *metadata;
357 	char **attrs;
358 	guint id;
359 	int i;
360 	GFileAttributeType type;
361 	gpointer value;
362 
363 	attrs = g_file_info_list_attributes (info, "metadata");
364 
365 	metadata = g_hash_table_new (NULL, NULL);
366 
367 	for (i = 0; attrs[i] != NULL; i++) {
368 		id = caja_metadata_get_id (attrs[i] + strlen ("metadata::"));
369 		if (id == 0) {
370 			continue;
371 		}
372 
373 		if (!g_file_info_get_attribute_data (info, attrs[i],
374 						     &type, &value, NULL)) {
375 			continue;
376 		}
377 
378 		if (type == G_FILE_ATTRIBUTE_TYPE_STRING) {
379 			g_hash_table_insert (metadata, GUINT_TO_POINTER (id),
380 					     g_strdup ((char *)value));
381 		} else if (type == G_FILE_ATTRIBUTE_TYPE_STRINGV) {
382 			id |= METADATA_ID_IS_LIST_MASK;
383 			g_hash_table_insert (metadata, GUINT_TO_POINTER (id),
384 					     g_strdupv ((char **)value));
385 		}
386 	}
387 
388 	g_strfreev (attrs);
389 
390 	return metadata;
391 }
392 
393 gboolean
caja_file_update_metadata_from_info(CajaFile * file,GFileInfo * info)394 caja_file_update_metadata_from_info (CajaFile *file,
395 					 GFileInfo *info)
396 {
397 	gboolean changed = FALSE;
398 
399 	if (g_file_info_has_namespace (info, "metadata")) {
400 		GHashTable *metadata;
401 
402 		metadata = get_metadata_from_info (info);
403 		if (!metadata_hash_equal (metadata,
404 					  file->details->metadata)) {
405 			changed = TRUE;
406 			clear_metadata (file);
407 			file->details->metadata = metadata;
408 		} else {
409 			metadata_hash_free (metadata);
410 		}
411 	} else if (file->details->metadata) {
412 		changed = TRUE;
413 		clear_metadata (file);
414 	}
415 	return changed;
416 }
417 
418 void
caja_file_clear_info(CajaFile * file)419 caja_file_clear_info (CajaFile *file)
420 {
421 	file->details->got_file_info = FALSE;
422 	if (file->details->get_info_error) {
423 		g_error_free (file->details->get_info_error);
424 		file->details->get_info_error = NULL;
425 	}
426 	/* Reset to default type, which might be other than unknown for
427 	   special kinds of files like the desktop or a search directory */
428 	file->details->type = CAJA_FILE_GET_CLASS (file)->default_file_type;
429 
430 	if (!file->details->got_custom_display_name) {
431 		caja_file_clear_display_name (file);
432 	}
433 
434 	if (!file->details->got_custom_activation_uri &&
435 	    file->details->activation_uri != NULL) {
436 		g_free (file->details->activation_uri);
437 		file->details->activation_uri = NULL;
438 	}
439 
440 	if (file->details->icon != NULL) {
441 		g_object_unref (file->details->icon);
442 		file->details->icon = NULL;
443 	}
444 
445 	g_free (file->details->thumbnail_path);
446 	file->details->thumbnail_path = NULL;
447 	file->details->thumbnailing_failed = FALSE;
448 
449 	file->details->is_launcher = FALSE;
450 	file->details->is_foreign_link = FALSE;
451 	file->details->is_trusted_link = FALSE;
452 	file->details->is_symlink = FALSE;
453 	file->details->is_hidden = FALSE;
454 	file->details->is_backup = FALSE;
455 	file->details->is_mountpoint = FALSE;
456 	file->details->uid = -1;
457 	file->details->gid = -1;
458 	file->details->can_read = TRUE;
459 	file->details->can_write = TRUE;
460 	file->details->can_execute = TRUE;
461 	file->details->can_delete = TRUE;
462 	file->details->can_trash = TRUE;
463 	file->details->can_rename = TRUE;
464 	file->details->can_mount = FALSE;
465 	file->details->can_unmount = FALSE;
466 	file->details->can_eject = FALSE;
467 	file->details->can_start = FALSE;
468 	file->details->can_start_degraded = FALSE;
469 	file->details->can_stop = FALSE;
470 	file->details->start_stop_type = G_DRIVE_START_STOP_TYPE_UNKNOWN;
471 	file->details->can_poll_for_media = FALSE;
472 	file->details->is_media_check_automatic = FALSE;
473 	file->details->has_permissions = FALSE;
474 	file->details->permissions = 0;
475 	file->details->size = -1;
476 	file->details->size_on_disk = -1;
477 	file->details->sort_order = 0;
478 	file->details->mtime = 0;
479 	file->details->atime = 0;
480 	file->details->ctime = 0;
481 	file->details->btime = 0;
482 	file->details->trash_time = 0;
483 	g_free (file->details->symlink_name);
484 	file->details->symlink_name = NULL;
485 	g_clear_pointer (&file->details->mime_type, g_ref_string_release);
486 	file->details->mime_type = NULL;
487 	g_free (file->details->selinux_context);
488 	file->details->selinux_context = NULL;
489 	g_free (file->details->description);
490 	file->details->description = NULL;
491 	g_clear_pointer (&file->details->owner, g_ref_string_release);
492 	file->details->owner = NULL;
493 	g_clear_pointer (&file->details->owner_real, g_ref_string_release);
494 	file->details->owner_real = NULL;
495 	g_clear_pointer (&file->details->group, g_ref_string_release);
496 	file->details->group = NULL;
497 
498 	g_clear_pointer (&file->details->filesystem_id, g_ref_string_release);
499 	file->details->filesystem_id = NULL;
500 
501 	clear_metadata (file);
502 }
503 
504 static CajaFile *
caja_file_new_from_filename(CajaDirectory * directory,const char * filename,gboolean self_owned)505 caja_file_new_from_filename (CajaDirectory *directory,
506 				 const char *filename,
507 				 gboolean self_owned)
508 {
509 	CajaFile *file;
510 
511 	g_assert (CAJA_IS_DIRECTORY (directory));
512 	g_assert (filename != NULL);
513 	g_assert (filename[0] != '\0');
514 
515 	if (CAJA_IS_DESKTOP_DIRECTORY (directory)) {
516 		if (self_owned) {
517 			file = CAJA_FILE (g_object_new (CAJA_TYPE_DESKTOP_DIRECTORY_FILE, NULL));
518 		} else {
519 			/* This doesn't normally happen, unless the user somehow types in a uri
520 			 * that references a file like this. (See #349840) */
521 			file = CAJA_FILE (g_object_new (CAJA_TYPE_VFS_FILE, NULL));
522 		}
523 	} else if (CAJA_IS_SEARCH_DIRECTORY (directory)) {
524 		if (self_owned) {
525 			file = CAJA_FILE (g_object_new (CAJA_TYPE_SEARCH_DIRECTORY_FILE, NULL));
526 		} else {
527 			/* This doesn't normally happen, unless the user somehow types in a uri
528 			 * that references a file like this. (See #349840) */
529 			file = CAJA_FILE (g_object_new (CAJA_TYPE_VFS_FILE, NULL));
530 		}
531 	} else if (g_str_has_suffix (filename, CAJA_SAVED_SEARCH_EXTENSION)) {
532 		file = CAJA_FILE (g_object_new (CAJA_TYPE_SAVED_SEARCH_FILE, NULL));
533 	} else {
534 		file = CAJA_FILE (g_object_new (CAJA_TYPE_VFS_FILE, NULL));
535 	}
536 
537 	file->details->directory = caja_directory_ref (directory);
538 
539 	file->details->name = g_ref_string_new (filename);
540 
541 #ifdef CAJA_FILE_DEBUG_REF
542 	DEBUG_REF_PRINTF("%10p ref'd", file);
543 #endif
544 
545 	return file;
546 }
547 
548 static void
modify_link_hash_table(CajaFile * file,ModifyListFunction modify_function)549 modify_link_hash_table (CajaFile *file,
550 			ModifyListFunction modify_function)
551 {
552 	char *target_uri;
553 	gboolean found;
554 	gpointer original_key;
555 	GList **list_ptr;
556 
557 	/* Check if there is a symlink name. If none, we are OK. */
558 	if (file->details->symlink_name == NULL || !caja_file_is_symbolic_link (file)) {
559 		return;
560 	}
561 
562 	/* Create the hash table first time through. */
563 	if (symbolic_links == NULL) {
564 		symbolic_links = g_hash_table_new (g_str_hash, g_str_equal);
565 	}
566 
567 	target_uri = caja_file_get_symbolic_link_target_uri (file);
568 
569 	/* Find the old contents of the hash table. */
570 	found = g_hash_table_lookup_extended
571 		(symbolic_links, target_uri,
572 		 &original_key, (gpointer *)&list_ptr);
573 	if (!found) {
574 		list_ptr = g_new0 (GList *, 1);
575 		original_key = g_strdup (target_uri);
576 		g_hash_table_insert (symbolic_links, original_key, list_ptr);
577 	}
578 	(* modify_function) (list_ptr, file);
579 	if (*list_ptr == NULL) {
580 		g_hash_table_remove (symbolic_links, target_uri);
581 		g_free (list_ptr);
582 		g_free (original_key);
583 	}
584 	g_free (target_uri);
585 }
586 
587 static void
symbolic_link_weak_notify(gpointer data,GObject * where_the_object_was)588 symbolic_link_weak_notify (gpointer      data,
589 			   GObject      *where_the_object_was)
590 {
591 	GList **list = data;
592 	/* This really shouldn't happen, but we're seeing some strange things in
593 	   bug #358172 where the symlink hashtable isn't correctly updated. */
594 	*list = g_list_remove (*list, where_the_object_was);
595 }
596 
597 static void
add_to_link_hash_table_list(GList ** list,CajaFile * file)598 add_to_link_hash_table_list (GList **list, CajaFile *file)
599 {
600 	if (g_list_find (*list, file) != NULL) {
601 		g_warning ("Adding file to symlink_table multiple times. "
602 			   "Please add feedback of what you were doing at https://bugzilla.gnome.org/show_bug.cgi?id=358172\n");
603 		return;
604 	}
605 	g_object_weak_ref (G_OBJECT (file), symbolic_link_weak_notify, list);
606 	*list = g_list_prepend (*list, file);
607 }
608 
609 static void
add_to_link_hash_table(CajaFile * file)610 add_to_link_hash_table (CajaFile *file)
611 {
612 	modify_link_hash_table (file, add_to_link_hash_table_list);
613 }
614 
615 static void
remove_from_link_hash_table_list(GList ** list,CajaFile * file)616 remove_from_link_hash_table_list (GList **list, CajaFile *file)
617 {
618 	if (g_list_find (*list, file) != NULL) {
619 		g_object_weak_unref (G_OBJECT (file), symbolic_link_weak_notify, list);
620 		*list = g_list_remove (*list, file);
621 	}
622 }
623 
624 static void
remove_from_link_hash_table(CajaFile * file)625 remove_from_link_hash_table (CajaFile *file)
626 {
627 	modify_link_hash_table (file, remove_from_link_hash_table_list);
628 }
629 
630 CajaFile *
caja_file_new_from_info(CajaDirectory * directory,GFileInfo * info)631 caja_file_new_from_info (CajaDirectory *directory,
632 			     GFileInfo *info)
633 {
634 	CajaFile *file;
635 	const char *mime_type;
636 
637 	g_return_val_if_fail (CAJA_IS_DIRECTORY (directory), NULL);
638 	g_return_val_if_fail (info != NULL, NULL);
639 
640 	mime_type = g_file_info_get_content_type (info);
641 	if (mime_type &&
642 	    strcmp (mime_type, CAJA_SAVED_SEARCH_MIMETYPE) == 0) {
643 		g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
644 		file = CAJA_FILE (g_object_new (CAJA_TYPE_SAVED_SEARCH_FILE, NULL));
645 	} else {
646 		file = CAJA_FILE (g_object_new (CAJA_TYPE_VFS_FILE, NULL));
647 	}
648 
649 	file->details->directory = caja_directory_ref (directory);
650 
651 	update_info_and_name (file, info);
652 
653 #ifdef CAJA_FILE_DEBUG_REF
654 	DEBUG_REF_PRINTF("%10p ref'd", file);
655 #endif
656 
657 	return file;
658 }
659 
660 static CajaFile *
caja_file_get_internal(GFile * location,gboolean create)661 caja_file_get_internal (GFile *location, gboolean create)
662 {
663 	gboolean self_owned;
664 	CajaDirectory *directory;
665 	CajaFile *file;
666 	GFile *parent;
667 	char *basename;
668 
669 	g_assert (location != NULL);
670 
671 	parent = g_file_get_parent (location);
672 
673 	self_owned = FALSE;
674 	if (parent == NULL) {
675 		self_owned = TRUE;
676 		parent = g_object_ref (location);
677 	}
678 
679 	/* Get object that represents the directory. */
680 	directory = caja_directory_get_internal (parent, create);
681 
682 	g_object_unref (parent);
683 
684 	/* Get the name for the file. */
685 	if (self_owned && directory != NULL) {
686 		basename = caja_directory_get_name_for_self_as_new_file (directory);
687 	} else {
688 		basename = g_file_get_basename (location);
689 	}
690 	/* Check to see if it's a file that's already known. */
691 	if (directory == NULL) {
692 		file = NULL;
693 	} else if (self_owned) {
694 		file = directory->details->as_file;
695 	} else {
696 		file = caja_directory_find_file_by_name (directory, basename);
697 	}
698 
699 	/* Ref or create the file. */
700 	if (file != NULL) {
701 		caja_file_ref (file);
702 	} else if (create && directory != NULL) {
703 		file = caja_file_new_from_filename (directory, basename, self_owned);
704 		if (self_owned) {
705 			g_assert (directory->details->as_file == NULL);
706 			directory->details->as_file = file;
707 		} else {
708 			caja_directory_add_file (directory, file);
709 		}
710 	}
711 
712 	g_free (basename);
713 	caja_directory_unref (directory);
714 
715 	return file;
716 }
717 
718 CajaFile *
caja_file_get(GFile * location)719 caja_file_get (GFile *location)
720 {
721 	return caja_file_get_internal (location, TRUE);
722 }
723 
724 CajaFile *
caja_file_get_existing(GFile * location)725 caja_file_get_existing (GFile *location)
726 {
727 	return caja_file_get_internal (location, FALSE);
728 }
729 
730 CajaFile *
caja_file_get_existing_by_uri(const char * uri)731 caja_file_get_existing_by_uri (const char *uri)
732 {
733 	GFile *location;
734 	CajaFile *file;
735 
736 	location = g_file_new_for_uri (uri);
737 	file = caja_file_get_internal (location, FALSE);
738 	g_object_unref (location);
739 
740 	return file;
741 }
742 
743 CajaFile *
caja_file_get_by_uri(const char * uri)744 caja_file_get_by_uri (const char *uri)
745 {
746 	GFile *location;
747 	CajaFile *file;
748 
749 	location = g_file_new_for_uri (uri);
750 	file = caja_file_get_internal (location, TRUE);
751 	g_object_unref (location);
752 
753 	return file;
754 }
755 
756 gboolean
caja_file_is_self_owned(CajaFile * file)757 caja_file_is_self_owned (CajaFile *file)
758 {
759 	return file->details->directory->details->as_file == file;
760 }
761 
762 static void
finalize(GObject * object)763 finalize (GObject *object)
764 {
765 	CajaDirectory *directory;
766 	CajaFile *file;
767 
768 	file = CAJA_FILE (object);
769 
770 	g_assert (file->details->operations_in_progress == NULL);
771 
772 	if (file->details->is_thumbnailing) {
773 		char *uri;
774 
775 		uri = caja_file_get_uri (file);
776 		caja_thumbnail_remove_from_queue (uri);
777 		g_free (uri);
778 	}
779 
780 	caja_async_destroying_file (file);
781 
782 	remove_from_link_hash_table (file);
783 
784 	directory = file->details->directory;
785 
786 	if (caja_file_is_self_owned (file)) {
787 		directory->details->as_file = NULL;
788 	} else {
789 		if (!file->details->is_gone) {
790 			caja_directory_remove_file (directory, file);
791 		}
792 	}
793 
794 	if (file->details->get_info_error) {
795 		g_error_free (file->details->get_info_error);
796 	}
797 
798 	caja_directory_unref (directory);
799 	g_clear_pointer (&file->details->name, g_ref_string_release);
800 	g_clear_pointer (&file->details->display_name, g_ref_string_release);
801 	g_free (file->details->display_name_collation_key);
802 	g_clear_pointer (&file->details->edit_name, g_ref_string_release);
803 	if (file->details->icon) {
804 		g_object_unref (file->details->icon);
805 	}
806 	g_free (file->details->thumbnail_path);
807 	g_free (file->details->symlink_name);
808 	g_clear_pointer (&file->details->mime_type, g_ref_string_release);
809 	g_clear_pointer (&file->details->owner, g_ref_string_release);
810 	g_clear_pointer (&file->details->owner_real, g_ref_string_release);
811 	g_clear_pointer (&file->details->group, g_ref_string_release);
812 	g_free (file->details->selinux_context);
813 	g_free (file->details->description);
814 	g_free (file->details->top_left_text);
815 	g_free (file->details->custom_icon);
816 	g_free (file->details->activation_uri);
817 	g_free (file->details->compare_by_emblem_cache);
818 
819 	if (file->details->thumbnail) {
820 		g_object_unref (file->details->thumbnail);
821 	}
822 	if (file->details->mount) {
823 		g_signal_handlers_disconnect_by_func (file->details->mount, file_mount_unmounted, file);
824 		g_object_unref (file->details->mount);
825 	}
826 
827 	g_clear_pointer (&file->details->filesystem_id, g_ref_string_release);
828 
829 	g_list_free_full (file->details->mime_list, g_free);
830 	g_list_free_full (file->details->pending_extension_emblems, g_free);
831 	g_list_free_full (file->details->extension_emblems, g_free);
832 	g_list_free_full (file->details->pending_info_providers, g_object_unref);
833 
834 	if (file->details->pending_extension_attributes) {
835 		g_hash_table_destroy (file->details->pending_extension_attributes);
836 	}
837 
838 	if (file->details->extension_attributes) {
839 		g_hash_table_destroy (file->details->extension_attributes);
840 	}
841 
842 	if (file->details->metadata) {
843 		metadata_hash_free (file->details->metadata);
844 	}
845 
846 	G_OBJECT_CLASS (caja_file_parent_class)->finalize (object);
847 }
848 
849 CajaFile *
caja_file_ref(CajaFile * file)850 caja_file_ref (CajaFile *file)
851 {
852 	if (file == NULL) {
853 		return NULL;
854 	}
855 	g_return_val_if_fail (CAJA_IS_FILE (file), NULL);
856 
857 #ifdef CAJA_FILE_DEBUG_REF
858 	DEBUG_REF_PRINTF("%10p ref'd", file);
859 #endif
860 
861 	return g_object_ref (file);
862 }
863 
864 void
caja_file_unref(CajaFile * file)865 caja_file_unref (CajaFile *file)
866 {
867 	if (file == NULL) {
868 		return;
869 	}
870 
871 	g_return_if_fail (CAJA_IS_FILE (file));
872 
873 #ifdef CAJA_FILE_DEBUG_REF
874 	DEBUG_REF_PRINTF("%10p unref'd", file);
875 #endif
876 
877 	g_object_unref (file);
878 }
879 
880 /**
881  * caja_file_get_parent_uri_for_display:
882  *
883  * Get the uri for the parent directory.
884  *
885  * @file: The file in question.
886  *
887  * Return value: A string representing the parent's location,
888  * formatted for user display (including stripping "file://").
889  * If the parent is NULL, returns the empty string.
890  */
891 char *
caja_file_get_parent_uri_for_display(CajaFile * file)892 caja_file_get_parent_uri_for_display (CajaFile *file)
893 {
894 	GFile *parent;
895 	char *result;
896 
897 	g_assert (CAJA_IS_FILE (file));
898 
899 	parent = caja_file_get_parent_location (file);
900 	if (parent) {
901 		result = g_file_get_parse_name (parent);
902 		g_object_unref (parent);
903 	} else {
904 		result = g_strdup ("");
905 	}
906 
907 	return result;
908 }
909 
910 /**
911  * caja_file_get_parent_uri:
912  *
913  * Get the uri for the parent directory.
914  *
915  * @file: The file in question.
916  *
917  * Return value: A string for the parent's location, in "raw URI" form.
918  * Use caja_file_get_parent_uri_for_display instead if the
919  * result is to be displayed on-screen.
920  * If the parent is NULL, returns the empty string.
921  */
922 char *
caja_file_get_parent_uri(CajaFile * file)923 caja_file_get_parent_uri (CajaFile *file)
924 {
925 	g_assert (CAJA_IS_FILE (file));
926 
927 	if (caja_file_is_self_owned (file)) {
928 		/* Callers expect an empty string, not a NULL. */
929 		return g_strdup ("");
930 	}
931 
932 	return caja_directory_get_uri (file->details->directory);
933 }
934 
935 GFile *
caja_file_get_parent_location(CajaFile * file)936 caja_file_get_parent_location (CajaFile *file)
937 {
938 	g_assert (CAJA_IS_FILE (file));
939 
940 	if (caja_file_is_self_owned (file)) {
941 		/* Callers expect an empty string, not a NULL. */
942 		return NULL;
943 	}
944 
945 	return caja_directory_get_location (file->details->directory);
946 }
947 
948 CajaFile *
caja_file_get_parent(CajaFile * file)949 caja_file_get_parent (CajaFile *file)
950 {
951 	g_assert (CAJA_IS_FILE (file));
952 
953 	if (caja_file_is_self_owned (file)) {
954 		return NULL;
955 	}
956 
957 	return caja_directory_get_corresponding_file (file->details->directory);
958 }
959 
960 /**
961  * caja_file_can_read:
962  *
963  * Check whether the user is allowed to read the contents of this file.
964  *
965  * @file: The file to check.
966  *
967  * Return value: FALSE if the user is definitely not allowed to read
968  * the contents of the file. If the user has read permission, or
969  * the code can't tell whether the user has read permission,
970  * returns TRUE (so failures must always be handled).
971  */
972 gboolean
caja_file_can_read(CajaFile * file)973 caja_file_can_read (CajaFile *file)
974 {
975 	g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
976 
977 	return file->details->can_read;
978 }
979 
980 /**
981  * caja_file_can_write:
982  *
983  * Check whether the user is allowed to write to this file.
984  *
985  * @file: The file to check.
986  *
987  * Return value: FALSE if the user is definitely not allowed to write
988  * to the file. If the user has write permission, or
989  * the code can't tell whether the user has write permission,
990  * returns TRUE (so failures must always be handled).
991  */
992 gboolean
caja_file_can_write(CajaFile * file)993 caja_file_can_write (CajaFile *file)
994 {
995 	g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
996 
997 	return file->details->can_write;
998 }
999 
1000 /**
1001  * caja_file_can_execute:
1002  *
1003  * Check whether the user is allowed to execute this file.
1004  *
1005  * @file: The file to check.
1006  *
1007  * Return value: FALSE if the user is definitely not allowed to execute
1008  * the file. If the user has execute permission, or
1009  * the code can't tell whether the user has execute permission,
1010  * returns TRUE (so failures must always be handled).
1011  */
1012 gboolean
caja_file_can_execute(CajaFile * file)1013 caja_file_can_execute (CajaFile *file)
1014 {
1015 	g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
1016 
1017 	return file->details->can_execute;
1018 }
1019 
1020 gboolean
caja_file_can_mount(CajaFile * file)1021 caja_file_can_mount (CajaFile *file)
1022 {
1023 	g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
1024 
1025 	return file->details->can_mount;
1026 }
1027 
1028 gboolean
caja_file_can_unmount(CajaFile * file)1029 caja_file_can_unmount (CajaFile *file)
1030 {
1031 	g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
1032 
1033 	return file->details->can_unmount ||
1034 		(file->details->mount != NULL &&
1035 		 g_mount_can_unmount (file->details->mount));
1036 }
1037 
1038 gboolean
caja_file_can_eject(CajaFile * file)1039 caja_file_can_eject (CajaFile *file)
1040 {
1041 	g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
1042 
1043 	return file->details->can_eject ||
1044 		(file->details->mount != NULL &&
1045 		 g_mount_can_eject (file->details->mount));
1046 }
1047 
1048 gboolean
caja_file_can_start(CajaFile * file)1049 caja_file_can_start (CajaFile *file)
1050 {
1051 	gboolean ret;
1052 	GDrive *drive;
1053 
1054 	g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
1055 
1056 	ret = FALSE;
1057 
1058 	if (file->details->can_start) {
1059 		ret = TRUE;
1060 		goto out;
1061 	}
1062 
1063 	if (file->details->mount != NULL) {
1064 		drive = g_mount_get_drive (file->details->mount);
1065 		if (drive != NULL) {
1066 			ret = g_drive_can_start (drive);
1067 			g_object_unref (drive);
1068 		}
1069 	}
1070 
1071  out:
1072 	return ret;
1073 }
1074 
1075 gboolean
caja_file_can_start_degraded(CajaFile * file)1076 caja_file_can_start_degraded (CajaFile *file)
1077 {
1078 	gboolean ret;
1079 	GDrive *drive;
1080 
1081 	g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
1082 
1083 	ret = FALSE;
1084 
1085 	if (file->details->can_start_degraded) {
1086 		ret = TRUE;
1087 		goto out;
1088 	}
1089 
1090 	if (file->details->mount != NULL) {
1091 		drive = g_mount_get_drive (file->details->mount);
1092 		if (drive != NULL) {
1093 			ret = g_drive_can_start_degraded (drive);
1094 			g_object_unref (drive);
1095 		}
1096 	}
1097 
1098  out:
1099 	return ret;
1100 }
1101 
1102 gboolean
caja_file_can_poll_for_media(CajaFile * file)1103 caja_file_can_poll_for_media (CajaFile *file)
1104 {
1105 	gboolean ret;
1106 	GDrive *drive;
1107 
1108 	g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
1109 
1110 	ret = FALSE;
1111 
1112 	if (file->details->can_poll_for_media) {
1113 		ret = TRUE;
1114 		goto out;
1115 	}
1116 
1117 	if (file->details->mount != NULL) {
1118 		drive = g_mount_get_drive (file->details->mount);
1119 		if (drive != NULL) {
1120 			ret = g_drive_can_poll_for_media (drive);
1121 			g_object_unref (drive);
1122 		}
1123 	}
1124 
1125  out:
1126 	return ret;
1127 }
1128 
1129 gboolean
caja_file_is_media_check_automatic(CajaFile * file)1130 caja_file_is_media_check_automatic (CajaFile *file)
1131 {
1132 	gboolean ret;
1133 	GDrive *drive;
1134 
1135 	g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
1136 
1137 	ret = FALSE;
1138 
1139 	if (file->details->is_media_check_automatic) {
1140 		ret = TRUE;
1141 		goto out;
1142 	}
1143 
1144 	if (file->details->mount != NULL) {
1145 		drive = g_mount_get_drive (file->details->mount);
1146 		if (drive != NULL) {
1147 			ret = g_drive_is_media_check_automatic (drive);
1148 			g_object_unref (drive);
1149 		}
1150 	}
1151 
1152  out:
1153 	return ret;
1154 }
1155 
1156 
1157 gboolean
caja_file_can_stop(CajaFile * file)1158 caja_file_can_stop (CajaFile *file)
1159 {
1160 	gboolean ret;
1161 	GDrive *drive;
1162 
1163 	g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
1164 
1165 	ret = FALSE;
1166 
1167 	if (file->details->can_stop) {
1168 		ret = TRUE;
1169 		goto out;
1170 	}
1171 
1172 	if (file->details->mount != NULL) {
1173 		drive = g_mount_get_drive (file->details->mount);
1174 		if (drive != NULL) {
1175 			ret = g_drive_can_stop (drive);
1176 			g_object_unref (drive);
1177 		}
1178 	}
1179 
1180  out:
1181 	return ret;
1182 }
1183 
1184 GDriveStartStopType
caja_file_get_start_stop_type(CajaFile * file)1185 caja_file_get_start_stop_type (CajaFile *file)
1186 {
1187 	GDriveStartStopType ret;
1188 	GDrive *drive;
1189 
1190 	g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
1191 
1192 	ret = G_DRIVE_START_STOP_TYPE_UNKNOWN;
1193 
1194 	ret = file->details->start_stop_type;
1195 	if (ret != G_DRIVE_START_STOP_TYPE_UNKNOWN)
1196 		goto out;
1197 
1198 	if (file->details->mount != NULL) {
1199 		drive = g_mount_get_drive (file->details->mount);
1200 		if (drive != NULL) {
1201 			ret = g_drive_get_start_stop_type (drive);
1202 			g_object_unref (drive);
1203 		}
1204 	}
1205 
1206  out:
1207 	return ret;
1208 }
1209 
1210 void
caja_file_mount(CajaFile * file,GMountOperation * mount_op,GCancellable * cancellable,CajaFileOperationCallback callback,gpointer callback_data)1211 caja_file_mount (CajaFile                   *file,
1212 		     GMountOperation                *mount_op,
1213 		     GCancellable                   *cancellable,
1214 		     CajaFileOperationCallback   callback,
1215 		     gpointer                        callback_data)
1216 {
1217 	GError *error;
1218 
1219 	if (CAJA_FILE_GET_CLASS (file)->mount == NULL) {
1220 		if (callback) {
1221 			error = NULL;
1222 			g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
1223                                              _("This file cannot be mounted"));
1224 			callback (file, NULL, error, callback_data);
1225 			g_error_free (error);
1226 		}
1227 	} else {
1228 		CAJA_FILE_GET_CLASS (file)->mount (file, mount_op, cancellable, callback, callback_data);
1229 	}
1230 }
1231 
1232 typedef struct {
1233 	CajaFile *file;
1234 	CajaFileOperationCallback callback;
1235 	gpointer callback_data;
1236 } UnmountData;
1237 
1238 static void
unmount_done(void * callback_data)1239 unmount_done (void *callback_data)
1240 {
1241 	UnmountData *data;
1242 
1243 	data = (UnmountData *)callback_data;
1244 	if (data->callback) {
1245 		data->callback (data->file, NULL, NULL, data->callback_data);
1246 	}
1247 	caja_file_unref (data->file);
1248 	g_free (data);
1249 }
1250 
1251 void
caja_file_unmount(CajaFile * file,GMountOperation * mount_op,GCancellable * cancellable,CajaFileOperationCallback callback,gpointer callback_data)1252 caja_file_unmount (CajaFile                   *file,
1253 		       GMountOperation                *mount_op,
1254 		       GCancellable                   *cancellable,
1255 		       CajaFileOperationCallback   callback,
1256 		       gpointer                        callback_data)
1257 {
1258 	GError *error;
1259 	UnmountData *data;
1260 
1261 	if (file->details->can_unmount) {
1262 		if (CAJA_FILE_GET_CLASS (file)->unmount != NULL) {
1263 			CAJA_FILE_GET_CLASS (file)->unmount (file, mount_op, cancellable, callback, callback_data);
1264 		} else {
1265 			if (callback) {
1266 				error = NULL;
1267 				g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
1268 						     _("This file cannot be unmounted"));
1269 				callback (file, NULL, error, callback_data);
1270 				g_error_free (error);
1271 			}
1272 		}
1273 	} else if (file->details->mount != NULL &&
1274 		   g_mount_can_unmount (file->details->mount)) {
1275 		data = g_new0 (UnmountData, 1);
1276 		data->file = caja_file_ref (file);
1277 		data->callback = callback;
1278 		data->callback_data = callback_data;
1279 		caja_file_operations_unmount_mount_full (NULL, file->details->mount, FALSE, TRUE, unmount_done, data);
1280 	} else if (callback) {
1281 		callback (file, NULL, NULL, callback_data);
1282 	}
1283 }
1284 
1285 void
caja_file_eject(CajaFile * file,GMountOperation * mount_op,GCancellable * cancellable,CajaFileOperationCallback callback,gpointer callback_data)1286 caja_file_eject (CajaFile                   *file,
1287 		     GMountOperation                *mount_op,
1288 		     GCancellable                   *cancellable,
1289 		     CajaFileOperationCallback   callback,
1290 		     gpointer                        callback_data)
1291 {
1292 	GError *error;
1293 	UnmountData *data;
1294 
1295 	if (file->details->can_eject) {
1296 		if (CAJA_FILE_GET_CLASS (file)->eject != NULL) {
1297 			CAJA_FILE_GET_CLASS (file)->eject (file, mount_op, cancellable, callback, callback_data);
1298 		} else {
1299 			if (callback) {
1300 				error = NULL;
1301 				g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
1302 						     _("This file cannot be ejected"));
1303 				callback (file, NULL, error, callback_data);
1304 				g_error_free (error);
1305 			}
1306 		}
1307 	} else if (file->details->mount != NULL &&
1308 		   g_mount_can_eject (file->details->mount)) {
1309 		data = g_new0 (UnmountData, 1);
1310 		data->file = caja_file_ref (file);
1311 		data->callback = callback;
1312 		data->callback_data = callback_data;
1313 		caja_file_operations_unmount_mount_full (NULL, file->details->mount, TRUE, TRUE, unmount_done, data);
1314 	} else if (callback) {
1315 		callback (file, NULL, NULL, callback_data);
1316 	}
1317 }
1318 
1319 void
caja_file_start(CajaFile * file,GMountOperation * start_op,GCancellable * cancellable,CajaFileOperationCallback callback,gpointer callback_data)1320 caja_file_start (CajaFile                   *file,
1321 		     GMountOperation                *start_op,
1322 		     GCancellable                   *cancellable,
1323 		     CajaFileOperationCallback   callback,
1324 		     gpointer                        callback_data)
1325 {
1326 	GError *error;
1327 
1328 	if ((file->details->can_start || file->details->can_start_degraded) &&
1329 	    CAJA_FILE_GET_CLASS (file)->start != NULL) {
1330 		CAJA_FILE_GET_CLASS (file)->start (file, start_op, cancellable, callback, callback_data);
1331 	} else {
1332 		if (callback) {
1333 			error = NULL;
1334 			g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
1335                                              _("This file cannot be started"));
1336 			callback (file, NULL, error, callback_data);
1337 			g_error_free (error);
1338 		}
1339 	}
1340 }
1341 
1342 static void
file_stop_callback(GObject * source_object,GAsyncResult * res,gpointer callback_data)1343 file_stop_callback (GObject *source_object,
1344 		    GAsyncResult *res,
1345 		    gpointer callback_data)
1346 {
1347 	CajaFileOperation *op;
1348 	gboolean stopped;
1349 	GError *error;
1350 
1351 	op = callback_data;
1352 
1353 	error = NULL;
1354 	stopped = g_drive_stop_finish (G_DRIVE (source_object),
1355 				       res, &error);
1356 
1357 	if (!stopped &&
1358 	    error->domain == G_IO_ERROR &&
1359 	    (error->code == G_IO_ERROR_FAILED_HANDLED ||
1360 	     error->code == G_IO_ERROR_CANCELLED)) {
1361 		g_error_free (error);
1362 		error = NULL;
1363 	}
1364 
1365 	caja_file_operation_complete (op, NULL, error);
1366 	if (error) {
1367 		g_error_free (error);
1368 	}
1369 }
1370 
1371 void
caja_file_stop(CajaFile * file,GMountOperation * mount_op,GCancellable * cancellable,CajaFileOperationCallback callback,gpointer callback_data)1372 caja_file_stop (CajaFile                   *file,
1373 		    GMountOperation                *mount_op,
1374 		    GCancellable                   *cancellable,
1375 		    CajaFileOperationCallback   callback,
1376 		    gpointer                        callback_data)
1377 {
1378 	GError *error;
1379 
1380 	if (CAJA_FILE_GET_CLASS (file)->stop != NULL) {
1381 		if (file->details->can_stop) {
1382 			CAJA_FILE_GET_CLASS (file)->stop (file, mount_op, cancellable, callback, callback_data);
1383 		} else {
1384 			if (callback) {
1385 				error = NULL;
1386 				g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
1387 						     _("This file cannot be stopped"));
1388 				callback (file, NULL, error, callback_data);
1389 				g_error_free (error);
1390 			}
1391 		}
1392 	} else {
1393 		GDrive *drive;
1394 
1395 		drive = NULL;
1396 		if (file->details->mount != NULL)
1397 			drive = g_mount_get_drive (file->details->mount);
1398 
1399 		if (drive != NULL && g_drive_can_stop (drive)) {
1400 			CajaFileOperation *op;
1401 
1402 			op = caja_file_operation_new (file, callback, callback_data);
1403 			if (cancellable) {
1404 				g_object_unref (op->cancellable);
1405 				op->cancellable = g_object_ref (cancellable);
1406 			}
1407 
1408 			g_drive_stop (drive,
1409 				      G_MOUNT_UNMOUNT_NONE,
1410 				      mount_op,
1411 				      op->cancellable,
1412 				      file_stop_callback,
1413 				      op);
1414 		} else {
1415 			if (callback) {
1416 				error = NULL;
1417 				g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
1418 						     _("This file cannot be stopped"));
1419 				callback (file, NULL, error, callback_data);
1420 				g_error_free (error);
1421 			}
1422 		}
1423 
1424 		if (drive != NULL) {
1425 			g_object_unref (drive);
1426 		}
1427 	}
1428 }
1429 
1430 void
caja_file_poll_for_media(CajaFile * file)1431 caja_file_poll_for_media (CajaFile *file)
1432 {
1433 	if (file->details->can_poll_for_media) {
1434 		if (CAJA_FILE_GET_CLASS (file)->stop != NULL) {
1435 			CAJA_FILE_GET_CLASS (file)->poll_for_media (file);
1436 		}
1437 	} else if (file->details->mount != NULL) {
1438 		GDrive *drive;
1439 		drive = g_mount_get_drive (file->details->mount);
1440 		if (drive != NULL) {
1441 			g_drive_poll_for_media (drive,
1442 						NULL,  /* cancellable */
1443 						NULL,  /* GAsyncReadyCallback */
1444 						NULL); /* user_data */
1445 			g_object_unref (drive);
1446 		}
1447 	}
1448 }
1449 
1450 /**
1451  * caja_file_is_desktop_directory:
1452  *
1453  * Check whether this file is the desktop directory.
1454  *
1455  * @file: The file to check.
1456  *
1457  * Return value: TRUE if this is the physical desktop directory.
1458  */
1459 gboolean
caja_file_is_desktop_directory(CajaFile * file)1460 caja_file_is_desktop_directory (CajaFile *file)
1461 {
1462 	GFile *dir;
1463 
1464 	dir = file->details->directory->details->location;
1465 
1466 	if (dir == NULL) {
1467 		return FALSE;
1468 	}
1469 
1470 	return caja_is_desktop_directory_file (dir, file->details->name);
1471 }
1472 
1473 static gboolean
is_desktop_file(CajaFile * file)1474 is_desktop_file (CajaFile *file)
1475 {
1476 	return caja_file_is_mime_type (file, "application/x-desktop");
1477 }
1478 
1479 static gboolean
can_rename_desktop_file(CajaFile * file)1480 can_rename_desktop_file (CajaFile *file)
1481 {
1482 	GFile *location;
1483 	gboolean res;
1484 
1485 	location = caja_file_get_location (file);
1486 	res = g_file_is_native (location);
1487 	g_object_unref (location);
1488 	return res;
1489 }
1490 
1491 /**
1492  * caja_file_can_rename:
1493  *
1494  * Check whether the user is allowed to change the name of the file.
1495  *
1496  * @file: The file to check.
1497  *
1498  * Return value: FALSE if the user is definitely not allowed to change
1499  * the name of the file. If the user is allowed to change the name, or
1500  * the code can't tell whether the user is allowed to change the name,
1501  * returns TRUE (so rename failures must always be handled).
1502  */
1503 gboolean
caja_file_can_rename(CajaFile * file)1504 caja_file_can_rename (CajaFile *file)
1505 {
1506 	gboolean can_rename;
1507 
1508 	g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
1509 
1510 	/* Nonexistent files can't be renamed. */
1511 	if (caja_file_is_gone (file)) {
1512 		return FALSE;
1513 	}
1514 
1515 	/* Self-owned files can't be renamed */
1516 	if (caja_file_is_self_owned (file)) {
1517 		return FALSE;
1518 	}
1519 
1520 	if ((is_desktop_file (file) && !can_rename_desktop_file (file)) ||
1521 	     caja_file_is_home (file)) {
1522 		return FALSE;
1523 	}
1524 
1525 	can_rename = TRUE;
1526 
1527 	/* Certain types of links can't be renamed */
1528 	if (CAJA_IS_DESKTOP_ICON_FILE (file)) {
1529 		CajaDesktopLink *link;
1530 
1531 		link = caja_desktop_icon_file_get_link (CAJA_DESKTOP_ICON_FILE (file));
1532 
1533 		if (link != NULL) {
1534 			can_rename = caja_desktop_link_can_rename (link);
1535 			g_object_unref (link);
1536 		}
1537 	}
1538 
1539 	if (!can_rename) {
1540 		return FALSE;
1541 	}
1542 
1543 	return file->details->can_rename;
1544 }
1545 
1546 gboolean
caja_file_can_delete(CajaFile * file)1547 caja_file_can_delete (CajaFile *file)
1548 {
1549 	g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
1550 
1551 	/* Nonexistent files can't be deleted. */
1552 	if (caja_file_is_gone (file)) {
1553 		return FALSE;
1554 	}
1555 
1556 	/* Self-owned files can't be deleted */
1557 	if (caja_file_is_self_owned (file)) {
1558 		return FALSE;
1559 	}
1560 
1561 	return file->details->can_delete;
1562 }
1563 
1564 gboolean
caja_file_can_trash(CajaFile * file)1565 caja_file_can_trash (CajaFile *file)
1566 {
1567 	g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
1568 
1569 	/* Nonexistent files can't be deleted. */
1570 	if (caja_file_is_gone (file)) {
1571 		return FALSE;
1572 	}
1573 
1574 	/* Self-owned files can't be deleted */
1575 	if (caja_file_is_self_owned (file)) {
1576 		return FALSE;
1577 	}
1578 
1579 	return file->details->can_trash;
1580 }
1581 
1582 GFile *
caja_file_get_location(CajaFile * file)1583 caja_file_get_location (CajaFile *file)
1584 {
1585 	GFile *dir;
1586 
1587 	g_return_val_if_fail (CAJA_IS_FILE (file), NULL);
1588 
1589 	dir = file->details->directory->details->location;
1590 
1591 	if (caja_file_is_self_owned (file)) {
1592 		return g_object_ref (dir);
1593 	}
1594 
1595 	return g_file_get_child (dir, file->details->name);
1596 }
1597 
1598 /* Return the actual uri associated with the passed-in file. */
1599 char *
caja_file_get_uri(CajaFile * file)1600 caja_file_get_uri (CajaFile *file)
1601 {
1602 	char *uri;
1603 	GFile *loc;
1604 
1605 	g_return_val_if_fail (CAJA_IS_FILE (file), NULL);
1606 
1607 	loc = caja_file_get_location (file);
1608 	uri = g_file_get_uri (loc);
1609 	g_object_unref (loc);
1610 
1611 	return uri;
1612 }
1613 
1614 char *
caja_file_get_uri_scheme(CajaFile * file)1615 caja_file_get_uri_scheme (CajaFile *file)
1616 {
1617 	GFile *loc;
1618 	char *scheme;
1619 
1620 	g_return_val_if_fail (CAJA_IS_FILE (file), NULL);
1621 
1622 	if (file->details->directory == NULL ||
1623 	    file->details->directory->details->location == NULL) {
1624 		return NULL;
1625 	}
1626 
1627 	loc = caja_directory_get_location (file->details->directory);
1628 	scheme = g_file_get_uri_scheme (loc);
1629 	g_object_unref (loc);
1630 
1631 	return scheme;
1632 }
1633 
1634 CajaFileOperation *
caja_file_operation_new(CajaFile * file,CajaFileOperationCallback callback,gpointer callback_data)1635 caja_file_operation_new (CajaFile *file,
1636 			     CajaFileOperationCallback callback,
1637 			     gpointer callback_data)
1638 {
1639 	CajaFileOperation *op;
1640 
1641 	op = g_new0 (CajaFileOperation, 1);
1642 	op->file = caja_file_ref (file);
1643 	op->callback = callback;
1644 	op->callback_data = callback_data;
1645 	op->cancellable = g_cancellable_new ();
1646 
1647 	op->file->details->operations_in_progress = g_list_prepend
1648 		(op->file->details->operations_in_progress, op);
1649 
1650 	return op;
1651 }
1652 
1653 static void
caja_file_operation_remove(CajaFileOperation * op)1654 caja_file_operation_remove (CajaFileOperation *op)
1655 {
1656 	op->file->details->operations_in_progress = g_list_remove
1657 		(op->file->details->operations_in_progress, op);
1658 }
1659 
1660 void
caja_file_operation_free(CajaFileOperation * op)1661 caja_file_operation_free (CajaFileOperation *op)
1662 {
1663 	caja_file_operation_remove (op);
1664 	caja_file_unref (op->file);
1665 	g_object_unref (op->cancellable);
1666 	if (op->free_data) {
1667 		op->free_data (op->data);
1668 	}
1669 	// Start UNDO-REDO
1670 	caja_undostack_manager_add_action (caja_undostack_manager_instance(),
1671 										   op->undo_redo_data);
1672 	// End UNDO-REDO
1673 	g_free (op);
1674 }
1675 
1676 void
caja_file_operation_complete(CajaFileOperation * op,GFile * result_file,GError * error)1677 caja_file_operation_complete (CajaFileOperation *op, GFile *result_file, GError *error)
1678 {
1679 	/* Claim that something changed even if the operation failed.
1680 	 * This makes it easier for some clients who see the "reverting"
1681 	 * as "changing back".
1682 	 */
1683 	caja_file_operation_remove (op);
1684 	caja_file_changed (op->file);
1685 	if (op->callback) {
1686 		(* op->callback) (op->file, result_file, error, op->callback_data);
1687 	}
1688 	caja_file_operation_free (op);
1689 }
1690 
1691 void
caja_file_operation_cancel(CajaFileOperation * op)1692 caja_file_operation_cancel (CajaFileOperation *op)
1693 {
1694 	/* Cancel the operation if it's still in progress. */
1695 	g_cancellable_cancel (op->cancellable);
1696 }
1697 
1698 static void
rename_get_info_callback(GObject * source_object,GAsyncResult * res,gpointer callback_data)1699 rename_get_info_callback (GObject *source_object,
1700 			  GAsyncResult *res,
1701 			  gpointer callback_data)
1702 {
1703 	CajaFileOperation *op;
1704 	GFileInfo *new_info;
1705 	GError *error;
1706 
1707 	op = callback_data;
1708 
1709 	error = NULL;
1710 	new_info = g_file_query_info_finish (G_FILE (source_object), res, &error);
1711 	if (new_info != NULL) {
1712 		CajaDirectory *directory;
1713 		CajaFile *existing_file;
1714 		char *old_name;
1715 		char *old_uri;
1716 		char *new_uri;
1717 		const char *new_name;
1718 
1719 		directory = op->file->details->directory;
1720 
1721 		new_name = g_file_info_get_name (new_info);
1722 
1723 		/* If there was another file by the same name in this
1724 		 * directory, mark it gone.
1725 		 */
1726 		existing_file = caja_directory_find_file_by_name (directory, new_name);
1727 		if (existing_file != NULL) {
1728 			caja_file_mark_gone (existing_file);
1729 			caja_file_changed (existing_file);
1730 		}
1731 
1732 		old_uri = caja_file_get_uri (op->file);
1733 		old_name = g_strdup (op->file->details->name);
1734 
1735 		update_info_and_name (op->file, new_info);
1736 
1737 		g_free (old_name);
1738 
1739 		new_uri = caja_file_get_uri (op->file);
1740 		caja_directory_moved (old_uri, new_uri);
1741 		g_free (new_uri);
1742 		g_free (old_uri);
1743 
1744 		/* the rename could have affected the display name if e.g.
1745 		 * we're in a vfolder where the name comes from a desktop file
1746 		 * and a rename affects the contents of the desktop file.
1747 		 */
1748 		if (op->file->details->got_custom_display_name) {
1749 			caja_file_invalidate_attributes (op->file,
1750 							     CAJA_FILE_ATTRIBUTE_INFO |
1751 							     CAJA_FILE_ATTRIBUTE_LINK_INFO);
1752 		}
1753 
1754 		g_object_unref (new_info);
1755 	}
1756 	caja_file_operation_complete (op, NULL, error);
1757 	if (error) {
1758 		g_error_free (error);
1759 	}
1760 }
1761 
1762 static void
rename_callback(GObject * source_object,GAsyncResult * res,gpointer callback_data)1763 rename_callback (GObject *source_object,
1764 		 GAsyncResult *res,
1765 		 gpointer callback_data)
1766 {
1767 	CajaFileOperation *op;
1768 	GFile *new_file;
1769 	GError *error;
1770 
1771 	op = callback_data;
1772 
1773 	error = NULL;
1774 	new_file = g_file_set_display_name_finish (G_FILE (source_object),
1775 						   res, &error);
1776 
1777 	if (new_file != NULL) {
1778 		// Start UNDO-REDO
1779 		caja_undostack_manager_data_set_rename_information(op->undo_redo_data, G_FILE (source_object), new_file);
1780 		// End UNDO-REDO
1781 		g_file_query_info_async (new_file,
1782 					 CAJA_FILE_DEFAULT_ATTRIBUTES,
1783 					 0,
1784 					 G_PRIORITY_DEFAULT,
1785 					 op->cancellable,
1786 					 rename_get_info_callback, op);
1787 	} else {
1788 		caja_file_operation_complete (op, NULL, error);
1789 		g_error_free (error);
1790 	}
1791 }
1792 
1793 static gboolean
name_is(CajaFile * file,const char * new_name)1794 name_is (CajaFile *file, const char *new_name)
1795 {
1796 	const char *old_name;
1797 	old_name = file->details->name;
1798 	return strcmp (new_name, old_name) == 0;
1799 }
1800 
1801 void
caja_file_rename(CajaFile * file,const char * new_name,CajaFileOperationCallback callback,gpointer callback_data)1802 caja_file_rename (CajaFile *file,
1803 		      const char *new_name,
1804 		      CajaFileOperationCallback callback,
1805 		      gpointer callback_data)
1806 {
1807 	CajaFileOperation *op;
1808 	char *old_name;
1809 	char *new_file_name;
1810 	gboolean success, name_changed;
1811 	gboolean is_renameable_desktop_file;
1812 	GFile *location;
1813 	GError *error;
1814 
1815 	g_return_if_fail (CAJA_IS_FILE (file));
1816 	g_return_if_fail (new_name != NULL);
1817 	g_return_if_fail (callback != NULL);
1818 
1819 	is_renameable_desktop_file =
1820 		is_desktop_file (file) && can_rename_desktop_file (file);
1821 
1822 	/* Return an error for incoming names containing path separators.
1823 	 * But not for .desktop files as '/' are allowed for them */
1824 	if (strstr (new_name, "/") != NULL && !is_renameable_desktop_file) {
1825 		error = g_error_new (G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
1826 				     _("Slashes are not allowed in filenames"));
1827 		(* callback) (file, NULL, error, callback_data);
1828 		g_error_free (error);
1829 		return;
1830 	}
1831 
1832 	/* Can't rename a file that's already gone.
1833 	 * We need to check this here because there may be a new
1834 	 * file with the same name.
1835 	 */
1836 	if (caja_file_is_gone (file)) {
1837 	       	/* Claim that something changed even if the rename
1838 		 * failed. This makes it easier for some clients who
1839 		 * see the "reverting" to the old name as "changing
1840 		 * back".
1841 		 */
1842 		caja_file_changed (file);
1843 		error = g_error_new (G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
1844 				     _("File not found"));
1845 		(* callback) (file, NULL, error, callback_data);
1846 		g_error_free (error);
1847 		return;
1848 	}
1849 
1850 	/* Test the name-hasn't-changed case explicitly, for two reasons.
1851 	 * (1) rename returns an error if new & old are same.
1852 	 * (2) We don't want to send file-changed signal if nothing changed.
1853 	 */
1854 	if (!CAJA_IS_DESKTOP_ICON_FILE (file) &&
1855 	    !is_renameable_desktop_file &&
1856 	    name_is (file, new_name)) {
1857 		(* callback) (file, NULL, NULL, callback_data);
1858 		return;
1859 	}
1860 
1861 	/* Self-owned files can't be renamed. Test the name-not-actually-changing
1862 	 * case before this case.
1863 	 */
1864 	if (caja_file_is_self_owned (file)) {
1865 	       	/* Claim that something changed even if the rename
1866 		 * failed. This makes it easier for some clients who
1867 		 * see the "reverting" to the old name as "changing
1868 		 * back".
1869 		 */
1870 		caja_file_changed (file);
1871 		error = g_error_new (G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
1872 				     _("Toplevel files cannot be renamed"));
1873 
1874 		(* callback) (file, NULL, error, callback_data);
1875 		g_error_free (error);
1876 		return;
1877 	}
1878 
1879 	if (CAJA_IS_DESKTOP_ICON_FILE (file)) {
1880 		CajaDesktopLink *link;
1881 
1882 		link = caja_desktop_icon_file_get_link (CAJA_DESKTOP_ICON_FILE (file));
1883 		old_name = caja_file_get_display_name (file);
1884 
1885 		if ((old_name != NULL && strcmp (new_name, old_name) == 0)) {
1886 			success = TRUE;
1887 		} else {
1888 			success = (link != NULL && caja_desktop_link_rename (link, new_name));
1889 		}
1890 
1891 		if (success) {
1892 			(* callback) (file, NULL, NULL, callback_data);
1893 		} else {
1894 			error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED,
1895 					     _("Unable to rename desktop icon"));
1896 			(* callback) (file, NULL, error, callback_data);
1897 			g_error_free (error);
1898 		}
1899 
1900 		g_free (old_name);
1901 		g_object_unref (link);
1902 		return;
1903 	}
1904 
1905 	if (is_renameable_desktop_file) {
1906 		char *uri;
1907 
1908 		/* Don't actually change the name if the new name is the same.
1909 		 * This helps for the vfolder method where this can happen and
1910 		 * we want to minimize actual changes
1911 		 */
1912 		uri = caja_file_get_uri (file);
1913 		old_name = caja_link_local_get_text (uri);
1914 		if (old_name != NULL && strcmp (new_name, old_name) == 0) {
1915 			success = TRUE;
1916 			name_changed = FALSE;
1917 		} else {
1918 			success = caja_link_local_set_text (uri, new_name);
1919 			name_changed = TRUE;
1920 		}
1921 		g_free (old_name);
1922 		g_free (uri);
1923 
1924 		if (!success) {
1925 			error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED,
1926 					     _("Unable to rename desktop file"));
1927 			(* callback) (file, NULL, error, callback_data);
1928 			g_error_free (error);
1929 			return;
1930 		}
1931 		if (!g_str_has_suffix(new_name, ".desktop"))
1932 		   new_file_name = g_strdup_printf ("%s.desktop", new_name);
1933 		else
1934 		   new_file_name = g_strdup_printf("%s", new_name);
1935 		new_file_name = g_strdelimit (new_file_name, "/", '-');
1936 
1937 		if (name_is (file, new_file_name)) {
1938 			if (name_changed) {
1939 				caja_file_invalidate_attributes (file,
1940 								     CAJA_FILE_ATTRIBUTE_INFO |
1941 								     CAJA_FILE_ATTRIBUTE_LINK_INFO);
1942 			}
1943 
1944 			(* callback) (file, NULL, NULL, callback_data);
1945 			g_free (new_file_name);
1946 			return;
1947 		}
1948 	} else {
1949 		new_file_name = g_strdup (new_name);
1950 	}
1951 
1952 	/* Set up a renaming operation. */
1953 	op = caja_file_operation_new (file, callback, callback_data);
1954 	op->is_rename = TRUE;
1955 
1956 	/* Do the renaming. */
1957 
1958 	location = caja_file_get_location (file);
1959 
1960 	// Start UNDO-REDO
1961 	if (!caja_undostack_manager_is_undo_redo(caja_undostack_manager_instance())) {
1962 		op->undo_redo_data = caja_undostack_manager_data_new (CAJA_UNDOSTACK_RENAME, 1);
1963 	}
1964 	// End UNDO-REDO
1965 
1966 	g_file_set_display_name_async (location,
1967 				       new_file_name,
1968 				       G_PRIORITY_DEFAULT,
1969 				       op->cancellable,
1970 				       rename_callback,
1971 				       op);
1972 	g_free (new_file_name);
1973 	g_object_unref (location);
1974 }
1975 
1976 gboolean
caja_file_rename_in_progress(CajaFile * file)1977 caja_file_rename_in_progress (CajaFile *file)
1978 {
1979 	GList *node;
1980 	CajaFileOperation *op = NULL;
1981 
1982 	for (node = file->details->operations_in_progress; node != NULL; node = node->next) {
1983 		op = node->data;
1984 		if (op->is_rename) {
1985 			return TRUE;
1986 		}
1987 	}
1988 	return FALSE;
1989 }
1990 
1991 void
caja_file_cancel(CajaFile * file,CajaFileOperationCallback callback,gpointer callback_data)1992 caja_file_cancel (CajaFile *file,
1993 		      CajaFileOperationCallback callback,
1994 		      gpointer callback_data)
1995 {
1996 	GList *node, *next;
1997 	CajaFileOperation *op = NULL;
1998 
1999 	for (node = file->details->operations_in_progress; node != NULL; node = next) {
2000 		next = node->next;
2001 		op = node->data;
2002 
2003 		g_assert (op->file == file);
2004 		if (op->callback == callback && op->callback_data == callback_data) {
2005 			caja_file_operation_cancel (op);
2006 		}
2007 	}
2008 }
2009 
2010 gboolean
caja_file_matches_uri(CajaFile * file,const char * match_uri)2011 caja_file_matches_uri (CajaFile *file, const char *match_uri)
2012 {
2013 	GFile *match_file, *location;
2014 	gboolean result;
2015 
2016 	g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
2017 	g_return_val_if_fail (match_uri != NULL, FALSE);
2018 
2019 	location = caja_file_get_location (file);
2020 	match_file = g_file_new_for_uri (match_uri);
2021 	result = g_file_equal (location, match_file);
2022 	g_object_unref (location);
2023 	g_object_unref (match_file);
2024 
2025 	return result;
2026 }
2027 
2028 int
caja_file_compare_location(CajaFile * file_1,CajaFile * file_2)2029 caja_file_compare_location (CajaFile *file_1,
2030                                 CajaFile *file_2)
2031 {
2032 	GFile *loc_a, *loc_b;
2033 	gboolean res;
2034 
2035 	loc_a = caja_file_get_location (file_1);
2036 	loc_b = caja_file_get_location (file_2);
2037 
2038 	res = !g_file_equal (loc_a, loc_b);
2039 
2040 	g_object_unref (loc_a);
2041 	g_object_unref (loc_b);
2042 
2043 	return (gint) res;
2044 }
2045 
2046 gboolean
caja_file_is_local(CajaFile * file)2047 caja_file_is_local (CajaFile *file)
2048 {
2049 	g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
2050 
2051 	return caja_directory_is_local (file->details->directory);
2052 }
2053 
2054 static void
update_link(CajaFile * link_file,CajaFile * target_file)2055 update_link (CajaFile *link_file, CajaFile *target_file)
2056 {
2057 	g_assert (CAJA_IS_FILE (link_file));
2058 	g_assert (CAJA_IS_FILE (target_file));
2059 
2060 	/* FIXME bugzilla.gnome.org 42044: If we don't put any code
2061 	 * here then the hash table is a waste of time.
2062 	 */
2063 }
2064 
2065 static GList *
get_link_files(CajaFile * target_file)2066 get_link_files (CajaFile *target_file)
2067 {
2068 	GList **link_files;
2069 
2070 	if (symbolic_links == NULL) {
2071 		link_files = NULL;
2072 	} else {
2073 		char *uri;
2074 
2075 		uri = caja_file_get_uri (target_file);
2076 		link_files = g_hash_table_lookup (symbolic_links, uri);
2077 		g_free (uri);
2078 	}
2079 	if (link_files) {
2080 		return caja_file_list_copy (*link_files);
2081 	}
2082 	return NULL;
2083 }
2084 
2085 static void
update_links_if_target(CajaFile * target_file)2086 update_links_if_target (CajaFile *target_file)
2087 {
2088 	GList *link_files, *p;
2089 
2090 	link_files = get_link_files (target_file);
2091 	for (p = link_files; p != NULL; p = p->next) {
2092 		update_link (CAJA_FILE (p->data), target_file);
2093 	}
2094 	caja_file_list_free (link_files);
2095 }
2096 
2097 static gboolean
update_info_internal(CajaFile * file,GFileInfo * info,gboolean update_name)2098 update_info_internal (CajaFile *file,
2099 		      GFileInfo *info,
2100 		      gboolean update_name)
2101 {
2102 	gboolean changed;
2103 	gboolean is_symlink, is_hidden, is_backup, is_mountpoint;
2104 	gboolean has_permissions;
2105 	guint32 permissions;
2106 	gboolean can_read, can_write, can_execute, can_delete, can_trash, can_rename, can_mount, can_unmount, can_eject;
2107 	gboolean can_start, can_start_degraded, can_stop, can_poll_for_media, is_media_check_automatic;
2108 	GDriveStartStopType start_stop_type;
2109 	gboolean thumbnailing_failed;
2110 	int uid, gid;
2111 	goffset size;
2112 	goffset size_on_disk;
2113 	int sort_order;
2114 	time_t atime, mtime, ctime, btime;
2115 	time_t trash_time;
2116 	const char * time_string;
2117 	const char *symlink_name, *mime_type, *selinux_context, *thumbnail_path;
2118 	GFileType file_type;
2119 	GIcon *icon;
2120 	const char *description;
2121 	const char *filesystem_id;
2122 	const char *trash_orig_path;
2123 	const char *group, *owner, *owner_real;
2124 	gboolean free_owner, free_group;
2125 
2126 	if (file->details->is_gone) {
2127 		return FALSE;
2128 	}
2129 
2130 	if (info == NULL) {
2131 		caja_file_mark_gone (file);
2132 		return TRUE;
2133 	}
2134 
2135 	file->details->file_info_is_up_to_date = TRUE;
2136 
2137 	/* FIXME bugzilla.gnome.org 42044: Need to let links that
2138 	 * point to the old name know that the file has been renamed.
2139 	 */
2140 
2141 	remove_from_link_hash_table (file);
2142 
2143 	changed = FALSE;
2144 
2145 	if (!file->details->got_file_info) {
2146 		changed = TRUE;
2147 	}
2148 	file->details->got_file_info = TRUE;
2149 
2150 	changed |= caja_file_set_display_name (file,
2151 						  g_file_info_get_display_name (info),
2152 						  g_file_info_get_edit_name (info),
2153 						  FALSE);
2154 
2155 	file_type = g_file_info_get_file_type (info);
2156 	if (file->details->type != file_type) {
2157 		changed = TRUE;
2158 	}
2159 	file->details->type = file_type;
2160 
2161 	if (!file->details->got_custom_activation_uri) {
2162 		const char *activation_uri;
2163 
2164 		activation_uri = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI);
2165 		if (activation_uri == NULL) {
2166 			if (file->details->activation_uri) {
2167 				g_free (file->details->activation_uri);
2168 				file->details->activation_uri = NULL;
2169 				changed = TRUE;
2170 			}
2171 		} else {
2172 			char *old_activation_uri;
2173 
2174 			old_activation_uri = file->details->activation_uri;
2175 			file->details->activation_uri = g_strdup (activation_uri);
2176 
2177 			if (old_activation_uri) {
2178 				if (strcmp (old_activation_uri,
2179 					    file->details->activation_uri) != 0) {
2180 					changed = TRUE;
2181 				}
2182 				g_free (old_activation_uri);
2183 			} else {
2184 				changed = TRUE;
2185 			}
2186 		}
2187 	}
2188 
2189 	is_symlink = g_file_info_get_is_symlink (info);
2190 	if (file->details->is_symlink != is_symlink) {
2191 		changed = TRUE;
2192 	}
2193 	file->details->is_symlink = is_symlink;
2194 
2195 	is_hidden = g_file_info_get_is_hidden (info);
2196 	is_backup = g_file_info_get_is_backup (info);
2197 	if (file->details->is_hidden != is_hidden ||
2198 	    file->details->is_backup != is_backup) {
2199 		changed = TRUE;
2200 	}
2201 	file->details->is_hidden = is_hidden;
2202 	file->details->is_backup = is_backup;
2203 
2204 	is_mountpoint = g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_UNIX_IS_MOUNTPOINT);
2205 	if (file->details->is_mountpoint != is_mountpoint) {
2206 		changed = TRUE;
2207 	}
2208 	file->details->is_mountpoint = is_mountpoint;
2209 
2210 	has_permissions = g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_UNIX_MODE);
2211 	permissions = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE);;
2212 	if (file->details->has_permissions != has_permissions ||
2213 	    file->details->permissions != permissions) {
2214 		changed = TRUE;
2215 	}
2216 	file->details->has_permissions = has_permissions;
2217 	file->details->permissions = permissions;
2218 
2219 	/* We default to TRUE for this if we can't know */
2220 	can_read = TRUE;
2221 	can_write = TRUE;
2222 	can_execute = TRUE;
2223 	can_delete = TRUE;
2224 	can_trash = TRUE;
2225 	can_rename = TRUE;
2226 	can_mount = FALSE;
2227 	can_unmount = FALSE;
2228 	can_eject = FALSE;
2229 	can_start = FALSE;
2230 	can_start_degraded = FALSE;
2231 	can_stop = FALSE;
2232 	can_poll_for_media = FALSE;
2233 	is_media_check_automatic = FALSE;
2234 	start_stop_type = G_DRIVE_START_STOP_TYPE_UNKNOWN;
2235 	if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ)) {
2236 		can_read = g_file_info_get_attribute_boolean (info,
2237 							      G_FILE_ATTRIBUTE_ACCESS_CAN_READ);
2238 	}
2239 	if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE)) {
2240 		can_write = g_file_info_get_attribute_boolean (info,
2241 							       G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE);
2242 	}
2243 	if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE)) {
2244 		can_execute = g_file_info_get_attribute_boolean (info,
2245 								G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE);
2246 	}
2247 	if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE)) {
2248 		can_delete = g_file_info_get_attribute_boolean (info,
2249 								G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE);
2250 	}
2251 	if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH)) {
2252 		can_trash = g_file_info_get_attribute_boolean (info,
2253 							       G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH);
2254 	}
2255 	if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME)) {
2256 		can_rename = g_file_info_get_attribute_boolean (info,
2257 								G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME);
2258 	}
2259 	if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_MOUNT)) {
2260 		can_mount = g_file_info_get_attribute_boolean (info,
2261 							       G_FILE_ATTRIBUTE_MOUNTABLE_CAN_MOUNT);
2262 	}
2263 	if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_UNMOUNT)) {
2264 		can_unmount = g_file_info_get_attribute_boolean (info,
2265 								 G_FILE_ATTRIBUTE_MOUNTABLE_CAN_UNMOUNT);
2266 	}
2267 	if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_EJECT)) {
2268 		can_eject = g_file_info_get_attribute_boolean (info,
2269 							       G_FILE_ATTRIBUTE_MOUNTABLE_CAN_EJECT);
2270 	}
2271 	if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_START)) {
2272 		can_start = g_file_info_get_attribute_boolean (info,
2273 							       G_FILE_ATTRIBUTE_MOUNTABLE_CAN_START);
2274 	}
2275 	if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_START_DEGRADED)) {
2276 		can_start_degraded = g_file_info_get_attribute_boolean (info,
2277 							       G_FILE_ATTRIBUTE_MOUNTABLE_CAN_START_DEGRADED);
2278 	}
2279 	if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_STOP)) {
2280 		can_stop = g_file_info_get_attribute_boolean (info,
2281 							      G_FILE_ATTRIBUTE_MOUNTABLE_CAN_STOP);
2282 	}
2283 	if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_MOUNTABLE_START_STOP_TYPE)) {
2284 		start_stop_type = g_file_info_get_attribute_uint32 (info,
2285 								    G_FILE_ATTRIBUTE_MOUNTABLE_START_STOP_TYPE);
2286 	}
2287 	if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_POLL)) {
2288 		can_poll_for_media = g_file_info_get_attribute_boolean (info,
2289 									G_FILE_ATTRIBUTE_MOUNTABLE_CAN_POLL);
2290 	}
2291 	if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_MOUNTABLE_IS_MEDIA_CHECK_AUTOMATIC)) {
2292 		is_media_check_automatic = g_file_info_get_attribute_boolean (info,
2293 									      G_FILE_ATTRIBUTE_MOUNTABLE_IS_MEDIA_CHECK_AUTOMATIC);
2294 	}
2295 	if (file->details->can_read != can_read ||
2296 	    file->details->can_write != can_write ||
2297 	    file->details->can_execute != can_execute ||
2298 	    file->details->can_delete != can_delete ||
2299 	    file->details->can_trash != can_trash ||
2300 	    file->details->can_rename != can_rename ||
2301 	    file->details->can_mount != can_mount ||
2302 	    file->details->can_unmount != can_unmount ||
2303 	    file->details->can_eject != can_eject ||
2304 	    file->details->can_start != can_start ||
2305 	    file->details->can_start_degraded != can_start_degraded ||
2306 	    file->details->can_stop != can_stop ||
2307 	    file->details->start_stop_type != start_stop_type ||
2308 	    file->details->can_poll_for_media != can_poll_for_media ||
2309 	    file->details->is_media_check_automatic != is_media_check_automatic) {
2310 		changed = TRUE;
2311 	}
2312 
2313 	file->details->can_read = can_read;
2314 	file->details->can_write = can_write;
2315 	file->details->can_execute = can_execute;
2316 	file->details->can_delete = can_delete;
2317 	file->details->can_trash = can_trash;
2318 	file->details->can_rename = can_rename;
2319 	file->details->can_mount = can_mount;
2320 	file->details->can_unmount = can_unmount;
2321 	file->details->can_eject = can_eject;
2322 	file->details->can_start = can_start;
2323 	file->details->can_start_degraded = can_start_degraded;
2324 	file->details->can_stop = can_stop;
2325 	file->details->start_stop_type = start_stop_type;
2326 	file->details->can_poll_for_media = can_poll_for_media;
2327 	file->details->is_media_check_automatic = is_media_check_automatic;
2328 
2329 	free_owner = FALSE;
2330 	owner = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_OWNER_USER);
2331 	owner_real = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_OWNER_USER_REAL);
2332 	free_group = FALSE;
2333 	group = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_OWNER_GROUP);
2334 
2335 	uid = -1;
2336 	gid = -1;
2337 	if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_UNIX_UID)) {
2338 		uid = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_UID);
2339 		if (owner == NULL) {
2340 			free_owner = TRUE;
2341 			owner = g_strdup_printf ("%d", uid);
2342 		}
2343 	}
2344 	if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_UNIX_GID)) {
2345 		gid = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_GID);
2346 		if (group == NULL) {
2347 			free_group = TRUE;
2348 			group = g_strdup_printf ("%d", gid);
2349 		}
2350 	}
2351 	if (file->details->uid != uid ||
2352 	    file->details->gid != gid) {
2353 		changed = TRUE;
2354 	}
2355 	file->details->uid = uid;
2356 	file->details->gid = gid;
2357 
2358 	if (eel_strcmp (file->details->owner, owner) != 0) {
2359 		changed = TRUE;
2360 		g_clear_pointer (&file->details->owner, g_ref_string_release);
2361 		file->details->owner = g_ref_string_new_intern (owner);
2362 	}
2363 
2364 	if (eel_strcmp (file->details->owner_real, owner_real) != 0) {
2365 		changed = TRUE;
2366 		g_clear_pointer (&file->details->owner_real, g_ref_string_release);
2367 		file->details->owner_real = g_ref_string_new_intern (owner_real);
2368 	}
2369 
2370 	if (eel_strcmp (file->details->group, group) != 0) {
2371 		changed = TRUE;
2372 		g_clear_pointer (&file->details->group, g_ref_string_release);
2373 		file->details->group = g_ref_string_new_intern (group);
2374 	}
2375 
2376 	if (free_owner) {
2377 		g_free ((char *)owner);
2378 	}
2379 	if (free_group) {
2380 		g_free ((char *)group);
2381 	}
2382 
2383 	size = -1;
2384 	if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SIZE)) {
2385 		size = g_file_info_get_size (info);
2386 	}
2387 	if (file->details->size != size) {
2388 		changed = TRUE;
2389 	}
2390 	file->details->size = size;
2391 
2392 	size_on_disk = -1;
2393 	if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE)) {
2394 		size_on_disk = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE);
2395 	}
2396 	if (file->details->size_on_disk != size_on_disk) {
2397 		changed = TRUE;
2398 	}
2399 	file->details->size_on_disk = size_on_disk;
2400 
2401 	sort_order = g_file_info_get_sort_order (info);
2402 	if (file->details->sort_order != sort_order) {
2403 		changed = TRUE;
2404 	}
2405 	file->details->sort_order = sort_order;
2406 
2407 	atime = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_ACCESS);
2408 	ctime = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_CHANGED);
2409 	mtime = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
2410 	btime = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_CREATED);
2411 	if (file->details->atime != atime ||
2412 	    file->details->mtime != mtime ||
2413 	    file->details->ctime != ctime ||
2414 	    file->details->btime != btime) {
2415 		if (file->details->thumbnail == NULL) {
2416 			file->details->thumbnail_is_up_to_date = FALSE;
2417 		}
2418 
2419 		changed = TRUE;
2420 	}
2421 	file->details->atime = atime;
2422 	file->details->ctime = ctime;
2423 	file->details->mtime = mtime;
2424 	file->details->btime = btime;
2425 
2426 	if (file->details->thumbnail != NULL &&
2427 	    file->details->thumbnail_mtime != 0 &&
2428 	    file->details->thumbnail_mtime != mtime) {
2429 		file->details->thumbnail_is_up_to_date = FALSE;
2430 		changed = TRUE;
2431 	}
2432 
2433 	icon = g_file_info_get_icon (info);
2434 	if (!g_icon_equal (icon, file->details->icon)) {
2435 		changed = TRUE;
2436 
2437 		if (file->details->icon) {
2438 			g_object_unref (file->details->icon);
2439 		}
2440 		file->details->icon = g_object_ref (icon);
2441 	}
2442 
2443 	thumbnail_path =  g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
2444 	if (eel_strcmp (file->details->thumbnail_path, thumbnail_path) != 0) {
2445 		changed = TRUE;
2446 		g_free (file->details->thumbnail_path);
2447 		file->details->thumbnail_path = g_strdup (thumbnail_path);
2448 	}
2449 
2450 	thumbnailing_failed =  g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED);
2451 	if (file->details->thumbnailing_failed != thumbnailing_failed) {
2452 		changed = TRUE;
2453 		file->details->thumbnailing_failed = thumbnailing_failed;
2454 	}
2455 
2456 	symlink_name = g_file_info_get_symlink_target (info);
2457 	if (eel_strcmp (file->details->symlink_name, symlink_name) != 0) {
2458 		changed = TRUE;
2459 		g_free (file->details->symlink_name);
2460 		file->details->symlink_name = g_strdup (symlink_name);
2461 	}
2462 
2463 	mime_type = g_file_info_get_content_type (info);
2464 	if (eel_strcmp (file->details->mime_type, mime_type) != 0) {
2465 		changed = TRUE;
2466 		g_clear_pointer (&file->details->mime_type, g_ref_string_release);
2467 		file->details->mime_type = g_ref_string_new_intern (mime_type);
2468 	}
2469 
2470 	selinux_context = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_SELINUX_CONTEXT);
2471 	if (eel_strcmp (file->details->selinux_context, selinux_context) != 0) {
2472 		changed = TRUE;
2473 		g_free (file->details->selinux_context);
2474 		file->details->selinux_context = g_strdup (selinux_context);
2475 	}
2476 
2477 	description = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_DESCRIPTION);
2478 	if (eel_strcmp (file->details->description, description) != 0) {
2479 		changed = TRUE;
2480 		g_free (file->details->description);
2481 		file->details->description = g_strdup (description);
2482 	}
2483 
2484 	filesystem_id = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_ID_FILESYSTEM);
2485 	if (eel_strcmp (file->details->filesystem_id, filesystem_id) != 0) {
2486 		changed = TRUE;
2487 		g_clear_pointer (&file->details->filesystem_id, g_ref_string_release);
2488 		file->details->filesystem_id = g_ref_string_new_intern (filesystem_id);
2489 	}
2490 
2491 	trash_time = 0;
2492 	time_string = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_TRASH_DELETION_DATE);
2493 	if (time_string != NULL) {
2494 #if GLIB_CHECK_VERSION(2,61,2)
2495 		GDateTime *dt;
2496 		GTimeZone *tz;
2497 		tz = g_time_zone_new_local ();
2498 		dt = g_date_time_new_from_iso8601 (time_string, tz);
2499 		if (dt) {
2500 			trash_time = (time_t) g_date_time_to_unix (dt);
2501 			g_date_time_unref (dt);
2502 		}
2503 		g_time_zone_unref (tz);
2504 #else
2505 		GTimeVal g_trash_time;
2506 		g_time_val_from_iso8601 (time_string, &g_trash_time);
2507 		trash_time = g_trash_time.tv_sec;
2508 #endif
2509 	}
2510 	if (file->details->trash_time != trash_time) {
2511 		changed = TRUE;
2512 		file->details->trash_time = trash_time;
2513 	}
2514 
2515 	trash_orig_path = g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_TRASH_ORIG_PATH);
2516 	if (eel_strcmp (file->details->trash_orig_path, trash_orig_path) != 0) {
2517 		changed = TRUE;
2518 		g_free (file->details->trash_orig_path);
2519 		file->details->trash_orig_path = g_strdup (trash_orig_path);
2520 	}
2521 
2522 	changed |=
2523 		caja_file_update_metadata_from_info (file, info);
2524 
2525 	if (update_name) {
2526 		const char *name;
2527 
2528 		name = g_file_info_get_name (info);
2529 		if (file->details->name == NULL ||
2530 		    strcmp (file->details->name, name) != 0) {
2531 			GList *node;
2532 
2533 			changed = TRUE;
2534 
2535 			node = caja_directory_begin_file_name_change
2536 				(file->details->directory, file);
2537 
2538 			g_clear_pointer (&file->details->name, g_ref_string_release);
2539 			if (eel_strcmp (file->details->display_name, name) == 0) {
2540 				file->details->name = g_ref_string_acquire (file->details->display_name);
2541 			} else {
2542 				file->details->name = g_ref_string_new (name);
2543 			}
2544 
2545 			if (!file->details->got_custom_display_name &&
2546 			    g_file_info_get_display_name (info) == NULL) {
2547 				/* If the file info's display name is NULL,
2548 				 * caja_file_set_display_name() did
2549 				 * not unset the display name.
2550 				 */
2551 				caja_file_clear_display_name (file);
2552 			}
2553 
2554 			caja_directory_end_file_name_change
2555 				(file->details->directory, file, node);
2556 		}
2557 	}
2558 
2559 	if (changed) {
2560 		add_to_link_hash_table (file);
2561 
2562 		update_links_if_target (file);
2563 	}
2564 
2565 	return changed;
2566 }
2567 
2568 static gboolean
update_info_and_name(CajaFile * file,GFileInfo * info)2569 update_info_and_name (CajaFile *file,
2570 		      GFileInfo *info)
2571 {
2572 	return update_info_internal (file, info, TRUE);
2573 }
2574 
2575 gboolean
caja_file_update_info(CajaFile * file,GFileInfo * info)2576 caja_file_update_info (CajaFile *file,
2577 			   GFileInfo *info)
2578 {
2579 	return update_info_internal (file, info, FALSE);
2580 }
2581 
2582 void
caja_file_refresh_info(CajaFile * file)2583 caja_file_refresh_info (CajaFile *file)
2584 {
2585 	GFile *gfile;
2586 	GFileInfo *new_info;
2587 
2588 	gfile = caja_file_get_location (file);
2589 	new_info = g_file_query_info (gfile, CAJA_FILE_DEFAULT_ATTRIBUTES,
2590 	                              G_FILE_QUERY_INFO_NONE, NULL, NULL);
2591 	if (new_info != NULL) {
2592 		if (caja_file_update_info (file, new_info)) {
2593 			caja_file_changed (file);
2594 		}
2595 		g_object_unref (new_info);
2596 	}
2597 	g_object_unref (gfile);
2598 }
2599 
2600 static gboolean
update_name_internal(CajaFile * file,const char * name,gboolean in_directory)2601 update_name_internal (CajaFile *file,
2602 		      const char *name,
2603 		      gboolean in_directory)
2604 {
2605 	GList *node;
2606 
2607 	g_assert (name != NULL);
2608 
2609 	if (file->details->is_gone) {
2610 		return FALSE;
2611 	}
2612 
2613 	if (name_is (file, name)) {
2614 		return FALSE;
2615 	}
2616 
2617 	node = NULL;
2618 	if (in_directory) {
2619 		node = caja_directory_begin_file_name_change
2620 			(file->details->directory, file);
2621 	}
2622 
2623 	g_clear_pointer (&file->details->name, g_ref_string_release);
2624 	file->details->name = g_ref_string_new (name);
2625 
2626 	if (!file->details->got_custom_display_name) {
2627 		caja_file_clear_display_name (file);
2628 	}
2629 
2630 	if (in_directory) {
2631 		caja_directory_end_file_name_change
2632 			(file->details->directory, file, node);
2633 	}
2634 
2635 	return TRUE;
2636 }
2637 
2638 gboolean
caja_file_update_name(CajaFile * file,const char * name)2639 caja_file_update_name (CajaFile *file, const char *name)
2640 {
2641 	gboolean ret;
2642 
2643 	ret = update_name_internal (file, name, TRUE);
2644 
2645 	if (ret) {
2646 		update_links_if_target (file);
2647 	}
2648 
2649 	return ret;
2650 }
2651 
2652 gboolean
caja_file_update_name_and_directory(CajaFile * file,const char * name,CajaDirectory * new_directory)2653 caja_file_update_name_and_directory (CajaFile *file,
2654 					 const char *name,
2655 					 CajaDirectory *new_directory)
2656 {
2657 	CajaDirectory *old_directory;
2658 	FileMonitors *monitors;
2659 
2660 	g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
2661 	g_return_val_if_fail (CAJA_IS_DIRECTORY (file->details->directory), FALSE);
2662 	g_return_val_if_fail (!file->details->is_gone, FALSE);
2663 	g_return_val_if_fail (!caja_file_is_self_owned (file), FALSE);
2664 	g_return_val_if_fail (CAJA_IS_DIRECTORY (new_directory), FALSE);
2665 
2666 	old_directory = file->details->directory;
2667 	if (old_directory == new_directory) {
2668 		if (name) {
2669 			return update_name_internal (file, name, TRUE);
2670 		} else {
2671 			return FALSE;
2672 		}
2673 	}
2674 
2675 	caja_file_ref (file);
2676 
2677 	/* FIXME bugzilla.gnome.org 42044: Need to let links that
2678 	 * point to the old name know that the file has been moved.
2679 	 */
2680 
2681 	remove_from_link_hash_table (file);
2682 
2683 	monitors = caja_directory_remove_file_monitors (old_directory, file);
2684 	caja_directory_remove_file (old_directory, file);
2685 
2686 	file->details->directory = caja_directory_ref (new_directory);
2687 	caja_directory_unref (old_directory);
2688 
2689 	if (name) {
2690 		update_name_internal (file, name, FALSE);
2691 	}
2692 
2693 	caja_directory_add_file (new_directory, file);
2694 	caja_directory_add_file_monitors (new_directory, file, monitors);
2695 
2696 	add_to_link_hash_table (file);
2697 
2698 	update_links_if_target (file);
2699 
2700 	caja_file_unref (file);
2701 
2702 	return TRUE;
2703 }
2704 
2705 void
caja_file_set_directory(CajaFile * file,CajaDirectory * new_directory)2706 caja_file_set_directory (CajaFile *file,
2707 			     CajaDirectory *new_directory)
2708 {
2709 	caja_file_update_name_and_directory (file, NULL, new_directory);
2710 }
2711 
2712 static Knowledge
get_item_count(CajaFile * file,guint * count)2713 get_item_count (CajaFile *file,
2714 		guint *count)
2715 {
2716 	gboolean known, unreadable;
2717 
2718 	known = caja_file_get_directory_item_count
2719 		(file, count, &unreadable);
2720 	if (!known) {
2721 		return UNKNOWN;
2722 	}
2723 	if (unreadable) {
2724 		return UNKNOWABLE;
2725 	}
2726 	return KNOWN;
2727 }
2728 
2729 static Knowledge
get_size(CajaFile * file,goffset * size,gboolean size_on_disk)2730 get_size (CajaFile *file,
2731 	  goffset *size,
2732 	  gboolean size_on_disk)
2733 {
2734 	/* If we tried and failed, then treat it like there is no size
2735 	 * to know.
2736 	 */
2737 	if (file->details->get_info_failed) {
2738 		return UNKNOWABLE;
2739 	}
2740 
2741 	/* If the info is NULL that means we haven't even tried yet,
2742 	 * so it's just unknown, not unknowable.
2743 	 */
2744 	if (!file->details->got_file_info) {
2745 		return UNKNOWN;
2746 	}
2747 
2748 	/* If we got info with no size in it, it means there is no
2749 	 * such thing as a size as far as mate-vfs is concerned,
2750 	 * so "unknowable".
2751 	 */
2752 	if (size_on_disk && file->details->size_on_disk == -1) {
2753 		return UNKNOWABLE;
2754 	}
2755 
2756 	if (!size_on_disk && file->details->size == -1) {
2757 		return UNKNOWABLE;
2758 	}
2759 
2760 	/* We have a size! */
2761 	if (size_on_disk) {
2762 		*size = file->details->size_on_disk;
2763 	} else {
2764 		*size = file->details->size;
2765 	}
2766 
2767 	return KNOWN;
2768 }
2769 
2770 static Knowledge
get_time(CajaFile * file,time_t * time_out,CajaDateType type)2771 get_time (CajaFile *file,
2772 	  time_t *time_out,
2773 	  CajaDateType type)
2774 {
2775 	time_t time;
2776 
2777 	/* If we tried and failed, then treat it like there is no size
2778 	 * to know.
2779 	 */
2780 	if (file->details->get_info_failed) {
2781 		return UNKNOWABLE;
2782 	}
2783 
2784 	/* If the info is NULL that means we haven't even tried yet,
2785 	 * so it's just unknown, not unknowable.
2786 	 */
2787 	if (!file->details->got_file_info) {
2788 		return UNKNOWN;
2789 	}
2790 
2791 	time = 0;
2792 	switch (type) {
2793 	case CAJA_DATE_TYPE_MODIFIED:
2794 		time = file->details->mtime;
2795 		break;
2796 	case CAJA_DATE_TYPE_ACCESSED:
2797 		time = file->details->atime;
2798 		break;
2799 	case CAJA_DATE_TYPE_CREATED:
2800 		time = file->details->btime;
2801 		break;
2802 	case CAJA_DATE_TYPE_TRASHED:
2803 		time = file->details->trash_time;
2804 		break;
2805 	default:
2806 		g_assert_not_reached ();
2807 		break;
2808 	}
2809 
2810 	*time_out = time;
2811 
2812 	/* If we got info with no modification time in it, it means
2813 	 * there is no such thing as a modification time as far as
2814 	 * mate-vfs is concerned, so "unknowable".
2815 	 */
2816 	if (time == 0) {
2817 		return UNKNOWABLE;
2818 	}
2819 	return KNOWN;
2820 }
2821 
2822 static int
compare_directories_by_count(CajaFile * file_1,CajaFile * file_2)2823 compare_directories_by_count (CajaFile *file_1, CajaFile *file_2)
2824 {
2825 	/* Sort order:
2826 	 *   Directories with unknown # of items
2827 	 *   Directories with "unknowable" # of items
2828 	 *   Directories with 0 items
2829 	 *   Directories with n items
2830 	 */
2831 
2832 	Knowledge count_known_1, count_known_2;
2833 	guint count_1, count_2;
2834 
2835 	count_known_1 = get_item_count (file_1, &count_1);
2836 	count_known_2 = get_item_count (file_2, &count_2);
2837 
2838 	if (count_known_1 > count_known_2) {
2839 		return -1;
2840 	}
2841 	if (count_known_1 < count_known_2) {
2842 		return +1;
2843 	}
2844 
2845 	/* count_known_1 and count_known_2 are equal now. Check if count
2846 	 * details are UNKNOWABLE or UNKNOWN.
2847 	 */
2848 	if (count_known_1 == UNKNOWABLE || count_known_1 == UNKNOWN) {
2849 		return 0;
2850 	}
2851 
2852 	if (count_1 < count_2) {
2853 		return -1;
2854 	}
2855 	if (count_1 > count_2) {
2856 		return +1;
2857 	}
2858 
2859 	return 0;
2860 }
2861 
2862 static int
compare_files_by_size(CajaFile * file_1,CajaFile * file_2,gboolean size_on_disk)2863 compare_files_by_size (CajaFile *file_1, CajaFile *file_2, gboolean size_on_disk)
2864 {
2865 	/* Sort order:
2866 	 *   Files with unknown size.
2867 	 *   Files with "unknowable" size.
2868 	 *   Files with smaller sizes.
2869 	 *   Files with large sizes.
2870 	 */
2871 
2872 	Knowledge size_known_1, size_known_2;
2873 	goffset size_1 = 0, size_2 = 0;
2874 
2875 	size_known_1 = get_size (file_1, &size_1, size_on_disk);
2876 	size_known_2 = get_size (file_2, &size_2, size_on_disk);
2877 
2878 	if (size_known_1 > size_known_2) {
2879 		return -1;
2880 	}
2881 	if (size_known_1 < size_known_2) {
2882 		return +1;
2883 	}
2884 
2885 	/* size_known_1 and size_known_2 are equal now. Check if size
2886 	 * details are UNKNOWABLE or UNKNOWN
2887 	 */
2888 	if (size_known_1 == UNKNOWABLE || size_known_1 == UNKNOWN) {
2889 		return 0;
2890 	}
2891 
2892 	if (size_1 < size_2) {
2893 		return -1;
2894 	}
2895 	if (size_1 > size_2) {
2896 		return +1;
2897 	}
2898 
2899 	return 0;
2900 }
2901 
2902 static int
compare_by_size(CajaFile * file_1,CajaFile * file_2,gboolean size_on_disk)2903 compare_by_size (CajaFile *file_1, CajaFile *file_2, gboolean size_on_disk)
2904 {
2905 	/* Sort order:
2906 	 *   Directories with n items
2907 	 *   Directories with 0 items
2908 	 *   Directories with "unknowable" # of items
2909 	 *   Directories with unknown # of items
2910 	 *   Files with large sizes.
2911 	 *   Files with smaller sizes.
2912 	 *   Files with "unknowable" size.
2913 	 *   Files with unknown size.
2914 	 */
2915 
2916 	gboolean is_directory_1, is_directory_2;
2917 
2918 	is_directory_1 = caja_file_is_directory (file_1);
2919 	is_directory_2 = caja_file_is_directory (file_2);
2920 
2921 	if (is_directory_1 && !is_directory_2) {
2922 		return -1;
2923 	}
2924 	if (is_directory_2 && !is_directory_1) {
2925 		return +1;
2926 	}
2927 
2928 	if (is_directory_1) {
2929 		return compare_directories_by_count (file_1, file_2);
2930 	} else {
2931 		return compare_files_by_size (file_1, file_2, size_on_disk);
2932 	}
2933 }
2934 
2935 static int
compare_by_display_name(CajaFile * file_1,CajaFile * file_2)2936 compare_by_display_name (CajaFile *file_1, CajaFile *file_2)
2937 {
2938 	const char *name_1, *name_2;
2939 	const char *key_1, *key_2;
2940 	gboolean sort_last_1, sort_last_2;
2941 	int compare;
2942 
2943 	name_1 = caja_file_peek_display_name (file_1);
2944 	name_2 = caja_file_peek_display_name (file_2);
2945 
2946 	sort_last_1 = name_1[0] == SORT_LAST_CHAR1 || name_1[0] == SORT_LAST_CHAR2;
2947 	sort_last_2 = name_2[0] == SORT_LAST_CHAR1 || name_2[0] == SORT_LAST_CHAR2;
2948 
2949 	if (sort_last_1 && !sort_last_2) {
2950 		compare = +1;
2951 	} else if (!sort_last_1 && sort_last_2) {
2952 		compare = -1;
2953 	} else {
2954 		key_1 = caja_file_peek_display_name_collation_key (file_1);
2955 		key_2 = caja_file_peek_display_name_collation_key (file_2);
2956 		compare = strcmp (key_1, key_2);
2957 	}
2958 
2959 	return compare;
2960 }
2961 
2962 static int
compare_by_directory_name(CajaFile * file_1,CajaFile * file_2)2963 compare_by_directory_name (CajaFile *file_1, CajaFile *file_2)
2964 {
2965 	char *directory_1, *directory_2;
2966 	int compare;
2967 
2968 	if (file_1->details->directory == file_2->details->directory) {
2969 		return 0;
2970 	}
2971 
2972 	directory_1 = caja_file_get_parent_uri_for_display (file_1);
2973 	directory_2 = caja_file_get_parent_uri_for_display (file_2);
2974 
2975 	compare = g_utf8_collate (directory_1, directory_2);
2976 
2977 	g_free (directory_1);
2978 	g_free (directory_2);
2979 
2980 	return compare;
2981 }
2982 
2983 static gboolean
file_has_note(CajaFile * file)2984 file_has_note (CajaFile *file)
2985 {
2986 	char *note;
2987 	gboolean res;
2988 
2989 	note = caja_file_get_metadata (file, CAJA_METADATA_KEY_ANNOTATION, NULL);
2990 	res = note != NULL && note[0] != 0;
2991 	g_free (note);
2992 
2993 	return res;
2994 }
2995 
2996 static GList *
prepend_automatic_keywords(CajaFile * file,GList * names)2997 prepend_automatic_keywords (CajaFile *file,
2998 			    GList *names)
2999 {
3000 	/* Prepend in reverse order. */
3001 	CajaFile *parent;
3002 
3003 	parent = caja_file_get_parent (file);
3004 
3005 #ifdef TRASH_IS_FAST_ENOUGH
3006 	if (caja_file_is_in_trash (file)) {
3007 		names = g_list_prepend
3008 			(names, g_strdup (CAJA_FILE_EMBLEM_NAME_TRASH));
3009 	}
3010 #endif
3011 	if (file_has_note (file)) {
3012 		names = g_list_prepend
3013 			(names, g_strdup (CAJA_FILE_EMBLEM_NAME_NOTE));
3014 	}
3015 
3016 	/* Trash files are assumed to be read-only,
3017 	 * so we want to ignore them here. */
3018 	if (!caja_file_can_write (file) &&
3019 	    !caja_file_is_in_trash (file) &&
3020 	    (parent == NULL || caja_file_can_write (parent))) {
3021 		names = g_list_prepend
3022 			(names, g_strdup (CAJA_FILE_EMBLEM_NAME_CANT_WRITE));
3023 	}
3024 	if (!caja_file_can_read (file)) {
3025 		names = g_list_prepend
3026 			(names, g_strdup (CAJA_FILE_EMBLEM_NAME_CANT_READ));
3027 	}
3028 	if (caja_file_is_symbolic_link (file)) {
3029 		names = g_list_prepend
3030 			(names, g_strdup (CAJA_FILE_EMBLEM_NAME_SYMBOLIC_LINK));
3031 	}
3032 
3033 	if (parent) {
3034 		caja_file_unref (parent);
3035 	}
3036 
3037 
3038 	return names;
3039 }
3040 
3041 static void
fill_emblem_cache_if_needed(CajaFile * file)3042 fill_emblem_cache_if_needed (CajaFile *file)
3043 {
3044 	GList *node, *keywords;
3045 	char *scanner;
3046 	size_t length;
3047 
3048 	if (file->details->compare_by_emblem_cache != NULL) {
3049 		/* Got a cache already. */
3050 		return;
3051 	}
3052 
3053 	keywords = caja_file_get_keywords (file);
3054 
3055 	/* Add up the keyword string lengths */
3056 	length = 1;
3057 	for (node = keywords; node != NULL; node = node->next) {
3058 		length += strlen ((const char *) node->data) + 1;
3059 	}
3060 
3061 	/* Now that we know how large the cache struct needs to be, allocate it. */
3062 	file->details->compare_by_emblem_cache = g_malloc (sizeof(CajaFileSortByEmblemCache) + length);
3063 
3064 	/* Copy them into the cache. */
3065 	scanner = file->details->compare_by_emblem_cache->emblem_keywords;
3066 	for (node = keywords; node != NULL; node = node->next) {
3067 		length = strlen ((const char *) node->data) + 1;
3068 		memcpy (scanner, (const char *) node->data, length);
3069 		scanner += length;
3070 	}
3071 
3072 	/* Zero-terminate so we can tell where the list ends. */
3073 	*scanner = 0;
3074 
3075         g_list_free_full (keywords, g_free);
3076 }
3077 
3078 static int
compare_by_emblems(CajaFile * file_1,CajaFile * file_2)3079 compare_by_emblems (CajaFile *file_1, CajaFile *file_2)
3080 {
3081 	const char *keyword_cache_1, *keyword_cache_2;
3082 	int compare_result;
3083 
3084 	fill_emblem_cache_if_needed (file_1);
3085 	fill_emblem_cache_if_needed (file_2);
3086 
3087 	/* We ignore automatic emblems, and only sort by user-added keywords. */
3088 	compare_result = 0;
3089 	keyword_cache_1 = file_1->details->compare_by_emblem_cache->emblem_keywords;
3090 	keyword_cache_2 = file_2->details->compare_by_emblem_cache->emblem_keywords;
3091 	for (; *keyword_cache_1 != '\0' && *keyword_cache_2 != '\0';) {
3092 		size_t length;
3093 
3094 		compare_result = g_utf8_collate (keyword_cache_1, keyword_cache_2);
3095 		if (compare_result != 0) {
3096 			return compare_result;
3097 		}
3098 
3099 		/* Advance to the next keyword */
3100 		length = strlen (keyword_cache_1);
3101 		keyword_cache_1 += length + 1;
3102 		keyword_cache_2 += length + 1;
3103 	}
3104 
3105 
3106 	/* One or both is now NULL. */
3107 	if (*keyword_cache_1 != '\0') {
3108 		g_assert (*keyword_cache_2 == '\0');
3109 		return -1;
3110 	} else if (*keyword_cache_2 != '\0') {
3111 		return +1;
3112 	}
3113 
3114 	return 0;
3115 }
3116 
3117 static int
compare_by_type(CajaFile * file_1,CajaFile * file_2)3118 compare_by_type (CajaFile *file_1, CajaFile *file_2)
3119 {
3120 	gboolean is_directory_1;
3121 	gboolean is_directory_2;
3122 	char *type_string_1;
3123 	char *type_string_2;
3124 	int result;
3125 
3126 	/* Directories go first. Then, if mime types are identical,
3127 	 * don't bother getting strings (for speed). This assumes
3128 	 * that the string is dependent entirely on the mime type,
3129 	 * which is true now but might not be later.
3130 	 */
3131 	is_directory_1 = caja_file_is_directory (file_1);
3132 	is_directory_2 = caja_file_is_directory (file_2);
3133 
3134 	if (is_directory_1 && is_directory_2) {
3135 		return 0;
3136 	}
3137 
3138 	if (is_directory_1) {
3139 		return -1;
3140 	}
3141 
3142 	if (is_directory_2) {
3143 		return +1;
3144 	}
3145 
3146 	if (file_1->details->mime_type != NULL &&
3147 	    file_2->details->mime_type != NULL &&
3148 	    strcmp (file_1->details->mime_type,
3149                     file_2->details->mime_type) == 0) {
3150 		return 0;
3151 	}
3152 
3153 	type_string_1 = caja_file_get_type_as_string (file_1);
3154 	type_string_2 = caja_file_get_type_as_string (file_2);
3155 
3156 	result = g_utf8_collate (type_string_1, type_string_2);
3157 
3158 	g_free (type_string_1);
3159 	g_free (type_string_2);
3160 
3161 	return result;
3162 }
3163 
3164 static int
compare_by_time(CajaFile * file_1,CajaFile * file_2,CajaDateType type)3165 compare_by_time (CajaFile *file_1, CajaFile *file_2, CajaDateType type)
3166 {
3167 	/* Sort order:
3168 	 *   Files with unknown times.
3169 	 *   Files with "unknowable" times.
3170 	 *   Files with older times.
3171 	 *   Files with newer times.
3172 	 */
3173 
3174 	Knowledge time_known_1, time_known_2;
3175 	time_t time_1, time_2;
3176 
3177 	time_1 = 0;
3178 	time_2 = 0;
3179 
3180 	time_known_1 = get_time (file_1, &time_1, type);
3181 	time_known_2 = get_time (file_2, &time_2, type);
3182 
3183 	if (time_known_1 > time_known_2) {
3184 		return -1;
3185 	}
3186 	if (time_known_1 < time_known_2) {
3187 		return +1;
3188 	}
3189 
3190 	/* Now time_known_1 is equal to time_known_2. Check whether
3191 	 * we failed to get modification times for files
3192 	 */
3193 	if(time_known_1 == UNKNOWABLE || time_known_1 == UNKNOWN) {
3194 		return 0;
3195 	}
3196 
3197 	if (time_1 < time_2) {
3198 		return -1;
3199 	}
3200 	if (time_1 > time_2) {
3201 		return +1;
3202 	}
3203 
3204 	return 0;
3205 }
3206 
3207 static int
compare_by_full_path(CajaFile * file_1,CajaFile * file_2)3208 compare_by_full_path (CajaFile *file_1, CajaFile *file_2)
3209 {
3210 	int compare;
3211 
3212 	compare = compare_by_directory_name (file_1, file_2);
3213 	if (compare != 0) {
3214 		return compare;
3215 	}
3216 	return compare_by_display_name (file_1, file_2);
3217 }
3218 
3219 /* prev_extension_segment:
3220  * @basename The basename of a file
3221  * @rem_chars A pointer to the amount of remaining characters
3222  *
3223  * Finds the next segment delimiter to the left. A starting character of '.' is
3224  * set to '\0'.
3225  *
3226  * Return value: The start of the previous segment (right of the dot) or
3227  * basename if there are none remaining.
3228  */
3229 static char *
prev_extension_segment(char * basename,int * rem_chars)3230 prev_extension_segment (char *basename, int *rem_chars)
3231 {
3232 	if (*basename == '.') {
3233 		*basename = 0;
3234 		basename--;
3235 		(*rem_chars)--;
3236 	}
3237 
3238 	while (*rem_chars > 0 && *basename != '.') {
3239 		(*rem_chars)--;
3240 		basename--;
3241 	}
3242 
3243 	return basename + 1;
3244 }
3245 
3246 /* is_valid_extension_segment:
3247  * @segment Part of a modifiable zero-terminated string
3248  * @segment_index The index of the current segment
3249  *
3250  * Uses a heuristic to identify valid file extensions.
3251  *
3252  * Return value: Whether the segment is part of the file extension.
3253  */
3254 static gboolean
is_valid_extension_segment(const char * segment,int segment_index)3255 is_valid_extension_segment (const char *segment, int segment_index)
3256 {
3257 	gboolean result;
3258 	gboolean has_letters;
3259 	int char_offset;
3260 	switch (segment_index) {
3261 		case 0:
3262 			/* extremely long segments are probably not part of the extension */
3263 			result = strlen (segment) < 20;
3264 			break;
3265 		default:
3266 			has_letters = FALSE;
3267 			char_offset = 0;
3268 			while (TRUE) {
3269 				char c;
3270 
3271 				c = *(segment + char_offset);
3272 				if (c == '\0') {
3273 					result = has_letters;
3274 					break;
3275 				}
3276 				/* allow digits if there are also letters */
3277 				else if (isalpha (c)) {
3278 					has_letters = TRUE;
3279 				}
3280 				/* fail if it is neither digit nor letter */
3281 				else if (!isdigit (c)) {
3282 					result = FALSE;
3283 					break;
3284 				}
3285 
3286 				if (char_offset >= SORT_BY_EXTENSION_FOLLOWING_MAX_LENGTH) {
3287 					result = FALSE;
3288 					break;
3289 				}
3290 				char_offset++;
3291 			}
3292 	}
3293 	return result;
3294 }
3295 
3296 static int
compare_by_extension_segments(CajaFile * file_1,CajaFile * file_2)3297 compare_by_extension_segments (CajaFile *file_1, CajaFile *file_2)
3298 {
3299 	char *name_1, *name_2;
3300 	char *segment_1, *segment_2;
3301 	int compare;
3302 	int rem_chars_1, rem_chars_2;
3303 	gboolean done_1, done_2;
3304 	gboolean is_directory_1, is_directory_2;
3305 	int segment_index;
3306 
3307 
3308 	/* Directories do not have an extension */
3309 	is_directory_1 = caja_file_is_directory (file_1);
3310 	is_directory_2 = caja_file_is_directory (file_2);
3311 
3312 	if (is_directory_1 && is_directory_2) {
3313 		return 0;
3314 	} else if (is_directory_1) {
3315 		return -1;
3316 	} else if (is_directory_2) {
3317 		return 1;
3318 	}
3319 
3320 	name_1 = caja_file_get_display_name (file_1);
3321 	name_2 = caja_file_get_display_name (file_2);
3322 	rem_chars_1 = strlen (name_1);
3323 	rem_chars_2 = strlen (name_2);
3324 
3325 	/* Point to one after the zero character */
3326 	segment_1 = name_1 + rem_chars_1 + 1;
3327 	segment_2 = name_2 + rem_chars_2 + 1;
3328 
3329 	segment_index = 0;
3330 	do {
3331 		segment_1 = prev_extension_segment (segment_1 - 1, &rem_chars_1);
3332 		segment_2 = prev_extension_segment (segment_2 - 1, &rem_chars_2);
3333 
3334 		done_1 = rem_chars_1 <= 0 || !is_valid_extension_segment (segment_1, segment_index);
3335 		done_2 = rem_chars_2 <= 0 || !is_valid_extension_segment (segment_2, segment_index);
3336 		if (done_1 && !done_2) {
3337 			compare = -1;
3338 			break;
3339 		}
3340 		else if (!done_1 && done_2) {
3341 			compare = 1;
3342 			break;
3343 		}
3344 		else if (done_1 && done_2) {
3345 			compare = 0;
3346 			break;
3347 		}
3348 
3349 		segment_index++;
3350 		if (segment_index > SORT_BY_EXTENSION_MAX_SEGMENTS - 1) {
3351 			break;
3352 		}
3353 		compare = strcmp (segment_1, segment_2);
3354 	} while (compare == 0);
3355 
3356 	g_free (name_1);
3357 	g_free (name_2);
3358 
3359 	return compare;
3360 }
3361 
3362 static gchar *
caja_file_get_extension_as_string(CajaFile * file)3363 caja_file_get_extension_as_string (CajaFile *file)
3364 {
3365 	int rem_chars;
3366 	char *segment;
3367 
3368 	if (!caja_file_is_directory (file)) {
3369 		char *name;
3370 
3371 		name = caja_file_get_display_name (file);
3372 		rem_chars = strlen (name);
3373 		segment = prev_extension_segment (name + rem_chars, &rem_chars);
3374 
3375 		if (rem_chars > 0 && is_valid_extension_segment (segment, 0)) {
3376 			int segment_index;
3377 			char *right_segment;
3378 			char *result;
3379 
3380 			segment_index = 1;
3381 			do {
3382 				right_segment = segment;
3383 				segment = prev_extension_segment (segment - 1, &rem_chars);
3384 				if (rem_chars > 0 && is_valid_extension_segment (segment, segment_index)) {
3385 					/* remove zero-termination of segment */
3386 					*(right_segment - 1) = '.';
3387 				}
3388 				else {
3389 					break;
3390 				}
3391 
3392 				segment_index++;
3393 			} while (segment_index < SORT_BY_EXTENSION_MAX_SEGMENTS + 1);
3394 			result = g_strdup (right_segment);
3395 			g_free (name);
3396 			return result;
3397 		}
3398 		g_free (name);
3399 	}
3400 	return g_strdup ("");
3401 }
3402 
3403 static int
caja_file_compare_for_sort_internal(CajaFile * file_1,CajaFile * file_2,gboolean directories_first,gboolean reversed)3404 caja_file_compare_for_sort_internal (CajaFile *file_1,
3405 					 CajaFile *file_2,
3406 					 gboolean directories_first,
3407 					 gboolean reversed)
3408 {
3409 	gboolean is_directory_1, is_directory_2;
3410 
3411 	if (directories_first) {
3412 		is_directory_1 = caja_file_is_directory (file_1);
3413 		is_directory_2 = caja_file_is_directory (file_2);
3414 
3415 		if (is_directory_1 && !is_directory_2) {
3416 			return -1;
3417 		}
3418 
3419 		if (is_directory_2 && !is_directory_1) {
3420 			return +1;
3421 		}
3422 	}
3423 
3424 	if (file_1->details->sort_order < file_2->details->sort_order) {
3425 		return reversed ? 1 : -1;
3426 	} else if (file_1->details->sort_order > file_2->details->sort_order) {
3427 		return reversed ? -1 : 1;
3428 	}
3429 
3430 	return 0;
3431 }
3432 
3433 /**
3434  * caja_file_compare_for_sort:
3435  * @file_1: A file object
3436  * @file_2: Another file object
3437  * @sort_type: Sort criterion
3438  * @directories_first: Put all directories before any non-directories
3439  * @reversed: Reverse the order of the items, except that
3440  * the directories_first flag is still respected.
3441  *
3442  * Return value: int < 0 if @file_1 should come before file_2 in a
3443  * sorted list; int > 0 if @file_2 should come before file_1 in a
3444  * sorted list; 0 if @file_1 and @file_2 are equal for this sort criterion. Note
3445  * that each named sort type may actually break ties several ways, with the name
3446  * of the sort criterion being the primary but not only differentiator.
3447  **/
3448 int
caja_file_compare_for_sort(CajaFile * file_1,CajaFile * file_2,CajaFileSortType sort_type,gboolean directories_first,gboolean reversed)3449 caja_file_compare_for_sort (CajaFile *file_1,
3450 				CajaFile *file_2,
3451 				CajaFileSortType sort_type,
3452 				gboolean directories_first,
3453 				gboolean reversed)
3454 {
3455 	int result;
3456 
3457 	if (file_1 == file_2) {
3458 		return 0;
3459 	}
3460 
3461 	result = caja_file_compare_for_sort_internal (file_1, file_2, directories_first, reversed);
3462 
3463 	if (result == 0) {
3464 		switch (sort_type) {
3465 		case CAJA_FILE_SORT_BY_DISPLAY_NAME:
3466 			result = compare_by_display_name (file_1, file_2);
3467 			if (result == 0) {
3468 				result = compare_by_directory_name (file_1, file_2);
3469 			}
3470 			break;
3471 		case CAJA_FILE_SORT_BY_DIRECTORY:
3472 			result = compare_by_full_path (file_1, file_2);
3473 			break;
3474 		case CAJA_FILE_SORT_BY_SIZE:
3475 			/* Compare directory sizes ourselves, then if necessary
3476 			 * use MateVFS to compare file sizes.
3477 			 */
3478 			result = compare_by_size (file_1, file_2, FALSE);
3479 			if (result == 0) {
3480 				result = compare_by_full_path (file_1, file_2);
3481 			}
3482 			break;
3483 		case CAJA_FILE_SORT_BY_SIZE_ON_DISK:
3484 			/* Compare directory sizes ourselves, then if necessary
3485 			 * use MateVFS to compare file sizes.
3486 			 */
3487 			result = compare_by_size (file_1, file_2, TRUE);
3488 			if (result == 0) {
3489 				result = compare_by_full_path (file_1, file_2);
3490 			}
3491 			break;
3492 		case CAJA_FILE_SORT_BY_TYPE:
3493 			/* MateVFS doesn't know about our special text for certain
3494 			 * mime types, so we handle the mime-type sorting ourselves.
3495 			 */
3496 			result = compare_by_type (file_1, file_2);
3497 			if (result == 0) {
3498 				result = compare_by_full_path (file_1, file_2);
3499 			}
3500 			break;
3501 		case CAJA_FILE_SORT_BY_MTIME:
3502 			result = compare_by_time (file_1, file_2, CAJA_DATE_TYPE_MODIFIED);
3503 			if (result == 0) {
3504 				result = compare_by_full_path (file_1, file_2);
3505 			}
3506 			break;
3507 		case CAJA_FILE_SORT_BY_BTIME:
3508 			result = compare_by_time (file_1, file_2, CAJA_DATE_TYPE_CREATED);
3509 			if (result == 0) {
3510 				result = compare_by_full_path (file_1, file_2);
3511 			}
3512 			break;
3513 		case CAJA_FILE_SORT_BY_ATIME:
3514 			result = compare_by_time (file_1, file_2, CAJA_DATE_TYPE_ACCESSED);
3515 			if (result == 0) {
3516 				result = compare_by_full_path (file_1, file_2);
3517 			}
3518 			break;
3519 		case CAJA_FILE_SORT_BY_TRASHED_TIME:
3520 			result = compare_by_time (file_1, file_2, CAJA_DATE_TYPE_TRASHED);
3521 			if (result == 0) {
3522 				result = compare_by_full_path (file_1, file_2);
3523 			}
3524 			break;
3525 		case CAJA_FILE_SORT_BY_EMBLEMS:
3526 			/* MateVFS doesn't know squat about our emblems, so
3527 			 * we handle comparing them here, before falling back
3528 			 * to tie-breakers.
3529 			 */
3530 			result = compare_by_emblems (file_1, file_2);
3531 			if (result == 0) {
3532 				result = compare_by_full_path (file_1, file_2);
3533 			}
3534 			break;
3535 		case CAJA_FILE_SORT_BY_EXTENSION:
3536 			result = compare_by_extension_segments (file_1, file_2);
3537 			if (result == 0) {
3538 				result = compare_by_full_path (file_1, file_2);
3539 			}
3540 			break;
3541 		default:
3542 			g_return_val_if_reached (0);
3543 		}
3544 
3545 		if (reversed) {
3546 			result = -result;
3547 		}
3548 	}
3549 
3550 	return result;
3551 }
3552 
3553 int
caja_file_compare_for_sort_by_attribute_q(CajaFile * file_1,CajaFile * file_2,GQuark attribute,gboolean directories_first,gboolean reversed)3554 caja_file_compare_for_sort_by_attribute_q   (CajaFile                   *file_1,
3555 						 CajaFile                   *file_2,
3556 						 GQuark                          attribute,
3557 						 gboolean                        directories_first,
3558 						 gboolean                        reversed)
3559 {
3560 	int result;
3561 
3562 	if (file_1 == file_2) {
3563 		return 0;
3564 	}
3565 
3566 	/* Convert certain attributes into CajaFileSortTypes and use
3567 	 * caja_file_compare_for_sort()
3568 	 */
3569 	if (attribute == 0 || attribute == attribute_name_q) {
3570 		return caja_file_compare_for_sort (file_1, file_2,
3571 						       CAJA_FILE_SORT_BY_DISPLAY_NAME,
3572 						       directories_first,
3573 						       reversed);
3574 	} else if (attribute == attribute_size_q) {
3575 		return caja_file_compare_for_sort (file_1, file_2,
3576 						       CAJA_FILE_SORT_BY_SIZE,
3577 						       directories_first,
3578 						       reversed);
3579 	} else if (attribute == attribute_size_on_disk_q) {
3580 		return caja_file_compare_for_sort (file_1, file_2,
3581 						       CAJA_FILE_SORT_BY_SIZE_ON_DISK,
3582 						       directories_first,
3583 						       reversed);
3584 	} else if (attribute == attribute_type_q) {
3585 		return caja_file_compare_for_sort (file_1, file_2,
3586 						       CAJA_FILE_SORT_BY_TYPE,
3587 						       directories_first,
3588 						       reversed);
3589 	} else if (attribute == attribute_modification_date_q || attribute == attribute_date_modified_q) {
3590 		return caja_file_compare_for_sort (file_1, file_2,
3591 						       CAJA_FILE_SORT_BY_MTIME,
3592 						       directories_first,
3593 						       reversed);
3594         } else if (attribute == attribute_creation_date_q || attribute == attribute_date_created_q) {
3595                 return caja_file_compare_for_sort (file_1, file_2,
3596                                                        CAJA_FILE_SORT_BY_BTIME,
3597                                                        directories_first,
3598                                                        reversed);
3599         } else if (attribute == attribute_accessed_date_q || attribute == attribute_date_accessed_q) {
3600 		return caja_file_compare_for_sort (file_1, file_2,
3601 						       CAJA_FILE_SORT_BY_ATIME,
3602 						       directories_first,
3603 						       reversed);
3604         } else if (attribute == attribute_trashed_on_q) {
3605 		return caja_file_compare_for_sort (file_1, file_2,
3606 						       CAJA_FILE_SORT_BY_TRASHED_TIME,
3607 						       directories_first,
3608 						       reversed);
3609 	} else if (attribute == attribute_emblems_q) {
3610 		return caja_file_compare_for_sort (file_1, file_2,
3611 						       CAJA_FILE_SORT_BY_EMBLEMS,
3612 						       directories_first,
3613 						       reversed);
3614 	} else if (attribute == attribute_extension_q) {
3615 		return caja_file_compare_for_sort (file_1, file_2,
3616 						       CAJA_FILE_SORT_BY_EXTENSION,
3617 						       directories_first,
3618 						       reversed);
3619 	}
3620 
3621 	/* it is a normal attribute, compare by strings */
3622 
3623 	result = caja_file_compare_for_sort_internal (file_1, file_2, directories_first, reversed);
3624 
3625 	if (result == 0) {
3626 		char *value_1;
3627 		char *value_2;
3628 
3629 		value_1 = caja_file_get_string_attribute_q (file_1,
3630 								attribute);
3631 		value_2 = caja_file_get_string_attribute_q (file_2,
3632 								attribute);
3633 
3634 		if (value_1 != NULL && value_2 != NULL) {
3635 			result = strcmp (value_1, value_2);
3636 		}
3637 
3638 		g_free (value_1);
3639 		g_free (value_2);
3640 
3641 		if (reversed) {
3642 			result = -result;
3643 		}
3644 	}
3645 
3646 	return result;
3647 }
3648 
3649 int
caja_file_compare_for_sort_by_attribute(CajaFile * file_1,CajaFile * file_2,const char * attribute,gboolean directories_first,gboolean reversed)3650 caja_file_compare_for_sort_by_attribute     (CajaFile                   *file_1,
3651 						 CajaFile                   *file_2,
3652 						 const char                     *attribute,
3653 						 gboolean                        directories_first,
3654 						 gboolean                        reversed)
3655 {
3656 	return caja_file_compare_for_sort_by_attribute_q (file_1, file_2,
3657 							      g_quark_from_string (attribute),
3658 							      directories_first,
3659 							      reversed);
3660 }
3661 
3662 
3663 /**
3664  * caja_file_compare_name:
3665  * @file: A file object
3666  * @pattern: A string we are comparing it with
3667  *
3668  * Return value: result of a comparison of the file name and the given pattern,
3669  * using the same sorting order as sort by name.
3670  **/
3671 int
caja_file_compare_display_name(CajaFile * file,const char * pattern)3672 caja_file_compare_display_name (CajaFile *file,
3673 				    const char *pattern)
3674 {
3675 	const char *name;
3676 	int result;
3677 
3678 	g_return_val_if_fail (pattern != NULL, -1);
3679 
3680 	name = caja_file_peek_display_name (file);
3681 	result = g_utf8_collate (name, pattern);
3682 	return result;
3683 }
3684 
3685 
3686 gboolean
caja_file_is_hidden_file(CajaFile * file)3687 caja_file_is_hidden_file (CajaFile *file)
3688 {
3689 	return file->details->is_hidden;
3690 }
3691 
3692 static gboolean
caja_file_is_backup_file(CajaFile * file)3693 caja_file_is_backup_file (CajaFile *file)
3694 {
3695 	return file->details->is_backup;
3696 }
3697 
3698 /**
3699  * caja_file_should_show:
3700  * @file: the file to check.
3701  * @show_hidden: whether we want to show hidden files or not.
3702  * @show_backup: whether we want to show backup files or not.
3703  *
3704  * Determines if a #CajaFile should be shown. Note that when browsing
3705  * a trash directory, this function will always return %TRUE.
3706  *
3707  * Returns: %TRUE if the file should be shown, %FALSE if it shouldn't.
3708  */
3709 gboolean
caja_file_should_show(CajaFile * file,gboolean show_hidden,gboolean show_foreign,gboolean show_backup)3710 caja_file_should_show (CajaFile *file,
3711 		       gboolean show_hidden,
3712 		       gboolean show_foreign,
3713 		       gboolean show_backup)
3714 {
3715 	/* Never hide any files in trash. */
3716 	if (caja_file_is_in_trash (file)) {
3717 		return TRUE;
3718 	} else {
3719 		return (show_hidden || !caja_file_is_hidden_file (file)) &&
3720 			(show_backup || !caja_file_is_backup_file (file)) &&
3721 			(show_foreign || !(caja_file_is_in_desktop (file) && caja_file_is_foreign_link (file)));
3722 	}
3723 }
3724 
3725 gboolean
caja_file_is_home(CajaFile * file)3726 caja_file_is_home (CajaFile *file)
3727 {
3728 	GFile *dir;
3729 
3730 	dir = file->details->directory->details->location;
3731 	if (dir == NULL) {
3732 		return FALSE;
3733 	}
3734 
3735 	return caja_is_home_directory_file (dir, file->details->name);
3736 }
3737 
3738 gboolean
caja_file_is_in_desktop(CajaFile * file)3739 caja_file_is_in_desktop (CajaFile *file)
3740 {
3741 	if (file->details->directory->details->location) {
3742 		return caja_is_desktop_directory (file->details->directory->details->location);
3743 	}
3744 	return FALSE;
3745 
3746 }
3747 
3748 static gboolean
filter_hidden_partition_callback(gpointer data,gpointer callback_data)3749 filter_hidden_partition_callback (gpointer data,
3750 					     gpointer callback_data)
3751 {
3752 	CajaFile *file;
3753 	FilterOptions options;
3754 
3755 	file = CAJA_FILE (data);
3756 	options = GPOINTER_TO_INT (callback_data);
3757 
3758 	return caja_file_should_show (file,
3759 				      options & SHOW_HIDDEN,
3760 				      TRUE,
3761 				      options & SHOW_BACKUP);
3762 }
3763 
3764 GList *
caja_file_list_filter_hidden(GList * files,gboolean show_hidden)3765 caja_file_list_filter_hidden (GList    *files,
3766 					     gboolean  show_hidden)
3767 {
3768 	GList *filtered_files;
3769 	GList *removed_files;
3770 
3771 	/* FIXME bugzilla.gnome.org 40653:
3772 	 * Eventually this should become a generic filtering thingy.
3773 	 */
3774 
3775 	filtered_files = caja_file_list_copy (files);
3776 	filtered_files = eel_g_list_partition (filtered_files,
3777 					       filter_hidden_partition_callback,
3778 					       GINT_TO_POINTER ((show_hidden ? SHOW_HIDDEN : 0)),
3779 					       &removed_files);
3780 	caja_file_list_free (removed_files);
3781 
3782 	return filtered_files;
3783 }
3784 
3785 char *
caja_file_get_metadata(CajaFile * file,const char * key,const char * default_metadata)3786 caja_file_get_metadata (CajaFile *file,
3787 			    const char *key,
3788 			    const char *default_metadata)
3789 {
3790 	guint id;
3791 	char *value;
3792 
3793 	g_return_val_if_fail (key != NULL, g_strdup (default_metadata));
3794 	g_return_val_if_fail (key[0] != '\0', g_strdup (default_metadata));
3795 
3796 	if (file == NULL ||
3797 	    file->details->metadata == NULL) {
3798 		return g_strdup (default_metadata);
3799 	}
3800 
3801 	g_return_val_if_fail (CAJA_IS_FILE (file), g_strdup (default_metadata));
3802 
3803 	id = caja_metadata_get_id (key);
3804 	value = g_hash_table_lookup (file->details->metadata, GUINT_TO_POINTER (id));
3805 
3806 	if (value) {
3807 		return g_strdup (value);
3808 	}
3809 	return g_strdup (default_metadata);
3810 }
3811 
3812 GList *
caja_file_get_metadata_list(CajaFile * file,const char * key)3813 caja_file_get_metadata_list (CajaFile *file,
3814 				 const char *key)
3815 {
3816 	guint id;
3817 	char **value;
3818 
3819 	g_return_val_if_fail (key != NULL, NULL);
3820 	g_return_val_if_fail (key[0] != '\0', NULL);
3821 
3822 	if (file == NULL ||
3823 	    file->details->metadata == NULL) {
3824 		return NULL;
3825 	}
3826 
3827 	g_return_val_if_fail (CAJA_IS_FILE (file), NULL);
3828 
3829 	id = caja_metadata_get_id (key);
3830 	id |= METADATA_ID_IS_LIST_MASK;
3831 
3832 	value = g_hash_table_lookup (file->details->metadata, GUINT_TO_POINTER (id));
3833 
3834 	if (value) {
3835 		GList *res;
3836 		int i;
3837 
3838 		res = NULL;
3839 		for (i = 0; value[i] != NULL; i++) {
3840 			res = g_list_prepend (res, g_strdup (value[i]));
3841 		}
3842 		return g_list_reverse (res);
3843 	}
3844 
3845 	return NULL;
3846 }
3847 
3848 void
caja_file_set_metadata(CajaFile * file,const char * key,const char * default_metadata,const char * metadata)3849 caja_file_set_metadata (CajaFile *file,
3850 			    const char *key,
3851 			    const char *default_metadata,
3852 			    const char *metadata)
3853 {
3854 	const char *val;
3855 
3856 	g_return_if_fail (CAJA_IS_FILE (file));
3857 	g_return_if_fail (key != NULL);
3858 	g_return_if_fail (key[0] != '\0');
3859 
3860 	val = metadata;
3861 	if (val == NULL) {
3862 		val = default_metadata;
3863 	}
3864 
3865 	EEL_CALL_METHOD
3866 		(CAJA_FILE_CLASS, file,
3867 		 set_metadata, (file, key, val));
3868 }
3869 
3870 void
caja_file_set_metadata_list(CajaFile * file,const char * key,GList * list)3871 caja_file_set_metadata_list (CajaFile *file,
3872 				 const char *key,
3873 				 GList *list)
3874 {
3875 	char **val;
3876 	int len, i;
3877 	GList *l;
3878 
3879 	g_return_if_fail (CAJA_IS_FILE (file));
3880 	g_return_if_fail (key != NULL);
3881 	g_return_if_fail (key[0] != '\0');
3882 
3883 	len = g_list_length (list);
3884 	val = g_new (char *, len + 1);
3885 	for (l = list, i = 0; l != NULL; l = l->next, i++) {
3886 		val[i] = l->data;
3887 	}
3888 	val[i] = NULL;
3889 
3890 	EEL_CALL_METHOD
3891 		(CAJA_FILE_CLASS, file,
3892 		 set_metadata_as_list, (file, key, val));
3893 
3894 	g_free (val);
3895 }
3896 
3897 
3898 gboolean
caja_file_get_boolean_metadata(CajaFile * file,const char * key,gboolean default_metadata)3899 caja_file_get_boolean_metadata (CajaFile *file,
3900 				    const char   *key,
3901 				    gboolean      default_metadata)
3902 {
3903 	char *result_as_string;
3904 	gboolean result;
3905 
3906 	g_return_val_if_fail (key != NULL, default_metadata);
3907 	g_return_val_if_fail (key[0] != '\0', default_metadata);
3908 
3909 	if (file == NULL) {
3910 		return default_metadata;
3911 	}
3912 
3913 	g_return_val_if_fail (CAJA_IS_FILE (file), default_metadata);
3914 
3915 	result_as_string = caja_file_get_metadata
3916 		(file, key, default_metadata ? "true" : "false");
3917 	g_assert (result_as_string != NULL);
3918 
3919 	if (g_ascii_strcasecmp (result_as_string, "true") == 0) {
3920 		result = TRUE;
3921 	} else if (g_ascii_strcasecmp (result_as_string, "false") == 0) {
3922 		result = FALSE;
3923 	} else {
3924 		g_error ("boolean metadata with value other than true or false");
3925 		result = default_metadata;
3926 	}
3927 
3928 	g_free (result_as_string);
3929 	return result;
3930 }
3931 
3932 int
caja_file_get_integer_metadata(CajaFile * file,const char * key,int default_metadata)3933 caja_file_get_integer_metadata (CajaFile *file,
3934 				    const char   *key,
3935 				    int           default_metadata)
3936 {
3937 	char *result_as_string;
3938 	char default_as_string[32];
3939 	int result;
3940 	char c;
3941 
3942 	g_return_val_if_fail (key != NULL, default_metadata);
3943 	g_return_val_if_fail (key[0] != '\0', default_metadata);
3944 
3945 	if (file == NULL) {
3946 		return default_metadata;
3947 	}
3948 	g_return_val_if_fail (CAJA_IS_FILE (file), default_metadata);
3949 
3950 	g_snprintf (default_as_string, sizeof (default_as_string), "%d", default_metadata);
3951 	result_as_string = caja_file_get_metadata
3952 		(file, key, default_as_string);
3953 
3954 	/* Normally we can't get a a NULL, but we check for it here to
3955 	 * handle the oddball case of a non-existent directory.
3956 	 */
3957 	if (result_as_string == NULL) {
3958 		result = default_metadata;
3959 	} else {
3960 		if (sscanf (result_as_string, " %d %c", &result, &c) != 1) {
3961 			result = default_metadata;
3962 		}
3963 		g_free (result_as_string);
3964 	}
3965 
3966 	return result;
3967 }
3968 
3969 static gboolean
get_time_from_time_string(const char * time_string,time_t * time)3970 get_time_from_time_string (const char *time_string,
3971 			   time_t *time)
3972 {
3973 	long scanned_time;
3974 	char c;
3975 
3976 	g_assert (time != NULL);
3977 
3978 	/* Only accept string if it has one integer with nothing
3979 	 * afterwards.
3980 	 */
3981 	if (time_string == NULL ||
3982 	    sscanf (time_string, "%ld%c", &scanned_time, &c) != 1) {
3983 		return FALSE;
3984 	}
3985 	*time = (time_t) scanned_time;
3986 	return TRUE;
3987 }
3988 
3989 time_t
caja_file_get_time_metadata(CajaFile * file,const char * key)3990 caja_file_get_time_metadata (CajaFile *file,
3991 				 const char   *key)
3992 {
3993 	time_t time;
3994 	char *time_string;
3995 
3996 	time_string = caja_file_get_metadata (file, key, NULL);
3997 	if (!get_time_from_time_string (time_string, &time)) {
3998 		time = UNDEFINED_TIME;
3999 	}
4000 	g_free (time_string);
4001 
4002 	return time;
4003 }
4004 
4005 void
caja_file_set_time_metadata(CajaFile * file,const char * key,time_t time)4006 caja_file_set_time_metadata (CajaFile *file,
4007 				 const char   *key,
4008 				 time_t        time)
4009 {
4010 	char time_str[21];
4011 	char *metadata;
4012 
4013 	if (time != UNDEFINED_TIME) {
4014 		/* 2^64 turns out to be 20 characters */
4015 		g_snprintf (time_str, 20, "%ld", (long int)time);
4016 		time_str[20] = '\0';
4017 		metadata = time_str;
4018 	} else {
4019 		metadata = NULL;
4020 	}
4021 
4022 	caja_file_set_metadata (file, key, NULL, metadata);
4023 }
4024 
4025 
4026 void
caja_file_set_boolean_metadata(CajaFile * file,const char * key,gboolean default_metadata,gboolean metadata)4027 caja_file_set_boolean_metadata (CajaFile *file,
4028 				    const char   *key,
4029 				    gboolean      default_metadata,
4030 				    gboolean      metadata)
4031 {
4032 	g_return_if_fail (CAJA_IS_FILE (file));
4033 	g_return_if_fail (key != NULL);
4034 	g_return_if_fail (key[0] != '\0');
4035 
4036 	caja_file_set_metadata (file, key,
4037 				    default_metadata ? "true" : "false",
4038 				    metadata ? "true" : "false");
4039 }
4040 
4041 void
caja_file_set_integer_metadata(CajaFile * file,const char * key,int default_metadata,int metadata)4042 caja_file_set_integer_metadata (CajaFile *file,
4043 				    const char   *key,
4044 				    int           default_metadata,
4045 				    int           metadata)
4046 {
4047 	char value_as_string[32];
4048 	char default_as_string[32];
4049 
4050 	g_return_if_fail (CAJA_IS_FILE (file));
4051 	g_return_if_fail (key != NULL);
4052 	g_return_if_fail (key[0] != '\0');
4053 
4054 	g_snprintf (value_as_string, sizeof (value_as_string), "%d", metadata);
4055 	g_snprintf (default_as_string, sizeof (default_as_string), "%d", default_metadata);
4056 
4057 	caja_file_set_metadata (file, key,
4058 				    default_as_string, value_as_string);
4059 }
4060 
4061 static const char *
caja_file_peek_display_name_collation_key(CajaFile * file)4062 caja_file_peek_display_name_collation_key (CajaFile *file)
4063 {
4064 	const char *res;
4065 
4066 	res = file->details->display_name_collation_key;
4067 	if (res == NULL)
4068 		res = "";
4069 
4070 	return res;
4071 }
4072 
4073 static const char *
caja_file_peek_display_name(CajaFile * file)4074 caja_file_peek_display_name (CajaFile *file)
4075 {
4076 	/*
4077 	stefano-k: Imported 15_nautilus_file_peek_crash.patch from debian nautilus
4078 	Date: Thu, 27 Jan 2011 10:22:10 +0000
4079 	Subject: Prevent a crash in nautilus_file_peek_display_name() on invalid NautilusFile
4080 	This is more a workaround only, expect assert failures at other
4081 	places when something bad happens. There's a race condition somewhere,
4082 	this patch only prevents immediate crash.
4083 	Patch by Marcus Husar <marcus.husar@rose.uni-heidelberg.de>
4084 	https://bugzilla.gnome.org/show_bug.cgi?id=602500
4085 	*/
4086 	if (file == NULL || caja_file_is_gone (file))
4087 		return "";
4088 
4089 	/* Default to display name based on filename if its not set yet */
4090 
4091 	if (file->details->display_name == NULL) {
4092 		const char *name;
4093 
4094 		name = file->details->name;
4095 		if (g_utf8_validate (name, -1, NULL)) {
4096 			caja_file_set_display_name (file,
4097 							name,
4098 							NULL,
4099 							FALSE);
4100 		} else {
4101 			char *escaped_name;
4102 
4103 			escaped_name = g_uri_escape_string (name, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
4104 			caja_file_set_display_name (file,
4105 							escaped_name,
4106 							NULL,
4107 							FALSE);
4108 			g_free (escaped_name);
4109 		}
4110 	}
4111 
4112 	return file->details->display_name;
4113 }
4114 
4115 char *
caja_file_get_display_name(CajaFile * file)4116 caja_file_get_display_name (CajaFile *file)
4117 {
4118 	return g_strdup (caja_file_peek_display_name (file));
4119 }
4120 
4121 char *
caja_file_get_edit_name(CajaFile * file)4122 caja_file_get_edit_name (CajaFile *file)
4123 {
4124 	const char *res;
4125 
4126 	res = file->details->edit_name;
4127 	if (res == NULL)
4128 		res = "";
4129 
4130 	return g_strdup (res);
4131 }
4132 
4133 char *
caja_file_get_name(CajaFile * file)4134 caja_file_get_name (CajaFile *file)
4135 {
4136 	return g_strdup (file->details->name);
4137 }
4138 
4139 /**
4140  * caja_file_get_description:
4141  * @file: a #CajaFile.
4142  *
4143  * Gets the standard::description key from @file, if
4144  * it has been cached.
4145  *
4146  * Returns: a string containing the value of the standard::description
4147  * 	key, or %NULL.
4148  */
4149 char *
caja_file_get_description(CajaFile * file)4150 caja_file_get_description (CajaFile *file)
4151 {
4152 	return g_strdup (file->details->description);
4153 }
4154 
4155 void
caja_file_monitor_add(CajaFile * file,gconstpointer client,CajaFileAttributes attributes)4156 caja_file_monitor_add (CajaFile *file,
4157 			   gconstpointer client,
4158 			   CajaFileAttributes attributes)
4159 {
4160 	g_return_if_fail (CAJA_IS_FILE (file));
4161 	g_return_if_fail (client != NULL);
4162 
4163 	EEL_CALL_METHOD
4164 		(CAJA_FILE_CLASS, file,
4165 		 monitor_add, (file, client, attributes));
4166 }
4167 
4168 void
caja_file_monitor_remove(CajaFile * file,gconstpointer client)4169 caja_file_monitor_remove (CajaFile *file,
4170 			      gconstpointer client)
4171 {
4172 	g_return_if_fail (CAJA_IS_FILE (file));
4173 	g_return_if_fail (client != NULL);
4174 
4175 	EEL_CALL_METHOD
4176 		(CAJA_FILE_CLASS, file,
4177 		 monitor_remove, (file, client));
4178 }
4179 
4180 gboolean
caja_file_is_launcher(CajaFile * file)4181 caja_file_is_launcher (CajaFile *file)
4182 {
4183 	return file->details->is_launcher;
4184 }
4185 
4186 gboolean
caja_file_is_foreign_link(CajaFile * file)4187 caja_file_is_foreign_link (CajaFile *file)
4188 {
4189 	return file->details->is_foreign_link;
4190 }
4191 
4192 gboolean
caja_file_is_trusted_link(CajaFile * file)4193 caja_file_is_trusted_link (CajaFile *file)
4194 {
4195 	return file->details->is_trusted_link;
4196 }
4197 
4198 gboolean
caja_file_has_activation_uri(CajaFile * file)4199 caja_file_has_activation_uri (CajaFile *file)
4200 {
4201 	return file->details->activation_uri != NULL;
4202 }
4203 
4204 
4205 /* Return the uri associated with the passed-in file, which may not be
4206  * the actual uri if the file is an desktop file or a caja
4207  * xml link file.
4208  */
4209 char *
caja_file_get_activation_uri(CajaFile * file)4210 caja_file_get_activation_uri (CajaFile *file)
4211 {
4212 	g_return_val_if_fail (CAJA_IS_FILE (file), NULL);
4213 
4214 	if (file->details->activation_uri != NULL) {
4215 		return g_strdup (file->details->activation_uri);
4216 	}
4217 
4218 	return caja_file_get_uri (file);
4219 }
4220 
4221 GFile *
caja_file_get_activation_location(CajaFile * file)4222 caja_file_get_activation_location (CajaFile *file)
4223 {
4224 	g_return_val_if_fail (CAJA_IS_FILE (file), NULL);
4225 
4226 	if (file->details->activation_uri != NULL) {
4227 		return g_file_new_for_uri (file->details->activation_uri);
4228 	}
4229 
4230 	return caja_file_get_location (file);
4231 }
4232 
4233 
4234 char *
caja_file_get_drop_target_uri(CajaFile * file)4235 caja_file_get_drop_target_uri (CajaFile *file)
4236 {
4237 	char *uri, *target_uri;
4238 	GFile *location;
4239 
4240 	g_return_val_if_fail (CAJA_IS_FILE (file), NULL);
4241 
4242 	if (CAJA_IS_DESKTOP_ICON_FILE (file)) {
4243 		CajaDesktopLink *link;
4244 
4245 		link = caja_desktop_icon_file_get_link (CAJA_DESKTOP_ICON_FILE (file));
4246 
4247 		if (link != NULL) {
4248 			location = caja_desktop_link_get_activation_location (link);
4249 			g_object_unref (link);
4250 			if (location != NULL) {
4251 				uri = g_file_get_uri (location);
4252 				g_object_unref (location);
4253 				return uri;
4254 			}
4255 		}
4256 	}
4257 
4258 	uri = caja_file_get_uri (file);
4259 
4260 	/* Check for Caja link */
4261 	if (caja_file_is_caja_link (file)) {
4262 		location = caja_file_get_location (file);
4263 		/* FIXME bugzilla.gnome.org 43020: This does sync. I/O and works only locally. */
4264 		if (g_file_is_native (location)) {
4265 			target_uri = caja_link_local_get_link_uri (uri);
4266 			if (target_uri != NULL) {
4267 				g_free (uri);
4268 				uri = target_uri;
4269 			}
4270 		}
4271 		g_object_unref (location);
4272 	}
4273 
4274 	return uri;
4275 }
4276 
4277 static gboolean
is_uri_relative(const char * uri)4278 is_uri_relative (const char *uri)
4279 {
4280 	char *scheme;
4281 	gboolean ret;
4282 
4283 	scheme = g_uri_parse_scheme (uri);
4284 	ret = (scheme == NULL);
4285 	g_free (scheme);
4286 	return ret;
4287 }
4288 
4289 static char *
get_custom_icon_metadata_uri(CajaFile * file)4290 get_custom_icon_metadata_uri (CajaFile *file)
4291 {
4292 	char *custom_icon_uri;
4293 	char *uri;
4294 
4295 	uri = caja_file_get_metadata (file, CAJA_METADATA_KEY_CUSTOM_ICON, NULL);
4296 	if (uri != NULL &&
4297 	    caja_file_is_directory (file) &&
4298 	    is_uri_relative (uri)) {
4299 		char *dir_uri;
4300 
4301 		dir_uri = caja_file_get_uri (file);
4302 		custom_icon_uri = g_build_filename (dir_uri, uri, NULL);
4303 		g_free (dir_uri);
4304 		g_free (uri);
4305 	} else {
4306 		custom_icon_uri = uri;
4307 	}
4308 	return custom_icon_uri;
4309 }
4310 
4311 static GIcon *
get_custom_icon(CajaFile * file)4312 get_custom_icon (CajaFile *file)
4313 {
4314 	char *custom_icon_uri;
4315 	GFile *icon_file;
4316 	GIcon *icon;
4317 
4318 	if (file == NULL) {
4319 		return NULL;
4320 	}
4321 
4322 	icon = NULL;
4323 
4324 	/* Metadata takes precedence */
4325 	custom_icon_uri = get_custom_icon_metadata_uri (file);
4326 
4327 	if (custom_icon_uri) {
4328 		icon_file = g_file_new_for_uri (custom_icon_uri);
4329 		icon = g_file_icon_new (icon_file);
4330 		g_object_unref (icon_file);
4331 		g_free (custom_icon_uri);
4332 	}
4333 
4334 	if (icon == NULL && file->details->got_link_info && file->details->custom_icon != NULL) {
4335 		if (g_path_is_absolute (file->details->custom_icon)) {
4336 			icon_file = g_file_new_for_path (file->details->custom_icon);
4337 			icon = g_file_icon_new (icon_file);
4338 			g_object_unref (icon_file);
4339 		} else {
4340 			icon = g_themed_icon_new (file->details->custom_icon);
4341 		}
4342  	}
4343 
4344 	return icon;
4345 }
4346 
4347 
4348 static guint64 cached_thumbnail_limit;
4349 int cached_thumbnail_size;
4350 static int show_image_thumbs;
4351 
4352 GFilesystemPreviewType
caja_file_get_filesystem_use_preview(CajaFile * file)4353 caja_file_get_filesystem_use_preview (CajaFile *file)
4354 {
4355 	GFilesystemPreviewType use_preview;
4356 	CajaFile *parent;
4357 
4358 	parent = caja_file_get_parent (file);
4359 	if (parent != NULL) {
4360 		use_preview = parent->details->filesystem_use_preview;
4361 		g_object_unref (parent);
4362 	} else {
4363 		use_preview = 0;
4364 	}
4365 
4366 	return use_preview;
4367 }
4368 
4369 gboolean
caja_file_should_show_thumbnail(CajaFile * file)4370 caja_file_should_show_thumbnail (CajaFile *file)
4371 {
4372 	const char *mime_type;
4373 	GFilesystemPreviewType use_preview;
4374 
4375 	use_preview = caja_file_get_filesystem_use_preview (file);
4376 
4377 	mime_type = file->details->mime_type;
4378 	if (mime_type == NULL) {
4379 		mime_type = "application/octet-stream";
4380 	}
4381 
4382 	/* If the thumbnail has already been created, don't care about the size
4383 	 * of the original file.
4384 	 */
4385 	if (caja_thumbnail_is_mimetype_limited_by_size (mime_type) &&
4386 	    file->details->thumbnail_path == NULL &&
4387 	    caja_file_get_size (file) > cached_thumbnail_limit) {
4388 		return FALSE;
4389 	}
4390 
4391 	if (show_image_thumbs == CAJA_SPEED_TRADEOFF_ALWAYS) {
4392 		if (use_preview == G_FILESYSTEM_PREVIEW_TYPE_NEVER) {
4393 			return FALSE;
4394 		} else {
4395 			return TRUE;
4396 		}
4397 	} else if (show_image_thumbs == CAJA_SPEED_TRADEOFF_NEVER) {
4398 		return FALSE;
4399 	} else {
4400 		if (use_preview == G_FILESYSTEM_PREVIEW_TYPE_NEVER) {
4401 			/* file system says to never thumbnail anything */
4402 			return FALSE;
4403 		} else if (use_preview == G_FILESYSTEM_PREVIEW_TYPE_IF_LOCAL) {
4404 			/* file system says we should treat file as if it's local */
4405 			return TRUE;
4406 		} else {
4407 			/* only local files */
4408 			return caja_file_is_local (file);
4409 		}
4410 	}
4411 
4412 	return FALSE;
4413 }
4414 
4415 static void
prepend_icon_name(const char * name,GThemedIcon * icon)4416 prepend_icon_name (const char *name,
4417 		   GThemedIcon *icon)
4418 {
4419 	g_themed_icon_prepend_name(icon, name);
4420 }
4421 
4422 GIcon *
caja_file_get_gicon(CajaFile * file,CajaFileIconFlags flags)4423 caja_file_get_gicon (CajaFile *file,
4424 			 CajaFileIconFlags flags)
4425 {
4426 	const char * const * names;
4427 	GIcon *icon, *mount_icon = NULL, *emblemed_icon;
4428 	gboolean is_folder = FALSE, is_preview = FALSE, is_inode_directory = FALSE;
4429 
4430 	if (file == NULL) {
4431 		return NULL;
4432 	}
4433 
4434 	icon = get_custom_icon (file);
4435 	if (icon != NULL) {
4436 		return icon;
4437 	}
4438 
4439 	if (file->details->icon) {
4440 		icon = NULL;
4441 
4442 		/* fetch the mount icon here, we'll use it later */
4443 		if (flags & CAJA_FILE_ICON_FLAGS_USE_MOUNT_ICON ||
4444 		    flags & CAJA_FILE_ICON_FLAGS_USE_MOUNT_ICON_AS_EMBLEM) {
4445 			GMount *mount;
4446 
4447 			mount = caja_file_get_mount (file);
4448 
4449 			if (mount != NULL) {
4450 				mount_icon = g_mount_get_icon (mount);
4451 				g_object_unref (mount);
4452 			}
4453 		}
4454 
4455 		if (((flags & CAJA_FILE_ICON_FLAGS_EMBEDDING_TEXT) ||
4456 		     (flags & CAJA_FILE_ICON_FLAGS_FOR_DRAG_ACCEPT) ||
4457 		     (flags & CAJA_FILE_ICON_FLAGS_FOR_OPEN_FOLDER) ||
4458 		     (flags & CAJA_FILE_ICON_FLAGS_USE_MOUNT_ICON) ||
4459 		     (flags & CAJA_FILE_ICON_FLAGS_USE_MOUNT_ICON_AS_EMBLEM) ||
4460 		     ((flags & CAJA_FILE_ICON_FLAGS_IGNORE_VISITING) == 0 &&
4461 		      caja_file_has_open_window (file))) &&
4462 		    G_IS_THEMED_ICON (file->details->icon)) {
4463 			GPtrArray *prepend_array;
4464 			int i;
4465 
4466 			names = g_themed_icon_get_names (G_THEMED_ICON (file->details->icon));
4467 			prepend_array = g_ptr_array_new ();
4468 
4469 			for (i = 0; names[i] != NULL; i++) {
4470 				const char *name;
4471 
4472 				name = names[i];
4473 
4474 				if (strcmp (name, "folder") == 0) {
4475 					is_folder = TRUE;
4476 				}
4477 				if (strcmp (name, "inode-directory") == 0) {
4478 					is_inode_directory = TRUE;
4479 				}
4480 				if (strcmp (name, "text-x-generic") == 0 &&
4481 				    (flags & CAJA_FILE_ICON_FLAGS_EMBEDDING_TEXT)) {
4482 					is_preview = TRUE;
4483 				}
4484 			}
4485 
4486 			/* Here, we add icons in reverse order of precedence,
4487 			 * because they are later prepended */
4488 			if (is_preview) {
4489 				g_ptr_array_add (prepend_array, "text-x-preview");
4490 			}
4491 
4492 			/* "folder" should override "inode-directory", not the other way around */
4493 			if (is_inode_directory) {
4494 				g_ptr_array_add (prepend_array, "folder");
4495 			}
4496 			if (is_folder && (flags & CAJA_FILE_ICON_FLAGS_FOR_OPEN_FOLDER)) {
4497 				g_ptr_array_add (prepend_array, "folder-open");
4498 			}
4499 			if (is_folder &&
4500 			    (flags & CAJA_FILE_ICON_FLAGS_IGNORE_VISITING) == 0 &&
4501 			    caja_file_has_open_window (file)) {
4502 				g_ptr_array_add (prepend_array, "folder-visiting");
4503 			}
4504 			if (is_folder &&
4505 			    (flags & CAJA_FILE_ICON_FLAGS_FOR_DRAG_ACCEPT)) {
4506 				g_ptr_array_add (prepend_array, "folder-drag-accept");
4507 			}
4508 
4509 			if (prepend_array->len) {
4510 				/* When constructing GThemed Icon, pointers from the array
4511 				 * are reused, but not the array itself, so the cast is safe */
4512 				icon = g_themed_icon_new_from_names ((char**) names, -1);
4513 				g_ptr_array_foreach (prepend_array, (GFunc) prepend_icon_name, icon);
4514 			}
4515 
4516 			g_ptr_array_free (prepend_array, TRUE);
4517 		}
4518 
4519 		if (icon == NULL) {
4520 			icon = g_object_ref (file->details->icon);
4521 		}
4522 
4523 		if ((flags & CAJA_FILE_ICON_FLAGS_USE_MOUNT_ICON) &&
4524 		    mount_icon != NULL) {
4525 			g_object_unref (icon);
4526 			icon = mount_icon;
4527 		} else if ((flags & CAJA_FILE_ICON_FLAGS_USE_MOUNT_ICON_AS_EMBLEM) &&
4528 			     mount_icon != NULL && !g_icon_equal (mount_icon, icon)) {
4529 			GEmblem *emblem;
4530 
4531 			emblem = g_emblem_new (mount_icon);
4532 			emblemed_icon = g_emblemed_icon_new (icon, emblem);
4533 
4534 			g_object_unref (emblem);
4535 			g_object_unref (icon);
4536 			g_object_unref (mount_icon);
4537 
4538 			icon = emblemed_icon;
4539 		} else if (mount_icon != NULL) {
4540 			g_object_unref (mount_icon);
4541 		}
4542 
4543 		return icon;
4544 	}
4545 
4546 	return g_themed_icon_new ("text-x-generic");
4547 }
4548 
4549 static GIcon *
get_default_file_icon(CajaFileIconFlags flags)4550 get_default_file_icon (CajaFileIconFlags flags)
4551 {
4552 	static GIcon *fallback_icon = NULL;
4553 	static GIcon *fallback_icon_preview = NULL;
4554 	if (fallback_icon == NULL) {
4555 		fallback_icon = g_themed_icon_new ("text-x-generic");
4556 		fallback_icon_preview = g_themed_icon_new ("text-x-preview");
4557 		g_themed_icon_append_name (G_THEMED_ICON (fallback_icon_preview), "text-x-generic");
4558 	}
4559 	if (flags & CAJA_FILE_ICON_FLAGS_EMBEDDING_TEXT) {
4560 		return fallback_icon_preview;
4561 	} else {
4562 		return fallback_icon;
4563 	}
4564 }
4565 
4566 CajaIconInfo *
caja_file_get_icon(CajaFile * file,int size,int scale,CajaFileIconFlags flags)4567 caja_file_get_icon (CajaFile *file,
4568 			int size,
4569 			int scale,
4570 			CajaFileIconFlags flags)
4571 {
4572 	CajaIconInfo *icon;
4573 	GIcon *gicon;
4574 	GdkPixbuf *scaled_pixbuf;
4575 
4576 	if (file == NULL) {
4577 		return NULL;
4578 	}
4579 
4580 	gicon = get_custom_icon (file);
4581 	if (gicon) {
4582 		GdkPixbuf *pixbuf;
4583 
4584 		icon = caja_icon_info_lookup (gicon, size, scale);
4585 		g_object_unref (gicon);
4586 
4587 		pixbuf = caja_icon_info_get_pixbuf (icon);
4588 		if (pixbuf != NULL) {
4589 			if (!file->details->is_launcher && !gdk_pixbuf_get_has_alpha (pixbuf)) {
4590 				caja_ui_frame_image (&pixbuf);
4591 			}
4592 			g_object_unref (icon);
4593 
4594 			icon = caja_icon_info_new_for_pixbuf (pixbuf, scale);
4595 			g_object_unref (pixbuf);
4596 		}
4597 
4598 		return icon;
4599 	}
4600 
4601 	if (flags & CAJA_FILE_ICON_FLAGS_USE_THUMBNAILS &&
4602 	    caja_file_should_show_thumbnail (file)) {
4603 		int modified_size;
4604 
4605 		if (flags & CAJA_FILE_ICON_FLAGS_FORCE_THUMBNAIL_SIZE) {
4606 			modified_size = size * scale;
4607 			} else {
4608 			modified_size = size * scale * cached_thumbnail_size / CAJA_ICON_SIZE_STANDARD;
4609 		}
4610 
4611 		if (file->details->thumbnail) {
4612 			int w, h, s;
4613 			double thumb_scale;
4614 			GdkPixbuf *raw_pixbuf;
4615 
4616 			raw_pixbuf = g_object_ref (file->details->thumbnail);
4617 
4618 			w = gdk_pixbuf_get_width (raw_pixbuf);
4619 			h = gdk_pixbuf_get_height (raw_pixbuf);
4620 
4621 			s = MAX (w, h);
4622 			/* Don't scale up small thumbnails in the standard view */
4623 			if (s <= cached_thumbnail_size) {
4624 				thumb_scale = (double)size / CAJA_ICON_SIZE_STANDARD;
4625 			}
4626 			else {
4627 				thumb_scale = (double)modified_size / s;
4628 			}
4629 			/* Make sure that icons don't get smaller than CAJA_ICON_SIZE_SMALLEST */
4630 			if (s*thumb_scale <= CAJA_ICON_SIZE_SMALLEST) {
4631 				thumb_scale = (double) CAJA_ICON_SIZE_SMALLEST / s;
4632 			}
4633 
4634 			scaled_pixbuf = gdk_pixbuf_scale_simple (raw_pixbuf,
4635 								 MAX (w * thumb_scale, 1),
4636 								 MAX (h * thumb_scale, 1),
4637 								 GDK_INTERP_BILINEAR);
4638 
4639 			/* Render frames only for thumbnails of non-image files
4640 			   and for images with no alpha channel. */
4641 			gboolean is_image = file->details->mime_type &&
4642 				(strncmp (file->details->mime_type, "image/", 6) == 0);
4643 			if (!is_image || !gdk_pixbuf_get_has_alpha (raw_pixbuf)) {
4644 				caja_ui_frame_image (&scaled_pixbuf);
4645 			}
4646 
4647 			g_object_unref (raw_pixbuf);
4648 
4649 			/* Don't scale up if more than 25%, then read the original
4650 			   image instead. We don't want to compare to exactly 100%,
4651 			   since the zoom level 150% gives thumbnails at 144, which is
4652 			   ok to scale up from 128. */
4653 			if (modified_size > 128 * 1.25 * scale &&
4654 			    !file->details->thumbnail_wants_original &&
4655 			    caja_can_thumbnail_internally (file)) {
4656 				/* Invalidate if we resize upward */
4657 				file->details->thumbnail_wants_original = TRUE;
4658 				caja_file_invalidate_attributes (file, CAJA_FILE_ATTRIBUTE_THUMBNAIL);
4659 			}
4660 
4661 			icon = caja_icon_info_new_for_pixbuf (scaled_pixbuf, scale);
4662 			g_object_unref (scaled_pixbuf);
4663 			return icon;
4664 		} else if (file->details->thumbnail_path == NULL &&
4665 			   file->details->can_read &&
4666 			   !file->details->is_thumbnailing &&
4667 			   !file->details->thumbnailing_failed) {
4668 			if (caja_can_thumbnail (file)) {
4669 				caja_create_thumbnail (file);
4670 			}
4671 		}
4672 	}
4673 
4674 	if (file->details->is_thumbnailing &&
4675 	    flags & CAJA_FILE_ICON_FLAGS_USE_THUMBNAILS)
4676 		gicon = g_themed_icon_new (ICON_NAME_THUMBNAIL_LOADING);
4677 	else
4678 		gicon = caja_file_get_gicon (file, flags);
4679 
4680 	if (gicon) {
4681 		icon = caja_icon_info_lookup (gicon, size, scale);
4682 		g_object_unref (gicon);
4683 		return icon;
4684 	} else {
4685 		return caja_icon_info_lookup (get_default_file_icon (flags), size, scale);
4686 	}
4687 }
4688 
4689 cairo_surface_t *
caja_file_get_icon_surface(CajaFile * file,int size,gboolean force_size,int scale,CajaFileIconFlags flags)4690 caja_file_get_icon_surface (CajaFile *file,
4691                             int size,
4692                             gboolean force_size,
4693                             int scale,
4694                             CajaFileIconFlags flags)
4695 {
4696 	CajaIconInfo *info;
4697 	cairo_surface_t *surface;
4698 
4699 	info = caja_file_get_icon (file, size, scale, flags);
4700 	if (force_size) {
4701 		surface =  caja_icon_info_get_surface_at_size (info, size);
4702 	} else {
4703 		surface = caja_icon_info_get_surface (info);
4704 	}
4705 	g_object_unref (info);
4706 
4707 	return surface;
4708 }
4709 
4710 char *
caja_file_get_custom_icon(CajaFile * file)4711 caja_file_get_custom_icon (CajaFile *file)
4712 {
4713 	char *custom_icon;
4714 
4715 	if (file == NULL) {
4716 		return NULL;
4717 	}
4718 
4719 	/* Metadata takes precedence */
4720 	custom_icon = get_custom_icon_metadata_uri (file);
4721 
4722 	if (custom_icon == NULL && file->details->got_link_info) {
4723 		custom_icon = g_strdup (file->details->custom_icon);
4724  	}
4725 
4726 	return custom_icon;
4727 }
4728 
4729 
4730 gboolean
caja_file_get_date(CajaFile * file,CajaDateType date_type,time_t * date)4731 caja_file_get_date (CajaFile *file,
4732 			CajaDateType date_type,
4733 			time_t *date)
4734 {
4735 	if (date != NULL) {
4736 		*date = 0;
4737 	}
4738 
4739 	g_return_val_if_fail (date_type == CAJA_DATE_TYPE_CHANGED
4740 			      || date_type == CAJA_DATE_TYPE_ACCESSED
4741 			      || date_type == CAJA_DATE_TYPE_MODIFIED
4742 	                      || date_type == CAJA_DATE_TYPE_CREATED
4743 			      || date_type == CAJA_DATE_TYPE_TRASHED
4744 			      || date_type == CAJA_DATE_TYPE_PERMISSIONS_CHANGED, FALSE);
4745 
4746 	if (file == NULL) {
4747 		return FALSE;
4748 	}
4749 
4750 	g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
4751 
4752 	return EEL_CALL_METHOD_WITH_RETURN_VALUE
4753 		(CAJA_FILE_CLASS, file,
4754 		 get_date, (file, date_type, date));
4755 }
4756 
4757 static char *
caja_file_get_where_string(CajaFile * file)4758 caja_file_get_where_string (CajaFile *file)
4759 {
4760 	if (file == NULL) {
4761 		return NULL;
4762 	}
4763 
4764 	g_return_val_if_fail (CAJA_IS_FILE (file), NULL);
4765 
4766 	return EEL_CALL_METHOD_WITH_RETURN_VALUE
4767 		(CAJA_FILE_CLASS, file,
4768 		 get_where_string, (file));
4769 }
4770 
4771 static const char *TODAY_TIME_FORMATS [] = {
4772 	/* Today, use special word.
4773 	 * strftime patterns preceeded with the widest
4774 	 * possible resulting string for that pattern.
4775 	 *
4776 	 * Note to localizers: You can look at man strftime
4777 	 * for details on the format, but you should only use
4778 	 * the specifiers from the C standard, not extensions.
4779 	 * These include "%" followed by one of
4780 	 * "aAbBcdHIjmMpSUwWxXyYZ". There are two extensions
4781 	 * in the Caja version of strftime that can be
4782 	 * used (and match GNU extensions). Putting a "-"
4783 	 * between the "%" and any numeric directive will turn
4784 	 * off zero padding, and putting a "_" there will use
4785 	 * space padding instead of zero padding.
4786 	 */
4787 	N_("today at 00:00:00 PM"),
4788 	N_("today at %-I:%M:%S %p"),
4789 
4790 	N_("today at 00:00 PM"),
4791 	N_("today at %-I:%M %p"),
4792 
4793 	N_("today, 00:00 PM"),
4794 	N_("today, %-I:%M %p"),
4795 
4796 	N_("today"),
4797 	N_("today"),
4798 
4799 	NULL
4800 };
4801 
4802 static const char *YESTERDAY_TIME_FORMATS [] = {
4803 	/* Yesterday, use special word.
4804 	 * Note to localizers: Same issues as "today" string.
4805 	 */
4806 	N_("yesterday at 00:00:00 PM"),
4807 	N_("yesterday at %-I:%M:%S %p"),
4808 
4809 	N_("yesterday at 00:00 PM"),
4810 	N_("yesterday at %-I:%M %p"),
4811 
4812 	N_("yesterday, 00:00 PM"),
4813 	N_("yesterday, %-I:%M %p"),
4814 
4815 	N_("yesterday"),
4816 	N_("yesterday"),
4817 
4818 	NULL
4819 };
4820 
4821 static const char *CURRENT_WEEK_TIME_FORMATS [] = {
4822 	/* Current week, include day of week.
4823 	 * Note to localizers: Same issues as "today" string.
4824 	 * The width measurement templates correspond to
4825 	 * the day/month name with the most letters.
4826 	 */
4827 	N_("Wednesday, September 00 0000 at 00:00:00 PM"),
4828 	N_("%A, %B %-d %Y at %-I:%M:%S %p"),
4829 
4830 	N_("Mon, Oct 00 0000 at 00:00:00 PM"),
4831 	N_("%a, %b %-d %Y at %-I:%M:%S %p"),
4832 
4833 	N_("Mon, Oct 00 0000 at 00:00 PM"),
4834 	N_("%a, %b %-d %Y at %-I:%M %p"),
4835 
4836 	N_("Oct 00 0000 at 00:00 PM"),
4837 	N_("%b %-d %Y at %-I:%M %p"),
4838 
4839 	N_("Oct 00 0000, 00:00 PM"),
4840 	N_("%b %-d %Y, %-I:%M %p"),
4841 
4842 	N_("00/00/00, 00:00 PM"),
4843 	N_("%m/%-d/%y, %-I:%M %p"),
4844 
4845 	N_("00/00/00"),
4846 	N_("%m/%d/%y"),
4847 
4848 	NULL
4849 };
4850 
4851 static char *
caja_file_fit_date_as_string(CajaFile * file,CajaDateType date_type,int width,CajaWidthMeasureCallback measure_callback,CajaTruncateCallback truncate_callback,void * measure_context)4852 caja_file_fit_date_as_string (CajaFile *file,
4853 				  CajaDateType date_type,
4854 				  int width,
4855 				  CajaWidthMeasureCallback measure_callback,
4856 				  CajaTruncateCallback truncate_callback,
4857 				  void *measure_context)
4858 {
4859 	time_t file_time_raw;
4860 	const char **formats;
4861 	const char *format;
4862 	char *date_string;
4863 	gchar *result = NULL;
4864 	int i;
4865 	GDateTime *date_time, *today;
4866 	GTimeSpan file_date_age;
4867 
4868 	if (!caja_file_get_date (file, date_type, &file_time_raw)) {
4869 		return NULL;
4870 	}
4871 
4872 	date_time = g_date_time_new_from_unix_local (file_time_raw);
4873 
4874 	if (date_format_pref == CAJA_DATE_FORMAT_LOCALE) {
4875 		result = g_date_time_format (date_time, "%c");
4876 		goto out;
4877 	} else if (date_format_pref == CAJA_DATE_FORMAT_ISO) {
4878 		result = g_date_time_format (date_time, "%Y-%m-%d %H:%M:%S");
4879 		goto out;
4880 	}
4881 
4882 	today = g_date_time_new_now_local ();
4883 	file_date_age = g_date_time_difference (today, date_time);
4884 	g_date_time_unref (today);
4885 
4886 	/* Format varies depending on how old the date is. This minimizes
4887 	 * the length (and thus clutter & complication) of typical dates
4888 	 * while providing sufficient detail for recent dates to make
4889 	 * them maximally understandable at a glance. Keep all format
4890 	 * strings separate rather than combining bits & pieces for
4891 	 * internationalization's sake.
4892 	 */
4893 
4894 	if (file_date_age < G_TIME_SPAN_DAY) {
4895 		formats = TODAY_TIME_FORMATS;
4896 	} else if (file_date_age < 2 * G_TIME_SPAN_DAY) {
4897 		formats = YESTERDAY_TIME_FORMATS;
4898 	} else {
4899 		formats = CURRENT_WEEK_TIME_FORMATS;
4900 	}
4901 
4902 	/* Find the date format that just fits the required width. Instead of measuring
4903 	 * the resulting string width directly, measure the width of a template that represents
4904 	 * the widest possible version of a date in a given format. This is done by using M, m
4905 	 * and 0 for the variable letters/digits respectively.
4906 	 */
4907 	format = NULL;
4908 
4909 	for (i = 0; ; i += 2) {
4910 		const char *width_template;
4911 
4912 		width_template = (formats [i] ? _(formats [i]) : NULL);
4913 		if (width_template == NULL) {
4914 			/* no more formats left */
4915 			g_assert (format != NULL);
4916 
4917 			/* Can't fit even the shortest format -- return an ellipsized form in the
4918 			 * shortest format
4919 			 */
4920 
4921 			date_string = g_date_time_format (date_time, format);
4922 
4923 			if (truncate_callback == NULL) {
4924 				result = date_string;
4925 				break;
4926 			}
4927 
4928 			result = (* truncate_callback) (date_string, width, measure_context);
4929 			g_free (date_string);
4930 			break;
4931 		}
4932 
4933 		format = _(formats [i + 1]);
4934 
4935 		if (measure_callback == NULL) {
4936 			/* don't care about fitting the width */
4937 			break;
4938 		}
4939 
4940 		if ((* measure_callback) (width_template, measure_context) <= width) {
4941 			/* The template fits, this is the format we can fit. */
4942 			break;
4943 		}
4944 	}
4945 
4946 	if (result == NULL) {
4947 		result = g_date_time_format (date_time, format);
4948 	}
4949 
4950 out:
4951 	g_date_time_unref (date_time);
4952 	return result;
4953 }
4954 
4955 /**
4956  * caja_file_fit_modified_date_as_string:
4957  *
4958  * Get a user-displayable string representing a file modification date,
4959  * truncated to @width using the measuring and truncating callbacks.
4960  * @file: CajaFile representing the file in question.
4961  * @width: The desired resulting string width.
4962  * @measure_callback: The callback used to measure the string width.
4963  * @truncate_callback: The callback used to truncate the string to a desired width.
4964  * @measure_context: Data neede when measuring and truncating.
4965  *
4966  * Returns: Newly allocated string ready to display to the user.
4967  *
4968  **/
4969 char *
caja_file_fit_modified_date_as_string(CajaFile * file,int width,CajaWidthMeasureCallback measure_callback,CajaTruncateCallback truncate_callback,void * measure_context)4970 caja_file_fit_modified_date_as_string (CajaFile *file,
4971 					   int width,
4972 					   CajaWidthMeasureCallback measure_callback,
4973 					   CajaTruncateCallback truncate_callback,
4974 					   void *measure_context)
4975 {
4976 	return caja_file_fit_date_as_string (file, CAJA_DATE_TYPE_MODIFIED,
4977 		width, measure_callback, truncate_callback, measure_context);
4978 }
4979 
4980 static char *
caja_file_get_trash_original_file_parent_as_string(CajaFile * file)4981 caja_file_get_trash_original_file_parent_as_string (CajaFile *file)
4982 {
4983 	if (file->details->trash_orig_path != NULL) {
4984 		CajaFile *orig_file, *parent;
4985 		GFile *location;
4986 		char *filename;
4987 
4988 		orig_file = caja_file_get_trash_original_file (file);
4989 		parent = caja_file_get_parent (orig_file);
4990 		location = caja_file_get_location (parent);
4991 
4992 		filename = g_file_get_parse_name (location);
4993 
4994 		g_object_unref (location);
4995 		caja_file_unref (parent);
4996 		caja_file_unref (orig_file);
4997 
4998 		return filename;
4999 	}
5000 
5001 	return NULL;
5002 }
5003 
5004 /**
5005  * caja_file_get_date_as_string:
5006  *
5007  * Get a user-displayable string representing a file modification date.
5008  * The caller is responsible for g_free-ing this string.
5009  * @file: CajaFile representing the file in question.
5010  *
5011  * Returns: Newly allocated string ready to display to the user.
5012  *
5013  **/
5014 static char *
caja_file_get_date_as_string(CajaFile * file,CajaDateType date_type)5015 caja_file_get_date_as_string (CajaFile *file, CajaDateType date_type)
5016 {
5017 	return caja_file_fit_date_as_string (file, date_type,
5018 		0, NULL, NULL, NULL);
5019 }
5020 
5021 static CajaSpeedTradeoffValue show_directory_item_count;
5022 static CajaSpeedTradeoffValue show_text_in_icons;
5023 
5024 static void
show_text_in_icons_changed_callback(gpointer callback_data)5025 show_text_in_icons_changed_callback (gpointer callback_data)
5026 {
5027 	show_text_in_icons = g_settings_get_enum (caja_preferences, CAJA_PREFERENCES_SHOW_TEXT_IN_ICONS);
5028 }
5029 
5030 static void
show_directory_item_count_changed_callback(gpointer callback_data)5031 show_directory_item_count_changed_callback (gpointer callback_data)
5032 {
5033 	show_directory_item_count = g_settings_get_enum (caja_preferences, CAJA_PREFERENCES_SHOW_DIRECTORY_ITEM_COUNTS);
5034 }
5035 
5036 static gboolean
get_speed_tradeoff_preference_for_file(CajaFile * file,CajaSpeedTradeoffValue value)5037 get_speed_tradeoff_preference_for_file (CajaFile *file, CajaSpeedTradeoffValue value)
5038 {
5039 	GFilesystemPreviewType use_preview;
5040 
5041 	g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
5042 
5043 	use_preview = caja_file_get_filesystem_use_preview (file);
5044 
5045 	if (value == CAJA_SPEED_TRADEOFF_ALWAYS) {
5046 		if (use_preview == G_FILESYSTEM_PREVIEW_TYPE_NEVER) {
5047 			return FALSE;
5048 		} else {
5049 			return TRUE;
5050 		}
5051 	}
5052 
5053 	if (value == CAJA_SPEED_TRADEOFF_NEVER) {
5054 		return FALSE;
5055 	}
5056 
5057 	g_assert (value == CAJA_SPEED_TRADEOFF_LOCAL_ONLY);
5058 
5059 	if (use_preview == G_FILESYSTEM_PREVIEW_TYPE_NEVER) {
5060 		/* file system says to never preview anything */
5061 		return FALSE;
5062 	} else if (use_preview == G_FILESYSTEM_PREVIEW_TYPE_IF_LOCAL) {
5063 		/* file system says we should treat file as if it's local */
5064 		return TRUE;
5065 	} else {
5066 		/* only local files */
5067 		return caja_file_is_local (file);
5068 	}
5069 }
5070 
5071 gboolean
caja_file_should_show_directory_item_count(CajaFile * file)5072 caja_file_should_show_directory_item_count (CajaFile *file)
5073 {
5074 	static gboolean show_directory_item_count_callback_added = FALSE;
5075 
5076 	g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
5077 
5078 	if (file->details->mime_type &&
5079 	    strcmp (file->details->mime_type, "x-directory/smb-share") == 0) {
5080 		return FALSE;
5081 	}
5082 
5083 	/* Add the callback once for the life of our process */
5084 	if (!show_directory_item_count_callback_added) {
5085 		g_signal_connect_swapped (caja_preferences,
5086 								  "changed::" CAJA_PREFERENCES_SHOW_DIRECTORY_ITEM_COUNTS,
5087 								  G_CALLBACK(show_directory_item_count_changed_callback),
5088 								  NULL);
5089 		show_directory_item_count_callback_added = TRUE;
5090 
5091 		/* Peek for the first time */
5092 		show_directory_item_count_changed_callback (NULL);
5093 	}
5094 
5095 	return get_speed_tradeoff_preference_for_file (file, show_directory_item_count);
5096 }
5097 
5098 gboolean
caja_file_should_show_type(CajaFile * file)5099 caja_file_should_show_type (CajaFile *file)
5100 {
5101 	char *uri;
5102 	gboolean ret;
5103 
5104 	g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
5105 
5106 	uri = caja_file_get_uri (file);
5107 	ret = ((strcmp (uri, "computer:///") != 0) &&
5108 	       (strcmp (uri, "network:///") != 0) &&
5109 	       (strcmp (uri, "smb:///") != 0));
5110 	g_free (uri);
5111 
5112 	return ret;
5113 }
5114 
5115 gboolean
caja_file_should_get_top_left_text(CajaFile * file)5116 caja_file_should_get_top_left_text (CajaFile *file)
5117 {
5118 	static gboolean show_text_in_icons_callback_added = FALSE;
5119 
5120 	g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
5121 
5122 	/* Add the callback once for the life of our process */
5123 	if (!show_text_in_icons_callback_added) {
5124 		g_signal_connect_swapped (caja_preferences,
5125 								  "changed::" CAJA_PREFERENCES_SHOW_TEXT_IN_ICONS,
5126 								  G_CALLBACK (show_text_in_icons_changed_callback),
5127 								  NULL);
5128 		show_text_in_icons_callback_added = TRUE;
5129 
5130 		/* Peek for the first time */
5131 		show_text_in_icons_changed_callback (NULL);
5132 	}
5133 
5134 	if (show_text_in_icons == CAJA_SPEED_TRADEOFF_ALWAYS) {
5135 		return TRUE;
5136 	}
5137 
5138 	if (show_text_in_icons == CAJA_SPEED_TRADEOFF_NEVER) {
5139 		return FALSE;
5140 	}
5141 
5142 	return get_speed_tradeoff_preference_for_file (file, show_text_in_icons);
5143 }
5144 
5145 /**
5146  * caja_file_get_directory_item_count
5147  *
5148  * Get the number of items in a directory.
5149  * @file: CajaFile representing a directory.
5150  * @count: Place to put count.
5151  * @count_unreadable: Set to TRUE (if non-NULL) if permissions prevent
5152  * the item count from being read on this directory. Otherwise set to FALSE.
5153  *
5154  * Returns: TRUE if count is available.
5155  *
5156  **/
5157 gboolean
caja_file_get_directory_item_count(CajaFile * file,guint * count,gboolean * count_unreadable)5158 caja_file_get_directory_item_count (CajaFile *file,
5159 					guint *count,
5160 					gboolean *count_unreadable)
5161 {
5162 	if (count != NULL) {
5163 		*count = 0;
5164 	}
5165 	if (count_unreadable != NULL) {
5166 		*count_unreadable = FALSE;
5167 	}
5168 
5169 	g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
5170 
5171 	if (!caja_file_is_directory (file)) {
5172 		return FALSE;
5173 	}
5174 
5175 	if (!caja_file_should_show_directory_item_count (file)) {
5176 		return FALSE;
5177 	}
5178 
5179 	return EEL_CALL_METHOD_WITH_RETURN_VALUE
5180 		(CAJA_FILE_CLASS, file,
5181 		 get_item_count, (file, count, count_unreadable));
5182 }
5183 
5184 /**
5185  * caja_file_get_deep_counts
5186  *
5187  * Get the statistics about items inside a directory.
5188  * @file: CajaFile representing a directory or file.
5189  * @directory_count: Place to put count of directories inside.
5190  * @files_count: Place to put count of files inside.
5191  * @unreadable_directory_count: Number of directories encountered
5192  * that were unreadable.
5193  * @total_size: Total size of all files and directories visited.
5194  * @total_size_on_disk: Total size on disk of all files and directories visited.
5195  * @force: Whether the deep counts should even be collected if
5196  * caja_file_should_show_directory_item_count returns FALSE
5197  * for this file.
5198  *
5199  * Returns: Status to indicate whether sizes are available.
5200  *
5201  **/
5202 CajaRequestStatus
caja_file_get_deep_counts(CajaFile * file,guint * directory_count,guint * file_count,guint * unreadable_directory_count,goffset * total_size,goffset * total_size_on_disk,gboolean force)5203 caja_file_get_deep_counts (CajaFile *file,
5204 			       guint *directory_count,
5205 			       guint *file_count,
5206 			       guint *unreadable_directory_count,
5207 			       goffset *total_size,
5208 			       goffset *total_size_on_disk,
5209 			       gboolean force)
5210 {
5211 	if (directory_count != NULL) {
5212 		*directory_count = 0;
5213 	}
5214 	if (file_count != NULL) {
5215 		*file_count = 0;
5216 	}
5217 	if (unreadable_directory_count != NULL) {
5218 		*unreadable_directory_count = 0;
5219 	}
5220 	if (total_size != NULL) {
5221 		*total_size = 0;
5222 	}
5223 	if (total_size_on_disk != NULL) {
5224 		*total_size_on_disk = 0;
5225 	}
5226 
5227 	g_return_val_if_fail (CAJA_IS_FILE (file), CAJA_REQUEST_DONE);
5228 
5229 	if (!force && !caja_file_should_show_directory_item_count (file)) {
5230 		/* Set field so an existing value isn't treated as up-to-date
5231 		 * when preference changes later.
5232 		 */
5233 		file->details->deep_counts_status = CAJA_REQUEST_NOT_STARTED;
5234 		return file->details->deep_counts_status;
5235 	}
5236 
5237 	return EEL_CALL_METHOD_WITH_RETURN_VALUE
5238 		(CAJA_FILE_CLASS, file,
5239 		 get_deep_counts, (file,
5240 				   directory_count,
5241 				   file_count,
5242 				   unreadable_directory_count,
5243 				   total_size,
5244 				   total_size_on_disk));
5245 }
5246 
5247 void
caja_file_recompute_deep_counts(CajaFile * file)5248 caja_file_recompute_deep_counts (CajaFile *file)
5249 {
5250 	if (file->details->deep_counts_status != CAJA_REQUEST_IN_PROGRESS) {
5251 		file->details->deep_counts_status = CAJA_REQUEST_NOT_STARTED;
5252 		if (file->details->directory != NULL) {
5253 			caja_directory_add_file_to_work_queue (file->details->directory, file);
5254 			caja_directory_async_state_changed (file->details->directory);
5255 		}
5256 	}
5257 }
5258 
5259 gboolean
caja_file_can_get_size(CajaFile * file)5260 caja_file_can_get_size (CajaFile *file)
5261 {
5262 	return file->details->size == -1;
5263 }
5264 
5265 
5266 /**
5267  * caja_file_get_size
5268  *
5269  * Get the file size.
5270  * @file: CajaFile representing the file in question.
5271  *
5272  * Returns: Size in bytes.
5273  *
5274  **/
5275 goffset
caja_file_get_size(CajaFile * file)5276 caja_file_get_size (CajaFile *file)
5277 {
5278 	/* Before we have info on the file, we don't know the size. */
5279 	if (file->details->size == -1)
5280 		return 0;
5281 	return file->details->size;
5282 }
5283 
5284 /**
5285  * caja_file_get_size_on_disk
5286  *
5287  * Get the file size on disk (how many bytes is using on the filesystem).
5288  * e.g.: usually files with 1 byte will use a whole inode so it will return the
5289  * size of 1 inode. If the file is sparse the size on disk will be equal or less
5290  * than the size of the file.
5291  * @file: CajaFile representing the file in question.
5292  *
5293  * Returns: Size in bytes.
5294  *
5295  **/
5296 goffset
caja_file_get_size_on_disk(CajaFile * file)5297 caja_file_get_size_on_disk (CajaFile *file)
5298 {
5299 	/* Before we have info on the file, we don't know the size. */
5300 	if (file->details->size_on_disk == -1)
5301 		return 0;
5302 	return file->details->size_on_disk;
5303 }
5304 
5305 time_t
caja_file_get_mtime(CajaFile * file)5306 caja_file_get_mtime (CajaFile *file)
5307 {
5308 	return file->details->mtime;
5309 }
5310 
5311 
5312 static void
set_attributes_get_info_callback(GObject * source_object,GAsyncResult * res,gpointer callback_data)5313 set_attributes_get_info_callback (GObject *source_object,
5314 				  GAsyncResult *res,
5315 				  gpointer callback_data)
5316 {
5317 	CajaFileOperation *op;
5318 	GFileInfo *new_info;
5319 	GError *error;
5320 
5321 	op = callback_data;
5322 
5323 	error = NULL;
5324 	new_info = g_file_query_info_finish (G_FILE (source_object), res, &error);
5325 	if (new_info != NULL) {
5326 		if (caja_file_update_info (op->file, new_info)) {
5327 			caja_file_changed (op->file);
5328 		}
5329 		g_object_unref (new_info);
5330 	}
5331 	caja_file_operation_complete (op, NULL, error);
5332 	if (error) {
5333 		g_error_free (error);
5334 	}
5335 }
5336 
5337 
5338 static void
set_attributes_callback(GObject * source_object,GAsyncResult * result,gpointer callback_data)5339 set_attributes_callback (GObject *source_object,
5340 			 GAsyncResult *result,
5341 			 gpointer callback_data)
5342 {
5343 	CajaFileOperation *op;
5344 	GError *error;
5345 	gboolean res;
5346 
5347 	op = callback_data;
5348 
5349 	error = NULL;
5350 	res = g_file_set_attributes_finish (G_FILE (source_object),
5351 					    result,
5352 					    NULL,
5353 					    &error);
5354 
5355 	if (res) {
5356 		g_file_query_info_async (G_FILE (source_object),
5357 					 CAJA_FILE_DEFAULT_ATTRIBUTES,
5358 					 0,
5359 					 G_PRIORITY_DEFAULT,
5360 					 op->cancellable,
5361 					 set_attributes_get_info_callback, op);
5362 	} else {
5363 		caja_file_operation_complete (op, NULL, error);
5364 		g_error_free (error);
5365 	}
5366 }
5367 
5368 void
caja_file_set_attributes(CajaFile * file,GFileInfo * attributes,CajaFileOperationCallback callback,gpointer callback_data)5369 caja_file_set_attributes (CajaFile *file,
5370 			      GFileInfo *attributes,
5371 			      CajaFileOperationCallback callback,
5372 			      gpointer callback_data)
5373 {
5374 	CajaFileOperation *op;
5375 	GFile *location;
5376 
5377 	op = caja_file_operation_new (file, callback, callback_data);
5378 
5379 	location = caja_file_get_location (file);
5380 	g_file_set_attributes_async (location,
5381 				     attributes,
5382 				     0,
5383 				     G_PRIORITY_DEFAULT,
5384 				     op->cancellable,
5385 				     set_attributes_callback,
5386 				     op);
5387 	g_object_unref (location);
5388 }
5389 
5390 
5391 /**
5392  * caja_file_can_get_permissions:
5393  *
5394  * Check whether the permissions for a file are determinable.
5395  * This might not be the case for files on non-UNIX file systems.
5396  *
5397  * @file: The file in question.
5398  *
5399  * Return value: TRUE if the permissions are valid.
5400  */
5401 gboolean
caja_file_can_get_permissions(CajaFile * file)5402 caja_file_can_get_permissions (CajaFile *file)
5403 {
5404 	return file->details->has_permissions;
5405 }
5406 
5407 /**
5408  * caja_file_can_set_permissions:
5409  *
5410  * Check whether the current user is allowed to change
5411  * the permissions of a file.
5412  *
5413  * @file: The file in question.
5414  *
5415  * Return value: TRUE if the current user can change the
5416  * permissions of @file, FALSE otherwise. It's always possible
5417  * that when you actually try to do it, you will fail.
5418  */
5419 gboolean
caja_file_can_set_permissions(CajaFile * file)5420 caja_file_can_set_permissions (CajaFile *file)
5421 {
5422 	uid_t user_id;
5423 
5424 	if (file->details->uid != -1 &&
5425 	    caja_file_is_local (file)) {
5426 		/* Check the user. */
5427 		user_id = geteuid();
5428 
5429 		/* Owner is allowed to set permissions. */
5430 		if (user_id == (uid_t) file->details->uid) {
5431 			return TRUE;
5432 		}
5433 
5434 		/* Root is also allowed to set permissions. */
5435 		if (user_id == 0) {
5436 			return TRUE;
5437 		}
5438 
5439 		/* Nobody else is allowed. */
5440 		return FALSE;
5441 	}
5442 
5443 	/* pretend to have full chmod rights when no info is available, relevant when
5444 	 * the FS can't provide ownership info, for instance for FTP */
5445 	return TRUE;
5446 }
5447 
5448 guint
caja_file_get_permissions(CajaFile * file)5449 caja_file_get_permissions (CajaFile *file)
5450 {
5451 	g_return_val_if_fail (caja_file_can_get_permissions (file), 0);
5452 
5453 	return file->details->permissions;
5454 }
5455 
5456 /**
5457  * caja_file_set_permissions:
5458  *
5459  * Change a file's permissions. This should only be called if
5460  * caja_file_can_set_permissions returned TRUE.
5461  *
5462  * @file: CajaFile representing the file in question.
5463  * @new_permissions: New permissions value. This is the whole
5464  * set of permissions, not a delta.
5465  **/
5466 void
caja_file_set_permissions(CajaFile * file,guint32 new_permissions,CajaFileOperationCallback callback,gpointer callback_data)5467 caja_file_set_permissions (CajaFile *file,
5468 			       guint32 new_permissions,
5469 			       CajaFileOperationCallback callback,
5470 			       gpointer callback_data)
5471 {
5472 	GFileInfo *info;
5473 
5474 	if (!caja_file_can_set_permissions (file)) {
5475 		GError *error;
5476 
5477 		/* Claim that something changed even if the permission change failed.
5478 		 * This makes it easier for some clients who see the "reverting"
5479 		 * to the old permissions as "changing back".
5480 		 */
5481 		caja_file_changed (file);
5482 		error = g_error_new (G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
5483 				     _("Not allowed to set permissions"));
5484 		(* callback) (file, NULL, error, callback_data);
5485 		g_error_free (error);
5486 		return;
5487 	}
5488 
5489 	/* Test the permissions-haven't-changed case explicitly
5490 	 * because we don't want to send the file-changed signal if
5491 	 * nothing changed.
5492 	 */
5493 	if (new_permissions == file->details->permissions) {
5494 		(* callback) (file, NULL, NULL, callback_data);
5495 		return;
5496 	}
5497 
5498 	// Start UNDO-REDO
5499 	if (!caja_undostack_manager_is_undo_redo(caja_undostack_manager_instance())) {
5500 		CajaUndoStackActionData* undo_redo_data = caja_undostack_manager_data_new (CAJA_UNDOSTACK_SETPERMISSIONS, 1);
5501 		caja_undostack_manager_data_set_file_permissions(undo_redo_data, caja_file_get_uri(file), file->details->permissions, new_permissions);
5502 		caja_undostack_manager_add_action (caja_undostack_manager_instance(),
5503 										   undo_redo_data);
5504 	}
5505 	// End UNDO-REDO
5506 
5507 	info = g_file_info_new ();
5508 	g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE, new_permissions);
5509 	caja_file_set_attributes (file, info, callback, callback_data);
5510 	g_object_unref (info);
5511 }
5512 
5513 /**
5514  * caja_file_can_get_selinux_context:
5515  *
5516  * Check whether the selinux context for a file are determinable.
5517  * This might not be the case for files on non-UNIX file systems,
5518  * files without a context or systems that don't support selinux.
5519  *
5520  * @file: The file in question.
5521  *
5522  * Return value: TRUE if the permissions are valid.
5523  */
5524 gboolean
caja_file_can_get_selinux_context(CajaFile * file)5525 caja_file_can_get_selinux_context (CajaFile *file)
5526 {
5527 	return file->details->selinux_context != NULL;
5528 }
5529 
5530 
5531 /**
5532  * caja_file_get_selinux_context:
5533  *
5534  * Get a user-displayable string representing a file's selinux
5535  * context
5536  * @file: CajaFile representing the file in question.
5537  *
5538  * Returns: Newly allocated string ready to display to the user.
5539  *
5540  **/
5541 char *
caja_file_get_selinux_context(CajaFile * file)5542 caja_file_get_selinux_context (CajaFile *file)
5543 {
5544 	char *translated;
5545 	char *raw;
5546 
5547 	g_return_val_if_fail (CAJA_IS_FILE (file), NULL);
5548 
5549 	if (!caja_file_can_get_selinux_context (file)) {
5550 		return NULL;
5551 	}
5552 
5553 	raw = file->details->selinux_context;
5554 
5555 #ifdef HAVE_SELINUX
5556 	if (selinux_raw_to_trans_context (raw, &translated) == 0) {
5557 		char *tmp;
5558 		tmp = g_strdup (translated);
5559 		freecon (translated);
5560 		translated = tmp;
5561 	}
5562 	else
5563 #endif
5564 	{
5565 		translated = g_strdup (raw);
5566 	}
5567 
5568 	return translated;
5569 }
5570 
5571 static char *
get_real_name(const char * name,const char * gecos)5572 get_real_name (const char *name, const char *gecos)
5573 {
5574 	char *locale_string, *part_before_comma, *capitalized_login_name, *real_name;
5575 
5576 	if (gecos == NULL) {
5577 		return NULL;
5578 	}
5579 
5580 	locale_string = eel_str_strip_substring_and_after (gecos, ",");
5581 	if (!g_utf8_validate (locale_string, -1, NULL)) {
5582 		part_before_comma = g_locale_to_utf8 (locale_string, -1, NULL, NULL, NULL);
5583 		g_free (locale_string);
5584 	} else {
5585 		part_before_comma = locale_string;
5586 	}
5587 
5588 	if (!g_utf8_validate (name, -1, NULL)) {
5589 		locale_string = g_locale_to_utf8 (name, -1, NULL, NULL, NULL);
5590 	} else {
5591 		locale_string = g_strdup (name);
5592 	}
5593 
5594 	capitalized_login_name = eel_str_capitalize (locale_string);
5595 	g_free (locale_string);
5596 
5597 	if (capitalized_login_name == NULL) {
5598 		real_name = part_before_comma;
5599 	} else {
5600 		real_name = eel_str_replace_substring
5601 			(part_before_comma, "&", capitalized_login_name);
5602 		g_free (part_before_comma);
5603 	}
5604 
5605 
5606 	if (eel_str_is_empty (real_name)
5607 	    || eel_strcmp (name, real_name) == 0
5608 	    || eel_strcmp (capitalized_login_name, real_name) == 0) {
5609 		g_free (real_name);
5610 		real_name = NULL;
5611 	}
5612 
5613 	g_free (capitalized_login_name);
5614 
5615 	return real_name;
5616 }
5617 
5618 static gboolean
get_group_id_from_group_name(const char * group_name,uid_t * gid)5619 get_group_id_from_group_name (const char *group_name, uid_t *gid)
5620 {
5621 	struct group *group;
5622 
5623 	g_assert (gid != NULL);
5624 
5625 	group = getgrnam (group_name);
5626 
5627 	if (group == NULL) {
5628 		return FALSE;
5629 	}
5630 
5631 	*gid = group->gr_gid;
5632 
5633 	return TRUE;
5634 }
5635 
5636 static gboolean
get_ids_from_user_name(const char * user_name,uid_t * uid,uid_t * gid)5637 get_ids_from_user_name (const char *user_name, uid_t *uid, uid_t *gid)
5638 {
5639 	struct passwd *password_info;
5640 
5641 	g_assert (uid != NULL || gid != NULL);
5642 
5643 	password_info = getpwnam (user_name);
5644 
5645 	if (password_info == NULL) {
5646 		return FALSE;
5647 	}
5648 
5649 	if (uid != NULL) {
5650 		*uid = password_info->pw_uid;
5651 	}
5652 
5653 	if (gid != NULL) {
5654 		*gid = password_info->pw_gid;
5655 	}
5656 
5657 	return TRUE;
5658 }
5659 
5660 static gboolean
get_user_id_from_user_name(const char * user_name,uid_t * id)5661 get_user_id_from_user_name (const char *user_name, uid_t *id)
5662 {
5663 	return get_ids_from_user_name (user_name, id, NULL);
5664 }
5665 
5666 static gboolean
get_id_from_digit_string(const char * digit_string,uid_t * id)5667 get_id_from_digit_string (const char *digit_string, uid_t *id)
5668 {
5669 	long scanned_id;
5670 	char c;
5671 
5672 	g_assert (id != NULL);
5673 
5674 	/* Only accept string if it has one integer with nothing
5675 	 * afterwards.
5676 	 */
5677 	if (sscanf (digit_string, "%ld%c", &scanned_id, &c) != 1) {
5678 		return FALSE;
5679 	}
5680 	*id = scanned_id;
5681 	return TRUE;
5682 }
5683 
5684 /**
5685  * caja_file_can_get_owner:
5686  *
5687  * Check whether the owner a file is determinable.
5688  * This might not be the case for files on non-UNIX file systems.
5689  *
5690  * @file: The file in question.
5691  *
5692  * Return value: TRUE if the owner is valid.
5693  */
5694 gboolean
caja_file_can_get_owner(CajaFile * file)5695 caja_file_can_get_owner (CajaFile *file)
5696 {
5697 	/* Before we have info on a file, the owner is unknown. */
5698 	return file->details->uid != -1;
5699 }
5700 
5701 /**
5702  * caja_file_get_owner_name:
5703  *
5704  * Get the user name of the file's owner. If the owner has no
5705  * name, returns the userid as a string. The caller is responsible
5706  * for g_free-ing this string.
5707  *
5708  * @file: The file in question.
5709  *
5710  * Return value: A newly-allocated string.
5711  */
5712 char *
caja_file_get_owner_name(CajaFile * file)5713 caja_file_get_owner_name (CajaFile *file)
5714 {
5715 	return caja_file_get_owner_as_string (file, FALSE);
5716 }
5717 
5718 /**
5719  * caja_file_can_set_owner:
5720  *
5721  * Check whether the current user is allowed to change
5722  * the owner of a file.
5723  *
5724  * @file: The file in question.
5725  *
5726  * Return value: TRUE if the current user can change the
5727  * owner of @file, FALSE otherwise. It's always possible
5728  * that when you actually try to do it, you will fail.
5729  */
5730 gboolean
caja_file_can_set_owner(CajaFile * file)5731 caja_file_can_set_owner (CajaFile *file)
5732 {
5733 	/* Not allowed to set the owner if we can't
5734 	 * even read it. This can happen on non-UNIX file
5735 	 * systems.
5736 	 */
5737 	if (!caja_file_can_get_owner (file)) {
5738 		return FALSE;
5739 	}
5740 
5741 	/* Only root is also allowed to set the owner. */
5742 	return geteuid() == 0;
5743 }
5744 
5745 /**
5746  * caja_file_set_owner:
5747  *
5748  * Set the owner of a file. This will only have any effect if
5749  * caja_file_can_set_owner returns TRUE.
5750  *
5751  * @file: The file in question.
5752  * @user_name_or_id: The user name to set the owner to.
5753  * If the string does not match any user name, and the
5754  * string is an integer, the owner will be set to the
5755  * userid represented by that integer.
5756  * @callback: Function called when asynch owner change succeeds or fails.
5757  * @callback_data: Parameter passed back with callback function.
5758  */
5759 void
caja_file_set_owner(CajaFile * file,const char * user_name_or_id,CajaFileOperationCallback callback,gpointer callback_data)5760 caja_file_set_owner (CajaFile *file,
5761 			 const char *user_name_or_id,
5762 			 CajaFileOperationCallback callback,
5763 			 gpointer callback_data)
5764 {
5765 	GError *error;
5766 	GFileInfo *info;
5767 	uid_t new_id;
5768 
5769 	if (!caja_file_can_set_owner (file)) {
5770 		/* Claim that something changed even if the permission
5771 		 * change failed. This makes it easier for some
5772 		 * clients who see the "reverting" to the old owner as
5773 		 * "changing back".
5774 		 */
5775 		caja_file_changed (file);
5776 		error = g_error_new (G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
5777 				     _("Not allowed to set owner"));
5778 		(* callback) (file, NULL, error, callback_data);
5779 		g_error_free (error);
5780 		return;
5781 	}
5782 
5783 	/* If no match treating user_name_or_id as name, try treating
5784 	 * it as id.
5785 	 */
5786 	if (!get_user_id_from_user_name (user_name_or_id, &new_id)
5787 	    && !get_id_from_digit_string (user_name_or_id, &new_id)) {
5788 		/* Claim that something changed even if the permission
5789 		 * change failed. This makes it easier for some
5790 		 * clients who see the "reverting" to the old owner as
5791 		 * "changing back".
5792 		 */
5793 		caja_file_changed (file);
5794 		error = g_error_new (G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
5795 				     _("Specified owner '%s' doesn't exist"), user_name_or_id);
5796 		(* callback) (file, NULL, error, callback_data);
5797 		g_error_free (error);
5798 		return;
5799 	}
5800 
5801 	/* Test the owner-hasn't-changed case explicitly because we
5802 	 * don't want to send the file-changed signal if nothing
5803 	 * changed.
5804 	 */
5805 	if (new_id == (uid_t) file->details->uid) {
5806 		(* callback) (file, NULL, NULL, callback_data);
5807 		return;
5808 	}
5809 
5810     // Start UNDO-REDO
5811 	if (!caja_undostack_manager_is_undo_redo(caja_undostack_manager_instance())) {
5812 		char* current_owner = caja_file_get_owner_as_string (file, FALSE);
5813 		CajaUndoStackActionData* undo_redo_data = caja_undostack_manager_data_new (CAJA_UNDOSTACK_CHANGEOWNER, 1);
5814 		caja_undostack_manager_data_set_owner_change_information(undo_redo_data, caja_file_get_uri(file), current_owner, user_name_or_id);
5815 		caja_undostack_manager_add_action (caja_undostack_manager_instance(),
5816 										   undo_redo_data);
5817         g_free(current_owner);
5818 	}
5819 	// End UNDO-REDO
5820 
5821 	info = g_file_info_new ();
5822 	g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_UID, new_id);
5823 	caja_file_set_attributes (file, info, callback, callback_data);
5824 	g_object_unref (info);
5825 }
5826 
5827 /**
5828  * caja_get_user_names:
5829  *
5830  * Get a list of user names. For users with a different associated
5831  * "real name", the real name follows the standard user name, separated
5832  * by a carriage return. The caller is responsible for freeing this list
5833  * and its contents.
5834  */
5835 GList *
caja_get_user_names(void)5836 caja_get_user_names (void)
5837 {
5838 	GList *list;
5839 	char *name;
5840 	struct passwd *user;
5841 
5842 	list = NULL;
5843 
5844 	setpwent ();
5845 
5846 	while ((user = getpwent ()) != NULL) {
5847 		char *real_name;
5848 
5849 		real_name = get_real_name (user->pw_name, user->pw_gecos);
5850 		if (real_name != NULL) {
5851 			name = g_strconcat (user->pw_name, "\n", real_name, NULL);
5852 		} else {
5853 			name = g_strdup (user->pw_name);
5854 		}
5855 		g_free (real_name);
5856 		list = g_list_prepend (list, name);
5857 	}
5858 
5859 	endpwent ();
5860 
5861 	return eel_g_str_list_alphabetize (list);
5862 }
5863 
5864 /**
5865  * caja_file_can_get_group:
5866  *
5867  * Check whether the group a file is determinable.
5868  * This might not be the case for files on non-UNIX file systems.
5869  *
5870  * @file: The file in question.
5871  *
5872  * Return value: TRUE if the group is valid.
5873  */
5874 gboolean
caja_file_can_get_group(CajaFile * file)5875 caja_file_can_get_group (CajaFile *file)
5876 {
5877 	/* Before we have info on a file, the group is unknown. */
5878 	return file->details->gid != -1;
5879 }
5880 
5881 /**
5882  * caja_file_get_group_name:
5883  *
5884  * Get the name of the file's group. If the group has no
5885  * name, returns the groupid as a string. The caller is responsible
5886  * for g_free-ing this string.
5887  *
5888  * @file: The file in question.
5889  *
5890  * Return value: A newly-allocated string.
5891  **/
5892 char *
caja_file_get_group_name(CajaFile * file)5893 caja_file_get_group_name (CajaFile *file)
5894 {
5895 	return g_strdup (file->details->group);
5896 }
5897 
5898 /**
5899  * caja_file_can_set_group:
5900  *
5901  * Check whether the current user is allowed to change
5902  * the group of a file.
5903  *
5904  * @file: The file in question.
5905  *
5906  * Return value: TRUE if the current user can change the
5907  * group of @file, FALSE otherwise. It's always possible
5908  * that when you actually try to do it, you will fail.
5909  */
5910 gboolean
caja_file_can_set_group(CajaFile * file)5911 caja_file_can_set_group (CajaFile *file)
5912 {
5913 	uid_t user_id;
5914 
5915 	/* Not allowed to set the permissions if we can't
5916 	 * even read them. This can happen on non-UNIX file
5917 	 * systems.
5918 	 */
5919 	if (!caja_file_can_get_group (file)) {
5920 		return FALSE;
5921 	}
5922 
5923 	/* Check the user. */
5924 	user_id = geteuid();
5925 
5926 	/* Owner is allowed to set group (with restrictions). */
5927 	if (user_id == (uid_t) file->details->uid) {
5928 		return TRUE;
5929 	}
5930 
5931 	/* Root is also allowed to set group. */
5932 	if (user_id == 0) {
5933 		return TRUE;
5934 	}
5935 
5936 	/* Nobody else is allowed. */
5937 	return FALSE;
5938 }
5939 
5940 /* Get a list of group names, filtered to only the ones
5941  * that contain the given username. If the username is
5942  * NULL, returns a list of all group names.
5943  */
5944 static GList *
caja_get_group_names_for_user(void)5945 caja_get_group_names_for_user (void)
5946 {
5947 	GList *list;
5948 	int count, i;
5949 	gid_t gid_list[NGROUPS_MAX + 1];
5950 	struct group *group = NULL;
5951 
5952 
5953 	list = NULL;
5954 
5955 	count = getgroups (NGROUPS_MAX + 1, gid_list);
5956 	for (i = 0; i < count; i++) {
5957 		group = getgrgid (gid_list[i]);
5958 		if (group == NULL)
5959 			break;
5960 
5961 		list = g_list_prepend (list, g_strdup (group->gr_name));
5962 	}
5963 
5964 	return eel_g_str_list_alphabetize (list);
5965 }
5966 
5967 /**
5968  * caja_get_group_names:
5969  *
5970  * Get a list of all group names.
5971  */
5972 GList *
caja_get_all_group_names(void)5973 caja_get_all_group_names (void)
5974 {
5975 	GList *list;
5976 	struct group *group;
5977 
5978 	list = NULL;
5979 
5980 	setgrent ();
5981 
5982 	while ((group = getgrent ()) != NULL)
5983 		list = g_list_prepend (list, g_strdup (group->gr_name));
5984 
5985 	endgrent ();
5986 
5987 	return eel_g_str_list_alphabetize (list);
5988 }
5989 
5990 /**
5991  * caja_file_get_settable_group_names:
5992  *
5993  * Get a list of all group names that the current user
5994  * can set the group of a specific file to.
5995  *
5996  * @file: The CajaFile in question.
5997  */
5998 GList *
caja_file_get_settable_group_names(CajaFile * file)5999 caja_file_get_settable_group_names (CajaFile *file)
6000 {
6001 	uid_t user_id;
6002 	GList *result;
6003 
6004 	if (!caja_file_can_set_group (file)) {
6005 		return NULL;
6006 	}
6007 
6008 	/* Check the user. */
6009 	user_id = geteuid();
6010 
6011 	if (user_id == 0) {
6012 		/* Root is allowed to set group to anything. */
6013 		result = caja_get_all_group_names ();
6014 	} else if (user_id == (uid_t) file->details->uid) {
6015 		/* Owner is allowed to set group to any that owner is member of. */
6016 		result = caja_get_group_names_for_user ();
6017 	} else {
6018 		g_warning ("unhandled case in caja_get_settable_group_names");
6019 		result = NULL;
6020 	}
6021 
6022 	return result;
6023 }
6024 
6025 /**
6026  * caja_file_set_group:
6027  *
6028  * Set the group of a file. This will only have any effect if
6029  * caja_file_can_set_group returns TRUE.
6030  *
6031  * @file: The file in question.
6032  * @group_name_or_id: The group name to set the owner to.
6033  * If the string does not match any group name, and the
6034  * string is an integer, the group will be set to the
6035  * group id represented by that integer.
6036  * @callback: Function called when asynch group change succeeds or fails.
6037  * @callback_data: Parameter passed back with callback function.
6038  */
6039 void
caja_file_set_group(CajaFile * file,const char * group_name_or_id,CajaFileOperationCallback callback,gpointer callback_data)6040 caja_file_set_group (CajaFile *file,
6041 			 const char *group_name_or_id,
6042 			 CajaFileOperationCallback callback,
6043 			 gpointer callback_data)
6044 {
6045 	GError *error;
6046 	GFileInfo *info;
6047 	uid_t new_id;
6048 
6049 	if (!caja_file_can_set_group (file)) {
6050 		/* Claim that something changed even if the group
6051 		 * change failed. This makes it easier for some
6052 		 * clients who see the "reverting" to the old group as
6053 		 * "changing back".
6054 		 */
6055 		caja_file_changed (file);
6056 		error = g_error_new (G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
6057 				     _("Not allowed to set group"));
6058 		(* callback) (file, NULL, error, callback_data);
6059 		g_error_free (error);
6060 		return;
6061 	}
6062 
6063 	/* If no match treating group_name_or_id as name, try treating
6064 	 * it as id.
6065 	 */
6066 	if (!get_group_id_from_group_name (group_name_or_id, &new_id)
6067 	    && !get_id_from_digit_string (group_name_or_id, &new_id)) {
6068 		/* Claim that something changed even if the group
6069 		 * change failed. This makes it easier for some
6070 		 * clients who see the "reverting" to the old group as
6071 		 * "changing back".
6072 		 */
6073 		caja_file_changed (file);
6074 		error = g_error_new (G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
6075 				     _("Specified group '%s' doesn't exist"), group_name_or_id);
6076 		(* callback) (file, NULL, error, callback_data);
6077 		g_error_free (error);
6078 		return;
6079 	}
6080 
6081 	if (new_id == (gid_t) file->details->gid) {
6082 		(* callback) (file, NULL, NULL, callback_data);
6083 		return;
6084 	}
6085 
6086 	// Start UNDO-REDO
6087 	if (!caja_undostack_manager_is_undo_redo(caja_undostack_manager_instance())) {
6088 		char* current_group = caja_file_get_group_name (file);
6089 		CajaUndoStackActionData* undo_redo_data = caja_undostack_manager_data_new (CAJA_UNDOSTACK_CHANGEGROUP, 1);
6090 		caja_undostack_manager_data_set_group_change_information(undo_redo_data, caja_file_get_uri(file), current_group, group_name_or_id);
6091 		caja_undostack_manager_add_action (caja_undostack_manager_instance(),
6092 											undo_redo_data);
6093         g_free(current_group);
6094 	}
6095 	// End UNDO-REDO
6096 
6097 	info = g_file_info_new ();
6098 	g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_GID, new_id);
6099 	caja_file_set_attributes (file, info, callback, callback_data);
6100 	g_object_unref (info);
6101 }
6102 
6103 /**
6104  * caja_file_get_octal_permissions_as_string:
6105  *
6106  * Get a user-displayable string representing a file's permissions
6107  * as an octal number. The caller
6108  * is responsible for g_free-ing this string.
6109  * @file: CajaFile representing the file in question.
6110  *
6111  * Returns: Newly allocated string ready to display to the user.
6112  *
6113  **/
6114 static char *
caja_file_get_octal_permissions_as_string(CajaFile * file)6115 caja_file_get_octal_permissions_as_string (CajaFile *file)
6116 {
6117 	guint32 permissions;
6118 
6119 	g_assert (CAJA_IS_FILE (file));
6120 
6121 	if (!caja_file_can_get_permissions (file)) {
6122 		return NULL;
6123 	}
6124 
6125 	permissions = file->details->permissions;
6126 	return g_strdup_printf ("%03o", permissions);
6127 }
6128 
6129 /**
6130  * caja_file_get_permissions_as_string:
6131  *
6132  * Get a user-displayable string representing a file's permissions. The caller
6133  * is responsible for g_free-ing this string.
6134  * @file: CajaFile representing the file in question.
6135  *
6136  * Returns: Newly allocated string ready to display to the user.
6137  *
6138  **/
6139 static char *
caja_file_get_permissions_as_string(CajaFile * file)6140 caja_file_get_permissions_as_string (CajaFile *file)
6141 {
6142 	guint32 permissions;
6143 	gboolean is_directory;
6144 	gboolean is_link;
6145 	gboolean suid, sgid, sticky;
6146 
6147 	if (!caja_file_can_get_permissions (file)) {
6148 		return NULL;
6149 	}
6150 
6151 	g_assert (CAJA_IS_FILE (file));
6152 
6153 	permissions = file->details->permissions;
6154 	is_directory = caja_file_is_directory (file);
6155 	is_link = caja_file_is_symbolic_link (file);
6156 
6157 	/* We use ls conventions for displaying these three obscure flags */
6158 	suid = permissions & S_ISUID;
6159 	sgid = permissions & S_ISGID;
6160 	sticky = permissions & S_ISVTX;
6161 
6162 	return g_strdup_printf ("%c%c%c%c%c%c%c%c%c%c",
6163 				 is_link ? 'l' : is_directory ? 'd' : '-',
6164 		 		 permissions & S_IRUSR ? 'r' : '-',
6165 				 permissions & S_IWUSR ? 'w' : '-',
6166 				 permissions & S_IXUSR
6167 				 	? (suid ? 's' : 'x')
6168 				 	: (suid ? 'S' : '-'),
6169 				 permissions & S_IRGRP ? 'r' : '-',
6170 				 permissions & S_IWGRP ? 'w' : '-',
6171 				 permissions & S_IXGRP
6172 				 	? (sgid ? 's' : 'x')
6173 				 	: (sgid ? 'S' : '-'),
6174 				 permissions & S_IROTH ? 'r' : '-',
6175 				 permissions & S_IWOTH ? 'w' : '-',
6176 				 permissions & S_IXOTH
6177 				 	? (sticky ? 't' : 'x')
6178 				 	: (sticky ? 'T' : '-'));
6179 }
6180 
6181 /**
6182  * caja_file_get_owner_as_string:
6183  *
6184  * Get a user-displayable string representing a file's owner. The caller
6185  * is responsible for g_free-ing this string.
6186  * @file: CajaFile representing the file in question.
6187  * @include_real_name: Whether or not to append the real name (if any)
6188  * for this user after the user name.
6189  *
6190  * Returns: Newly allocated string ready to display to the user.
6191  *
6192  **/
6193 static char *
caja_file_get_owner_as_string(CajaFile * file,gboolean include_real_name)6194 caja_file_get_owner_as_string (CajaFile *file, gboolean include_real_name)
6195 {
6196 	char *user_name;
6197 
6198 	/* Before we have info on a file, the owner is unknown. */
6199 	if (file->details->owner == NULL &&
6200 	    file->details->owner_real == NULL) {
6201 		return NULL;
6202 	}
6203 
6204 	if (file->details->owner_real == NULL) {
6205 		user_name = g_strdup (file->details->owner);
6206 	} else if (file->details->owner == NULL) {
6207 		user_name = g_strdup (file->details->owner_real);
6208 	} else if (include_real_name &&
6209 		   strcmp (file->details->owner, file->details->owner_real) != 0) {
6210 		user_name = g_strdup_printf ("%s - %s",
6211 					     file->details->owner,
6212 					     file->details->owner_real);
6213 	} else {
6214 		user_name = g_strdup (file->details->owner);
6215 	}
6216 
6217 	return user_name;
6218 }
6219 
6220 static char *
format_item_count_for_display(guint item_count,gboolean includes_directories,gboolean includes_files)6221 format_item_count_for_display (guint item_count,
6222 			       gboolean includes_directories,
6223 			       gboolean includes_files)
6224 {
6225 	g_assert (includes_directories || includes_files);
6226 
6227 	return g_strdup_printf (includes_directories
6228 			? (includes_files
6229 			   ? ngettext ("%'u item", "%'u items", item_count)
6230 			   : ngettext ("%'u folder", "%'u folders", item_count))
6231 			: ngettext ("%'u file", "%'u files", item_count), item_count);
6232 }
6233 
6234 /**
6235  * caja_file_get_size_as_string:
6236  *
6237  * Get a user-displayable string representing a file size. The caller
6238  * is responsible for g_free-ing this string. The string is an item
6239  * count for directories.
6240  * @file: CajaFile representing the file in question.
6241  * @size_on_disk: If TRUE will return the size on disk. If FALSE return file size.
6242  *
6243  * Returns: Newly allocated string ready to display to the user.
6244  *
6245  **/
6246 static char *
caja_file_get_size_as_string(CajaFile * file,gboolean size_on_disk)6247 caja_file_get_size_as_string (CajaFile *file,
6248 							  gboolean  size_on_disk)
6249 {
6250 	guint item_count;
6251 	gboolean count_unreadable;
6252 	goffset size;
6253 
6254 	if (file == NULL) {
6255 		return NULL;
6256 	}
6257 
6258 	g_assert (CAJA_IS_FILE (file));
6259 
6260 	if (caja_file_is_directory (file)) {
6261 		if (!caja_file_get_directory_item_count (file, &item_count, &count_unreadable)) {
6262 			return NULL;
6263 		}
6264 		return format_item_count_for_display (item_count, TRUE, TRUE);
6265 	}
6266 
6267 	if (size_on_disk) {
6268 		size = file->details->size_on_disk;
6269 	} else {
6270 		size = file->details->size;
6271 	}
6272 
6273 	if (size == -1) {
6274 		return NULL;
6275 	}
6276 
6277 	if (g_settings_get_boolean (caja_preferences, CAJA_PREFERENCES_USE_IEC_UNITS))
6278 		return g_format_size_full (size, G_FORMAT_SIZE_IEC_UNITS);
6279 	else
6280 		return g_format_size (size);
6281 }
6282 
6283 /**
6284  * caja_file_get_size_as_string_with_real_size:
6285  *
6286  * Get a user-displayable string representing a file size. The caller
6287  * is responsible for g_free-ing this string. The string is an item
6288  * count for directories.
6289  * This function adds the real size in the string.
6290  * @file: CajaFile representing the file in question.
6291  *
6292  * Returns: Newly allocated string ready to display to the user.
6293  *
6294  **/
6295 static char *
caja_file_get_size_as_string_with_real_size(CajaFile * file,gboolean size_on_disk)6296 caja_file_get_size_as_string_with_real_size (CajaFile *file,
6297 											 gboolean  size_on_disk)
6298 {
6299 	guint item_count;
6300 	gboolean count_unreadable;
6301 	goffset size;
6302 	char * formatted;
6303 	char * formatted_plus_real;
6304 	char * real_size;
6305 
6306 	if (file == NULL) {
6307 		return NULL;
6308 	}
6309 
6310 	g_assert (CAJA_IS_FILE (file));
6311 
6312 	if (caja_file_is_directory (file)) {
6313 		if (!caja_file_get_directory_item_count (file, &item_count, &count_unreadable)) {
6314 			return NULL;
6315 		}
6316 		return format_item_count_for_display (item_count, TRUE, TRUE);
6317 	}
6318 
6319 	if (size_on_disk) {
6320 		size = file->details->size_on_disk;
6321 	} else {
6322 		size = file->details->size;
6323 	}
6324 
6325 	if (size == -1) {
6326 		return NULL;
6327 	}
6328 
6329 	if (g_settings_get_boolean (caja_preferences, CAJA_PREFERENCES_USE_IEC_UNITS))
6330 		formatted = g_format_size_full (size, G_FORMAT_SIZE_IEC_UNITS);
6331 	else
6332 		formatted = g_format_size(size);
6333 
6334 	/* Do this in a separate stage so that we don't have to put G_GUINT64_FORMAT in the translated string */
6335 	real_size = g_strdup_printf (_("%"G_GUINT64_FORMAT), (guint64) size);
6336 	formatted_plus_real = g_strdup_printf (_("%s (%s bytes)"), formatted, real_size);
6337 	g_free (real_size);
6338 	g_free (formatted);
6339 	return formatted_plus_real;
6340 }
6341 
6342 static char *
caja_file_get_deep_count_as_string_internal(CajaFile * file,gboolean report_size,gboolean report_size_on_disk,gboolean report_directory_count,gboolean report_file_count)6343 caja_file_get_deep_count_as_string_internal (CajaFile *file,
6344 						 gboolean report_size,
6345 						 gboolean report_size_on_disk,
6346 						 gboolean report_directory_count,
6347 						 gboolean report_file_count)
6348 {
6349 	CajaRequestStatus status;
6350 	guint directory_count;
6351 	guint file_count;
6352 	guint unreadable_count;
6353 	guint total_count;
6354 	goffset total_size;
6355 	goffset total_size_on_disk;
6356 
6357 	/* Can't ask for more than one of those: size, size on disk or (directory and/or file count) */
6358 	g_assert (!(report_size && report_size_on_disk));
6359 	g_assert (!(report_size         && (report_directory_count || report_file_count)));
6360 	g_assert (!(report_size_on_disk && (report_directory_count || report_file_count)));
6361 
6362 	/* Must ask for something */
6363 	g_assert (report_size || report_size_on_disk || report_directory_count || report_file_count);
6364 
6365 	if (file == NULL) {
6366 		return NULL;
6367 	}
6368 
6369 	g_assert (CAJA_IS_FILE (file));
6370 	g_assert (caja_file_is_directory (file));
6371 
6372 	status = caja_file_get_deep_counts (file,
6373 										&directory_count,
6374 										&file_count,
6375 										&unreadable_count,
6376 										&total_size,
6377 										&total_size_on_disk,
6378 										FALSE);
6379 
6380 	/* Check whether any info is available. */
6381 	if (status == CAJA_REQUEST_NOT_STARTED) {
6382 		return NULL;
6383 	}
6384 
6385 	total_count = file_count + directory_count;
6386 
6387 	if (total_count == 0) {
6388 		switch (status) {
6389 		case CAJA_REQUEST_IN_PROGRESS:
6390 			/* Don't return confident "zero" until we're finished looking,
6391 			 * because of next case.
6392 			 */
6393 			return NULL;
6394 		case CAJA_REQUEST_DONE:
6395 			/* Don't return "zero" if we there were contents but we couldn't read them. */
6396 			if (unreadable_count != 0) {
6397 				return NULL;
6398 			}
6399 		default: break;
6400 		}
6401 	}
6402 
6403 	/* Note that we don't distinguish the "everything was readable" case
6404 	 * from the "some things but not everything was readable" case here.
6405 	 * Callers can distinguish them using caja_file_get_deep_counts
6406 	 * directly if desired.
6407 	 */
6408 	if (report_size)
6409 	{
6410 		if (g_settings_get_boolean (caja_preferences, CAJA_PREFERENCES_USE_IEC_UNITS))
6411 			return g_format_size_full (total_size, G_FORMAT_SIZE_IEC_UNITS);
6412 		else
6413 			return g_format_size(total_size);
6414 	}
6415 
6416 	if (report_size_on_disk)
6417 	{
6418 		if (g_settings_get_boolean (caja_preferences, CAJA_PREFERENCES_USE_IEC_UNITS))
6419 			return g_format_size_full (total_size_on_disk, G_FORMAT_SIZE_IEC_UNITS);
6420 		else
6421 			return g_format_size (total_size_on_disk);
6422 	}
6423 
6424 	return format_item_count_for_display (report_directory_count
6425 		? (report_file_count ? total_count : directory_count)
6426 		: file_count,
6427 		report_directory_count, report_file_count);
6428 }
6429 
6430 /**
6431  * caja_file_get_deep_size_as_string:
6432  *
6433  * Get a user-displayable string representing the size of all contained
6434  * items (only makes sense for directories). The caller
6435  * is responsible for g_free-ing this string.
6436  * @file: CajaFile representing the file in question.
6437  * @size_on_disk: if TRUE will return the size on disk, else return size of file.
6438  *
6439  * Returns: Newly allocated string ready to display to the user.
6440  *
6441  **/
6442 static char *
caja_file_get_deep_size_as_string(CajaFile * file,gboolean size_on_disk)6443 caja_file_get_deep_size_as_string (CajaFile *file, gboolean size_on_disk)
6444 {
6445 	if (size_on_disk) {
6446 		return caja_file_get_deep_count_as_string_internal (file, FALSE, TRUE, FALSE, FALSE);
6447 	} else {
6448 		return caja_file_get_deep_count_as_string_internal (file, TRUE, FALSE, FALSE, FALSE);
6449 	}
6450 }
6451 
6452 /**
6453  * caja_file_get_deep_total_count_as_string:
6454  *
6455  * Get a user-displayable string representing the count of all contained
6456  * items (only makes sense for directories). The caller
6457  * is responsible for g_free-ing this string.
6458  * @file: CajaFile representing the file in question.
6459  *
6460  * Returns: Newly allocated string ready to display to the user.
6461  *
6462  **/
6463 static char *
caja_file_get_deep_total_count_as_string(CajaFile * file)6464 caja_file_get_deep_total_count_as_string (CajaFile *file)
6465 {
6466 	return caja_file_get_deep_count_as_string_internal (file, FALSE, FALSE, TRUE, TRUE);
6467 }
6468 
6469 /**
6470  * caja_file_get_deep_file_count_as_string:
6471  *
6472  * Get a user-displayable string representing the count of all contained
6473  * items, not including directories. It only makes sense to call this
6474  * function on a directory. The caller
6475  * is responsible for g_free-ing this string.
6476  * @file: CajaFile representing the file in question.
6477  *
6478  * Returns: Newly allocated string ready to display to the user.
6479  *
6480  **/
6481 static char *
caja_file_get_deep_file_count_as_string(CajaFile * file)6482 caja_file_get_deep_file_count_as_string (CajaFile *file)
6483 {
6484 	return caja_file_get_deep_count_as_string_internal (file, FALSE, FALSE, FALSE, TRUE);
6485 }
6486 
6487 /**
6488  * caja_file_get_deep_directory_count_as_string:
6489  *
6490  * Get a user-displayable string representing the count of all contained
6491  * directories. It only makes sense to call this
6492  * function on a directory. The caller
6493  * is responsible for g_free-ing this string.
6494  * @file: CajaFile representing the file in question.
6495  *
6496  * Returns: Newly allocated string ready to display to the user.
6497  *
6498  **/
6499 static char *
caja_file_get_deep_directory_count_as_string(CajaFile * file)6500 caja_file_get_deep_directory_count_as_string (CajaFile *file)
6501 {
6502 	return caja_file_get_deep_count_as_string_internal (file, FALSE, FALSE, TRUE, FALSE);
6503 }
6504 
6505 /**
6506  * caja_file_get_string_attribute:
6507  *
6508  * Get a user-displayable string from a named attribute. Use g_free to
6509  * free this string. If the value is unknown, returns NULL. You can call
6510  * caja_file_get_string_attribute_with_default if you want a non-NULL
6511  * default.
6512  *
6513  * @file: CajaFile representing the file in question.
6514  * @attribute_name: The name of the desired attribute. The currently supported
6515  * set includes "name", "type", "mime_type", "size", "size_on_disk", "deep_size", "deep_size_on_disk",
6516  * "deep_directory_count", "deep_file_count", "deep_total_count", "date_modified", "date_changed",
6517  * "date_accessed", "date_permissions", "owner", "group", "permissions", "octal_permissions", "uri", "where",
6518  * "link_target", "volume", "free_space", "selinux_context", "trashed_on", "trashed_orig_path"
6519  *
6520  * Returns: Newly allocated string ready to display to the user, or NULL
6521  * if the value is unknown or @attribute_name is not supported.
6522  *
6523  **/
6524 char *
caja_file_get_string_attribute_q(CajaFile * file,GQuark attribute_q)6525 caja_file_get_string_attribute_q (CajaFile *file, GQuark attribute_q)
6526 {
6527 	char *extension_attribute;
6528 
6529 	if (attribute_q == attribute_name_q) {
6530 		return caja_file_get_display_name (file);
6531 	}
6532 	if (attribute_q == attribute_type_q) {
6533 		return caja_file_get_type_as_string (file);
6534 	}
6535 	if (attribute_q == attribute_mime_type_q) {
6536 		return caja_file_get_mime_type (file);
6537 	}
6538 	if (attribute_q == attribute_size_q) {
6539 		return caja_file_get_size_as_string (file, FALSE);
6540 	}
6541 	if (attribute_q == attribute_size_on_disk_q) {
6542 		return caja_file_get_size_as_string (file, TRUE);
6543 	}
6544 	if (attribute_q == attribute_size_detail_q) {
6545 		return caja_file_get_size_as_string_with_real_size (file, FALSE);
6546 	}
6547 	if (attribute_q == attribute_size_on_disk_detail_q) {
6548         return caja_file_get_size_as_string_with_real_size (file, TRUE);
6549 	}
6550 	if (attribute_q == attribute_deep_size_q) {
6551 		return caja_file_get_deep_size_as_string (file, FALSE);
6552 	}
6553 	if (attribute_q == attribute_deep_size_on_disk_q) {
6554 		return caja_file_get_deep_size_as_string (file, TRUE);
6555 	}
6556 	if (attribute_q == attribute_deep_file_count_q) {
6557 		return caja_file_get_deep_file_count_as_string (file);
6558 	}
6559 	if (attribute_q == attribute_deep_directory_count_q) {
6560 		return caja_file_get_deep_directory_count_as_string (file);
6561 	}
6562 	if (attribute_q == attribute_deep_total_count_q) {
6563 		return caja_file_get_deep_total_count_as_string (file);
6564 	}
6565 	if (attribute_q == attribute_trash_orig_path_q) {
6566 		return caja_file_get_trash_original_file_parent_as_string (file);
6567 	}
6568 	if (attribute_q == attribute_date_modified_q) {
6569 		return caja_file_get_date_as_string (file,
6570 							 CAJA_DATE_TYPE_MODIFIED);
6571 	}
6572 	if (attribute_q == attribute_date_changed_q) {
6573 		return caja_file_get_date_as_string (file,
6574 							 CAJA_DATE_TYPE_CHANGED);
6575 	}
6576 	if (attribute_q == attribute_date_accessed_q) {
6577 		return caja_file_get_date_as_string (file,
6578 							 CAJA_DATE_TYPE_ACCESSED);
6579 	}
6580 	if (attribute_q == attribute_date_created_q) {
6581 		return caja_file_get_date_as_string (file,
6582 		                                     CAJA_DATE_TYPE_CREATED);
6583 	}
6584 	if (attribute_q == attribute_trashed_on_q) {
6585 		return caja_file_get_date_as_string (file,
6586 							 CAJA_DATE_TYPE_TRASHED);
6587 	}
6588 	if (attribute_q == attribute_date_permissions_q) {
6589 		return caja_file_get_date_as_string (file,
6590 							 CAJA_DATE_TYPE_PERMISSIONS_CHANGED);
6591 	}
6592 	if (attribute_q == attribute_extension_q) {
6593 		return caja_file_get_extension_as_string (file);
6594 	}
6595 	if (attribute_q == attribute_permissions_q) {
6596 		return caja_file_get_permissions_as_string (file);
6597 	}
6598 	if (attribute_q == attribute_selinux_context_q) {
6599 		return caja_file_get_selinux_context (file);
6600 	}
6601 	if (attribute_q == attribute_octal_permissions_q) {
6602 		return caja_file_get_octal_permissions_as_string (file);
6603 	}
6604 	if (attribute_q == attribute_owner_q) {
6605 		return caja_file_get_owner_as_string (file, TRUE);
6606 	}
6607 	if (attribute_q == attribute_group_q) {
6608 		return caja_file_get_group_name (file);
6609 	}
6610 	if (attribute_q == attribute_uri_q) {
6611 		return caja_file_get_uri (file);
6612 	}
6613 	if (attribute_q == attribute_where_q) {
6614 		return caja_file_get_where_string (file);
6615 	}
6616 	if (attribute_q == attribute_link_target_q) {
6617 		return caja_file_get_symbolic_link_target_path (file);
6618 	}
6619 	if (attribute_q == attribute_volume_q) {
6620 		return caja_file_get_volume_name (file);
6621 	}
6622 	if (attribute_q == attribute_free_space_q) {
6623 		return caja_file_get_volume_free_space (file);
6624 	}
6625 
6626 	extension_attribute = NULL;
6627 
6628 	if (file->details->pending_extension_attributes) {
6629 		extension_attribute = g_hash_table_lookup (file->details->pending_extension_attributes,
6630 							   GINT_TO_POINTER (attribute_q));
6631 	}
6632 
6633 	if (extension_attribute == NULL && file->details->extension_attributes) {
6634 		extension_attribute = g_hash_table_lookup (file->details->extension_attributes,
6635 							   GINT_TO_POINTER (attribute_q));
6636 	}
6637 
6638 	return g_strdup (extension_attribute);
6639 }
6640 
6641 char *
caja_file_get_string_attribute(CajaFile * file,const char * attribute_name)6642 caja_file_get_string_attribute (CajaFile *file, const char *attribute_name)
6643 {
6644 	return caja_file_get_string_attribute_q (file, g_quark_from_string (attribute_name));
6645 }
6646 
6647 
6648 /**
6649  * caja_file_get_string_attribute_with_default:
6650  *
6651  * Get a user-displayable string from a named attribute. Use g_free to
6652  * free this string. If the value is unknown, returns a string representing
6653  * the unknown value, which varies with attribute. You can call
6654  * caja_file_get_string_attribute if you want NULL instead of a default
6655  * result.
6656  *
6657  * @file: CajaFile representing the file in question.
6658  * @attribute_name: The name of the desired attribute. See the description of
6659  * caja_file_get_string for the set of available attributes.
6660  *
6661  * Returns: Newly allocated string ready to display to the user, or a string
6662  * such as "unknown" if the value is unknown or @attribute_name is not supported.
6663  *
6664  **/
6665 char *
caja_file_get_string_attribute_with_default_q(CajaFile * file,GQuark attribute_q)6666 caja_file_get_string_attribute_with_default_q (CajaFile *file, GQuark attribute_q)
6667 {
6668 	char *result;
6669 	guint item_count;
6670 	gboolean count_unreadable;
6671 	CajaRequestStatus status;
6672 
6673 	result = caja_file_get_string_attribute_q (file, attribute_q);
6674 	if (result != NULL) {
6675 		return result;
6676 	}
6677 
6678 	/* Supply default values for the ones we know about. */
6679 	/* FIXME bugzilla.gnome.org 40646:
6680 	 * Use hash table and switch statement or function pointers for speed?
6681 	 */
6682 	if (attribute_q == attribute_size_q) {
6683 		if (!caja_file_should_show_directory_item_count (file)) {
6684 			return g_strdup ("--");
6685 		}
6686 		count_unreadable = FALSE;
6687 		if (caja_file_is_directory (file)) {
6688 			caja_file_get_directory_item_count (file, &item_count, &count_unreadable);
6689 		}
6690 		return g_strdup (count_unreadable ? _("? items") : "...");
6691 	}
6692 	if (attribute_q == attribute_deep_size_q) {
6693 		status = caja_file_get_deep_counts (file, NULL, NULL, NULL, NULL, NULL, FALSE);
6694 		if (status == CAJA_REQUEST_DONE) {
6695 			/* This means no contents at all were readable */
6696 			return g_strdup (_("? bytes"));
6697 		}
6698 		return g_strdup ("...");
6699 	}
6700     if (attribute_q == attribute_deep_size_on_disk_q) {
6701 		status = caja_file_get_deep_counts (file, NULL, NULL, NULL, NULL, NULL, FALSE);
6702 		if (status == CAJA_REQUEST_DONE) {
6703 			/* This means no contents at all were readable */
6704 			return g_strdup (_("? bytes"));
6705 		}
6706 		return g_strdup ("...");
6707 	}
6708 	if (attribute_q == attribute_deep_file_count_q
6709 	    || attribute_q == attribute_deep_directory_count_q
6710 	    || attribute_q == attribute_deep_total_count_q) {
6711 		status = caja_file_get_deep_counts (file, NULL, NULL, NULL, NULL, NULL, FALSE);
6712 		if (status == CAJA_REQUEST_DONE) {
6713 			/* This means no contents at all were readable */
6714 			return g_strdup (_("? items"));
6715 		}
6716 		return g_strdup ("...");
6717 	}
6718 	if (attribute_q == attribute_type_q) {
6719 		return g_strdup (_("unknown type"));
6720 	}
6721 	if (attribute_q == attribute_mime_type_q) {
6722 		return g_strdup (_("unknown MIME type"));
6723 	}
6724 	if (attribute_q == attribute_trashed_on_q) {
6725 		/* If n/a */
6726 		return g_strdup ("");
6727 	}
6728 	if (attribute_q == attribute_trash_orig_path_q) {
6729 		/* If n/a */
6730 		return g_strdup ("");
6731 	}
6732 
6733 	/* Fallback, use for both unknown attributes and attributes
6734 	 * for which we have no more appropriate default.
6735 	 */
6736 	return g_strdup (_("unknown"));
6737 }
6738 
6739 char *
caja_file_get_string_attribute_with_default(CajaFile * file,const char * attribute_name)6740 caja_file_get_string_attribute_with_default (CajaFile *file, const char *attribute_name)
6741 {
6742 	return caja_file_get_string_attribute_with_default_q (file, g_quark_from_string (attribute_name));
6743 }
6744 
6745 gboolean
caja_file_is_date_sort_attribute_q(GQuark attribute_q)6746 caja_file_is_date_sort_attribute_q (GQuark attribute_q)
6747 {
6748 	if (attribute_q == attribute_modification_date_q ||
6749 	    attribute_q == attribute_date_modified_q ||
6750 	    attribute_q == attribute_creation_date_q ||
6751 	    attribute_q == attribute_date_created_q ||
6752 	    attribute_q == attribute_accessed_date_q ||
6753 	    attribute_q == attribute_date_accessed_q ||
6754 	    attribute_q == attribute_date_changed_q ||
6755 	    attribute_q == attribute_trashed_on_q ||
6756 	    attribute_q == attribute_date_permissions_q) {
6757 		return TRUE;
6758 	}
6759 
6760 	return FALSE;
6761 }
6762 
6763 /**
6764  * get_description:
6765  *
6766  * Get a user-displayable string representing a file type. The caller
6767  * is responsible for g_free-ing this string.
6768  * @file: CajaFile representing the file in question.
6769  *
6770  * Returns: Newly allocated string ready to display to the user.
6771  *
6772  **/
6773 static char *
get_description(CajaFile * file)6774 get_description (CajaFile *file)
6775 {
6776 	const char *mime_type;
6777 	char *description;
6778 
6779 	g_assert (CAJA_IS_FILE (file));
6780 
6781 	mime_type = file->details->mime_type;
6782 	if (eel_str_is_empty (mime_type)) {
6783 		return NULL;
6784 	}
6785 
6786 	if (g_content_type_is_unknown (mime_type) &&
6787 	    caja_file_is_executable (file)) {
6788 		return g_strdup (_("program"));
6789 	}
6790 
6791 	description = g_content_type_get_description (mime_type);
6792 	if (!eel_str_is_empty (description)) {
6793 		return description;
6794 	}
6795 
6796 	return g_strdup (mime_type);
6797 }
6798 
6799 /* Takes ownership of string */
6800 static char *
update_description_for_link(CajaFile * file,char * string)6801 update_description_for_link (CajaFile *file, char *string)
6802 {
6803 	if (caja_file_is_symbolic_link (file)) {
6804 		char *res;
6805 
6806 		g_assert (!caja_file_is_broken_symbolic_link (file));
6807 		if (string == NULL) {
6808 			return g_strdup (_("link"));
6809 		}
6810 		/* Note to localizers: convert file type string for file
6811 		 * (e.g. "folder", "plain text") to file type for symbolic link
6812 		 * to that kind of file (e.g. "link to folder").
6813 		 */
6814 		res = g_strdup_printf (_("Link to %s"), string);
6815 		g_free (string);
6816 		return res;
6817 	}
6818 
6819 	return string;
6820 }
6821 
6822 static char *
caja_file_get_type_as_string(CajaFile * file)6823 caja_file_get_type_as_string (CajaFile *file)
6824 {
6825 	if (file == NULL) {
6826 		return NULL;
6827 	}
6828 
6829 	if (caja_file_is_broken_symbolic_link (file)) {
6830 		return g_strdup (_("link (broken)"));
6831 	}
6832 
6833 	return update_description_for_link (file, get_description (file));
6834 }
6835 
6836 /**
6837  * caja_file_get_file_type
6838  *
6839  * Return this file's type.
6840  * @file: CajaFile representing the file in question.
6841  *
6842  * Returns: The type.
6843  *
6844  **/
6845 GFileType
caja_file_get_file_type(CajaFile * file)6846 caja_file_get_file_type (CajaFile *file)
6847 {
6848 	if (file == NULL) {
6849 		return G_FILE_TYPE_UNKNOWN;
6850 	}
6851 
6852 	return file->details->type;
6853 }
6854 
6855 /**
6856  * caja_file_get_mime_type
6857  *
6858  * Return this file's default mime type.
6859  * @file: CajaFile representing the file in question.
6860  *
6861  * Returns: The mime type.
6862  *
6863  **/
6864 char *
caja_file_get_mime_type(CajaFile * file)6865 caja_file_get_mime_type (CajaFile *file)
6866 {
6867 	if (file != NULL) {
6868 		g_return_val_if_fail (CAJA_IS_FILE (file), NULL);
6869 		if (file->details->mime_type != NULL) {
6870 			return g_strdup (file->details->mime_type);
6871 		}
6872 	}
6873 	return g_strdup ("application/octet-stream");
6874 }
6875 
6876 /**
6877  * caja_file_is_mime_type
6878  *
6879  * Check whether a file is of a particular MIME type, or inherited
6880  * from it.
6881  * @file: CajaFile representing the file in question.
6882  * @mime_type: The MIME-type string to test (e.g. "text/plain")
6883  *
6884  * Return value: TRUE if @mime_type exactly matches the
6885  * file's MIME type.
6886  *
6887  **/
6888 gboolean
caja_file_is_mime_type(CajaFile * file,const char * mime_type)6889 caja_file_is_mime_type (CajaFile *file, const char *mime_type)
6890 {
6891 	g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
6892 	g_return_val_if_fail (mime_type != NULL, FALSE);
6893 
6894 	if (file->details->mime_type == NULL) {
6895 		return FALSE;
6896 	}
6897 	return g_content_type_is_a (file->details->mime_type,
6898 				    mime_type);
6899 }
6900 
6901 gboolean
caja_file_is_launchable(CajaFile * file)6902 caja_file_is_launchable (CajaFile *file)
6903 {
6904 	gboolean type_can_be_executable;
6905 
6906 	type_can_be_executable = FALSE;
6907 	if (file->details->mime_type != NULL) {
6908 		type_can_be_executable =
6909 			g_content_type_can_be_executable (file->details->mime_type);
6910 	}
6911 
6912 	return type_can_be_executable &&
6913 		caja_file_can_get_permissions (file) &&
6914 		caja_file_can_execute (file) &&
6915 		caja_file_is_executable (file) &&
6916 		!caja_file_is_directory (file);
6917 }
6918 
6919 
6920 /**
6921  * caja_file_get_emblem_icons
6922  *
6923  * Return the list of names of emblems that this file should display,
6924  * in canonical order.
6925  * @file: CajaFile representing the file in question.
6926  *
6927  * Returns: A list of emblem names.
6928  *
6929  **/
6930 GList *
caja_file_get_emblem_icons(CajaFile * file,char ** exclude)6931 caja_file_get_emblem_icons (CajaFile *file,
6932 				char **exclude)
6933 {
6934 	GList *keywords, *l;
6935 	GList *icons;
6936 	char *icon_names[2];
6937 	char *keyword;
6938 	int i;
6939 	GIcon *icon;
6940 
6941 	if (file == NULL) {
6942 		return NULL;
6943 	}
6944 
6945 	g_return_val_if_fail (CAJA_IS_FILE (file), NULL);
6946 
6947 	keywords = caja_file_get_keywords (file);
6948 	keywords = prepend_automatic_keywords (file, keywords);
6949 
6950 	icons = NULL;
6951 	for (l = keywords; l != NULL; l = l->next) {
6952 		keyword = l->data;
6953 
6954 #ifdef TRASH_IS_FAST_ENOUGH
6955 		if (strcmp (keyword, CAJA_FILE_EMBLEM_NAME_TRASH) == 0) {
6956 			char *uri;
6957 			gboolean file_is_trash;
6958 			/* Leave out the trash emblem for the trash itself, since
6959 			 * putting a trash emblem on a trash icon is gilding the
6960 			 * lily.
6961 			 */
6962 			uri = caja_file_get_uri (file);
6963 			file_is_trash = strcmp (uri, EEL_TRASH_URI) == 0;
6964 			g_free (uri);
6965 			if (file_is_trash) {
6966 				continue;
6967 			}
6968 		}
6969 #endif
6970 		if (exclude) {
6971 			for (i = 0; exclude[i] != NULL; i++) {
6972 				if (strcmp (exclude[i], keyword) == 0) {
6973 					continue;
6974 				}
6975 			}
6976 		}
6977 
6978 
6979 		icon_names[0] = g_strconcat ("emblem-", keyword, NULL);
6980 		icon_names[1] = keyword;
6981 		icon = g_themed_icon_new_from_names (icon_names, 2);
6982 		g_free (icon_names[0]);
6983 
6984 		icons = g_list_prepend (icons, icon);
6985 	}
6986 
6987         g_list_free_full (keywords, g_free);
6988 
6989 	return icons;
6990 }
6991 
6992 GList *
caja_file_get_emblem_pixbufs(CajaFile * file,int size,gboolean force_size,char ** exclude)6993 caja_file_get_emblem_pixbufs (CajaFile *file,
6994 				  int size,
6995 				  gboolean force_size,
6996 				  char **exclude)
6997 {
6998 	GList *icons, *l;
6999 	GList *pixbufs;
7000 	GdkPixbuf *pixbuf;
7001 	GIcon *icon = NULL;
7002 	CajaIconInfo *icon_info = NULL;
7003 
7004 	icons = caja_file_get_emblem_icons (file, exclude);
7005 	pixbufs = NULL;
7006 
7007 	for (l = icons; l != NULL; l = l->next) {
7008 		icon = l->data;
7009 
7010 		icon_info = caja_icon_info_lookup (icon, size, 1);
7011 		if (force_size) {
7012 			pixbuf = caja_icon_info_get_pixbuf_nodefault_at_size (icon_info, size);
7013 		} else {
7014 			pixbuf = caja_icon_info_get_pixbuf_nodefault (icon_info);
7015 		}
7016 
7017 		if (pixbuf) {
7018 			pixbufs = g_list_prepend (pixbufs, pixbuf);
7019 		}
7020 
7021 
7022 		g_object_unref (icon_info);
7023 		g_object_unref (icon);
7024 	}
7025 	g_list_free (icons);
7026 
7027 	return g_list_reverse (pixbufs);
7028 
7029 
7030 }
7031 
7032 static GList *
sort_keyword_list_and_remove_duplicates(GList * keywords)7033 sort_keyword_list_and_remove_duplicates (GList *keywords)
7034 {
7035 	GList *p;
7036 
7037 	if (keywords != NULL) {
7038 		GList *duplicate_link = NULL;
7039 
7040 		keywords = eel_g_str_list_alphabetize (keywords);
7041 
7042 		p = keywords;
7043 		while (p->next != NULL) {
7044 			if (strcmp ((const char *) p->data, (const char *) p->next->data) == 0) {
7045 				duplicate_link = p->next;
7046 				keywords = g_list_remove_link (keywords, duplicate_link);
7047 				g_list_free_full (duplicate_link, g_free);
7048 			} else {
7049 				p = p->next;
7050 			}
7051 		}
7052 	}
7053 
7054 	return keywords;
7055 }
7056 
7057 /**
7058  * caja_file_get_keywords
7059  *
7060  * Return this file's keywords.
7061  * @file: CajaFile representing the file in question.
7062  *
7063  * Returns: A list of keywords.
7064  *
7065  **/
7066 GList *
caja_file_get_keywords(CajaFile * file)7067 caja_file_get_keywords (CajaFile *file)
7068 {
7069 	GList *keywords;
7070 
7071 	if (file == NULL) {
7072 		return NULL;
7073 	}
7074 
7075 	g_return_val_if_fail (CAJA_IS_FILE (file), NULL);
7076 
7077 	/* Put all the keywords into a list. */
7078 	keywords = caja_file_get_metadata_list
7079 		(file, CAJA_METADATA_KEY_EMBLEMS);
7080 
7081 	keywords = g_list_concat (keywords, g_list_copy_deep (file->details->extension_emblems, (GCopyFunc) g_strdup, NULL));
7082 	keywords = g_list_concat (keywords, g_list_copy_deep (file->details->pending_extension_emblems, (GCopyFunc) g_strdup, NULL));
7083 
7084 	return sort_keyword_list_and_remove_duplicates (keywords);
7085 }
7086 
7087 /**
7088  * caja_file_set_keywords
7089  *
7090  * Change this file's keywords.
7091  * @file: CajaFile representing the file in question.
7092  * @keywords: New set of keywords (a GList of strings).
7093  *
7094  **/
7095 void
caja_file_set_keywords(CajaFile * file,GList * keywords)7096 caja_file_set_keywords (CajaFile *file, GList *keywords)
7097 {
7098 	GList *canonical_keywords;
7099 
7100 	/* Invalidate the emblem compare cache */
7101 	g_free (file->details->compare_by_emblem_cache);
7102 	file->details->compare_by_emblem_cache = NULL;
7103 
7104 	g_return_if_fail (CAJA_IS_FILE (file));
7105 
7106 	canonical_keywords = sort_keyword_list_and_remove_duplicates
7107 		(g_list_copy (keywords));
7108 	caja_file_set_metadata_list
7109 		(file, CAJA_METADATA_KEY_EMBLEMS, canonical_keywords);
7110 	g_list_free (canonical_keywords);
7111 }
7112 
7113 /**
7114  * caja_file_is_symbolic_link
7115  *
7116  * Check if this file is a symbolic link.
7117  * @file: CajaFile representing the file in question.
7118  *
7119  * Returns: True if the file is a symbolic link.
7120  *
7121  **/
7122 gboolean
caja_file_is_symbolic_link(CajaFile * file)7123 caja_file_is_symbolic_link (CajaFile *file)
7124 {
7125 	return file->details->is_symlink;
7126 }
7127 
7128 gboolean
caja_file_is_mountpoint(CajaFile * file)7129 caja_file_is_mountpoint (CajaFile *file)
7130 {
7131 	return file->details->is_mountpoint;
7132 }
7133 
7134 GMount *
caja_file_get_mount(CajaFile * file)7135 caja_file_get_mount (CajaFile *file)
7136 {
7137 	if (file->details->mount) {
7138 		return g_object_ref (file->details->mount);
7139 	}
7140 	return NULL;
7141 }
7142 
7143 static void
file_mount_unmounted(GMount * mount,gpointer data)7144 file_mount_unmounted (GMount *mount,
7145 		      gpointer data)
7146 {
7147 	CajaFile *file;
7148 
7149 	file = CAJA_FILE (data);
7150 
7151 	caja_file_invalidate_attributes (file, CAJA_FILE_ATTRIBUTE_MOUNT);
7152 }
7153 
7154 void
caja_file_set_mount(CajaFile * file,GMount * mount)7155 caja_file_set_mount (CajaFile *file,
7156 			 GMount *mount)
7157 {
7158 	if (file->details->mount) {
7159 		g_signal_handlers_disconnect_by_func (file->details->mount, file_mount_unmounted, file);
7160 		g_object_unref (file->details->mount);
7161 		file->details->mount = NULL;
7162 	}
7163 
7164 	if (mount) {
7165 		file->details->mount = g_object_ref (mount);
7166 		g_signal_connect (mount, "unmounted",
7167 				  G_CALLBACK (file_mount_unmounted), file);
7168 	}
7169 }
7170 
7171 /**
7172  * caja_file_is_broken_symbolic_link
7173  *
7174  * Check if this file is a symbolic link with a missing target.
7175  * @file: CajaFile representing the file in question.
7176  *
7177  * Returns: True if the file is a symbolic link with a missing target.
7178  *
7179  **/
7180 gboolean
caja_file_is_broken_symbolic_link(CajaFile * file)7181 caja_file_is_broken_symbolic_link (CajaFile *file)
7182 {
7183 	if (file == NULL) {
7184 		return FALSE;
7185 	}
7186 
7187 	g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
7188 
7189 	/* Non-broken symbolic links return the target's type for get_file_type. */
7190 	return caja_file_get_file_type (file) == G_FILE_TYPE_SYMBOLIC_LINK;
7191 }
7192 
7193 static void
get_fs_free_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)7194 get_fs_free_cb (GObject *source_object,
7195 		GAsyncResult *res,
7196 		gpointer user_data)
7197 {
7198 	CajaDirectory *directory;
7199 	guint64 free_space;
7200 	GFileInfo *info;
7201 
7202 	directory = CAJA_DIRECTORY (user_data);
7203 
7204 	free_space = (guint64)-1;
7205 	info = g_file_query_filesystem_info_finish (G_FILE (source_object),
7206 						    res, NULL);
7207 	if (info) {
7208 		if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE)) {
7209 			free_space = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE);
7210 		}
7211 		g_object_unref (info);
7212 	}
7213 
7214 	if (directory->details->free_space != free_space) {
7215 		CajaFile *file;
7216 
7217 		directory->details->free_space = free_space;
7218 		file = caja_directory_get_existing_corresponding_file (directory);
7219 		if (file) {
7220 			caja_file_emit_changed (file);
7221 			caja_file_unref (file);
7222 		}
7223 	}
7224 	caja_directory_unref (directory);
7225 }
7226 
7227 /**
7228  * caja_file_get_volume_free_space
7229  * Get a nicely formatted char with free space on the file's volume
7230  * @file: CajaFile representing the file in question.
7231  *
7232  * Returns: newly-allocated copy of file size in a formatted string
7233  */
7234 char *
caja_file_get_volume_free_space(CajaFile * file)7235 caja_file_get_volume_free_space (CajaFile *file)
7236 {
7237 	CajaDirectory *directory;
7238 	char *res;
7239 	time_t now;
7240 
7241 	directory = caja_directory_get_for_file (file);
7242 
7243 	now = time (NULL);
7244 	/* Update first time and then every 2 seconds */
7245 	if (directory->details->free_space_read == 0 ||
7246 	    (now - directory->details->free_space_read) > 2)  {
7247 		GFile *location;
7248 
7249 		directory->details->free_space_read = now;
7250 		location = caja_file_get_location (file);
7251 		g_file_query_filesystem_info_async (location,
7252 						    G_FILE_ATTRIBUTE_FILESYSTEM_FREE,
7253 						    0, NULL,
7254 						    get_fs_free_cb,
7255 						    directory); /* Inherits ref */
7256 		g_object_unref (location);
7257 	} else {
7258 		caja_directory_unref (directory);
7259 	}
7260 
7261 
7262 	res = NULL;
7263 
7264 	if (directory->details->free_space != (guint64) -1)
7265 	{
7266 		if (g_settings_get_boolean (caja_preferences, CAJA_PREFERENCES_USE_IEC_UNITS))
7267 			res = g_format_size_full (directory->details->free_space, G_FORMAT_SIZE_IEC_UNITS);
7268 		else
7269 			res = g_format_size(directory->details->free_space);
7270 	}
7271 
7272 	return res;
7273 }
7274 
7275 /**
7276  * caja_file_get_volume_name
7277  * Get the path of the volume the file resides on
7278  * @file: CajaFile representing the file in question.
7279  *
7280  * Returns: newly-allocated copy of the volume name of the target file,
7281  * if the volume name isn't set, it returns the mount path of the volume
7282  */
7283 char *
caja_file_get_volume_name(CajaFile * file)7284 caja_file_get_volume_name (CajaFile *file)
7285 {
7286 	GFile *location;
7287 	char *res;
7288 	GMount *mount;
7289 
7290 	res = NULL;
7291 
7292 	location = caja_file_get_location (file);
7293 	mount = g_file_find_enclosing_mount (location, NULL, NULL);
7294 	if (mount) {
7295 		res = g_strdup (g_mount_get_name (mount));
7296 		g_object_unref (mount);
7297 	}
7298 	g_object_unref (location);
7299 
7300 	return res;
7301 }
7302 
7303 /**
7304  * caja_file_get_symbolic_link_target_path
7305  *
7306  * Get the file path of the target of a symbolic link. It is an error
7307  * to call this function on a file that isn't a symbolic link.
7308  * @file: CajaFile representing the symbolic link in question.
7309  *
7310  * Returns: newly-allocated copy of the file path of the target of the symbolic link.
7311  */
7312 char *
caja_file_get_symbolic_link_target_path(CajaFile * file)7313 caja_file_get_symbolic_link_target_path (CajaFile *file)
7314 {
7315 	if (!caja_file_is_symbolic_link (file)) {
7316 		g_warning ("File has symlink target, but  is not marked as symlink");
7317 	}
7318 
7319 	return g_strdup (file->details->symlink_name);
7320 }
7321 
7322 /**
7323  * caja_file_get_symbolic_link_target_uri
7324  *
7325  * Get the uri of the target of a symbolic link. It is an error
7326  * to call this function on a file that isn't a symbolic link.
7327  * @file: CajaFile representing the symbolic link in question.
7328  *
7329  * Returns: newly-allocated copy of the uri of the target of the symbolic link.
7330  */
7331 char *
caja_file_get_symbolic_link_target_uri(CajaFile * file)7332 caja_file_get_symbolic_link_target_uri (CajaFile *file)
7333 {
7334 	if (!caja_file_is_symbolic_link (file)) {
7335 		g_warning ("File has symlink target, but  is not marked as symlink");
7336 	}
7337 
7338 	if (file->details->symlink_name == NULL) {
7339 		return NULL;
7340 	} else {
7341 		GFile *location, *parent, *target;
7342 		char *target_uri;
7343 
7344 		target = NULL;
7345 
7346 		location = caja_file_get_location (file);
7347 		parent = g_file_get_parent (location);
7348 		g_object_unref (location);
7349 		if (parent) {
7350 			target = g_file_resolve_relative_path (parent, file->details->symlink_name);
7351 			g_object_unref (parent);
7352 		}
7353 
7354 		target_uri = NULL;
7355 		if (target) {
7356 			target_uri = g_file_get_uri (target);
7357 			g_object_unref (target);
7358 		}
7359 		return target_uri;
7360 	}
7361 }
7362 
7363 /**
7364  * caja_file_is_caja_link
7365  *
7366  * Check if this file is a "caja link", meaning a historical
7367  * caja xml link file or a desktop file.
7368  * @file: CajaFile representing the file in question.
7369  *
7370  * Returns: True if the file is a caja link.
7371  *
7372  **/
7373 gboolean
caja_file_is_caja_link(CajaFile * file)7374 caja_file_is_caja_link (CajaFile *file)
7375 {
7376     if (file->details->mime_type == NULL)
7377     {
7378         return FALSE;
7379     }
7380     return g_content_type_equals (file->details->mime_type,
7381                                   "application/x-desktop");
7382 }
7383 
7384 /**
7385  * caja_file_is_directory
7386  *
7387  * Check if this file is a directory.
7388  * @file: CajaFile representing the file in question.
7389  *
7390  * Returns: TRUE if @file is a directory.
7391  *
7392  **/
7393 gboolean
caja_file_is_directory(CajaFile * file)7394 caja_file_is_directory (CajaFile *file)
7395 {
7396 	return caja_file_get_file_type (file) == G_FILE_TYPE_DIRECTORY;
7397 }
7398 
7399 /**
7400  * caja_file_is_user_special_directory
7401  *
7402  * Check if this file is a special platform directory.
7403  * @file: CajaFile representing the file in question.
7404  * @special_directory: GUserDirectory representing the type to test for
7405  *
7406  * Returns: TRUE if @file is a special directory of the given kind.
7407  */
7408 gboolean
caja_file_is_user_special_directory(CajaFile * file,GUserDirectory special_directory)7409 caja_file_is_user_special_directory (CajaFile *file,
7410 					 GUserDirectory special_directory)
7411 {
7412 	gboolean is_special_dir;
7413 	const gchar *special_dir;
7414 
7415 	special_dir = g_get_user_special_dir (special_directory);
7416 	is_special_dir = FALSE;
7417 
7418 	if (special_dir) {
7419 		GFile *loc;
7420 		GFile *special_gfile;
7421 
7422 		loc = caja_file_get_location (file);
7423 		special_gfile = g_file_new_for_path (special_dir);
7424 		is_special_dir = g_file_equal (loc, special_gfile);
7425 		g_object_unref (special_gfile);
7426 		g_object_unref (loc);
7427 	}
7428 
7429 	return is_special_dir;
7430 }
7431 
7432 gboolean
caja_file_is_archive(CajaFile * file)7433 caja_file_is_archive (CajaFile *file)
7434 {
7435 	char *mime_type;
7436 	int i;
7437 	static const char * archive_mime_types[] = { "application/x-gtar",
7438 						     "application/x-zip",
7439 						     "application/x-zip-compressed",
7440 						     "application/zip",
7441 						     "application/x-zip",
7442 						     "application/x-tar",
7443 						     "application/x-7z-compressed",
7444 						     "application/x-rar",
7445 						     "application/x-rar-compressed",
7446 						     "application/x-jar",
7447 						     "application/x-java-archive",
7448 						     "application/x-war",
7449 						     "application/x-ear",
7450 						     "application/x-arj",
7451 						     "application/x-gzip",
7452 						     "application/x-bzip-compressed-tar",
7453 						     "application/x-compressed-tar" };
7454 
7455 	g_return_val_if_fail (file != NULL, FALSE);
7456 
7457 	mime_type = caja_file_get_mime_type (file);
7458 	for (i = 0; i < G_N_ELEMENTS (archive_mime_types); i++) {
7459 		if (!strcmp (mime_type, archive_mime_types[i])) {
7460 			g_free (mime_type);
7461 			return TRUE;
7462 		}
7463 	}
7464 	g_free (mime_type);
7465 
7466 	return FALSE;
7467 }
7468 
7469 
7470 /**
7471  * caja_file_is_in_trash
7472  *
7473  * Check if this file is a file in trash.
7474  * @file: CajaFile representing the file in question.
7475  *
7476  * Returns: TRUE if @file is in a trash.
7477  *
7478  **/
7479 gboolean
caja_file_is_in_trash(CajaFile * file)7480 caja_file_is_in_trash (CajaFile *file)
7481 {
7482 	g_assert (CAJA_IS_FILE (file));
7483 
7484 	return caja_directory_is_in_trash (file->details->directory);
7485 }
7486 
7487 GError *
caja_file_get_file_info_error(CajaFile * file)7488 caja_file_get_file_info_error (CajaFile *file)
7489 {
7490 	if (!file->details->get_info_failed) {
7491 		return NULL;
7492 	}
7493 
7494 	return file->details->get_info_error;
7495 }
7496 
7497 /**
7498  * caja_file_contains_text
7499  *
7500  * Check if this file contains text.
7501  * This is private and is used to decide whether or not to read the top left text.
7502  * @file: CajaFile representing the file in question.
7503  *
7504  * Returns: TRUE if @file has a text MIME type.
7505  *
7506  **/
7507 gboolean
caja_file_contains_text(CajaFile * file)7508 caja_file_contains_text (CajaFile *file)
7509 {
7510 	if (file == NULL) {
7511 		return FALSE;
7512 	}
7513 
7514 	/* All text files inherit from text/plain */
7515 	return caja_file_is_mime_type (file, "text/plain");
7516 }
7517 
7518 /**
7519  * caja_file_is_binary
7520  *
7521  * Check if this file is a binary file.
7522  * This is private and is used to decide whether or not to show the diff
7523  * button in the file conflict dialog.
7524  * @file: CajaFile representing the file in question.
7525  *
7526  * Returns: TRUE if @file is a binary file.
7527  *
7528  **/
7529 gboolean
caja_file_is_binary(CajaFile * file)7530 caja_file_is_binary (CajaFile *file)
7531 {
7532 	if (!caja_file_can_read(file))
7533 	{
7534 		return FALSE;
7535 	}
7536 
7537 	gboolean is_binary = FALSE;
7538 	int i = 0;
7539 	FILE *fp;
7540 
7541 	/* Check the first 4096 bytes of the files. If these contains a 0,
7542 	 * we can assume the file is binary.
7543 	 * This idea is taken from python code of meld.
7544 	 */
7545 
7546 	fp = g_fopen (g_file_get_path (caja_file_get_location (file)), "r");
7547 	if (fp == NULL)
7548 	{
7549 		return FALSE;
7550 	}
7551 
7552 	while (!feof (fp)) {
7553 		int c;
7554 
7555 		if (i > 4096) {
7556 			break;
7557 		}
7558 		c = fgetc(fp);
7559 		if (c == 0) {
7560 			is_binary = TRUE;
7561 			break;
7562 		}
7563 		i++;
7564 	}
7565 	fclose(fp);
7566 
7567 	return is_binary;
7568 }
7569 
7570 /**
7571  * caja_file_is_executable
7572  *
7573  * Check if this file is executable at all.
7574  * @file: CajaFile representing the file in question.
7575  *
7576  * Returns: TRUE if any of the execute bits are set. FALSE if
7577  * not, or if the permissions are unknown.
7578  *
7579  **/
7580 gboolean
caja_file_is_executable(CajaFile * file)7581 caja_file_is_executable (CajaFile *file)
7582 {
7583 	if (!file->details->has_permissions) {
7584 		/* File's permissions field is not valid.
7585 		 * Can't access specific permissions, so return FALSE.
7586 		 */
7587 		return FALSE;
7588 	}
7589 
7590 	return file->details->can_execute;
7591 }
7592 
7593 /**
7594  * caja_file_peek_top_left_text
7595  *
7596  * Peek at the text from the top left of the file.
7597  * @file: CajaFile representing the file in question.
7598  *
7599  * Returns: NULL if there is no text readable, otherwise, the text.
7600  *          This string is owned by the file object and should not
7601  *          be kept around or freed.
7602  *
7603  **/
7604 char *
caja_file_peek_top_left_text(CajaFile * file,gboolean need_large_text,gboolean * needs_loading)7605 caja_file_peek_top_left_text (CajaFile *file,
7606 				  gboolean  need_large_text,
7607 				  gboolean *needs_loading)
7608 {
7609 	g_return_val_if_fail (CAJA_IS_FILE (file), NULL);
7610 
7611 	if (!caja_file_should_get_top_left_text (file)) {
7612 		if (needs_loading) {
7613 			*needs_loading = FALSE;
7614 		}
7615 		return NULL;
7616 	}
7617 
7618 	if (needs_loading) {
7619 		*needs_loading = !file->details->top_left_text_is_up_to_date;
7620 		if (need_large_text) {
7621 			*needs_loading |= file->details->got_top_left_text != file->details->got_large_top_left_text;
7622 		}
7623 	}
7624 
7625 	/* Show " ..." in the file until we read the contents in. */
7626 	if (!file->details->got_top_left_text) {
7627 
7628 		if (caja_file_contains_text (file)) {
7629 			return " ...";
7630 		}
7631 		return NULL;
7632 	}
7633 
7634 	/* Show what we read in. */
7635 	return file->details->top_left_text;
7636 }
7637 
7638 /**
7639  * caja_file_get_top_left_text
7640  *
7641  * Get the text from the top left of the file.
7642  * @file: CajaFile representing the file in question.
7643  *
7644  * Returns: NULL if there is no text readable, otherwise, the text.
7645  *
7646  **/
7647 char *
caja_file_get_top_left_text(CajaFile * file)7648 caja_file_get_top_left_text (CajaFile *file)
7649 {
7650 	return g_strdup (caja_file_peek_top_left_text (file, FALSE, NULL));
7651 }
7652 
7653 char *
caja_file_get_filesystem_id(CajaFile * file)7654 caja_file_get_filesystem_id (CajaFile *file)
7655 {
7656 	return g_strdup (file->details->filesystem_id);
7657 }
7658 
7659 CajaFile *
caja_file_get_trash_original_file(CajaFile * file)7660 caja_file_get_trash_original_file (CajaFile *file)
7661 {
7662 	CajaFile *original_file;
7663 
7664 	original_file = NULL;
7665 
7666 	if (file->details->trash_orig_path != NULL) {
7667 		GFile *location;
7668 
7669 		location = g_file_new_for_path (file->details->trash_orig_path);
7670 		original_file = caja_file_get (location);
7671 		g_object_unref (location);
7672 	}
7673 
7674 	return original_file;
7675 
7676 }
7677 
7678 void
caja_file_mark_gone(CajaFile * file)7679 caja_file_mark_gone (CajaFile *file)
7680 {
7681 	CajaDirectory *directory;
7682 
7683 	if (file->details->is_gone)
7684 		return;
7685 
7686 	file->details->is_gone = TRUE;
7687 
7688 	update_links_if_target (file);
7689 
7690 	/* Drop it from the symlink hash ! */
7691 	remove_from_link_hash_table (file);
7692 
7693 	/* Let the directory know it's gone. */
7694 	directory = file->details->directory;
7695 	if (!caja_file_is_self_owned (file)) {
7696 		caja_directory_remove_file (directory, file);
7697 	}
7698 
7699 	caja_file_clear_info (file);
7700 
7701 	/* FIXME bugzilla.gnome.org 42429:
7702 	 * Maybe we can get rid of the name too eventually, but
7703 	 * for now that would probably require too many if statements
7704 	 * everywhere anyone deals with the name. Maybe we can give it
7705 	 * a hard-coded "<deleted>" name or something.
7706 	 */
7707 }
7708 
7709 /**
7710  * caja_file_changed
7711  *
7712  * Notify the user that this file has changed.
7713  * @file: CajaFile representing the file in question.
7714  **/
7715 void
caja_file_changed(CajaFile * file)7716 caja_file_changed (CajaFile *file)
7717 {
7718 	GList fake_list;
7719 
7720 	g_return_if_fail (CAJA_IS_FILE (file));
7721 
7722 	if (caja_file_is_self_owned (file)) {
7723 		caja_file_emit_changed (file);
7724 	} else {
7725 		fake_list.data = file;
7726 		fake_list.next = NULL;
7727 		fake_list.prev = NULL;
7728 		caja_directory_emit_change_signals
7729 			(file->details->directory, &fake_list);
7730 	}
7731 }
7732 
7733 /**
7734  * caja_file_updated_deep_count_in_progress
7735  *
7736  * Notify clients that a newer deep count is available for
7737  * the directory in question.
7738  */
7739 void
caja_file_updated_deep_count_in_progress(CajaFile * file)7740 caja_file_updated_deep_count_in_progress (CajaFile *file) {
7741 	GList *link_files, *node;
7742 
7743 	g_assert (CAJA_IS_FILE (file));
7744 	g_assert (caja_file_is_directory (file));
7745 
7746 	/* Send out a signal. */
7747 	g_signal_emit (file, signals[UPDATED_DEEP_COUNT_IN_PROGRESS], 0, file);
7748 
7749 	/* Tell link files pointing to this object about the change. */
7750 	link_files = get_link_files (file);
7751 	for (node = link_files; node != NULL; node = node->next) {
7752 		caja_file_updated_deep_count_in_progress (CAJA_FILE (node->data));
7753 	}
7754 	caja_file_list_free (link_files);
7755 }
7756 
7757 /**
7758  * caja_file_emit_changed
7759  *
7760  * Emit a file changed signal.
7761  * This can only be called by the directory, since the directory
7762  * also has to emit a files_changed signal.
7763  *
7764  * @file: CajaFile representing the file in question.
7765  **/
7766 void
caja_file_emit_changed(CajaFile * file)7767 caja_file_emit_changed (CajaFile *file)
7768 {
7769 	GList *link_files, *p;
7770 
7771 	g_assert (CAJA_IS_FILE (file));
7772 
7773 
7774 	/* Invalidate the emblem compare cache. -- This is not the cleanest
7775 	 * place to do it but it is the one guaranteed bottleneck through
7776 	 * which all change notifications pass.
7777 	 */
7778 	g_free (file->details->compare_by_emblem_cache);
7779 	file->details->compare_by_emblem_cache = NULL;
7780 
7781 	/* Send out a signal. */
7782 	g_signal_emit (file, signals[CHANGED], 0, file);
7783 
7784 	/* Tell link files pointing to this object about the change. */
7785 	link_files = get_link_files (file);
7786 	for (p = link_files; p != NULL; p = p->next) {
7787 		if (p->data != file) {
7788 			caja_file_changed (CAJA_FILE (p->data));
7789 		}
7790 	}
7791 	caja_file_list_free (link_files);
7792 }
7793 
7794 /**
7795  * caja_file_is_gone
7796  *
7797  * Check if a file has already been deleted.
7798  * @file: CajaFile representing the file in question.
7799  *
7800  * Returns: TRUE if the file is already gone.
7801  **/
7802 gboolean
caja_file_is_gone(CajaFile * file)7803 caja_file_is_gone (CajaFile *file)
7804 {
7805 	g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
7806 
7807 	return file->details->is_gone;
7808 }
7809 
7810 /**
7811  * caja_file_is_not_yet_confirmed
7812  *
7813  * Check if we're in a state where we don't know if a file really
7814  * exists or not, before the initial I/O is complete.
7815  * @file: CajaFile representing the file in question.
7816  *
7817  * Returns: TRUE if the file is already gone.
7818  **/
7819 gboolean
caja_file_is_not_yet_confirmed(CajaFile * file)7820 caja_file_is_not_yet_confirmed (CajaFile *file)
7821 {
7822 	g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
7823 
7824 	return !file->details->got_file_info;
7825 }
7826 
7827 /**
7828  * caja_file_check_if_ready
7829  *
7830  * Check whether the values for a set of file attributes are
7831  * currently available, without doing any additional work. This
7832  * is useful for callers that want to reflect updated information
7833  * when it is ready but don't want to force the work required to
7834  * obtain the information, which might be slow network calls, e.g.
7835  *
7836  * @file: The file being queried.
7837  * @file_attributes: A bit-mask with the desired information.
7838  *
7839  * Return value: TRUE if all of the specified attributes are currently readable.
7840  */
7841 gboolean
caja_file_check_if_ready(CajaFile * file,CajaFileAttributes file_attributes)7842 caja_file_check_if_ready (CajaFile *file,
7843 			      CajaFileAttributes file_attributes)
7844 {
7845 	/* To be parallel with call_when_ready, return
7846 	 * TRUE for NULL file.
7847 	 */
7848 	if (file == NULL) {
7849 		return TRUE;
7850 	}
7851 
7852 	g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
7853 
7854 	return EEL_CALL_METHOD_WITH_RETURN_VALUE
7855 		(CAJA_FILE_CLASS, file,
7856 		 check_if_ready, (file, file_attributes));
7857 }
7858 
7859 void
caja_file_call_when_ready(CajaFile * file,CajaFileAttributes file_attributes,CajaFileCallback callback,gpointer callback_data)7860 caja_file_call_when_ready (CajaFile *file,
7861 			       CajaFileAttributes file_attributes,
7862 			       CajaFileCallback callback,
7863 			       gpointer callback_data)
7864 
7865 {
7866 	if (file == NULL) {
7867 		(* callback) (file, callback_data);
7868 		return;
7869 	}
7870 
7871 	g_return_if_fail (CAJA_IS_FILE (file));
7872 
7873 	EEL_CALL_METHOD
7874 		(CAJA_FILE_CLASS, file,
7875 		 call_when_ready, (file, file_attributes,
7876 				   callback, callback_data));
7877 }
7878 
7879 void
caja_file_cancel_call_when_ready(CajaFile * file,CajaFileCallback callback,gpointer callback_data)7880 caja_file_cancel_call_when_ready (CajaFile *file,
7881 				      CajaFileCallback callback,
7882 				      gpointer callback_data)
7883 {
7884 	g_return_if_fail (callback != NULL);
7885 
7886 	if (file == NULL) {
7887 		return;
7888 	}
7889 
7890 	g_return_if_fail (CAJA_IS_FILE (file));
7891 
7892 	EEL_CALL_METHOD
7893 		(CAJA_FILE_CLASS, file,
7894 		 cancel_call_when_ready, (file, callback, callback_data));
7895 }
7896 
7897 static void
invalidate_directory_count(CajaFile * file)7898 invalidate_directory_count (CajaFile *file)
7899 {
7900 	file->details->directory_count_is_up_to_date = FALSE;
7901 }
7902 
7903 static void
invalidate_deep_counts(CajaFile * file)7904 invalidate_deep_counts (CajaFile *file)
7905 {
7906 	file->details->deep_counts_status = CAJA_REQUEST_NOT_STARTED;
7907 }
7908 
7909 static void
invalidate_mime_list(CajaFile * file)7910 invalidate_mime_list (CajaFile *file)
7911 {
7912 	file->details->mime_list_is_up_to_date = FALSE;
7913 }
7914 
7915 static void
invalidate_top_left_text(CajaFile * file)7916 invalidate_top_left_text (CajaFile *file)
7917 {
7918 	file->details->top_left_text_is_up_to_date = FALSE;
7919 }
7920 
7921 static void
invalidate_file_info(CajaFile * file)7922 invalidate_file_info (CajaFile *file)
7923 {
7924 	file->details->file_info_is_up_to_date = FALSE;
7925 }
7926 
7927 static void
invalidate_link_info(CajaFile * file)7928 invalidate_link_info (CajaFile *file)
7929 {
7930 	file->details->link_info_is_up_to_date = FALSE;
7931 }
7932 
7933 static void
invalidate_thumbnail(CajaFile * file)7934 invalidate_thumbnail (CajaFile *file)
7935 {
7936 	file->details->thumbnail_is_up_to_date = FALSE;
7937 }
7938 
7939 static void
invalidate_mount(CajaFile * file)7940 invalidate_mount (CajaFile *file)
7941 {
7942 	file->details->mount_is_up_to_date = FALSE;
7943 }
7944 
7945 void
caja_file_invalidate_extension_info_internal(CajaFile * file)7946 caja_file_invalidate_extension_info_internal (CajaFile *file)
7947 {
7948 	if (file->details->pending_info_providers)
7949 		g_list_free_full (file->details->pending_info_providers, g_object_unref);
7950 
7951 	file->details->pending_info_providers =
7952 		caja_extensions_get_for_type (CAJA_TYPE_INFO_PROVIDER);
7953 }
7954 
7955 void
caja_file_invalidate_attributes_internal(CajaFile * file,CajaFileAttributes file_attributes)7956 caja_file_invalidate_attributes_internal (CajaFile *file,
7957 					      CajaFileAttributes file_attributes)
7958 {
7959 	Request request;
7960 
7961 	if (file == NULL) {
7962 		return;
7963 	}
7964 
7965 	if (CAJA_IS_DESKTOP_ICON_FILE (file)) {
7966 		/* Desktop icon files are always up to date.
7967 		 * If we invalidate their attributes they
7968 		 * will lose data, so we just ignore them.
7969 		 */
7970 		return;
7971 	}
7972 
7973 	request = caja_directory_set_up_request (file_attributes);
7974 
7975 	if (REQUEST_WANTS_TYPE (request, REQUEST_DIRECTORY_COUNT)) {
7976 		invalidate_directory_count (file);
7977 	}
7978 	if (REQUEST_WANTS_TYPE (request, REQUEST_DEEP_COUNT)) {
7979 		invalidate_deep_counts (file);
7980 	}
7981 	if (REQUEST_WANTS_TYPE (request, REQUEST_MIME_LIST)) {
7982 		invalidate_mime_list (file);
7983 	}
7984 	if (REQUEST_WANTS_TYPE (request, REQUEST_FILE_INFO)) {
7985 		invalidate_file_info (file);
7986 	}
7987 	if (REQUEST_WANTS_TYPE (request, REQUEST_TOP_LEFT_TEXT)) {
7988 		invalidate_top_left_text (file);
7989 	}
7990 	if (REQUEST_WANTS_TYPE (request, REQUEST_LINK_INFO)) {
7991 		invalidate_link_info (file);
7992 	}
7993 	if (REQUEST_WANTS_TYPE (request, REQUEST_EXTENSION_INFO)) {
7994 		caja_file_invalidate_extension_info_internal (file);
7995 	}
7996 	if (REQUEST_WANTS_TYPE (request, REQUEST_THUMBNAIL)) {
7997 		invalidate_thumbnail (file);
7998 	}
7999 	if (REQUEST_WANTS_TYPE (request, REQUEST_MOUNT)) {
8000 		invalidate_mount (file);
8001 	}
8002 
8003 	/* FIXME bugzilla.gnome.org 45075: implement invalidating metadata */
8004 }
8005 
8006 gboolean
caja_file_has_open_window(CajaFile * file)8007 caja_file_has_open_window (CajaFile *file)
8008 {
8009 	return file->details->has_open_window;
8010 }
8011 
8012 void
caja_file_set_has_open_window(CajaFile * file,gboolean has_open_window)8013 caja_file_set_has_open_window (CajaFile *file,
8014 				   gboolean has_open_window)
8015 {
8016 	has_open_window = (has_open_window != FALSE);
8017 
8018 	if (file->details->has_open_window != has_open_window) {
8019 		file->details->has_open_window = has_open_window;
8020 		caja_file_changed (file);
8021 	}
8022 }
8023 
8024 
8025 gboolean
caja_file_is_thumbnailing(CajaFile * file)8026 caja_file_is_thumbnailing (CajaFile *file)
8027 {
8028 	g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
8029 
8030 	return file->details->is_thumbnailing;
8031 }
8032 
8033 void
caja_file_set_is_thumbnailing(CajaFile * file,gboolean is_thumbnailing)8034 caja_file_set_is_thumbnailing (CajaFile *file,
8035 				   gboolean is_thumbnailing)
8036 {
8037 	g_return_if_fail (CAJA_IS_FILE (file));
8038 
8039 	file->details->is_thumbnailing = is_thumbnailing;
8040 }
8041 
8042 
8043 /**
8044  * caja_file_invalidate_attributes
8045  *
8046  * Invalidate the specified attributes and force a reload.
8047  * @file: CajaFile representing the file in question.
8048  * @file_attributes: attributes to froget.
8049  **/
8050 
8051 void
caja_file_invalidate_attributes(CajaFile * file,CajaFileAttributes file_attributes)8052 caja_file_invalidate_attributes (CajaFile *file,
8053 				     CajaFileAttributes file_attributes)
8054 {
8055 	/* Cancel possible in-progress loads of any of these attributes */
8056 	caja_directory_cancel_loading_file_attributes (file->details->directory,
8057 							   file,
8058 							   file_attributes);
8059 
8060 	/* Actually invalidate the values */
8061 	caja_file_invalidate_attributes_internal (file, file_attributes);
8062 
8063 	caja_directory_add_file_to_work_queue (file->details->directory, file);
8064 
8065 	/* Kick off I/O if necessary */
8066 	caja_directory_async_state_changed (file->details->directory);
8067 }
8068 
8069 CajaFileAttributes
caja_file_get_all_attributes(void)8070 caja_file_get_all_attributes (void)
8071 {
8072 	return  CAJA_FILE_ATTRIBUTE_INFO |
8073 		CAJA_FILE_ATTRIBUTE_LINK_INFO |
8074 		CAJA_FILE_ATTRIBUTE_DEEP_COUNTS |
8075 		CAJA_FILE_ATTRIBUTE_DIRECTORY_ITEM_COUNT |
8076 		CAJA_FILE_ATTRIBUTE_DIRECTORY_ITEM_MIME_TYPES |
8077 		CAJA_FILE_ATTRIBUTE_TOP_LEFT_TEXT |
8078 		CAJA_FILE_ATTRIBUTE_LARGE_TOP_LEFT_TEXT |
8079 		CAJA_FILE_ATTRIBUTE_EXTENSION_INFO |
8080 		CAJA_FILE_ATTRIBUTE_THUMBNAIL |
8081 		CAJA_FILE_ATTRIBUTE_MOUNT;
8082 }
8083 
8084 void
caja_file_invalidate_all_attributes(CajaFile * file)8085 caja_file_invalidate_all_attributes (CajaFile *file)
8086 {
8087 	CajaFileAttributes all_attributes;
8088 
8089 	all_attributes = caja_file_get_all_attributes ();
8090 	caja_file_invalidate_attributes (file, all_attributes);
8091 }
8092 
8093 
8094 /**
8095  * caja_file_dump
8096  *
8097  * Debugging call, prints out the contents of the file
8098  * fields.
8099  *
8100  * @file: file to dump.
8101  **/
8102 void
caja_file_dump(CajaFile * file)8103 caja_file_dump (CajaFile *file)
8104 {
8105 	long size = file->details->deep_size;
8106 	long size_on_disk = file->details->deep_size_on_disk;
8107 	char *uri;
8108 	const char *file_kind;
8109 
8110 	uri = caja_file_get_uri (file);
8111 	g_print ("uri: %s \n", uri);
8112 	if (!file->details->got_file_info) {
8113 		g_print ("no file info \n");
8114 	} else if (file->details->get_info_failed) {
8115 		g_print ("failed to get file info \n");
8116 	} else {
8117 		g_print ("size: %ld \n", size);
8118 		g_print ("disk size: %ld \n", size_on_disk);
8119 		switch (file->details->type) {
8120 		case G_FILE_TYPE_REGULAR:
8121 			file_kind = "regular file";
8122 			break;
8123 		case G_FILE_TYPE_DIRECTORY:
8124 			file_kind = "folder";
8125 			break;
8126 		case G_FILE_TYPE_SPECIAL:
8127 			file_kind = "special";
8128 			break;
8129 		case G_FILE_TYPE_SYMBOLIC_LINK:
8130 			file_kind = "symbolic link";
8131 			break;
8132 		case G_FILE_TYPE_UNKNOWN:
8133 		default:
8134 			file_kind = "unknown";
8135 			break;
8136 		}
8137 		g_print ("kind: %s \n", file_kind);
8138 		if (file->details->type == G_FILE_TYPE_SYMBOLIC_LINK) {
8139 			g_print ("link to %s \n", file->details->symlink_name);
8140 			/* FIXME bugzilla.gnome.org 42430: add following of symlinks here */
8141 		}
8142 		/* FIXME bugzilla.gnome.org 42431: add permissions and other useful stuff here */
8143 	}
8144 	g_free (uri);
8145 }
8146 
8147 /**
8148  * caja_file_list_ref
8149  *
8150  * Ref all the files in a list.
8151  * @list: GList of files.
8152  **/
8153 GList *
caja_file_list_ref(GList * list)8154 caja_file_list_ref (GList *list)
8155 {
8156 	g_list_foreach (list, (GFunc) caja_file_ref, NULL);
8157 	return list;
8158 }
8159 
8160 /**
8161  * caja_file_list_unref
8162  *
8163  * Unref all the files in a list.
8164  * @list: GList of files.
8165  **/
8166 void
caja_file_list_unref(GList * list)8167 caja_file_list_unref (GList *list)
8168 {
8169 	g_list_foreach (list, (GFunc) caja_file_unref, NULL);
8170 }
8171 
8172 /**
8173  * caja_file_list_free
8174  *
8175  * Free a list of files after unrefing them.
8176  * @list: GList of files.
8177  **/
8178 void
caja_file_list_free(GList * list)8179 caja_file_list_free (GList *list)
8180 {
8181 	caja_file_list_unref (list);
8182 	g_list_free (list);
8183 }
8184 
8185 /**
8186  * caja_file_list_copy
8187  *
8188  * Copy the list of files, making a new ref of each,
8189  * @list: GList of files.
8190  **/
8191 GList *
caja_file_list_copy(GList * list)8192 caja_file_list_copy (GList *list)
8193 {
8194 	return g_list_copy (caja_file_list_ref (list));
8195 }
8196 
8197 static gboolean
get_attributes_for_default_sort_type(CajaFile * file,gboolean * is_download,gboolean * is_trash)8198 get_attributes_for_default_sort_type (CajaFile *file,
8199 				      gboolean *is_download,
8200 				      gboolean *is_trash)
8201 {
8202 	gboolean is_download_dir, is_desktop_dir, is_trash_dir, retval;
8203 
8204 	*is_download = FALSE;
8205 	*is_trash = FALSE;
8206 	retval = FALSE;
8207 
8208 	/* special handling for certain directories */
8209 	if (file && caja_file_is_directory (file)) {
8210 		is_download_dir =
8211 			caja_file_is_user_special_directory (file, G_USER_DIRECTORY_DOWNLOAD);
8212 		is_desktop_dir =
8213 			caja_file_is_user_special_directory (file, G_USER_DIRECTORY_DESKTOP);
8214 		is_trash_dir =
8215 			caja_file_is_in_trash (file);
8216 
8217 		if (is_download_dir && !is_desktop_dir) {
8218 			*is_download = TRUE;
8219 			retval = TRUE;
8220 		} else if (is_trash_dir) {
8221 			*is_trash = TRUE;
8222 			retval = TRUE;
8223 		}
8224 	}
8225 
8226 	return retval;
8227 }
8228 
8229 CajaFileSortType
caja_file_get_default_sort_type(CajaFile * file,gboolean * reversed)8230 caja_file_get_default_sort_type (CajaFile *file,
8231 				     gboolean *reversed)
8232 {
8233 	CajaFileSortType retval;
8234 	gboolean is_download, is_trash, res;
8235 
8236 	retval = CAJA_FILE_SORT_NONE;
8237 	is_download = is_trash = FALSE;
8238 	res = get_attributes_for_default_sort_type (file, &is_download, &is_trash);
8239 
8240 	if (res) {
8241 		if (is_download) {
8242 			retval = CAJA_FILE_SORT_BY_MTIME;
8243 		} else if (is_trash) {
8244 			retval = CAJA_FILE_SORT_BY_TRASHED_TIME;
8245 		}
8246 
8247 		if (reversed != NULL) {
8248 			*reversed = res;
8249 		}
8250 	}
8251 
8252 	return retval;
8253 }
8254 
8255 const gchar *
caja_file_get_default_sort_attribute(CajaFile * file,gboolean * reversed)8256 caja_file_get_default_sort_attribute (CajaFile *file,
8257 					  gboolean *reversed)
8258 {
8259 	const gchar *retval;
8260 	gboolean is_download, is_trash, res;
8261 
8262 	retval = NULL;
8263 	is_download = is_trash = FALSE;
8264 	res = get_attributes_for_default_sort_type (file, &is_download, &is_trash);
8265 
8266 	if (res) {
8267 		if (is_download) {
8268 			retval = g_quark_to_string (attribute_date_modified_q);
8269 		} else if (is_trash) {
8270 			retval = g_quark_to_string (attribute_trashed_on_q);
8271 		}
8272 
8273 		if (reversed != NULL) {
8274 			*reversed = res;
8275 		}
8276 	}
8277 
8278 	return retval;
8279 }
8280 
8281 static int
compare_by_display_name_cover(gconstpointer a,gconstpointer b)8282 compare_by_display_name_cover (gconstpointer a, gconstpointer b)
8283 {
8284 	return compare_by_display_name (CAJA_FILE (a), CAJA_FILE (b));
8285 }
8286 
8287 /**
8288  * caja_file_list_sort_by_display_name
8289  *
8290  * Sort the list of files by file name.
8291  * @list: GList of files.
8292  **/
8293 GList *
caja_file_list_sort_by_display_name(GList * list)8294 caja_file_list_sort_by_display_name (GList *list)
8295 {
8296 	return g_list_sort (list, compare_by_display_name_cover);
8297 }
8298 
8299 static GList *ready_data_list = NULL;
8300 
8301 typedef struct
8302 {
8303 	GList *file_list;
8304 	GList *remaining_files;
8305 	CajaFileListCallback callback;
8306 	gpointer callback_data;
8307 } FileListReadyData;
8308 
8309 static void
file_list_ready_data_free(FileListReadyData * data)8310 file_list_ready_data_free (FileListReadyData *data)
8311 {
8312 	GList *l;
8313 
8314 	l = g_list_find (ready_data_list, data);
8315 	if (l != NULL) {
8316 		ready_data_list = g_list_delete_link (ready_data_list, l);
8317 
8318 		caja_file_list_free (data->file_list);
8319 		g_list_free (data->remaining_files);
8320 		g_free (data);
8321 	}
8322 }
8323 
8324 static FileListReadyData *
file_list_ready_data_new(GList * file_list,CajaFileListCallback callback,gpointer callback_data)8325 file_list_ready_data_new (GList *file_list,
8326 			  CajaFileListCallback callback,
8327 			  gpointer callback_data)
8328 {
8329 	FileListReadyData *data;
8330 
8331 	data = g_new0 (FileListReadyData, 1);
8332 	data->file_list = caja_file_list_copy (file_list);
8333 	data->remaining_files = g_list_copy (file_list);
8334 	data->callback = callback;
8335 	data->callback_data = callback_data;
8336 
8337 	ready_data_list = g_list_prepend (ready_data_list, data);
8338 
8339 	return data;
8340 }
8341 
8342 static void
file_list_file_ready_callback(CajaFile * file,gpointer user_data)8343 file_list_file_ready_callback (CajaFile *file,
8344 			       gpointer user_data)
8345 {
8346 	FileListReadyData *data;
8347 
8348 	data = user_data;
8349 	data->remaining_files = g_list_remove (data->remaining_files, file);
8350 
8351 	if (data->remaining_files == NULL) {
8352 		if (data->callback) {
8353 			(*data->callback) (data->file_list, data->callback_data);
8354 		}
8355 
8356 		file_list_ready_data_free (data);
8357 	}
8358 }
8359 
8360 void
caja_file_list_call_when_ready(GList * file_list,CajaFileAttributes attributes,CajaFileListHandle ** handle,CajaFileListCallback callback,gpointer callback_data)8361 caja_file_list_call_when_ready (GList *file_list,
8362                                 CajaFileAttributes attributes,
8363                                 CajaFileListHandle **handle,
8364                                 CajaFileListCallback callback,
8365                                 gpointer callback_data)
8366 {
8367 	GList *l;
8368 	FileListReadyData *data;
8369 	CajaFile *file = NULL;
8370 
8371 	g_return_if_fail (file_list != NULL);
8372 
8373 	data = file_list_ready_data_new
8374 		(file_list, callback, callback_data);
8375 
8376 	if (handle) {
8377 		*handle = (CajaFileListHandle *) data;
8378 	}
8379 
8380 
8381 	l = file_list;
8382 	while (l != NULL) {
8383 		file = CAJA_FILE (l->data);
8384 		/* Need to do this here, as the list can be modified by this call */
8385 		l = l->next;
8386 
8387 		if (file)
8388 			caja_file_call_when_ready (file,
8389 						   attributes,
8390 						   file_list_file_ready_callback,
8391 						   data);
8392 	}
8393 }
8394 
8395 void
caja_file_list_cancel_call_when_ready(CajaFileListHandle * handle)8396 caja_file_list_cancel_call_when_ready (CajaFileListHandle *handle)
8397 {
8398 	GList *l;
8399 	FileListReadyData *data;
8400 
8401 	g_return_if_fail (handle != NULL);
8402 
8403 	data = (FileListReadyData *) handle;
8404 
8405 	l = g_list_find (ready_data_list, data);
8406 	if (l != NULL) {
8407 		CajaFile *file = NULL;
8408 
8409 		for (l = data->remaining_files; l != NULL; l = l->next) {
8410 			file = CAJA_FILE (l->data);
8411 
8412 			EEL_CALL_METHOD
8413 				(CAJA_FILE_CLASS, file,
8414 				 cancel_call_when_ready, (file, file_list_file_ready_callback, data));
8415 		}
8416 
8417 		file_list_ready_data_free (data);
8418 	}
8419 }
8420 
8421 static char *
try_to_make_utf8(const char * text,int * length)8422 try_to_make_utf8 (const char *text, int *length)
8423 {
8424 	static const char *encodings_to_try[2];
8425 	static int n_encodings_to_try = 0;
8426         gsize converted_length;
8427         GError *conversion_error;
8428 	char *utf8_text;
8429 	int i;
8430 
8431 	if (n_encodings_to_try == 0) {
8432 		const char *charset;
8433 		gboolean charset_is_utf8;
8434 
8435 		charset_is_utf8 = g_get_charset (&charset);
8436 		if (!charset_is_utf8) {
8437 			encodings_to_try[n_encodings_to_try++] = charset;
8438 		}
8439 
8440 		if (g_ascii_strcasecmp (charset, "ISO-8859-1") != 0) {
8441 			encodings_to_try[n_encodings_to_try++] = "ISO-8859-1";
8442 		}
8443 	}
8444 
8445         utf8_text = NULL;
8446 	for (i = 0; i < n_encodings_to_try; i++) {
8447 		conversion_error = NULL;
8448 		utf8_text = g_convert (text, *length,
8449 					   "UTF-8", encodings_to_try[i],
8450 					   NULL, &converted_length, &conversion_error);
8451 		if (utf8_text != NULL) {
8452 			*length = converted_length;
8453 			break;
8454 		}
8455 		g_error_free (conversion_error);
8456 	}
8457 
8458 	return utf8_text;
8459 }
8460 
8461 
8462 
8463 /* Extract the top left part of the read-in text. */
8464 char *
caja_extract_top_left_text(const char * text,gboolean large,int length)8465 caja_extract_top_left_text (const char *text,
8466 				gboolean large,
8467 				int length)
8468 {
8469         GString* buffer;
8470 	const gchar *in;
8471 	const gchar *end;
8472 	int line, i;
8473 	gunichar c;
8474 	char *text_copy;
8475 	const char *utf8_end;
8476 	gboolean validated;
8477 	int max_bytes, max_lines, max_cols;
8478 
8479 	if (large) {
8480 		max_bytes = CAJA_FILE_LARGE_TOP_LEFT_TEXT_MAXIMUM_BYTES;
8481 		max_lines = CAJA_FILE_LARGE_TOP_LEFT_TEXT_MAXIMUM_LINES;
8482 		max_cols = CAJA_FILE_LARGE_TOP_LEFT_TEXT_MAXIMUM_CHARACTERS_PER_LINE;
8483 	} else {
8484 		max_bytes = CAJA_FILE_TOP_LEFT_TEXT_MAXIMUM_BYTES;
8485 		max_lines = CAJA_FILE_TOP_LEFT_TEXT_MAXIMUM_LINES;
8486 		max_cols = CAJA_FILE_TOP_LEFT_TEXT_MAXIMUM_CHARACTERS_PER_LINE;
8487 	}
8488 
8489 
8490 
8491         text_copy = NULL;
8492         if (text != NULL) {
8493 		/* Might be a partial utf8 character at the end if we didn't read whole file */
8494 		validated = g_utf8_validate (text, length, &utf8_end);
8495 		if (!validated &&
8496 		    !(length >= max_bytes &&
8497 		      text + length - utf8_end < 6)) {
8498 			text_copy = try_to_make_utf8 (text, &length);
8499 			text = text_copy;
8500 		} else if (!validated) {
8501 			length = utf8_end - text;
8502 		}
8503         }
8504 
8505 	if (text == NULL || length == 0) {
8506 		return NULL;
8507 	}
8508 
8509 	buffer = g_string_new ("");
8510 	end = text + length; in = text;
8511 
8512 	for (line = 0; line < max_lines; line++) {
8513 		/* Extract one line. */
8514 		for (i = 0; i < max_cols; ) {
8515 			if (*in == '\n') {
8516 				break;
8517 			}
8518 
8519 			c = g_utf8_get_char (in);
8520 
8521 			if (g_unichar_isprint (c)) {
8522 				g_string_append_unichar (buffer, c);
8523 				i++;
8524 			}
8525 
8526 			in = g_utf8_next_char (in);
8527 			if (in == end) {
8528 				goto done;
8529 			}
8530 		}
8531 
8532 		/* Skip the rest of the line. */
8533 		while (*in != '\n') {
8534 			if (++in == end) {
8535 				goto done;
8536 			}
8537 		}
8538 		if (++in == end) {
8539 			goto done;
8540 		}
8541 
8542 		/* Put a new-line separator in. */
8543 		g_string_append_c(buffer, '\n');
8544 	}
8545  done:
8546 	g_free (text_copy);
8547 
8548 	return g_string_free(buffer, FALSE);
8549 }
8550 
8551 static void
thumbnail_limit_changed_callback(gpointer user_data)8552 thumbnail_limit_changed_callback (gpointer user_data)
8553 {
8554 	g_settings_get (caja_preferences,
8555 					CAJA_PREFERENCES_IMAGE_FILE_THUMBNAIL_LIMIT,
8556 					"t", &cached_thumbnail_limit);
8557 
8558 	/* Tell the world that icons might have changed. We could invent a narrower-scope
8559 	 * signal to mean only "thumbnails might have changed" if this ends up being slow
8560 	 * for some reason.
8561 	 */
8562 	emit_change_signals_for_all_files_in_all_directories ();
8563 }
8564 
8565 static void
thumbnail_size_changed_callback(gpointer user_data)8566 thumbnail_size_changed_callback (gpointer user_data)
8567 {
8568 	cached_thumbnail_size = g_settings_get_int (caja_icon_view_preferences, CAJA_PREFERENCES_ICON_VIEW_THUMBNAIL_SIZE);
8569 
8570 	/* Tell the world that icons might have changed. We could invent a narrower-scope
8571 	 * signal to mean only "thumbnails might have changed" if this ends up being slow
8572 	 * for some reason.
8573 	 */
8574 	emit_change_signals_for_all_files_in_all_directories ();
8575 }
8576 
8577 static void
show_thumbnails_changed_callback(gpointer user_data)8578 show_thumbnails_changed_callback (gpointer user_data)
8579 {
8580 	show_image_thumbs = g_settings_get_enum (caja_preferences, CAJA_PREFERENCES_SHOW_IMAGE_FILE_THUMBNAILS);
8581 
8582 	/* Tell the world that icons might have changed. We could invent a narrower-scope
8583 	 * signal to mean only "thumbnails might have changed" if this ends up being slow
8584 	 * for some reason.
8585 	 */
8586 	emit_change_signals_for_all_files_in_all_directories ();
8587 }
8588 
8589 static void
mime_type_data_changed_callback(GObject * signaller,gpointer user_data)8590 mime_type_data_changed_callback (GObject *signaller, gpointer user_data)
8591 {
8592 	/* Tell the world that icons might have changed. We could invent a narrower-scope
8593 	 * signal to mean only "thumbnails might have changed" if this ends up being slow
8594 	 * for some reason.
8595 	 */
8596 	emit_change_signals_for_all_files_in_all_directories ();
8597 }
8598 
8599 static void
icon_theme_changed_callback(GtkIconTheme * icon_theme,gpointer user_data)8600 icon_theme_changed_callback (GtkIconTheme *icon_theme,
8601 			     gpointer user_data)
8602 {
8603 	/* Clear all pixmap caches as the icon => pixmap lookup changed */
8604 	caja_icon_info_clear_caches ();
8605 
8606 	/* Tell the world that icons might have changed. We could invent a narrower-scope
8607 	 * signal to mean only "thumbnails might have changed" if this ends up being slow
8608 	 * for some reason.
8609 	 */
8610 	emit_change_signals_for_all_files_in_all_directories ();
8611 }
8612 
8613 static void
caja_file_class_init(CajaFileClass * class)8614 caja_file_class_init (CajaFileClass *class)
8615 {
8616 	GtkIconTheme *icon_theme;
8617 
8618 	caja_file_info_getter = caja_file_get_internal;
8619 
8620 	attribute_name_q = g_quark_from_static_string ("name");
8621 	attribute_size_q = g_quark_from_static_string ("size");
8622 	attribute_size_on_disk_q = g_quark_from_static_string ("size_on_disk");
8623 	attribute_type_q = g_quark_from_static_string ("type");
8624 	attribute_modification_date_q = g_quark_from_static_string ("modification_date");
8625 	attribute_date_modified_q = g_quark_from_static_string ("date_modified");
8626 	attribute_creation_date_q = g_quark_from_static_string ("creation_date");
8627 	attribute_date_created_q = g_quark_from_static_string ("date_created");
8628 	attribute_accessed_date_q = g_quark_from_static_string ("accessed_date");
8629 	attribute_date_accessed_q = g_quark_from_static_string ("date_accessed");
8630 	attribute_emblems_q = g_quark_from_static_string ("emblems");
8631 	attribute_extension_q = g_quark_from_static_string ("extension");
8632 	attribute_mime_type_q = g_quark_from_static_string ("mime_type");
8633 	attribute_size_detail_q = g_quark_from_static_string ("size_detail");
8634 	attribute_size_on_disk_detail_q = g_quark_from_static_string ("size_on_disk_detail");
8635 	attribute_deep_size_q = g_quark_from_static_string ("deep_size");
8636 	attribute_deep_size_on_disk_q = g_quark_from_static_string ("deep_size_on_disk");
8637 	attribute_deep_file_count_q = g_quark_from_static_string ("deep_file_count");
8638 	attribute_deep_directory_count_q = g_quark_from_static_string ("deep_directory_count");
8639 	attribute_deep_total_count_q = g_quark_from_static_string ("deep_total_count");
8640 	attribute_date_changed_q = g_quark_from_static_string ("date_changed");
8641 	attribute_trashed_on_q = g_quark_from_static_string ("trashed_on");
8642 	attribute_trash_orig_path_q = g_quark_from_static_string ("trash_orig_path");
8643 	attribute_date_permissions_q = g_quark_from_static_string ("date_permissions");
8644 	attribute_permissions_q = g_quark_from_static_string ("permissions");
8645 	attribute_selinux_context_q = g_quark_from_static_string ("selinux_context");
8646 	attribute_octal_permissions_q = g_quark_from_static_string ("octal_permissions");
8647 	attribute_owner_q = g_quark_from_static_string ("owner");
8648 	attribute_group_q = g_quark_from_static_string ("group");
8649 	attribute_uri_q = g_quark_from_static_string ("uri");
8650 	attribute_where_q = g_quark_from_static_string ("where");
8651 	attribute_link_target_q = g_quark_from_static_string ("link_target");
8652 	attribute_volume_q = g_quark_from_static_string ("volume");
8653 	attribute_free_space_q = g_quark_from_static_string ("free_space");
8654 
8655 	G_OBJECT_CLASS (class)->finalize = finalize;
8656 	G_OBJECT_CLASS (class)->constructor = caja_file_constructor;
8657 
8658 	signals[CHANGED] =
8659 		g_signal_new ("changed",
8660 		              G_TYPE_FROM_CLASS (class),
8661 		              G_SIGNAL_RUN_LAST,
8662 		              G_STRUCT_OFFSET (CajaFileClass, changed),
8663 		              NULL, NULL,
8664 		              g_cclosure_marshal_VOID__VOID,
8665 		              G_TYPE_NONE, 0);
8666 
8667 	signals[UPDATED_DEEP_COUNT_IN_PROGRESS] =
8668 		g_signal_new ("updated_deep_count_in_progress",
8669 		              G_TYPE_FROM_CLASS (class),
8670 		              G_SIGNAL_RUN_LAST,
8671 		              G_STRUCT_OFFSET (CajaFileClass, updated_deep_count_in_progress),
8672 		              NULL, NULL,
8673 			      g_cclosure_marshal_VOID__VOID,
8674 		              G_TYPE_NONE, 0);
8675 
8676 	eel_g_settings_add_auto_enum (caja_preferences,
8677 				                  CAJA_PREFERENCES_DATE_FORMAT,
8678 				                  &date_format_pref);
8679 
8680 	thumbnail_limit_changed_callback (NULL);
8681 	g_signal_connect_swapped (caja_preferences,
8682 							  "changed::" CAJA_PREFERENCES_IMAGE_FILE_THUMBNAIL_LIMIT,
8683 							  G_CALLBACK (thumbnail_limit_changed_callback),
8684 							  NULL);
8685 	thumbnail_size_changed_callback (NULL);
8686 	g_signal_connect_swapped (caja_icon_view_preferences,
8687 							  "changed::" CAJA_PREFERENCES_ICON_VIEW_THUMBNAIL_SIZE,
8688 							  G_CALLBACK (thumbnail_size_changed_callback),
8689 							  NULL);
8690 	show_thumbnails_changed_callback (NULL);
8691 	g_signal_connect_swapped (caja_preferences,
8692 							  "changed::" CAJA_PREFERENCES_SHOW_IMAGE_FILE_THUMBNAILS,
8693 							  G_CALLBACK (show_thumbnails_changed_callback),
8694 							  NULL);
8695 
8696 	icon_theme = gtk_icon_theme_get_default ();
8697 	g_signal_connect_object (icon_theme,
8698 				 "changed",
8699 				 G_CALLBACK (icon_theme_changed_callback),
8700 				 NULL, 0);
8701 
8702 	g_signal_connect (caja_signaller_get_current (),
8703 			  "mime_data_changed",
8704 			  G_CALLBACK (mime_type_data_changed_callback),
8705 			  NULL);
8706 }
8707 
8708 static void
caja_file_add_emblem(CajaFile * file,const char * emblem_name)8709 caja_file_add_emblem (CajaFile *file,
8710 			  const char *emblem_name)
8711 {
8712 	if (file->details->pending_info_providers) {
8713 		file->details->pending_extension_emblems = g_list_prepend (file->details->pending_extension_emblems,
8714 									   g_strdup (emblem_name));
8715 	} else {
8716 		file->details->extension_emblems = g_list_prepend (file->details->extension_emblems,
8717 								   g_strdup (emblem_name));
8718 	}
8719 
8720 	caja_file_changed (file);
8721 }
8722 
8723 static void
caja_file_add_string_attribute(CajaFile * file,const char * attribute_name,const char * value)8724 caja_file_add_string_attribute (CajaFile *file,
8725 				    const char *attribute_name,
8726 				    const char *value)
8727 {
8728 	if (file->details->pending_info_providers) {
8729 		/* Lazily create hashtable */
8730 		if (!file->details->pending_extension_attributes) {
8731 			file->details->pending_extension_attributes =
8732 				g_hash_table_new_full (g_direct_hash, g_direct_equal,
8733 						       NULL,
8734 						       (GDestroyNotify)g_free);
8735 		}
8736 		g_hash_table_insert (file->details->pending_extension_attributes,
8737 				     GINT_TO_POINTER (g_quark_from_string (attribute_name)),
8738 				     g_strdup (value));
8739 	} else {
8740 		if (!file->details->extension_attributes) {
8741 			file->details->extension_attributes =
8742 				g_hash_table_new_full (g_direct_hash, g_direct_equal,
8743 						       NULL,
8744 						       (GDestroyNotify)g_free);
8745 		}
8746 		g_hash_table_insert (file->details->extension_attributes,
8747 				     GINT_TO_POINTER (g_quark_from_string (attribute_name)),
8748 				     g_strdup (value));
8749 	}
8750 
8751 	caja_file_changed (file);
8752 }
8753 
8754 static void
caja_file_invalidate_extension_info(CajaFile * file)8755 caja_file_invalidate_extension_info (CajaFile *file)
8756 {
8757 	caja_file_invalidate_attributes (file, CAJA_FILE_ATTRIBUTE_EXTENSION_INFO);
8758 }
8759 
8760 void
caja_file_info_providers_done(CajaFile * file)8761 caja_file_info_providers_done (CajaFile *file)
8762 {
8763 	g_list_free_full (file->details->extension_emblems, g_free);
8764 	file->details->extension_emblems = file->details->pending_extension_emblems;
8765 	file->details->pending_extension_emblems = NULL;
8766 
8767 	if (file->details->extension_attributes) {
8768 		g_hash_table_destroy (file->details->extension_attributes);
8769 	}
8770 
8771 	file->details->extension_attributes = file->details->pending_extension_attributes;
8772 	file->details->pending_extension_attributes = NULL;
8773 
8774 	caja_file_changed (file);
8775 }
8776 
8777 static void
caja_file_info_iface_init(CajaFileInfoIface * iface)8778 caja_file_info_iface_init (CajaFileInfoIface *iface)
8779 {
8780 	iface->is_gone = caja_file_is_gone;
8781 	iface->get_name = caja_file_get_name;
8782 	iface->get_file_type = caja_file_get_file_type;
8783 	iface->get_location = caja_file_get_location;
8784 	iface->get_uri = caja_file_get_uri;
8785 	iface->get_parent_location = caja_file_get_parent_location;
8786 	iface->get_parent_uri = caja_file_get_parent_uri;
8787 	iface->get_parent_info = caja_file_get_parent;
8788 	iface->get_mount = caja_file_get_mount;
8789 	iface->get_uri_scheme = caja_file_get_uri_scheme;
8790 	iface->get_activation_uri = caja_file_get_activation_uri;
8791 	iface->get_mime_type = caja_file_get_mime_type;
8792 	iface->is_mime_type = caja_file_is_mime_type;
8793 	iface->is_directory = caja_file_is_directory;
8794 	iface->can_write = caja_file_can_write;
8795 	iface->add_emblem = caja_file_add_emblem;
8796 	iface->get_string_attribute = caja_file_get_string_attribute;
8797 	iface->add_string_attribute = caja_file_add_string_attribute;
8798 	iface->invalidate_extension_info = caja_file_invalidate_extension_info;
8799 }
8800 
8801 #if !defined (CAJA_OMIT_SELF_CHECK)
8802 
8803 void
caja_self_check_file(void)8804 caja_self_check_file (void)
8805 {
8806 	CajaFile *file_1;
8807 	CajaFile *file_2;
8808 	GList *list;
8809 
8810         /* refcount checks */
8811 
8812         EEL_CHECK_INTEGER_RESULT (caja_directory_number_outstanding (), 0);
8813 
8814 	file_1 = caja_file_get_by_uri ("file:///home/");
8815 
8816 	EEL_CHECK_INTEGER_RESULT (G_OBJECT (file_1)->ref_count, 1);
8817 	EEL_CHECK_INTEGER_RESULT (G_OBJECT (file_1->details->directory)->ref_count, 1);
8818         EEL_CHECK_INTEGER_RESULT (caja_directory_number_outstanding (), 1);
8819 
8820 	caja_file_unref (file_1);
8821 
8822         EEL_CHECK_INTEGER_RESULT (caja_directory_number_outstanding (), 0);
8823 
8824 	file_1 = caja_file_get_by_uri ("file:///etc");
8825 	file_2 = caja_file_get_by_uri ("file:///usr");
8826 
8827         list = NULL;
8828         list = g_list_prepend (list, file_1);
8829         list = g_list_prepend (list, file_2);
8830 
8831         caja_file_list_ref (list);
8832 
8833 	EEL_CHECK_INTEGER_RESULT (G_OBJECT (file_1)->ref_count, 2);
8834 	EEL_CHECK_INTEGER_RESULT (G_OBJECT (file_2)->ref_count, 2);
8835 
8836 	caja_file_list_unref (list);
8837 
8838 	EEL_CHECK_INTEGER_RESULT (G_OBJECT (file_1)->ref_count, 1);
8839 	EEL_CHECK_INTEGER_RESULT (G_OBJECT (file_2)->ref_count, 1);
8840 
8841 	caja_file_list_free (list);
8842 
8843         EEL_CHECK_INTEGER_RESULT (caja_directory_number_outstanding (), 0);
8844 
8845 
8846         /* name checks */
8847 	file_1 = caja_file_get_by_uri ("file:///home/");
8848 
8849 	EEL_CHECK_STRING_RESULT (caja_file_get_name (file_1), "home");
8850 
8851 	EEL_CHECK_BOOLEAN_RESULT (caja_file_get_by_uri ("file:///home/") == file_1, TRUE);
8852 	caja_file_unref (file_1);
8853 
8854 	EEL_CHECK_BOOLEAN_RESULT (caja_file_get_by_uri ("file:///home") == file_1, TRUE);
8855 	caja_file_unref (file_1);
8856 
8857 	caja_file_unref (file_1);
8858 
8859 	file_1 = caja_file_get_by_uri ("file:///home");
8860 	EEL_CHECK_STRING_RESULT (caja_file_get_name (file_1), "home");
8861 	caja_file_unref (file_1);
8862 
8863 #if 0
8864 	/* ALEX: I removed this, because it was breaking distchecks.
8865 	 * It used to work, but when canonical uris changed from
8866 	 * foo: to foo:/// it broke. I don't expect it to matter
8867 	 * in real life */
8868 	file_1 = caja_file_get_by_uri (":");
8869 	EEL_CHECK_STRING_RESULT (caja_file_get_name (file_1), ":");
8870 	caja_file_unref (file_1);
8871 #endif
8872 
8873 	file_1 = caja_file_get_by_uri ("eazel:");
8874 	EEL_CHECK_STRING_RESULT (caja_file_get_name (file_1), "eazel");
8875 	caja_file_unref (file_1);
8876 
8877 	/* sorting */
8878 	file_1 = caja_file_get_by_uri ("file:///etc");
8879 	file_2 = caja_file_get_by_uri ("file:///usr");
8880 
8881 	EEL_CHECK_INTEGER_RESULT (G_OBJECT (file_1)->ref_count, 1);
8882 	EEL_CHECK_INTEGER_RESULT (G_OBJECT (file_2)->ref_count, 1);
8883 
8884 	EEL_CHECK_BOOLEAN_RESULT (caja_file_compare_for_sort (file_1, file_2, CAJA_FILE_SORT_BY_DISPLAY_NAME, FALSE, FALSE) < 0, TRUE);
8885 	EEL_CHECK_BOOLEAN_RESULT (caja_file_compare_for_sort (file_1, file_2, CAJA_FILE_SORT_BY_DISPLAY_NAME, FALSE, TRUE) > 0, TRUE);
8886 	EEL_CHECK_BOOLEAN_RESULT (caja_file_compare_for_sort (file_1, file_1, CAJA_FILE_SORT_BY_DISPLAY_NAME, FALSE, FALSE) == 0, TRUE);
8887 	EEL_CHECK_BOOLEAN_RESULT (caja_file_compare_for_sort (file_1, file_1, CAJA_FILE_SORT_BY_DISPLAY_NAME, TRUE, FALSE) == 0, TRUE);
8888 	EEL_CHECK_BOOLEAN_RESULT (caja_file_compare_for_sort (file_1, file_1, CAJA_FILE_SORT_BY_DISPLAY_NAME, FALSE, TRUE) == 0, TRUE);
8889 	EEL_CHECK_BOOLEAN_RESULT (caja_file_compare_for_sort (file_1, file_1, CAJA_FILE_SORT_BY_DISPLAY_NAME, TRUE, TRUE) == 0, TRUE);
8890 
8891 	caja_file_unref (file_1);
8892 	caja_file_unref (file_2);
8893 }
8894 
8895 #endif /* !CAJA_OMIT_SELF_CHECK */
8896