xref: /haiku/src/kits/storage/mime/Database.cpp (revision 4a32f48e70297d9a634646f01e08c2f451ecd6bd)
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 
43 namespace BPrivate {
44 namespace Storage {
45 namespace Mime {
46 
47 
48 Database::NotificationListener::~NotificationListener()
49 {
50 }
51 
52 
53 /*!
54 	\class Database
55 	\brief Mime::Database is the master of the MIME data base.
56 
57 	All write and non-atomic read accesses are carried out by this class.
58 
59 	\note No error checking (other than checks for NULL pointers) is performed
60 	      by this class on the mime type strings passed to it. It's assumed
61 	      that this sort of checking has been done beforehand.
62 */
63 
64 // constructor
65 /*!	\brief Creates and initializes a Mime::Database object.
66 */
67 Database::Database(DatabaseLocation* databaseLocation, MimeSniffer* mimeSniffer,
68 	NotificationListener* notificationListener)
69 	:
70 	fStatus(B_NO_INIT),
71 	fLocation(databaseLocation),
72 	fNotificationListener(notificationListener),
73 	fAssociatedTypes(databaseLocation, mimeSniffer),
74 	fInstalledTypes(databaseLocation),
75 	fSnifferRules(databaseLocation, mimeSniffer),
76 	fSupportingApps(databaseLocation),
77 	fDeferredInstallNotificationsLocker("deferred install notifications"),
78 	fDeferredInstallNotifications()
79 {
80 	// make sure the user's MIME DB directory exists
81 	fStatus = create_directory(fLocation->WritableDirectory(),
82 		S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
83 }
84 
85 // destructor
86 /*!	\brief Frees all resources associated with this object.
87 */
88 Database::~Database()
89 {
90 }
91 
92 // InitCheck
93 /*! \brief Returns the initialization status of the object.
94 	\return
95 	- B_OK: success
96 	- "error code": failure
97 */
98 status_t
99 Database::InitCheck() const
100 {
101 	return fStatus;
102 }
103 
104 // Install
105 /*!	\brief Installs the given type in the database
106 	\note The R5 version of this call returned an unreliable result if the
107 	      MIME type was already installed. Ours simply returns B_OK.
108 	\param type Pointer to a NULL-terminated string containing the MIME type of interest
109 	\param decsription Pointer to a NULL-terminated string containing the new long description
110 	\return
111 	- B_OK: success
112 	- B_FILE_EXISTS: the type is already installed
113 	- "error code": failure
114 */
115 status_t
116 Database::Install(const char *type)
117 {
118 	if (type == NULL)
119 		return B_BAD_VALUE;
120 
121 	BEntry entry;
122 	status_t err = entry.SetTo(fLocation->WritablePathForType(type));
123 	if (err == B_OK || err == B_ENTRY_NOT_FOUND) {
124 		if (entry.Exists())
125 			err = B_FILE_EXISTS;
126 		else {
127 			bool didCreate = false;
128 			BNode node;
129 			err = fLocation->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 */
146 status_t
147 Database::Delete(const char *type)
148 {
149 	if (type == NULL)
150 		return B_BAD_VALUE;
151 
152 	// Open the type
153 	BEntry entry;
154 	status_t status = entry.SetTo(fLocation->WritablePathForType(type));
155 	if (status != B_OK)
156 		return status;
157 
158 	// Remove it
159 	if (entry.IsDirectory()) {
160 		// We need to remove all files in this directory
161 		BDirectory directory(&entry);
162 		if (directory.InitCheck() == B_OK) {
163 			size_t length = strlen(type);
164 			char subType[B_PATH_NAME_LENGTH];
165 			memcpy(subType, type, length);
166 			subType[length++] = '/';
167 
168 			BEntry subEntry;
169 			while (directory.GetNextEntry(&subEntry) == B_OK) {
170 				// Construct MIME type and remove it
171 				if (subEntry.GetName(subType + length) == B_OK) {
172 					status = Delete(subType);
173 					if (status != B_OK)
174 						return status;
175 				}
176 			}
177 		}
178 	}
179 
180 	status = entry.Remove();
181 
182 	if (status == B_OK) {
183 		// Notify the installed types database
184 		fInstalledTypes.RemoveType(type);
185 		// Notify the supporting apps database
186 		fSupportingApps.DeleteSupportedTypes(type, true);
187 		// Notify the monitor service
188 		_SendDeleteNotification(type);
189 	}
190 
191 	return status;
192 }
193 
194 
195 status_t
196 Database::_SetStringValue(const char *type, int32 what, const char* attribute,
197 	type_code attributeType, size_t maxLength, const char *value)
198 {
199 	size_t length = value != NULL ? strlen(value) : 0;
200 	if (type == NULL || value == NULL || length >= maxLength)
201 		return B_BAD_VALUE;
202 
203 	char oldValue[maxLength];
204 	status_t status = fLocation->ReadAttribute(type, attribute, oldValue,
205 		maxLength, attributeType);
206 	if (status >= B_OK && !strcmp(value, oldValue)) {
207 		// nothing has changed, no need to write back the data
208 		return B_OK;
209 	}
210 
211 	bool didCreate = false;
212 	status = fLocation->WriteAttribute(type, attribute, value, length + 1,
213 		attributeType, &didCreate);
214 
215 	if (status == B_OK) {
216 		if (didCreate)
217 			_SendInstallNotification(type);
218 		else
219 			_SendMonitorUpdate(what, type, B_META_MIME_MODIFIED);
220 	}
221 
222 	return status;
223 }
224 
225 
226 // SetAppHint
227 /*!	\brief Sets the application hint for the given MIME type
228 	\param type Pointer to a NULL-terminated string containing the MIME type of interest
229 	\param decsription Pointer to an entry_ref containing the location of an application
230 	       that should be used when launching an application with this signature.
231 */
232 status_t
233 Database::SetAppHint(const char *type, const entry_ref *ref)
234 {
235 	DBG(OUT("Database::SetAppHint()\n"));
236 
237 	if (type == NULL || ref == NULL)
238 		return B_BAD_VALUE;
239 
240 	BPath path;
241 	status_t status = path.SetTo(ref);
242 	if (status < B_OK)
243 		return status;
244 
245 	return _SetStringValue(type, B_APP_HINT_CHANGED, kAppHintAttr,
246 		kAppHintType, B_PATH_NAME_LENGTH, path.Path());
247 }
248 
249 // SetAttrInfo
250 /*! \brief Stores a BMessage describing the format of attributes typically associated with
251 	files of the given MIME type
252 
253 	See BMimeType::SetAttrInfo() for description of the expected message format.
254 
255 	The \c BMessage::what value is ignored.
256 
257 	\param info Pointer to a pre-allocated and properly formatted BMessage containing
258 	            information about the file attributes typically associated with the
259 	            MIME type.
260 	\return
261 	- \c B_OK: Success
262 	- "error code": Failure
263 */
264 status_t
265 Database::SetAttrInfo(const char *type, const BMessage *info)
266 {
267 	DBG(OUT("Database::SetAttrInfo()\n"));
268 
269 	if (type == NULL || info == NULL)
270 		return B_BAD_VALUE;
271 
272 	bool didCreate = false;
273 	status_t status = fLocation->WriteMessageAttribute(type, kAttrInfoAttr,
274 		*info, &didCreate);
275 	if (status == B_OK) {
276 		if (didCreate)
277 			_SendInstallNotification(type);
278 		else
279 			_SendMonitorUpdate(B_ATTR_INFO_CHANGED, type, B_META_MIME_MODIFIED);
280 	}
281 
282 	return status;
283 }
284 
285 
286 // SetShortDescription
287 /*!	\brief Sets the short description for the given MIME type
288 	\param type Pointer to a NULL-terminated string containing the MIME type of interest
289 	\param decsription Pointer to a NULL-terminated string containing the new short description
290 */
291 status_t
292 Database::SetShortDescription(const char *type, const char *description)
293 {
294 	DBG(OUT("Database::SetShortDescription()\n"));
295 
296 	return _SetStringValue(type, B_SHORT_DESCRIPTION_CHANGED, kShortDescriptionAttr,
297 		kShortDescriptionType, B_MIME_TYPE_LENGTH, description);
298 }
299 
300 // SetLongDescription
301 /*!	\brief Sets the long description for the given MIME type
302 	\param type Pointer to a NULL-terminated string containing the MIME type of interest
303 	\param decsription Pointer to a NULL-terminated string containing the new long description
304 */
305 status_t
306 Database::SetLongDescription(const char *type, const char *description)
307 {
308 	DBG(OUT("Database::SetLongDescription()\n"));
309 
310 	size_t length = description != NULL ? strlen(description) : 0;
311 	if (type == NULL || description == NULL || length >= B_MIME_TYPE_LENGTH)
312 		return B_BAD_VALUE;
313 
314 	return _SetStringValue(type, B_LONG_DESCRIPTION_CHANGED, kLongDescriptionAttr,
315 		kLongDescriptionType, B_MIME_TYPE_LENGTH, description);
316 }
317 
318 
319 /*!
320 	\brief Sets the list of filename extensions associated with the MIME type
321 
322 	The list of extensions is given in a pre-allocated BMessage pointed to by
323 	the \c extensions parameter. Please see BMimeType::SetFileExtensions()
324 	for a description of the expected message format.
325 
326 	\param extensions Pointer to a pre-allocated, properly formatted BMessage containing
327 	                  the new list of file extensions to associate with this MIME type.
328 	\return
329 	- \c B_OK: Success
330 	- "error code": Failure
331 */
332 status_t
333 Database::SetFileExtensions(const char *type, const BMessage *extensions)
334 {
335 	DBG(OUT("Database::SetFileExtensions()\n"));
336 
337 	if (type == NULL || extensions == NULL)
338 		return B_BAD_VALUE;
339 
340 	bool didCreate = false;
341 	status_t status = fLocation->WriteMessageAttribute(type,
342 		kFileExtensionsAttr, *extensions, &didCreate);
343 
344 	if (status == B_OK) {
345 		if (didCreate) {
346 			_SendInstallNotification(type);
347 		} else {
348 			_SendMonitorUpdate(B_FILE_EXTENSIONS_CHANGED, type,
349 				B_META_MIME_MODIFIED);
350 		}
351 	}
352 
353 	return status;
354 }
355 
356 
357 /*!
358 	\brief Sets a bitmap icon for the given mime type
359 */
360 status_t
361 Database::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 */
372 status_t
373 Database::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 */
383 status_t
384 Database::SetIcon(const char *type, const void *data, size_t dataSize)
385 {
386 	return SetIconForType(type, NULL, data, dataSize);
387 }
388 
389 
390 status_t
391 Database::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 */
424 status_t
425 Database::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 */
505 status_t
506 Database::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 */
560 status_t
561 Database::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 */
574 status_t
575 Database::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 */
613 status_t
614 Database::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 */
673 status_t
674 Database::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 */
692 status_t
693 Database::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 */
713 status_t
714 Database::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 */
725 status_t
726 Database::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 */
736 status_t
737 Database::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 */
768 status_t
769 Database::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 */
833 status_t
834 Database::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 */
862 status_t
863 Database::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 */
957 status_t
958 Database::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 */
974 status_t
975 Database::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 */
1000 status_t
1001 Database::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 */
1022 status_t
1023 Database::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 */
1044 status_t
1045 Database::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 */
1066 status_t
1067 Database::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 */
1088 status_t
1089 Database::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 */
1111 status_t
1112 Database::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 */
1135 status_t
1136 Database::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 */
1166 status_t
1167 Database::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 */
1200 status_t
1201 Database::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 */
1231 status_t
1232 Database::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 */
1268 status_t
1269 Database::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 */
1300 status_t
1301 Database::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 
1328 void
1329 Database::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 
1351 void
1352 Database::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
1374 status_t
1375 Database::_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
1382 status_t
1383 Database::_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 */
1398 status_t
1399 Database::_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 */
1429 status_t
1430 Database::_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 */
1458 status_t
1459 Database::_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 */
1484 status_t
1485 Database::_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 */
1507 status_t
1508 Database::_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 
1526 Database::DeferredInstallNotification*
1527 Database::_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 
1544 bool
1545 Database::_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