xref: /haiku/src/kits/storage/mime/SupportingApps.cpp (revision aa3083e086e5a929c061c72983e09d916c548a38)
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 		// Read through the list of new supported types, creating the new
172 		// supported types list and adding the app as a supporting app for
173 		// each type.
174 		newTypes.clear();
175 		const char *type;
176 		for (int32 i = 0; types->FindString(kTypesField, i, &type) == B_OK;
177 				i++) {
178 			newTypes.insert(type);
179 			AddSupportingApp(type, app);
180 		}
181 
182 		// Update the list of stranded types by removing any types that are newly
183 		// re-supported and adding any types that are newly un-supported
184 		for (std::set<std::string>::const_iterator i = newTypes.begin();
185 				i != newTypes.end(); i++) {
186 			strandedTypes.erase(*i);
187 		}
188 		for (std::set<std::string>::const_iterator i = oldTypes.begin();
189 				i != oldTypes.end(); i++) {
190 			if (newTypes.find(*i) == newTypes.end())
191 				strandedTypes.insert(*i);
192 		}
193 
194 		// Now, if we're doing a full sync, remove the app as a supporting
195 		// app for any of its stranded types and then clear said list of
196 		// stranded types.
197 		if (fullSync) {
198 			for (std::set<std::string>::const_iterator i = strandedTypes.begin();
199 					i != strandedTypes.end(); i++) {
200 				RemoveSupportingApp((*i).c_str(), app);
201 			}
202 			strandedTypes.clear();
203 		}
204 	}
205 	return err;
206 }
207 
208 
209 /*! \brief Clears the given application's supported types list and optionally
210 	removes the application from each of said types' supporting apps list.
211 	\param app The application whose supported types you are clearing
212 	\param fullSync See SupportingApps::SetSupportedTypes()
213 */
214 status_t
215 SupportingApps::DeleteSupportedTypes(const char *app, bool fullSync)
216 {
217 	BMessage types;
218 	return SetSupportedTypes(app, &types, fullSync);
219 }
220 
221 // AddSupportingApp
222 /*! \brief Adds the given application signature to the set of supporting
223 	apps for the given type.
224 
225 	\param type The full mime type
226 	\param app The full application signature (i.e. "application/app-subtype")
227 	\return
228 	- B_OK: success, even if the app was already in the supporting apps list
229 	- "error code": failure
230 */
231 status_t
232 SupportingApps::AddSupportingApp(const char *type, const char *app)
233 {
234 	status_t err = type && app ? B_OK : B_BAD_VALUE;
235 	if (!err)
236 		fSupportingApps[type].insert(app);
237 	return err;
238 }
239 
240 // RemoveSupportingApp
241 /*! \brief Removes the given application signature from the set of supporting
242 	apps for the given type.
243 
244 	\param type The full mime type
245 	\param app The full application signature (i.e. "application/app-subtype")
246 	\return
247 	- B_OK: success, even if the app was not found in the supporting apps list
248 	- "error code": failure
249 */
250 status_t
251 SupportingApps::RemoveSupportingApp(const char *type, const char *app)
252 {
253 	status_t err = type && app ? B_OK : B_BAD_VALUE;
254 	if (!err)
255 		fSupportingApps[type].erase(app);
256 	return err;
257 }
258 
259 // BuildSupportingAppsTable
260 /*! \brief Crawls the mime database and builds a list of supporting application
261 	signatures for every supported type.
262 */
263 status_t
264 SupportingApps::BuildSupportingAppsTable()
265 {
266 	fSupportedTypes.clear();
267 	fSupportingApps.clear();
268 	fStrandedTypes.clear();
269 
270 	DatabaseDirectory dir;
271 	status_t status = dir.Init(fDatabaseLocation, "application");
272 
273 	// Build the supporting apps table based on the mime database
274 	if (status == B_OK) {
275 		dir.Rewind();
276 
277 		// Handle each application type
278 		while (true) {
279 			entry_ref ref;
280 			status = dir.GetNextRef(&ref);
281 			if (status < B_OK) {
282 				// If we've come to the end of list, it's not an error
283 				if (status == B_ENTRY_NOT_FOUND)
284 					status = B_OK;
285 				break;
286 			}
287 
288 			// read application signature from file
289 			BString appSignature;
290 			BNode node(&ref);
291 			if (node.InitCheck() == B_OK && node.ReadAttrString(kTypeAttr,
292 					&appSignature) >= B_OK) {
293 				// Read in the list of supported types
294 				BMessage msg;
295 				if (fDatabaseLocation->ReadMessageAttribute(appSignature,
296 						kSupportedTypesAttr, msg) == B_OK) {
297 					// Iterate through the supported types, adding them to the list of
298 					// supported types for the application and adding the application's
299 					// signature to the list of supporting apps for each type
300 					BString type;
301 					std::set<std::string> &supportedTypes = fSupportedTypes[appSignature.String()];
302 					for (int i = 0; msg.FindString(kTypesField, i, &type) == B_OK; i++) {
303 						type.ToLower();
304 							// MIME types are case insensitive, so we lowercase everything
305 						supportedTypes.insert(type.String());
306 						AddSupportingApp(type.String(), appSignature.String());
307 					}
308 				}
309 			}
310 		}
311 	}
312 
313 	if (status == B_OK)
314 		fHaveDoneFullBuild = true;
315 	else
316 		DBG(OUT("SupportingApps::BuildSupportingAppsTable() failed: %s\n", strerror(status)));
317 
318 	return status;
319 }
320 
321 } // namespace Mime
322 } // namespace Storage
323 } // namespace BPrivate
324 
325