1 /* 2 * Copyright 2002-2006, Haiku. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Tyler Dauwalder 7 */ 8 9 /*! 10 \file database_support.cpp 11 Private mime database functions and constants 12 */ 13 14 #include <AppMisc.h> 15 #include <DataIO.h> 16 #include <Directory.h> 17 #include <Entry.h> 18 #include <FindDirectory.h> 19 #include <Message.h> 20 #include <Node.h> 21 #include <Path.h> 22 #include <storage_support.h> 23 #include <TypeConstants.h> 24 25 #include <fs_attr.h> // For struct attr_info 26 #include <new> // For new(nothrow) 27 #include <stdio.h> 28 #include <string.h> 29 30 #include "mime/database_support.h" 31 32 //#define DBG(x) x 33 #define DBG(x) 34 #define OUT printf 35 36 37 namespace BPrivate { 38 namespace Storage { 39 namespace Mime { 40 41 42 #define ATTR_PREFIX "META:" 43 #define MINI_ICON_ATTR_PREFIX ATTR_PREFIX "M:" 44 #define LARGE_ICON_ATTR_PREFIX ATTR_PREFIX "L:" 45 46 const char *kMiniIconAttrPrefix = MINI_ICON_ATTR_PREFIX; 47 const char *kLargeIconAttrPrefix = LARGE_ICON_ATTR_PREFIX; 48 const char *kIconAttrPrefix = ATTR_PREFIX; 49 50 // attribute names 51 const char *kFileTypeAttr = "BEOS:TYPE"; 52 const char *kTypeAttr = ATTR_PREFIX "TYPE"; 53 const char *kAppHintAttr = ATTR_PREFIX "PPATH"; 54 const char *kAttrInfoAttr = ATTR_PREFIX "ATTR_INFO"; 55 const char *kShortDescriptionAttr = ATTR_PREFIX "S:DESC"; 56 const char *kLongDescriptionAttr = ATTR_PREFIX "L:DESC"; 57 const char *kFileExtensionsAttr = ATTR_PREFIX "EXTENS"; 58 const char *kMiniIconAttr = MINI_ICON_ATTR_PREFIX "STD_ICON"; 59 const char *kLargeIconAttr = LARGE_ICON_ATTR_PREFIX "STD_ICON"; 60 const char *kIconAttr = ATTR_PREFIX "ICON"; 61 const char *kPreferredAppAttr = ATTR_PREFIX "PREF_APP"; 62 const char *kSnifferRuleAttr = ATTR_PREFIX "SNIFF_RULE"; 63 const char *kSupportedTypesAttr = ATTR_PREFIX "FILE_TYPES"; 64 65 // attribute data types (as used in the R5 database) 66 const int32 kFileTypeType = 'MIMS'; // B_MIME_STRING_TYPE 67 const int32 kTypeType = B_STRING_TYPE; 68 const int32 kAppHintType = 'MPTH'; 69 const int32 kAttrInfoType = B_MESSAGE_TYPE; 70 const int32 kShortDescriptionType = 'MSDC'; 71 const int32 kLongDescriptionType = 'MLDC'; 72 const int32 kFileExtensionsType = B_MESSAGE_TYPE; 73 const int32 kMiniIconType = B_MINI_ICON_TYPE; 74 const int32 kLargeIconType = B_LARGE_ICON_TYPE; 75 const int32 kIconType = B_VECTOR_ICON_TYPE; 76 const int32 kPreferredAppType = 'MSIG'; 77 const int32 kSnifferRuleType = B_STRING_TYPE; 78 const int32 kSupportedTypesType = B_MESSAGE_TYPE; 79 80 // Message fields 81 const char *kApplicationsField = "applications"; 82 const char *kExtensionsField = "extensions"; 83 const char *kSupertypesField = "super_types"; 84 const char *kSupportingAppsSubCountField = "be:sub"; 85 const char *kSupportingAppsSuperCountField = "be:super"; 86 const char *kTypesField = "types"; 87 88 // Mime types 89 const char *kGenericFileType = "application/octet-stream"; 90 const char *kDirectoryType = "application/x-vnd.Be-directory"; 91 const char *kSymlinkType = "application/x-vnd.Be-symlink"; 92 const char *kMetaMimeType = "application/x-vnd.Be-meta-mime"; 93 94 // Error codes 95 const status_t kMimeGuessFailureError = B_ERRORS_END+1; 96 97 static pthread_once_t sDatabaseDirectoryInitOnce = PTHREAD_ONCE_INIT; 98 static std::string sDatabaseDirectory; 99 static std::string sApplicationDatabaseDirectory; 100 101 102 static void 103 init_database_directories() 104 { 105 BPath path; 106 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) 107 sDatabaseDirectory = path.Path(); 108 else 109 sDatabaseDirectory = "/boot/home/config/settings"; 110 111 sDatabaseDirectory += "/beos_mime"; 112 sApplicationDatabaseDirectory = sDatabaseDirectory + "/application"; 113 } 114 115 116 const std::string 117 get_database_directory() 118 { 119 pthread_once(&sDatabaseDirectoryInitOnce, &init_database_directories); 120 return sDatabaseDirectory; 121 } 122 123 124 const std::string 125 get_application_database_directory() 126 { 127 pthread_once(&sDatabaseDirectoryInitOnce, &init_database_directories); 128 return sApplicationDatabaseDirectory; 129 } 130 131 132 // type_to_filename 133 //! Converts the given MIME type to an absolute path in the MIME database. 134 std::string 135 type_to_filename(const char *type) 136 { 137 return get_database_directory() + "/" + BPrivate::Storage::to_lower(type); 138 } 139 140 // open_type 141 /*! \brief Opens a BNode on the given type, failing if the type has no 142 corresponding file in the database. 143 \param type The MIME type to open 144 \param result Pointer to a pre-allocated BNode into which 145 is opened on the given MIME type 146 */ 147 status_t 148 open_type(const char *type, BNode *result) 149 { 150 if (type == NULL || result == NULL) 151 return B_BAD_VALUE; 152 153 status_t status = result->SetTo(type_to_filename(type).c_str()); 154 155 // TODO: this can be removed again later - we just didn't write this 156 // attribute is before at all... 157 #if 1 158 if (status == B_OK) { 159 // check if the MIME:TYPE attribute exist, and create it if not 160 attr_info info; 161 if (result->GetAttrInfo(kTypeAttr, &info) != B_OK) 162 result->WriteAttr(kTypeAttr, B_STRING_TYPE, 0, type, strlen(type) + 1); 163 } 164 #endif 165 166 return status; 167 } 168 169 // open_or_create_type 170 /*! \brief Opens a BNode on the given type, creating a node of the 171 appropriate flavor if necessary. 172 173 All MIME types are converted to lowercase for use in the filesystem. 174 \param type The MIME type to open 175 \param result Pointer to a pre-allocated BNode into which 176 is opened on the given MIME type 177 */ 178 status_t 179 open_or_create_type(const char *type, BNode *result, bool *didCreate) 180 { 181 if (didCreate) 182 *didCreate = false; 183 std::string filename; 184 std::string typeLower = BPrivate::Storage::to_lower(type); 185 status_t err = (type && result ? B_OK : B_BAD_VALUE); 186 if (!err) { 187 filename = type_to_filename(type); 188 err = result->SetTo(filename.c_str()); 189 } 190 if (err == B_ENTRY_NOT_FOUND) { 191 // Figure out what type of node we need to create 192 // + Supertype == directory 193 // + Non-supertype == file 194 size_t pos = typeLower.find_first_of('/'); 195 if (pos == std::string::npos) { 196 // Supertype == directory 197 BDirectory parent(get_database_directory().c_str()); 198 err = parent.InitCheck(); 199 if (!err) 200 err = parent.CreateDirectory(typeLower.c_str(), NULL); 201 } else { 202 // Non-supertype == file 203 std::string super(typeLower, 0, pos); 204 std::string sub(typeLower, pos+1); 205 BDirectory parent((get_database_directory() + "/" + super).c_str()); 206 err = parent.InitCheck(); 207 if (!err) 208 err = parent.CreateFile(sub.c_str(), NULL); 209 } 210 // Now try opening again 211 if (err == B_OK) 212 err = result->SetTo(filename.c_str()); 213 if (err == B_OK && didCreate) 214 *didCreate = true; 215 if (err == B_OK) { 216 // write META:TYPE attribute 217 result->WriteAttr(kTypeAttr, B_STRING_TYPE, 0, type, strlen(type) + 1); 218 } 219 } 220 return err; 221 } 222 223 // read_mime_attr 224 /*! \brief Reads up to \c len bytes of the given data from the given attribute 225 for the given MIME type. 226 227 If no entry for the given type exists in the database, the function fails, 228 and the contents of \c data are undefined. 229 230 \param type The MIME type 231 \param attr The attribute name 232 \param data Pointer to a memory buffer into which the data should be copied 233 \param len The maximum number of bytes to read 234 \param datatype The expected data type 235 \return If successful, the number of bytes read is returned, otherwise, an 236 error code is returned. 237 */ 238 ssize_t 239 read_mime_attr(const char *type, const char *attr, void *data, 240 size_t len, type_code datatype) 241 { 242 BNode node; 243 ssize_t err = (type && attr && data ? B_OK : B_BAD_VALUE); 244 if (!err) 245 err = open_type(type, &node); 246 if (!err) 247 err = node.ReadAttr(attr, datatype, 0, data, len); 248 return err; 249 } 250 251 // read_mime_attr_message 252 /*! \brief Reads a flattened BMessage from the given attribute of the given 253 MIME type. 254 255 If no entry for the given type exists in the database, or if the data 256 stored in the attribute is not a flattened BMessage, the function fails 257 and the contents of \c msg are undefined. 258 259 \param type The MIME type 260 \param attr The attribute name 261 \param data Pointer to a pre-allocated BMessage into which the attribute 262 data is unflattened. 263 */ 264 status_t 265 read_mime_attr_message(const char *type, const char *attr, BMessage *msg) 266 { 267 BNode node; 268 attr_info info; 269 char *buffer = NULL; 270 ssize_t err = (type && attr && msg ? B_OK : B_BAD_VALUE); 271 if (!err) 272 err = open_type(type, &node); 273 if (!err) 274 err = node.GetAttrInfo(attr, &info); 275 if (!err) 276 err = info.type == B_MESSAGE_TYPE ? B_OK : B_BAD_VALUE; 277 if (!err) { 278 buffer = new(std::nothrow) char[info.size]; 279 if (!buffer) 280 err = B_NO_MEMORY; 281 } 282 if (!err) 283 err = node.ReadAttr(attr, B_MESSAGE_TYPE, 0, buffer, info.size); 284 if (err >= 0) 285 err = err == info.size ? (status_t)B_OK : (status_t)B_FILE_ERROR; 286 if (!err) 287 err = msg->Unflatten(buffer); 288 delete [] buffer; 289 return err; 290 } 291 292 // read_mime_attr_string 293 /*! \brief Reads a BString from the given attribute of the given 294 MIME type. 295 296 If no entry for the given type exists in the database, the function fails 297 and the contents of \c str are undefined. 298 299 \param type The MIME type 300 \param attr The attribute name 301 \param str Pointer to a pre-allocated BString into which the attribute 302 data stored. 303 */ 304 status_t 305 read_mime_attr_string(const char *type, const char *attr, BString *str) 306 { 307 BNode node; 308 status_t err = (type && attr && str ? B_OK : B_BAD_VALUE); 309 if (!err) 310 err = open_type(type, &node); 311 if (!err) 312 err = node.ReadAttrString(attr, str); 313 return err; 314 } 315 316 // write_mime_attr 317 /*! \brief Writes \c len bytes of the given data to the given attribute 318 for the given MIME type. 319 320 If no entry for the given type exists in the database, it is created. 321 322 \param type The MIME type 323 \param attr The attribute name 324 \param data Pointer to the data to write 325 \param len The number of bytes to write 326 \param datatype The data type of the given data 327 */ 328 status_t 329 write_mime_attr(const char *type, const char *attr, const void *data, 330 size_t len, type_code datatype, bool *didCreate) 331 { 332 BNode node; 333 status_t err = (type && attr && data ? B_OK : B_BAD_VALUE); 334 if (!err) 335 err = open_or_create_type(type, &node, didCreate); 336 if (!err) { 337 ssize_t bytes = node.WriteAttr(attr, datatype, 0, data, len); 338 if (bytes < B_OK) 339 err = bytes; 340 else 341 err = (bytes != (ssize_t)len ? (status_t)B_FILE_ERROR : (status_t)B_OK); 342 } 343 return err; 344 } 345 346 // write_mime_attr_message 347 /*! \brief Flattens the given \c BMessage and writes it to the given attribute 348 of the given MIME type. 349 350 If no entry for the given type exists in the database, it is created. 351 352 \param type The MIME type 353 \param attr The attribute name 354 \param msg The BMessage to flatten and write 355 */ 356 status_t 357 write_mime_attr_message(const char *type, const char *attr, const BMessage *msg, bool *didCreate) 358 { 359 BNode node; 360 BMallocIO data; 361 ssize_t bytes; 362 ssize_t err = (type && attr && msg ? B_OK : B_BAD_VALUE); 363 if (!err) 364 err = data.SetSize(msg->FlattenedSize()); 365 if (!err) 366 err = msg->Flatten(&data, &bytes); 367 if (!err) 368 err = bytes == msg->FlattenedSize() ? B_OK : B_ERROR; 369 if (!err) 370 err = open_or_create_type(type, &node, didCreate); 371 if (!err) 372 err = node.WriteAttr(attr, B_MESSAGE_TYPE, 0, data.Buffer(), data.BufferLength()); 373 if (err >= 0) 374 err = err == (ssize_t)data.BufferLength() ? (ssize_t)B_OK : (ssize_t)B_FILE_ERROR; 375 return err; 376 } 377 378 // delete_attribute 379 //! Deletes the given attribute for the given type 380 /*! 381 \param type The mime type 382 \param attr The attribute name 383 \return 384 - B_OK: success 385 - B_ENTRY_NOT_FOUND: no such type or attribute 386 - "error code": failure 387 */ 388 status_t 389 delete_attribute(const char *type, const char *attr) 390 { 391 status_t err = type ? B_OK : B_BAD_VALUE; 392 BNode node; 393 if (!err) 394 err = open_type(type, &node); 395 if (!err) 396 err = node.RemoveAttr(attr); 397 return err; 398 } 399 400 401 402 } // namespace Mime 403 } // namespace Storage 404 } // namespace BPrivate 405 406