xref: /haiku/src/kits/storage/Mime.cpp (revision be012e21222c4d8d70082d12353acb163dc60ba8)
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 <Node.h>
35 #include <Path.h>
36 #include <RegistrarDefs.h>
37 #include <Roster.h>
38 #include <RosterPrivate.h>
39 
40 
41 using namespace BPrivate;
42 
43 enum {
44 	NOT_IMPLEMENTED	= B_ERROR,
45 };
46 
47 
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 
86 // Updates the MIME information (i.e MIME type) for one or more files.
87 int
88 update_mime_info(const char *path, int recursive, int synchronous, int force)
89 {
90 	// Force recursion when given a NULL path
91 	if (!path)
92 		recursive = true;
93 
94 	return do_mime_update(B_REG_MIME_UPDATE_MIME_INFO, path, recursive,
95 		synchronous, force);
96 }
97 
98 
99 // Creates a MIME database entry for one or more applications.
100 status_t
101 create_app_meta_mime(const char *path, int recursive, int synchronous,
102 	int force)
103 {
104 	// Force recursion when given a NULL path
105 	if (!path)
106 		recursive = true;
107 
108 	return do_mime_update(B_REG_MIME_CREATE_APP_META_MIME, path, recursive,
109 		synchronous, force);
110 }
111 
112 
113 // Retrieves an icon associated with a given device.
114 status_t
115 get_device_icon(const char *device, void *icon, int32 size)
116 {
117 	if (device == NULL || icon == NULL
118 		|| (size != B_LARGE_ICON && size != B_MINI_ICON))
119 		return B_BAD_VALUE;
120 
121 	int fd = open(device, O_RDONLY);
122 	if (fd < 0)
123 		return errno;
124 
125 	// ToDo: The mounted directories for volumes can also have META:X:STD_ICON
126 	// attributes. Should those attributes override the icon returned by
127 	// ioctl(,B_GET_ICON,)?
128 	device_icon iconData = {size, icon};
129 	if (ioctl(fd, B_GET_ICON, &iconData) != 0) {
130 		// legacy icon was not available, try vector icon
131 		close(fd);
132 
133 		uint8* data;
134 		size_t dataSize;
135 		type_code type;
136 		status_t status = get_device_icon(device, &data, &dataSize, &type);
137 		if (status == B_OK) {
138 			BBitmap* icon32 = new(std::nothrow) BBitmap(
139 				BRect(0, 0, size - 1, size - 1), B_BITMAP_NO_SERVER_LINK,
140 				B_RGBA32);
141 			BBitmap* icon8 = new(std::nothrow) BBitmap(
142 				BRect(0, 0, size - 1, size - 1), B_BITMAP_NO_SERVER_LINK,
143 				B_CMAP8);
144 
145 			ArrayDeleter<uint8> dataDeleter(data);
146 			ObjectDeleter<BBitmap> icon32Deleter(icon32);
147 			ObjectDeleter<BBitmap> icon8Deleter(icon8);
148 
149 			if (icon32 == NULL || icon32->InitCheck() != B_OK || icon8 == NULL
150 				|| icon8->InitCheck() != B_OK) {
151 				return B_NO_MEMORY;
152 			}
153 
154 			status = BIconUtils::GetVectorIcon(data, dataSize, icon32);
155 			if (status == B_OK)
156 				status = BIconUtils::ConvertToCMAP8(icon32, icon8);
157 			if (status == B_OK)
158 				memcpy(icon, icon8->Bits(), icon8->BitsLength());
159 
160 			return status;
161 		}
162 		return errno;
163 	}
164 
165 	close(fd);
166 	return B_OK;
167 }
168 
169 
170 // Retrieves an icon associated with a given device.
171 status_t
172 get_device_icon(const char *device, BBitmap *icon, icon_size which)
173 {
174 	// check parameters
175 	if (device == NULL || icon == NULL)
176 		return B_BAD_VALUE;
177 
178 	uint8* data;
179 	size_t size;
180 	type_code type;
181 	status_t status = get_device_icon(device, &data, &size, &type);
182 	if (status == B_OK) {
183 		status = BIconUtils::GetVectorIcon(data, size, icon);
184 		delete[] data;
185 		return status;
186 	}
187 
188 	// Vector icon was not available, try old one
189 
190 	BRect rect;
191 	if (which == B_MINI_ICON)
192 		rect.Set(0, 0, 15, 15);
193 	else if (which == B_LARGE_ICON)
194 		rect.Set(0, 0, 31, 31);
195 
196 	BBitmap* bitmap = icon;
197 	int32 iconSize = which;
198 
199 	if (icon->ColorSpace() != B_CMAP8
200 		|| (which != B_MINI_ICON && which != B_LARGE_ICON)) {
201 		if (which < B_LARGE_ICON)
202 			iconSize = B_MINI_ICON;
203 		else
204 			iconSize = B_LARGE_ICON;
205 
206 		bitmap = new(std::nothrow) BBitmap(
207 			BRect(0, 0, iconSize - 1, iconSize -1), B_BITMAP_NO_SERVER_LINK,
208 			B_CMAP8);
209 		if (bitmap == NULL || bitmap->InitCheck() != B_OK) {
210 			delete bitmap;
211 			return B_NO_MEMORY;
212 		}
213 	}
214 
215 	// get the icon, convert temporary data into bitmap if necessary
216 	status = get_device_icon(device, bitmap->Bits(), iconSize);
217 	if (status == B_OK && icon != bitmap)
218 		status = BIconUtils::ConvertFromCMAP8(bitmap, icon);
219 
220 	if (icon != bitmap)
221 		delete bitmap;
222 
223 	return status;
224 }
225 
226 
227 status_t
228 get_device_icon(const char *device, uint8** _data, size_t* _size,
229 	type_code* _type)
230 {
231 	if (device == NULL || _data == NULL || _size == NULL || _type == NULL)
232 		return B_BAD_VALUE;
233 
234 	int fd = open(device, O_RDONLY);
235 	if (fd < 0)
236 		return errno;
237 
238 	// Try to get the icon by name first
239 
240 	char name[B_FILE_NAME_LENGTH];
241 	if (ioctl(fd, B_GET_ICON_NAME, name) >= 0) {
242 		status_t status = get_named_icon(name, _data, _size, _type);
243 		if (status == B_OK) {
244 			close(fd);
245 			return B_OK;
246 		}
247 	}
248 
249 	// Getting the named icon failed, try vector icon next
250 
251 	// NOTE: The actual icon size is unknown as of yet. After the first call
252 	// to B_GET_VECTOR_ICON, the actual size is known and the final buffer
253 	// is allocated with the correct size. If the buffer needed to be
254 	// larger, then the temporary buffer above will not yet contain the
255 	// valid icon data. In that case, a second call to B_GET_VECTOR_ICON
256 	// retrieves it into the final buffer.
257 	uint8 data[8192];
258 	device_icon iconData = {sizeof(data), data};
259 	status_t status = ioctl(fd, B_GET_VECTOR_ICON, &iconData,
260 		sizeof(device_icon));
261 	if (status != 0)
262 		status = errno;
263 
264 	if (status == B_OK) {
265 		*_data = new(std::nothrow) uint8[iconData.icon_size];
266 		if (*_data == NULL)
267 			status = B_NO_MEMORY;
268 	}
269 
270 	if (status == B_OK) {
271 		if (iconData.icon_size > (int32)sizeof(data)) {
272 			// the stack buffer does not contain the data, see NOTE above
273 			iconData.icon_data = *_data;
274 			status = ioctl(fd, B_GET_VECTOR_ICON, &iconData,
275 				sizeof(device_icon));
276 			if (status != 0)
277 				status = errno;
278 		} else
279 			memcpy(*_data, data, iconData.icon_size);
280 
281 		*_size = iconData.icon_size;
282 		*_type = B_VECTOR_ICON_TYPE;
283 	}
284 
285 	// TODO: also support getting the old icon?
286 	close(fd);
287 	return status;
288 }
289 
290 
291 status_t
292 get_named_icon(const char* name, BBitmap* icon, icon_size which)
293 {
294 	// check parameters
295 	if (name == NULL || icon == NULL)
296 		return B_BAD_VALUE;
297 
298 	BRect rect;
299 	if (which == B_MINI_ICON)
300 		rect.Set(0, 0, 15, 15);
301 	else if (which == B_LARGE_ICON)
302 		rect.Set(0, 0, 31, 31);
303 	else
304 		return B_BAD_VALUE;
305 
306 	if (icon->Bounds() != rect)
307 		return B_BAD_VALUE;
308 
309 	uint8* data;
310 	size_t size;
311 	type_code type;
312 	status_t status = get_named_icon(name, &data, &size, &type);
313 	if (status == B_OK) {
314 		status = BIconUtils::GetVectorIcon(data, size, icon);
315 		delete[] data;
316 	}
317 
318 	return status;
319 }
320 
321 
322 status_t
323 get_named_icon(const char* name, uint8** _data, size_t* _size, type_code* _type)
324 {
325 	if (name == NULL || _data == NULL || _size == NULL || _type == NULL)
326 		return B_BAD_VALUE;
327 
328 	directory_which kWhich[] = {
329 		B_USER_NONPACKAGED_DATA_DIRECTORY,
330 		B_USER_DATA_DIRECTORY,
331 		B_SYSTEM_NONPACKAGED_DATA_DIRECTORY,
332 		B_SYSTEM_DATA_DIRECTORY,
333 	};
334 
335 	status_t status = B_ENTRY_NOT_FOUND;
336 	BFile file;
337 	off_t size;
338 
339 	for (uint32 i = 0; i < sizeof(kWhich) / sizeof(kWhich[0]); i++) {
340 		BPath path;
341 		if (find_directory(kWhich[i], &path) != B_OK)
342 			continue;
343 
344 		path.Append("icons");
345 		path.Append(name);
346 
347 		status = file.SetTo(path.Path(), B_READ_ONLY);
348 		if (status == B_OK) {
349 			status = file.GetSize(&size);
350 			if (size > 1024 * 1024)
351 				status = B_ERROR;
352 		}
353 		if (status == B_OK)
354 			break;
355 	}
356 
357 	if (status != B_OK)
358 		return status;
359 
360 	*_data = new(std::nothrow) uint8[size];
361 	if (*_data == NULL)
362 		return B_NO_MEMORY;
363 
364 	if (file.Read(*_data, size) != size) {
365 		delete[] *_data;
366 		return B_ERROR;
367 	}
368 
369 	*_size = size;
370 	*_type = B_VECTOR_ICON_TYPE;
371 		// TODO: for now
372 
373 	return B_OK;
374 }
375