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