xref: /haiku/src/kits/storage/Mime.cpp (revision e705c841d784f0035a0ef3e9e96f6e017df16681)
1 /*
2  * Copyright 2002-2008, Haiku Inc.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Tyler Dauwalder
7  *		Ingo Weinhold, bonefish@users.sf.net
8  *		Axel Dörfler, axeld@pinc-software.de
9  */
10 
11 
12 #include <errno.h>
13 #include <new>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <sys/ioctl.h>
17 #include <unistd.h>
18 
19 #include <AutoDeleter.h>
20 #include <Bitmap.h>
21 #include <Drivers.h>
22 #include <Entry.h>
23 #include <File.h>
24 #include <FindDirectory.h>
25 #include <fs_attr.h>
26 #include <fs_info.h>
27 #include <IconUtils.h>
28 #include <Mime.h>
29 #include <MimeType.h>
30 #include <Node.h>
31 #include <Path.h>
32 #include <RegistrarDefs.h>
33 #include <Roster.h>
34 #include <RosterPrivate.h>
35 
36 
37 using namespace BPrivate;
38 
39 
40 // Helper function that contacts the registrar for mime update calls
41 status_t
42 do_mime_update(int32 what, const char* path, int recursive,
43 	int synchronous, int force)
44 {
45 	BEntry root;
46 	entry_ref ref;
47 
48 	status_t err = root.SetTo(path ? path : "/");
49 	if (!err)
50 		err = root.GetRef(&ref);
51 	if (!err) {
52 		BMessage msg(what);
53 		BMessage reply;
54 		status_t result;
55 
56 		// Build and send the message, read the reply
57 		if (!err)
58 			err = msg.AddRef("entry", &ref);
59 		if (!err)
60 			err = msg.AddBool("recursive", recursive);
61 		if (!err)
62 			err = msg.AddBool("synchronous", synchronous);
63 		if (!err)
64 			err = msg.AddInt32("force", force);
65 		if (!err)
66 			err = BRoster::Private().SendTo(&msg, &reply, true);
67 		if (!err)
68 			err = reply.what == B_REG_RESULT ? B_OK : B_BAD_VALUE;
69 		if (!err)
70 			err = reply.FindInt32("result", &result);
71 		if (!err)
72 			err = result;
73 	}
74 	return err;
75 }
76 
77 
78 // Updates the MIME information (i.e MIME type) for one or more files.
79 int
80 update_mime_info(const char* path, int recursive, int synchronous, int force)
81 {
82 	// Force recursion when given a NULL path
83 	if (!path)
84 		recursive = true;
85 
86 	return do_mime_update(B_REG_MIME_UPDATE_MIME_INFO, path, recursive,
87 		synchronous, force);
88 }
89 
90 
91 // Creates a MIME database entry for one or more applications.
92 status_t
93 create_app_meta_mime(const char* path, int recursive, int synchronous,
94 	int force)
95 {
96 	// Force recursion when given a NULL path
97 	if (!path)
98 		recursive = true;
99 
100 	return do_mime_update(B_REG_MIME_CREATE_APP_META_MIME, path, recursive,
101 		synchronous, force);
102 }
103 
104 
105 // Retrieves an icon associated with a given device.
106 status_t
107 get_device_icon(const char* device, void* icon, int32 size)
108 {
109 	if (device == NULL || icon == NULL
110 		|| (size != B_LARGE_ICON && size != B_MINI_ICON))
111 		return B_BAD_VALUE;
112 
113 	int fd = open(device, O_RDONLY);
114 	if (fd < 0)
115 		return errno;
116 
117 	// ToDo: The mounted directories for volumes can also have META:X:STD_ICON
118 	// attributes. Should those attributes override the icon returned by
119 	// ioctl(,B_GET_ICON,)?
120 	device_icon iconData = {size, icon};
121 	if (ioctl(fd, B_GET_ICON, &iconData) != 0) {
122 		// legacy icon was not available, try vector icon
123 		close(fd);
124 
125 		uint8* data;
126 		size_t dataSize;
127 		type_code type;
128 		status_t status = get_device_icon(device, &data, &dataSize, &type);
129 		if (status == B_OK) {
130 			BBitmap* icon32 = new(std::nothrow) BBitmap(
131 				BRect(0, 0, size - 1, size - 1), B_BITMAP_NO_SERVER_LINK,
132 				B_RGBA32);
133 			BBitmap* icon8 = new(std::nothrow) BBitmap(
134 				BRect(0, 0, size - 1, size - 1), B_BITMAP_NO_SERVER_LINK,
135 				B_CMAP8);
136 
137 			ArrayDeleter<uint8> dataDeleter(data);
138 			ObjectDeleter<BBitmap> icon32Deleter(icon32);
139 			ObjectDeleter<BBitmap> icon8Deleter(icon8);
140 
141 			if (icon32 == NULL || icon32->InitCheck() != B_OK || icon8 == NULL
142 				|| icon8->InitCheck() != B_OK) {
143 				return B_NO_MEMORY;
144 			}
145 
146 			status = BIconUtils::GetVectorIcon(data, dataSize, icon32);
147 			if (status == B_OK)
148 				status = BIconUtils::ConvertToCMAP8(icon32, icon8);
149 			if (status == B_OK)
150 				memcpy(icon, icon8->Bits(), icon8->BitsLength());
151 
152 			return status;
153 		}
154 		return errno;
155 	}
156 
157 	close(fd);
158 	return B_OK;
159 }
160 
161 
162 // Retrieves an icon associated with a given device.
163 status_t
164 get_device_icon(const char* device, BBitmap* icon, icon_size which)
165 {
166 	// check parameters
167 	if (device == NULL || icon == NULL)
168 		return B_BAD_VALUE;
169 
170 	uint8* data;
171 	size_t size;
172 	type_code type;
173 	status_t status = get_device_icon(device, &data, &size, &type);
174 	if (status == B_OK) {
175 		status = BIconUtils::GetVectorIcon(data, size, icon);
176 		delete[] data;
177 		return status;
178 	}
179 
180 	// Vector icon was not available, try old one
181 
182 	BRect rect;
183 	if (which == B_MINI_ICON)
184 		rect.Set(0, 0, 15, 15);
185 	else if (which == B_LARGE_ICON)
186 		rect.Set(0, 0, 31, 31);
187 
188 	BBitmap* bitmap = icon;
189 	int32 iconSize = which;
190 
191 	if (icon->ColorSpace() != B_CMAP8
192 		|| (which != B_MINI_ICON && which != B_LARGE_ICON)) {
193 		if (which < B_LARGE_ICON)
194 			iconSize = B_MINI_ICON;
195 		else
196 			iconSize = B_LARGE_ICON;
197 
198 		bitmap = new(std::nothrow) BBitmap(
199 			BRect(0, 0, iconSize - 1, iconSize -1), B_BITMAP_NO_SERVER_LINK,
200 			B_CMAP8);
201 		if (bitmap == NULL || bitmap->InitCheck() != B_OK) {
202 			delete bitmap;
203 			return B_NO_MEMORY;
204 		}
205 	}
206 
207 	// get the icon, convert temporary data into bitmap if necessary
208 	status = get_device_icon(device, bitmap->Bits(), iconSize);
209 	if (status == B_OK && icon != bitmap)
210 		status = BIconUtils::ConvertFromCMAP8(bitmap, icon);
211 
212 	if (icon != bitmap)
213 		delete bitmap;
214 
215 	return status;
216 }
217 
218 
219 status_t
220 get_device_icon(const char* device, uint8** _data, size_t* _size,
221 	type_code* _type)
222 {
223 	if (device == NULL || _data == NULL || _size == NULL || _type == NULL)
224 		return B_BAD_VALUE;
225 
226 	int fd = open(device, O_RDONLY);
227 	if (fd < 0)
228 		return errno;
229 
230 	// Try to get the icon by name first
231 
232 	char name[B_FILE_NAME_LENGTH];
233 	if (ioctl(fd, B_GET_ICON_NAME, name) >= 0) {
234 		status_t status = get_named_icon(name, _data, _size, _type);
235 		if (status == B_OK) {
236 			close(fd);
237 			return B_OK;
238 		}
239 	}
240 
241 	// Getting the named icon failed, try vector icon next
242 
243 	// NOTE: The actual icon size is unknown as of yet. After the first call
244 	// to B_GET_VECTOR_ICON, the actual size is known and the final buffer
245 	// is allocated with the correct size. If the buffer needed to be
246 	// larger, then the temporary buffer above will not yet contain the
247 	// valid icon data. In that case, a second call to B_GET_VECTOR_ICON
248 	// retrieves it into the final buffer.
249 	uint8 data[8192];
250 	device_icon iconData = {sizeof(data), data};
251 	status_t status = ioctl(fd, B_GET_VECTOR_ICON, &iconData,
252 		sizeof(device_icon));
253 	if (status != 0)
254 		status = errno;
255 
256 	if (status == B_OK) {
257 		*_data = new(std::nothrow) uint8[iconData.icon_size];
258 		if (*_data == NULL)
259 			status = B_NO_MEMORY;
260 	}
261 
262 	if (status == B_OK) {
263 		if (iconData.icon_size > (int32)sizeof(data)) {
264 			// the stack buffer does not contain the data, see NOTE above
265 			iconData.icon_data = *_data;
266 			status = ioctl(fd, B_GET_VECTOR_ICON, &iconData,
267 				sizeof(device_icon));
268 			if (status != 0)
269 				status = errno;
270 		} else
271 			memcpy(*_data, data, iconData.icon_size);
272 
273 		*_size = iconData.icon_size;
274 		*_type = B_VECTOR_ICON_TYPE;
275 	}
276 
277 	// TODO: also support getting the old icon?
278 	close(fd);
279 	return status;
280 }
281 
282 
283 status_t
284 get_named_icon(const char* name, BBitmap* icon, icon_size which)
285 {
286 	// check parameters
287 	if (name == NULL || icon == NULL)
288 		return B_BAD_VALUE;
289 
290 	BRect rect;
291 	if (which == B_MINI_ICON)
292 		rect.Set(0, 0, 15, 15);
293 	else if (which == B_LARGE_ICON)
294 		rect.Set(0, 0, 31, 31);
295 	else
296 		return B_BAD_VALUE;
297 
298 	if (icon->Bounds() != rect)
299 		return B_BAD_VALUE;
300 
301 	uint8* data;
302 	size_t size;
303 	type_code type;
304 	status_t status = get_named_icon(name, &data, &size, &type);
305 	if (status == B_OK) {
306 		status = BIconUtils::GetVectorIcon(data, size, icon);
307 		delete[] data;
308 	}
309 
310 	return status;
311 }
312 
313 
314 status_t
315 get_named_icon(const char* name, uint8** _data, size_t* _size, type_code* _type)
316 {
317 	if (name == NULL || _data == NULL || _size == NULL || _type == NULL)
318 		return B_BAD_VALUE;
319 
320 	directory_which kWhich[] = {
321 		B_USER_NONPACKAGED_DATA_DIRECTORY,
322 		B_USER_DATA_DIRECTORY,
323 		B_SYSTEM_NONPACKAGED_DATA_DIRECTORY,
324 		B_SYSTEM_DATA_DIRECTORY,
325 	};
326 
327 	status_t status = B_ENTRY_NOT_FOUND;
328 	BFile file;
329 	off_t size;
330 
331 	for (uint32 i = 0; i < sizeof(kWhich) / sizeof(kWhich[0]); i++) {
332 		BPath path;
333 		if (find_directory(kWhich[i], &path) != B_OK)
334 			continue;
335 
336 		path.Append("icons");
337 		path.Append(name);
338 
339 		status = file.SetTo(path.Path(), B_READ_ONLY);
340 		if (status == B_OK) {
341 			status = file.GetSize(&size);
342 			if (size > 1024 * 1024)
343 				status = B_ERROR;
344 		}
345 		if (status == B_OK)
346 			break;
347 	}
348 
349 	if (status != B_OK)
350 		return status;
351 
352 	*_data = new(std::nothrow) uint8[size];
353 	if (*_data == NULL)
354 		return B_NO_MEMORY;
355 
356 	if (file.Read(*_data, size) != size) {
357 		delete[] *_data;
358 		return B_ERROR;
359 	}
360 
361 	*_size = size;
362 	*_type = B_VECTOR_ICON_TYPE;
363 		// TODO: for now
364 
365 	return B_OK;
366 }
367