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