1 /* $Id$ */
2 /* Copyright (c) 2012-2015 Pierre Pronchery <khorben@defora.org> */
3 /* This file is part of DeforaOS Desktop Browser */
4 /* Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  *    this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  *    this list of conditions and the following disclaimer in the documentation
13  *    and/or other materials provided with the distribution.
14  *
15  * 3. Neither the name of the authors nor the names of the contributors may be
16  *    used to endorse or promote products derived from this software without
17  *    specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS "AS IS" AND ANY
20  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY
23  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
29 
30 
31 
32 #if defined(__sun)
33 # include <fcntl.h>
34 # include <unistd.h>
35 #endif
36 #include <stdlib.h>
37 #include <string.h>
38 #include <libgen.h>
39 #include "../include/Browser/vfs.h"
40 
41 
42 /* public */
43 /* functions */
44 /* accessors */
45 /* browser_vfs_is_mountpoint */
browser_vfs_is_mountpoint(struct stat * lst,dev_t parent)46 int browser_vfs_is_mountpoint(struct stat * lst, dev_t parent)
47 {
48 	return (lst->st_dev != parent) ? 1 : 0;
49 }
50 
51 
52 /* useful */
53 /* browser_vfs_lstat */
browser_vfs_lstat(char const * filename,struct stat * st)54 int browser_vfs_lstat(char const * filename, struct stat * st)
55 {
56 	return lstat(filename, st);
57 }
58 
59 
60 /* browser_vfs_closedir */
browser_vfs_closedir(DIR * dir)61 int browser_vfs_closedir(DIR * dir)
62 {
63 	return closedir(dir);
64 }
65 
66 
67 /* browser_vfs_mime_icon */
68 static GdkPixbuf * _mime_icon_emblem(GdkPixbuf * pixbuf, int size,
69 		char const * emblem);
70 static GdkPixbuf * _mime_icon_folder(Mime * mime, char const * filename,
71 		struct stat * lst, struct stat * st, int size);
72 static gboolean _mime_icon_folder_in_home(struct stat * pst);
73 static gboolean _mime_icon_folder_is_home(struct stat * st);
74 
browser_vfs_mime_icon(Mime * mime,char const * filename,char const * type,struct stat * lst,struct stat * st,int size)75 GdkPixbuf * browser_vfs_mime_icon(Mime * mime, char const * filename,
76 		char const * type, struct stat * lst, struct stat * st,
77 		int size)
78 {
79 	GdkPixbuf * ret = NULL;
80 	mode_t mode = (lst != NULL) ? lst->st_mode : 0;
81 	struct stat s;
82 	char const * emblem;
83 
84 	if(filename == NULL)
85 		return NULL;
86 	if(type == NULL)
87 		type = browser_vfs_mime_type(mime, filename,
88 				S_ISLNK(mode) ? 0 : mode);
89 	if(st == NULL && browser_vfs_stat(filename, &s) == 0)
90 		st = &s;
91 	if(S_ISDIR(mode) || (st != NULL && S_ISDIR(st->st_mode)))
92 		ret = _mime_icon_folder(mime, filename, lst, st, size);
93 	else if(S_ISLNK(mode) && (st != NULL && S_ISDIR(st->st_mode)))
94 		ret = _mime_icon_folder(mime, filename, lst, st, size);
95 	else
96 		mime_icons(mime, type, size, &ret, -1);
97 	if(ret == NULL || lst == NULL)
98 		return ret;
99 	/* determine the emblem */
100 	if(S_ISCHR(lst->st_mode) || S_ISBLK(lst->st_mode))
101 		emblem = "emblem-system";
102 	else if(S_ISLNK(lst->st_mode))
103 		emblem = "emblem-symbolic-link";
104 	else if((lst->st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0)
105 		emblem = "emblem-unreadable";
106 	else if((lst->st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0)
107 		emblem = "emblem-readonly";
108 	else
109 		emblem = NULL;
110 	/* apply the emblem if relevant */
111 	if(emblem != NULL)
112 		ret = _mime_icon_emblem(ret, size, emblem);
113 	return ret;
114 }
115 
_mime_icon_emblem(GdkPixbuf * pixbuf,int size,char const * emblem)116 static GdkPixbuf * _mime_icon_emblem(GdkPixbuf * pixbuf, int size,
117 		char const * emblem)
118 {
119 	int esize;
120 	GdkPixbuf * epixbuf;
121 	GtkIconTheme * icontheme;
122 #if GTK_CHECK_VERSION(2, 14, 0)
123 	const unsigned int flags = GTK_ICON_LOOKUP_USE_BUILTIN
124 		| GTK_ICON_LOOKUP_FORCE_SIZE;
125 #else
126 	const unsigned int flags = GTK_ICON_LOOKUP_USE_BUILTIN;
127 #endif
128 
129 	/* work on a copy */
130 	epixbuf = gdk_pixbuf_copy(pixbuf);
131 	g_object_unref(pixbuf);
132 	pixbuf = epixbuf;
133 	/* determine the size of the emblem */
134 	if(size >= 96)
135 		esize = 32;
136 	else if(size >= 48)
137 		esize = 24;
138 	else
139 		esize = 12;
140 	/* obtain the emblem's icon */
141 	icontheme = gtk_icon_theme_get_default();
142 	if((epixbuf = gtk_icon_theme_load_icon(icontheme, emblem, esize, flags,
143 					NULL)) == NULL)
144 		return pixbuf;
145 	/* blit the emblem */
146 #if 0 /* XXX does not show anything (bottom right) */
147 	gdk_pixbuf_composite(epixbuf, pixbuf, size - esize, size - esize,
148 			esize, esize, 0, 0, 1.0, 1.0, GDK_INTERP_NEAREST,
149 			255);
150 #else /* blitting at the top left instead */
151 	gdk_pixbuf_composite(epixbuf, pixbuf, 0, 0, esize, esize, 0, 0,
152 			1.0, 1.0, GDK_INTERP_NEAREST, 255);
153 #endif
154 	g_object_unref(epixbuf);
155 	return pixbuf;
156 }
157 
_mime_icon_folder(Mime * mime,char const * filename,struct stat * lst,struct stat * st,int size)158 static GdkPixbuf * _mime_icon_folder(Mime * mime, char const * filename,
159 		struct stat * lst, struct stat * st, int size)
160 {
161 	GdkPixbuf * ret = NULL;
162 	char const * icon = NULL;
163 	struct stat ls;
164 	struct stat ps;
165 	char * p;
166 	size_t i;
167 	struct
168 	{
169 		char const * name;
170 		char const * icon;
171 	} name_icon[] =
172 	{
173 		{ "DCIM",	"folder-pictures"	},
174 		{ "Desktop",	"user-desktop"		},
175 		{ "Documents",	"folder-documents"	},
176 		{ "Download",	"folder-download"	},
177 		{ "Downloads",	"folder-download"	},
178 		{ "Music",	"folder-music"		},
179 		{ "Pictures",	"folder-pictures"	},
180 		{ "public_html","folder-publicshare"	},
181 		{ "Templates",	"folder-templates"	},
182 		{ "Video",	"folder-videos"		},
183 		{ "Videos",	"folder-videos"		},
184 	};
185 	GtkIconTheme * icontheme;
186 	const unsigned int flags = GTK_ICON_LOOKUP_FORCE_SIZE;
187 
188 	if(lst == NULL && browser_vfs_lstat(filename, &ls) == 0)
189 		lst = &ls;
190 	/* check if the folder is special */
191 	if((p = strdup(filename)) != NULL
192 			&& (lst == NULL || !S_ISLNK(lst->st_mode))
193 			&& st != NULL
194 			&& browser_vfs_lstat(dirname(p), &ps) == 0)
195 	{
196 		if(st->st_dev != ps.st_dev || st->st_ino == ps.st_ino)
197 			icon = "mount-point";
198 		else if(_mime_icon_folder_is_home(st))
199 			icon = "folder_home";
200 		else if(_mime_icon_folder_in_home(&ps))
201 			/* check if the folder is special */
202 			for(i = 0; i < sizeof(name_icon) / sizeof(*name_icon);
203 					i++)
204 				if(strcasecmp(basename(p), name_icon[i].name)
205 						== 0)
206 				{
207 					icon = name_icon[i].icon;
208 					break;
209 				}
210 	}
211 	free(p);
212 	if(icon != NULL)
213 	{
214 		icontheme = gtk_icon_theme_get_default();
215 		ret = gtk_icon_theme_load_icon(icontheme, icon, size, flags,
216 				NULL);
217 	}
218 	/* generic fallback */
219 	if(ret == NULL)
220 		mime_icons(mime, "inode/directory", size, &ret, -1);
221 	return ret;
222 }
223 
_mime_icon_folder_in_home(struct stat * pst)224 static gboolean _mime_icon_folder_in_home(struct stat * pst)
225 {
226 	static char const * homedir = NULL;
227 	static struct stat hst;
228 
229 	if(homedir == NULL)
230 	{
231 		if((homedir = getenv("HOME")) == NULL
232 				&& (homedir = g_get_home_dir()) == NULL)
233 			return FALSE;
234 		if(browser_vfs_stat(homedir, &hst) != 0)
235 		{
236 			homedir = NULL;
237 			return FALSE;
238 		}
239 	}
240 	return (hst.st_dev == pst->st_dev && hst.st_ino == pst->st_ino)
241 		? TRUE : FALSE;
242 }
243 
_mime_icon_folder_is_home(struct stat * st)244 static gboolean _mime_icon_folder_is_home(struct stat * st)
245 {
246 	/* FIXME code duplicated from _mime_icon_folder_in_home() */
247 	static char const * homedir = NULL;
248 	static struct stat hst;
249 
250 	if(homedir == NULL)
251 	{
252 		if((homedir = getenv("HOME")) == NULL
253 				&& (homedir = g_get_home_dir()) == NULL)
254 			return FALSE;
255 		if(browser_vfs_stat(homedir, &hst) != 0)
256 		{
257 			homedir = NULL;
258 			return FALSE;
259 		}
260 	}
261 	return (hst.st_dev == st->st_dev && hst.st_ino == st->st_ino)
262 		? TRUE : FALSE;
263 }
264 
265 
266 /* browser_vfs_mime_type */
browser_vfs_mime_type(Mime * mime,char const * filename,mode_t mode)267 char const * browser_vfs_mime_type(Mime * mime, char const * filename,
268 		mode_t mode)
269 {
270 	char const * ret = NULL;
271 	struct stat st;
272 	struct stat pst;
273 	char * p = NULL;
274 
275 	if(mode == 0 && filename != NULL
276 			&& browser_vfs_lstat(filename, &st) == 0)
277 		mode = st.st_mode;
278 	if(S_ISDIR(mode))
279 	{
280 		/* look for mountpoints */
281 		if(filename != NULL && (p = strdup(filename)) != NULL
282 				&& browser_vfs_lstat(filename, &st) == 0
283 				&& browser_vfs_lstat(dirname(p), &pst) == 0
284 				&& (st.st_dev != pst.st_dev
285 					|| st.st_ino == pst.st_ino))
286 			ret = "inode/mountpoint";
287 		else
288 			ret = "inode/directory";
289 		free(p);
290 		return ret;
291 	}
292 	else if(S_ISBLK(mode))
293 		return "inode/blockdevice";
294 	else if(S_ISCHR(mode))
295 		return "inode/chardevice";
296 	else if(S_ISFIFO(mode))
297 		return "inode/fifo";
298 	else if(S_ISLNK(mode))
299 		return "inode/symlink";
300 #ifdef S_ISSOCK
301 	else if(S_ISSOCK(mode))
302 		return "inode/socket";
303 #endif
304 	if(mime != NULL && filename != NULL)
305 		ret = mime_type(mime, filename);
306 	if(ret == NULL && (mode & S_IXUSR) != 0)
307 		ret = "application/x-executable";
308 	return ret;
309 }
310 
311 
312 /* browser_vfs_opendir */
browser_vfs_opendir(char const * filename,struct stat * st)313 DIR * browser_vfs_opendir(char const * filename, struct stat * st)
314 {
315 	DIR * dir;
316 	int fd;
317 
318 #ifdef DEBUG
319 	fprintf(stderr, "DEBUG: %s(\"%s\", %p)\n", __func__, filename, st);
320 #endif
321 	if(st == NULL)
322 		return opendir(filename);
323 #if defined(__sun)
324 	if((fd = open(filename, O_RDONLY)) < 0
325 			|| (dir = fdopendir(fd)) == NULL)
326 	{
327 		if(fd >= 0)
328 			close(fd);
329 		return NULL;
330 	}
331 #else
332 	if((dir = opendir(filename)) == NULL)
333 		return NULL;
334 	fd = dirfd(dir);
335 #endif
336 	if(fstat(fd, st) != 0)
337 	{
338 		browser_vfs_closedir(dir);
339 		return NULL;
340 	}
341 	return dir;
342 }
343 
344 
345 /* browser_vfs_readdir */
browser_vfs_readdir(DIR * dir)346 struct dirent * browser_vfs_readdir(DIR * dir)
347 {
348 	return readdir(dir);
349 }
350 
351 
352 /* browser_vfs_stat */
browser_vfs_stat(char const * filename,struct stat * st)353 int browser_vfs_stat(char const * filename, struct stat * st)
354 {
355 	return stat(filename, st);
356 }
357