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