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