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