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