1/*
2 * Copyright 2002-2010 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Ingo Weinhold, bonefish@users.sf.net
7 *		Axel D��rfler, axeld@pinc-software.de
8 */
9
10
11#include <NodeInfo.h>
12
13#include <new>
14#include <string.h>
15
16#include <MimeTypes.h>
17#include <Bitmap.h>
18#include <Entry.h>
19#include <IconUtils.h>
20#include <Node.h>
21#include <Path.h>
22#include <Rect.h>
23
24#include <fs_attr.h>
25#include <fs_info.h>
26
27
28using namespace std;
29
30
31// attribute names
32#define NI_BEOS "BEOS"
33static const char* kNITypeAttribute			= NI_BEOS ":TYPE";
34static const char* kNIPreferredAppAttribute	= NI_BEOS ":PREF_APP";
35static const char* kNIAppHintAttribute		= NI_BEOS ":PPATH";
36static const char* kNIMiniIconAttribute		= NI_BEOS ":M:STD_ICON";
37static const char* kNILargeIconAttribute	= NI_BEOS ":L:STD_ICON";
38static const char* kNIIconAttribute			= NI_BEOS ":ICON";
39
40
41//	#pragma mark - BNodeInfo
42
43
44BNodeInfo::BNodeInfo()
45	:
46	fNode(NULL),
47	fCStatus(B_NO_INIT)
48{
49}
50
51
52BNodeInfo::BNodeInfo(BNode* node)
53	:
54	fNode(NULL),
55	fCStatus(B_NO_INIT)
56{
57	fCStatus = SetTo(node);
58}
59
60
61BNodeInfo::~BNodeInfo()
62{
63}
64
65
66status_t
67BNodeInfo::SetTo(BNode* node)
68{
69	fNode = NULL;
70	// check parameter
71	fCStatus = (node && node->InitCheck() == B_OK ? B_OK : B_BAD_VALUE);
72	if (fCStatus == B_OK)
73		fNode = node;
74
75	return fCStatus;
76}
77
78
79status_t
80BNodeInfo::InitCheck() const
81{
82	return fCStatus;
83}
84
85
86status_t
87BNodeInfo::GetType(char* type) const
88{
89	// check parameter and initialization
90	status_t result = (type ? B_OK : B_BAD_VALUE);
91	if (result == B_OK && InitCheck() != B_OK)
92		result = B_NO_INIT;
93
94	// get the attribute info and check type and length of the attr contents
95	attr_info attrInfo;
96	if (result == B_OK)
97		result = fNode->GetAttrInfo(kNITypeAttribute, &attrInfo);
98
99	if (result == B_OK && attrInfo.type != B_MIME_STRING_TYPE)
100		result = B_BAD_TYPE;
101
102	if (result == B_OK && attrInfo.size > B_MIME_TYPE_LENGTH)
103		result = B_BAD_DATA;
104
105	// read the data
106	if (result == B_OK) {
107		ssize_t read = fNode->ReadAttr(kNITypeAttribute, attrInfo.type, 0,
108									   type, attrInfo.size);
109		if (read < 0)
110			result = read;
111		else if (read != attrInfo.size)
112			result = B_ERROR;
113
114		if (result == B_OK) {
115			// attribute strings doesn't have to be null terminated
116			type[min_c(attrInfo.size, B_MIME_TYPE_LENGTH - 1)] = '\0';
117		}
118	}
119
120	return result;
121}
122
123
124status_t
125BNodeInfo::SetType(const char* type)
126{
127	// check parameter and initialization
128	status_t result = B_OK;
129	if (result == B_OK && type && strlen(type) >= B_MIME_TYPE_LENGTH)
130		result = B_BAD_VALUE;
131
132	if (result == B_OK && InitCheck() != B_OK)
133		result = B_NO_INIT;
134
135	// write/remove the attribute
136	if (result == B_OK) {
137		if (type != NULL) {
138			size_t toWrite = strlen(type) + 1;
139			ssize_t written = fNode->WriteAttr(kNITypeAttribute,
140				B_MIME_STRING_TYPE, 0, type, toWrite);
141			if (written < 0)
142				result = written;
143			else if (written != (ssize_t)toWrite)
144				result = B_ERROR;
145		} else
146			result = fNode->RemoveAttr(kNITypeAttribute);
147	}
148
149	return result;
150}
151
152
153status_t
154BNodeInfo::GetIcon(BBitmap* icon, icon_size which) const
155{
156	const char* iconAttribute = kNIIconAttribute;
157	const char* miniIconAttribute = kNIMiniIconAttribute;
158	const char* largeIconAttribute = kNILargeIconAttribute;
159
160	return BIconUtils::GetIcon(fNode, iconAttribute, miniIconAttribute,
161		largeIconAttribute, which, icon);
162}
163
164
165status_t
166BNodeInfo::SetIcon(const BBitmap* icon, icon_size which)
167{
168	status_t result = B_OK;
169
170	// set some icon size related variables
171	const char* attribute = NULL;
172	BRect bounds;
173	uint32 attrType = 0;
174	size_t attrSize = 0;
175
176	switch (which) {
177		case B_MINI_ICON:
178			attribute = kNIMiniIconAttribute;
179			bounds.Set(0, 0, 15, 15);
180			attrType = B_MINI_ICON_TYPE;
181			attrSize = 16 * 16;
182			break;
183
184		case B_LARGE_ICON:
185			attribute = kNILargeIconAttribute;
186			bounds.Set(0, 0, 31, 31);
187			attrType = B_LARGE_ICON_TYPE;
188			attrSize = 32 * 32;
189			break;
190
191		default:
192			result = B_BAD_VALUE;
193			break;
194	}
195
196	// check parameter and initialization
197	if (result == B_OK && icon != NULL
198		&& (icon->InitCheck() != B_OK || icon->Bounds() != bounds)) {
199		result = B_BAD_VALUE;
200	}
201	if (result == B_OK && InitCheck() != B_OK)
202		result = B_NO_INIT;
203
204	// write/remove the attribute
205	if (result == B_OK) {
206		if (icon != NULL) {
207			bool otherColorSpace = (icon->ColorSpace() != B_CMAP8);
208			ssize_t written = 0;
209			if (otherColorSpace) {
210				BBitmap bitmap(bounds, B_BITMAP_NO_SERVER_LINK, B_CMAP8);
211				result = bitmap.InitCheck();
212				if (result == B_OK)
213					result = bitmap.ImportBits(icon);
214
215				if (result == B_OK) {
216					written = fNode->WriteAttr(attribute, attrType, 0,
217						bitmap.Bits(), attrSize);
218				}
219			} else {
220				written = fNode->WriteAttr(attribute, attrType, 0,
221					icon->Bits(), attrSize);
222			}
223			if (result == B_OK) {
224				if (written < 0)
225					result = written;
226				else if (written != (ssize_t)attrSize)
227					result = B_ERROR;
228			}
229		} else {
230			// no icon given => remove
231			result = fNode->RemoveAttr(attribute);
232		}
233	}
234
235	return result;
236}
237
238
239status_t
240BNodeInfo::GetIcon(uint8** data, size_t* size, type_code* type) const
241{
242	// check params
243	if (data == NULL || size == NULL || type == NULL)
244		return B_BAD_VALUE;
245
246	// check initialization
247	if (InitCheck() != B_OK)
248		return B_NO_INIT;
249
250	// get the attribute info and check type and size of the attr contents
251	attr_info attrInfo;
252	status_t result = fNode->GetAttrInfo(kNIIconAttribute, &attrInfo);
253	if (result != B_OK)
254		return result;
255
256	// chicken out on unrealisticly large attributes
257	if (attrInfo.size > 128 * 1024)
258		return B_ERROR;
259
260	// fill the params
261	*type = attrInfo.type;
262	*size = attrInfo.size;
263	*data = new (nothrow) uint8[*size];
264	if (*data == NULL)
265		return B_NO_MEMORY;
266
267	// featch the data
268	ssize_t read = fNode->ReadAttr(kNIIconAttribute, *type, 0, *data, *size);
269	if (read != attrInfo.size) {
270		delete[] *data;
271		*data = NULL;
272		return B_ERROR;
273	}
274
275	return B_OK;
276}
277
278
279status_t
280BNodeInfo::SetIcon(const uint8* data, size_t size)
281{
282	// check initialization
283	if (InitCheck() != B_OK)
284		return B_NO_INIT;
285
286	status_t result = B_OK;
287
288	// write/remove the attribute
289	if (data && size > 0) {
290		ssize_t written = fNode->WriteAttr(kNIIconAttribute,
291			B_VECTOR_ICON_TYPE, 0, data, size);
292		if (written < 0)
293			result = (status_t)written;
294		else if (written != (ssize_t)size)
295			result = B_ERROR;
296	} else {
297		// no icon given => remove
298		result = fNode->RemoveAttr(kNIIconAttribute);
299	}
300
301	return result;
302}
303
304
305status_t
306BNodeInfo::GetPreferredApp(char* signature, app_verb verb) const
307{
308	// check parameter and initialization
309	status_t result = (signature && verb == B_OPEN ? B_OK : B_BAD_VALUE);
310	if (result == B_OK && InitCheck() != B_OK)
311		result = B_NO_INIT;
312
313	// get the attribute info and check type and length of the attr contents
314	attr_info attrInfo;
315	if (result == B_OK)
316		result = fNode->GetAttrInfo(kNIPreferredAppAttribute, &attrInfo);
317
318	if (result == B_OK && attrInfo.type != B_MIME_STRING_TYPE)
319		result = B_BAD_TYPE;
320
321	if (result == B_OK && attrInfo.size > B_MIME_TYPE_LENGTH)
322		result = B_BAD_DATA;
323
324	// read the data
325	if (result == B_OK) {
326		ssize_t read = fNode->ReadAttr(kNIPreferredAppAttribute, attrInfo.type,
327			0, signature, attrInfo.size);
328		if (read < 0)
329			result = read;
330		else if (read != attrInfo.size)
331			result = B_ERROR;
332
333		if (result == B_OK) {
334			// attribute strings doesn't have to be null terminated
335			signature[min_c(attrInfo.size, B_MIME_TYPE_LENGTH - 1)] = '\0';
336		}
337	}
338
339	return result;
340}
341
342
343status_t
344BNodeInfo::SetPreferredApp(const char* signature, app_verb verb)
345{
346	// check parameters and initialization
347	status_t result = (verb == B_OPEN ? B_OK : B_BAD_VALUE);
348	if (result == B_OK && signature && strlen(signature) >= B_MIME_TYPE_LENGTH)
349		result = B_BAD_VALUE;
350
351	if (result == B_OK && InitCheck() != B_OK)
352		result = B_NO_INIT;
353
354	// write/remove the attribute
355	if (result == B_OK) {
356		if (signature) {
357			size_t toWrite = strlen(signature) + 1;
358			ssize_t written = fNode->WriteAttr(kNIPreferredAppAttribute,
359				B_MIME_STRING_TYPE, 0, signature, toWrite);
360			if (written < 0)
361				result = written;
362			else if (written != (ssize_t)toWrite)
363				result = B_ERROR;
364		} else
365			result = fNode->RemoveAttr(kNIPreferredAppAttribute);
366	}
367
368	return result;
369}
370
371
372status_t
373BNodeInfo::GetAppHint(entry_ref* ref) const
374{
375	// check parameter and initialization
376	status_t result = (ref ? B_OK : B_BAD_VALUE);
377	if (result == B_OK && InitCheck() != B_OK)
378		result = B_NO_INIT;
379
380	// get the attribute info and check type and length of the attr contents
381	attr_info attrInfo;
382	if (result == B_OK)
383		result = fNode->GetAttrInfo(kNIAppHintAttribute, &attrInfo);
384
385	// NOTE: The attribute type should be B_STRING_TYPE, but R5 uses
386	// B_MIME_STRING_TYPE.
387	if (result == B_OK && attrInfo.type != B_MIME_STRING_TYPE)
388		result = B_BAD_TYPE;
389
390	if (result == B_OK && attrInfo.size > B_PATH_NAME_LENGTH)
391		result = B_BAD_DATA;
392
393	// read the data
394	if (result == B_OK) {
395		char path[B_PATH_NAME_LENGTH];
396		ssize_t read = fNode->ReadAttr(kNIAppHintAttribute, attrInfo.type, 0,
397			path, attrInfo.size);
398		if (read < 0)
399			result = read;
400		else if (read != attrInfo.size)
401			result = B_ERROR;
402
403		// get the entry_ref for the path
404		if (result == B_OK) {
405			// attribute strings doesn't have to be null terminated
406			path[min_c(attrInfo.size, B_PATH_NAME_LENGTH - 1)] = '\0';
407			result = get_ref_for_path(path, ref);
408		}
409	}
410
411	return result;
412}
413
414
415status_t
416BNodeInfo::SetAppHint(const entry_ref* ref)
417{
418	// check parameter and initialization
419	if (InitCheck() != B_OK)
420		return B_NO_INIT;
421
422	status_t result = B_OK;
423	if (ref != NULL) {
424		// write/remove the attribute
425		BPath path;
426		result = path.SetTo(ref);
427		if (result == B_OK) {
428			size_t toWrite = strlen(path.Path()) + 1;
429			ssize_t written = fNode->WriteAttr(kNIAppHintAttribute,
430				B_MIME_STRING_TYPE, 0, path.Path(), toWrite);
431			if (written < 0)
432				result = written;
433			else if (written != (ssize_t)toWrite)
434				result = B_ERROR;
435		}
436	} else
437		result = fNode->RemoveAttr(kNIAppHintAttribute);
438
439	return result;
440}
441
442
443status_t
444BNodeInfo::GetTrackerIcon(BBitmap* icon, icon_size which) const
445{
446	if (icon == NULL)
447		return B_BAD_VALUE;
448
449	// set some icon size related variables
450	BRect bounds;
451	switch (which) {
452		case B_MINI_ICON:
453			bounds.Set(0, 0, 15, 15);
454			break;
455
456		case B_LARGE_ICON:
457			bounds.Set(0, 0, 31, 31);
458			break;
459
460		default:
461//			result = B_BAD_VALUE;
462			// NOTE: added to be less strict and support scaled icons
463			bounds = icon->Bounds();
464			break;
465	}
466
467	// check parameters and initialization
468	if (icon->InitCheck() != B_OK || icon->Bounds() != bounds)
469		return B_BAD_VALUE;
470
471	if (InitCheck() != B_OK)
472		return B_NO_INIT;
473
474	// Ask GetIcon() first.
475	if (GetIcon(icon, which) == B_OK)
476		return B_OK;
477
478	// If not successful, see if the node has a type available at all.
479	// If no type is available, use one of the standard types.
480	status_t result = B_OK;
481	char mimeString[B_MIME_TYPE_LENGTH];
482	if (GetType(mimeString) != B_OK) {
483		// Get the icon from a mime type...
484		BMimeType type;
485
486		struct stat stat;
487		result = fNode->GetStat(&stat);
488		if (result == B_OK) {
489			// no type available -- get the icon for the appropriate type
490			// (file/dir/etc.)
491			if (S_ISREG(stat.st_mode)) {
492				// is it an application (executable) or just a regular file?
493				if ((stat.st_mode & S_IXUSR) != 0)
494					type.SetTo(B_APP_MIME_TYPE);
495				else
496					type.SetTo(B_FILE_MIME_TYPE);
497			} else if (S_ISDIR(stat.st_mode)) {
498				// it's either a volume or just a standard directory
499				fs_info info;
500				if (fs_stat_dev(stat.st_dev, &info) == 0
501					&& stat.st_ino == info.root) {
502					type.SetTo(B_VOLUME_MIME_TYPE);
503				} else
504					type.SetTo(B_DIRECTORY_MIME_TYPE);
505			} else if (S_ISLNK(stat.st_mode))
506				type.SetTo(B_SYMLINK_MIME_TYPE);
507		} else {
508			// GetStat() failed. Return the icon for
509			// "application/octet-stream" from the MIME database.
510			type.SetTo(B_FILE_MIME_TYPE);
511		}
512
513		return type.GetIcon(icon, which);
514	} else {
515		// We know the mimetype of the node.
516		bool success = false;
517
518		// Get the preferred application and ask the MIME database, if that
519		// application has a special icon for the node's file type.
520		char signature[B_MIME_TYPE_LENGTH];
521		if (GetPreferredApp(signature) == B_OK) {
522			BMimeType type(signature);
523			success = type.GetIconForType(mimeString, icon, which) == B_OK;
524		}
525
526		// ToDo: Confirm Tracker asks preferred app icons before asking
527		// mime icons.
528
529		BMimeType nodeType(mimeString);
530
531		// Ask the MIME database for the preferred application for the node's
532		// file type and whether this application has a special icon for the
533		// type.
534		if (!success && nodeType.GetPreferredApp(signature) == B_OK) {
535			BMimeType type(signature);
536			success = type.GetIconForType(mimeString, icon, which) == B_OK;
537		}
538
539		// Ask the MIME database whether there is an icon for the node's file
540		// type.
541		if (!success)
542			success = nodeType.GetIcon(icon, which) == B_OK;
543
544		// Get the super type if still no success.
545		BMimeType superType;
546		if (!success && nodeType.GetSupertype(&superType) == B_OK) {
547			// Ask the MIME database for the preferred application for the
548			// node's super type and whether this application has a special
549			// icon for the type.
550			if (superType.GetPreferredApp(signature) == B_OK) {
551				BMimeType type(signature);
552				success = type.GetIconForType(superType.Type(), icon,
553					which) == B_OK;
554			}
555			// Get the icon of the super type itself.
556			if (!success)
557				success = superType.GetIcon(icon, which) == B_OK;
558		}
559
560		if (success)
561			return B_OK;
562	}
563
564	return B_ERROR;
565}
566
567
568status_t
569BNodeInfo::GetTrackerIcon(const entry_ref* ref, BBitmap* icon, icon_size which)
570{
571	// check ref param
572	status_t result = (ref ? B_OK : B_BAD_VALUE);
573
574	// init a BNode
575	BNode node;
576	if (result == B_OK)
577		result = node.SetTo(ref);
578
579	// init a BNodeInfo
580	BNodeInfo nodeInfo;
581	if (result == B_OK)
582		result = nodeInfo.SetTo(&node);
583
584	// let the non-static GetTrackerIcon() do the dirty work
585	if (result == B_OK)
586		result = nodeInfo.GetTrackerIcon(icon, which);
587
588	return result;
589}
590
591
592/*!	This method is provided for binary compatibility purposes
593	(for example the program "Guido" depends on it.)
594*/
595extern "C"
596status_t
597GetTrackerIcon__9BNodeInfoP9entry_refP7BBitmap9icon_size(
598	BNodeInfo* nodeInfo, entry_ref* ref,
599	BBitmap* bitmap, icon_size iconSize)
600{
601	// NOTE: nodeInfo is ignored - maybe that's wrong!
602	return BNodeInfo::GetTrackerIcon(ref, bitmap, iconSize);
603}
604
605
606void BNodeInfo::_ReservedNodeInfo1() {}
607void BNodeInfo::_ReservedNodeInfo2() {}
608void BNodeInfo::_ReservedNodeInfo3() {}
609
610
611#ifdef _BEOS_R5_COMPATIBLE_
612/*!	Assignment operator is declared private to prevent it from being created
613	automatically by the compiler.
614*/
615BNodeInfo&
616BNodeInfo::operator=(const BNodeInfo &nodeInfo)
617{
618	return *this;
619}
620
621
622/*!	Copy constructor is declared private to prevent it from being created
623	automatically by the compiler.
624*/
625BNodeInfo::BNodeInfo(const BNodeInfo &)
626{
627}
628#endif
629