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)
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
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)
40 #include "gui_support.h"
43 #include "usericons.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)
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
)
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
)
84 guchar
*target_path
; /* For symlinks, this is the target, otherwise the original path */
88 g_object_unref(item
->_image
);
92 item
->mime_type
= NULL
;
94 info
= g_file_query_info(path
, "standard::size,unix::*", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS
, NULL
, &error
);
96 g_warning("Can't stat: %s", error
->message
);
99 item
->lstat_errno
= errno
; // XXX
100 item
->base_type
= TYPE_ERROR
;
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
);
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
;
129 if (xattr_have(path
))
130 item
->flags
|= ITEM_FLAG_HAS_XATTR
;
133 target_path
= g_file_get_path(path
); /* NULL if not a local path */
135 if (S_ISLNK(item
->mode
))
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
;
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
);
151 item
->flags
|= ITEM_FLAG_SYMLINK
;
153 // TODO: resolve with GIO
154 link_target
= pathdup(target_path
);
158 target_path
= link_target
;
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
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.
184 plain_path
= g_file_get_path(path
);
185 examine_dir(plain_path
, item
, info
);
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
;
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
;
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
);
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
);
245 DirItem
*diritem_new(const guchar
*leafname
)
249 item
= g_new(DirItem
, 1);
250 item
->leafname
= g_strdup(leafname
);
251 item
->may_delete
= FALSE
;
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
);
261 void diritem_free(DirItem
*item
)
263 g_return_if_fail(item
!= NULL
);
266 g_object_unref(item
->_image
);
268 collate_key_free(item
->leafname_collate
);
269 g_free(item
->leafname
);
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
);
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
)
301 static GString
*tmp
= NULL
;
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 */
319 return; /* Don't trust world-writable dirs */
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
);
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
);
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... */
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
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
);
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
);