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 45 SupportingApps::SupportingApps(DatabaseLocation* databaseLocation) 46 : 47 fDatabaseLocation(databaseLocation), 48 fHaveDoneFullBuild(false) 49 { 50 } 51 52 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 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 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 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 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 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 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