xref: /haiku/src/kits/storage/mime/DatabaseLocation.cpp (revision 5e96d7d537fbec23bad4ae9b4c8e7b02e769f0c6)
1 /*
2  * Copyright 2002-2014, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Tyler Dauwalder
7  *		Rene Gollent, rene@gollent.com.
8  *		Ingo Weinhold <ingo_weinhold@gmx.de>
9  */
10 
11 
12 #include <mime/DatabaseLocation.h>
13 
14 #include <stdlib.h>
15 #include <syslog.h>
16 
17 #include <new>
18 
19 #include <Bitmap.h>
20 #include <DataIO.h>
21 #include <Directory.h>
22 #include <File.h>
23 #include <fs_attr.h>
24 #include <IconUtils.h>
25 #include <Message.h>
26 #include <Node.h>
27 
28 #include <AutoDeleter.h>
29 #include <mime/database_support.h>
30 
31 
32 namespace BPrivate {
33 namespace Storage {
34 namespace Mime {
35 
36 
37 DatabaseLocation::DatabaseLocation()
38 	:
39 	fDirectories()
40 {
41 }
42 
43 
44 DatabaseLocation::~DatabaseLocation()
45 {
46 }
47 
48 
49 bool
50 DatabaseLocation::AddDirectory(const BString& directory)
51 {
52 	return !directory.IsEmpty() && fDirectories.Add(directory);
53 }
54 
55 
56 /*! \brief Opens a BNode on the given type, failing if the type has no
57 		corresponding file in the database.
58 	\param type The MIME type to open.
59 	\param _node Node opened on the given MIME type.
60 */
61 status_t
62 DatabaseLocation::OpenType(const char* type, BNode& _node) const
63 {
64 	if (type == NULL)
65 		return B_BAD_VALUE;
66 
67 	int32 index;
68 	return _OpenType(type, _node, index);
69 }
70 
71 
72 /*! \brief Opens a BNode on the given type, creating a node of the
73 		appropriate flavor if requested (and necessary).
74 	All MIME types are converted to lowercase for use in the filesystem.
75 	\param type The MIME type to open.
76 	\param _node Node opened on the given MIME type.
77 	\param _didCreate If not \c NULL, the variable the pointer refers to is
78 		set to \c true, if the node has been newly created, to \c false
79 		otherwise.
80 */
81 status_t
82 DatabaseLocation::OpenWritableType(const char* type, BNode& _node, bool create,
83 	bool* _didCreate) const
84 {
85 	if (_didCreate)
86 		*_didCreate = false;
87 
88 	// See, if the type already exists.
89 	int32 index;
90 	status_t error = _OpenType(type, _node, index);
91 	if (error == B_OK) {
92 		if (index == 0)
93 			return B_OK;
94 		else if (!create)
95 			return B_ENTRY_NOT_FOUND;
96 
97 		// The caller wants a editable node, but the node found is not in the
98 		// user's settings directory. Copy the node.
99 		BNode nodeToClone(_node);
100 		if (nodeToClone.InitCheck() != B_OK)
101 			return nodeToClone.InitCheck();
102 
103 		error = _CopyTypeNode(nodeToClone, type, _node);
104 		if (error != B_OK) {
105 			_node.Unset();
106 			return error;
107 		}
108 
109 		if (_didCreate != NULL)
110 			*_didCreate = true;
111 		return error;
112 	} else if (!create)
113 		return B_ENTRY_NOT_FOUND;
114 
115 	// type doesn't exist yet -- create the respective node
116 	error = _CreateTypeNode(type, _node);
117 	if (error != B_OK)
118 		return error;
119 
120 	// write the type attribute
121 	size_t toWrite = strlen(type) + 1;
122 	ssize_t bytesWritten = _node.WriteAttr(kTypeAttr, B_STRING_TYPE, 0, type,
123 		toWrite);
124 	if (bytesWritten < 0)
125 		error = bytesWritten;
126 	else if ((size_t)bytesWritten != toWrite)
127 		error = B_FILE_ERROR;
128 
129 	if (error != B_OK) {
130 		_node.Unset();
131 		return error;
132 	}
133 
134 	if (_didCreate != NULL)
135 		*_didCreate = true;
136 	return B_OK;
137 }
138 
139 
140 /*! \brief Reads up to \c len bytes of the given data from the given attribute
141 	       for the given MIME type.
142 
143 	If no entry for the given type exists in the database, the function fails,
144 	and the contents of \c data are undefined.
145 
146 	\param type The MIME type
147 	\param attribute The attribute name
148 	\param data Pointer to a memory buffer into which the data should be copied
149 	\param length The maximum number of bytes to read
150 	\param datatype The expected data type
151 	\return If successful, the number of bytes read is returned, otherwise, an
152 	        error code is returned.
153 */
154 ssize_t
155 DatabaseLocation::ReadAttribute(const char* type, const char* attribute,
156 	void* data, size_t length, type_code datatype) const
157 {
158 	if (type == NULL || attribute == NULL || data == NULL)
159 		return B_BAD_VALUE;
160 
161 	BNode node;
162 	status_t error = OpenType(type, node);
163 	if (error != B_OK)
164 		return error;
165 
166 	return node.ReadAttr(attribute, datatype, 0, data, length);
167 }
168 
169 
170 /*! \brief Reads a flattened BMessage from the given attribute of the given
171 	MIME type.
172 
173 	If no entry for the given type exists in the database, or if the data
174 	stored in the attribute is not a flattened BMessage, the function fails
175 	and the contents of \c msg are undefined.
176 
177 	\param type The MIME type
178 	\param attribute The attribute name
179 	\param data Reference to a pre-allocated BMessage into which the attribute
180 			    data is unflattened.
181 */
182 status_t
183 DatabaseLocation::ReadMessageAttribute(const char* type, const char* attribute,
184 	BMessage& _message) const
185 {
186 	if (type == NULL || attribute == NULL)
187 		return B_BAD_VALUE;
188 
189 	BNode node;
190 	attr_info info;
191 
192 	status_t error = OpenType(type, node);
193 	if (error != B_OK)
194 		return error;
195 
196 	error = node.GetAttrInfo(attribute, &info);
197 	if (error != B_OK)
198 		return error;
199 
200 	if (info.type != B_MESSAGE_TYPE)
201 		return B_BAD_VALUE;
202 
203 	void* buffer = malloc(info.size);
204 	if (buffer == NULL)
205 		return B_NO_MEMORY;
206 	MemoryDeleter bufferDeleter(buffer);
207 
208 	ssize_t bytesRead = node.ReadAttr(attribute, B_MESSAGE_TYPE, 0, buffer,
209 		info.size);
210 	if (bytesRead != info.size)
211 		return bytesRead < 0 ? (status_t)bytesRead : (status_t)B_FILE_ERROR;
212 
213 	return _message.Unflatten((const char*)buffer);
214 }
215 
216 
217 /*! \brief Reads a BString from the given attribute of the given
218 	MIME type.
219 
220 	If no entry for the given type exists in the database, the function fails
221 	and the contents of \c str are undefined.
222 
223 	\param type The MIME type
224 	\param attribute The attribute name
225 	\param _string Reference to a pre-allocated BString into which the attribute
226 			   data stored.
227 */
228 status_t
229 DatabaseLocation::ReadStringAttribute(const char* type, const char* attribute,
230 	BString& _string) const
231 {
232 	if (type == NULL || attribute == NULL)
233 		return B_BAD_VALUE;
234 
235 	BNode node;
236 	status_t error = OpenType(type, node);
237 	if (error != B_OK)
238 		return error;
239 
240 	return node.ReadAttrString(attribute, &_string);
241 }
242 
243 
244 /*! \brief Writes \c len bytes of the given data to the given attribute
245 	       for the given MIME type.
246 
247 	If no entry for the given type exists in the database, it is created.
248 
249 	\param type The MIME type
250 	\param attribute The attribute name
251 	\param data Pointer to the data to write
252 	\param length The number of bytes to write
253 	\param datatype The data type of the given data
254 */
255 status_t
256 DatabaseLocation::WriteAttribute(const char* type, const char* attribute,
257 	const void* data, size_t length, type_code datatype, bool* _didCreate) const
258 {
259 	if (type == NULL || attribute == NULL || data == NULL)
260 		return B_BAD_VALUE;
261 
262 	BNode node;
263 	status_t error = OpenWritableType(type, node, true, _didCreate);
264 	if (error != B_OK)
265 		return error;
266 
267 	ssize_t bytesWritten = node.WriteAttr(attribute, datatype, 0, data, length);
268 	if (bytesWritten < 0)
269 		return bytesWritten;
270 	return bytesWritten == (ssize_t)length
271 		? (status_t)B_OK : (status_t)B_FILE_ERROR;
272 }
273 
274 
275 /*! \brief Flattens the given \c BMessage and writes it to the given attribute
276 	       of the given MIME type.
277 
278 	If no entry for the given type exists in the database, it is created.
279 
280 	\param type The MIME type
281 	\param attribute The attribute name
282 	\param message The BMessage to flatten and write
283 */
284 status_t
285 DatabaseLocation::WriteMessageAttribute(const char* type, const char* attribute,
286 	const BMessage& message, bool* _didCreate) const
287 {
288 	BMallocIO data;
289 	status_t error = data.SetSize(message.FlattenedSize());
290 	if (error != B_OK)
291 		return error;
292 
293 	ssize_t bytes;
294 	error = message.Flatten(&data, &bytes);
295 	if (error != B_OK)
296 		return error;
297 
298 	return WriteAttribute(type, attribute, data.Buffer(), data.BufferLength(),
299 		B_MESSAGE_TYPE, _didCreate);
300 }
301 
302 
303 //! Deletes the given attribute for the given type
304 /*!
305 	\param type The mime type
306 	\param attribute The attribute name
307 	\return
308     - B_OK: success
309     - B_ENTRY_NOT_FOUND: no such type or attribute
310     - "error code": failure
311 */
312 status_t
313 DatabaseLocation::DeleteAttribute(const char* type, const char* attribute) const
314 {
315 	if (type == NULL || attribute == NULL)
316 		return B_BAD_VALUE;
317 
318 	BNode node;
319 	status_t error = OpenWritableType(type, node, false);
320 	if (error != B_OK)
321 		return error;
322 
323 	return node.RemoveAttr(attribute);
324 }
325 
326 
327 /*! \brief Fetches the application hint for the given MIME type.
328 
329 	The entry_ref pointed to by \c ref must be pre-allocated.
330 
331 	\param type The MIME type of interest
332 	\param _ref Reference to a pre-allocated \c entry_ref struct into
333 	           which the location of the hint application is copied.
334 
335 	\return
336 	- \c B_OK: Success
337 	- \c B_ENTRY_NOT_FOUND: No app hint exists for the given type
338 	- "error code": Failure
339 */
340 status_t
341 DatabaseLocation::GetAppHint(const char* type, entry_ref& _ref)
342 {
343 	if (type == NULL)
344 		return B_BAD_VALUE;
345 
346 	char path[B_PATH_NAME_LENGTH];
347 	BEntry entry;
348 	ssize_t status = ReadAttribute(type, kAppHintAttr, path, B_PATH_NAME_LENGTH,
349 		kAppHintType);
350 
351 	if (status >= B_OK)
352 		status = entry.SetTo(path);
353 	if (status == B_OK)
354 		status = entry.GetRef(&_ref);
355 
356 	return status;
357 }
358 
359 
360 /*! \brief Fetches from the MIME database a BMessage describing the attributes
361 	typically associated with files of the given MIME type
362 
363 	The attribute information is returned in a pre-allocated BMessage pointed to
364 	by the \c info parameter (note that the any prior contents of the message
365 	will be destroyed). Please see BMimeType::SetAttrInfo() for a description
366 	of the expected format of such a message.
367 
368 	\param _info Reference to a pre-allocated BMessage into which information
369 		about the MIME type's associated file attributes is stored.
370 	\return
371 	- \c B_OK: Success
372 	- "error code": Failure
373 */
374 status_t
375 DatabaseLocation::GetAttributesInfo(const char* type, BMessage& _info)
376 {
377 	status_t err = ReadMessageAttribute(type, kAttrInfoAttr, _info);
378 	if (err == B_ENTRY_NOT_FOUND) {
379 		// return an empty message
380 		_info.MakeEmpty();
381 		err = B_OK;
382 	}
383 	if (err == B_OK) {
384 		_info.what = 233;
385 			// Don't know why, but that's what R5 does.
386 		err = _info.AddString("type", type);
387 	}
388 	return err;
389 }
390 
391 
392 /*!	\brief Fetches the short description for the given MIME type.
393 
394 	The string pointed to by \c description must be long enough to
395 	hold the short description; a length of \c B_MIME_TYPE_LENGTH is
396 	recommended.
397 
398 	\param type The MIME type of interest
399 	\param description Pointer to a pre-allocated string into which the short
400 		description is copied. If the function fails, the contents of the string
401 		are undefined.
402 
403 	\return
404 	- \c B_OK: Success
405 	- \c B_ENTRY_NOT_FOUND: No short description exists for the given type
406 	- "error code": Failure
407 */
408 status_t
409 DatabaseLocation::GetShortDescription(const char* type, char* description)
410 {
411 	ssize_t err = ReadAttribute(type, kShortDescriptionAttr, description,
412 		B_MIME_TYPE_LENGTH, kShortDescriptionType);
413 	return err >= 0 ? B_OK : err ;
414 }
415 
416 
417 //!	Fetches the long description for the given MIME type.
418 /*!	The string pointed to by \c description must be long enough to
419 	hold the long description; a length of \c B_MIME_TYPE_LENGTH is
420 	recommended.
421 
422 	\param type The MIME type of interest
423 	\param description Pointer to a pre-allocated string into which the long
424 		description is copied. If the function fails, the contents of the string
425 		are undefined.
426 
427 	\return
428 	- \c B_OK: Success
429 	- \c B_ENTRY_NOT_FOUND: No long description exists for the given type
430 	- "error code": Failure
431 */
432 status_t
433 DatabaseLocation::GetLongDescription(const char* type, char* description)
434 {
435 	ssize_t err = ReadAttribute(type, kLongDescriptionAttr, description,
436 		B_MIME_TYPE_LENGTH, kLongDescriptionType);
437 	return err >= 0 ? B_OK : err ;
438 }
439 
440 
441 /*!	\brief Fetches a BMessage describing the MIME type's associated filename
442 	extensions
443 
444 	The list of extensions is returned in a pre-allocated BMessage pointed to
445 	by the \c extensions parameter (note that the any prior contents of the
446 	message will be destroyed). Please see BMimeType::GetFileExtensions() for
447 	a description of the message format.
448 
449 	\param extensions Reference to a pre-allocated BMessage into which the MIME
450 	                  type's associated file extensions will be stored.
451 	\return
452 	- \c B_OK: Success
453 	- "error code": Failure
454 */
455 status_t
456 DatabaseLocation::GetFileExtensions(const char* type, BMessage& _extensions)
457 {
458 	status_t err = ReadMessageAttribute(type, kFileExtensionsAttr, _extensions);
459 	if (err == B_ENTRY_NOT_FOUND) {
460 		// return an empty message
461 		_extensions.MakeEmpty();
462 		err = B_OK;
463 	}
464 	if (err == B_OK) {
465 		_extensions.what = 234;	// Don't know why, but that's what R5 does.
466 		err = _extensions.AddString("type", type);
467 	}
468 	return err;
469 }
470 
471 
472 /*!	\brief Fetches the icon of given size associated with the given MIME type
473 
474 	The bitmap pointed to by \c icon must be of the proper size (\c 32x32
475 	for \c B_LARGE_ICON, \c 16x16 for \c B_MINI_ICON) and color depth
476 	(\c B_CMAP8).
477 
478 	\param type The mime type
479 	\param icon Reference to a pre-allocated bitmap of proper dimensions and
480 		color depth
481 	\param size The size icon you're interested in (\c B_LARGE_ICON or
482 		\c B_MINI_ICON)
483 */
484 status_t
485 DatabaseLocation::GetIcon(const char* type, BBitmap& _icon, icon_size size)
486 {
487 	return GetIconForType(type, NULL, _icon, size);
488 }
489 
490 
491 //! Fetches the vector icon associated with the given MIME type
492 /**	\param type The mime type
493 	\param _data Reference via which the allocated icon data is returned. You
494 		need to free the buffer once you're done with it.
495 	\param _size Reference via which the size of the icon data is returned.
496 */
497 status_t
498 DatabaseLocation::GetIcon(const char* type, uint8*& _data, size_t& _size)
499 {
500 	return GetIconForType(type, NULL, _data, _size);
501 }
502 
503 
504 /*! \brief Fetches the large or mini icon used by an application of this type
505 		for files of the given type.
506 
507 	The type of the \c BMimeType object is not required to actually be a subtype
508 	of \c "application/"; that is the intended use however, and calling
509 	\c GetIconForType() on a non-application type will likely return
510 	\c B_ENTRY_NOT_FOUND.
511 
512 	The icon is copied into the \c BBitmap pointed to by \c icon. The bitmap
513 	must be the proper size: \c 32x32 for the large icon, \c 16x16 for the mini
514 	icon.
515 
516 	\param type The MIME type
517 	\param fileType Pointer to a pre-allocated string containing the MIME type
518 		whose custom icon you wish to fetch. If NULL, works just like
519 		GetIcon().
520 	\param icon Reference to a pre-allocated \c BBitmap of proper size and
521 		colorspace into which the icon is copied.
522 	\param icon_size Value that specifies which icon to return. Currently
523 		\c B_LARGE_ICON and \c B_MINI_ICON are supported.
524 	\return
525 	- \c B_OK: Success
526 	- \c B_ENTRY_NOT_FOUND: No icon of the given size exists for the given type
527 	- "error code": Failure
528 
529 */
530 status_t
531 DatabaseLocation::GetIconForType(const char* type, const char* fileType,
532 	BBitmap& _icon, icon_size which)
533 {
534 	if (type == NULL)
535 		return B_BAD_VALUE;
536 
537 	// open the node for the given type
538 	BNode node;
539 	ssize_t err = OpenType(type, node);
540 	if (err < B_OK)
541 		return (status_t)err;
542 
543 	// construct our attribute name
544 	BString vectorIconAttrName;
545 	BString smallIconAttrName;
546 	BString largeIconAttrName;
547 
548 	if (fileType) {
549 		BString lowerCaseFileType(fileType);
550 		lowerCaseFileType.ToLower();
551 
552 		vectorIconAttrName << kIconAttrPrefix << lowerCaseFileType;
553 		smallIconAttrName << kMiniIconAttrPrefix << lowerCaseFileType;
554 		largeIconAttrName << kLargeIconAttrPrefix << lowerCaseFileType;
555 	} else {
556 		vectorIconAttrName = kIconAttr;
557 		smallIconAttrName = kMiniIconAttr;
558 		largeIconAttrName = kLargeIconAttr;
559 	}
560 
561 	return BIconUtils::GetIcon(&node, vectorIconAttrName, smallIconAttrName,
562 		largeIconAttrName, which, &_icon);
563 
564 //	ssize_t err = type && icon ? B_OK : B_BAD_VALUE;
565 //
566 //	// Figure out what kind of data we *should* find
567 //	uint32 attrType = 0;
568 //	ssize_t attrSize = 0;
569 //	BRect bounds;
570 //
571 //	if (!err) {
572 //		switch (which) {
573 //			case B_MINI_ICON:
574 //				bounds.Set(0, 0, 15, 15);
575 //				attrType = kMiniIconType;
576 //				attrSize = 16 * 16;
577 //				break;
578 //			case B_LARGE_ICON:
579 //				bounds.Set(0, 0, 31, 31);
580 //				attrType = kLargeIconType;
581 //				attrSize = 32 * 32;
582 //				break;
583 //			default:
584 //				err = B_BAD_VALUE;
585 //				break;
586 //		}
587 //	}
588 //	// Construct our attribute name
589 //	std::string attr;
590 //	if (fileType) {
591 //		attr = (which == B_MINI_ICON
592 //	              ? kMiniIconAttrPrefix
593 //	                : kLargeIconAttrPrefix)
594 //	                  + BPrivate::Storage::to_lower(fileType);
595 //	} else {
596 //		attr = (which == B_MINI_ICON) ? kMiniIconAttr : kLargeIconAttr;
597 //	}
598 //	// Check the icon and attribute to see if they match
599 //	if (!err) {
600 //		err = (icon->InitCheck() == B_OK
601 //				&& icon->Bounds() == bounds) ? B_OK : B_BAD_VALUE;
602 //	}
603 //
604 //	BNode node;
605 //	if (!err)
606 //		err = open_type(type, &node);
607 //
608 //	attr_info info;
609 //	if (!err)
610 //		err = node.GetAttrInfo(attr.c_str(), &info);
611 //
612 //	if (!err)
613 //		err = (attrType == info.type && attrSize == info.size) ? B_OK : B_BAD_VALUE;
614 //	// read the attribute
615 //	if (!err) {
616 //		bool otherColorSpace = (icon->ColorSpace() != B_CMAP8);
617 //		char *buffer = NULL;
618 //		if (otherColorSpace) {
619 //			// other color space than stored in attribute
620 //			buffer = new(std::nothrow) char[attrSize];
621 //			if (!buffer)
622 //				err = B_NO_MEMORY;
623 //			if (!err)
624 //				err = node.ReadAttr(attr.c_str(), attrType, 0, buffer, attrSize);
625 //		} else {
626 //			// same color space, just read direct
627 //			err = node.ReadAttr(attr.c_str(), attrType, 0, icon->Bits(), attrSize);
628 //		}
629 //		if (err >= 0)
630 //			err = (err == attrSize) ? (status_t)B_OK : (status_t)B_FILE_ERROR;
631 //		if (otherColorSpace) {
632 //			if (!err) {
633 //				err = icon->ImportBits(buffer, attrSize, B_ANY_BYTES_PER_ROW,
634 //									   0, B_CMAP8);
635 //			}
636 //			delete[] buffer;
637 //		}
638 //	}
639 //
640 //	return err;
641 }
642 
643 
644 /*! \brief Fetches the vector icon used by an application of this type for files
645 		of the given type.
646 
647 	The type of the \c BMimeType object is not required to actually be a subtype
648 	of \c "application/"; that is the intended use however, and calling
649 	\c GetIconForType() on a non-application type will likely return
650 	\c B_ENTRY_NOT_FOUND.
651 
652 	The icon data is allocated and returned in \a _data.
653 
654 	\param type The MIME type
655 	\param fileType Reference to a pre-allocated string containing the MIME type
656 		whose custom icon you wish to fetch. If NULL, works just like GetIcon().
657 	\param _data Reference via which the icon data is returned on success.
658 	\param _size Reference via which the size of the icon data is returned.
659 	\return
660 	- \c B_OK: Success
661 	- \c B_ENTRY_NOT_FOUND: No vector icon exists for the given type
662 	- "error code": Failure
663 */
664 status_t
665 DatabaseLocation::GetIconForType(const char* type, const char* fileType,
666 	uint8*& _data, size_t& _size)
667 {
668 	if (type == NULL)
669 		return B_BAD_VALUE;
670 
671 	// open the node for the given type
672 	BNode node;
673 	ssize_t err = OpenType(type, node);
674 	if (err < B_OK)
675 		return (status_t)err;
676 
677 	// construct our attribute name
678 	BString iconAttrName;
679 
680 	if (fileType)
681 		iconAttrName << kIconAttrPrefix << BString(fileType).ToLower();
682 	else
683 		iconAttrName = kIconAttr;
684 
685 	// get info about attribute for that name
686 	attr_info info;
687 	if (!err)
688 		err = node.GetAttrInfo(iconAttrName, &info);
689 
690 	// validate attribute type
691 	if (!err)
692 		err = (info.type == B_VECTOR_ICON_TYPE) ? B_OK : B_BAD_VALUE;
693 
694 	// allocate a buffer and read the attribute data into it
695 	if (!err) {
696 		uint8* buffer = new(std::nothrow) uint8[info.size];
697 		if (!buffer)
698 			err = B_NO_MEMORY;
699 		if (!err) {
700 			err = node.ReadAttr(iconAttrName, B_VECTOR_ICON_TYPE, 0, buffer,
701 				info.size);
702 		}
703 
704 		if (err >= 0)
705 			err = (err == info.size) ? (ssize_t)B_OK : (ssize_t)B_FILE_ERROR;
706 
707 		if (!err) {
708 			// success, set data pointer and size
709 			_data = buffer;
710 			_size = info.size;
711 		} else {
712 			delete[] buffer;
713 		}
714 	}
715 
716 	return err;
717 }
718 
719 
720 /*!	\brief Fetches signature of the MIME type's preferred application for the
721 		given action.
722 
723 	The string pointed to by \c signature must be long enough to
724 	hold the short description; a length of \c B_MIME_TYPE_LENGTH is
725 	recommended.
726 
727 	Currently, the only supported app verb is \c B_OPEN.
728 
729 	\param type The MIME type of interest
730 	\param description Pointer to a pre-allocated string into which the
731 		preferred application's signature is copied. If the function fails,
732 		the contents of the string are undefined.
733 	\param verb \c The action of interest
734 
735 	\return
736 	- \c B_OK: Success
737 	- \c B_ENTRY_NOT_FOUND: No such preferred application exists
738 	- "error code": Failure
739 */
740 status_t
741 DatabaseLocation::GetPreferredApp(const char* type, char* signature,
742 	app_verb verb)
743 {
744 	// Since B_OPEN is the currently the only app_verb, it is essentially
745 	// ignored
746 	ssize_t err = ReadAttribute(type, kPreferredAppAttr, signature,
747 		B_MIME_TYPE_LENGTH, kPreferredAppType);
748 	return err >= 0 ? B_OK : err ;
749 }
750 
751 
752 /*! \brief Fetches the sniffer rule for the given MIME type.
753 	\param type The MIME type of interest
754 	\param _result Pointer to a pre-allocated BString into which the type's
755 		sniffer rule is copied.
756 	\return
757 	- \c B_OK: Success
758 	- \c B_ENTRY_NOT_FOUND: No such preferred application exists
759 	- "error code": Failure
760 */
761 status_t
762 DatabaseLocation::GetSnifferRule(const char* type, BString& _result)
763 {
764 	return ReadStringAttribute(type, kSnifferRuleAttr, _result);
765 }
766 
767 
768 status_t
769 DatabaseLocation::GetSupportedTypes(const char* type, BMessage& _types)
770 {
771 	status_t err = ReadMessageAttribute(type, kSupportedTypesAttr, _types);
772 	if (err == B_ENTRY_NOT_FOUND) {
773 		// return an empty message
774 		_types.MakeEmpty();
775 		err = B_OK;
776 	}
777 	if (err == B_OK) {
778 		_types.what = 0;
779 		err = _types.AddString("type", type);
780 	}
781 	return err;
782 }
783 
784 
785 //! Checks if the given MIME type is present in the database
786 bool
787 DatabaseLocation::IsInstalled(const char* type)
788 {
789 	BNode node;
790 	return OpenType(type, node) == B_OK;
791 }
792 
793 
794 BString
795 DatabaseLocation::_TypeToFilename(const char* type, int32 index) const
796 {
797 	BString path = fDirectories.StringAt(index);
798 	return path << '/' << BString(type).ToLower();
799 }
800 
801 
802 status_t
803 DatabaseLocation::_OpenType(const char* type, BNode& _node, int32& _index) const
804 {
805 	int32 count = fDirectories.CountStrings();
806 	for (int32 i = 0; i < count; i++) {
807 		status_t error = _node.SetTo(_TypeToFilename(type, i));
808 		attr_info attrInfo;
809 		if (error == B_OK && _node.GetAttrInfo(kTypeAttr, &attrInfo) == B_OK) {
810 			_index = i;
811 			return B_OK;
812 		}
813 	}
814 
815 	return B_ENTRY_NOT_FOUND;
816 }
817 
818 
819 status_t
820 DatabaseLocation::_CreateTypeNode(const char* type, BNode& _node) const
821 {
822 	const char* slash = strchr(type, '/');
823 	BString superTypeName;
824 	if (slash != NULL)
825 		superTypeName.SetTo(type, slash - type);
826 	else
827 		superTypeName = type;
828 	superTypeName.ToLower();
829 
830 	// open/create the directory for the supertype
831 	BDirectory parent(WritableDirectory());
832 	status_t error = parent.InitCheck();
833 	if (error != B_OK)
834 		return error;
835 
836 	BDirectory superTypeDirectory;
837 	if (BEntry(&parent, superTypeName).Exists())
838 		error = superTypeDirectory.SetTo(&parent, superTypeName);
839 	else
840 		error = parent.CreateDirectory(superTypeName, &superTypeDirectory);
841 	if (error != B_OK)
842 		return error;
843 
844 	// create the subtype
845 	BFile subTypeFile;
846 	if (slash != NULL) {
847 		error = superTypeDirectory.CreateFile(BString(slash + 1).ToLower(),
848 			&subTypeFile);
849 		if (error != B_OK)
850 			return error;
851 	}
852 
853 	// assign the result
854 	if (slash != NULL)
855 		_node = subTypeFile;
856 	else
857 		_node = superTypeDirectory;
858 	return _node.InitCheck();
859 }
860 
861 
862 status_t
863 DatabaseLocation::_CopyTypeNode(BNode& source, const char* type, BNode& _target)
864 	const
865 {
866 	status_t error = _CreateTypeNode(type, _target);
867 	if (error != B_OK)
868 		return error;
869 
870 	// copy the attributes
871 	MemoryDeleter bufferDeleter;
872 	size_t bufferSize = 0;
873 
874 	source.RewindAttrs();
875 	char attribute[B_ATTR_NAME_LENGTH];
876 	while (source.GetNextAttrName(attribute) == B_OK) {
877 		attr_info info;
878 		error = source.GetAttrInfo(attribute, &info);
879 		if (error != B_OK) {
880 			syslog(LOG_ERR, "Failed to get info for attribute \"%s\" of MIME "
881 				"type \"%s\": %s", attribute, type, strerror(error));
882 			continue;
883 		}
884 
885 		// resize our buffer, if necessary
886 		if (info.size > (off_t)bufferSize) {
887 			bufferDeleter.SetTo(malloc(info.size));
888 			if (bufferDeleter.Get() == NULL)
889 				return B_NO_MEMORY;
890 			bufferSize = info.size;
891 		}
892 
893 		ssize_t bytesRead = source.ReadAttr(attribute, info.type, 0,
894 			bufferDeleter.Get(), info.size);
895 		if (bytesRead != info.size) {
896 			syslog(LOG_ERR, "Failed to read attribute \"%s\" of MIME "
897 				"type \"%s\": %s", attribute, type,
898 		  		bytesRead < 0 ? strerror(bytesRead) : "short read");
899 			continue;
900 		}
901 
902 		ssize_t bytesWritten = _target.WriteAttr(attribute, info.type, 0,
903 			bufferDeleter.Get(), info.size);
904 		if (bytesWritten < 0) {
905 			syslog(LOG_ERR, "Failed to write attribute \"%s\" of MIME "
906 				"type \"%s\": %s", attribute, type,
907 		  		bytesWritten < 0 ? strerror(bytesWritten) : "short write");
908 			continue;
909 		}
910 	}
911 
912 	return B_OK;
913 }
914 
915 
916 } // namespace Mime
917 } // namespace Storage
918 } // namespace BPrivate
919