xref: /haiku/src/kits/storage/NodeInfo.cpp (revision 13581b3d2a71545960b98fefebc5225b5bf29072)
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 
28 using namespace std;
29 
30 
31 // attribute names
32 #define NI_BEOS "BEOS"
33 static const char* kNITypeAttribute			= NI_BEOS ":TYPE";
34 static const char* kNIPreferredAppAttribute	= NI_BEOS ":PREF_APP";
35 static const char* kNIAppHintAttribute		= NI_BEOS ":PPATH";
36 static const char* kNIMiniIconAttribute		= NI_BEOS ":M:STD_ICON";
37 static const char* kNILargeIconAttribute	= NI_BEOS ":L:STD_ICON";
38 static const char* kNIIconAttribute			= NI_BEOS ":ICON";
39 
40 
41 //	#pragma mark - BNodeInfo
42 
43 
44 BNodeInfo::BNodeInfo()
45 	:
46 	fNode(NULL),
47 	fCStatus(B_NO_INIT)
48 {
49 }
50 
51 
52 BNodeInfo::BNodeInfo(BNode* node)
53 	:
54 	fNode(NULL),
55 	fCStatus(B_NO_INIT)
56 {
57 	fCStatus = SetTo(node);
58 }
59 
60 
61 BNodeInfo::~BNodeInfo()
62 {
63 }
64 
65 
66 status_t
67 BNodeInfo::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 
79 status_t
80 BNodeInfo::InitCheck() const
81 {
82 	return fCStatus;
83 }
84 
85 
86 status_t
87 BNodeInfo::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 
124 status_t
125 BNodeInfo::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 
153 status_t
154 BNodeInfo::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 
165 status_t
166 BNodeInfo::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 
239 status_t
240 BNodeInfo::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 
279 status_t
280 BNodeInfo::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 
305 status_t
306 BNodeInfo::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 
343 status_t
344 BNodeInfo::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 
372 status_t
373 BNodeInfo::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 
415 status_t
416 BNodeInfo::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 
443 status_t
444 BNodeInfo::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 
568 status_t
569 BNodeInfo::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 */
595 extern "C"
596 status_t
597 GetTrackerIcon__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 
606 void BNodeInfo::_ReservedNodeInfo1() {}
607 void BNodeInfo::_ReservedNodeInfo2() {}
608 void 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 */
615 BNodeInfo&
616 BNodeInfo::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 */
625 BNodeInfo::BNodeInfo(const BNodeInfo &)
626 {
627 }
628 #endif
629