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