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