r3911: When checking for world-writable dirs, use symlink target info for symlinks.
[rox-filer.git] / ROX-Filer / src / diritem.c
blob8c839dc6c66722d881668996f509b6ffb7afd38a
1 /*
2 * $Id$
4 * Copyright (C) 2005, the ROX-Filer team.
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation; either version 2 of the License, or (at your option)
9 * any later version.
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
16 * You should have received a copy of the GNU General Public License along with
17 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
18 * Place, Suite 330, Boston, MA 02111-1307 USA
21 /* diritem.c - get details about files */
23 /* Don't load icons larger than 400K (this is rather excessive, basically
24 * we just want to stop people crashing the filer with huge icons).
26 #define MAX_ICON_SIZE (400 * 1024)
28 #include "config.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 static Option o_ignore_exec;
51 #define RECENT_DELAY (5 * 60) /* Time in seconds to consider a file recent */
52 #define ABOUT_NOW(time) (diritem_recent_time - time < RECENT_DELAY)
53 /* If you want to make use of the RECENT flag, make sure this is set to
54 * the current time before calling diritem_restat().
56 time_t diritem_recent_time;
58 /* Static prototypes */
59 static void examine_dir(const guchar *path, DirItem *item,
60 struct stat *link_target);
62 /****************************************************************
63 * EXTERNAL INTERFACE *
64 ****************************************************************/
66 void diritem_init(void)
68 option_add_int(&o_ignore_exec, "display_ignore_exec", TRUE);
70 read_globicons();
73 /* Bring this item's structure uptodate.
74 * 'parent' is optional; it saves one stat() for directories.
76 void diritem_restat(const guchar *path, DirItem *item, struct stat *parent)
78 struct stat info;
80 if (item->_image)
82 g_object_unref(item->_image);
83 item->_image = NULL;
85 item->flags = 0;
86 item->mime_type = NULL;
88 if (mc_lstat(path, &info) == -1)
90 item->lstat_errno = errno;
91 item->base_type = TYPE_ERROR;
92 item->size = 0;
93 item->mode = 0;
94 item->mtime = item->ctime = item->atime = 0;
95 item->uid = (uid_t) -1;
96 item->gid = (gid_t) -1;
98 else
100 guchar *target_path;
102 item->lstat_errno = 0;
103 item->size = info.st_size;
104 item->mode = info.st_mode;
105 item->atime = info.st_atime;
106 item->ctime = info.st_ctime;
107 item->mtime = info.st_mtime;
108 item->uid = info.st_uid;
109 item->gid = info.st_gid;
110 if (ABOUT_NOW(item->mtime) || ABOUT_NOW(item->ctime))
111 item->flags |= ITEM_FLAG_RECENT;
113 if (xtype_have_attr(path))
114 item->flags |= ITEM_FLAG_HAS_XATTR;
116 if (S_ISLNK(info.st_mode))
118 if (mc_stat(path, &info))
119 item->base_type = TYPE_ERROR;
120 else
121 item->base_type =
122 mode_to_base_type(info.st_mode);
124 item->flags |= ITEM_FLAG_SYMLINK;
126 target_path = readlink_dup(path);
128 else
130 item->base_type = mode_to_base_type(info.st_mode);
131 target_path = (guchar *) path;
134 if (item->base_type == TYPE_DIRECTORY)
136 if (mount_is_mounted(target_path, &info,
137 target_path == path ? parent : NULL))
138 item->flags |= ITEM_FLAG_MOUNT_POINT
139 | ITEM_FLAG_MOUNTED;
140 else if (g_hash_table_lookup(fstab_mounts,
141 target_path))
142 item->flags |= ITEM_FLAG_MOUNT_POINT;
145 if (path != target_path)
146 g_free(target_path);
149 if (item->base_type == TYPE_DIRECTORY)
151 /* KRJW: info.st_uid will be the uid of the dir, regardless
152 * of whether `path' is a dir or a symlink to one. Note that
153 * if path is a symlink to a dir, item->uid will be the uid
154 * of the *symlink*, but we really want the uid of the dir
155 * to which the symlink points.
157 examine_dir(path, item, &info);
159 else if (item->base_type == TYPE_FILE)
161 /* Type determined from path before checking for executables
162 * because some mounts show everything as executable and we
163 * still want to use the file name to set the mime type.
165 if (item->flags & ITEM_FLAG_SYMLINK)
167 guchar *link_path;
168 link_path = readlink_dup(path);
169 item->mime_type = type_from_path(link_path
170 ? link_path
171 : path);
172 g_free(link_path);
174 else
175 item->mime_type = xtype_get(path);
177 /* Note: for symlinks we need the mode of the target */
178 if (info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
180 /* Note that the flag is set for ALL executable
181 * files, but the mime_type is only
182 * application_executable if the file doesn't have a
183 * known extension when that option is in force.
185 item->flags |= ITEM_FLAG_EXEC_FILE;
187 if (!o_ignore_exec.int_value)
188 item->mime_type = application_executable;
191 if (!item->mime_type)
193 if (item->size == 0)
194 item->mime_type = text_plain;
195 else
196 item->mime_type = mime_type_from_contents(path);
199 if (!item->mime_type)
200 item->mime_type = item->flags & ITEM_FLAG_EXEC_FILE
201 ? application_executable
202 : text_plain;
204 check_globicon(path, item);
206 else
207 check_globicon(path, item);
209 if (!item->mime_type)
210 item->mime_type = mime_type_from_base_type(item->base_type);
213 DirItem *diritem_new(const guchar *leafname)
215 DirItem *item;
217 item = g_new(DirItem, 1);
218 item->leafname = g_strdup(leafname);
219 item->may_delete = FALSE;
220 item->_image = NULL;
221 item->base_type = TYPE_UNKNOWN;
222 item->flags = ITEM_FLAG_NEED_RESCAN_QUEUE;
223 item->mime_type = NULL;
224 item->leafname_collate = collate_key_new(leafname);
226 return item;
229 void diritem_free(DirItem *item)
231 g_return_if_fail(item != NULL);
233 if (item->_image)
234 g_object_unref(item->_image);
235 item->_image = NULL;
236 collate_key_free(item->leafname_collate);
237 g_free(item->leafname);
238 g_free(item);
241 /* For use by di_image() only. Sets item->_image. */
242 void _diritem_get_image(DirItem *item)
244 g_return_if_fail(item->_image == NULL);
246 if (item->base_type == TYPE_ERROR)
248 item->_image = im_error;
249 g_object_ref(im_error);
251 else
252 item->_image = type_to_icon(item->mime_type);
255 /****************************************************************
256 * INTERNAL FUNCTIONS *
257 ****************************************************************/
259 /* Fill in more details of the DirItem for a directory item.
260 * - Looks for an image (but maybe still NULL on error)
261 * - Updates ITEM_FLAG_APPDIR
263 * link_target contains stat info for the link target for symlinks (or for the
264 * item itself if not a link).
266 static void examine_dir(const guchar *path, DirItem *item,
267 struct stat *link_target)
269 struct stat info;
270 static GString *tmp = NULL;
271 uid_t uid = link_target->st_uid;
273 if (!tmp)
274 tmp = g_string_new(NULL);
276 check_globicon(path, item);
278 if (item->flags & ITEM_FLAG_MOUNT_POINT)
280 item->mime_type = inode_mountpoint;
281 return; /* Try to avoid automounter problems */
284 if (link_target->st_mode & S_IWOTH)
285 return; /* Don't trust world-writable dirs */
287 /* Finding the icon:
289 * - If it contains a .DirIcon then that's the icon
290 * - If it contains an AppRun then it's an application
291 * - If it contains an AppRun but no .DirIcon then try to
292 * use AppIcon.xpm as the icon.
294 * .DirIcon and AppRun must have the same owner as the
295 * directory itself, to prevent abuse of /tmp, etc.
296 * For symlinks, we want the symlink's owner.
299 g_string_printf(tmp, "%s/.DirIcon", path);
301 if (item->_image)
302 goto no_diricon; /* Already got an icon */
304 if (mc_lstat(tmp->str, &info) != 0 || info.st_uid != uid)
305 goto no_diricon; /* Missing, or wrong owner */
307 if (S_ISLNK(info.st_mode) && mc_stat(tmp->str, &info) != 0)
308 goto no_diricon; /* Bad symlink */
310 if (info.st_size > MAX_ICON_SIZE || !S_ISREG(info.st_mode))
311 goto no_diricon; /* Too big, or non-regular file */
313 /* Try to load image; may still get NULL... */
314 item->_image = g_fscache_lookup(pixmap_cache, tmp->str);
316 no_diricon:
318 /* Try to find AppRun... */
319 g_string_truncate(tmp, tmp->len - 8);
320 g_string_append(tmp, "AppRun");
322 if (mc_lstat(tmp->str, &info) != 0 || info.st_uid != uid)
323 goto out; /* Missing, or wrong owner */
325 if (!(info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
326 goto out; /* Not executable */
328 item->flags |= ITEM_FLAG_APPDIR;
330 /* Try to load AppIcon.xpm... */
332 if (item->_image)
333 goto out; /* Already got an icon */
335 g_string_truncate(tmp, tmp->len - 3);
336 g_string_append(tmp, "Icon.xpm");
338 /* Note: since AppRun is valid we don't need to check AppIcon.xpm
339 * so carefully.
342 if (mc_stat(tmp->str, &info) != 0)
343 goto out; /* Missing, or broken symlink */
345 if (info.st_size > MAX_ICON_SIZE || !S_ISREG(info.st_mode))
346 goto out; /* Too big, or non-regular file */
348 /* Try to load image; may still get NULL... */
349 item->_image = g_fscache_lookup(pixmap_cache, tmp->str);
351 out:
353 if ((item->flags & ITEM_FLAG_APPDIR) && !item->_image)
355 /* This is an application without an icon */
356 item->_image = im_appdir;
357 g_object_ref(item->_image);