1/*
2 * Copyright 2002-2006, Haiku.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Tyler Dauwalder
7 *		Ingo Weinhold, bonefish@users.sf.net
8 *		Axel Dörfler, axeld@pinc-software.de
9 */
10
11
12#include "SupportingApps.h"
13#include <mime/database_support.h>
14
15#include <storage_support.h>
16
17#include <Directory.h>
18#include <Message.h>
19#include <MimeType.h>
20#include <Path.h>
21#include <String.h>
22
23#include <new>
24#include <stdio.h>
25#include <iostream>
26
27#define DBG(x) x
28//#define DBG(x)
29#define OUT printf
30
31namespace BPrivate {
32namespace Storage {
33namespace Mime {
34
35/*!
36	\class SupportingApps
37	\brief Supporting apps information for the entire database
38*/
39
40// Constructor
41//! Constructs a new SupportingApps object
42SupportingApps::SupportingApps()
43	: fHaveDoneFullBuild(false)
44{
45}
46
47// Destructor
48//! Destroys the SupportingApps object
49SupportingApps::~SupportingApps()
50{
51}
52
53// GetSupportingApps
54/*! \brief Returns a list of signatures of supporting applications for the
55	given type in the pre-allocated \c BMessage pointed to by \c apps.
56
57	See \c BMimeType::GetSupportingApps() for more information.
58*/
59status_t
60SupportingApps::GetSupportingApps(const char *type, BMessage *apps)
61{
62	status_t err = type && apps ? B_OK : B_BAD_VALUE;
63	// See if we need to do our initial build still
64	if (!err && !fHaveDoneFullBuild)
65		err = BuildSupportingAppsTable();
66
67	if (!err) {
68		// Clear the message, as we're just going to add to it
69		apps->MakeEmpty();
70
71		BMimeType mime(type);
72		err = mime.InitCheck();
73		if (!err) {
74			if (mime.IsSupertypeOnly()) {
75				// Add the apps that support this supertype (plus their count)
76				std::set<std::string> &superApps = fSupportingApps[type];
77				int32 count = 0;
78				std::set<std::string>::const_iterator i;
79				for (i = superApps.begin(); i != superApps.end() && !err; i++) {
80					err = apps->AddString(kApplicationsField, (*i).c_str());
81					count++;
82				}
83				if (!err)
84					err = apps->AddInt32(kSupportingAppsSuperCountField, count);
85			} else {
86				// Add the apps that support this subtype (plus their count)
87				std::set<std::string> &subApps = fSupportingApps[type];
88				int32 count = 0;
89				std::set<std::string>::const_iterator i;
90				for (i = subApps.begin(); i != subApps.end() && !err; i++) {
91					err = apps->AddString(kApplicationsField, (*i).c_str());
92					count++;
93				}
94				if (!err)
95					err = apps->AddInt32(kSupportingAppsSubCountField, count);
96
97				// Now add any apps that support the supertype, but not the
98				// subtype (plus their count).
99				BMimeType superMime;
100				err = mime.GetSupertype(&superMime);
101				if (!err)
102					err = superMime.InitCheck();
103				if (!err) {
104					std::set<std::string> &superApps = fSupportingApps[superMime.Type()];
105					count = 0;
106					for (i = superApps.begin(); i != superApps.end() && !err; i++) {
107						if (subApps.find(*i) == subApps.end()) {
108							err = apps->AddString(kApplicationsField, (*i).c_str());
109							count++;
110						}
111					}
112					if (!err)
113						err = apps->AddInt32(kSupportingAppsSuperCountField, count);
114				}
115			}
116		}
117	}
118	return err;
119}
120
121// SetSupportedTypes
122/*! \brief Sets the list of supported types for the given application and
123	updates the supporting apps mappings.
124
125	All types listed as being supported types will including the given
126	app signature in their list of supporting apps following this call.
127
128	If \a fullSync is true, all types previously but no longer supported
129	by this application with no longer list this application as a
130	supporting app.
131
132	If \a fullSync is false, said previously supported types will be
133	saved to a "stranded types" mapping and appropriately synchronized
134	the next time SetSupportedTypes() is called with a \c true \a fullSync
135	parameter.
136
137	The stranded types mapping is properly maintained even in the event
138	of types being removed and then re-added to the list of supporting
139	types with consecutive \c false \a fullSync parameters.
140
141	\param app The application whose supported types you are setting
142	\param types Pointer to a \c BMessage containing an array of supported
143	             mime types in its \c Mime::kTypesField field.
144	\param fullSync If \c true, \c app is removed as a supporting application
145	                for any types for which it is no longer a supporting application
146	                (including types which were removed as supporting types with
147	                previous callsto SetSupportedTypes(..., false)). If \c false,
148	                said mappings are not updated until the next SetSupportedTypes(..., true)
149	                call.
150*/
151status_t
152SupportingApps::SetSupportedTypes(const char *app, const BMessage *types, bool fullSync)
153{
154	status_t err = app && types ? B_OK : B_BAD_VALUE;
155	if (!fHaveDoneFullBuild)
156		return err;
157
158	std::set<std::string> oldTypes;
159	std::set<std::string> &newTypes = fSupportedTypes[app];
160	std::set<std::string> &strandedTypes = fStrandedTypes[app];
161	// Make a copy of the previous types if we're doing a full sync
162	if (!err)
163		oldTypes = newTypes;
164
165	if (!err) {
166		// Read through the list of new supported types, creating the new
167		// supported types list and adding the app as a supporting app for
168		// each type.
169		newTypes.clear();
170		const char *type;
171		for (int32 i = 0; types->FindString(kTypesField, i, &type) == B_OK;
172				i++) {
173			newTypes.insert(type);
174			AddSupportingApp(type, app);
175		}
176
177		// Update the list of stranded types by removing any types that are newly
178		// re-supported and adding any types that are newly un-supported
179		for (std::set<std::string>::const_iterator i = newTypes.begin();
180				i != newTypes.end(); i++) {
181			strandedTypes.erase(*i);
182		}
183		for (std::set<std::string>::const_iterator i = oldTypes.begin();
184				i != oldTypes.end(); i++) {
185			if (newTypes.find(*i) == newTypes.end())
186				strandedTypes.insert(*i);
187		}
188
189		// Now, if we're doing a full sync, remove the app as a supporting
190		// app for any of its stranded types and then clear said list of
191		// stranded types.
192		if (fullSync) {
193			for (std::set<std::string>::const_iterator i = strandedTypes.begin();
194					i != strandedTypes.end(); i++) {
195				RemoveSupportingApp((*i).c_str(), app);
196			}
197			strandedTypes.clear();
198		}
199	}
200	return err;
201}
202
203
204/*! \brief Clears the given application's supported types list and optionally
205	removes the application from each of said types' supporting apps list.
206	\param app The application whose supported types you are clearing
207	\param fullSync See SupportingApps::SetSupportedTypes()
208*/
209status_t
210SupportingApps::DeleteSupportedTypes(const char *app, bool fullSync)
211{
212	BMessage types;
213	return SetSupportedTypes(app, &types, fullSync);
214}
215
216// AddSupportingApp
217/*! \brief Adds the given application signature to the set of supporting
218	apps for the given type.
219
220	\param type The full mime type
221	\param app The full application signature (i.e. "application/app-subtype")
222	\return
223	- B_OK: success, even if the app was already in the supporting apps list
224	- "error code": failure
225*/
226status_t
227SupportingApps::AddSupportingApp(const char *type, const char *app)
228{
229	status_t err = type && app ? B_OK : B_BAD_VALUE;
230	if (!err)
231		fSupportingApps[type].insert(app);
232	return err;
233}
234
235// RemoveSupportingApp
236/*! \brief Removes the given application signature from the set of supporting
237	apps for the given type.
238
239	\param type The full mime type
240	\param app The full application signature (i.e. "application/app-subtype")
241	\return
242	- B_OK: success, even if the app was not found in the supporting apps list
243	- "error code": failure
244*/
245status_t
246SupportingApps::RemoveSupportingApp(const char *type, const char *app)
247{
248	status_t err = type && app ? B_OK : B_BAD_VALUE;
249	if (!err)
250		fSupportingApps[type].erase(app);
251	return err;
252}
253
254// BuildSupportingAppsTable
255/*! \brief Crawls the mime database and builds a list of supporting application
256	signatures for every supported type.
257*/
258status_t
259SupportingApps::BuildSupportingAppsTable()
260{
261	fSupportedTypes.clear();
262	fSupportingApps.clear();
263	fStrandedTypes.clear();
264
265	BDirectory dir;
266	status_t status = dir.SetTo(get_application_database_directory().c_str());
267
268	// Build the supporting apps table based on the mime database
269	if (status == B_OK) {
270		dir.Rewind();
271
272		// Handle each application type
273		while (true) {
274			entry_ref ref;
275			status = dir.GetNextRef(&ref);
276			if (status < B_OK) {
277				// If we've come to the end of list, it's not an error
278				if (status == B_ENTRY_NOT_FOUND)
279					status = B_OK;
280				break;
281			}
282
283			// read application signature from file
284			BString appSignature;
285			BNode node(&ref);
286			if (node.InitCheck() == B_OK && node.ReadAttrString(kTypeAttr,
287					&appSignature) >= B_OK) {
288				// Read in the list of supported types
289				BMessage msg;
290				if (read_mime_attr_message(appSignature.String(), kSupportedTypesAttr,
291						&msg) == B_OK) {
292					// Iterate through the supported types, adding them to the list of
293					// supported types for the application and adding the application's
294					// signature to the list of supporting apps for each type
295					BString type;
296					std::set<std::string> &supportedTypes = fSupportedTypes[appSignature.String()];
297					for (int i = 0; msg.FindString(kTypesField, i, &type) == B_OK; i++) {
298						type.ToLower();
299							// MIME types are case insensitive, so we lowercase everything
300						supportedTypes.insert(type.String());
301						AddSupportingApp(type.String(), appSignature.String());
302					}
303				}
304			}
305		}
306	}
307
308	if (status == B_OK)
309		fHaveDoneFullBuild = true;
310	else
311		DBG(OUT("SupportingApps::BuildSupportingAppsTable() failed: %s\n", strerror(status)));
312
313	return status;
314}
315
316} // namespace Mime
317} // namespace Storage
318} // namespace BPrivate
319
320