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