1/*
2 * Copyright 2002-2006, 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 */
9
10/*!
11	\file UpdateMimeInfoThread.h
12	UpdateMimeInfoThread implementation
13*/
14
15#include "mime/UpdateMimeInfoThread.h"
16
17#include <AppFileInfo.h>
18#include <Bitmap.h>
19#include <fs_attr.h>
20#include <Message.h>
21#include <Messenger.h>
22#include <mime/database_support.h>
23#include <Node.h>
24#include <Resources.h>
25#include <String.h>
26
27#if !defined(__BEOS__) || defined(__HAIKU__)
28#	include <MimeType.h>
29#else
30#	define B_VECTOR_ICON_TYPE 'VICN'
31#endif
32
33#ifndef B_UPDATE_MIME_INFO_FORCE_UPDATE_ALL
34#	define B_UPDATE_MIME_INFO_FORCE_UPDATE_ALL 2
35#endif
36#ifndef B_UPDATE_MIME_INFO_FORCE_KEEP_TYPE
37#	define B_UPDATE_MIME_INFO_FORCE_KEEP_TYPE 1
38#endif
39#ifndef B_BITMAP_NO_SERVER_LINK
40#	define B_BITMAP_NO_SERVER_LINK 0
41#endif
42
43namespace BPrivate {
44namespace Storage {
45namespace Mime {
46
47static const char *kAppFlagsAttribute = "BEOS:APP_FLAGS";
48
49
50static status_t
51update_icon(BAppFileInfo &appFileInfoRead, BAppFileInfo &appFileInfoWrite,
52	const char *type, BBitmap &icon, icon_size iconSize)
53{
54	status_t err = appFileInfoRead.GetIconForType(type, &icon, iconSize);
55	if (err == B_OK)
56		err = appFileInfoWrite.SetIconForType(type, &icon, iconSize);
57	else if (err == B_ENTRY_NOT_FOUND || err == B_NAME_NOT_FOUND) {
58		err = appFileInfoWrite.SetIconForType(type, NULL, iconSize);
59#if defined(__BEOS__) && !defined(__HAIKU__)
60		// gives an error if the attribute didn't exist yet...
61		err = B_OK;
62#endif
63	}
64
65	return err;
66}
67
68
69/*!
70	This updates the vector icon of the file from its resources.
71	Instead of putting this functionality into BAppFileInfo (as done in Haiku),
72	we're doing it here, so that the mimeset executable that runs under BeOS
73	can make use of it as well.
74*/
75static status_t
76update_vector_icon(BFile& file, const char *type)
77{
78	// try to read icon from resources
79	BResources resources(&file);
80
81	BString name("BEOS:");
82
83	// check type param
84	if (type) {
85		if (BMimeType::IsValid(type))
86			name += type;
87		else
88			return B_BAD_VALUE;
89	} else
90		name += "ICON";
91
92	size_t size;
93	const void* data = resources.LoadResource(B_VECTOR_ICON_TYPE, name.String(), &size);
94	if (data == NULL) {
95		// remove attribute; the resources don't have an icon
96		file.RemoveAttr(name.String());
97		return B_OK;
98	}
99
100	// update icon
101
102	ssize_t written = file.WriteAttr(name.String(), B_VECTOR_ICON_TYPE, 0, data, size);
103	if (written < B_OK)
104		return written;
105	if ((size_t)written < size) {
106		file.RemoveAttr(name.String());
107		return B_ERROR;
108	}
109	return B_OK;
110}
111
112
113#if defined(__BEOS__) || !defined(__HAIKU__)
114// BMimeType::GuessMimeType() doesn't seem to work under BeOS
115status_t
116guess_mime_type(const void *_buffer, int32 length, BMimeType *type)
117{
118	const uint8 *buffer = (const uint8*)_buffer;
119	if (!buffer || !type)
120		return B_BAD_VALUE;
121
122	// we only know ELF files
123	if (length >= 4 && buffer[0] == 0x7f && buffer[1] == 'E' && buffer[2] == 'L'
124		&&  buffer[3] == 'F') {
125		return type->SetType(B_ELF_APP_MIME_TYPE);
126	}
127
128	return type->SetType(B_FILE_MIME_TYPE);
129}
130
131
132status_t
133guess_mime_type(const entry_ref *ref, BMimeType *type)
134{
135	if (!ref || !type)
136		return B_BAD_VALUE;
137
138	// get BEntry
139	BEntry entry;
140	status_t error = entry.SetTo(ref);
141	if (error != B_OK)
142		return error;
143
144	// does entry exist?
145	if (!entry.Exists())
146		return B_ENTRY_NOT_FOUND;
147
148	// check entry type
149	if (entry.IsDirectory())
150		return type->SetType("application/x-vnd.be-directory");
151	if (entry.IsSymLink())
152		return type->SetType("application/x-vnd.be-symlink");
153	if (!entry.IsFile())
154		return B_ERROR;
155
156	// we have a file, read the first 4 bytes
157	BFile file;
158	char buffer[4];
159	if (file.SetTo(ref, B_READ_ONLY) == B_OK
160		&& file.Read(buffer, 4) == 4) {
161		return guess_mime_type(buffer, 4, type);
162	}
163
164	// we couldn't open or read the file
165	return type->SetType(B_FILE_MIME_TYPE);
166}
167#endif
168
169
170static bool
171is_shared_object_mime_type(BMimeType &type)
172{
173	return (type == B_APP_MIME_TYPE);
174}
175
176
177//	#pragma mark -
178
179
180//! Creates a new UpdateMimeInfoThread object
181UpdateMimeInfoThread::UpdateMimeInfoThread(const char *name, int32 priority,
182	BMessenger managerMessenger, const entry_ref *root, bool recursive,
183	int32 force, BMessage *replyee)
184	: MimeUpdateThread(name, priority, managerMessenger, root, recursive, force,
185		replyee)
186{
187}
188
189// DoMimeUpdate
190/*! \brief Performs an update_mime_info() update on the given entry
191
192	If the entry has no \c BEOS:TYPE attribute, or if \c fForce is true, the
193	entry is sniffed and its \c BEOS:TYPE attribute is set accordingly.
194*/
195status_t
196UpdateMimeInfoThread::DoMimeUpdate(const entry_ref *entry, bool *entryIsDir)
197{
198	if (entry == NULL)
199		return B_BAD_VALUE;
200
201	bool updateType = false;
202	bool updateAppInfo = false;
203
204	BNode node;
205	status_t err = node.SetTo(entry);
206	if (!err && entryIsDir)
207		*entryIsDir = node.IsDirectory();
208	if (!err) {
209		// If not forced, only update if the entry has no file type attribute
210		attr_info info;
211		if (fForce == B_UPDATE_MIME_INFO_FORCE_UPDATE_ALL
212			|| node.GetAttrInfo(kFileTypeAttr, &info) == B_ENTRY_NOT_FOUND)
213			updateType = true;
214
215		updateAppInfo = updateType
216			|| fForce == B_UPDATE_MIME_INFO_FORCE_KEEP_TYPE;
217	}
218
219	// guess the MIME type
220	BMimeType type;
221	if (!err && (updateType || updateAppInfo)) {
222		err = BMimeType::GuessMimeType(entry, &type);
223#if defined(__BEOS__) && !defined(__HAIKU__)
224		// GuessMimeType() doesn't seem to work correctly under BeOS
225		if (err)
226			err = guess_mime_type(entry, &type);
227#endif
228		if (!err)
229			err = type.InitCheck();
230	}
231
232	// update the MIME type
233	if (!err && updateType) {
234		const char *typeStr = type.Type();
235		ssize_t len = strlen(typeStr) + 1;
236		ssize_t bytes = node.WriteAttr(kFileTypeAttr, kFileTypeType, 0,
237			typeStr, len);
238		if (bytes < B_OK)
239			err = bytes;
240		else
241			err = (bytes != len ? (status_t)B_FILE_ERROR : (status_t)B_OK);
242	}
243
244	// update the app file info attributes, if this is a shared object
245	BFile file;
246	BAppFileInfo appFileInfoRead;
247	BAppFileInfo appFileInfoWrite;
248	if (!err && updateAppInfo && node.IsFile()
249		&& is_shared_object_mime_type(type)
250		&& file.SetTo(entry, B_READ_WRITE) == B_OK
251		&& appFileInfoRead.SetTo(&file) == B_OK
252		&& appFileInfoWrite.SetTo(&file) == B_OK) {
253		// we read from resources and write to attributes
254		appFileInfoRead.SetInfoLocation(B_USE_RESOURCES);
255		appFileInfoWrite.SetInfoLocation(B_USE_ATTRIBUTES);
256
257		// signature
258		char signature[B_MIME_TYPE_LENGTH];
259		err = appFileInfoRead.GetSignature(signature);
260		if (err == B_OK)
261			err = appFileInfoWrite.SetSignature(signature);
262		else if (err == B_ENTRY_NOT_FOUND || err == B_NAME_NOT_FOUND || err == B_BAD_VALUE) {
263			// BeOS returns B_BAD_VALUE on shared libraries
264			err = appFileInfoWrite.SetSignature(NULL);
265#if defined(__BEOS__) && !defined(__HAIKU__)
266			err = B_OK;
267#endif
268		}
269		if (err != B_OK)
270			return err;
271
272		// catalog entry
273		char catalogEntry[B_MIME_TYPE_LENGTH * 3];
274		err = appFileInfoRead.GetCatalogEntry(catalogEntry);
275		if (err == B_OK)
276			err = appFileInfoWrite.SetCatalogEntry(catalogEntry);
277		else if (err == B_ENTRY_NOT_FOUND)
278			err = appFileInfoWrite.SetCatalogEntry(NULL);
279		if (err != B_OK)
280			return err;
281
282		// app flags
283		uint32 appFlags;
284		err = appFileInfoRead.GetAppFlags(&appFlags);
285		if (err == B_OK) {
286			err = appFileInfoWrite.SetAppFlags(appFlags);
287		} else if (err == B_ENTRY_NOT_FOUND || err == B_NAME_NOT_FOUND || err == B_BAD_VALUE) {
288			file.RemoveAttr(kAppFlagsAttribute);
289			err = B_OK;
290		}
291		if (err != B_OK)
292			return err;
293
294		// supported types
295		BMessage supportedTypes;
296		bool hasSupportedTypes = false;
297		err = appFileInfoRead.GetSupportedTypes(&supportedTypes);
298		if (err == B_OK) {
299			err = appFileInfoWrite.SetSupportedTypes(&supportedTypes);
300			hasSupportedTypes = true;
301		} else if (err == B_ENTRY_NOT_FOUND || err == B_NAME_NOT_FOUND || err == B_BAD_VALUE) {
302#if defined(__BEOS__) && !defined(__HAIKU__)
303			file.RemoveAttr(kSupportedTypesAttr);
304			err = B_OK;
305#else
306			err = appFileInfoWrite.SetSupportedTypes(NULL);
307#endif
308		}
309		if (err != B_OK)
310			return err;
311
312		// vector icon
313		err = update_vector_icon(file, NULL);
314		if (err != B_OK)
315			return err;
316
317		// small icon
318		BBitmap smallIcon(BRect(0, 0, 15, 15), B_BITMAP_NO_SERVER_LINK,
319			B_CMAP8);
320		if (smallIcon.InitCheck() != B_OK)
321			return smallIcon.InitCheck();
322		err = update_icon(appFileInfoRead, appFileInfoWrite, NULL, smallIcon,
323			B_MINI_ICON);
324		if (err != B_OK)
325			return err;
326
327		// large icon
328		BBitmap largeIcon(BRect(0, 0, 31, 31), B_BITMAP_NO_SERVER_LINK,
329			B_CMAP8);
330		if (largeIcon.InitCheck() != B_OK)
331			return largeIcon.InitCheck();
332		err = update_icon(appFileInfoRead, appFileInfoWrite, NULL, largeIcon,
333			B_LARGE_ICON);
334		if (err != B_OK)
335			return err;
336
337		// version infos
338		const version_kind versionKinds[]
339			= {B_APP_VERSION_KIND, B_SYSTEM_VERSION_KIND};
340		for (int i = 0; i < 2; i++) {
341			version_kind kind = versionKinds[i];
342			version_info versionInfo;
343			err = appFileInfoRead.GetVersionInfo(&versionInfo, kind);
344			if (err == B_OK)
345				err = appFileInfoWrite.SetVersionInfo(&versionInfo, kind);
346			else if (err == B_ENTRY_NOT_FOUND || err == B_NAME_NOT_FOUND || err == B_BAD_VALUE) {
347#if !defined(HAIKU_HOST_PLATFORM_DANO) && !defined(HAIKU_HOST_PLATFORM_BEOS) && !defined(HAIKU_HOST_PLATFORM_BONE)
348				// BeOS crashes when calling SetVersionInfo() with a NULL pointer
349				err = appFileInfoWrite.SetVersionInfo(NULL, kind);
350#else
351				err = B_OK;
352#endif
353			}
354			if (err != B_OK)
355				return err;
356		}
357
358		// icons for supported types
359		if (hasSupportedTypes) {
360			const char *supportedType;
361			for (int32 i = 0;
362					supportedTypes.FindString("types", i, &supportedType) == B_OK;
363				 	i++) {
364				// vector icon
365				err = update_vector_icon(file, supportedType);
366				if (err != B_OK)
367					return err;
368
369				// small icon
370				err = update_icon(appFileInfoRead, appFileInfoWrite,
371					supportedType, smallIcon, B_MINI_ICON);
372				if (err != B_OK)
373					return err;
374
375				// large icon
376				err = update_icon(appFileInfoRead, appFileInfoWrite,
377					supportedType, largeIcon, B_LARGE_ICON);
378				if (err != B_OK)
379					return err;
380			}
381		}
382	}
383
384	return err;
385}
386
387}	// namespace Mime
388}	// namespace Storage
389}	// namespace BPrivate
390