Scan directories using GIO.
[rox-filer.git] / ROX-Filer / src / diritem.c
blob28009a8b9e97cea141c797c5aee99e05c0a4a7db
1 /*
2 * ROX-Filer, filer for the ROX desktop project
3 * Copyright (C) 2006, Thomas Leonard and others (see changelog for details).
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
17 * Place, Suite 330, Boston, MA 02111-1307 USA
20 /* diritem.c - get details about files */
22 /* Don't load icons larger than 400K (this is rather excessive, basically
23 * we just want to stop people crashing the filer with huge icons).
25 #define MAX_ICON_SIZE (400 * 1024)
27 #include "config.h"
29 #include <gio/gio.h>
30 #include <gtk/gtk.h>
31 #include <errno.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <ctype.h>
36 #include "global.h"
38 #include "diritem.h"
39 #include "support.h"
40 #include "gui_support.h"
41 #include "mount.h"
42 #include "type.h"
43 #include "usericons.h"
44 #include "options.h"
45 #include "fscache.h"
46 #include "pixmaps.h"
47 #include "xtypes.h"
49 #define RECENT_DELAY (5 * 60) /* Time in seconds to consider a file recent */
50 #define ABOUT_NOW(time) (diritem_recent_time - time < RECENT_DELAY)
51 /* If you want to make use of the RECENT flag, make sure this is set to
52 * the current time before calling diritem_restat().
54 time_t diritem_recent_time;
56 /* Static prototypes */
57 static void examine_dir(const guchar *path, DirItem *item, GFileInfo *link_target);
59 /****************************************************************
60 * EXTERNAL INTERFACE *
61 ****************************************************************/
63 void diritem_init(void)
65 read_globicons();
68 /* Bring this item's structure uptodate.
69 * 'parent' is not used XXX
71 void diritem_restat(const guchar *path, DirItem *item, struct stat *parent)
73 GFile *gfile;
75 gfile = g_file_new_for_path(path);
76 diritem_restat_gfile(gfile, item);
77 g_object_unref(gfile);
80 void diritem_restat_gfile(GFile *path, DirItem *item)
82 GFileInfo *info;
83 GError *error = NULL;
84 guchar *target_path; /* For symlinks, this is the target, otherwise the original path */
86 if (item->_image)
88 g_object_unref(item->_image);
89 item->_image = NULL;
91 item->flags = 0;
92 item->mime_type = NULL;
94 info = g_file_query_info(path, "standard::size,unix::*", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &error);
95 if (!info) {
96 g_warning("Can't stat: %s", error->message);
97 g_error_free(error);
99 item->lstat_errno = errno; // XXX
100 item->base_type = TYPE_ERROR;
101 item->size = 0;
102 item->mode = 0;
103 item->mtime = item->ctime = item->atime = 0;
104 item->uid = (uid_t) -1;
105 item->gid = (gid_t) -1;
107 item->mime_type = mime_type_from_base_type(item->base_type);
109 return;
112 /* Note: if path is a symlink the info currently holds the details of the symlink.
113 * But, a bit lower down, it will change to the details of the target.
116 item->lstat_errno = 0;
117 item->size = g_file_info_get_attribute_uint64(info, G_FILE_ATTRIBUTE_STANDARD_SIZE);
118 item->mode = g_file_info_get_attribute_uint32(info, G_FILE_ATTRIBUTE_UNIX_MODE);
119 item->atime = g_file_info_get_attribute_uint32(info, G_FILE_ATTRIBUTE_TIME_ACCESS);
120 item->ctime = g_file_info_get_attribute_uint32(info, G_FILE_ATTRIBUTE_TIME_CHANGED);
121 item->mtime = g_file_info_get_attribute_uint32(info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
122 item->uid = g_file_info_get_attribute_uint32(info, G_FILE_ATTRIBUTE_UNIX_UID);
123 item->gid = g_file_info_get_attribute_uint32(info, G_FILE_ATTRIBUTE_UNIX_GID);
125 if (ABOUT_NOW(item->mtime) || ABOUT_NOW(item->ctime))
126 item->flags |= ITEM_FLAG_RECENT;
128 #if 0
129 if (xattr_have(path))
130 item->flags |= ITEM_FLAG_HAS_XATTR;
131 #endif
133 target_path = g_file_get_path(path); /* NULL if not a local path */
135 if (S_ISLNK(item->mode))
137 char *link_target;
138 GFileInfo *target_info;
139 target_info = g_file_query_info(path, "standard::size,unix::*,standard::symlink-target", G_FILE_QUERY_INFO_NONE, NULL, NULL);
140 if (target_info == NULL)
142 item->base_type = TYPE_ERROR;
144 else
146 item->base_type = mode_to_base_type(g_file_info_get_attribute_uint32(target_info, G_FILE_ATTRIBUTE_UNIX_MODE));
147 g_object_unref(info);
148 info = target_info;
151 item->flags |= ITEM_FLAG_SYMLINK;
153 // TODO: resolve with GIO
154 link_target = pathdup(target_path);
155 if (link_target)
157 g_free(target_path);
158 target_path = link_target;
161 else
163 item->base_type = mode_to_base_type(item->mode);
166 if (item->base_type == TYPE_DIRECTORY)
168 if (g_file_info_get_attribute_boolean(info, G_FILE_ATTRIBUTE_UNIX_IS_MOUNTPOINT))
169 item->flags |= ITEM_FLAG_MOUNT_POINT
170 | ITEM_FLAG_MOUNTED;
171 else if (target_path && g_hash_table_lookup(fstab_mounts, target_path))
172 item->flags |= ITEM_FLAG_MOUNT_POINT;
175 if (item->base_type == TYPE_DIRECTORY)
177 /* KRJW: info.st_uid will be the uid of the dir, regardless
178 * of whether `path' is a dir or a symlink to one. Note that
179 * if path is a symlink to a dir, item->uid will be the uid
180 * of the *symlink*, but we really want the uid of the dir
181 * to which the symlink points.
183 char *plain_path;
184 plain_path = g_file_get_path(path);
185 examine_dir(plain_path, item, info);
186 g_free(plain_path);
188 else if (item->base_type == TYPE_FILE)
190 if (item->size == 0 || target_path == NULL) // TODO: target_path
191 item->mime_type = text_plain;
192 else
193 item->mime_type = type_from_path(target_path);
195 /* Note: for symlinks we need the mode of the target */
196 if (g_file_info_get_attribute_uint32(info, G_FILE_ATTRIBUTE_UNIX_MODE) & (S_IXUSR | S_IXGRP | S_IXOTH))
198 /* Note that the flag is set for ALL executable
199 * files, but the mime_type must also be executable
200 * for clicking on the file to run it.
202 item->flags |= ITEM_FLAG_EXEC_FILE;
204 if (item->mime_type == NULL ||
205 item->mime_type == application_octet_stream)
207 item->mime_type = application_executable;
209 else if (item->mime_type == text_plain &&
210 !strchr(item->leafname, '.'))
212 item->mime_type = application_x_shellscript;
215 else if (item->mime_type == application_x_desktop)
217 item->flags |= ITEM_FLAG_EXEC_FILE;
220 if (!item->mime_type)
221 item->mime_type = text_plain;
223 if (target_path)
224 check_globicon(target_path, item);
226 if (item->mime_type == application_x_desktop && item->_image == NULL && target_path) // TODO
228 item->_image = g_fscache_lookup(desktop_icon_cache, target_path);
231 else
233 if (target_path)
234 check_globicon(target_path, item);
237 if (!item->mime_type)
238 item->mime_type = mime_type_from_base_type(item->base_type);
240 g_object_unref(info);
242 g_free(target_path);
245 DirItem *diritem_new(const guchar *leafname)
247 DirItem *item;
249 item = g_new(DirItem, 1);
250 item->leafname = g_strdup(leafname);
251 item->may_delete = FALSE;
252 item->_image = NULL;
253 item->base_type = TYPE_UNKNOWN;
254 item->flags = ITEM_FLAG_NEED_RESCAN_QUEUE;
255 item->mime_type = NULL;
256 item->leafname_collate = collate_key_new(leafname);
258 return item;
261 void diritem_free(DirItem *item)
263 g_return_if_fail(item != NULL);
265 if (item->_image)
266 g_object_unref(item->_image);
267 item->_image = NULL;
268 collate_key_free(item->leafname_collate);
269 g_free(item->leafname);
270 g_free(item);
273 /* For use by di_image() only. Sets item->_image. */
274 void _diritem_get_image(DirItem *item)
276 g_return_if_fail(item->_image == NULL);
278 if (item->base_type == TYPE_ERROR)
280 item->_image = im_error;
281 g_object_ref(im_error);
283 else
284 item->_image = type_to_icon(item->mime_type);
287 /****************************************************************
288 * INTERNAL FUNCTIONS *
289 ****************************************************************/
291 /* Fill in more details of the DirItem for a directory item.
292 * - Looks for an image (but maybe still NULL on error)
293 * - Updates ITEM_FLAG_APPDIR
295 * link_target contains stat info for the link target for symlinks (or for the
296 * item itself if not a link).
298 static void examine_dir(const guchar *path, DirItem *item, GFileInfo *link_target)
300 struct stat info;
301 static GString *tmp = NULL;
302 uid_t uid;
303 gint32 mode;
305 if (!tmp)
306 tmp = g_string_new(NULL);
308 check_globicon(path, item);
310 mode = g_file_info_get_attribute_uint32(link_target, G_FILE_ATTRIBUTE_UNIX_MODE);
312 if (item->flags & ITEM_FLAG_MOUNT_POINT)
314 item->mime_type = inode_mountpoint;
315 return; /* Try to avoid automounter problems */
318 if (mode & S_IWOTH)
319 return; /* Don't trust world-writable dirs */
321 /* Finding the icon:
323 * - If it contains a .DirIcon then that's the icon
324 * - If it contains an AppRun then it's an application
325 * - If it contains an AppRun but no .DirIcon then try to
326 * use AppIcon.xpm as the icon.
328 * .DirIcon and AppRun must have the same owner as the
329 * directory itself, to prevent abuse of /tmp, etc.
330 * For symlinks, we want the symlink's owner.
333 g_string_printf(tmp, "%s/.DirIcon", path);
335 uid = g_file_info_get_attribute_uint32(link_target, G_FILE_ATTRIBUTE_UNIX_UID);
337 if (item->_image)
338 goto no_diricon; /* Already got an icon */
340 if (mc_lstat(tmp->str, &info) != 0 || info.st_uid != uid)
341 goto no_diricon; /* Missing, or wrong owner */
343 if (S_ISLNK(mode) && mc_stat(tmp->str, &info) != 0)
344 goto no_diricon; /* Bad symlink */
346 if (info.st_size > MAX_ICON_SIZE || !S_ISREG(info.st_mode))
347 goto no_diricon; /* Too big, or non-regular file */
349 /* Try to load image; may still get NULL... */
350 item->_image = g_fscache_lookup(pixmap_cache, tmp->str);
352 no_diricon:
354 /* Try to find AppRun... */
355 g_string_truncate(tmp, tmp->len - 8);
356 g_string_append(tmp, "AppRun");
358 if (mc_lstat(tmp->str, &info) != 0 || info.st_uid != uid)
359 goto out; /* Missing, or wrong owner */
361 if (!(info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
362 goto out; /* Not executable */
364 item->flags |= ITEM_FLAG_APPDIR;
366 /* Try to load AppIcon.xpm... */
368 if (item->_image)
369 goto out; /* Already got an icon */
371 g_string_truncate(tmp, tmp->len - 3);
372 g_string_append(tmp, "Icon.xpm");
374 /* Note: since AppRun is valid we don't need to check AppIcon.xpm
375 * so carefully.
378 if (mc_stat(tmp->str, &info) != 0)
379 goto out; /* Missing, or broken symlink */
381 if (info.st_size > MAX_ICON_SIZE || !S_ISREG(info.st_mode))
382 goto out; /* Too big, or non-regular file */
384 /* Try to load image; may still get NULL... */
385 item->_image = g_fscache_lookup(pixmap_cache, tmp->str);
387 out:
389 if ((item->flags & ITEM_FLAG_APPDIR) && !item->_image)
391 /* This is an application without an icon */
392 item->_image = im_appdir;
393 g_object_ref(item->_image);