1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
2 
3    caja-directory-async.c: Caja directory model state machine.
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 <gtk/gtk.h>
27 #include <libxml/parser.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 
31 #include <eel/eel-glib-extensions.h>
32 
33 #include "caja-directory-notify.h"
34 #include "caja-directory-private.h"
35 #include "caja-file-attributes.h"
36 #include "caja-file-private.h"
37 #include "caja-file-utilities.h"
38 #include "caja-metadata.h"
39 #include "caja-signaller.h"
40 #include "caja-global-preferences.h"
41 #include "caja-link.h"
42 #include "caja-marshal.h"
43 
44 /* turn this on to see messages about each load_directory call: */
45 #if 0
46 #define DEBUG_LOAD_DIRECTORY
47 #endif
48 
49 /* turn this on to check if async. job calls are balanced */
50 #if 0
51 #define DEBUG_ASYNC_JOBS
52 #endif
53 
54 /* turn this on to log things starting and stopping */
55 #if 0
56 #define DEBUG_START_STOP
57 #endif
58 
59 #define DIRECTORY_LOAD_ITEMS_PER_CALLBACK 100
60 
61 /* Keep async. jobs down to this number for all directories. */
62 #define MAX_ASYNC_JOBS 10
63 
64 struct TopLeftTextReadState
65 {
66     CajaDirectory *directory;
67     CajaFile *file;
68     gboolean large;
69     GCancellable *cancellable;
70 };
71 
72 struct LinkInfoReadState
73 {
74     CajaDirectory *directory;
75     GCancellable *cancellable;
76     CajaFile *file;
77 };
78 
79 struct ThumbnailState
80 {
81     CajaDirectory *directory;
82     GCancellable *cancellable;
83     CajaFile *file;
84     gboolean trying_original;
85     gboolean tried_original;
86 };
87 
88 struct MountState
89 {
90     CajaDirectory *directory;
91     GCancellable *cancellable;
92     CajaFile *file;
93 };
94 
95 struct FilesystemInfoState
96 {
97     CajaDirectory *directory;
98     GCancellable *cancellable;
99     CajaFile *file;
100 };
101 
102 struct DirectoryLoadState
103 {
104     CajaDirectory *directory;
105     GCancellable *cancellable;
106     GFileEnumerator *enumerator;
107     GHashTable *load_mime_list_hash;
108     CajaFile *load_directory_file;
109     int load_file_count;
110 };
111 
112 struct MimeListState
113 {
114     CajaDirectory *directory;
115     CajaFile *mime_list_file;
116     GCancellable *cancellable;
117     GFileEnumerator *enumerator;
118     GHashTable *mime_list_hash;
119 };
120 
121 struct GetInfoState
122 {
123     CajaDirectory *directory;
124     GCancellable *cancellable;
125 };
126 
127 struct NewFilesState
128 {
129     CajaDirectory *directory;
130     GCancellable *cancellable;
131     int count;
132 };
133 
134 struct DirectoryCountState
135 {
136     CajaDirectory *directory;
137     CajaFile *count_file;
138     GCancellable *cancellable;
139     GFileEnumerator *enumerator;
140     int file_count;
141 };
142 
143 struct DeepCountState
144 {
145     CajaDirectory *directory;
146     GCancellable *cancellable;
147     GFileEnumerator *enumerator;
148     GFile *deep_count_location;
149     GList *deep_count_subdirectories;
150     GArray *seen_deep_count_inodes;
151     char *fs_id;
152 };
153 
154 
155 
156 typedef struct
157 {
158     CajaFile *file; /* Which file, NULL means all. */
159     union
160     {
161         CajaDirectoryCallback directory;
162         CajaFileCallback file;
163     } callback;
164     gpointer callback_data;
165     Request request;
166     gboolean active; /* Set to FALSE when the callback is triggered and
167 			  * scheduled to be called at idle, its still kept
168 			  * in the list so we can kill it when the file
169 			  * goes away.
170 			  */
171 } ReadyCallback;
172 
173 typedef struct
174 {
175     CajaFile *file; /* Which file, NULL means all. */
176     gboolean monitor_hidden_files; /* defines whether "all" includes hidden files */
177     gconstpointer client;
178     Request request;
179 } Monitor;
180 
181 typedef struct
182 {
183     CajaDirectory *directory;
184     CajaInfoProvider *provider;
185     CajaOperationHandle *handle;
186     CajaOperationResult result;
187 } InfoProviderResponse;
188 
189 typedef gboolean (* RequestCheck) (Request);
190 typedef gboolean (* FileCheck) (CajaFile *);
191 
192 /* Current number of async. jobs. */
193 static int async_job_count;
194 static GHashTable *waiting_directories;
195 #ifdef DEBUG_ASYNC_JOBS
196 static GHashTable *async_jobs;
197 #endif
198 
199 /* Forward declarations for functions that need them. */
200 static void     deep_count_load                               (DeepCountState         *state,
201         GFile                  *location);
202 static gboolean request_is_satisfied                          (CajaDirectory      *directory,
203         CajaFile           *file,
204         Request                 request);
205 static void     cancel_loading_attributes                     (CajaDirectory      *directory,
206         CajaFileAttributes  file_attributes);
207 static void     add_all_files_to_work_queue                   (CajaDirectory      *directory);
208 static void     link_info_done                                (CajaDirectory      *directory,
209         CajaFile           *file,
210         const char             *uri,
211         const char             *name,
212         const char             *icon,
213         gboolean                is_launcher,
214         gboolean                is_foreign);
215 static void     move_file_to_low_priority_queue               (CajaDirectory      *directory,
216         CajaFile           *file);
217 static void     move_file_to_extension_queue                  (CajaDirectory      *directory,
218         CajaFile           *file);
219 static void     caja_directory_invalidate_file_attributes (CajaDirectory      *directory,
220         CajaFileAttributes  file_attributes);
221 
222 /* Some helpers for case-insensitive strings.
223  * Move to caja-glib-extensions?
224  */
225 
226 static gboolean
istr_equal(gconstpointer v,gconstpointer v2)227 istr_equal (gconstpointer v, gconstpointer v2)
228 {
229     return g_ascii_strcasecmp (v, v2) == 0;
230 }
231 
232 static guint
istr_hash(gconstpointer key)233 istr_hash (gconstpointer key)
234 {
235     const char *p;
236     guint h;
237 
238     h = 0;
239     for (p = key; *p != '\0'; p++)
240     {
241         h = (h << 5) - h + g_ascii_tolower (*p);
242     }
243 
244     return h;
245 }
246 
247 static GHashTable *
istr_set_new(void)248 istr_set_new (void)
249 {
250     return g_hash_table_new_full (istr_hash, istr_equal, g_free, NULL);
251 }
252 
253 static void
add_istr_to_list(gpointer key,gpointer value,gpointer callback_data)254 add_istr_to_list (gpointer key, gpointer value, gpointer callback_data)
255 {
256     GList **list;
257 
258     list = callback_data;
259     *list = g_list_prepend (*list, g_strdup (key));
260 }
261 
262 static GList *
istr_set_get_as_list(GHashTable * table)263 istr_set_get_as_list (GHashTable *table)
264 {
265     GList *list;
266 
267     list = NULL;
268     g_hash_table_foreach (table, add_istr_to_list, &list);
269     return list;
270 }
271 
272 static void
istr_set_destroy(GHashTable * table)273 istr_set_destroy (GHashTable *table)
274 {
275     g_hash_table_destroy (table);
276 }
277 
278 static void
request_counter_add_request(RequestCounter counter,Request request)279 request_counter_add_request (RequestCounter counter,
280                              Request request)
281 {
282     guint i;
283 
284     for (i = 0; i < REQUEST_TYPE_LAST; i++)
285     {
286         if (REQUEST_WANTS_TYPE (request, i))
287         {
288             counter[i]++;
289         }
290     }
291 }
292 
293 static void
request_counter_remove_request(RequestCounter counter,Request request)294 request_counter_remove_request (RequestCounter counter,
295                                 Request request)
296 {
297     guint i;
298 
299     for (i = 0; i < REQUEST_TYPE_LAST; i++)
300     {
301         if (REQUEST_WANTS_TYPE (request, i))
302         {
303             counter[i]--;
304         }
305     }
306 }
307 
308 #if 0
309 static void
310 caja_directory_verify_request_counts (CajaDirectory *directory)
311 {
312     GList *l;
313     RequestCounter counters;
314     int i;
315     gboolean fail;
316 
317     fail = FALSE;
318     for (i = 0; i < REQUEST_TYPE_LAST; i ++)
319     {
320         counters[i] = 0;
321     }
322     for (l = directory->details->monitor_list; l != NULL; l = l->next)
323     {
324         Monitor *monitor = l->data;
325         request_counter_add_request (counters, monitor->request);
326     }
327     for (i = 0; i < REQUEST_TYPE_LAST; i ++)
328     {
329         if (counters[i] != directory->details->monitor_counters[i])
330         {
331             g_warning ("monitor counter for %i is wrong, expecting %d but found %d",
332                        i, counters[i], directory->details->monitor_counters[i]);
333             fail = TRUE;
334         }
335     }
336     for (i = 0; i < REQUEST_TYPE_LAST; i ++)
337     {
338         counters[i] = 0;
339     }
340     for (l = directory->details->call_when_ready_list; l != NULL; l = l->next)
341     {
342         ReadyCallback *callback = l->data;
343         request_counter_add_request (counters, callback->request);
344     }
345     for (i = 0; i < REQUEST_TYPE_LAST; i ++)
346     {
347         if (counters[i] != directory->details->call_when_ready_counters[i])
348         {
349             g_warning ("call when ready counter for %i is wrong, expecting %d but found %d",
350                        i, counters[i], directory->details->call_when_ready_counters[i]);
351             fail = TRUE;
352         }
353     }
354     g_assert (!fail);
355 }
356 #endif
357 
358 /* Start a job. This is really just a way of limiting the number of
359  * async. requests that we issue at any given time. Without this, the
360  * number of requests is unbounded.
361  */
362 static gboolean
async_job_start(CajaDirectory * directory,const char * job)363 async_job_start (CajaDirectory *directory,
364                  const char *job)
365 {
366 #ifdef DEBUG_ASYNC_JOBS
367     char *key;
368 #endif
369 
370 #ifdef DEBUG_START_STOP
371     g_message ("starting %s in %p", job, directory->details->location);
372 #endif
373 
374     g_assert (async_job_count >= 0);
375     g_assert (async_job_count <= MAX_ASYNC_JOBS);
376 
377     if (async_job_count >= MAX_ASYNC_JOBS)
378     {
379         if (waiting_directories == NULL)
380         {
381             waiting_directories = eel_g_hash_table_new_free_at_exit
382                                   (NULL, NULL,
383                                    "caja-directory-async.c: waiting_directories");
384         }
385 
386         g_hash_table_insert (waiting_directories,
387                              directory,
388                              directory);
389 
390         return FALSE;
391     }
392 
393 #ifdef DEBUG_ASYNC_JOBS
394     {
395         char *uri;
396         if (async_jobs == NULL)
397         {
398             async_jobs = eel_g_hash_table_new_free_at_exit
399                          (g_str_hash, g_str_equal,
400                           "caja-directory-async.c: async_jobs");
401         }
402         uri = caja_directory_get_uri (directory);
403         key = g_strconcat (uri, ": ", job, NULL);
404         if (g_hash_table_lookup (async_jobs, key) != NULL)
405         {
406             g_warning ("same job twice: %s in %s",
407                        job, uri);
408         }
409         g_free (uri);
410         g_hash_table_insert (async_jobs, key, directory);
411     }
412 #endif
413 
414     async_job_count += 1;
415     return TRUE;
416 }
417 
418 /* End a job. */
419 static void
async_job_end(CajaDirectory * directory,const char * job)420 async_job_end (CajaDirectory *directory,
421                const char *job)
422 {
423 #ifdef DEBUG_ASYNC_JOBS
424     char *key;
425     gpointer table_key, value;
426 #endif
427 
428 #ifdef DEBUG_START_STOP
429     g_message ("stopping %s in %p", job, directory->details->location);
430 #endif
431 
432     g_assert (async_job_count > 0);
433 
434 #ifdef DEBUG_ASYNC_JOBS
435     {
436         char *uri;
437         uri = caja_directory_get_uri (directory);
438         g_assert (async_jobs != NULL);
439         key = g_strconcat (uri, ": ", job, NULL);
440         if (!g_hash_table_lookup_extended (async_jobs, key, &table_key, &value))
441         {
442             g_warning ("ending job we didn't start: %s in %s",
443                        job, uri);
444         }
445         else
446         {
447             g_hash_table_remove (async_jobs, key);
448             g_free (table_key);
449         }
450         g_free (uri);
451         g_free (key);
452     }
453 #endif
454 
455     async_job_count -= 1;
456 }
457 
458 /* Helper to get one value from a hash table. */
459 static void
get_one_value_callback(gpointer key,gpointer value,gpointer callback_data)460 get_one_value_callback (gpointer key, gpointer value, gpointer callback_data)
461 {
462     gpointer *returned_value;
463 
464     returned_value = callback_data;
465     *returned_value = value;
466 }
467 
468 /* return a single value from a hash table. */
469 static gpointer
get_one_value(GHashTable * table)470 get_one_value (GHashTable *table)
471 {
472     gpointer value;
473 
474     value = NULL;
475     if (table != NULL)
476     {
477         g_hash_table_foreach (table, get_one_value_callback, &value);
478     }
479     return value;
480 }
481 
482 /* Wake up directories that are "blocked" as long as there are job
483  * slots available.
484  */
485 static void
async_job_wake_up(void)486 async_job_wake_up (void)
487 {
488     static gboolean already_waking_up = FALSE;
489     gpointer value;
490 
491     g_assert (async_job_count >= 0);
492     g_assert (async_job_count <= MAX_ASYNC_JOBS);
493 
494     if (already_waking_up)
495     {
496         return;
497     }
498 
499     already_waking_up = TRUE;
500     while (async_job_count < MAX_ASYNC_JOBS)
501     {
502         value = get_one_value (waiting_directories);
503         if (value == NULL)
504         {
505             break;
506         }
507         g_hash_table_remove (waiting_directories, value);
508         caja_directory_async_state_changed
509         (CAJA_DIRECTORY (value));
510     }
511     already_waking_up = FALSE;
512 }
513 
514 static void
directory_count_cancel(CajaDirectory * directory)515 directory_count_cancel (CajaDirectory *directory)
516 {
517     if (directory->details->count_in_progress != NULL)
518     {
519         g_cancellable_cancel (directory->details->count_in_progress->cancellable);
520     }
521 }
522 
523 static void
deep_count_cancel(CajaDirectory * directory)524 deep_count_cancel (CajaDirectory *directory)
525 {
526     if (directory->details->deep_count_in_progress != NULL)
527     {
528         g_assert (CAJA_IS_FILE (directory->details->deep_count_file));
529 
530         g_cancellable_cancel (directory->details->deep_count_in_progress->cancellable);
531 
532         directory->details->deep_count_file->details->deep_counts_status = CAJA_REQUEST_NOT_STARTED;
533 
534         directory->details->deep_count_in_progress->directory = NULL;
535         directory->details->deep_count_in_progress = NULL;
536         directory->details->deep_count_file = NULL;
537 
538         async_job_end (directory, "deep count");
539     }
540 }
541 
542 static void
mime_list_cancel(CajaDirectory * directory)543 mime_list_cancel (CajaDirectory *directory)
544 {
545     if (directory->details->mime_list_in_progress != NULL)
546     {
547         g_cancellable_cancel (directory->details->mime_list_in_progress->cancellable);
548     }
549 }
550 
551 static void
top_left_cancel(CajaDirectory * directory)552 top_left_cancel (CajaDirectory *directory)
553 {
554     if (directory->details->top_left_read_state != NULL)
555     {
556         g_cancellable_cancel (directory->details->top_left_read_state->cancellable);
557         directory->details->top_left_read_state->directory = NULL;
558         directory->details->top_left_read_state = NULL;
559 
560         async_job_end (directory, "top left");
561     }
562 }
563 
564 static void
link_info_cancel(CajaDirectory * directory)565 link_info_cancel (CajaDirectory *directory)
566 {
567     if (directory->details->link_info_read_state != NULL)
568     {
569         g_cancellable_cancel (directory->details->link_info_read_state->cancellable);
570         directory->details->link_info_read_state->directory = NULL;
571         directory->details->link_info_read_state = NULL;
572         async_job_end (directory, "link info");
573     }
574 }
575 
576 static void
thumbnail_cancel(CajaDirectory * directory)577 thumbnail_cancel (CajaDirectory *directory)
578 {
579     if (directory->details->thumbnail_state != NULL)
580     {
581         g_cancellable_cancel (directory->details->thumbnail_state->cancellable);
582         directory->details->thumbnail_state->directory = NULL;
583         directory->details->thumbnail_state = NULL;
584         async_job_end (directory, "thumbnail");
585     }
586 }
587 
588 static void
mount_cancel(CajaDirectory * directory)589 mount_cancel (CajaDirectory *directory)
590 {
591     if (directory->details->mount_state != NULL)
592     {
593         g_cancellable_cancel (directory->details->mount_state->cancellable);
594         directory->details->mount_state->directory = NULL;
595         directory->details->mount_state = NULL;
596         async_job_end (directory, "mount");
597     }
598 }
599 
600 static void
file_info_cancel(CajaDirectory * directory)601 file_info_cancel (CajaDirectory *directory)
602 {
603     if (directory->details->get_info_in_progress != NULL)
604     {
605         g_cancellable_cancel (directory->details->get_info_in_progress->cancellable);
606         directory->details->get_info_in_progress->directory = NULL;
607         directory->details->get_info_in_progress = NULL;
608         directory->details->get_info_file = NULL;
609 
610         async_job_end (directory, "file info");
611     }
612 }
613 
614 static void
new_files_cancel(CajaDirectory * directory)615 new_files_cancel (CajaDirectory *directory)
616 {
617     GList *l;
618 
619     if (directory->details->new_files_in_progress != NULL)
620     {
621         NewFilesState *state = NULL;
622 
623         for (l = directory->details->new_files_in_progress; l != NULL; l = l->next)
624         {
625             state = l->data;
626             g_cancellable_cancel (state->cancellable);
627             state->directory = NULL;
628         }
629         g_list_free (directory->details->new_files_in_progress);
630         directory->details->new_files_in_progress = NULL;
631     }
632 }
633 
634 static int
monitor_key_compare(gconstpointer a,gconstpointer data)635 monitor_key_compare (gconstpointer a,
636                      gconstpointer data)
637 {
638     const Monitor *monitor;
639     const Monitor *compare_monitor;
640 
641     monitor = a;
642     compare_monitor = data;
643 
644     if (monitor->client < compare_monitor->client)
645     {
646         return -1;
647     }
648     if (monitor->client > compare_monitor->client)
649     {
650         return +1;
651     }
652 
653     if (monitor->file < compare_monitor->file)
654     {
655         return -1;
656     }
657     if (monitor->file > compare_monitor->file)
658     {
659         return +1;
660     }
661 
662     return 0;
663 }
664 
665 static GList *
find_monitor(CajaDirectory * directory,CajaFile * file,gconstpointer client)666 find_monitor (CajaDirectory *directory,
667               CajaFile *file,
668               gconstpointer client)
669 {
670     Monitor monitor;
671 
672     monitor.client = client;
673     monitor.file = file;
674 
675     return g_list_find_custom (directory->details->monitor_list,
676                                &monitor,
677                                monitor_key_compare);
678 }
679 
680 static void
remove_monitor_link(CajaDirectory * directory,GList * link)681 remove_monitor_link (CajaDirectory *directory,
682                      GList *link)
683 {
684     if (link != NULL)
685     {
686         Monitor *monitor;
687 
688         monitor = link->data;
689         request_counter_remove_request (directory->details->monitor_counters,
690                                         monitor->request);
691         directory->details->monitor_list =
692             g_list_remove_link (directory->details->monitor_list, link);
693         g_free (monitor);
694         g_list_free_1 (link);
695     }
696 }
697 
698 static void
remove_monitor(CajaDirectory * directory,CajaFile * file,gconstpointer client)699 remove_monitor (CajaDirectory *directory,
700                 CajaFile *file,
701                 gconstpointer client)
702 {
703     remove_monitor_link (directory, find_monitor (directory, file, client));
704 }
705 
706 Request
caja_directory_set_up_request(CajaFileAttributes file_attributes)707 caja_directory_set_up_request (CajaFileAttributes file_attributes)
708 {
709     Request request;
710 
711     request = 0;
712 
713     if ((file_attributes & CAJA_FILE_ATTRIBUTE_DIRECTORY_ITEM_COUNT) != 0)
714     {
715         REQUEST_SET_TYPE (request, REQUEST_DIRECTORY_COUNT);
716     }
717 
718     if ((file_attributes & CAJA_FILE_ATTRIBUTE_DEEP_COUNTS) != 0)
719     {
720         REQUEST_SET_TYPE (request, REQUEST_DEEP_COUNT);
721     }
722 
723     if ((file_attributes & CAJA_FILE_ATTRIBUTE_DIRECTORY_ITEM_MIME_TYPES) != 0)
724     {
725         REQUEST_SET_TYPE (request, REQUEST_MIME_LIST);
726     }
727     if ((file_attributes & CAJA_FILE_ATTRIBUTE_INFO) != 0)
728     {
729         REQUEST_SET_TYPE (request, REQUEST_FILE_INFO);
730     }
731 
732     if (file_attributes & CAJA_FILE_ATTRIBUTE_LINK_INFO)
733     {
734         REQUEST_SET_TYPE (request, REQUEST_FILE_INFO);
735         REQUEST_SET_TYPE (request, REQUEST_LINK_INFO);
736     }
737 
738     if (file_attributes & CAJA_FILE_ATTRIBUTE_TOP_LEFT_TEXT)
739     {
740         REQUEST_SET_TYPE (request, REQUEST_TOP_LEFT_TEXT);
741         REQUEST_SET_TYPE (request, REQUEST_FILE_INFO);
742     }
743 
744     if (file_attributes & CAJA_FILE_ATTRIBUTE_LARGE_TOP_LEFT_TEXT)
745     {
746         REQUEST_SET_TYPE (request, REQUEST_LARGE_TOP_LEFT_TEXT);
747         REQUEST_SET_TYPE (request, REQUEST_FILE_INFO);
748     }
749 
750     if ((file_attributes & CAJA_FILE_ATTRIBUTE_EXTENSION_INFO) != 0)
751     {
752         REQUEST_SET_TYPE (request, REQUEST_EXTENSION_INFO);
753     }
754 
755     if (file_attributes & CAJA_FILE_ATTRIBUTE_THUMBNAIL)
756     {
757         REQUEST_SET_TYPE (request, REQUEST_THUMBNAIL);
758         REQUEST_SET_TYPE (request, REQUEST_FILE_INFO);
759     }
760 
761     if (file_attributes & CAJA_FILE_ATTRIBUTE_MOUNT)
762     {
763         REQUEST_SET_TYPE (request, REQUEST_MOUNT);
764         REQUEST_SET_TYPE (request, REQUEST_FILE_INFO);
765     }
766 
767     if (file_attributes & CAJA_FILE_ATTRIBUTE_FILESYSTEM_INFO)
768     {
769         REQUEST_SET_TYPE (request, REQUEST_FILESYSTEM_INFO);
770     }
771 
772     return request;
773 }
774 
775 static void
mime_db_changed_callback(GObject * ignore,CajaDirectory * dir)776 mime_db_changed_callback (GObject *ignore, CajaDirectory *dir)
777 {
778     CajaFileAttributes attrs;
779 
780     g_assert (dir != NULL);
781     g_assert (dir->details != NULL);
782 
783     attrs = CAJA_FILE_ATTRIBUTE_INFO |
784             CAJA_FILE_ATTRIBUTE_LINK_INFO |
785             CAJA_FILE_ATTRIBUTE_DIRECTORY_ITEM_MIME_TYPES;
786 
787     caja_directory_force_reload_internal (dir, attrs);
788 }
789 
790 void
caja_directory_monitor_add_internal(CajaDirectory * directory,CajaFile * file,gconstpointer client,gboolean monitor_hidden_files,CajaFileAttributes file_attributes,CajaDirectoryCallback callback,gpointer callback_data)791 caja_directory_monitor_add_internal (CajaDirectory *directory,
792                                      CajaFile *file,
793                                      gconstpointer client,
794                                      gboolean monitor_hidden_files,
795                                      CajaFileAttributes file_attributes,
796                                      CajaDirectoryCallback callback,
797                                      gpointer callback_data)
798 {
799     Monitor *monitor;
800 
801     g_assert (CAJA_IS_DIRECTORY (directory));
802 
803     /* Replace any current monitor for this client/file pair. */
804     remove_monitor (directory, file, client);
805 
806     /* Add the new monitor. */
807     monitor = g_new (Monitor, 1);
808     monitor->file = file;
809     monitor->monitor_hidden_files = monitor_hidden_files;
810     monitor->client = client;
811     monitor->request = caja_directory_set_up_request (file_attributes);
812 
813     if (file == NULL)
814     {
815         REQUEST_SET_TYPE (monitor->request, REQUEST_FILE_LIST);
816     }
817     directory->details->monitor_list =
818         g_list_prepend (directory->details->monitor_list, monitor);
819     request_counter_add_request (directory->details->monitor_counters,
820                                  monitor->request);
821 
822     if (callback != NULL)
823     {
824         GList *file_list;
825 
826         file_list = caja_directory_get_file_list (directory);
827         (* callback) (directory, file_list, callback_data);
828         caja_file_list_free (file_list);
829     }
830 
831     /* Start the "real" monitoring (FAM or whatever). */
832     /* We always monitor the whole directory since in practice
833      * caja almost always shows the whole directory anyway, and
834      * it allows us to avoid one file monitor per file in a directory.
835      */
836     if (directory->details->monitor == NULL)
837     {
838         directory->details->monitor = caja_monitor_directory (directory->details->location);
839     }
840 
841 
842     if (REQUEST_WANTS_TYPE (monitor->request, REQUEST_FILE_INFO) &&
843             directory->details->mime_db_monitor == 0)
844     {
845         directory->details->mime_db_monitor =
846             g_signal_connect_object (caja_signaller_get_current (),
847                                      "mime_data_changed",
848                                      G_CALLBACK (mime_db_changed_callback), directory, 0);
849     }
850 
851     /* Put the monitor file or all the files on the work queue. */
852     if (file != NULL)
853     {
854         caja_directory_add_file_to_work_queue (directory, file);
855     }
856     else
857     {
858         add_all_files_to_work_queue (directory);
859     }
860 
861     /* Kick off I/O. */
862     caja_directory_async_state_changed (directory);
863 }
864 
865 static void
set_file_unconfirmed(CajaFile * file,gboolean unconfirmed)866 set_file_unconfirmed (CajaFile *file, gboolean unconfirmed)
867 {
868     CajaDirectory *directory;
869 
870     g_assert (CAJA_IS_FILE (file));
871     g_assert (unconfirmed == FALSE || unconfirmed == TRUE);
872 
873     if (file->details->unconfirmed == unconfirmed)
874     {
875         return;
876     }
877     file->details->unconfirmed = unconfirmed;
878 
879     directory = file->details->directory;
880     if (unconfirmed)
881     {
882         directory->details->confirmed_file_count--;
883     }
884     else
885     {
886         directory->details->confirmed_file_count++;
887     }
888 }
889 
890 static gboolean show_hidden_files = TRUE;
891 
892 static void
show_hidden_files_changed_callback(gpointer callback_data)893 show_hidden_files_changed_callback (gpointer callback_data)
894 {
895     show_hidden_files = g_settings_get_boolean (caja_preferences, CAJA_PREFERENCES_SHOW_HIDDEN_FILES);
896 }
897 
898 static gboolean
should_skip_file(CajaDirectory * directory,GFileInfo * info)899 should_skip_file (CajaDirectory *directory, GFileInfo *info)
900 {
901     static gboolean show_hidden_files_changed_callback_installed = FALSE;
902 
903     /* Add the callback once for the life of our process */
904     if (!show_hidden_files_changed_callback_installed)
905     {
906         g_signal_connect_swapped (caja_preferences,
907                                   "changed::" CAJA_PREFERENCES_SHOW_HIDDEN_FILES,
908                                   G_CALLBACK(show_hidden_files_changed_callback),
909                                   NULL);
910         show_hidden_files_changed_callback_installed = TRUE;
911 
912         /* Peek for the first time */
913         show_hidden_files_changed_callback (NULL);
914     }
915 
916     if (!show_hidden_files && g_file_info_get_is_hidden (info))
917     {
918         return TRUE;
919     }
920 
921     return FALSE;
922 }
923 
924 static gboolean
dequeue_pending_idle_callback(gpointer callback_data)925 dequeue_pending_idle_callback (gpointer callback_data)
926 {
927     CajaDirectory *directory;
928     GList *pending_file_info;
929     GList *node, *next;
930     CajaFile *file;
931     GList *changed_files, *added_files;
932     GFileInfo *file_info;
933     const char *mimetype, *name;
934     DirectoryLoadState *dir_load_state;
935 
936     directory = CAJA_DIRECTORY (callback_data);
937 
938     caja_directory_ref (directory);
939 
940     directory->details->dequeue_pending_idle_id = 0;
941 
942     /* Handle the files in the order we saw them. */
943     pending_file_info = g_list_reverse (directory->details->pending_file_info);
944     directory->details->pending_file_info = NULL;
945 
946     /* If we are no longer monitoring, then throw away these. */
947     if (!caja_directory_is_file_list_monitored (directory))
948     {
949         caja_directory_async_state_changed (directory);
950         goto drain;
951     }
952 
953     added_files = NULL;
954     changed_files = NULL;
955 
956     dir_load_state = directory->details->directory_load_in_progress;
957 
958     /* Build a list of CajaFile objects. */
959     for (node = pending_file_info; node != NULL; node = node->next)
960     {
961         file_info = node->data;
962 
963         name = g_file_info_get_name (file_info);
964 
965         /* Update the file count. */
966         /* FIXME bugzilla.gnome.org 45063: This could count a
967          * file twice if we get it from both load_directory
968          * and from new_files_callback. Not too hard to fix by
969          * moving this into the actual callback instead of
970          * waiting for the idle function.
971          */
972         if (dir_load_state && !should_skip_file (directory, file_info))
973         {
974             dir_load_state->load_file_count += 1;
975 
976             /* Add the MIME type to the set. */
977             if ((mimetype = g_file_info_get_content_type (file_info)) != NULL)
978             {
979                 g_hash_table_add (dir_load_state->load_mime_list_hash,
980                                   g_strdup (mimetype));
981             }
982         }
983 
984         /* check if the file already exists */
985         file = caja_directory_find_file_by_name (directory, name);
986         if (file != NULL)
987         {
988             /* file already exists in dir, check if we still need to
989              *  emit file_added or if it changed */
990             set_file_unconfirmed (file, FALSE);
991             if (!file->details->is_added)
992             {
993                 /* We consider this newly added even if its in the list.
994                  * This can happen if someone called caja_file_get_by_uri()
995                  * on a file in the folder before the add signal was
996                  * emitted */
997                 caja_file_ref (file);
998                 file->details->is_added = TRUE;
999                 added_files = g_list_prepend (added_files, file);
1000             }
1001             else if (caja_file_update_info (file, file_info))
1002             {
1003                 /* File changed, notify about the change. */
1004                 caja_file_ref (file);
1005                 changed_files = g_list_prepend (changed_files, file);
1006             }
1007         }
1008         else
1009         {
1010             /* new file, create a caja file object and add it to the list */
1011             file = caja_file_new_from_info (directory, file_info);
1012             caja_directory_add_file (directory, file);
1013             file->details->is_added = TRUE;
1014             added_files = g_list_prepend (added_files, file);
1015         }
1016     }
1017 
1018     /* If we are done loading, then we assume that any unconfirmed
1019          * files are gone.
1020      */
1021     if (directory->details->directory_loaded)
1022     {
1023         for (node = directory->details->file_list;
1024                 node != NULL; node = next)
1025         {
1026             file = CAJA_FILE (node->data);
1027             next = node->next;
1028 
1029             if (file->details->unconfirmed)
1030             {
1031                 caja_file_ref (file);
1032                 changed_files = g_list_prepend (changed_files, file);
1033 
1034                 caja_file_mark_gone (file);
1035             }
1036         }
1037     }
1038 
1039     /* Send the changed and added signals. */
1040     caja_directory_emit_change_signals (directory, changed_files);
1041     caja_file_list_free (changed_files);
1042     caja_directory_emit_files_added (directory, added_files);
1043     caja_file_list_free (added_files);
1044 
1045     if (directory->details->directory_loaded &&
1046             !directory->details->directory_loaded_sent_notification)
1047     {
1048         /* Send the done_loading signal. */
1049         caja_directory_emit_done_loading (directory);
1050 
1051         if (dir_load_state)
1052         {
1053             file = dir_load_state->load_directory_file;
1054 
1055             file->details->directory_count = dir_load_state->load_file_count;
1056             file->details->directory_count_is_up_to_date = TRUE;
1057             file->details->got_directory_count = TRUE;
1058 
1059             file->details->got_mime_list = TRUE;
1060             file->details->mime_list_is_up_to_date = TRUE;
1061             g_list_free_full (file->details->mime_list, g_free);
1062             file->details->mime_list = istr_set_get_as_list
1063                                        (dir_load_state->load_mime_list_hash);
1064 
1065             caja_file_changed (file);
1066         }
1067 
1068         caja_directory_async_state_changed (directory);
1069 
1070         directory->details->directory_loaded_sent_notification = TRUE;
1071     }
1072 
1073 drain:
1074     g_list_free_full (pending_file_info, g_object_unref);
1075 
1076     /* Get the state machine running again. */
1077     caja_directory_async_state_changed (directory);
1078 
1079     caja_directory_unref (directory);
1080     return FALSE;
1081 }
1082 
1083 void
caja_directory_schedule_dequeue_pending(CajaDirectory * directory)1084 caja_directory_schedule_dequeue_pending (CajaDirectory *directory)
1085 {
1086     if (directory->details->dequeue_pending_idle_id == 0)
1087     {
1088         directory->details->dequeue_pending_idle_id
1089             = g_idle_add (dequeue_pending_idle_callback, directory);
1090     }
1091 }
1092 
1093 static void
directory_load_one(CajaDirectory * directory,GFileInfo * info)1094 directory_load_one (CajaDirectory *directory,
1095                     GFileInfo *info)
1096 {
1097     if (info == NULL)
1098     {
1099         return;
1100     }
1101 
1102     if (g_file_info_get_name (info) == NULL)
1103     {
1104         char *uri;
1105 
1106         uri = caja_directory_get_uri (directory);
1107         g_warning ("Got GFileInfo with NULL name in %s, ignoring. This shouldn't happen unless the gvfs backend is broken.\n", uri);
1108         g_free (uri);
1109 
1110         return;
1111     }
1112 
1113     /* Arrange for the "loading" part of the work. */
1114     g_object_ref (info);
1115     directory->details->pending_file_info
1116         = g_list_prepend (directory->details->pending_file_info, info);
1117     caja_directory_schedule_dequeue_pending (directory);
1118 }
1119 
1120 static void
directory_load_cancel(CajaDirectory * directory)1121 directory_load_cancel (CajaDirectory *directory)
1122 {
1123     DirectoryLoadState *state;
1124 
1125     state = directory->details->directory_load_in_progress;
1126     if (state != NULL)
1127     {
1128         CajaFile *file;
1129 
1130         file = state->load_directory_file;
1131         file->details->loading_directory = FALSE;
1132         if (file->details->directory != directory)
1133         {
1134             caja_directory_async_state_changed (file->details->directory);
1135         }
1136 
1137         g_cancellable_cancel (state->cancellable);
1138         state->directory = NULL;
1139         directory->details->directory_load_in_progress = NULL;
1140         async_job_end (directory, "file list");
1141     }
1142 }
1143 
1144 static void
file_list_cancel(CajaDirectory * directory)1145 file_list_cancel (CajaDirectory *directory)
1146 {
1147     directory_load_cancel (directory);
1148 
1149     if (directory->details->dequeue_pending_idle_id != 0)
1150     {
1151         g_source_remove (directory->details->dequeue_pending_idle_id);
1152         directory->details->dequeue_pending_idle_id = 0;
1153     }
1154 
1155     if (directory->details->pending_file_info != NULL)
1156     {
1157 	g_list_free_full (directory->details->pending_file_info, g_object_unref);
1158         directory->details->pending_file_info = NULL;
1159     }
1160 }
1161 
1162 static void
directory_load_done(CajaDirectory * directory,GError * error)1163 directory_load_done (CajaDirectory *directory,
1164                      GError *error)
1165 {
1166     GList *node;
1167 
1168     directory->details->directory_loaded = TRUE;
1169     directory->details->directory_loaded_sent_notification = FALSE;
1170 
1171     if (error != NULL)
1172     {
1173         /* The load did not complete successfully. This means
1174          * we don't know the status of the files in this directory.
1175          * We clear the unconfirmed bit on each file here so that
1176          * they won't be marked "gone" later -- we don't know enough
1177          * about them to know whether they are really gone.
1178          */
1179         for (node = directory->details->file_list;
1180                 node != NULL; node = node->next)
1181         {
1182             set_file_unconfirmed (CAJA_FILE (node->data), FALSE);
1183         }
1184 
1185         caja_directory_emit_load_error (directory, error);
1186     }
1187 
1188     /* Call the idle function right away. */
1189     if (directory->details->dequeue_pending_idle_id != 0)
1190     {
1191         g_source_remove (directory->details->dequeue_pending_idle_id);
1192     }
1193     dequeue_pending_idle_callback (directory);
1194 
1195     directory_load_cancel (directory);
1196 }
1197 
1198 void
caja_directory_monitor_remove_internal(CajaDirectory * directory,CajaFile * file,gconstpointer client)1199 caja_directory_monitor_remove_internal (CajaDirectory *directory,
1200                                         CajaFile *file,
1201                                         gconstpointer client)
1202 {
1203     g_assert (CAJA_IS_DIRECTORY (directory));
1204     g_assert (file == NULL || CAJA_IS_FILE (file));
1205     g_assert (client != NULL);
1206 
1207     remove_monitor (directory, file, client);
1208 
1209     if (directory->details->monitor != NULL
1210             && directory->details->monitor_list == NULL)
1211     {
1212         caja_monitor_cancel (directory->details->monitor);
1213         directory->details->monitor = NULL;
1214     }
1215 
1216     /* XXX - do we need to remove anything from the work queue? */
1217 
1218     caja_directory_async_state_changed (directory);
1219 }
1220 
1221 FileMonitors *
caja_directory_remove_file_monitors(CajaDirectory * directory,CajaFile * file)1222 caja_directory_remove_file_monitors (CajaDirectory *directory,
1223                                      CajaFile *file)
1224 {
1225     GList *result, **list, *node, *next;
1226     Monitor *monitor = NULL;
1227 
1228     g_assert (CAJA_IS_DIRECTORY (directory));
1229     g_assert (CAJA_IS_FILE (file));
1230     g_assert (file->details->directory == directory);
1231 
1232     result = NULL;
1233 
1234     list = &directory->details->monitor_list;
1235     for (node = directory->details->monitor_list; node != NULL; node = next)
1236     {
1237         next = node->next;
1238         monitor = node->data;
1239 
1240         if (monitor->file == file)
1241         {
1242             *list = g_list_remove_link (*list, node);
1243             result = g_list_concat (node, result);
1244             request_counter_remove_request (directory->details->monitor_counters,
1245                                             monitor->request);
1246         }
1247     }
1248 
1249     /* XXX - do we need to remove anything from the work queue? */
1250 
1251     caja_directory_async_state_changed (directory);
1252 
1253     return (FileMonitors *) result;
1254 }
1255 
1256 void
caja_directory_add_file_monitors(CajaDirectory * directory,CajaFile * file,FileMonitors * monitors)1257 caja_directory_add_file_monitors (CajaDirectory *directory,
1258                                   CajaFile *file,
1259                                   FileMonitors *monitors)
1260 {
1261     GList **list;
1262     GList *l;
1263     Monitor *monitor = NULL;
1264 
1265     g_assert (CAJA_IS_DIRECTORY (directory));
1266     g_assert (CAJA_IS_FILE (file));
1267     g_assert (file->details->directory == directory);
1268 
1269     if (monitors == NULL)
1270     {
1271         return;
1272     }
1273 
1274     for (l = (GList *)monitors; l != NULL; l = l->next)
1275     {
1276         monitor = l->data;
1277         request_counter_add_request (directory->details->monitor_counters,
1278                                      monitor->request);
1279     }
1280 
1281     list = &directory->details->monitor_list;
1282     *list = g_list_concat (*list, (GList *) monitors);
1283 
1284     caja_directory_add_file_to_work_queue (directory, file);
1285 
1286     caja_directory_async_state_changed (directory);
1287 }
1288 
1289 static int
ready_callback_key_compare(gconstpointer a,gconstpointer b)1290 ready_callback_key_compare (gconstpointer a, gconstpointer b)
1291 {
1292     const ReadyCallback *callback_a, *callback_b;
1293 
1294     callback_a = a;
1295     callback_b = b;
1296 
1297     if (callback_a->file < callback_b->file)
1298     {
1299         return -1;
1300     }
1301     if (callback_a->file > callback_b->file)
1302     {
1303         return 1;
1304     }
1305     if (callback_a->file == NULL)
1306     {
1307         /* ANSI C doesn't allow ordered compares of function pointers, so we cast them to
1308          * normal pointers to make some overly pedantic compilers (*cough* HP-UX *cough*)
1309          * compile this. Of course, on any compiler where ordered function pointers actually
1310          * break this probably won't work, but at least it will compile on platforms where it
1311          * works, but stupid compilers won't let you use it.
1312          */
1313         if ((void *)callback_a->callback.directory < (void *)callback_b->callback.directory)
1314         {
1315             return -1;
1316         }
1317         if ((void *)callback_a->callback.directory > (void *)callback_b->callback.directory)
1318         {
1319             return 1;
1320         }
1321     }
1322     else
1323     {
1324         if ((void *)callback_a->callback.file < (void *)callback_b->callback.file)
1325         {
1326             return -1;
1327         }
1328         if ((void *)callback_a->callback.file > (void *)callback_b->callback.file)
1329         {
1330             return 1;
1331         }
1332     }
1333     if (callback_a->callback_data < callback_b->callback_data)
1334     {
1335         return -1;
1336     }
1337     if (callback_a->callback_data > callback_b->callback_data)
1338     {
1339         return 1;
1340     }
1341     return 0;
1342 }
1343 
1344 static int
ready_callback_key_compare_only_active(gconstpointer a,gconstpointer b)1345 ready_callback_key_compare_only_active (gconstpointer a, gconstpointer b)
1346 {
1347     const ReadyCallback *callback_a;
1348 
1349     callback_a = a;
1350 
1351     /* Non active callbacks never match */
1352     if (!callback_a->active)
1353     {
1354         return -1;
1355     }
1356 
1357     return ready_callback_key_compare (a, b);
1358 }
1359 
1360 static void
ready_callback_call(CajaDirectory * directory,const ReadyCallback * callback)1361 ready_callback_call (CajaDirectory *directory,
1362                      const ReadyCallback *callback)
1363 {
1364     GList *file_list;
1365 
1366     /* Call the callback. */
1367     if (callback->file != NULL)
1368     {
1369         if (callback->callback.file)
1370         {
1371             (* callback->callback.file) (callback->file,
1372                                          callback->callback_data);
1373         }
1374     }
1375     else if (callback->callback.directory != NULL)
1376     {
1377         if (directory == NULL ||
1378                 !REQUEST_WANTS_TYPE (callback->request, REQUEST_FILE_LIST))
1379         {
1380             file_list = NULL;
1381         }
1382         else
1383         {
1384             file_list = caja_directory_get_file_list (directory);
1385         }
1386 
1387         /* Pass back the file list if the user was waiting for it. */
1388         (* callback->callback.directory) (directory,
1389                                           file_list,
1390                                           callback->callback_data);
1391 
1392         caja_file_list_free (file_list);
1393     }
1394 }
1395 
1396 void
caja_directory_call_when_ready_internal(CajaDirectory * directory,CajaFile * file,CajaFileAttributes file_attributes,gboolean wait_for_file_list,CajaDirectoryCallback directory_callback,CajaFileCallback file_callback,gpointer callback_data)1397 caja_directory_call_when_ready_internal (CajaDirectory *directory,
1398         CajaFile *file,
1399         CajaFileAttributes file_attributes,
1400         gboolean wait_for_file_list,
1401         CajaDirectoryCallback directory_callback,
1402         CajaFileCallback file_callback,
1403         gpointer callback_data)
1404 {
1405     ReadyCallback callback;
1406 
1407     g_assert (directory == NULL || CAJA_IS_DIRECTORY (directory));
1408     g_assert (file == NULL || CAJA_IS_FILE (file));
1409     g_assert (file != NULL || directory_callback != NULL);
1410 
1411     /* Construct a callback object. */
1412     callback.active = TRUE;
1413     callback.file = file;
1414     if (file == NULL)
1415     {
1416         callback.callback.directory = directory_callback;
1417     }
1418     else
1419     {
1420         callback.callback.file = file_callback;
1421     }
1422     callback.callback_data = callback_data;
1423     callback.request = caja_directory_set_up_request (file_attributes);
1424     if (wait_for_file_list)
1425     {
1426         REQUEST_SET_TYPE (callback.request, REQUEST_FILE_LIST);
1427     }
1428 
1429     /* Handle the NULL case. */
1430     if (directory == NULL)
1431     {
1432         ready_callback_call (NULL, &callback);
1433         return;
1434     }
1435 
1436     /* Check if the callback is already there. */
1437     if (g_list_find_custom (directory->details->call_when_ready_list,
1438                             &callback,
1439                             ready_callback_key_compare_only_active) != NULL)
1440     {
1441         if (file_callback != NULL && directory_callback != NULL)
1442         {
1443             g_warning ("tried to add a new callback while an old one was pending");
1444         }
1445         /* NULL callback means, just read it. Conflicts are ok. */
1446         return;
1447     }
1448 
1449     /* Add the new callback to the list. */
1450     directory->details->call_when_ready_list = g_list_prepend
1451             (directory->details->call_when_ready_list,
1452              g_memdup (&callback, sizeof (callback)));
1453     request_counter_add_request (directory->details->call_when_ready_counters,
1454                                  callback.request);
1455 
1456     /* Put the callback file or all the files on the work queue. */
1457     if (file != NULL)
1458     {
1459         caja_directory_add_file_to_work_queue (directory, file);
1460     }
1461     else
1462     {
1463         add_all_files_to_work_queue (directory);
1464     }
1465 
1466     caja_directory_async_state_changed (directory);
1467 }
1468 
1469 gboolean
caja_directory_check_if_ready_internal(CajaDirectory * directory,CajaFile * file,CajaFileAttributes file_attributes)1470 caja_directory_check_if_ready_internal (CajaDirectory *directory,
1471                                         CajaFile *file,
1472                                         CajaFileAttributes file_attributes)
1473 {
1474     Request request;
1475 
1476     g_assert (CAJA_IS_DIRECTORY (directory));
1477 
1478     request = caja_directory_set_up_request (file_attributes);
1479     return request_is_satisfied (directory, file, request);
1480 }
1481 
1482 static void
remove_callback_link_keep_data(CajaDirectory * directory,GList * link)1483 remove_callback_link_keep_data (CajaDirectory *directory,
1484                                 GList *link)
1485 {
1486     ReadyCallback *callback;
1487 
1488     callback = link->data;
1489 
1490     directory->details->call_when_ready_list = g_list_remove_link
1491             (directory->details->call_when_ready_list, link);
1492 
1493     request_counter_remove_request (directory->details->call_when_ready_counters,
1494                                     callback->request);
1495     g_list_free_1 (link);
1496 }
1497 
1498 static void
remove_callback_link(CajaDirectory * directory,GList * link)1499 remove_callback_link (CajaDirectory *directory,
1500                       GList *link)
1501 {
1502     ReadyCallback *callback;
1503 
1504     callback = link->data;
1505     remove_callback_link_keep_data (directory, link);
1506     g_free (callback);
1507 }
1508 
1509 void
caja_directory_cancel_callback_internal(CajaDirectory * directory,CajaFile * file,CajaDirectoryCallback directory_callback,CajaFileCallback file_callback,gpointer callback_data)1510 caja_directory_cancel_callback_internal (CajaDirectory *directory,
1511         CajaFile *file,
1512         CajaDirectoryCallback directory_callback,
1513         CajaFileCallback file_callback,
1514         gpointer callback_data)
1515 {
1516     ReadyCallback callback;
1517     GList *node;
1518 
1519     if (directory == NULL)
1520     {
1521         return;
1522     }
1523 
1524     g_assert (CAJA_IS_DIRECTORY (directory));
1525     g_assert (file == NULL || CAJA_IS_FILE (file));
1526     g_assert (file != NULL || directory_callback != NULL);
1527     g_assert (file == NULL || file_callback != NULL);
1528 
1529     /* Construct a callback object. */
1530     callback.file = file;
1531     if (file == NULL)
1532     {
1533         callback.callback.directory = directory_callback;
1534     }
1535     else
1536     {
1537         callback.callback.file = file_callback;
1538     }
1539     callback.callback_data = callback_data;
1540 
1541     /* Remove all queued callback from the list (including non-active). */
1542     do
1543     {
1544         node = g_list_find_custom (directory->details->call_when_ready_list,
1545                                    &callback,
1546                                    ready_callback_key_compare);
1547         if (node != NULL)
1548         {
1549             remove_callback_link (directory, node);
1550 
1551             caja_directory_async_state_changed (directory);
1552         }
1553     }
1554     while (node != NULL);
1555 }
1556 
1557 static void
new_files_state_unref(NewFilesState * state)1558 new_files_state_unref (NewFilesState *state)
1559 {
1560     state->count--;
1561 
1562     if (state->count == 0)
1563     {
1564         if (state->directory)
1565         {
1566             state->directory->details->new_files_in_progress =
1567                 g_list_remove (state->directory->details->new_files_in_progress,
1568                                state);
1569         }
1570 
1571         g_object_unref (state->cancellable);
1572         g_free (state);
1573     }
1574 }
1575 
1576 static void
new_files_callback(GObject * source_object,GAsyncResult * res,gpointer user_data)1577 new_files_callback (GObject *source_object,
1578                     GAsyncResult *res,
1579                     gpointer user_data)
1580 {
1581     CajaDirectory *directory;
1582     GFileInfo *info;
1583     NewFilesState *state;
1584 
1585     state = user_data;
1586 
1587     if (state->directory == NULL)
1588     {
1589         /* Operation was cancelled. Bail out */
1590         new_files_state_unref (state);
1591         return;
1592     }
1593 
1594     directory = caja_directory_ref (state->directory);
1595 
1596     /* Queue up the new file. */
1597     info = g_file_query_info_finish (G_FILE (source_object), res, NULL);
1598     if (info != NULL)
1599     {
1600         directory_load_one (directory, info);
1601         g_object_unref (info);
1602     }
1603 
1604     new_files_state_unref (state);
1605 
1606     caja_directory_unref (directory);
1607 }
1608 
1609 void
caja_directory_get_info_for_new_files(CajaDirectory * directory,GList * location_list)1610 caja_directory_get_info_for_new_files (CajaDirectory *directory,
1611                                        GList *location_list)
1612 {
1613     NewFilesState *state;
1614     GList *l;
1615     GFile *location = NULL;
1616 
1617     if (location_list == NULL)
1618     {
1619         return;
1620     }
1621 
1622     state = g_new (NewFilesState, 1);
1623     state->directory = directory;
1624     state->cancellable = g_cancellable_new ();
1625     state->count = 0;
1626 
1627     for (l = location_list; l != NULL; l = l->next)
1628     {
1629         location = l->data;
1630 
1631         state->count++;
1632 
1633         g_file_query_info_async (location,
1634                                  CAJA_FILE_DEFAULT_ATTRIBUTES,
1635                                  0,
1636                                  G_PRIORITY_DEFAULT,
1637                                  state->cancellable,
1638                                  new_files_callback, state);
1639     }
1640 
1641     directory->details->new_files_in_progress
1642         = g_list_prepend (directory->details->new_files_in_progress,
1643                           state);
1644 }
1645 
1646 void
caja_async_destroying_file(CajaFile * file)1647 caja_async_destroying_file (CajaFile *file)
1648 {
1649     CajaDirectory *directory;
1650     gboolean changed;
1651     GList *node, *next;
1652     ReadyCallback *callback = NULL;
1653     Monitor *monitor = NULL;
1654 
1655     directory = file->details->directory;
1656     changed = FALSE;
1657 
1658     /* Check for callbacks. */
1659     for (node = directory->details->call_when_ready_list; node != NULL; node = next)
1660     {
1661         next = node->next;
1662         callback = node->data;
1663 
1664         if (callback->file == file)
1665         {
1666             /* Client should have cancelled callback. */
1667             if (callback->active)
1668             {
1669                 g_warning ("destroyed file has call_when_ready pending");
1670             }
1671             remove_callback_link (directory, node);
1672             changed = TRUE;
1673         }
1674     }
1675 
1676     /* Check for monitors. */
1677     for (node = directory->details->monitor_list; node != NULL; node = next)
1678     {
1679         next = node->next;
1680         monitor = node->data;
1681 
1682         if (monitor->file == file)
1683         {
1684             /* Client should have removed monitor earlier. */
1685             g_warning ("destroyed file still being monitored");
1686             remove_monitor_link (directory, node);
1687             changed = TRUE;
1688         }
1689     }
1690 
1691     /* Check if it's a file that's currently being worked on.
1692      * If so, make that NULL so it gets canceled right away.
1693      */
1694     if (directory->details->count_in_progress != NULL &&
1695             directory->details->count_in_progress->count_file == file)
1696     {
1697         directory->details->count_in_progress->count_file = NULL;
1698         changed = TRUE;
1699     }
1700     if (directory->details->deep_count_file == file)
1701     {
1702         directory->details->deep_count_file = NULL;
1703         changed = TRUE;
1704     }
1705     if (directory->details->mime_list_in_progress != NULL &&
1706             directory->details->mime_list_in_progress->mime_list_file == file)
1707     {
1708         directory->details->mime_list_in_progress->mime_list_file = NULL;
1709         changed = TRUE;
1710     }
1711     if (directory->details->get_info_file == file)
1712     {
1713         directory->details->get_info_file = NULL;
1714         changed = TRUE;
1715     }
1716     if (directory->details->top_left_read_state != NULL
1717             && directory->details->top_left_read_state->file == file)
1718     {
1719         directory->details->top_left_read_state->file = NULL;
1720         changed = TRUE;
1721     }
1722     if (directory->details->link_info_read_state != NULL &&
1723             directory->details->link_info_read_state->file == file)
1724     {
1725         directory->details->link_info_read_state->file = NULL;
1726         changed = TRUE;
1727     }
1728     if (directory->details->extension_info_file == file)
1729     {
1730         directory->details->extension_info_file = NULL;
1731         changed = TRUE;
1732     }
1733 
1734     if (directory->details->thumbnail_state != NULL &&
1735             directory->details->thumbnail_state->file ==  file)
1736     {
1737         directory->details->thumbnail_state->file = NULL;
1738         changed = TRUE;
1739     }
1740 
1741     if (directory->details->mount_state != NULL &&
1742             directory->details->mount_state->file ==  file)
1743     {
1744         directory->details->mount_state->file = NULL;
1745         changed = TRUE;
1746     }
1747 
1748     if (directory->details->filesystem_info_state != NULL &&
1749             directory->details->filesystem_info_state->file == file)
1750     {
1751         directory->details->filesystem_info_state->file = NULL;
1752         changed = TRUE;
1753     }
1754 
1755     /* Let the directory take care of the rest. */
1756     if (changed)
1757     {
1758         caja_directory_async_state_changed (directory);
1759     }
1760 }
1761 
1762 static gboolean
lacks_directory_count(CajaFile * file)1763 lacks_directory_count (CajaFile *file)
1764 {
1765     return !file->details->directory_count_is_up_to_date
1766            && caja_file_should_show_directory_item_count (file);
1767 }
1768 
1769 static gboolean
should_get_directory_count_now(CajaFile * file)1770 should_get_directory_count_now (CajaFile *file)
1771 {
1772     return lacks_directory_count (file)
1773            && !file->details->loading_directory;
1774 }
1775 
1776 static gboolean
lacks_top_left(CajaFile * file)1777 lacks_top_left (CajaFile *file)
1778 {
1779     return file->details->file_info_is_up_to_date &&
1780            !file->details->top_left_text_is_up_to_date
1781            && caja_file_should_get_top_left_text (file);
1782 }
1783 
1784 static gboolean
lacks_large_top_left(CajaFile * file)1785 lacks_large_top_left (CajaFile *file)
1786 {
1787     return file->details->file_info_is_up_to_date &&
1788            (!file->details->top_left_text_is_up_to_date ||
1789             file->details->got_large_top_left_text != file->details->got_top_left_text)
1790            && caja_file_should_get_top_left_text (file);
1791 }
1792 static gboolean
lacks_info(CajaFile * file)1793 lacks_info (CajaFile *file)
1794 {
1795     return !file->details->file_info_is_up_to_date
1796            && !file->details->is_gone;
1797 }
1798 
1799 static gboolean
lacks_filesystem_info(CajaFile * file)1800 lacks_filesystem_info (CajaFile *file)
1801 {
1802     return !file->details->filesystem_info_is_up_to_date;
1803 }
1804 
1805 static gboolean
lacks_deep_count(CajaFile * file)1806 lacks_deep_count (CajaFile *file)
1807 {
1808     return file->details->deep_counts_status != CAJA_REQUEST_DONE;
1809 }
1810 
1811 static gboolean
lacks_mime_list(CajaFile * file)1812 lacks_mime_list (CajaFile *file)
1813 {
1814     return !file->details->mime_list_is_up_to_date;
1815 }
1816 
1817 static gboolean
should_get_mime_list(CajaFile * file)1818 should_get_mime_list (CajaFile *file)
1819 {
1820     return lacks_mime_list (file)
1821            && !file->details->loading_directory;
1822 }
1823 
1824 static gboolean
lacks_link_info(CajaFile * file)1825 lacks_link_info (CajaFile *file)
1826 {
1827     if (file->details->file_info_is_up_to_date &&
1828             !file->details->link_info_is_up_to_date)
1829     {
1830         if (caja_file_is_caja_link (file))
1831         {
1832             return TRUE;
1833         }
1834         else
1835         {
1836             link_info_done (file->details->directory, file, NULL, NULL, NULL, FALSE, FALSE);
1837             return FALSE;
1838         }
1839     }
1840     else
1841     {
1842         return FALSE;
1843     }
1844 }
1845 
1846 static gboolean
lacks_extension_info(CajaFile * file)1847 lacks_extension_info (CajaFile *file)
1848 {
1849     return file->details->pending_info_providers != NULL;
1850 }
1851 
1852 static gboolean
lacks_thumbnail(CajaFile * file)1853 lacks_thumbnail (CajaFile *file)
1854 {
1855     return caja_file_should_show_thumbnail (file) &&
1856            file->details->thumbnail_path != NULL &&
1857            !file->details->thumbnail_is_up_to_date;
1858 }
1859 
1860 static gboolean
lacks_mount(CajaFile * file)1861 lacks_mount (CajaFile *file)
1862 {
1863     return (!file->details->mount_is_up_to_date &&
1864             (
1865                 /* Unix mountpoint, could be a GMount */
1866                 file->details->is_mountpoint ||
1867 
1868                 /* The toplevel directory of something */
1869                 (file->details->type == G_FILE_TYPE_DIRECTORY &&
1870                  caja_file_is_self_owned (file)) ||
1871 
1872                 /* Mountable, could be a mountpoint */
1873                 (file->details->type == G_FILE_TYPE_MOUNTABLE)
1874 
1875             )
1876            );
1877 }
1878 
1879 static gboolean
has_problem(CajaDirectory * directory,CajaFile * file,FileCheck problem)1880 has_problem (CajaDirectory *directory, CajaFile *file, FileCheck problem)
1881 {
1882     GList *node;
1883 
1884     if (file != NULL)
1885     {
1886         return (* problem) (file);
1887     }
1888 
1889     for (node = directory->details->file_list; node != NULL; node = node->next)
1890     {
1891         if ((* problem) (node->data))
1892         {
1893             return TRUE;
1894         }
1895     }
1896 
1897     return FALSE;
1898 }
1899 
1900 static gboolean
request_is_satisfied(CajaDirectory * directory,CajaFile * file,Request request)1901 request_is_satisfied (CajaDirectory *directory,
1902                       CajaFile *file,
1903                       Request request)
1904 {
1905     if (REQUEST_WANTS_TYPE (request, REQUEST_FILE_LIST) &&
1906             !(directory->details->directory_loaded &&
1907               directory->details->directory_loaded_sent_notification))
1908     {
1909         return FALSE;
1910     }
1911 
1912     if (REQUEST_WANTS_TYPE (request, REQUEST_DIRECTORY_COUNT))
1913     {
1914         if (has_problem (directory, file, lacks_directory_count))
1915         {
1916             return FALSE;
1917         }
1918     }
1919 
1920     if (REQUEST_WANTS_TYPE (request, REQUEST_FILE_INFO))
1921     {
1922         if (has_problem (directory, file, lacks_info))
1923         {
1924             return FALSE;
1925         }
1926     }
1927 
1928     if (REQUEST_WANTS_TYPE (request, REQUEST_FILESYSTEM_INFO))
1929     {
1930         if (has_problem (directory, file, lacks_filesystem_info))
1931         {
1932             return FALSE;
1933         }
1934     }
1935 
1936     if (REQUEST_WANTS_TYPE (request, REQUEST_TOP_LEFT_TEXT))
1937     {
1938         if (has_problem (directory, file, lacks_top_left))
1939         {
1940             return FALSE;
1941         }
1942     }
1943 
1944     if (REQUEST_WANTS_TYPE (request, REQUEST_LARGE_TOP_LEFT_TEXT))
1945     {
1946         if (has_problem (directory, file, lacks_large_top_left))
1947         {
1948             return FALSE;
1949         }
1950     }
1951 
1952     if (REQUEST_WANTS_TYPE (request, REQUEST_DEEP_COUNT))
1953     {
1954         if (has_problem (directory, file, lacks_deep_count))
1955         {
1956             return FALSE;
1957         }
1958     }
1959 
1960     if (REQUEST_WANTS_TYPE (request, REQUEST_THUMBNAIL))
1961     {
1962         if (has_problem (directory, file, lacks_thumbnail))
1963         {
1964             return FALSE;
1965         }
1966     }
1967 
1968     if (REQUEST_WANTS_TYPE (request, REQUEST_MOUNT))
1969     {
1970         if (has_problem (directory, file, lacks_mount))
1971         {
1972             return FALSE;
1973         }
1974     }
1975 
1976     if (REQUEST_WANTS_TYPE (request, REQUEST_MIME_LIST))
1977     {
1978         if (has_problem (directory, file, lacks_mime_list))
1979         {
1980             return FALSE;
1981         }
1982     }
1983 
1984     if (REQUEST_WANTS_TYPE (request, REQUEST_LINK_INFO))
1985     {
1986         if (has_problem (directory, file, lacks_link_info))
1987         {
1988             return FALSE;
1989         }
1990     }
1991 
1992     return TRUE;
1993 }
1994 
1995 static gboolean
call_ready_callbacks_at_idle(gpointer callback_data)1996 call_ready_callbacks_at_idle (gpointer callback_data)
1997 {
1998     CajaDirectory *directory;
1999     GList *node, *next;
2000     ReadyCallback *callback;
2001 
2002     directory = CAJA_DIRECTORY (callback_data);
2003     directory->details->call_ready_idle_id = 0;
2004 
2005     caja_directory_ref (directory);
2006 
2007     callback = NULL;
2008     while (1)
2009     {
2010         /* Check if any callbacks are non-active and call them if they are. */
2011         for (node = directory->details->call_when_ready_list;
2012                 node != NULL; node = next)
2013         {
2014             next = node->next;
2015             callback = node->data;
2016             if (!callback->active)
2017             {
2018                 /* Non-active, remove and call */
2019                 break;
2020             }
2021         }
2022         if (node == NULL)
2023         {
2024             break;
2025         }
2026 
2027         /* Callbacks are one-shots, so remove it now. */
2028         remove_callback_link_keep_data (directory, node);
2029 
2030         /* Call the callback. */
2031         ready_callback_call (directory, callback);
2032         g_free (callback);
2033     }
2034 
2035     caja_directory_async_state_changed (directory);
2036 
2037     caja_directory_unref (directory);
2038 
2039     return FALSE;
2040 }
2041 
2042 static void
schedule_call_ready_callbacks(CajaDirectory * directory)2043 schedule_call_ready_callbacks (CajaDirectory *directory)
2044 {
2045     if (directory->details->call_ready_idle_id == 0)
2046     {
2047         directory->details->call_ready_idle_id
2048             = g_idle_add (call_ready_callbacks_at_idle, directory);
2049     }
2050 }
2051 
2052 /* Marks all callbacks that are ready as non-active and
2053  * calls them at idle time, unless they are removed
2054  * before then */
2055 static gboolean
call_ready_callbacks(CajaDirectory * directory)2056 call_ready_callbacks (CajaDirectory *directory)
2057 {
2058     gboolean found_any;
2059     GList *node, *next;
2060     ReadyCallback *callback = NULL;
2061 
2062     found_any = FALSE;
2063 
2064     /* Check if any callbacks are satisifed and mark them for call them if they are. */
2065     for (node = directory->details->call_when_ready_list;
2066             node != NULL; node = next)
2067     {
2068         next = node->next;
2069         callback = node->data;
2070         if (callback->active &&
2071                 request_is_satisfied (directory, callback->file, callback->request))
2072         {
2073             callback->active = FALSE;
2074             found_any = TRUE;
2075         }
2076     }
2077 
2078     if (found_any)
2079     {
2080         schedule_call_ready_callbacks (directory);
2081     }
2082 
2083     return found_any;
2084 }
2085 
2086 gboolean
caja_directory_has_active_request_for_file(CajaDirectory * directory,CajaFile * file)2087 caja_directory_has_active_request_for_file (CajaDirectory *directory,
2088         CajaFile *file)
2089 {
2090     GList *node;
2091     ReadyCallback *callback = NULL;
2092     Monitor *monitor = NULL;
2093 
2094     for (node = directory->details->call_when_ready_list;
2095             node != NULL; node = node->next)
2096     {
2097         callback = node->data;
2098         if (callback->file == file ||
2099                 callback->file == NULL)
2100         {
2101             return TRUE;
2102         }
2103     }
2104 
2105     for (node = directory->details->monitor_list;
2106             node != NULL; node = node->next)
2107     {
2108         monitor = node->data;
2109         if (monitor->file == file ||
2110                 monitor->file == NULL)
2111         {
2112             return TRUE;
2113         }
2114     }
2115 
2116     return FALSE;
2117 }
2118 
2119 
2120 /* This checks if there's a request for monitoring the file list. */
2121 gboolean
caja_directory_is_anyone_monitoring_file_list(CajaDirectory * directory)2122 caja_directory_is_anyone_monitoring_file_list (CajaDirectory *directory)
2123 {
2124     if (directory->details->call_when_ready_counters[REQUEST_FILE_LIST] > 0)
2125     {
2126         return TRUE;
2127     }
2128 
2129     if (directory->details->monitor_counters[REQUEST_FILE_LIST] > 0)
2130     {
2131         return TRUE;
2132     }
2133 
2134     return FALSE;
2135 }
2136 
2137 /* This checks if the file list being monitored. */
2138 gboolean
caja_directory_is_file_list_monitored(CajaDirectory * directory)2139 caja_directory_is_file_list_monitored (CajaDirectory *directory)
2140 {
2141     return directory->details->file_list_monitored;
2142 }
2143 
2144 static void
mark_all_files_unconfirmed(CajaDirectory * directory)2145 mark_all_files_unconfirmed (CajaDirectory *directory)
2146 {
2147     GList *node;
2148     CajaFile *file = NULL;
2149 
2150     for (node = directory->details->file_list; node != NULL; node = node->next)
2151     {
2152         file = node->data;
2153         set_file_unconfirmed (file, TRUE);
2154     }
2155 }
2156 
2157 static void
directory_load_state_free(DirectoryLoadState * state)2158 directory_load_state_free (DirectoryLoadState *state)
2159 {
2160     if (state->enumerator)
2161     {
2162         if (!g_file_enumerator_is_closed (state->enumerator))
2163         {
2164             g_file_enumerator_close_async (state->enumerator,
2165                                            0, NULL, NULL, NULL);
2166         }
2167         g_object_unref (state->enumerator);
2168     }
2169 
2170     if (state->load_mime_list_hash != NULL)
2171     {
2172         istr_set_destroy (state->load_mime_list_hash);
2173     }
2174     caja_file_unref (state->load_directory_file);
2175     g_object_unref (state->cancellable);
2176     g_free (state);
2177 }
2178 
2179 static void
more_files_callback(GObject * source_object,GAsyncResult * res,gpointer user_data)2180 more_files_callback (GObject *source_object,
2181                      GAsyncResult *res,
2182                      gpointer user_data)
2183 {
2184     DirectoryLoadState *state;
2185     CajaDirectory *directory;
2186     GError *error;
2187     GList *files, *l;
2188     GFileInfo *info = NULL;
2189 
2190     state = user_data;
2191 
2192     if (state->directory == NULL)
2193     {
2194         /* Operation was cancelled. Bail out */
2195         directory_load_state_free (state);
2196         return;
2197     }
2198 
2199     directory = caja_directory_ref (state->directory);
2200 
2201     g_assert (directory->details->directory_load_in_progress != NULL);
2202     g_assert (directory->details->directory_load_in_progress == state);
2203 
2204     error = NULL;
2205     files = g_file_enumerator_next_files_finish (state->enumerator,
2206             res, &error);
2207 
2208     for (l = files; l != NULL; l = l->next)
2209     {
2210         info = l->data;
2211         directory_load_one (directory, info);
2212         g_object_unref (info);
2213     }
2214 
2215     if (files == NULL)
2216     {
2217         directory_load_done (directory, error);
2218         directory_load_state_free (state);
2219     }
2220     else
2221     {
2222         g_file_enumerator_next_files_async (state->enumerator,
2223                                             DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
2224                                             G_PRIORITY_DEFAULT,
2225                                             state->cancellable,
2226                                             more_files_callback,
2227                                             state);
2228     }
2229 
2230     caja_directory_unref (directory);
2231 
2232     if (error)
2233     {
2234         g_error_free (error);
2235     }
2236 
2237     g_list_free (files);
2238 }
2239 
2240 static void
enumerate_children_callback(GObject * source_object,GAsyncResult * res,gpointer user_data)2241 enumerate_children_callback (GObject *source_object,
2242                              GAsyncResult *res,
2243                              gpointer user_data)
2244 {
2245     DirectoryLoadState *state;
2246     GFileEnumerator *enumerator;
2247     GError *error;
2248 
2249     state = user_data;
2250 
2251     if (state->directory == NULL)
2252     {
2253         /* Operation was cancelled. Bail out */
2254         directory_load_state_free (state);
2255         return;
2256     }
2257 
2258     error = NULL;
2259     enumerator = g_file_enumerate_children_finish  (G_FILE (source_object),
2260                  res, &error);
2261 
2262     if (enumerator == NULL)
2263     {
2264         directory_load_done (state->directory, error);
2265         g_error_free (error);
2266         directory_load_state_free (state);
2267         return;
2268     }
2269     else
2270     {
2271         state->enumerator = enumerator;
2272         g_file_enumerator_next_files_async (state->enumerator,
2273                                             DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
2274                                             G_PRIORITY_DEFAULT,
2275                                             state->cancellable,
2276                                             more_files_callback,
2277                                             state);
2278     }
2279 }
2280 
2281 
2282 /* Start monitoring the file list if it isn't already. */
2283 static void
start_monitoring_file_list(CajaDirectory * directory)2284 start_monitoring_file_list (CajaDirectory *directory)
2285 {
2286     DirectoryLoadState *state;
2287 
2288     if (!directory->details->file_list_monitored)
2289     {
2290         g_assert (!directory->details->directory_load_in_progress);
2291         directory->details->file_list_monitored = TRUE;
2292         caja_file_list_ref (directory->details->file_list);
2293     }
2294 
2295     if (directory->details->directory_loaded  ||
2296             directory->details->directory_load_in_progress != NULL)
2297     {
2298         return;
2299     }
2300 
2301     if (!async_job_start (directory, "file list"))
2302     {
2303         return;
2304     }
2305 
2306     mark_all_files_unconfirmed (directory);
2307 
2308     state = g_new0 (DirectoryLoadState, 1);
2309     state->directory = directory;
2310     state->cancellable = g_cancellable_new ();
2311     state->load_mime_list_hash = istr_set_new ();
2312     state->load_file_count = 0;
2313 
2314     g_assert (directory->details->location != NULL);
2315     state->load_directory_file =
2316         caja_directory_get_corresponding_file (directory);
2317     state->load_directory_file->details->loading_directory = TRUE;
2318 
2319 #ifdef DEBUG_LOAD_DIRECTORY
2320     g_message ("load_directory called to monitor file list of %p", directory->details->location);
2321 #endif
2322 
2323     directory->details->directory_load_in_progress = state;
2324 
2325     g_file_enumerate_children_async (directory->details->location,
2326                                      CAJA_FILE_DEFAULT_ATTRIBUTES,
2327                                      0, /* flags */
2328                                      G_PRIORITY_DEFAULT, /* prio */
2329                                      state->cancellable,
2330                                      enumerate_children_callback,
2331                                      state);
2332 }
2333 
2334 /* Stop monitoring the file list if it is being monitored. */
2335 void
caja_directory_stop_monitoring_file_list(CajaDirectory * directory)2336 caja_directory_stop_monitoring_file_list (CajaDirectory *directory)
2337 {
2338     if (!directory->details->file_list_monitored)
2339     {
2340         g_assert (directory->details->directory_load_in_progress == NULL);
2341         return;
2342     }
2343 
2344     directory->details->file_list_monitored = FALSE;
2345     file_list_cancel (directory);
2346     caja_file_list_unref (directory->details->file_list);
2347     directory->details->directory_loaded = FALSE;
2348 }
2349 
2350 static void
file_list_start_or_stop(CajaDirectory * directory)2351 file_list_start_or_stop (CajaDirectory *directory)
2352 {
2353     if (caja_directory_is_anyone_monitoring_file_list (directory))
2354     {
2355         start_monitoring_file_list (directory);
2356     }
2357     else
2358     {
2359         caja_directory_stop_monitoring_file_list (directory);
2360     }
2361 }
2362 
2363 void
caja_file_invalidate_count_and_mime_list(CajaFile * file)2364 caja_file_invalidate_count_and_mime_list (CajaFile *file)
2365 {
2366     CajaFileAttributes attributes;
2367 
2368     attributes = CAJA_FILE_ATTRIBUTE_DIRECTORY_ITEM_COUNT |
2369                  CAJA_FILE_ATTRIBUTE_DIRECTORY_ITEM_MIME_TYPES;
2370 
2371     caja_file_invalidate_attributes (file, attributes);
2372 }
2373 
2374 
2375 /* Reset count and mime list. Invalidating deep counts is handled by
2376  * itself elsewhere because it's a relatively heavyweight and
2377  * special-purpose operation (see bug 5863). Also, the shallow count
2378  * needs to be refreshed when filtering changes, but the deep count
2379  * deliberately does not take filtering into account.
2380  */
2381 void
caja_directory_invalidate_count_and_mime_list(CajaDirectory * directory)2382 caja_directory_invalidate_count_and_mime_list (CajaDirectory *directory)
2383 {
2384     CajaFile *file;
2385 
2386     file = caja_directory_get_existing_corresponding_file (directory);
2387     if (file != NULL)
2388     {
2389         caja_file_invalidate_count_and_mime_list (file);
2390     }
2391 
2392     caja_file_unref (file);
2393 }
2394 
2395 static void
caja_directory_invalidate_file_attributes(CajaDirectory * directory,CajaFileAttributes file_attributes)2396 caja_directory_invalidate_file_attributes (CajaDirectory      *directory,
2397         CajaFileAttributes  file_attributes)
2398 {
2399     GList *node;
2400 
2401     cancel_loading_attributes (directory, file_attributes);
2402 
2403     for (node = directory->details->file_list; node != NULL; node = node->next)
2404     {
2405         caja_file_invalidate_attributes_internal (CAJA_FILE (node->data),
2406                 file_attributes);
2407     }
2408 
2409     if (directory->details->as_file != NULL)
2410     {
2411         caja_file_invalidate_attributes_internal (directory->details->as_file,
2412                 file_attributes);
2413     }
2414 }
2415 
2416 void
caja_directory_force_reload_internal(CajaDirectory * directory,CajaFileAttributes file_attributes)2417 caja_directory_force_reload_internal (CajaDirectory     *directory,
2418                                       CajaFileAttributes file_attributes)
2419 {
2420     /* invalidate attributes that are getting reloaded for all files */
2421     caja_directory_invalidate_file_attributes (directory, file_attributes);
2422 
2423     /* Start a new directory load. */
2424     file_list_cancel (directory);
2425     directory->details->directory_loaded = FALSE;
2426 
2427     /* Start a new directory count. */
2428     caja_directory_invalidate_count_and_mime_list (directory);
2429 
2430     add_all_files_to_work_queue (directory);
2431     caja_directory_async_state_changed (directory);
2432 }
2433 
2434 static gboolean
monitor_includes_file(const Monitor * monitor,CajaFile * file)2435 monitor_includes_file (const Monitor *monitor,
2436                        CajaFile *file)
2437 {
2438     if (monitor->file == file)
2439     {
2440         return TRUE;
2441     }
2442     if (monitor->file != NULL)
2443     {
2444         return FALSE;
2445     }
2446     if (file == file->details->directory->details->as_file)
2447     {
2448         return FALSE;
2449     }
2450     return caja_file_should_show (file,
2451                                   monitor->monitor_hidden_files,
2452                                   TRUE,
2453 				  FALSE);
2454 }
2455 
2456 static gboolean
is_needy(CajaFile * file,FileCheck check_missing,RequestType request_type_wanted)2457 is_needy (CajaFile *file,
2458           FileCheck check_missing,
2459           RequestType request_type_wanted)
2460 {
2461     CajaDirectory *directory;
2462     GList *node;
2463 
2464     if (!(* check_missing) (file))
2465     {
2466         return FALSE;
2467     }
2468 
2469     directory = file->details->directory;
2470     if (directory->details->call_when_ready_counters[request_type_wanted] > 0)
2471     {
2472         ReadyCallback *callback = NULL;
2473 
2474         for (node = directory->details->call_when_ready_list;
2475                 node != NULL; node = node->next)
2476         {
2477             callback = node->data;
2478             if (callback->active &&
2479                     REQUEST_WANTS_TYPE (callback->request, request_type_wanted))
2480             {
2481                 if (callback->file == file)
2482                 {
2483                     return TRUE;
2484                 }
2485                 if (callback->file == NULL
2486                         && file != directory->details->as_file)
2487                 {
2488                     return TRUE;
2489                 }
2490             }
2491         }
2492     }
2493 
2494     if (directory->details->monitor_counters[request_type_wanted] > 0)
2495     {
2496         Monitor *monitor = NULL;
2497 
2498         for (node = directory->details->monitor_list;
2499                 node != NULL; node = node->next)
2500         {
2501             monitor = node->data;
2502             if (REQUEST_WANTS_TYPE (monitor->request, request_type_wanted))
2503             {
2504                 if (monitor_includes_file (monitor, file))
2505                 {
2506                     return TRUE;
2507                 }
2508             }
2509         }
2510     }
2511     return FALSE;
2512 }
2513 
2514 static void
directory_count_stop(CajaDirectory * directory)2515 directory_count_stop (CajaDirectory *directory)
2516 {
2517     if (directory->details->count_in_progress != NULL)
2518     {
2519         CajaFile *file;
2520 
2521         file = directory->details->count_in_progress->count_file;
2522         if (file != NULL)
2523         {
2524             g_assert (CAJA_IS_FILE (file));
2525             g_assert (file->details->directory == directory);
2526             if (is_needy (file,
2527                           should_get_directory_count_now,
2528                           REQUEST_DIRECTORY_COUNT))
2529             {
2530                 return;
2531             }
2532         }
2533 
2534         /* The count is not wanted, so stop it. */
2535         directory_count_cancel (directory);
2536     }
2537 }
2538 
2539 static guint
count_non_skipped_files(GList * list)2540 count_non_skipped_files (GList *list)
2541 {
2542     guint count;
2543     GList *node;
2544     GFileInfo *info = NULL;
2545 
2546     count = 0;
2547     for (node = list; node != NULL; node = node->next)
2548     {
2549         info = node->data;
2550         if (!should_skip_file (NULL, info))
2551         {
2552             count += 1;
2553         }
2554     }
2555     return count;
2556 }
2557 
2558 static void
count_children_done(CajaDirectory * directory,CajaFile * count_file,gboolean succeeded,int count)2559 count_children_done (CajaDirectory *directory,
2560                      CajaFile *count_file,
2561                      gboolean succeeded,
2562                      int count)
2563 {
2564     g_assert (CAJA_IS_FILE (count_file));
2565 
2566     count_file->details->directory_count_is_up_to_date = TRUE;
2567 
2568     /* Record either a failure or success. */
2569     if (!succeeded)
2570     {
2571         count_file->details->directory_count_failed = TRUE;
2572         count_file->details->got_directory_count = FALSE;
2573         count_file->details->directory_count = 0;
2574     }
2575     else
2576     {
2577         count_file->details->directory_count_failed = FALSE;
2578         count_file->details->got_directory_count = TRUE;
2579         count_file->details->directory_count = count;
2580     }
2581     directory->details->count_in_progress = NULL;
2582 
2583     /* Send file-changed even if count failed, so interested parties can
2584      * distinguish between unknowable and not-yet-known cases.
2585      */
2586     caja_file_changed (count_file);
2587 
2588     /* Start up the next one. */
2589     async_job_end (directory, "directory count");
2590     caja_directory_async_state_changed (directory);
2591 }
2592 
2593 static void
directory_count_state_free(DirectoryCountState * state)2594 directory_count_state_free (DirectoryCountState *state)
2595 {
2596     if (state->enumerator)
2597     {
2598         if (!g_file_enumerator_is_closed (state->enumerator))
2599         {
2600             g_file_enumerator_close_async (state->enumerator,
2601                                            0, NULL, NULL, NULL);
2602         }
2603         g_object_unref (state->enumerator);
2604     }
2605     g_object_unref (state->cancellable);
2606     caja_directory_unref (state->directory);
2607     g_free (state);
2608 }
2609 
2610 static void
count_more_files_callback(GObject * source_object,GAsyncResult * res,gpointer user_data)2611 count_more_files_callback (GObject *source_object,
2612                            GAsyncResult *res,
2613                            gpointer user_data)
2614 {
2615     DirectoryCountState *state;
2616     CajaDirectory *directory;
2617     GError *error;
2618     GList *files;
2619 
2620     state = user_data;
2621     directory = state->directory;
2622 
2623     if (g_cancellable_is_cancelled (state->cancellable))
2624     {
2625         /* Operation was cancelled. Bail out */
2626         directory->details->count_in_progress = NULL;
2627 
2628         async_job_end (directory, "directory count");
2629         caja_directory_async_state_changed (directory);
2630 
2631         directory_count_state_free (state);
2632 
2633         return;
2634     }
2635 
2636     g_assert (directory->details->count_in_progress != NULL);
2637     g_assert (directory->details->count_in_progress == state);
2638 
2639     error = NULL;
2640     files = g_file_enumerator_next_files_finish (state->enumerator,
2641             res, &error);
2642 
2643     state->file_count += count_non_skipped_files (files);
2644 
2645     if (files == NULL)
2646     {
2647         count_children_done (directory, state->count_file,
2648                              TRUE, state->file_count);
2649         directory_count_state_free (state);
2650     }
2651     else
2652     {
2653         g_file_enumerator_next_files_async (state->enumerator,
2654                                             DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
2655                                             G_PRIORITY_DEFAULT,
2656                                             state->cancellable,
2657                                             count_more_files_callback,
2658                                             state);
2659     }
2660 
2661     g_list_free_full (files, g_object_unref);
2662 
2663     if (error)
2664     {
2665         g_error_free (error);
2666     }
2667 }
2668 
2669 static void
count_children_callback(GObject * source_object,GAsyncResult * res,gpointer user_data)2670 count_children_callback (GObject *source_object,
2671                          GAsyncResult *res,
2672                          gpointer user_data)
2673 {
2674     DirectoryCountState *state;
2675     GFileEnumerator *enumerator;
2676     GError *error;
2677 
2678     state = user_data;
2679 
2680     if (g_cancellable_is_cancelled (state->cancellable))
2681     {
2682         CajaDirectory *directory;
2683 
2684         /* Operation was cancelled. Bail out */
2685         directory = state->directory;
2686         directory->details->count_in_progress = NULL;
2687 
2688         async_job_end (directory, "directory count");
2689         caja_directory_async_state_changed (directory);
2690 
2691         directory_count_state_free (state);
2692 
2693         return;
2694     }
2695 
2696     error = NULL;
2697     enumerator = g_file_enumerate_children_finish  (G_FILE (source_object),
2698                  res, &error);
2699 
2700     if (enumerator == NULL)
2701     {
2702         count_children_done (state->directory,
2703                              state->count_file,
2704                              FALSE, 0);
2705         g_error_free (error);
2706         directory_count_state_free (state);
2707         return;
2708     }
2709     else
2710     {
2711         state->enumerator = enumerator;
2712         g_file_enumerator_next_files_async (state->enumerator,
2713                                             DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
2714                                             G_PRIORITY_DEFAULT,
2715                                             state->cancellable,
2716                                             count_more_files_callback,
2717                                             state);
2718     }
2719 }
2720 
2721 static void
directory_count_start(CajaDirectory * directory,CajaFile * file,gboolean * doing_io)2722 directory_count_start (CajaDirectory *directory,
2723                        CajaFile *file,
2724                        gboolean *doing_io)
2725 {
2726     DirectoryCountState *state;
2727     GFile *location;
2728 
2729     if (directory->details->count_in_progress != NULL)
2730     {
2731         *doing_io = TRUE;
2732         return;
2733     }
2734 
2735     if (!is_needy (file,
2736                    should_get_directory_count_now,
2737                    REQUEST_DIRECTORY_COUNT))
2738     {
2739         return;
2740     }
2741     *doing_io = TRUE;
2742 
2743     if (!caja_file_is_directory (file))
2744     {
2745         file->details->directory_count_is_up_to_date = TRUE;
2746         file->details->directory_count_failed = FALSE;
2747         file->details->got_directory_count = FALSE;
2748 
2749         caja_directory_async_state_changed (directory);
2750         return;
2751     }
2752 
2753     if (!async_job_start (directory, "directory count"))
2754     {
2755         return;
2756     }
2757 
2758     /* Start counting. */
2759     state = g_new0 (DirectoryCountState, 1);
2760     state->count_file = file;
2761     state->directory = caja_directory_ref (directory);
2762     state->cancellable = g_cancellable_new ();
2763 
2764     directory->details->count_in_progress = state;
2765 
2766     location = caja_file_get_location (file);
2767 #ifdef DEBUG_LOAD_DIRECTORY
2768     {
2769         char *uri;
2770         uri = g_file_get_uri (location);
2771         g_message ("load_directory called to get shallow file count for %s", uri);
2772         g_free (uri);
2773     }
2774 #endif
2775 
2776     g_file_enumerate_children_async (location,
2777                                      G_FILE_ATTRIBUTE_STANDARD_NAME ","
2778                                      G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN ","
2779                                      G_FILE_ATTRIBUTE_STANDARD_IS_BACKUP,
2780                                      G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, /* flags */
2781                                      G_PRIORITY_DEFAULT, /* prio */
2782                                      state->cancellable,
2783                                      count_children_callback,
2784                                      state);
2785     g_object_unref (location);
2786 }
2787 
2788 static inline gboolean
seen_inode(DeepCountState * state,GFileInfo * info)2789 seen_inode (DeepCountState *state,
2790             GFileInfo *info)
2791 {
2792     guint64 inode, inode2;
2793     guint i;
2794 
2795     inode = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_UNIX_INODE);
2796 
2797     if (inode != 0)
2798     {
2799         for (i = 0; i < state->seen_deep_count_inodes->len; i++)
2800         {
2801             inode2 = g_array_index (state->seen_deep_count_inodes, guint64, i);
2802             if (inode == inode2)
2803             {
2804                 return TRUE;
2805             }
2806         }
2807     }
2808 
2809     return FALSE;
2810 }
2811 
2812 static inline void
mark_inode_as_seen(DeepCountState * state,GFileInfo * info)2813 mark_inode_as_seen (DeepCountState *state,
2814                     GFileInfo *info)
2815 {
2816     guint64 inode;
2817 
2818     inode = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_UNIX_INODE);
2819     if (inode != 0)
2820     {
2821         g_array_append_val (state->seen_deep_count_inodes, inode);
2822     }
2823 }
2824 
2825 static void
deep_count_one(DeepCountState * state,GFileInfo * info)2826 deep_count_one (DeepCountState *state,
2827                 GFileInfo *info)
2828 {
2829     CajaFile *file;
2830     gboolean is_seen_inode;
2831 
2832     if (should_skip_file (NULL, info))
2833     {
2834         return;
2835     }
2836 
2837     is_seen_inode = seen_inode (state, info);
2838     if (!is_seen_inode)
2839     {
2840         mark_inode_as_seen (state, info);
2841     }
2842 
2843     file = state->directory->details->deep_count_file;
2844 
2845     if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
2846     {
2847         const char *fs_id;
2848 
2849         /* Count the directory. */
2850         file->details->deep_directory_count += 1;
2851 
2852         /* Record the fact that we have to descend into this directory. */
2853 
2854         fs_id = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_ID_FILESYSTEM);
2855         if (g_strcmp0 (fs_id, state->fs_id) == 0)
2856         {
2857             GFile *subdir;
2858 
2859             /* only if it is on the same filesystem */
2860             subdir = g_file_get_child (state->deep_count_location, g_file_info_get_name (info));
2861             state->deep_count_subdirectories = g_list_prepend (state->deep_count_subdirectories, subdir);
2862         }
2863     }
2864     else
2865     {
2866         /* Even non-regular files count as files. */
2867         file->details->deep_file_count += 1;
2868     }
2869 
2870     /* Count the size. */
2871     if (!is_seen_inode && g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SIZE))
2872     {
2873         file->details->deep_size += g_file_info_get_size (info);
2874     }
2875     /* Count the disk size. */
2876     if (!is_seen_inode && g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE))
2877     {
2878         file->details->deep_size_on_disk +=
2879             g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE);
2880     }
2881 }
2882 
2883 static void
deep_count_state_free(DeepCountState * state)2884 deep_count_state_free (DeepCountState *state)
2885 {
2886     if (state->enumerator)
2887     {
2888         if (!g_file_enumerator_is_closed (state->enumerator))
2889         {
2890             g_file_enumerator_close_async (state->enumerator,
2891                                            0, NULL, NULL, NULL);
2892         }
2893         g_object_unref (state->enumerator);
2894     }
2895     g_object_unref (state->cancellable);
2896     if (state->deep_count_location)
2897     {
2898         g_object_unref (state->deep_count_location);
2899     }
2900     g_list_free_full (state->deep_count_subdirectories, g_object_unref);
2901     g_array_free (state->seen_deep_count_inodes, TRUE);
2902     g_free (state->fs_id);
2903     g_free (state);
2904 }
2905 
2906 static void
deep_count_next_dir(DeepCountState * state)2907 deep_count_next_dir (DeepCountState *state)
2908 {
2909     CajaFile *file;
2910     CajaDirectory *directory;
2911     gboolean done;
2912 
2913     directory = state->directory;
2914 
2915     g_object_unref (state->deep_count_location);
2916     state->deep_count_location = NULL;
2917 
2918     done = FALSE;
2919     file = directory->details->deep_count_file;
2920 
2921     if (state->deep_count_subdirectories != NULL)
2922     {
2923         GFile *location;
2924 
2925         /* Work on a new directory. */
2926         location = state->deep_count_subdirectories->data;
2927         state->deep_count_subdirectories = g_list_remove
2928                                            (state->deep_count_subdirectories, location);
2929         deep_count_load (state, location);
2930         g_object_unref (location);
2931     }
2932     else
2933     {
2934         file->details->deep_counts_status = CAJA_REQUEST_DONE;
2935         directory->details->deep_count_file = NULL;
2936         directory->details->deep_count_in_progress = NULL;
2937         deep_count_state_free (state);
2938         done = TRUE;
2939     }
2940 
2941     caja_file_updated_deep_count_in_progress (file);
2942 
2943     if (done)
2944     {
2945         caja_file_changed (file);
2946         async_job_end (directory, "deep count");
2947         caja_directory_async_state_changed (directory);
2948     }
2949 }
2950 
2951 static void
deep_count_more_files_callback(GObject * source_object,GAsyncResult * res,gpointer user_data)2952 deep_count_more_files_callback (GObject *source_object,
2953                                 GAsyncResult *res,
2954                                 gpointer user_data)
2955 {
2956     DeepCountState *state;
2957     CajaDirectory *directory;
2958     GList *files, *l;
2959     GFileInfo *info = NULL;
2960 
2961     state = user_data;
2962 
2963     if (state->directory == NULL)
2964     {
2965         /* Operation was cancelled. Bail out */
2966         deep_count_state_free (state);
2967         return;
2968     }
2969 
2970     directory = caja_directory_ref (state->directory);
2971 
2972     g_assert (directory->details->deep_count_in_progress != NULL);
2973     g_assert (directory->details->deep_count_in_progress == state);
2974 
2975     files = g_file_enumerator_next_files_finish (state->enumerator,
2976             res, NULL);
2977 
2978     for (l = files; l != NULL; l = l->next)
2979     {
2980         info = l->data;
2981         deep_count_one (state, info);
2982         g_object_unref (info);
2983     }
2984 
2985     if (files == NULL)
2986     {
2987         g_file_enumerator_close_async (state->enumerator, 0, NULL, NULL, NULL);
2988         g_object_unref (state->enumerator);
2989         state->enumerator = NULL;
2990 
2991         deep_count_next_dir (state);
2992     }
2993     else
2994     {
2995         g_file_enumerator_next_files_async (state->enumerator,
2996                                             DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
2997                                             G_PRIORITY_LOW,
2998                                             state->cancellable,
2999                                             deep_count_more_files_callback,
3000                                             state);
3001     }
3002 
3003     g_list_free (files);
3004 
3005     caja_directory_unref (directory);
3006 }
3007 
3008 static void
deep_count_callback(GObject * source_object,GAsyncResult * res,gpointer user_data)3009 deep_count_callback (GObject *source_object,
3010                      GAsyncResult *res,
3011                      gpointer user_data)
3012 {
3013     DeepCountState *state;
3014     GFileEnumerator *enumerator;
3015     CajaFile *file;
3016 
3017     state = user_data;
3018 
3019     if (state->directory == NULL)
3020     {
3021         /* Operation was cancelled. Bail out */
3022         deep_count_state_free (state);
3023         return;
3024     }
3025 
3026     file = state->directory->details->deep_count_file;
3027 
3028     enumerator = g_file_enumerate_children_finish  (G_FILE (source_object),	res, NULL);
3029 
3030     if (enumerator == NULL)
3031     {
3032         file->details->deep_unreadable_count += 1;
3033 
3034         deep_count_next_dir (state);
3035     }
3036     else
3037     {
3038         state->enumerator = enumerator;
3039         g_file_enumerator_next_files_async (state->enumerator,
3040                                             DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
3041                                             G_PRIORITY_LOW,
3042                                             state->cancellable,
3043                                             deep_count_more_files_callback,
3044                                             state);
3045     }
3046 }
3047 
3048 
3049 static void
deep_count_load(DeepCountState * state,GFile * location)3050 deep_count_load (DeepCountState *state, GFile *location)
3051 {
3052     state->deep_count_location = g_object_ref (location);
3053 
3054 #ifdef DEBUG_LOAD_DIRECTORY
3055     g_message ("load_directory called to get deep file count for %p", location);
3056 #endif
3057     g_file_enumerate_children_async (state->deep_count_location,
3058                                      G_FILE_ATTRIBUTE_STANDARD_NAME ","
3059                                      G_FILE_ATTRIBUTE_STANDARD_TYPE ","
3060                                      G_FILE_ATTRIBUTE_STANDARD_SIZE ","
3061                                      G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE ","
3062                                      G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN ","
3063                                      G_FILE_ATTRIBUTE_STANDARD_IS_BACKUP ","
3064                                      G_FILE_ATTRIBUTE_ID_FILESYSTEM ","
3065                                      G_FILE_ATTRIBUTE_UNIX_INODE,
3066                                      G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, /* flags */
3067                                      G_PRIORITY_LOW, /* prio */
3068                                      state->cancellable,
3069                                      deep_count_callback,
3070                                      state);
3071 }
3072 
3073 static void
deep_count_stop(CajaDirectory * directory)3074 deep_count_stop (CajaDirectory *directory)
3075 {
3076     if (directory->details->deep_count_in_progress != NULL)
3077     {
3078         CajaFile *file;
3079 
3080         file = directory->details->deep_count_file;
3081         if (file != NULL)
3082         {
3083             g_assert (CAJA_IS_FILE (file));
3084             g_assert (file->details->directory == directory);
3085             if (is_needy (file,
3086                           lacks_deep_count,
3087                           REQUEST_DEEP_COUNT))
3088             {
3089                 return;
3090             }
3091         }
3092 
3093         /* The count is not wanted, so stop it. */
3094         deep_count_cancel (directory);
3095     }
3096 }
3097 
3098 static void
deep_count_got_info(GObject * source_object,GAsyncResult * res,gpointer user_data)3099 deep_count_got_info (GObject *source_object,
3100                      GAsyncResult *res,
3101                      gpointer user_data)
3102 {
3103      GFileInfo *info;
3104      GFile *file = (GFile *)source_object;
3105      DeepCountState *state = (DeepCountState *)user_data;
3106 
3107      info = g_file_query_info_finish (file, res, NULL);
3108      if (info != NULL)
3109      {
3110          const char *id;
3111 
3112          id = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_ID_FILESYSTEM);
3113          state->fs_id = g_strdup (id);
3114          g_object_unref (info);
3115      }
3116      deep_count_load (state, file);
3117 }
3118 
3119 static void
deep_count_start(CajaDirectory * directory,CajaFile * file,gboolean * doing_io)3120 deep_count_start (CajaDirectory *directory,
3121                   CajaFile *file,
3122                   gboolean *doing_io)
3123 {
3124     GFile *location;
3125     DeepCountState *state;
3126 
3127     if (directory->details->deep_count_in_progress != NULL)
3128     {
3129         *doing_io = TRUE;
3130         return;
3131     }
3132 
3133     if (!is_needy (file,
3134                    lacks_deep_count,
3135                    REQUEST_DEEP_COUNT))
3136     {
3137         return;
3138     }
3139     *doing_io = TRUE;
3140 
3141     if (!caja_file_is_directory (file))
3142     {
3143         file->details->deep_counts_status = CAJA_REQUEST_DONE;
3144 
3145         caja_directory_async_state_changed (directory);
3146         return;
3147     }
3148 
3149     if (!async_job_start (directory, "deep count"))
3150     {
3151         return;
3152     }
3153 
3154     /* Start counting. */
3155     file->details->deep_counts_status = CAJA_REQUEST_IN_PROGRESS;
3156     file->details->deep_directory_count = 0;
3157     file->details->deep_file_count = 0;
3158     file->details->deep_unreadable_count = 0;
3159     file->details->deep_size = 0;
3160     file->details->deep_size_on_disk = 0;
3161     directory->details->deep_count_file = file;
3162 
3163     state = g_new0 (DeepCountState, 1);
3164     state->directory = directory;
3165     state->cancellable = g_cancellable_new ();
3166     state->seen_deep_count_inodes = g_array_new (FALSE, TRUE, sizeof (guint64));
3167     state->fs_id = NULL;
3168 
3169     directory->details->deep_count_in_progress = state;
3170 
3171     location = caja_file_get_location (file);
3172     g_file_query_info_async (location,
3173                              G_FILE_ATTRIBUTE_ID_FILESYSTEM,
3174                              G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
3175                              G_PRIORITY_DEFAULT,
3176                              NULL,
3177                              deep_count_got_info,
3178                              state);
3179     g_object_unref (location);
3180 }
3181 
3182 static void
mime_list_stop(CajaDirectory * directory)3183 mime_list_stop (CajaDirectory *directory)
3184 {
3185     if (directory->details->mime_list_in_progress != NULL)
3186     {
3187         CajaFile *file;
3188 
3189         file = directory->details->mime_list_in_progress->mime_list_file;
3190         if (file != NULL)
3191         {
3192             g_assert (CAJA_IS_FILE (file));
3193             g_assert (file->details->directory == directory);
3194             if (is_needy (file,
3195                           should_get_mime_list,
3196                           REQUEST_MIME_LIST))
3197             {
3198                 return;
3199             }
3200         }
3201 
3202         /* The count is not wanted, so stop it. */
3203         mime_list_cancel (directory);
3204     }
3205 }
3206 
3207 static void
mime_list_state_free(MimeListState * state)3208 mime_list_state_free (MimeListState *state)
3209 {
3210     if (state->enumerator)
3211     {
3212         if (!g_file_enumerator_is_closed (state->enumerator))
3213         {
3214             g_file_enumerator_close_async (state->enumerator,
3215                                            0, NULL, NULL, NULL);
3216         }
3217         g_object_unref (state->enumerator);
3218     }
3219     g_object_unref (state->cancellable);
3220     istr_set_destroy (state->mime_list_hash);
3221     caja_directory_unref (state->directory);
3222     g_free (state);
3223 }
3224 
3225 
3226 static void
mime_list_done(MimeListState * state,gboolean success)3227 mime_list_done (MimeListState *state, gboolean success)
3228 {
3229     CajaFile *file;
3230     CajaDirectory *directory;
3231 
3232     directory = state->directory;
3233     g_assert (directory != NULL);
3234 
3235     file = state->mime_list_file;
3236 
3237     file->details->mime_list_is_up_to_date = TRUE;
3238     g_list_free_full (file->details->mime_list, g_free);
3239     if (success)
3240     {
3241         file->details->mime_list_failed = TRUE;
3242         file->details->mime_list = NULL;
3243     }
3244     else
3245     {
3246         file->details->got_mime_list = TRUE;
3247         file->details->mime_list = istr_set_get_as_list	(state->mime_list_hash);
3248     }
3249     directory->details->mime_list_in_progress = NULL;
3250 
3251     /* Send file-changed even if getting the item type list
3252      * failed, so interested parties can distinguish between
3253      * unknowable and not-yet-known cases.
3254      */
3255     caja_file_changed (file);
3256 
3257     /* Start up the next one. */
3258     async_job_end (directory, "MIME list");
3259     caja_directory_async_state_changed (directory);
3260 }
3261 
3262 static void
mime_list_one(MimeListState * state,GFileInfo * info)3263 mime_list_one (MimeListState *state,
3264                GFileInfo     *info)
3265 {
3266     const char *mime_type;
3267 
3268     if (should_skip_file (NULL, info))
3269     {
3270         g_object_unref (info);
3271         return;
3272     }
3273 
3274     if ((mime_type = g_file_info_get_content_type (info)) != NULL)
3275     {
3276         g_hash_table_add (state->mime_list_hash, g_strdup (mime_type));
3277     }
3278 }
3279 
3280 static void
mime_list_callback(GObject * source_object,GAsyncResult * res,gpointer user_data)3281 mime_list_callback (GObject *source_object,
3282                     GAsyncResult *res,
3283                     gpointer user_data)
3284 {
3285     MimeListState *state;
3286     CajaDirectory *directory;
3287     GError *error;
3288     GList *files, *l;
3289     GFileInfo *info = NULL;
3290 
3291     state = user_data;
3292     directory = state->directory;
3293 
3294     if (g_cancellable_is_cancelled (state->cancellable))
3295     {
3296         /* Operation was cancelled. Bail out */
3297         directory->details->mime_list_in_progress = NULL;
3298 
3299         async_job_end (directory, "MIME list");
3300         caja_directory_async_state_changed (directory);
3301 
3302         mime_list_state_free (state);
3303 
3304         return;
3305     }
3306 
3307     g_assert (directory->details->mime_list_in_progress != NULL);
3308     g_assert (directory->details->mime_list_in_progress == state);
3309 
3310     error = NULL;
3311     files = g_file_enumerator_next_files_finish (state->enumerator,
3312             res, &error);
3313 
3314     for (l = files; l != NULL; l = l->next)
3315     {
3316         info = l->data;
3317         mime_list_one (state, info);
3318         g_object_unref (info);
3319     }
3320 
3321     if (files == NULL)
3322     {
3323         mime_list_done (state, error != NULL);
3324         mime_list_state_free (state);
3325     }
3326     else
3327     {
3328         g_file_enumerator_next_files_async (state->enumerator,
3329                                             DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
3330                                             G_PRIORITY_DEFAULT,
3331                                             state->cancellable,
3332                                             mime_list_callback,
3333                                             state);
3334     }
3335 
3336     g_list_free (files);
3337 
3338     if (error)
3339     {
3340         g_error_free (error);
3341     }
3342 }
3343 
3344 static void
list_mime_enum_callback(GObject * source_object,GAsyncResult * res,gpointer user_data)3345 list_mime_enum_callback (GObject *source_object,
3346                          GAsyncResult *res,
3347                          gpointer user_data)
3348 {
3349     MimeListState *state;
3350     GFileEnumerator *enumerator;
3351     GError *error;
3352 
3353     state = user_data;
3354 
3355     if (g_cancellable_is_cancelled (state->cancellable))
3356     {
3357         CajaDirectory *directory;
3358 
3359         /* Operation was cancelled. Bail out */
3360         directory = state->directory;
3361         directory->details->mime_list_in_progress = NULL;
3362 
3363         async_job_end (directory, "MIME list");
3364         caja_directory_async_state_changed (directory);
3365 
3366         mime_list_state_free (state);
3367 
3368         return;
3369     }
3370 
3371     error = NULL;
3372     enumerator = g_file_enumerate_children_finish  (G_FILE (source_object),
3373                  res, &error);
3374 
3375     if (enumerator == NULL)
3376     {
3377         mime_list_done (state, FALSE);
3378         g_error_free (error);
3379         mime_list_state_free (state);
3380         return;
3381     }
3382     else
3383     {
3384         state->enumerator = enumerator;
3385         g_file_enumerator_next_files_async (state->enumerator,
3386                                             DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
3387                                             G_PRIORITY_DEFAULT,
3388                                             state->cancellable,
3389                                             mime_list_callback,
3390                                             state);
3391     }
3392 }
3393 
3394 static void
mime_list_start(CajaDirectory * directory,CajaFile * file,gboolean * doing_io)3395 mime_list_start (CajaDirectory *directory,
3396                  CajaFile *file,
3397                  gboolean *doing_io)
3398 {
3399     MimeListState *state;
3400     GFile *location;
3401 
3402     mime_list_stop (directory);
3403 
3404     if (directory->details->mime_list_in_progress != NULL)
3405     {
3406         *doing_io = TRUE;
3407         return;
3408     }
3409 
3410     /* Figure out which file to get a mime list for. */
3411     if (!is_needy (file,
3412                    should_get_mime_list,
3413                    REQUEST_MIME_LIST))
3414     {
3415         return;
3416     }
3417     *doing_io = TRUE;
3418 
3419     if (!caja_file_is_directory (file))
3420     {
3421         g_list_free (file->details->mime_list);
3422         file->details->mime_list_failed = FALSE;
3423         file->details->got_mime_list = FALSE;
3424         file->details->mime_list_is_up_to_date = TRUE;
3425 
3426         caja_directory_async_state_changed (directory);
3427         return;
3428     }
3429 
3430     if (!async_job_start (directory, "MIME list"))
3431     {
3432         return;
3433     }
3434 
3435 
3436     state = g_new0 (MimeListState, 1);
3437     state->mime_list_file = file;
3438     state->directory = caja_directory_ref (directory);
3439     state->cancellable = g_cancellable_new ();
3440     state->mime_list_hash = istr_set_new ();
3441 
3442     directory->details->mime_list_in_progress = state;
3443 
3444     location = caja_file_get_location (file);
3445 #ifdef DEBUG_LOAD_DIRECTORY
3446     {
3447         char *uri;
3448         uri = g_file_get_uri (location);
3449         g_message ("load_directory called to get MIME list of %s", uri);
3450         g_free (uri);
3451     }
3452 #endif
3453 
3454     g_file_enumerate_children_async (location,
3455                                      G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
3456                                      0, /* flags */
3457                                      G_PRIORITY_LOW, /* prio */
3458                                      state->cancellable,
3459                                      list_mime_enum_callback,
3460                                      state);
3461     g_object_unref (location);
3462 }
3463 
3464 static void
top_left_stop(CajaDirectory * directory)3465 top_left_stop (CajaDirectory *directory)
3466 {
3467     if (directory->details->top_left_read_state != NULL)
3468     {
3469         CajaFile *file;
3470 
3471         file = directory->details->top_left_read_state->file;
3472         if (file != NULL)
3473         {
3474             g_assert (CAJA_IS_FILE (file));
3475             g_assert (file->details->directory == directory);
3476             if (is_needy (file,
3477                           lacks_top_left,
3478                           REQUEST_TOP_LEFT_TEXT) ||
3479                     is_needy (file,
3480                               lacks_large_top_left,
3481                               REQUEST_LARGE_TOP_LEFT_TEXT))
3482             {
3483                 return;
3484             }
3485         }
3486 
3487         /* The top left is not wanted, so stop it. */
3488         top_left_cancel (directory);
3489     }
3490 }
3491 
3492 static void
top_left_read_state_free(TopLeftTextReadState * state)3493 top_left_read_state_free (TopLeftTextReadState *state)
3494 {
3495     g_object_unref (state->cancellable);
3496     g_free (state);
3497 }
3498 
3499 static void
top_left_read_callback(GObject * source_object,GAsyncResult * res,gpointer callback_data)3500 top_left_read_callback (GObject *source_object,
3501                         GAsyncResult *res,
3502                         gpointer callback_data)
3503 {
3504     TopLeftTextReadState *state;
3505     CajaDirectory *directory;
3506     CajaFilePrivate *file_details;
3507     gsize file_size;
3508     char *file_contents;
3509 
3510     state = callback_data;
3511 
3512     if (state->directory == NULL)
3513     {
3514         /* Operation was cancelled. Bail out */
3515         top_left_read_state_free (state);
3516         return;
3517     }
3518 
3519     directory = caja_directory_ref (state->directory);
3520 
3521     file_details = state->file->details;
3522 
3523     file_details->top_left_text_is_up_to_date = TRUE;
3524     g_free (file_details->top_left_text);
3525 
3526     if (g_file_load_partial_contents_finish (G_FILE (source_object),
3527             res,
3528             &file_contents, &file_size,
3529             NULL, NULL))
3530     {
3531         file_details->top_left_text = caja_extract_top_left_text (file_contents, state->large, file_size);
3532         file_details->got_top_left_text = TRUE;
3533         file_details->got_large_top_left_text = state->large;
3534         g_free (file_contents);
3535     }
3536     else
3537     {
3538         file_details->top_left_text = NULL;
3539         file_details->got_top_left_text = FALSE;
3540         file_details->got_large_top_left_text = FALSE;
3541     }
3542 
3543     caja_file_changed (state->file);
3544 
3545     directory->details->top_left_read_state = NULL;
3546     async_job_end (directory, "top left");
3547 
3548     top_left_read_state_free (state);
3549 
3550     caja_directory_async_state_changed (directory);
3551 
3552     caja_directory_unref (directory);
3553 }
3554 
3555 static int
count_lines(const char * text,int length)3556 count_lines (const char *text, int length)
3557 {
3558     int count, i;
3559 
3560     count = 0;
3561     for (i = 0; i < length; i++)
3562     {
3563         count += *text++ == '\n';
3564     }
3565     return count;
3566 }
3567 
3568 static gboolean
top_left_read_more_callback(const char * file_contents,goffset bytes_read,gpointer callback_data)3569 top_left_read_more_callback (const char *file_contents,
3570                              goffset bytes_read,
3571                              gpointer callback_data)
3572 {
3573     TopLeftTextReadState *state;
3574 
3575     state = callback_data;
3576 
3577     /* Stop reading when we have enough. */
3578     if (state->large)
3579     {
3580         return bytes_read < CAJA_FILE_LARGE_TOP_LEFT_TEXT_MAXIMUM_BYTES &&
3581                count_lines (file_contents, bytes_read) <= CAJA_FILE_LARGE_TOP_LEFT_TEXT_MAXIMUM_LINES;
3582     }
3583     else
3584     {
3585         return bytes_read < CAJA_FILE_TOP_LEFT_TEXT_MAXIMUM_BYTES &&
3586                count_lines (file_contents, bytes_read) <= CAJA_FILE_TOP_LEFT_TEXT_MAXIMUM_LINES;
3587     }
3588 }
3589 
3590 static void
top_left_start(CajaDirectory * directory,CajaFile * file,gboolean * doing_io)3591 top_left_start (CajaDirectory *directory,
3592                 CajaFile *file,
3593                 gboolean *doing_io)
3594 {
3595     GFile *location;
3596     gboolean needs_large;
3597     TopLeftTextReadState *state;
3598 
3599     if (directory->details->top_left_read_state != NULL)
3600     {
3601         *doing_io = TRUE;
3602         return;
3603     }
3604 
3605     needs_large = FALSE;
3606 
3607     if (is_needy (file,
3608                   lacks_large_top_left,
3609                   REQUEST_LARGE_TOP_LEFT_TEXT))
3610     {
3611         needs_large = TRUE;
3612     }
3613 
3614     /* Figure out which file to read the top left for. */
3615     if (!(needs_large ||
3616             is_needy (file,
3617                       lacks_top_left,
3618                       REQUEST_TOP_LEFT_TEXT)))
3619     {
3620         return;
3621     }
3622     *doing_io = TRUE;
3623 
3624     if (!caja_file_contains_text (file))
3625     {
3626         g_free (file->details->top_left_text);
3627         file->details->top_left_text = NULL;
3628         file->details->got_top_left_text = FALSE;
3629         file->details->got_large_top_left_text = FALSE;
3630         file->details->top_left_text_is_up_to_date = TRUE;
3631 
3632         caja_directory_async_state_changed (directory);
3633         return;
3634     }
3635 
3636     if (!async_job_start (directory, "top left"))
3637     {
3638         return;
3639     }
3640 
3641     /* Start reading. */
3642     state = g_new0 (TopLeftTextReadState, 1);
3643     state->directory = directory;
3644     state->cancellable = g_cancellable_new ();
3645     state->large = needs_large;
3646     state->file = file;
3647 
3648     directory->details->top_left_read_state = state;
3649 
3650     location = caja_file_get_location (file);
3651     g_file_load_partial_contents_async (location,
3652                                         state->cancellable,
3653                                         top_left_read_more_callback,
3654                                         top_left_read_callback,
3655                                         state);
3656     g_object_unref (location);
3657 }
3658 
3659 static void
get_info_state_free(GetInfoState * state)3660 get_info_state_free (GetInfoState *state)
3661 {
3662     g_object_unref (state->cancellable);
3663     g_free (state);
3664 }
3665 
3666 static void
query_info_callback(GObject * source_object,GAsyncResult * res,gpointer user_data)3667 query_info_callback (GObject *source_object,
3668                      GAsyncResult *res,
3669                      gpointer user_data)
3670 {
3671     CajaDirectory *directory;
3672     CajaFile *get_info_file;
3673     GFileInfo *info;
3674     GetInfoState *state;
3675     GError *error;
3676 
3677     state = user_data;
3678 
3679     if (state->directory == NULL)
3680     {
3681         /* Operation was cancelled. Bail out */
3682         get_info_state_free (state);
3683         return;
3684     }
3685 
3686     directory = caja_directory_ref (state->directory);
3687 
3688     get_info_file = directory->details->get_info_file;
3689     g_assert (CAJA_IS_FILE (get_info_file));
3690 
3691     directory->details->get_info_file = NULL;
3692     directory->details->get_info_in_progress = NULL;
3693 
3694     /* ref here because we might be removing the last ref when we
3695      * mark the file gone below, but we need to keep a ref at
3696      * least long enough to send the change notification.
3697      */
3698     caja_file_ref (get_info_file);
3699 
3700     error = NULL;
3701     info = g_file_query_info_finish (G_FILE (source_object), res, &error);
3702 
3703     if (info == NULL)
3704     {
3705         if (error->domain == G_IO_ERROR && error->code == G_IO_ERROR_NOT_FOUND)
3706         {
3707             /* mark file as gone */
3708             caja_file_mark_gone (get_info_file);
3709         }
3710         get_info_file->details->file_info_is_up_to_date = TRUE;
3711         caja_file_clear_info (get_info_file);
3712         get_info_file->details->get_info_failed = TRUE;
3713         get_info_file->details->get_info_error = error;
3714     }
3715     else
3716     {
3717         caja_file_update_info (get_info_file, info);
3718         g_object_unref (info);
3719     }
3720 
3721     caja_file_changed (get_info_file);
3722     caja_file_unref (get_info_file);
3723 
3724     async_job_end (directory, "file info");
3725     caja_directory_async_state_changed (directory);
3726 
3727     caja_directory_unref (directory);
3728 
3729     get_info_state_free (state);
3730 }
3731 
3732 static void
file_info_stop(CajaDirectory * directory)3733 file_info_stop (CajaDirectory *directory)
3734 {
3735     if (directory->details->get_info_in_progress != NULL)
3736     {
3737         CajaFile *file;
3738 
3739         file = directory->details->get_info_file;
3740         if (file != NULL)
3741         {
3742             g_assert (CAJA_IS_FILE (file));
3743             g_assert (file->details->directory == directory);
3744             if (is_needy (file, lacks_info, REQUEST_FILE_INFO))
3745             {
3746                 return;
3747             }
3748         }
3749 
3750         /* The info is not wanted, so stop it. */
3751         file_info_cancel (directory);
3752     }
3753 }
3754 
3755 static void
file_info_start(CajaDirectory * directory,CajaFile * file,gboolean * doing_io)3756 file_info_start (CajaDirectory *directory,
3757                  CajaFile *file,
3758                  gboolean *doing_io)
3759 {
3760     GFile *location;
3761     GetInfoState *state;
3762 
3763     file_info_stop (directory);
3764 
3765     if (directory->details->get_info_in_progress != NULL)
3766     {
3767         *doing_io = TRUE;
3768         return;
3769     }
3770 
3771     if (!is_needy (file, lacks_info, REQUEST_FILE_INFO))
3772     {
3773         return;
3774     }
3775     *doing_io = TRUE;
3776 
3777     if (!async_job_start (directory, "file info"))
3778     {
3779         return;
3780     }
3781 
3782     directory->details->get_info_file = file;
3783     file->details->get_info_failed = FALSE;
3784     if (file->details->get_info_error)
3785     {
3786         g_error_free (file->details->get_info_error);
3787         file->details->get_info_error = NULL;
3788     }
3789 
3790     state = g_new (GetInfoState, 1);
3791     state->directory = directory;
3792     state->cancellable = g_cancellable_new ();
3793 
3794     directory->details->get_info_in_progress = state;
3795 
3796     location = caja_file_get_location (file);
3797     g_file_query_info_async (location,
3798                              CAJA_FILE_DEFAULT_ATTRIBUTES,
3799                              0,
3800                              G_PRIORITY_DEFAULT,
3801                              state->cancellable, query_info_callback, state);
3802     g_object_unref (location);
3803 }
3804 
is_trusted_system_desktop_file(GFile * file)3805 static gboolean is_trusted_system_desktop_file (GFile *file)
3806 {
3807     gboolean res = FALSE;
3808     GFileInfo *info;
3809     const gchar *target = NULL;
3810     GFile *location = NULL;
3811 
3812     info = g_file_query_info (file,
3813                               G_FILE_ATTRIBUTE_STANDARD_TYPE ","
3814                               G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET,
3815                               G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
3816                               NULL,
3817                               NULL);
3818 
3819     if (info == NULL)
3820     {
3821         return FALSE;
3822     }
3823 
3824     target = g_file_info_get_symlink_target (info);
3825     if (!target) {
3826         goto done;
3827     }
3828 
3829     location = g_file_new_for_path (target);
3830 
3831     res = caja_is_in_system_dir (location);
3832 
3833 done:
3834     if (location) {
3835         g_object_unref (location);
3836     }
3837     g_object_unref (info);
3838 
3839     return res;
3840 }
3841 
3842 static gboolean
is_link_trusted(CajaFile * file,gboolean is_launcher)3843 is_link_trusted (CajaFile *file,
3844                  gboolean is_launcher)
3845 {
3846     gboolean res = FALSE;
3847 
3848     if (!is_launcher)
3849     {
3850         return TRUE;
3851     }
3852 
3853     if (caja_file_can_execute (file) && caja_file_is_local (file))
3854     {
3855         GFile *location;
3856 
3857         location = caja_file_get_location (file);
3858 
3859         res = caja_is_in_system_dir (location) ||
3860               is_trusted_system_desktop_file (location) ||
3861               caja_is_in_desktop_dir (location);
3862 
3863         if (!res)
3864         {
3865               res = caja_file_get_boolean_metadata (file,
3866                                                     CAJA_METADATA_KEY_TRUSTED_LAUNCHER,
3867                                                     FALSE);
3868         }
3869 
3870         g_object_unref (location);
3871     }
3872 
3873     return res;
3874 }
3875 
3876 static void
link_info_done(CajaDirectory * directory,CajaFile * file,const char * uri,const char * name,const char * icon,gboolean is_launcher,gboolean is_foreign)3877 link_info_done (CajaDirectory *directory,
3878                 CajaFile *file,
3879                 const char *uri,
3880                 const char *name,
3881                 const char *icon,
3882                 gboolean is_launcher,
3883                 gboolean is_foreign)
3884 {
3885     gboolean is_trusted;
3886 
3887     file->details->link_info_is_up_to_date = TRUE;
3888 
3889     is_trusted = is_link_trusted (file, is_launcher);
3890 
3891     if (is_trusted)
3892     {
3893         caja_file_set_display_name (file, name, name, TRUE);
3894     }
3895     else
3896     {
3897         caja_file_set_display_name (file, NULL, NULL, TRUE);
3898     }
3899 
3900     file->details->got_link_info = TRUE;
3901     g_free (file->details->custom_icon);
3902     file->details->custom_icon = NULL;
3903     if (uri)
3904     {
3905         g_free (file->details->activation_uri);
3906         file->details->activation_uri = NULL;
3907         file->details->got_custom_activation_uri = TRUE;
3908         file->details->activation_uri = g_strdup (uri);
3909     }
3910     if (is_trusted)
3911     {
3912         file->details->custom_icon = g_strdup (icon);
3913     }
3914     file->details->is_launcher = is_launcher;
3915     file->details->is_foreign_link = is_foreign;
3916     file->details->is_trusted_link = is_trusted;
3917 
3918     caja_directory_async_state_changed (directory);
3919 }
3920 
3921 static gboolean
should_read_link_info_sync(CajaFile * file)3922 should_read_link_info_sync (CajaFile *file)
3923 {
3924 #ifdef READ_LOCAL_LINKS_SYNC
3925     return (caja_file_is_local (file) && !caja_file_is_directory (file));
3926 #else
3927     return FALSE;
3928 #endif
3929 }
3930 
3931 static void
link_info_stop(CajaDirectory * directory)3932 link_info_stop (CajaDirectory *directory)
3933 {
3934     if (directory->details->link_info_read_state != NULL)
3935     {
3936         CajaFile *file;
3937 
3938         file = directory->details->link_info_read_state->file;
3939 
3940         if (file != NULL)
3941         {
3942             g_assert (CAJA_IS_FILE (file));
3943             g_assert (file->details->directory == directory);
3944             if (is_needy (file,
3945                           lacks_link_info,
3946                           REQUEST_LINK_INFO))
3947             {
3948                 return;
3949             }
3950         }
3951 
3952         /* The link info is not wanted, so stop it. */
3953         link_info_cancel (directory);
3954     }
3955 }
3956 
3957 static void
link_info_got_data(CajaDirectory * directory,CajaFile * file,gboolean result,goffset bytes_read,char * file_contents)3958 link_info_got_data (CajaDirectory *directory,
3959                     CajaFile *file,
3960                     gboolean result,
3961                     goffset bytes_read,
3962                     char *file_contents)
3963 {
3964     char *uri, *name, *icon;
3965     gboolean is_launcher;
3966     gboolean is_foreign;
3967 
3968     caja_directory_ref (directory);
3969 
3970     uri = NULL;
3971     name = NULL;
3972     icon = NULL;
3973     is_launcher = FALSE;
3974     is_foreign = FALSE;
3975 
3976     /* Handle the case where we read the Caja link. */
3977     if (result)
3978     {
3979         char *link_uri;
3980 
3981         link_uri = caja_file_get_uri (file);
3982         caja_link_get_link_info_given_file_contents (file_contents, bytes_read, link_uri,
3983                 &uri, &name, &icon, &is_launcher, &is_foreign);
3984         g_free (link_uri);
3985     }
3986     else
3987     {
3988         /* FIXME bugzilla.gnome.org 42433: We should report this error to the user. */
3989     }
3990 
3991     caja_file_ref (file);
3992     link_info_done (directory, file, uri, name, icon, is_launcher, is_foreign);
3993     caja_file_changed (file);
3994     caja_file_unref (file);
3995 
3996     g_free (uri);
3997     g_free (name);
3998     g_free (icon);
3999 
4000     caja_directory_unref (directory);
4001 }
4002 
4003 static void
link_info_read_state_free(LinkInfoReadState * state)4004 link_info_read_state_free (LinkInfoReadState *state)
4005 {
4006     g_object_unref (state->cancellable);
4007     g_free (state);
4008 }
4009 
4010 static void
link_info_caja_link_read_callback(GObject * source_object,GAsyncResult * res,gpointer user_data)4011 link_info_caja_link_read_callback (GObject *source_object,
4012                                    GAsyncResult *res,
4013                                    gpointer user_data)
4014 {
4015     LinkInfoReadState *state;
4016     gsize file_size;
4017     char *file_contents;
4018     gboolean result;
4019     CajaDirectory *directory;
4020 
4021     state = user_data;
4022 
4023     if (state->directory == NULL)
4024     {
4025         /* Operation was cancelled. Bail out */
4026         link_info_read_state_free (state);
4027         return;
4028     }
4029 
4030     directory = caja_directory_ref (state->directory);
4031 
4032     result = g_file_load_contents_finish (G_FILE (source_object),
4033                                           res,
4034                                           &file_contents, &file_size,
4035                                           NULL, NULL);
4036 
4037     state->directory->details->link_info_read_state = NULL;
4038     async_job_end (state->directory, "link info");
4039 
4040     link_info_got_data (state->directory, state->file, result, file_size, file_contents);
4041 
4042     if (result)
4043     {
4044         g_free (file_contents);
4045     }
4046 
4047     link_info_read_state_free (state);
4048 
4049     caja_directory_unref (directory);
4050 }
4051 
4052 static void
link_info_start(CajaDirectory * directory,CajaFile * file,gboolean * doing_io)4053 link_info_start (CajaDirectory *directory,
4054                  CajaFile *file,
4055                  gboolean *doing_io)
4056 {
4057     GFile *location;
4058     gboolean caja_style_link;
4059     gsize file_size;
4060     char *file_contents;
4061     gboolean result;
4062     LinkInfoReadState *state;
4063 
4064     if (directory->details->link_info_read_state != NULL)
4065     {
4066         *doing_io = TRUE;
4067         return;
4068     }
4069 
4070     if (!is_needy (file,
4071                    lacks_link_info,
4072                    REQUEST_LINK_INFO))
4073     {
4074         return;
4075     }
4076     *doing_io = TRUE;
4077 
4078     /* Figure out if it is a link. */
4079     caja_style_link = caja_file_is_caja_link (file);
4080     location = caja_file_get_location (file);
4081 
4082     /* If it's not a link we are done. If it is, we need to read it. */
4083     if (!caja_style_link)
4084     {
4085         link_info_done (directory, file, NULL, NULL, NULL, FALSE, FALSE);
4086     }
4087     else if (should_read_link_info_sync (file))
4088     {
4089         result = g_file_load_contents (location, NULL, &file_contents, &file_size, NULL, NULL);
4090         link_info_got_data (directory, file, result, file_size, file_contents);
4091         g_free (file_contents);
4092     }
4093     else
4094     {
4095         if (!async_job_start (directory, "link info"))
4096         {
4097             g_object_unref (location);
4098             return;
4099         }
4100 
4101         state = g_new0 (LinkInfoReadState, 1);
4102         state->directory = directory;
4103         state->file = file;
4104         state->cancellable = g_cancellable_new ();
4105 
4106         directory->details->link_info_read_state = state;
4107 
4108         g_file_load_contents_async (location,
4109                                     state->cancellable,
4110                                     link_info_caja_link_read_callback,
4111                                     state);
4112     }
4113     g_object_unref (location);
4114 }
4115 
4116 static void
thumbnail_done(CajaDirectory * directory,CajaFile * file,GdkPixbuf * pixbuf,gboolean tried_original)4117 thumbnail_done (CajaDirectory *directory,
4118                 CajaFile *file,
4119                 GdkPixbuf *pixbuf,
4120                 gboolean tried_original)
4121 {
4122     file->details->thumbnail_is_up_to_date = TRUE;
4123     file->details->thumbnail_tried_original  = tried_original;
4124     if (file->details->thumbnail)
4125     {
4126         g_object_unref (file->details->thumbnail);
4127         file->details->thumbnail = NULL;
4128     }
4129     if (pixbuf)
4130     {
4131         time_t thumb_mtime = 0;
4132 
4133         if (tried_original)
4134         {
4135             thumb_mtime = file->details->mtime;
4136         }
4137         else
4138         {
4139             const char *thumb_mtime_str;
4140 
4141             thumb_mtime_str = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::MTime");
4142             if (thumb_mtime_str)
4143             {
4144                 thumb_mtime = atol (thumb_mtime_str);
4145             }
4146         }
4147 
4148         if (thumb_mtime == 0 ||
4149                 thumb_mtime == file->details->mtime)
4150         {
4151             file->details->thumbnail = g_object_ref (pixbuf);
4152             file->details->thumbnail_mtime = thumb_mtime;
4153         }
4154         else
4155         {
4156             g_free (file->details->thumbnail_path);
4157             file->details->thumbnail_path = NULL;
4158         }
4159     }
4160 
4161     caja_directory_async_state_changed (directory);
4162 }
4163 
4164 static void
thumbnail_stop(CajaDirectory * directory)4165 thumbnail_stop (CajaDirectory *directory)
4166 {
4167     if (directory->details->thumbnail_state != NULL)
4168     {
4169         CajaFile *file;
4170 
4171         file = directory->details->thumbnail_state->file;
4172 
4173         if (file != NULL)
4174         {
4175             g_assert (CAJA_IS_FILE (file));
4176             g_assert (file->details->directory == directory);
4177             if (is_needy (file,
4178                           lacks_thumbnail,
4179                           REQUEST_THUMBNAIL))
4180             {
4181                 return;
4182             }
4183         }
4184 
4185         /* The link info is not wanted, so stop it. */
4186         thumbnail_cancel (directory);
4187     }
4188 }
4189 
4190 static void
thumbnail_got_pixbuf(CajaDirectory * directory,CajaFile * file,GdkPixbuf * pixbuf,gboolean tried_original)4191 thumbnail_got_pixbuf (CajaDirectory *directory,
4192                       CajaFile *file,
4193                       GdkPixbuf *pixbuf,
4194                       gboolean tried_original)
4195 {
4196     caja_directory_ref (directory);
4197 
4198     caja_file_ref (file);
4199     thumbnail_done (directory, file, pixbuf, tried_original);
4200     caja_file_changed (file);
4201     caja_file_unref (file);
4202 
4203     if (pixbuf)
4204     {
4205         g_object_unref (pixbuf);
4206     }
4207 
4208     caja_directory_unref (directory);
4209 }
4210 
4211 static void
thumbnail_state_free(ThumbnailState * state)4212 thumbnail_state_free (ThumbnailState *state)
4213 {
4214     g_object_unref (state->cancellable);
4215     g_free (state);
4216 }
4217 
4218 extern int cached_thumbnail_size;
4219 
4220 /* scale very large images down to the max. size we need */
4221 static void
thumbnail_loader_size_prepared(GdkPixbufLoader * loader,int width,int height,gpointer user_data)4222 thumbnail_loader_size_prepared (GdkPixbufLoader *loader,
4223                                 int width,
4224                                 int height,
4225                                 gpointer user_data)
4226 {
4227     int max_thumbnail_size;
4228     double aspect_ratio;
4229 
4230     aspect_ratio = ((double) width) / height;
4231 
4232     /* cf. caja_file_get_icon() */
4233     max_thumbnail_size = CAJA_ICON_SIZE_LARGEST * cached_thumbnail_size / CAJA_ICON_SIZE_STANDARD;
4234     if (MAX (width, height) > max_thumbnail_size)
4235     {
4236         if (width > height)
4237         {
4238             width = max_thumbnail_size;
4239             height = width / aspect_ratio;
4240         }
4241         else
4242         {
4243             height = max_thumbnail_size;
4244             width = height * aspect_ratio;
4245         }
4246 
4247         gdk_pixbuf_loader_set_size (loader, width, height);
4248     }
4249 }
4250 
4251 static GdkPixbuf *
get_pixbuf_for_content(goffset file_len,char * file_contents)4252 get_pixbuf_for_content (goffset file_len,
4253                         char *file_contents)
4254 {
4255     gboolean res;
4256     GdkPixbuf *pixbuf, *pixbuf2;
4257     GdkPixbufLoader *loader;
4258     gsize chunk_len;
4259     pixbuf = NULL;
4260 
4261     loader = gdk_pixbuf_loader_new ();
4262     g_signal_connect (loader, "size-prepared",
4263                       G_CALLBACK (thumbnail_loader_size_prepared),
4264                       NULL);
4265 
4266     /* For some reason we have to write in chunks, or gdk-pixbuf fails */
4267     res = TRUE;
4268     while (res && file_len > 0)
4269     {
4270         chunk_len = file_len;
4271         res = gdk_pixbuf_loader_write (loader, (guchar *) file_contents, chunk_len, NULL);
4272         file_contents += chunk_len;
4273         file_len -= chunk_len;
4274     }
4275     if (res)
4276     {
4277         res = gdk_pixbuf_loader_close (loader, NULL);
4278     }
4279     if (res)
4280     {
4281         pixbuf = g_object_ref (gdk_pixbuf_loader_get_pixbuf (loader));
4282     }
4283     g_object_unref (G_OBJECT (loader));
4284 
4285     if (pixbuf)
4286     {
4287         pixbuf2 = gdk_pixbuf_apply_embedded_orientation (pixbuf);
4288         g_object_unref (pixbuf);
4289         pixbuf = pixbuf2;
4290     }
4291     return pixbuf;
4292 }
4293 
4294 
4295 static void
thumbnail_read_callback(GObject * source_object,GAsyncResult * res,gpointer user_data)4296 thumbnail_read_callback (GObject *source_object,
4297                          GAsyncResult *res,
4298                          gpointer user_data)
4299 {
4300     ThumbnailState *state;
4301     gsize file_size;
4302     char *file_contents;
4303     gboolean result;
4304     CajaDirectory *directory;
4305     GdkPixbuf *pixbuf;
4306 
4307     state = user_data;
4308 
4309     if (state->directory == NULL)
4310     {
4311         /* Operation was cancelled. Bail out */
4312         thumbnail_state_free (state);
4313         return;
4314     }
4315 
4316     directory = caja_directory_ref (state->directory);
4317 
4318     result = g_file_load_contents_finish (G_FILE (source_object),
4319                                           res,
4320                                           &file_contents, &file_size,
4321                                           NULL, NULL);
4322 
4323     pixbuf = NULL;
4324     if (result)
4325     {
4326         pixbuf = get_pixbuf_for_content (file_size, file_contents);
4327         g_free (file_contents);
4328     }
4329 
4330     if (pixbuf == NULL && state->trying_original)
4331     {
4332         GFile *location;
4333 
4334         state->trying_original = FALSE;
4335 
4336         location = g_file_new_for_path (state->file->details->thumbnail_path);
4337         g_file_load_contents_async (location,
4338                                     state->cancellable,
4339                                     thumbnail_read_callback,
4340                                     state);
4341         g_object_unref (location);
4342     }
4343     else
4344     {
4345         state->directory->details->thumbnail_state = NULL;
4346         async_job_end (state->directory, "thumbnail");
4347 
4348         thumbnail_got_pixbuf (state->directory, state->file, pixbuf, state->tried_original);
4349 
4350         thumbnail_state_free (state);
4351     }
4352 
4353     caja_directory_unref (directory);
4354 }
4355 
4356 static void
thumbnail_start(CajaDirectory * directory,CajaFile * file,gboolean * doing_io)4357 thumbnail_start (CajaDirectory *directory,
4358                  CajaFile *file,
4359                  gboolean *doing_io)
4360 {
4361     GFile *location;
4362     ThumbnailState *state;
4363 
4364     if (directory->details->thumbnail_state != NULL)
4365     {
4366         *doing_io = TRUE;
4367         return;
4368     }
4369 
4370     if (!is_needy (file,
4371                    lacks_thumbnail,
4372                    REQUEST_THUMBNAIL))
4373     {
4374         return;
4375     }
4376     *doing_io = TRUE;
4377 
4378     if (!async_job_start (directory, "thumbnail"))
4379     {
4380         return;
4381     }
4382 
4383     state = g_new0 (ThumbnailState, 1);
4384     state->directory = directory;
4385     state->file = file;
4386     state->cancellable = g_cancellable_new ();
4387 
4388     if (file->details->thumbnail_wants_original)
4389     {
4390         state->tried_original = TRUE;
4391         state->trying_original = TRUE;
4392         location = caja_file_get_location (file);
4393     }
4394     else
4395     {
4396         location = g_file_new_for_path (file->details->thumbnail_path);
4397     }
4398 
4399     directory->details->thumbnail_state = state;
4400 
4401     g_file_load_contents_async (location,
4402                                 state->cancellable,
4403                                 thumbnail_read_callback,
4404                                 state);
4405     g_object_unref (location);
4406 }
4407 
4408 static void
mount_stop(CajaDirectory * directory)4409 mount_stop (CajaDirectory *directory)
4410 {
4411     if (directory->details->mount_state != NULL)
4412     {
4413         CajaFile *file;
4414 
4415         file = directory->details->mount_state->file;
4416 
4417         if (file != NULL)
4418         {
4419             g_assert (CAJA_IS_FILE (file));
4420             g_assert (file->details->directory == directory);
4421             if (is_needy (file,
4422                           lacks_mount,
4423                           REQUEST_MOUNT))
4424             {
4425                 return;
4426             }
4427         }
4428 
4429         /* The link info is not wanted, so stop it. */
4430         mount_cancel (directory);
4431     }
4432 }
4433 
4434 static void
mount_state_free(MountState * state)4435 mount_state_free (MountState *state)
4436 {
4437     g_object_unref (state->cancellable);
4438     g_free (state);
4439 }
4440 
4441 static void
got_mount(MountState * state,GMount * mount)4442 got_mount (MountState *state, GMount *mount)
4443 {
4444     CajaDirectory *directory;
4445     CajaFile *file;
4446 
4447     directory = caja_directory_ref (state->directory);
4448 
4449     state->directory->details->mount_state = NULL;
4450     async_job_end (state->directory, "mount");
4451 
4452     file = caja_file_ref (state->file);
4453 
4454     file->details->mount_is_up_to_date = TRUE;
4455     caja_file_set_mount (file, mount);
4456 
4457     caja_directory_async_state_changed (directory);
4458     caja_file_changed (file);
4459 
4460     caja_file_unref (file);
4461 
4462     caja_directory_unref (directory);
4463 
4464     mount_state_free (state);
4465 
4466 }
4467 
4468 static void
find_enclosing_mount_callback(GObject * source_object,GAsyncResult * res,gpointer user_data)4469 find_enclosing_mount_callback (GObject *source_object,
4470                                GAsyncResult *res,
4471                                gpointer user_data)
4472 {
4473     GMount *mount;
4474     MountState *state;
4475 
4476     state = user_data;
4477     if (state->directory == NULL)
4478     {
4479         /* Operation was cancelled. Bail out */
4480         mount_state_free (state);
4481         return;
4482     }
4483 
4484     mount = g_file_find_enclosing_mount_finish (G_FILE (source_object),
4485             res, NULL);
4486 
4487     if (mount)
4488     {
4489         GFile *location, *root;
4490 
4491         root = g_mount_get_root (mount);
4492         location = caja_file_get_location (state->file);
4493         if (!g_file_equal (location, root))
4494         {
4495             g_object_unref (mount);
4496             mount = NULL;
4497         }
4498         g_object_unref (root);
4499         g_object_unref (location);
4500     }
4501 
4502     got_mount (state, mount);
4503 
4504     if (mount)
4505     {
4506         g_object_unref (mount);
4507     }
4508 }
4509 
4510 static GMount *
get_mount_at(GFile * target)4511 get_mount_at (GFile *target)
4512 {
4513     GVolumeMonitor *monitor;
4514     GFile *root;
4515     GList *mounts, *l;
4516     GMount *found;
4517 
4518     monitor = g_volume_monitor_get ();
4519     mounts = g_volume_monitor_get_mounts (monitor);
4520 
4521     found = NULL;
4522     for (l = mounts; l != NULL; l = l->next)
4523     {
4524         GMount *mount = G_MOUNT (l->data);
4525 
4526         if (g_mount_is_shadowed (mount))
4527             continue;
4528 
4529         root = g_mount_get_root (mount);
4530 
4531         if (g_file_equal (target, root))
4532         {
4533             found = g_object_ref (mount);
4534             break;
4535         }
4536 
4537         g_object_unref (root);
4538     }
4539 
4540     g_list_free_full (mounts, g_object_unref);
4541 
4542     g_object_unref (monitor);
4543 
4544     return found;
4545 }
4546 
4547 static void
mount_start(CajaDirectory * directory,CajaFile * file,gboolean * doing_io)4548 mount_start (CajaDirectory *directory,
4549              CajaFile *file,
4550              gboolean *doing_io)
4551 {
4552     GFile *location;
4553     MountState *state;
4554 
4555     if (directory->details->mount_state != NULL)
4556     {
4557         *doing_io = TRUE;
4558         return;
4559     }
4560 
4561     if (!is_needy (file,
4562                    lacks_mount,
4563                    REQUEST_MOUNT))
4564     {
4565         return;
4566     }
4567     *doing_io = TRUE;
4568 
4569     if (!async_job_start (directory, "mount"))
4570     {
4571         return;
4572     }
4573 
4574     state = g_new0 (MountState, 1);
4575     state->directory = directory;
4576     state->file = file;
4577     state->cancellable = g_cancellable_new ();
4578 
4579     location = caja_file_get_location (file);
4580 
4581     directory->details->mount_state = state;
4582 
4583     if (file->details->type == G_FILE_TYPE_MOUNTABLE)
4584     {
4585         GFile *target;
4586         GMount *mount;
4587 
4588         mount = NULL;
4589         target = caja_file_get_activation_location (file);
4590         if (target != NULL)
4591         {
4592             mount = get_mount_at (target);
4593             g_object_unref (target);
4594         }
4595 
4596         got_mount (state, mount);
4597 
4598         if (mount)
4599         {
4600             g_object_unref (mount);
4601         }
4602     }
4603     else
4604     {
4605         g_file_find_enclosing_mount_async (location,
4606                                            G_PRIORITY_DEFAULT,
4607                                            state->cancellable,
4608                                            find_enclosing_mount_callback,
4609                                            state);
4610     }
4611     g_object_unref (location);
4612 }
4613 
4614 static void
filesystem_info_cancel(CajaDirectory * directory)4615 filesystem_info_cancel (CajaDirectory *directory)
4616 {
4617     if (directory->details->filesystem_info_state != NULL)
4618     {
4619         g_cancellable_cancel (directory->details->filesystem_info_state->cancellable);
4620         directory->details->filesystem_info_state->directory = NULL;
4621         directory->details->filesystem_info_state = NULL;
4622         async_job_end (directory, "filesystem info");
4623     }
4624 }
4625 
4626 static void
filesystem_info_stop(CajaDirectory * directory)4627 filesystem_info_stop (CajaDirectory *directory)
4628 {
4629     if (directory->details->filesystem_info_state != NULL)
4630     {
4631         CajaFile *file;
4632 
4633         file = directory->details->filesystem_info_state->file;
4634 
4635         if (file != NULL)
4636         {
4637             g_assert (CAJA_IS_FILE (file));
4638             g_assert (file->details->directory == directory);
4639             if (is_needy (file,
4640                           lacks_filesystem_info,
4641                           REQUEST_FILESYSTEM_INFO))
4642             {
4643                 return;
4644             }
4645         }
4646 
4647         /* The filesystem info is not wanted, so stop it. */
4648         filesystem_info_cancel (directory);
4649     }
4650 }
4651 
4652 static void
filesystem_info_state_free(FilesystemInfoState * state)4653 filesystem_info_state_free (FilesystemInfoState *state)
4654 {
4655     g_object_unref (state->cancellable);
4656     g_free (state);
4657 }
4658 
4659 static void
got_filesystem_info(FilesystemInfoState * state,GFileInfo * info)4660 got_filesystem_info (FilesystemInfoState *state, GFileInfo *info)
4661 {
4662     CajaDirectory *directory;
4663     CajaFile *file;
4664 
4665     /* careful here, info may be NULL */
4666 
4667     directory = caja_directory_ref (state->directory);
4668 
4669     state->directory->details->filesystem_info_state = NULL;
4670     async_job_end (state->directory, "filesystem info");
4671 
4672     file = caja_file_ref (state->file);
4673 
4674     file->details->filesystem_info_is_up_to_date = TRUE;
4675     if (info != NULL)
4676     {
4677         file->details->filesystem_use_preview =
4678             g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW);
4679         file->details->filesystem_readonly =
4680             g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY);
4681     }
4682 
4683     caja_directory_async_state_changed (directory);
4684     caja_file_changed (file);
4685 
4686     caja_file_unref (file);
4687 
4688     caja_directory_unref (directory);
4689 
4690     filesystem_info_state_free (state);
4691 }
4692 
4693 static void
query_filesystem_info_callback(GObject * source_object,GAsyncResult * res,gpointer user_data)4694 query_filesystem_info_callback (GObject *source_object,
4695                                 GAsyncResult *res,
4696                                 gpointer user_data)
4697 {
4698     GFileInfo *info;
4699     FilesystemInfoState *state;
4700 
4701     state = user_data;
4702     if (state->directory == NULL)
4703     {
4704         /* Operation was cancelled. Bail out */
4705         filesystem_info_state_free (state);
4706         return;
4707     }
4708 
4709     info = g_file_query_filesystem_info_finish (G_FILE (source_object), res, NULL);
4710 
4711     got_filesystem_info (state, info);
4712 
4713     if (info != NULL)
4714     {
4715         g_object_unref (info);
4716     }
4717 }
4718 
4719 static void
filesystem_info_start(CajaDirectory * directory,CajaFile * file,gboolean * doing_io)4720 filesystem_info_start (CajaDirectory *directory,
4721                        CajaFile *file,
4722                        gboolean *doing_io)
4723 {
4724     GFile *location;
4725     FilesystemInfoState *state;
4726 
4727     if (directory->details->filesystem_info_state != NULL)
4728     {
4729         *doing_io = TRUE;
4730         return;
4731     }
4732 
4733     if (!is_needy (file,
4734                    lacks_filesystem_info,
4735                    REQUEST_FILESYSTEM_INFO))
4736     {
4737         return;
4738     }
4739     *doing_io = TRUE;
4740 
4741     if (!async_job_start (directory, "filesystem info"))
4742     {
4743         return;
4744     }
4745 
4746     state = g_new0 (FilesystemInfoState, 1);
4747     state->directory = directory;
4748     state->file = file;
4749     state->cancellable = g_cancellable_new ();
4750 
4751     location = caja_file_get_location (file);
4752 
4753     directory->details->filesystem_info_state = state;
4754 
4755     g_file_query_filesystem_info_async (location,
4756                                         G_FILE_ATTRIBUTE_FILESYSTEM_READONLY ","
4757                                         G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW,
4758                                         G_PRIORITY_DEFAULT,
4759                                         state->cancellable,
4760                                         query_filesystem_info_callback,
4761                                         state);
4762     g_object_unref (location);
4763 }
4764 
4765 static void
extension_info_cancel(CajaDirectory * directory)4766 extension_info_cancel (CajaDirectory *directory)
4767 {
4768     if (directory->details->extension_info_in_progress != NULL)
4769     {
4770         if (directory->details->extension_info_idle)
4771         {
4772             g_source_remove (directory->details->extension_info_idle);
4773         }
4774         else
4775         {
4776             caja_info_provider_cancel_update
4777             (directory->details->extension_info_provider,
4778              directory->details->extension_info_in_progress);
4779         }
4780 
4781         directory->details->extension_info_in_progress = NULL;
4782         directory->details->extension_info_file = NULL;
4783         directory->details->extension_info_provider = NULL;
4784         directory->details->extension_info_idle = 0;
4785 
4786         async_job_end (directory, "extension info");
4787     }
4788 }
4789 
4790 static void
extension_info_stop(CajaDirectory * directory)4791 extension_info_stop (CajaDirectory *directory)
4792 {
4793     if (directory->details->extension_info_in_progress != NULL)
4794     {
4795         CajaFile *file;
4796 
4797         file = directory->details->extension_info_file;
4798         if (file != NULL)
4799         {
4800             g_assert (CAJA_IS_FILE (file));
4801             g_assert (file->details->directory == directory);
4802             if (is_needy (file, lacks_extension_info, REQUEST_EXTENSION_INFO))
4803             {
4804                 return;
4805             }
4806         }
4807 
4808         /* The info is not wanted, so stop it. */
4809         extension_info_cancel (directory);
4810     }
4811 }
4812 
4813 static void
finish_info_provider(CajaDirectory * directory,CajaFile * file,CajaInfoProvider * provider)4814 finish_info_provider (CajaDirectory *directory,
4815                       CajaFile *file,
4816                       CajaInfoProvider *provider)
4817 {
4818     file->details->pending_info_providers =
4819         g_list_remove  (file->details->pending_info_providers,
4820                         provider);
4821     g_object_unref (provider);
4822 
4823     caja_directory_async_state_changed (directory);
4824 
4825     if (file->details->pending_info_providers == NULL)
4826     {
4827         caja_file_info_providers_done (file);
4828     }
4829 }
4830 
4831 
4832 static gboolean
info_provider_idle_callback(gpointer user_data)4833 info_provider_idle_callback (gpointer user_data)
4834 {
4835     InfoProviderResponse *response;
4836     CajaDirectory *directory;
4837 
4838     response = user_data;
4839     directory = response->directory;
4840 
4841     if (response->handle != directory->details->extension_info_in_progress
4842             || response->provider != directory->details->extension_info_provider)
4843     {
4844         g_warning ("Unexpected plugin response.  This probably indicates a bug in a Caja extension: handle=%p", response->handle);
4845     }
4846     else
4847     {
4848         CajaFile *file;
4849         async_job_end (directory, "extension info");
4850 
4851         file = directory->details->extension_info_file;
4852 
4853         directory->details->extension_info_file = NULL;
4854         directory->details->extension_info_provider = NULL;
4855         directory->details->extension_info_in_progress = NULL;
4856         directory->details->extension_info_idle = 0;
4857 
4858         finish_info_provider (directory, file, response->provider);
4859     }
4860 
4861     return FALSE;
4862 }
4863 
4864 static void
info_provider_callback(CajaInfoProvider * provider,CajaOperationHandle * handle,CajaOperationResult result,gpointer user_data)4865 info_provider_callback (CajaInfoProvider *provider,
4866                         CajaOperationHandle *handle,
4867                         CajaOperationResult result,
4868                         gpointer user_data)
4869 {
4870     InfoProviderResponse *response;
4871 
4872     response = g_new0 (InfoProviderResponse, 1);
4873     response->provider = provider;
4874     response->handle = handle;
4875     response->result = result;
4876     response->directory = CAJA_DIRECTORY (user_data);
4877 
4878     response->directory->details->extension_info_idle =
4879         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
4880                          info_provider_idle_callback, response,
4881                          g_free);
4882 }
4883 
4884 static void
extension_info_start(CajaDirectory * directory,CajaFile * file,gboolean * doing_io)4885 extension_info_start (CajaDirectory *directory,
4886                       CajaFile *file,
4887                       gboolean *doing_io)
4888 {
4889     CajaInfoProvider *provider;
4890     CajaOperationResult result;
4891     CajaOperationHandle *handle;
4892     GClosure *update_complete;
4893 
4894     if (directory->details->extension_info_in_progress != NULL)
4895     {
4896         *doing_io = TRUE;
4897         return;
4898     }
4899 
4900     if (!is_needy (file, lacks_extension_info, REQUEST_EXTENSION_INFO))
4901     {
4902         return;
4903     }
4904     *doing_io = TRUE;
4905 
4906     if (!async_job_start (directory, "extension info"))
4907     {
4908         return;
4909     }
4910 
4911     provider = file->details->pending_info_providers->data;
4912 
4913     update_complete = g_cclosure_new (G_CALLBACK (info_provider_callback),
4914                                       directory,
4915                                       NULL);
4916     g_closure_set_marshal (update_complete,
4917                            caja_marshal_VOID__POINTER_ENUM);
4918 
4919     result = caja_info_provider_update_file_info
4920              (provider,
4921               CAJA_FILE_INFO (file),
4922               update_complete,
4923               &handle);
4924 
4925     g_closure_unref (update_complete);
4926 
4927     if (result == CAJA_OPERATION_COMPLETE ||
4928             result == CAJA_OPERATION_FAILED)
4929     {
4930         finish_info_provider (directory, file, provider);
4931         async_job_end (directory, "extension info");
4932     }
4933     else
4934     {
4935         directory->details->extension_info_in_progress = handle;
4936         directory->details->extension_info_provider = provider;
4937         directory->details->extension_info_file = file;
4938     }
4939 }
4940 
4941 static void
start_or_stop_io(CajaDirectory * directory)4942 start_or_stop_io (CajaDirectory *directory)
4943 {
4944     CajaFile *file;
4945     gboolean doing_io;
4946 
4947     /* Start or stop reading files. */
4948     file_list_start_or_stop (directory);
4949 
4950     /* Stop any no longer wanted attribute fetches. */
4951     file_info_stop (directory);
4952     directory_count_stop (directory);
4953     deep_count_stop (directory);
4954     mime_list_stop (directory);
4955     top_left_stop (directory);
4956     link_info_stop (directory);
4957     extension_info_stop (directory);
4958     mount_stop (directory);
4959     thumbnail_stop (directory);
4960     filesystem_info_stop (directory);
4961 
4962     doing_io = FALSE;
4963     /* Take files that are all done off the queue. */
4964     while (!caja_file_queue_is_empty (directory->details->high_priority_queue))
4965     {
4966         file = caja_file_queue_head (directory->details->high_priority_queue);
4967 
4968         /* Start getting attributes if possible */
4969         file_info_start (directory, file, &doing_io);
4970         link_info_start (directory, file, &doing_io);
4971 
4972         if (doing_io)
4973         {
4974             return;
4975         }
4976 
4977         move_file_to_low_priority_queue (directory, file);
4978     }
4979 
4980     /* High priority queue must be empty */
4981     while (!caja_file_queue_is_empty (directory->details->low_priority_queue))
4982     {
4983         file = caja_file_queue_head (directory->details->low_priority_queue);
4984 
4985         /* Start getting attributes if possible */
4986         mount_start (directory, file, &doing_io);
4987         directory_count_start (directory, file, &doing_io);
4988         deep_count_start (directory, file, &doing_io);
4989         mime_list_start (directory, file, &doing_io);
4990         top_left_start (directory, file, &doing_io);
4991         thumbnail_start (directory, file, &doing_io);
4992         filesystem_info_start (directory, file, &doing_io);
4993 
4994         if (doing_io)
4995         {
4996             return;
4997         }
4998 
4999         move_file_to_extension_queue (directory, file);
5000     }
5001 
5002     /* Low priority queue must be empty */
5003     while (!caja_file_queue_is_empty (directory->details->extension_queue))
5004     {
5005         file = caja_file_queue_head (directory->details->extension_queue);
5006 
5007         /* Start getting attributes if possible */
5008         extension_info_start (directory, file, &doing_io);
5009         if (doing_io)
5010         {
5011             return;
5012         }
5013 
5014         caja_directory_remove_file_from_work_queue (directory, file);
5015     }
5016 }
5017 
5018 /* Call this when the monitor or call when ready list changes,
5019  * or when some I/O is completed.
5020  */
5021 void
caja_directory_async_state_changed(CajaDirectory * directory)5022 caja_directory_async_state_changed (CajaDirectory *directory)
5023 {
5024     /* Check if any callbacks are satisfied and call them if they
5025      * are. Do this last so that any changes done in start or stop
5026      * I/O functions immediately (not in callbacks) are taken into
5027      * consideration. If any callbacks are called, consider the
5028      * I/O state again so that we can release or cancel I/O that
5029      * is not longer needed once the callbacks are satisfied.
5030      */
5031 
5032     if (directory->details->in_async_service_loop)
5033     {
5034         directory->details->state_changed = TRUE;
5035         return;
5036     }
5037     directory->details->in_async_service_loop = TRUE;
5038     caja_directory_ref (directory);
5039     do
5040     {
5041         directory->details->state_changed = FALSE;
5042         start_or_stop_io (directory);
5043         if (call_ready_callbacks (directory))
5044         {
5045             directory->details->state_changed = TRUE;
5046         }
5047     }
5048     while (directory->details->state_changed);
5049     directory->details->in_async_service_loop = FALSE;
5050     caja_directory_unref (directory);
5051 
5052     /* Check if any directories should wake up. */
5053     async_job_wake_up ();
5054 }
5055 
5056 void
caja_directory_cancel(CajaDirectory * directory)5057 caja_directory_cancel (CajaDirectory *directory)
5058 {
5059     /* Arbitrary order (kept alphabetical). */
5060     deep_count_cancel (directory);
5061     directory_count_cancel (directory);
5062     file_info_cancel (directory);
5063     file_list_cancel (directory);
5064     link_info_cancel (directory);
5065     mime_list_cancel (directory);
5066     new_files_cancel (directory);
5067     top_left_cancel (directory);
5068     extension_info_cancel (directory);
5069     thumbnail_cancel (directory);
5070     mount_cancel (directory);
5071     filesystem_info_cancel (directory);
5072 
5073     /* We aren't waiting for anything any more. */
5074     if (waiting_directories != NULL)
5075     {
5076         g_hash_table_remove (waiting_directories, directory);
5077     }
5078 
5079     /* Check if any directories should wake up. */
5080     async_job_wake_up ();
5081 }
5082 
5083 static void
cancel_directory_count_for_file(CajaDirectory * directory,CajaFile * file)5084 cancel_directory_count_for_file (CajaDirectory *directory,
5085                                  CajaFile      *file)
5086 {
5087     if (directory->details->count_in_progress != NULL &&
5088             directory->details->count_in_progress->count_file == file)
5089     {
5090         directory_count_cancel (directory);
5091     }
5092 }
5093 
5094 static void
cancel_deep_counts_for_file(CajaDirectory * directory,CajaFile * file)5095 cancel_deep_counts_for_file (CajaDirectory *directory,
5096                              CajaFile      *file)
5097 {
5098     if (directory->details->deep_count_file == file)
5099     {
5100         deep_count_cancel (directory);
5101     }
5102 }
5103 
5104 static void
cancel_mime_list_for_file(CajaDirectory * directory,CajaFile * file)5105 cancel_mime_list_for_file (CajaDirectory *directory,
5106                            CajaFile      *file)
5107 {
5108     if (directory->details->mime_list_in_progress != NULL &&
5109             directory->details->mime_list_in_progress->mime_list_file == file)
5110     {
5111         mime_list_cancel (directory);
5112     }
5113 }
5114 
5115 static void
cancel_top_left_text_for_file(CajaDirectory * directory,CajaFile * file)5116 cancel_top_left_text_for_file (CajaDirectory *directory,
5117                                CajaFile      *file)
5118 {
5119     if (directory->details->top_left_read_state != NULL &&
5120             directory->details->top_left_read_state->file == file)
5121     {
5122         top_left_cancel (directory);
5123     }
5124 }
5125 
5126 static void
cancel_file_info_for_file(CajaDirectory * directory,CajaFile * file)5127 cancel_file_info_for_file (CajaDirectory *directory,
5128                            CajaFile      *file)
5129 {
5130     if (directory->details->get_info_file == file)
5131     {
5132         file_info_cancel (directory);
5133     }
5134 }
5135 
5136 static void
cancel_thumbnail_for_file(CajaDirectory * directory,CajaFile * file)5137 cancel_thumbnail_for_file (CajaDirectory *directory,
5138                            CajaFile      *file)
5139 {
5140     if (directory->details->thumbnail_state != NULL &&
5141             directory->details->thumbnail_state->file == file)
5142     {
5143         thumbnail_cancel (directory);
5144     }
5145 }
5146 
5147 static void
cancel_mount_for_file(CajaDirectory * directory,CajaFile * file)5148 cancel_mount_for_file (CajaDirectory *directory,
5149                        CajaFile      *file)
5150 {
5151     if (directory->details->mount_state != NULL &&
5152             directory->details->mount_state->file == file)
5153     {
5154         mount_cancel (directory);
5155     }
5156 }
5157 
5158 static void
cancel_filesystem_info_for_file(CajaDirectory * directory,CajaFile * file)5159 cancel_filesystem_info_for_file (CajaDirectory *directory,
5160                                  CajaFile      *file)
5161 {
5162     if (directory->details->filesystem_info_state != NULL &&
5163             directory->details->filesystem_info_state->file == file)
5164     {
5165         filesystem_info_cancel (directory);
5166     }
5167 }
5168 
5169 static void
cancel_link_info_for_file(CajaDirectory * directory,CajaFile * file)5170 cancel_link_info_for_file (CajaDirectory *directory,
5171                            CajaFile      *file)
5172 {
5173     if (directory->details->link_info_read_state != NULL &&
5174             directory->details->link_info_read_state->file == file)
5175     {
5176         link_info_cancel (directory);
5177     }
5178 }
5179 
5180 
5181 static void
cancel_loading_attributes(CajaDirectory * directory,CajaFileAttributes file_attributes)5182 cancel_loading_attributes (CajaDirectory *directory,
5183                            CajaFileAttributes file_attributes)
5184 {
5185     Request request;
5186 
5187     request = caja_directory_set_up_request (file_attributes);
5188 
5189     if (REQUEST_WANTS_TYPE (request, REQUEST_DIRECTORY_COUNT))
5190     {
5191         directory_count_cancel (directory);
5192     }
5193     if (REQUEST_WANTS_TYPE (request, REQUEST_DEEP_COUNT))
5194     {
5195         deep_count_cancel (directory);
5196     }
5197     if (REQUEST_WANTS_TYPE (request, REQUEST_MIME_LIST))
5198     {
5199         mime_list_cancel (directory);
5200     }
5201     if (REQUEST_WANTS_TYPE (request, REQUEST_TOP_LEFT_TEXT))
5202     {
5203         top_left_cancel (directory);
5204     }
5205     if (REQUEST_WANTS_TYPE (request, REQUEST_FILE_INFO))
5206     {
5207         file_info_cancel (directory);
5208     }
5209     if (REQUEST_WANTS_TYPE (request, REQUEST_FILESYSTEM_INFO))
5210     {
5211         filesystem_info_cancel (directory);
5212     }
5213     if (REQUEST_WANTS_TYPE (request, REQUEST_LINK_INFO))
5214     {
5215         link_info_cancel (directory);
5216     }
5217 
5218     if (REQUEST_WANTS_TYPE (request, REQUEST_EXTENSION_INFO))
5219     {
5220         extension_info_cancel (directory);
5221     }
5222 
5223     if (REQUEST_WANTS_TYPE (request, REQUEST_THUMBNAIL))
5224     {
5225         thumbnail_cancel (directory);
5226     }
5227 
5228     if (REQUEST_WANTS_TYPE (request, REQUEST_MOUNT))
5229     {
5230         mount_cancel (directory);
5231     }
5232 
5233     caja_directory_async_state_changed (directory);
5234 }
5235 
5236 void
caja_directory_cancel_loading_file_attributes(CajaDirectory * directory,CajaFile * file,CajaFileAttributes file_attributes)5237 caja_directory_cancel_loading_file_attributes (CajaDirectory      *directory,
5238         CajaFile           *file,
5239         CajaFileAttributes  file_attributes)
5240 {
5241     Request request;
5242 
5243     caja_directory_remove_file_from_work_queue (directory, file);
5244 
5245     request = caja_directory_set_up_request (file_attributes);
5246 
5247     if (REQUEST_WANTS_TYPE (request, REQUEST_DIRECTORY_COUNT))
5248     {
5249         cancel_directory_count_for_file (directory, file);
5250     }
5251     if (REQUEST_WANTS_TYPE (request, REQUEST_DEEP_COUNT))
5252     {
5253         cancel_deep_counts_for_file (directory, file);
5254     }
5255     if (REQUEST_WANTS_TYPE (request, REQUEST_MIME_LIST))
5256     {
5257         cancel_mime_list_for_file (directory, file);
5258     }
5259     if (REQUEST_WANTS_TYPE (request, REQUEST_TOP_LEFT_TEXT))
5260     {
5261         cancel_top_left_text_for_file (directory, file);
5262     }
5263     if (REQUEST_WANTS_TYPE (request, REQUEST_FILE_INFO))
5264     {
5265         cancel_file_info_for_file (directory, file);
5266     }
5267     if (REQUEST_WANTS_TYPE (request, REQUEST_FILESYSTEM_INFO))
5268     {
5269         cancel_filesystem_info_for_file (directory, file);
5270     }
5271     if (REQUEST_WANTS_TYPE (request, REQUEST_LINK_INFO))
5272     {
5273         cancel_link_info_for_file (directory, file);
5274     }
5275     if (REQUEST_WANTS_TYPE (request, REQUEST_THUMBNAIL))
5276     {
5277         cancel_thumbnail_for_file (directory, file);
5278     }
5279     if (REQUEST_WANTS_TYPE (request, REQUEST_MOUNT))
5280     {
5281         cancel_mount_for_file (directory, file);
5282     }
5283 
5284     caja_directory_async_state_changed (directory);
5285 }
5286 
5287 void
caja_directory_add_file_to_work_queue(CajaDirectory * directory,CajaFile * file)5288 caja_directory_add_file_to_work_queue (CajaDirectory *directory,
5289                                        CajaFile *file)
5290 {
5291     g_return_if_fail (file->details->directory == directory);
5292 
5293     caja_file_queue_enqueue (directory->details->high_priority_queue,
5294                              file);
5295 }
5296 
5297 
5298 static void
add_all_files_to_work_queue(CajaDirectory * directory)5299 add_all_files_to_work_queue (CajaDirectory *directory)
5300 {
5301     GList *node;
5302 
5303     for (node = directory->details->file_list; node != NULL; node = node->next)
5304     {
5305         CajaFile *file;
5306 
5307         file = CAJA_FILE (node->data);
5308 
5309         caja_directory_add_file_to_work_queue (directory, file);
5310     }
5311 }
5312 
5313 void
caja_directory_remove_file_from_work_queue(CajaDirectory * directory,CajaFile * file)5314 caja_directory_remove_file_from_work_queue (CajaDirectory *directory,
5315         CajaFile *file)
5316 {
5317     caja_file_queue_remove (directory->details->high_priority_queue,
5318                             file);
5319     caja_file_queue_remove (directory->details->low_priority_queue,
5320                             file);
5321     caja_file_queue_remove (directory->details->extension_queue,
5322                             file);
5323 }
5324 
5325 
5326 static void
move_file_to_low_priority_queue(CajaDirectory * directory,CajaFile * file)5327 move_file_to_low_priority_queue (CajaDirectory *directory,
5328                                  CajaFile *file)
5329 {
5330     /* Must add before removing to avoid ref underflow */
5331     caja_file_queue_enqueue (directory->details->low_priority_queue,
5332                              file);
5333     caja_file_queue_remove (directory->details->high_priority_queue,
5334                             file);
5335 }
5336 
5337 static void
move_file_to_extension_queue(CajaDirectory * directory,CajaFile * file)5338 move_file_to_extension_queue (CajaDirectory *directory,
5339                               CajaFile *file)
5340 {
5341     /* Must add before removing to avoid ref underflow */
5342     caja_file_queue_enqueue (directory->details->extension_queue,
5343                              file);
5344     caja_file_queue_remove (directory->details->low_priority_queue,
5345                             file);
5346 }
5347