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
32namespace BPrivate {
33namespace Storage {
34namespace Mime {
35
36
37DatabaseLocation::DatabaseLocation()
38	:
39	fDirectories()
40{
41}
42
43
44DatabaseLocation::~DatabaseLocation()
45{
46}
47
48
49bool
50DatabaseLocation::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*/
62status_t
63DatabaseLocation::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*/
86status_t
87DatabaseLocation::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*/
161ssize_t
162DatabaseLocation::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*/
191status_t
192DatabaseLocation::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*/
238status_t
239DatabaseLocation::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*/
267status_t
268DatabaseLocation::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*/
298status_t
299DatabaseLocation::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*/
326status_t
327DatabaseLocation::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*/
353status_t
354DatabaseLocation::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*/
386status_t
387DatabaseLocation::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*/
422status_t
423DatabaseLocation::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*/
447status_t
448DatabaseLocation::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*/
470status_t
471DatabaseLocation::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*/
503status_t
504DatabaseLocation::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*/
519status_t
520DatabaseLocation::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*/
551status_t
552DatabaseLocation::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*/
608status_t
609DatabaseLocation::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*/
684status_t
685DatabaseLocation::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*/
706status_t
707DatabaseLocation::GetSnifferRule(const char* type, BString& _result)
708{
709	return ReadStringAttribute(type, kSnifferRuleAttr, _result);
710}
711
712
713status_t
714DatabaseLocation::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
732bool
733DatabaseLocation::IsInstalled(const char* type)
734{
735	BNode node;
736	return OpenType(type, node) == B_OK;
737}
738
739
740BString
741DatabaseLocation::_TypeToFilename(const char* type, int32 index) const
742{
743	BString path = fDirectories.StringAt(index);
744	return path << '/' << BString(type).ToLower();
745}
746
747
748status_t
749DatabaseLocation::_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
765status_t
766DatabaseLocation::_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
809status_t
810DatabaseLocation::_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