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