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