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