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