2 * Copyright 2002-2014 Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
7 * Rene Gollent, rene@gollent.com
8 * Ingo Weinhold, ingo_weinhold@gmx.de
12 #include <mime/DatabaseLocation.h>
21 #include <Directory.h>
24 #include <IconUtils.h>
28 #include <AutoDeleter.h>
29 #include <mime/database_support.h>
37 DatabaseLocation::DatabaseLocation()
44 DatabaseLocation::~DatabaseLocation()
50 DatabaseLocation::AddDirectory(const BString
& directory
)
52 return !directory
.IsEmpty() && fDirectories
.Add(directory
);
56 /*! Opens a BNode on the given type, failing if the type has no
57 corresponding file in the database.
59 \param type The MIME type to open.
60 \param _node Node opened on the given MIME type.
63 DatabaseLocation::OpenType(const char* type
, BNode
& _node
) const
69 return _OpenType(type
, _node
, index
);
73 /*! Opens a BNode on the given type, creating a node of the
74 appropriate flavor if requested (and necessary).
76 All MIME types are converted to lowercase for use in the filesystem.
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
84 \return A status code.
87 DatabaseLocation::OpenWritableType(const char* type
, BNode
& _node
, bool create
,
88 bool* _didCreate
) const
93 // See, if the type already exists.
95 status_t result
= _OpenType(type
, _node
, index
);
100 return B_ENTRY_NOT_FOUND
;
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();
108 result
= _CopyTypeNode(nodeToClone
, type
, _node
);
109 if (result
!= B_OK
) {
114 if (_didCreate
!= NULL
)
119 return B_ENTRY_NOT_FOUND
;
121 // type doesn't exist yet -- create the respective node
122 result
= _CreateTypeNode(type
, _node
);
126 // write the type attribute
127 size_t toWrite
= strlen(type
) + 1;
128 ssize_t bytesWritten
= _node
.WriteAttr(kTypeAttr
, B_STRING_TYPE
, 0, type
,
130 if (bytesWritten
< 0)
131 result
= bytesWritten
;
132 else if ((size_t)bytesWritten
!= toWrite
)
133 result
= B_FILE_ERROR
;
135 if (result
!= B_OK
) {
140 if (_didCreate
!= NULL
)
146 /*! Reads up to \c length bytes of the given data from the given attribute
147 for the given MIME type.
149 If no entry for the given type exists in the database, the function fails,
150 and the contents of \c data are undefined.
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.
158 \return If successful, the number of bytes read is returned, otherwise, an
159 error code is returned.
162 DatabaseLocation::ReadAttribute(const char* type
, const char* attribute
,
163 void* data
, size_t length
, type_code datatype
) const
165 if (type
== NULL
|| attribute
== NULL
|| data
== NULL
)
169 status_t result
= OpenType(type
, node
);
173 return node
.ReadAttr(attribute
, datatype
, 0, data
, length
);
177 /*! Reads a flattened BMessage from the given attribute of the given
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.
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
189 \return A status code.
192 DatabaseLocation::ReadMessageAttribute(const char* type
, const char* attribute
,
193 BMessage
& _message
) const
195 if (type
== NULL
|| attribute
== NULL
)
201 status_t result
= OpenType(type
, node
);
205 result
= node
.GetAttrInfo(attribute
, &info
);
209 if (info
.type
!= B_MESSAGE_TYPE
)
212 void* buffer
= malloc(info
.size
);
215 MemoryDeleter
bufferDeleter(buffer
);
217 ssize_t bytesRead
= node
.ReadAttr(attribute
, B_MESSAGE_TYPE
, 0, buffer
,
219 if (bytesRead
!= info
.size
)
220 return bytesRead
< 0 ? (status_t
)bytesRead
: (status_t
)B_FILE_ERROR
;
222 return _message
.Unflatten((const char*)buffer
);
226 /*! Reads a BString from the given attribute of the given MIME type.
228 If no entry for the given type exists in the database, the function fails
229 and the contents of \c str are undefined.
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
236 \return A status code.
239 DatabaseLocation::ReadStringAttribute(const char* type
, const char* attribute
,
240 BString
& _string
) const
242 if (type
== NULL
|| attribute
== NULL
)
246 status_t result
= OpenType(type
, node
);
250 return node
.ReadAttrString(attribute
, &_string
);
254 /*! Writes \c len bytes of the given data to the given attribute
255 for the given MIME type.
257 If no entry for the given type exists in the database, it is created.
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.
265 \return A status code.
268 DatabaseLocation::WriteAttribute(const char* type
, const char* attribute
,
269 const void* data
, size_t length
, type_code datatype
, bool* _didCreate
) const
271 if (type
== NULL
|| attribute
== NULL
|| data
== NULL
)
275 status_t result
= OpenWritableType(type
, node
, true, _didCreate
);
279 ssize_t bytesWritten
= node
.WriteAttr(attribute
, datatype
, 0, data
, length
);
280 if (bytesWritten
< 0)
282 return bytesWritten
== (ssize_t
)length
283 ? (status_t
)B_OK
: (status_t
)B_FILE_ERROR
;
287 /*! Flattens the given \c BMessage and writes it to the given attribute
288 of the given MIME type.
290 If no entry for the given type exists in the database, it is created.
292 \param type The MIME type.
293 \param attribute The attribute name.
294 \param message The BMessage to flatten and write.
296 \return A status code.
299 DatabaseLocation::WriteMessageAttribute(const char* type
, const char* attribute
,
300 const BMessage
& message
, bool* _didCreate
) const
303 status_t result
= data
.SetSize(message
.FlattenedSize());
308 result
= message
.Flatten(&data
, &bytes
);
312 return WriteAttribute(type
, attribute
, data
.Buffer(), data
.BufferLength(),
313 B_MESSAGE_TYPE
, _didCreate
);
317 /*! Deletes the given attribute for the given type
319 \param type The mime type
320 \param attribute The attribute name
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.
327 DatabaseLocation::DeleteAttribute(const char* type
, const char* attribute
) const
329 if (type
== NULL
|| attribute
== NULL
)
333 status_t result
= OpenWritableType(type
, node
, false);
337 return node
.RemoveAttr(attribute
);
341 /*! Fetches the application hint for the given MIME type.
343 The entry_ref pointed to by \c ref must be pre-allocated.
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.
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
354 DatabaseLocation::GetAppHint(const char* type
, entry_ref
& _ref
)
359 char path
[B_PATH_NAME_LENGTH
];
361 ssize_t status
= ReadAttribute(type
, kAppHintAttr
, path
, B_PATH_NAME_LENGTH
,
365 status
= entry
.SetTo(path
);
367 status
= entry
.GetRef(&_ref
);
373 /*! Fetches from the MIME database a BMessage describing the attributes
374 typically associated with files of the given MIME type
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.
381 \param _info Reference to a pre-allocated BMessage into which information
382 about the MIME type's associated file attributes is stored.
384 \return A status code, \c B_OK on success or an error code on failure.
387 DatabaseLocation::GetAttributesInfo(const char* type
, BMessage
& _info
)
389 status_t result
= ReadMessageAttribute(type
, kAttrInfoAttr
, _info
);
391 if (result
== B_ENTRY_NOT_FOUND
) {
392 // return an empty message
397 if (result
== B_OK
) {
399 // Don't know why, but that's what R5 does.
400 result
= _info
.AddString("type", type
);
407 /*! Fetches the short description for the given MIME type.
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
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.
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.
423 DatabaseLocation::GetShortDescription(const char* type
, char* description
)
425 ssize_t result
= ReadAttribute(type
, kShortDescriptionAttr
, description
,
426 B_MIME_TYPE_LENGTH
, kShortDescriptionType
);
428 return result
>= 0 ? B_OK
: result
;
432 /*! Fetches the long description for the given MIME type.
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
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.
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
448 DatabaseLocation::GetLongDescription(const char* type
, char* description
)
450 ssize_t result
= ReadAttribute(type
, kLongDescriptionAttr
, description
,
451 B_MIME_TYPE_LENGTH
, kLongDescriptionType
);
453 return result
>= 0 ? B_OK
: result
;
457 /*! Fetches a BMessage describing the MIME type's associated filename
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.
465 \param extensions Reference to a pre-allocated BMessage into which the MIME
466 type's associated file extensions will be stored.
468 \return A status code, \c B_OK on success or an error code on failure.
471 DatabaseLocation::GetFileExtensions(const char* type
, BMessage
& _extensions
)
473 status_t result
= ReadMessageAttribute(type
, kFileExtensionsAttr
, _extensions
);
474 if (result
== B_ENTRY_NOT_FOUND
) {
475 // return an empty message
476 _extensions
.MakeEmpty();
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
);
489 /*! Fetches the icon of given size associated with the given MIME type.
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
495 \param type The mime type
496 \param icon Reference to a pre-allocated bitmap of proper dimensions and
498 \param size The size icon you're interested in (\c B_LARGE_ICON or
501 \return A status code.
504 DatabaseLocation::GetIcon(const char* type
, BBitmap
& _icon
, icon_size size
)
506 return GetIconForType(type
, NULL
, _icon
, size
);
510 /*! Fetches the vector icon associated with the given MIME type.
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.
517 \return A status code.
520 DatabaseLocation::GetIcon(const char* type
, uint8
*& _data
, size_t& _size
)
522 return GetIconForType(type
, NULL
, _data
, _size
);
526 /*! Fetches the large or mini icon used by an application of this type
527 for files of the given type.
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.
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
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
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.
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
552 DatabaseLocation::GetIconForType(const char* type
, const char* fileType
,
553 BBitmap
& _icon
, icon_size which
)
558 // open the node for the given type
560 status_t result
= OpenType(type
, node
);
564 // construct our attribute name
565 BString vectorIconAttrName
;
566 BString smallIconAttrName
;
567 BString largeIconAttrName
;
569 if (fileType
!= NULL
) {
570 BString
lowerCaseFileType(fileType
);
571 lowerCaseFileType
.ToLower();
573 vectorIconAttrName
<< kIconAttrPrefix
<< lowerCaseFileType
;
574 smallIconAttrName
<< kMiniIconAttrPrefix
<< lowerCaseFileType
;
575 largeIconAttrName
<< kLargeIconAttrPrefix
<< lowerCaseFileType
;
577 vectorIconAttrName
= kIconAttr
;
578 smallIconAttrName
= kMiniIconAttr
;
579 largeIconAttrName
= kLargeIconAttr
;
582 return BIconUtils::GetIcon(&node
, vectorIconAttrName
, smallIconAttrName
,
583 largeIconAttrName
, which
, &_icon
);
587 /*! Fetches the vector icon used by an application of this type for files
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.
595 The icon data is allocated and returned in \a _data.
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
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.
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.
609 DatabaseLocation::GetIconForType(const char* type
, const char* fileType
,
610 uint8
*& _data
, size_t& _size
)
615 // open the node for the given type
617 status_t result
= OpenType(type
, node
);
621 // construct our attribute name
622 BString iconAttrName
;
624 if (fileType
!= NULL
)
625 iconAttrName
<< kIconAttrPrefix
<< BString(fileType
).ToLower();
627 iconAttrName
= kIconAttr
;
629 // get info about attribute for that name
632 result
= node
.GetAttrInfo(iconAttrName
, &info
);
634 // validate attribute type
636 result
= (info
.type
== B_VECTOR_ICON_TYPE
) ? B_OK
: B_BAD_VALUE
;
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
];
642 result
= B_NO_MEMORY
;
644 ssize_t bytesRead
= -1;
645 if (result
== B_OK
) {
646 bytesRead
= node
.ReadAttr(iconAttrName
, B_VECTOR_ICON_TYPE
, 0, buffer
,
651 result
= bytesRead
== info
.size
? B_OK
: B_FILE_ERROR
;
653 if (result
== B_OK
) {
654 // success, set data pointer and size
665 /*! Fetches signature of the MIME type's preferred application for the
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
672 Currently, the only supported app verb is \c B_OPEN.
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
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
685 DatabaseLocation::GetPreferredApp(const char* type
, char* signature
,
688 // Since B_OPEN is the currently the only app_verb, it is essentially
690 ssize_t result
= ReadAttribute(type
, kPreferredAppAttr
, signature
,
691 B_MIME_TYPE_LENGTH
, kPreferredAppType
);
693 return result
>= 0 ? B_OK
: result
;
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.
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.
707 DatabaseLocation::GetSnifferRule(const char* type
, BString
& _result
)
709 return ReadStringAttribute(type
, kSnifferRuleAttr
, _result
);
714 DatabaseLocation::GetSupportedTypes(const char* type
, BMessage
& _types
)
716 status_t result
= ReadMessageAttribute(type
, kSupportedTypesAttr
, _types
);
717 if (result
== B_ENTRY_NOT_FOUND
) {
718 // return an empty message
722 if (result
== B_OK
) {
724 result
= _types
.AddString("type", type
);
731 //! Checks if the given MIME type is present in the database
733 DatabaseLocation::IsInstalled(const char* type
)
736 return OpenType(type
, node
) == B_OK
;
741 DatabaseLocation::_TypeToFilename(const char* type
, int32 index
) const
743 BString path
= fDirectories
.StringAt(index
);
744 return path
<< '/' << BString(type
).ToLower();
749 DatabaseLocation::_OpenType(const char* type
, BNode
& _node
, int32
& _index
) const
751 int32 count
= fDirectories
.CountStrings();
752 for (int32 i
= 0; i
< count
; i
++) {
753 status_t result
= _node
.SetTo(_TypeToFilename(type
, i
));
755 if (result
== B_OK
&& _node
.GetAttrInfo(kTypeAttr
, &attrInfo
) == B_OK
) {
761 return B_ENTRY_NOT_FOUND
;
766 DatabaseLocation::_CreateTypeNode(const char* type
, BNode
& _node
) const
768 const char* slash
= strchr(type
, '/');
769 BString superTypeName
;
771 superTypeName
.SetTo(type
, slash
- type
);
773 superTypeName
= type
;
774 superTypeName
.ToLower();
776 // open/create the directory for the supertype
777 BDirectory
parent(WritableDirectory());
778 status_t result
= parent
.InitCheck();
782 BDirectory superTypeDirectory
;
783 if (BEntry(&parent
, superTypeName
).Exists())
784 result
= superTypeDirectory
.SetTo(&parent
, superTypeName
);
786 result
= parent
.CreateDirectory(superTypeName
, &superTypeDirectory
);
791 // create the subtype
794 result
= superTypeDirectory
.CreateFile(BString(slash
+ 1).ToLower(),
804 _node
= superTypeDirectory
;
805 return _node
.InitCheck();
810 DatabaseLocation::_CopyTypeNode(BNode
& source
, const char* type
, BNode
& _target
)
813 status_t result
= _CreateTypeNode(type
, _target
);
817 // copy the attributes
818 MemoryDeleter bufferDeleter
;
819 size_t bufferSize
= 0;
821 source
.RewindAttrs();
822 char attribute
[B_ATTR_NAME_LENGTH
];
823 while (source
.GetNextAttrName(attribute
) == B_OK
) {
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
));
832 // resize our buffer, if necessary
833 if (info
.size
> (off_t
)bufferSize
) {
834 bufferDeleter
.SetTo(malloc(info
.size
));
835 if (bufferDeleter
.Get() == NULL
)
837 bufferSize
= info
.size
;
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");
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");
864 } // namespace Storage
865 } // namespace BPrivate