xref: /haiku/src/kits/storage/Mime.cpp (revision f75a7bf508f3156d63a14f8fd77c5e0ca4d08c42)
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 	\file Mime.cpp
13 	Mime type C functions implementation.
14 */
15 
16 #include <errno.h>
17 #include <new>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <sys/ioctl.h>
21 #include <unistd.h>
22 
23 #include <AutoDeleter.h>
24 #include <Bitmap.h>
25 #include <Drivers.h>
26 #include <Entry.h>
27 #include <File.h>
28 #include <FindDirectory.h>
29 #include <fs_attr.h>
30 #include <fs_info.h>
31 #include <IconUtils.h>
32 #include <Mime.h>
33 #include <MimeType.h>
34 #include <mime/database_access.h>
35 #include <Node.h>
36 #include <Path.h>
37 #include <RegistrarDefs.h>
38 #include <Roster.h>
39 #include <RosterPrivate.h>
40 
41 using namespace BPrivate;
42 
43 enum {
44 	NOT_IMPLEMENTED	= B_ERROR,
45 };
46 
47 // do_mime_update
48 //! Helper function that contacts the registrar for mime update calls
49 status_t
50 do_mime_update(int32 what, const char *path, int recursive,
51 	int synchronous, int force)
52 {
53 	BEntry root;
54 	entry_ref ref;
55 
56 	status_t err = root.SetTo(path ? path : "/");
57 	if (!err)
58 		err = root.GetRef(&ref);
59 	if (!err) {
60 		BMessage msg(what);
61 		BMessage reply;
62 		status_t result;
63 
64 		// Build and send the message, read the reply
65 		if (!err)
66 			err = msg.AddRef("entry", &ref);
67 		if (!err)
68 			err = msg.AddBool("recursive", recursive);
69 		if (!err)
70 			err = msg.AddBool("synchronous", synchronous);
71 		if (!err)
72 			err = msg.AddInt32("force", force);
73 		if (!err)
74 			err = BRoster::Private().SendTo(&msg, &reply, true);
75 		if (!err)
76 			err = reply.what == B_REG_RESULT ? B_OK : B_BAD_VALUE;
77 		if (!err)
78 			err = reply.FindInt32("result", &result);
79 		if (!err)
80 			err = result;
81 	}
82 	return err;
83 }
84 
85 // update_mime_info
86 /*!	\brief Updates the MIME information (i.e MIME type) for one or more files.
87 	If \a path points to a file, the MIME information for this file are
88 	updated only. If it points to a directory and \a recursive is non-null,
89 	the information for all the files in the given directory tree are updated.
90 	If path is \c NULL all files are considered; \a recursive is ignored in
91 	this case.
92 	\param path The path to a file or directory, or \c NULL.
93 	\param recursive Non-null to trigger recursive behavior.
94 	\param synchronous If non-null update_mime_info() waits until the
95 		   operation is finished, otherwise it returns immediately and the
96 		   update is done asynchronously.
97 	\param force Specifies how to handle files that already have MIME
98 		   information:
99 			- \c B_UPDATE_MIME_INFO_NO_FORCE: Files that already have a
100 			  \c BEOS:TYPE attribute won't be updated.
101 			- \c B_UPDATE_MIME_INFO_FORCE_KEEP_TYPE: Files that already have a
102 			  \c BEOS:TYPE attribute will be updated too, but \c BEOS:TYPE
103 			  itself will remain untouched.
104 			- \c B_UPDATE_MIME_INFO_FORCE_UPDATE_ALL: Similar to
105 			  \c B_UPDATE_MIME_INFO_FORCE_KEEP_TYPE, but the \c BEOS:TYPE
106 			  attribute will be updated too.
107 	\return
108 	- \c B_OK: Everything went fine.
109 	- An error code otherwise.
110 */
111 int
112 update_mime_info(const char *path, int recursive, int synchronous, int force)
113 {
114 	// Force recursion when given a NULL path
115 	if (!path)
116 		recursive = true;
117 
118 	return do_mime_update(B_REG_MIME_UPDATE_MIME_INFO, path, recursive,
119 		synchronous, force);
120 }
121 
122 // create_app_meta_mime
123 /*!	Creates a MIME database entry for one or more applications.
124 	If \a path points to an application file, a MIME DB entry is create for the
125 	application. If it points to a directory and \a recursive is non-null,
126 	entries are created for all application files in the given directory
127 	tree. If path is \c NULL all files are considered; \a recursive is
128 	ignored in this case.
129 	\param path The path to an application file, a directory, or \c NULL.
130 	\param recursive Non-null to trigger recursive behavior.
131 	\param synchronous If non-null create_app_meta_mime() waits until the
132 		   operation is finished, otherwise it returns immediately and the
133 		   operation is done asynchronously.
134 	\param force If non-null, entries are created even if they do already
135 		   exist.
136 	\return
137 	- \c B_OK: Everything went fine.
138 	- An error code otherwise.
139 */
140 status_t
141 create_app_meta_mime(const char *path, int recursive, int synchronous,
142 	int force)
143 {
144 	// Force recursion when given a NULL path
145 	if (!path)
146 		recursive = true;
147 
148 	return do_mime_update(B_REG_MIME_CREATE_APP_META_MIME, path, recursive,
149 		synchronous, force);
150 }
151 
152 
153 /*!	Retrieves an icon associated with a given device.
154 	\param dev The path to the device.
155 	\param icon A pointer to a buffer the icon data shall be written to.
156 	\param size The size of the icon. Currently the sizes 16 (small, i.e
157 	            \c B_MINI_ICON) and 32 (large, 	i.e. \c B_LARGE_ICON) are
158 	            supported.
159 
160 	\todo The mounted directories for volumes can also have META:X:STD_ICON
161 		  attributes. Should those attributes override the icon returned
162 		  by ioctl(,B_GET_ICON,)?
163 
164 	\return
165 	- \c B_OK: Everything went fine.
166 	- An error code otherwise.
167 */
168 status_t
169 get_device_icon(const char *device, void *icon, int32 size)
170 {
171 	if (device == NULL || icon == NULL
172 		|| (size != B_LARGE_ICON && size != B_MINI_ICON))
173 		return B_BAD_VALUE;
174 
175 	int fd = open(device, O_RDONLY);
176 	if (fd < 0)
177 		return errno;
178 
179 	device_icon iconData = {size, icon};
180 	if (ioctl(fd, B_GET_ICON, &iconData) != 0) {
181 		// legacy icon was not available, try vector icon
182 		close(fd);
183 
184 		uint8* data;
185 		size_t dataSize;
186 		type_code type;
187 		status_t status = get_device_icon(device, &data, &dataSize, &type);
188 		if (status == B_OK) {
189 			BBitmap* icon32 = new(std::nothrow) BBitmap(
190 				BRect(0, 0, size - 1, size - 1), B_BITMAP_NO_SERVER_LINK,
191 				B_RGBA32);
192 			BBitmap* icon8 = new(std::nothrow) BBitmap(
193 				BRect(0, 0, size - 1, size - 1), B_BITMAP_NO_SERVER_LINK,
194 				B_CMAP8);
195 
196 			ArrayDeleter<uint8> dataDeleter(data);
197 			ObjectDeleter<BBitmap> icon32Deleter(icon32);
198 			ObjectDeleter<BBitmap> icon8Deleter(icon8);
199 
200 			if (icon32 == NULL || icon32->InitCheck() != B_OK || icon8 == NULL
201 				|| icon8->InitCheck() != B_OK) {
202 				return B_NO_MEMORY;
203 			}
204 
205 			status = BIconUtils::GetVectorIcon(data, dataSize, icon32);
206 			if (status == B_OK)
207 				status = BIconUtils::ConvertToCMAP8(icon32, icon8);
208 			if (status == B_OK)
209 				memcpy(icon, icon8->Bits(), icon8->BitsLength());
210 
211 			return status;
212 		}
213 		return errno;
214 	}
215 
216 	close(fd);
217 	return B_OK;
218 }
219 
220 
221 /*!	Retrieves an icon associated with a given device.
222 	\param dev The path to the device.
223 	\param icon A pointer to a pre-allocated BBitmap of the correct dimension
224 		   to store the requested icon (16x16 for the mini and 32x32 for the
225 		   large icon).
226 	\param which Specifies the size of the icon to be retrieved:
227 		   \c B_MINI_ICON for the mini and \c B_LARGE_ICON for the large icon.
228 
229 	\todo The mounted directories for volumes can also have META:X:STD_ICON
230 		  attributes. Should those attributes override the icon returned
231 		  by ioctl(,B_GET_ICON,)?
232 
233 	\return
234 	- \c B_OK: Everything went fine.
235 	- An error code otherwise.
236 */
237 status_t
238 get_device_icon(const char *device, BBitmap *icon, icon_size which)
239 {
240 	// check parameters
241 	if (device == NULL || icon == NULL)
242 		return B_BAD_VALUE;
243 
244 	uint8* data;
245 	size_t size;
246 	type_code type;
247 	status_t status = get_device_icon(device, &data, &size, &type);
248 	if (status == B_OK) {
249 		status = BIconUtils::GetVectorIcon(data, size, icon);
250 		delete[] data;
251 		return status;
252 	}
253 
254 	// Vector icon was not available, try old one
255 
256 	BRect rect;
257 	if (which == B_MINI_ICON)
258 		rect.Set(0, 0, 15, 15);
259 	else if (which == B_LARGE_ICON)
260 		rect.Set(0, 0, 31, 31);
261 
262 	BBitmap* bitmap = icon;
263 	int32 iconSize = which;
264 
265 	if (icon->ColorSpace() != B_CMAP8
266 		|| (which != B_MINI_ICON && which != B_LARGE_ICON)) {
267 		if (which < B_LARGE_ICON)
268 			iconSize = B_MINI_ICON;
269 		else
270 			iconSize = B_LARGE_ICON;
271 
272 		bitmap = new(std::nothrow) BBitmap(
273 			BRect(0, 0, iconSize - 1, iconSize -1), B_BITMAP_NO_SERVER_LINK,
274 			B_CMAP8);
275 		if (bitmap == NULL || bitmap->InitCheck() != B_OK) {
276 			delete bitmap;
277 			return B_NO_MEMORY;
278 		}
279 	}
280 
281 	// get the icon, convert temporary data into bitmap if necessary
282 	status = get_device_icon(device, bitmap->Bits(), iconSize);
283 	if (status == B_OK && icon != bitmap)
284 		status = BIconUtils::ConvertFromCMAP8(bitmap, icon);
285 
286 	if (icon != bitmap)
287 		delete bitmap;
288 
289 	return status;
290 }
291 
292 
293 status_t
294 get_device_icon(const char *device, uint8** _data, size_t* _size,
295 	type_code* _type)
296 {
297 	if (device == NULL || _data == NULL || _size == NULL || _type == NULL)
298 		return B_BAD_VALUE;
299 
300 	int fd = open(device, O_RDONLY);
301 	if (fd < 0)
302 		return errno;
303 
304 	// Try to get the icon by name first
305 
306 	char name[B_FILE_NAME_LENGTH];
307 	if (ioctl(fd, B_GET_ICON_NAME, name) >= 0) {
308 		status_t status = get_named_icon(name, _data, _size, _type);
309 		if (status == B_OK) {
310 			close(fd);
311 			return B_OK;
312 		}
313 	}
314 
315 	// Getting the named icon failed, try vector icon next
316 
317 	// NOTE: The actual icon size is unknown as of yet. After the first call
318 	// to B_GET_VECTOR_ICON, the actual size is known and the final buffer
319 	// is allocated with the correct size. If the buffer needed to be
320 	// larger, then the temporary buffer above will not yet contain the
321 	// valid icon data. In that case, a second call to B_GET_VECTOR_ICON
322 	// retrieves it into the final buffer.
323 	uint8 data[8192];
324 	device_icon iconData = {sizeof(data), data};
325 	status_t status = ioctl(fd, B_GET_VECTOR_ICON, &iconData,
326 		sizeof(device_icon));
327 	if (status != 0)
328 		status = errno;
329 
330 	if (status == B_OK) {
331 		*_data = new(std::nothrow) uint8[iconData.icon_size];
332 		if (*_data == NULL)
333 			status = B_NO_MEMORY;
334 	}
335 
336 	if (status == B_OK) {
337 		if (iconData.icon_size > (int32)sizeof(data)) {
338 			// the stack buffer does not contain the data, see NOTE above
339 			iconData.icon_data = *_data;
340 			status = ioctl(fd, B_GET_VECTOR_ICON, &iconData,
341 				sizeof(device_icon));
342 			if (status != 0)
343 				status = errno;
344 		} else
345 			memcpy(*_data, data, iconData.icon_size);
346 
347 		*_size = iconData.icon_size;
348 		*_type = B_VECTOR_ICON_TYPE;
349 	}
350 
351 	// TODO: also support getting the old icon?
352 	close(fd);
353 	return status;
354 }
355 
356 
357 status_t
358 get_named_icon(const char* name, BBitmap* icon, icon_size which)
359 {
360 	// check parameters
361 	if (name == NULL || icon == NULL)
362 		return B_BAD_VALUE;
363 
364 	BRect rect;
365 	if (which == B_MINI_ICON)
366 		rect.Set(0, 0, 15, 15);
367 	else if (which == B_LARGE_ICON)
368 		rect.Set(0, 0, 31, 31);
369 	else
370 		return B_BAD_VALUE;
371 
372 	if (icon->Bounds() != rect)
373 		return B_BAD_VALUE;
374 
375 	uint8* data;
376 	size_t size;
377 	type_code type;
378 	status_t status = get_named_icon(name, &data, &size, &type);
379 	if (status == B_OK)
380 		status = BIconUtils::GetVectorIcon(data, size, icon);
381 
382 	delete[] data;
383 	return status;
384 }
385 
386 
387 status_t
388 get_named_icon(const char* name, uint8** _data, size_t* _size, type_code* _type)
389 {
390 	if (name == NULL || _data == NULL || _size == NULL || _type == NULL)
391 		return B_BAD_VALUE;
392 
393 	directory_which kWhich[] = {
394 		B_USER_DATA_DIRECTORY,
395 		B_COMMON_DATA_DIRECTORY,
396 		B_BEOS_DATA_DIRECTORY,
397 	};
398 
399 	status_t status = B_ENTRY_NOT_FOUND;
400 	BFile file;
401 	off_t size;
402 
403 	for (uint32 i = 0; i < sizeof(kWhich) / sizeof(kWhich[0]); i++) {
404 		BPath path;
405 		if (find_directory(kWhich[i], &path) != B_OK)
406 			continue;
407 
408 		path.Append("icons");
409 		path.Append(name);
410 
411 		status = file.SetTo(path.Path(), B_READ_ONLY);
412 		if (status == B_OK) {
413 			status = file.GetSize(&size);
414 			if (size > 1024 * 1024)
415 				status = B_ERROR;
416 		}
417 		if (status == B_OK)
418 			break;
419 	}
420 
421 	if (status != B_OK)
422 		return status;
423 
424 	*_data = new(std::nothrow) uint8[size];
425 	if (*_data == NULL)
426 		return B_NO_MEMORY;
427 
428 	if (file.Read(*_data, size) != size) {
429 		delete[] *_data;
430 		return B_ERROR;
431 	}
432 
433 	*_size = size;
434 	*_type = B_VECTOR_ICON_TYPE;
435 		// TODO: for now
436 
437 	return B_OK;
438 }
439