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