xref: /haiku/src/kits/storage/mime/Database.cpp (revision 55b40aa53a835472ec7952b138ae4256203d02e4)
1 /*
2  * Copyright 2002-2006, Haiku.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Tyler Dauwalder
7  *		Axel Dörfler, axeld@pinc-software.de
8  */
9 
10 
11 #include <mime/Database.h>
12 
13 #include <mime/database_access.h>
14 #include <mime/database_support.h>
15 #include <storage_support.h>
16 
17 #include <Application.h>
18 #include <Bitmap.h>
19 #include <DataIO.h>
20 #include <Directory.h>
21 #include <Entry.h>
22 #include <Locker.h>
23 #include <Message.h>
24 #include <MimeType.h>
25 #include <Node.h>
26 #include <Path.h>
27 #include <String.h>
28 #include <TypeConstants.h>
29 
30 #include <fs_attr.h>
31 
32 #include <iostream>
33 #include <new>
34 #include <stdio.h>
35 #include <string>
36 
37 
38 //#define DBG(x) x
39 #define DBG(x)
40 #define OUT printf
41 
42 
43 namespace BPrivate {
44 namespace Storage {
45 namespace Mime {
46 
47 /*!
48 	\class Database
49 	\brief Mime::Database is the master of the MIME data base.
50 
51 	All write and non-atomic read accesses are carried out by this class.
52 
53 	\note No error checking (other than checks for NULL pointers) is performed
54 	      by this class on the mime type strings passed to it. It's assumed
55 	      that this sort of checking has been done beforehand.
56 */
57 
58 // constructor
59 /*!	\brief Creates and initializes a Mime::Database object.
60 */
61 Database::Database()
62 	: fStatus(B_NO_INIT)
63 {
64 	// Do some really minor error checking
65 	BEntry entry(kDatabaseDir.c_str());
66 	fStatus = entry.Exists() ? B_OK : B_BAD_VALUE;
67 }
68 
69 // destructor
70 /*!	\brief Frees all resources associated with this object.
71 */
72 Database::~Database()
73 {
74 }
75 
76 // InitCheck
77 /*! \brief Returns the initialization status of the object.
78 	\return
79 	- B_OK: success
80 	- "error code": failure
81 */
82 status_t
83 Database::InitCheck() const
84 {
85 	return fStatus;
86 }
87 
88 // Install
89 /*!	\brief Installs the given type in the database
90 	\note The R5 version of this call returned an unreliable result if the
91 	      MIME type was already installed. Ours simply returns B_OK.
92 	\param type Pointer to a NULL-terminated string containing the MIME type of interest
93 	\param decsription Pointer to a NULL-terminated string containing the new long description
94 	\return
95 	- B_OK: success
96 	- B_FILE_EXISTS: the type is already installed
97 	- "error code": failure
98 */
99 status_t
100 Database::Install(const char *type)
101 {
102 	if (type == NULL)
103 		return B_BAD_VALUE;
104 
105 	BEntry entry;
106 	status_t err = entry.SetTo(type_to_filename(type).c_str());
107 	if (!err) {
108 		if (entry.Exists())
109 			err = B_FILE_EXISTS;
110 		else {
111 			bool didCreate = false;
112 			BNode node;
113 			err = open_or_create_type(type, &node, &didCreate);
114 			if (!err && didCreate) {
115 				fInstalledTypes.AddType(type);
116 				_SendInstallNotification(type);
117 			}
118 		}
119 	}
120 	return err;
121 }
122 
123 // Delete
124 /*!	\brief Removes the given type from the database
125 	\param type Pointer to a NULL-terminated string containing the MIME type of interest
126 	\return
127 	- B_OK: success
128 	- "error code": failure
129 */
130 status_t
131 Database::Delete(const char *type)
132 {
133 	if (type == NULL)
134 		return B_BAD_VALUE;
135 
136 	// Open the type
137 	BEntry entry;
138 	status_t status = entry.SetTo(type_to_filename(type).c_str());
139 	if (status != B_OK)
140 		return status;
141 
142 	// Remove it
143 	if (entry.IsDirectory()) {
144 		// We need to remove all files in this directory
145 		BDirectory directory(&entry);
146 		if (directory.InitCheck() == B_OK) {
147 			size_t length = strlen(type);
148 			char subType[B_PATH_NAME_LENGTH];
149 			memcpy(subType, type, length);
150 			subType[length++] = '/';
151 
152 			BEntry subEntry;
153 			while (directory.GetNextEntry(&subEntry) == B_OK) {
154 				// Construct MIME type and remove it
155 				if (subEntry.GetName(subType + length) == B_OK) {
156 					status = Delete(subType);
157 					if (status != B_OK)
158 						return status;
159 				}
160 			}
161 		}
162 	}
163 
164 	status = entry.Remove();
165 
166 	if (status == B_OK) {
167 		// Notify the installed types database
168 		fInstalledTypes.RemoveType(type);
169 		// Notify the supporting apps database
170 		fSupportingApps.DeleteSupportedTypes(type, true);
171 		// Notify the monitor service
172 		_SendDeleteNotification(type);
173 	}
174 
175 	return status;
176 }
177 
178 
179 status_t
180 Database::_SetStringValue(const char *type, int32 what, const char* attribute,
181 	type_code attributeType, size_t maxLength, const char *value)
182 {
183 	size_t length = value != NULL ? strlen(value) : 0;
184 	if (type == NULL || value == NULL || length >= maxLength)
185 		return B_BAD_VALUE;
186 
187 	char oldValue[maxLength];
188 	status_t status = read_mime_attr(type, attribute, oldValue,
189 		maxLength, attributeType);
190 	if (status >= B_OK && !strcmp(value, oldValue)) {
191 		// nothing has changed, no need to write back the data
192 		return B_OK;
193 	}
194 
195 	bool didCreate = false;
196 	status = write_mime_attr(type, attribute, value, length + 1,
197 		attributeType, &didCreate);
198 
199 	if (status == B_OK) {
200 		if (didCreate)
201 			_SendInstallNotification(type);
202 
203 		_SendMonitorUpdate(what, type, B_META_MIME_MODIFIED);
204 	}
205 
206 	return status;
207 }
208 
209 
210 // SetAppHint
211 /*!	\brief Sets the application hint for the given MIME type
212 	\param type Pointer to a NULL-terminated string containing the MIME type of interest
213 	\param decsription Pointer to an entry_ref containing the location of an application
214 	       that should be used when launching an application with this signature.
215 */
216 status_t
217 Database::SetAppHint(const char *type, const entry_ref *ref)
218 {
219 	DBG(OUT("Database::SetAppHint()\n"));
220 
221 	if (type == NULL || ref == NULL)
222 		return B_BAD_VALUE;
223 
224 	BPath path;
225 	status_t status = path.SetTo(ref);
226 	if (status < B_OK)
227 		return status;
228 
229 	return _SetStringValue(type, B_APP_HINT_CHANGED, kAppHintAttr,
230 		kAppHintType, B_PATH_NAME_LENGTH, path.Path());
231 }
232 
233 // SetAttrInfo
234 /*! \brief Stores a BMessage describing the format of attributes typically associated with
235 	files of the given MIME type
236 
237 	See BMimeType::SetAttrInfo() for description of the expected message format.
238 
239 	The \c BMessage::what value is ignored.
240 
241 	\param info Pointer to a pre-allocated and properly formatted BMessage containing
242 	            information about the file attributes typically associated with the
243 	            MIME type.
244 	\return
245 	- \c B_OK: Success
246 	- "error code": Failure
247 */
248 status_t
249 Database::SetAttrInfo(const char *type, const BMessage *info)
250 {
251 	DBG(OUT("Database::SetAttrInfo()\n"));
252 
253 	if (type == NULL || info == NULL)
254 		return B_BAD_VALUE;
255 
256 	bool didCreate = false;
257 	status_t status = write_mime_attr_message(type, kAttrInfoAttr, info, &didCreate);
258 	if (status == B_OK) {
259 		if (didCreate)
260 			_SendInstallNotification(type);
261 		_SendMonitorUpdate(B_ATTR_INFO_CHANGED, type, B_META_MIME_MODIFIED);
262 	}
263 
264 	return status;
265 }
266 
267 
268 // SetShortDescription
269 /*!	\brief Sets the short description for the given MIME type
270 	\param type Pointer to a NULL-terminated string containing the MIME type of interest
271 	\param decsription Pointer to a NULL-terminated string containing the new short description
272 */
273 status_t
274 Database::SetShortDescription(const char *type, const char *description)
275 {
276 	DBG(OUT("Database::SetShortDescription()\n"));
277 
278 	return _SetStringValue(type, B_SHORT_DESCRIPTION_CHANGED, kShortDescriptionAttr,
279 		kShortDescriptionType, B_MIME_TYPE_LENGTH, description);
280 }
281 
282 // SetLongDescription
283 /*!	\brief Sets the long description for the given MIME type
284 	\param type Pointer to a NULL-terminated string containing the MIME type of interest
285 	\param decsription Pointer to a NULL-terminated string containing the new long description
286 */
287 status_t
288 Database::SetLongDescription(const char *type, const char *description)
289 {
290 	DBG(OUT("Database::SetLongDescription()\n"));
291 
292 	size_t length = description != NULL ? strlen(description) : 0;
293 	if (type == NULL || description == NULL || length >= B_MIME_TYPE_LENGTH)
294 		return B_BAD_VALUE;
295 
296 	return _SetStringValue(type, B_LONG_DESCRIPTION_CHANGED, kLongDescriptionAttr,
297 		kLongDescriptionType, B_MIME_TYPE_LENGTH, description);
298 }
299 
300 
301 /*!
302 	\brief Sets the list of filename extensions associated with the MIME type
303 
304 	The list of extensions is given in a pre-allocated BMessage pointed to by
305 	the \c extensions parameter. Please see BMimeType::SetFileExtensions()
306 	for a description of the expected message format.
307 
308 	\param extensions Pointer to a pre-allocated, properly formatted BMessage containing
309 	                  the new list of file extensions to associate with this MIME type.
310 	\return
311 	- \c B_OK: Success
312 	- "error code": Failure
313 */
314 status_t
315 Database::SetFileExtensions(const char *type, const BMessage *extensions)
316 {
317 	DBG(OUT("Database::SetFileExtensions()\n"));
318 
319 	if (type == NULL || extensions == NULL)
320 		return B_BAD_VALUE;
321 
322 	bool didCreate = false;
323 	status_t status = write_mime_attr_message(type, kFileExtensionsAttr,
324 		extensions, &didCreate);
325 
326 	if (status == B_OK) {
327 		if (didCreate)
328 			_SendInstallNotification(type);
329 		_SendMonitorUpdate(B_FILE_EXTENSIONS_CHANGED, type, B_META_MIME_MODIFIED);
330 	}
331 
332 	return status;
333 }
334 
335 
336 /*!
337 	\brief Sets the icon for the given mime type
338 
339 	This is the version I would have used if I could have gotten a BBitmap
340 	to the registrar somehow. Since R5::BBitmap::Instantiate is causing a
341 	violent crash, I've copied most of the icon	color conversion code into
342 	Mime::get_icon_data() so BMimeType::SetIcon() can get at it.
343 
344 	Once we have a sufficiently complete OBOS::BBitmap implementation, we
345 	ought to be able to use this version of SetIcon() again. At that point,
346 	I'll add some real documentation.
347 */
348 status_t
349 Database::SetIcon(const char *type, const void *data, size_t dataSize,
350 	icon_size which)
351 {
352 	return SetIconForType(type, NULL, data, dataSize, which);
353 }
354 
355 status_t
356 Database::SetIcon(const char *type, const void *data, size_t dataSize)
357 {
358 	return SetIconForType(type, NULL, data, dataSize);
359 }
360 
361 // SetIconForType
362 /*! \brief Sets the large or mini icon used by an application of this type for
363 	files of the given type.
364 
365 	The type of the \c BMimeType object is not required to actually be a subtype of
366 	\c "application/"; that is the intended use however, and application-specific
367 	icons are not expected to be present for non-application types.
368 
369 	The bitmap data pointed to by \c data must be of the proper size (\c 32x32
370 	for \c B_LARGE_ICON, \c 16x16 for \c B_MINI_ICON) and the proper color
371 	space (B_CMAP8).
372 
373 	\param type The MIME type
374 	\param fileType The MIME type whose custom icon you wish to set.
375 	\param data Pointer to an array of bitmap data of proper dimensions and color depth
376 	\param dataSize The length of the array pointed to by \c data
377 	\param size The size icon you're expecting (\c B_LARGE_ICON or \c B_MINI_ICON)
378 	\return
379 	- \c B_OK: Success
380 	- "error code": Failure
381 
382 */
383 status_t
384 Database::SetIconForType(const char *type, const char *fileType,
385 	const void *data, size_t dataSize, icon_size which)
386 {
387 	DBG(OUT("Database::SetIconForType()\n"));
388 
389 	if (type == NULL || data == NULL)
390 		return B_BAD_VALUE;
391 
392 	int32 attrType = 0;
393 
394 	// Figure out what kind of data we *should* have
395 	switch (which) {
396 		case B_MINI_ICON:
397 			attrType = kMiniIconType;
398 			break;
399 		case B_LARGE_ICON:
400 			attrType = kLargeIconType;
401 			break;
402 
403 		default:
404 			return B_BAD_VALUE;
405 	}
406 
407 	size_t attrSize = (size_t)which * (size_t)which;
408 	// Double check the data we've been given
409 	if (dataSize != attrSize)
410 		return B_BAD_VALUE;
411 
412 	// Construct our attribute name
413 	std::string attr;
414 	if (fileType) {
415 		attr = (which == B_MINI_ICON
416 			? kMiniIconAttrPrefix : kLargeIconAttrPrefix)
417 			+ BPrivate::Storage::to_lower(fileType);
418 	} else
419 		attr = which == B_MINI_ICON ? kMiniIconAttr : kLargeIconAttr;
420 
421 	// Write the icon data
422 	BNode node;
423 	bool didCreate = false;
424 
425 	status_t err = open_or_create_type(type, &node, &didCreate);
426 	if (!err && didCreate)
427 		_SendInstallNotification(type);
428 
429 	if (!err)
430 		err = node.WriteAttr(attr.c_str(), attrType, 0, data, attrSize);
431 	if (err >= 0)
432 		err = err == (ssize_t)attrSize ? (status_t)B_OK : (status_t)B_FILE_ERROR;
433 	if (!err) {
434 		if (fileType) {
435 			_SendMonitorUpdate(B_ICON_FOR_TYPE_CHANGED, type, fileType,
436 				(which == B_LARGE_ICON), B_META_MIME_MODIFIED);
437 		} else {
438 			_SendMonitorUpdate(B_ICON_CHANGED, type, (which == B_LARGE_ICON),
439 				B_META_MIME_MODIFIED);
440 		}
441 	}
442 	return err;
443 }
444 
445 // SetIconForType
446 /*! \brief Sets the vector icon used by an application of this type for
447 	files of the given type.
448 
449 	The type of the \c BMimeType object is not required to actually be a subtype of
450 	\c "application/"; that is the intended use however, and application-specific
451 	icons are not expected to be present for non-application types.
452 
453 	\param type The MIME type
454 	\param fileType The MIME type whose custom icon you wish to set.
455 	\param data Pointer to an array of vector data
456 	\param dataSize The length of the array pointed to by \c data
457 	\return
458 	- \c B_OK: Success
459 	- "error code": Failure
460 
461 */
462 status_t
463 Database::SetIconForType(const char *type, const char *fileType,
464 	const void *data, size_t dataSize)
465 {
466 	DBG(OUT("Database::SetIconForType()\n"));
467 
468 	if (type == NULL || data == NULL)
469 		return B_BAD_VALUE;
470 
471 	int32 attrType = B_RAW_TYPE;
472 
473 	// Construct our attribute name
474 	std::string attr;
475 	if (fileType) {
476 		attr = kIconAttrPrefix + BPrivate::Storage::to_lower(fileType);
477 	} else
478 		attr = kIconAttr;
479 
480 	// Write the icon data
481 	BNode node;
482 	bool didCreate = false;
483 
484 	status_t err = open_or_create_type(type, &node, &didCreate);
485 	if (!err && didCreate)
486 		_SendInstallNotification(type);
487 
488 	if (!err)
489 		err = node.WriteAttr(attr.c_str(), attrType, 0, data, dataSize);
490 	if (err >= 0)
491 		err = err == (ssize_t)dataSize ? (status_t)B_OK : (status_t)B_FILE_ERROR;
492 	if (!err) {
493 		// TODO: extra notification for vector icons (currently
494 		// passing "true" for B_LARGE_ICON)?
495 		if (fileType) {
496 			_SendMonitorUpdate(B_ICON_FOR_TYPE_CHANGED, type, fileType,
497 				true, B_META_MIME_MODIFIED);
498 		} else {
499 			_SendMonitorUpdate(B_ICON_CHANGED, type, true,
500 				B_META_MIME_MODIFIED);
501 		}
502 	}
503 	return err;
504 }
505 
506 // SetPreferredApp
507 /*!	\brief Sets the signature of the preferred application for the given app verb
508 
509 	Currently, the only supported app verb is \c B_OPEN
510 	\param type Pointer to a NULL-terminated string containing the MIME type of interest
511 	\param signature Pointer to a NULL-terminated string containing the MIME signature
512 	                 of the new preferred application
513 	\param verb \c app_verb action for which the new preferred application is applicable
514 */
515 status_t
516 Database::SetPreferredApp(const char *type, const char *signature, app_verb verb)
517 {
518 	DBG(OUT("Database::SetPreferredApp()\n"));
519 
520 	// TODO: use "verb" some day!
521 
522 	return _SetStringValue(type, B_PREFERRED_APP_CHANGED, kPreferredAppAttr,
523 		kPreferredAppType, B_MIME_TYPE_LENGTH, signature);
524 }
525 
526 // SetSnifferRule
527 /*! \brief Sets the mime sniffer rule for the given mime type
528 */
529 status_t
530 Database::SetSnifferRule(const char *type, const char *rule)
531 {
532 	DBG(OUT("Database::SetSnifferRule()\n"));
533 
534 	if (type == NULL || rule == NULL)
535 		return B_BAD_VALUE;
536 
537 	bool didCreate = false;
538 	status_t status = write_mime_attr(type, kSnifferRuleAttr, rule, strlen(rule) + 1,
539 		kSnifferRuleType, &didCreate);
540 
541 	if (status == B_OK)
542 		status = fSnifferRules.SetSnifferRule(type, rule);
543 
544 	if (status == B_OK) {
545 		if (didCreate)
546 			_SendInstallNotification(type);
547 		_SendMonitorUpdate(B_SNIFFER_RULE_CHANGED, type, B_META_MIME_MODIFIED);
548 	}
549 
550 	return status;
551 }
552 
553 // SetSupportedTypes
554 /*!	\brief Sets the list of MIME types supported by the MIME type and
555 	syncs the internal supporting apps database either partially or
556 	completely.
557 
558 	Please see BMimeType::SetSupportedTypes() for details.
559 	\param type The mime type of interest
560 	\param types The supported types to be assigned to the file.
561 	\param syncAll \c true to also synchronize the previously supported
562 		   types, \c false otherwise.
563 	\return
564 	- \c B_OK: success
565 	- other error codes: failure
566 */
567 status_t
568 Database::SetSupportedTypes(const char *type, const BMessage *types, bool fullSync)
569 {
570 	DBG(OUT("Database::SetSupportedTypes()\n"));
571 
572 	if (type == NULL || types == NULL)
573 		return B_BAD_VALUE;
574 
575 	// Install the types
576 	const char *supportedType;
577 	for (int32 i = 0; types->FindString("types", i, &supportedType) == B_OK; i++) {
578 		if (!is_installed(supportedType)) {
579 			if (Install(supportedType) != B_OK)
580 				break;
581 
582 			// Since the type has been introduced by this application
583 			// we take the liberty and make it the preferred handler
584 			// for them, too.
585 			SetPreferredApp(supportedType, type, B_OPEN);
586 		}
587 	}
588 
589 	// Write the attr
590 	bool didCreate = false;
591 	status_t status = write_mime_attr_message(type, kSupportedTypesAttr, types,
592 		&didCreate);
593 
594 	// Notify the monitor if we created the type when we opened it
595 	if (status == B_OK && didCreate)
596 		_SendInstallNotification(type);
597 
598 	// Update the supporting apps map
599 	if (status == B_OK)
600 		status = fSupportingApps.SetSupportedTypes(type, types, fullSync);
601 
602 	// Notify the monitor
603 	if (status == B_OK)
604 		_SendMonitorUpdate(B_SUPPORTED_TYPES_CHANGED, type, B_META_MIME_MODIFIED);
605 
606 	return status;
607 }
608 
609 
610 // GetInstalledSupertypes
611 /*! \brief Fetches a BMessage listing all the MIME supertypes currently
612 	installed in the MIME database.
613 
614 	The types are copied into the \c "super_types" field of the passed-in \c BMessage.
615 	The \c BMessage must be pre-allocated.
616 
617 	\param super_types Pointer to a pre-allocated \c BMessage into which the
618 	                   MIME supertypes will be copied.
619 	\return
620 	- \c B_OK: Success
621 	- "error code": Failure
622 */
623 status_t
624 Database::GetInstalledSupertypes(BMessage *supertypes)
625 {
626 	return fInstalledTypes.GetInstalledSupertypes(supertypes);
627 }
628 
629 // GetInstalledTypes
630 /*! \brief Fetches a BMessage listing all the MIME types currently installed
631 	in the MIME database.
632 
633 	The types are copied into the \c "types" field of the passed-in \c BMessage.
634 	The \c BMessage must be pre-allocated.
635 
636 	\param types Pointer to a pre-allocated \c BMessage into which the
637 	             MIME types will be copied.
638 	\return
639 	- \c B_OK: Success
640 	- "error code": Failure
641 */
642 status_t
643 Database::GetInstalledTypes(BMessage *types)
644 {
645 	return fInstalledTypes.GetInstalledTypes(types);
646 }
647 
648 // GetInstalledTypes
649 /*! \brief Fetches a BMessage listing all the MIME subtypes of the given
650 	supertype currently installed in the MIME database.
651 
652 	The types are copied into the \c "types" field of the passed-in \c BMessage.
653 	The \c BMessage must be pre-allocated.
654 
655 	\param super_type Pointer to a string containing the MIME supertype whose
656 	                  subtypes you wish to retrieve.
657 	\param subtypes Pointer to a pre-allocated \c BMessage into which the appropriate
658 	                MIME subtypes will be copied.
659 	\return
660 	- \c B_OK: Success
661 	- "error code": Failure
662 */
663 status_t
664 Database::GetInstalledTypes(const char *supertype, BMessage *subtypes)
665 {
666 	return fInstalledTypes.GetInstalledTypes(supertype, subtypes);
667 }
668 
669 // GetSupportingApps
670 /*! \brief Fetches a \c BMessage containing a list of MIME signatures of
671 	applications that are able to handle files of this MIME type.
672 
673 	Please see BMimeType::GetSupportingApps() for more details.
674 */
675 status_t
676 Database::GetSupportingApps(const char *type, BMessage *signatures)
677 {
678 	return fSupportingApps.GetSupportingApps(type, signatures);
679 }
680 
681 // GetAssociatedTypes
682 /*! \brief Returns a list of mime types associated with the given file extension
683 
684 	Please see BMimeType::GetAssociatedTypes() for more details.
685 */
686 status_t
687 Database::GetAssociatedTypes(const char *extension, BMessage *types)
688 {
689 	return B_ERROR;
690 }
691 
692 // GuessMimeType
693 /*!	\brief Guesses a MIME type for the entry referred to by the given
694 	\c entry_ref.
695 
696 	This version of GuessMimeType() combines the features of the other
697 	versions, plus adds a few tricks of its own:
698 	- If the entry is a meta mime entry (i.e. has a \c "META:TYPE" attribute),
699 	  the type returned is \c "application/x-vnd.be-meta-mime".
700 	- If the entry is a directory, the type returned is
701 	  \c "application/x-vnd.be-directory".
702 	- If the entry is a symlink, the type returned is
703 	  \c "application/x-vnd.be-symlink".
704 	- If the entry is a regular file, the file data is sniffed and, the
705 	  type returned is the mime type with the matching rule of highest
706 	  priority.
707 	- If sniffing fails, the filename is checked for known extensions.
708 	- If the extension check fails, the type returned is
709 	  \c "application/octet-stream".
710 
711 	\param ref Pointer to the entry_ref referring to the entry.
712 	\param type Pointer to a pre-allocated BString which is set to the
713 		   resulting MIME type.
714 	\return
715 	- \c B_OK: success (even if the guess returned is "application/octet-stream")
716 	- other error code: failure
717 */
718 status_t
719 Database::GuessMimeType(const entry_ref *ref, BString *result)
720 {
721 	if (ref == NULL || result == NULL)
722 		return B_BAD_VALUE;
723 
724 	BNode node;
725 	struct stat statData;
726 	status_t status = node.SetTo(ref);
727 	if (status < B_OK)
728 		return status;
729 
730 	attr_info info;
731 	if (node.GetAttrInfo(kTypeAttr, &info) == B_OK) {
732 		// Check for a META:TYPE attribute
733 		result->SetTo(kMetaMimeType);
734 		return B_OK;
735 	}
736 
737 	// See if we have a directory, a symlink, or a vanilla file
738 	status = node.GetStat(&statData);
739 	if (status < B_OK)
740 		return status;
741 
742 	if (S_ISDIR(statData.st_mode)) {
743 		// Directory
744 		result->SetTo(kDirectoryType);
745 	} else if (S_ISLNK(statData.st_mode)) {
746 		// Symlink
747 		result->SetTo(kSymlinkType);
748 	} else if (S_ISREG(statData.st_mode)) {
749 		// Vanilla file: sniff first
750 		status = fSnifferRules.GuessMimeType(ref, result);
751 
752 		// If that fails, check extensions
753 		if (status == kMimeGuessFailureError)
754 			status = fAssociatedTypes.GuessMimeType(ref, result);
755 
756 		// If that fails, return the generic file type
757 		if (status == kMimeGuessFailureError) {
758 			result->SetTo(kGenericFileType);
759 			status = B_OK;
760 		}
761 	} else {
762 		// TODO: we could filter out devices, ...
763 		return B_BAD_TYPE;
764 	}
765 
766 	return status;
767 }
768 
769 // GuessMimeType
770 /*!	\brief Guesses a MIME type for the supplied chunk of data.
771 
772 	See \c SnifferRules::GuessMimeType(BPositionIO*, BString*)
773 	for more details.
774 
775 	\param buffer Pointer to the data buffer.
776 	\param length Size of the buffer in bytes.
777 	\param type Pointer to a pre-allocated BString which is set to the
778 		   resulting MIME type.
779 	\return
780 	- \c B_OK: success
781 	- error code: failure
782 */
783 status_t
784 Database::GuessMimeType(const void *buffer, int32 length, BString *result)
785 {
786 	if (buffer == NULL || result == NULL)
787 		return B_BAD_VALUE;
788 
789 	status_t status = fSnifferRules.GuessMimeType(buffer, length, result);
790 	if (status == kMimeGuessFailureError) {
791 		result->SetTo(kGenericFileType);
792 		return B_OK;
793 	}
794 
795 	return status;
796 }
797 
798 // GuessMimeType
799 /*!	\brief Guesses a MIME type for the given filename.
800 
801 	Only the filename itself is taken into consideration (in particular its
802 	name extension), not the entry or corresponding data it refers to (in fact,
803 	an entry with that name need not exist at all.
804 
805 	\param filename The filename.
806 	\param type Pointer to a pre-allocated BString which is set to the
807 		   resulting MIME type.
808 	\return
809 	- \c B_OK: success
810 	- error code: failure
811 */
812 status_t
813 Database::GuessMimeType(const char *filename, BString *result)
814 {
815 	if (filename == NULL || result == NULL)
816 		return B_BAD_VALUE;
817 
818 	status_t status = fAssociatedTypes.GuessMimeType(filename, result);
819 	if (status == kMimeGuessFailureError) {
820 		result->SetTo(kGenericFileType);
821 		return B_OK;
822 	}
823 
824 	return status;
825 }
826 
827 
828 /*!	\brief Subscribes the given BMessenger to the MIME monitor service
829 
830 	Notification messages will be sent with a \c BMessage::what value
831 	of \c B_META_MIME_CHANGED. Notification messages have the following
832 	fields:
833 
834 	<table>
835 		<tr>
836 			<td> Name </td>
837 			<td> Type </td>
838 			<td> Description </td>
839 		</tr>
840 		<tr>
841 			<td> \c be:type </td>
842 			<td> \c B_STRING_TYPE </td>
843 			<td> The MIME type that was changed </td>
844 		</tr>
845 		<tr>
846 			<td> \c be:which </td>
847 			<td> \c B_INT32_TYPE </td>
848 			<td> Bitmask describing which attributes were changed (see below) </td>
849 		</tr>
850 		<tr>
851 			<td> \c be:extra_type </td>
852 			<td> \c B_STRING_TYPE </td>
853 			<td> Additional MIME type string (applicable to B_ICON_FOR_TYPE_CHANGED notifications only)</td>
854 		</tr>
855 		<tr>
856 			<td> \c be:large_icon </td>
857 			<td> \c B_BOOL_TYPE </td>
858 			<td> \c true if the large icon was changed, \c false if the small icon
859 			     was changed (applicable to B_ICON_[FOR_TYPE_]CHANGED updates only) </td>
860 		</tr>
861 	</table>
862 
863 	The \c be:which field of the message describes which attributes were updated, and
864 	may be the bitwise \c OR of any of the following values:
865 
866 	<table>
867 		<tr>
868 			<td> Value </td>
869 			<td> Triggered By </td>
870 		</tr>
871 		<tr>
872 			<td> \c B_ICON_CHANGED </td>
873 			<td> \c BMimeType::SetIcon() </td>
874 		</tr>
875 		<tr>
876 			<td> \c B_PREFERRED_APP_CHANGED </td>
877 			<td> \c BMimeType::SetPreferredApp() </td>
878 		</tr>
879 		<tr>
880 			<td> \c B_ATTR_INFO_CHANGED </td>
881 			<td> \c BMimeType::SetAttrInfo() </td>
882 		</tr>
883 		<tr>
884 			<td> \c B_FILE_EXTENSIONS_CHANGED </td>
885 			<td> \c BMimeType::SetFileExtensions() </td>
886 		</tr>
887 		<tr>
888 			<td> \c B_SHORT_DESCRIPTION_CHANGED </td>
889 			<td> \c BMimeType::SetShortDescription() </td>
890 		</tr>
891 		<tr>
892 			<td> \c B_LONG_DESCRIPTION_CHANGED </td>
893 			<td> \c BMimeType::SetLongDescription() </td>
894 		</tr>
895 		<tr>
896 			<td> \c B_ICON_FOR_TYPE_CHANGED </td>
897 			<td> \c BMimeType::SetIconForType() </td>
898 		</tr>
899 		<tr>
900 			<td> \c B_APP_HINT_CHANGED </td>
901 			<td> \c BMimeType::SetAppHint() </td>
902 		</tr>
903 	</table>
904 
905 	\param target The \c BMessenger to subscribe to the MIME monitor service
906 */
907 status_t
908 Database::StartWatching(BMessenger target)
909 {
910 	DBG(OUT("Database::StartWatching()\n"));
911 
912 	if (!target.IsValid())
913 		return B_BAD_VALUE;
914 
915 	fMonitorMessengers.insert(target);
916 	return B_OK;
917 }
918 
919 
920 /*!
921 	Unsubscribes the given BMessenger from the MIME monitor service
922 	\param target The \c BMessenger to unsubscribe
923 */
924 status_t
925 Database::StopWatching(BMessenger target)
926 {
927 	DBG(OUT("Database::StopWatching()\n"));
928 
929 	if (!target.IsValid())
930 		return B_BAD_VALUE;
931 
932 	status_t status = fMonitorMessengers.find(target) != fMonitorMessengers.end()
933 		? (status_t)B_OK : (status_t)B_ENTRY_NOT_FOUND;
934 	if (status == B_OK)
935 		fMonitorMessengers.erase(target);
936 
937 	return status;
938 }
939 
940 
941 /*!	\brief Deletes the app hint attribute for the given type
942 
943 	A \c B_APP_HINT_CHANGED notification is sent to the mime monitor service.
944 	\param type The mime type of interest
945 	\return
946 	- B_OK: success
947 	- B_ENTRY_NOT_FOUND: no such attribute existed
948 	- "error code": failure
949 */
950 status_t
951 Database::DeleteAppHint(const char *type)
952 {
953 	status_t status = delete_attribute(type, kAppHintAttr);
954 	if (status == B_OK)
955 		_SendMonitorUpdate(B_APP_HINT_CHANGED, type, B_META_MIME_DELETED);
956 
957 	return status;
958 }
959 
960 
961 /*!	\brief Deletes the attribute info attribute for the given type
962 
963 	A \c B_ATTR_INFO_CHANGED notification is sent to the mime monitor service.
964 	\param type The mime type of interest
965 	\return
966 	- B_OK: success
967 	- B_ENTRY_NOT_FOUND: no such attribute existed
968 	- "error code": failure
969 */
970 status_t
971 Database::DeleteAttrInfo(const char *type)
972 {
973 	status_t status = delete_attribute(type, kAttrInfoAttr);
974 	if (status == B_OK)
975 		_SendMonitorUpdate(B_ATTR_INFO_CHANGED, type, B_META_MIME_DELETED);
976 
977 	return status;
978 }
979 
980 
981 /*!	\brief Deletes the short description attribute for the given type
982 
983 	A \c B_SHORT_DESCRIPTION_CHANGED notification is sent to the mime monitor service.
984 	\param type The mime type of interest
985 	\return
986 	- B_OK: success
987 	- B_ENTRY_NOT_FOUND: no such attribute existed
988 	- "error code": failure
989 */
990 status_t
991 Database::DeleteShortDescription(const char *type)
992 {
993 	status_t status = delete_attribute(type, kShortDescriptionAttr);
994 	if (status == B_OK)
995 		_SendMonitorUpdate(B_SHORT_DESCRIPTION_CHANGED, type, B_META_MIME_DELETED);
996 
997 	return status;
998 }
999 
1000 
1001 /*!	\brief Deletes the long description attribute for the given type
1002 
1003 	A \c B_LONG_DESCRIPTION_CHANGED notification is sent to the mime monitor service.
1004 	\param type The mime type of interest
1005 	\return
1006 	- B_OK: success
1007 	- B_ENTRY_NOT_FOUND: no such attribute existed
1008 	- "error code": failure
1009 */
1010 status_t
1011 Database::DeleteLongDescription(const char *type)
1012 {
1013 	status_t status = delete_attribute(type, kLongDescriptionAttr);
1014 	if (status == B_OK)
1015 		_SendMonitorUpdate(B_LONG_DESCRIPTION_CHANGED, type, B_META_MIME_DELETED);
1016 
1017 	return status;
1018 }
1019 
1020 
1021 /*!	\brief Deletes the associated file extensions attribute for the given type
1022 
1023 	A \c B_FILE_EXTENSIONS_CHANGED notification is sent to the mime monitor service.
1024 	\param type The mime type of interest
1025 	\return
1026 	- B_OK: success
1027 	- B_ENTRY_NOT_FOUND: no such attribute existed
1028 	- "error code": failure
1029 */
1030 status_t
1031 Database::DeleteFileExtensions(const char *type)
1032 {
1033 	status_t status = delete_attribute(type, kFileExtensionsAttr);
1034 	if (status == B_OK)
1035 		_SendMonitorUpdate(B_FILE_EXTENSIONS_CHANGED, type, B_META_MIME_DELETED);
1036 
1037 	return status;
1038 }
1039 
1040 
1041 /*!	\brief Deletes the icon of the given size for the given type
1042 
1043 	A \c B_ICON_CHANGED notification is sent to the mime monitor service.
1044 	\param type The mime type of interest
1045 	\param which The icon size of interest
1046 	\return
1047 	- B_OK: success
1048 	- B_ENTRY_NOT_FOUND: no such attribute existed
1049 	- "error code": failure
1050 */
1051 status_t
1052 Database::DeleteIcon(const char *type, icon_size which)
1053 {
1054 	const char *attr = which == B_MINI_ICON ? kMiniIconAttr : kLargeIconAttr;
1055 	status_t status = delete_attribute(type, attr);
1056 	if (status == B_OK)
1057 		_SendMonitorUpdate(B_ICON_CHANGED, type, which, B_META_MIME_DELETED);
1058 
1059 	return status;
1060 }
1061 
1062 
1063 /*!	\brief Deletes the vector icon for the given type
1064 
1065 	A \c B_ICON_CHANGED notification is sent to the mime monitor service.
1066 	\param type The mime type of interest
1067 	\return
1068 	- B_OK: success
1069 	- B_ENTRY_NOT_FOUND: no such attribute existed
1070 	- "error code": failure
1071 */
1072 status_t
1073 Database::DeleteIcon(const char *type)
1074 {
1075 	// TODO: exta notification for vector icon (uses B_LARGE_ICON now)
1076 	status_t status = delete_attribute(type, kIconAttr);
1077 	if (status == B_OK)
1078 		_SendMonitorUpdate(B_ICON_CHANGED, type, B_LARGE_ICON,
1079 						   B_META_MIME_DELETED);
1080 
1081 	return status;
1082 }
1083 
1084 
1085 /*!	\brief Deletes the icon of the given size associated with the given file
1086 		type for the given application signature.
1087 
1088     (If this function seems confusing, please see BMimeType::GetIconForType() for a
1089     better description of what the *IconForType() functions are used for.)
1090 
1091 	A \c B_ICON_FOR_TYPE_CHANGED notification is sent to the mime monitor service.
1092 	\param type The mime type of the application whose custom icon you are deleting.
1093 	\param fileType The mime type for which you no longer wish \c type to have a custom icon.
1094 	\param which The icon size of interest
1095 	\return
1096 	- B_OK: success
1097 	- B_ENTRY_NOT_FOUND: no such attribute existed
1098 	- "error code": failure
1099 */
1100 status_t
1101 Database::DeleteIconForType(const char *type, const char *fileType, icon_size which)
1102 {
1103 	if (fileType == NULL)
1104 		return B_BAD_VALUE;
1105 
1106 	std::string attr = (which == B_MINI_ICON
1107 		? kMiniIconAttrPrefix : kLargeIconAttrPrefix) + BPrivate::Storage::to_lower(fileType);
1108 
1109 	status_t status = delete_attribute(type, attr.c_str());
1110 	if (status == B_OK) {
1111 		_SendMonitorUpdate(B_ICON_FOR_TYPE_CHANGED, type, fileType,
1112 			which == B_LARGE_ICON, B_META_MIME_DELETED);
1113 	}
1114 
1115 	return status;
1116 }
1117 
1118 
1119 /*!	\brief Deletes the vector icon associated with the given file
1120 		type for the given application signature.
1121 
1122     (If this function seems confusing, please see BMimeType::GetIconForType() for a
1123     better description of what the *IconForType() functions are used for.)
1124 
1125 	A \c B_ICON_FOR_TYPE_CHANGED notification is sent to the mime monitor service.
1126 	\param type The mime type of the application whose custom icon you are deleting.
1127 	\param fileType The mime type for which you no longer wish \c type to have a custom icon.
1128 	\return
1129 	- B_OK: success
1130 	- B_ENTRY_NOT_FOUND: no such attribute existed
1131 	- "error code": failure
1132 */
1133 status_t
1134 Database::DeleteIconForType(const char *type, const char *fileType)
1135 {
1136 	if (fileType == NULL)
1137 		return B_BAD_VALUE;
1138 
1139 	std::string attr = kIconAttrPrefix + BPrivate::Storage::to_lower(fileType);
1140 
1141 	// TODO: introduce extra notification for vector icons?
1142 	// (uses B_LARGE_ICON now)
1143 	status_t status = delete_attribute(type, attr.c_str());
1144 	if (status == B_OK) {
1145 		_SendMonitorUpdate(B_ICON_FOR_TYPE_CHANGED, type, fileType,
1146 			true, B_META_MIME_DELETED);
1147 	}
1148 
1149 	return status;
1150 }
1151 
1152 
1153 // DeletePreferredApp
1154 //! Deletes the preferred app for the given app verb for the given type
1155 /*! A \c B_PREFERRED_APP_CHANGED notification is sent to the mime monitor service.
1156 	\param type The mime type of interest
1157 	\param which The app verb of interest
1158 	\return
1159 	- B_OK: success
1160 	- B_ENTRY_NOT_FOUND: no such attribute existed
1161 	- "error code": failure
1162 */
1163 status_t
1164 Database::DeletePreferredApp(const char *type, app_verb verb)
1165 {
1166 	status_t status;
1167 
1168 	switch (verb) {
1169 		case B_OPEN:
1170 			status = delete_attribute(type, kPreferredAppAttr);
1171 			break;
1172 
1173 		default:
1174 			return B_BAD_VALUE;
1175 	}
1176 
1177 	/*! \todo The R5 monitor makes no note of which app_verb value was updated. If
1178 		additional app_verb values besides \c B_OPEN are someday added, the format
1179 		of the MIME monitor messages will need to be augmented.
1180 	*/
1181 	if (status == B_OK)
1182 		_SendMonitorUpdate(B_PREFERRED_APP_CHANGED, type, B_META_MIME_DELETED);
1183 
1184 	return status;
1185 }
1186 
1187 // DeleteSnifferRule
1188 //! Deletes the sniffer rule for the given type
1189 /*! A \c B_SNIFFER_RULE_CHANGED notification is sent to the mime monitor service,
1190 	and the corresponding rule is removed from the internal database of sniffer
1191 	rules.
1192 	\param type The mime type of interest
1193 	\return
1194 	- B_OK: success
1195 	- B_ENTRY_NOT_FOUND: no such attribute existed
1196 	- "error code": failure
1197 */
1198 status_t
1199 Database::DeleteSnifferRule(const char *type)
1200 {
1201 	status_t status = delete_attribute(type, kSnifferRuleAttr);
1202 	if (status == B_OK)
1203 		status = fSnifferRules.DeleteSnifferRule(type);
1204 	if (status == B_OK)
1205 		_SendMonitorUpdate(B_SNIFFER_RULE_CHANGED, type, B_META_MIME_DELETED);
1206 
1207 	return status;
1208 }
1209 
1210 // DeleteSupportedTypes
1211 //! Deletes the supported types list for the given type
1212 /*! A \c B_SUPPORTED_TYPES_CHANGED notification is sent to the mime monitor service.
1213 	If \c fullSync is \c true, the given type is removed from the internal list
1214 	of supporting applictions for each previously supported type. If \c fullSync
1215 	is \c false, the said removal will occur the next time SetSupportedTypes() or
1216 	DeleteSupportedTypes() is called with a \c true \c fullSync paramter, or
1217 	\c Delete() is called for the given type.
1218 	\param type The mime type of interest
1219 	\param fullSync Whether or not to remove the type as a supporting app for
1220 	                all previously supported types
1221 	\return
1222 	- B_OK: success
1223 	- B_ENTRY_NOT_FOUND: no such attribute existed
1224 	- "error code": failure
1225 */
1226 status_t
1227 Database::DeleteSupportedTypes(const char *type, bool fullSync)
1228 {
1229 	status_t status = delete_attribute(type, kSupportedTypesAttr);
1230 
1231 	// Update the supporting apps database. If fullSync is specified,
1232 	// do so even if the supported types attribute didn't exist, as
1233 	// stranded types *may* exist in the database due to previous
1234 	// calls to {Set,Delete}SupportedTypes() with fullSync == false.
1235 	if (status == B_OK)
1236 		status = fSupportingApps.DeleteSupportedTypes(type, fullSync);
1237 	else if (fullSync && status == B_ENTRY_NOT_FOUND)
1238 		fSupportingApps.DeleteSupportedTypes(type, fullSync);
1239 
1240 	// Send a monitor notification
1241 	if (status == B_OK)
1242 		_SendMonitorUpdate(B_SUPPORTED_TYPES_CHANGED, type, B_META_MIME_DELETED);
1243 
1244 	return status;
1245 }
1246 
1247 
1248 //! \brief Sends a \c B_MIME_TYPE_CREATED notification to the mime monitor service
1249 status_t
1250 Database::_SendInstallNotification(const char *type)
1251 {
1252 	return _SendMonitorUpdate(B_MIME_TYPE_CREATED, type, B_META_MIME_MODIFIED);
1253 }
1254 
1255 
1256 //! \brief Sends a \c B_MIME_TYPE_DELETED notification to the mime monitor service
1257 status_t
1258 Database::_SendDeleteNotification(const char *type)
1259 {
1260 	// Tell the backend first
1261 	return _SendMonitorUpdate(B_MIME_TYPE_DELETED, type, B_META_MIME_MODIFIED);
1262 }
1263 
1264 // _SendMonitorUpdate
1265 /*! \brief Sends an update notification to all BMessengers that have
1266 	subscribed to the MIME Monitor service
1267 	\param type The MIME type that was updated
1268 	\param which Bitmask describing which attribute was updated
1269 	\param extraType The MIME type to which the change is applies
1270 	\param largeIcon \true if the the large icon was updated, \false if the
1271 		   small icon was updated
1272 */
1273 status_t
1274 Database::_SendMonitorUpdate(int32 which, const char *type, const char *extraType,
1275 	bool largeIcon, int32 action)
1276 {
1277 	BMessage msg(B_META_MIME_CHANGED);
1278 	status_t err;
1279 
1280 	err = msg.AddInt32("be:which", which);
1281 	if (!err)
1282 		err = msg.AddString("be:type", type);
1283 	if (!err)
1284 		err = msg.AddString("be:extra_type", extraType);
1285 	if (!err)
1286 		err = msg.AddBool("be:large_icon", largeIcon);
1287 	if (!err)
1288 		err = msg.AddInt32("be:action", action);
1289 	if (!err)
1290 		err = _SendMonitorUpdate(msg);
1291 	return err;
1292 }
1293 
1294 // _SendMonitorUpdate
1295 /*! \brief Sends an update notification to all BMessengers that have
1296 	subscribed to the MIME Monitor service
1297 	\param type The MIME type that was updated
1298 	\param which Bitmask describing which attribute was updated
1299 	\param extraType The MIME type to which the change is applies
1300 */
1301 status_t
1302 Database::_SendMonitorUpdate(int32 which, const char *type, const char *extraType,
1303 	int32 action)
1304 {
1305 	BMessage msg(B_META_MIME_CHANGED);
1306 
1307 	status_t err = msg.AddInt32("be:which", which);
1308 	if (!err)
1309 		err = msg.AddString("be:type", type);
1310 	if (!err)
1311 		err = msg.AddString("be:extra_type", extraType);
1312 	if (!err)
1313 		err = msg.AddInt32("be:action", action);
1314 	if (!err)
1315 		err = _SendMonitorUpdate(msg);
1316 	return err;
1317 }
1318 
1319 // _SendMonitorUpdate
1320 /*! \brief Sends an update notification to all BMessengers that have
1321 	subscribed to the MIME Monitor service
1322 	\param type The MIME type that was updated
1323 	\param which Bitmask describing which attribute was updated
1324 	\param largeIcon \true if the the large icon was updated, \false if the
1325 		   small icon was updated
1326 */
1327 status_t
1328 Database::_SendMonitorUpdate(int32 which, const char *type, bool largeIcon, int32 action)
1329 {
1330 	BMessage msg(B_META_MIME_CHANGED);
1331 
1332 	status_t err = msg.AddInt32("be:which", which);
1333 	if (!err)
1334 		err = msg.AddString("be:type", type);
1335 	if (!err)
1336 		err = msg.AddBool("be:large_icon", largeIcon);
1337 	if (!err)
1338 		err = msg.AddInt32("be:action", action);
1339 	if (!err)
1340 		err = _SendMonitorUpdate(msg);
1341 	return err;
1342 }
1343 
1344 // _SendMonitorUpdate
1345 /*! \brief Sends an update notification to all BMessengers that have
1346 	subscribed to the MIME Monitor service
1347 	\param type The MIME type that was updated
1348 	\param which Bitmask describing which attribute was updated
1349 */
1350 status_t
1351 Database::_SendMonitorUpdate(int32 which, const char *type, int32 action)
1352 {
1353 	BMessage msg(B_META_MIME_CHANGED);
1354 
1355 	status_t err = msg.AddInt32("be:which", which);
1356 	if (!err)
1357 		err = msg.AddString("be:type", type);
1358 	if (!err)
1359 		err = msg.AddInt32("be:action", action);
1360 	if (!err)
1361 		err = _SendMonitorUpdate(msg);
1362 	return err;
1363 }
1364 
1365 // _SendMonitorUpdate
1366 /*! \brief Sends an update notification to all BMessengers that have subscribed to
1367 	the MIME Monitor service
1368 	\param BMessage A preformatted MIME monitor message to be sent to all subscribers
1369 */
1370 status_t
1371 Database::_SendMonitorUpdate(BMessage &msg)
1372 {
1373 //	DBG(OUT("Database::_SendMonitorUpdate(BMessage&)\n"));
1374 	status_t err;
1375 	std::set<BMessenger>::const_iterator i;
1376 	for (i = fMonitorMessengers.begin(); i != fMonitorMessengers.end(); i++) {
1377 		status_t err = (*i).SendMessage(&msg, (BHandler*)NULL);
1378 		if (err) {
1379 			DBG(OUT("Database::_SendMonitorUpdate(BMessage&): BMessenger::SendMessage failed, 0x%lx\n", err));
1380 		}
1381 	}
1382 //	DBG(OUT("Database::_SendMonitorUpdate(BMessage&) done\n"));
1383 	err = B_OK;
1384 	return err;
1385 }
1386 
1387 } // namespace Mime
1388 } // namespace Storage
1389 } // namespace BPrivate
1390 
1391