xref: /haiku/src/kits/storage/mime/SupportingApps.cpp (revision 2f470aec1c92ce6917b8a903e343795dc77af41f)
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 #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 
31 namespace BPrivate {
32 namespace Storage {
33 namespace 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
42 SupportingApps::SupportingApps()
43 	: fHaveDoneFullBuild(false)
44 {
45 }
46 
47 // Destructor
48 //! Destroys the SupportingApps object
49 SupportingApps::~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 */
59 status_t
60 SupportingApps::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 */
151 status_t
152 SupportingApps::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 */
209 status_t
210 SupportingApps::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 */
226 status_t
227 SupportingApps::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 */
245 status_t
246 SupportingApps::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 */
258 status_t
259 SupportingApps::BuildSupportingAppsTable()
260 {
261 	fSupportedTypes.clear();
262 	fSupportingApps.clear();
263 	fStrandedTypes.clear();
264 
265 	BDirectory dir;
266 	status_t status = dir.SetTo(kApplicationDatabaseDir.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