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
37using namespace BPrivate;
38
39
40// Helper function that contacts the registrar for mime update calls
41status_t
42do_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.
79int
80update_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.
92status_t
93create_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.
106status_t
107get_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, sizeof(device_icon)) != 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.
163status_t
164get_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
219status_t
220get_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, sizeof(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
283status_t
284get_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
314status_t
315get_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