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