1 /* 2 * Copyright 2002-2008, Haiku Inc. 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 \file Mime.cpp 13 Mime type C functions implementation. 14 */ 15 16 #include <errno.h> 17 #include <new> 18 #include <stdlib.h> 19 #include <string.h> 20 #include <sys/ioctl.h> 21 #include <unistd.h> 22 23 #include <AutoDeleter.h> 24 #include <Bitmap.h> 25 #include <Drivers.h> 26 #include <Entry.h> 27 #include <File.h> 28 #include <FindDirectory.h> 29 #include <fs_attr.h> 30 #include <fs_info.h> 31 #include <IconUtils.h> 32 #include <Mime.h> 33 #include <MimeType.h> 34 #include <mime/database_access.h> 35 #include <Node.h> 36 #include <Path.h> 37 #include <RegistrarDefs.h> 38 #include <Roster.h> 39 #include <RosterPrivate.h> 40 41 using namespace BPrivate; 42 43 enum { 44 NOT_IMPLEMENTED = B_ERROR, 45 }; 46 47 // do_mime_update 48 //! Helper function that contacts the registrar for mime update calls 49 status_t 50 do_mime_update(int32 what, const char *path, int recursive, 51 int synchronous, int force) 52 { 53 BEntry root; 54 entry_ref ref; 55 56 status_t err = root.SetTo(path ? path : "/"); 57 if (!err) 58 err = root.GetRef(&ref); 59 if (!err) { 60 BMessage msg(what); 61 BMessage reply; 62 status_t result; 63 64 // Build and send the message, read the reply 65 if (!err) 66 err = msg.AddRef("entry", &ref); 67 if (!err) 68 err = msg.AddBool("recursive", recursive); 69 if (!err) 70 err = msg.AddBool("synchronous", synchronous); 71 if (!err) 72 err = msg.AddInt32("force", force); 73 if (!err) 74 err = BRoster::Private().SendTo(&msg, &reply, true); 75 if (!err) 76 err = reply.what == B_REG_RESULT ? B_OK : B_BAD_VALUE; 77 if (!err) 78 err = reply.FindInt32("result", &result); 79 if (!err) 80 err = result; 81 } 82 return err; 83 } 84 85 // update_mime_info 86 /*! \brief Updates the MIME information (i.e MIME type) for one or more files. 87 If \a path points to a file, the MIME information for this file are 88 updated only. If it points to a directory and \a recursive is non-null, 89 the information for all the files in the given directory tree are updated. 90 If path is \c NULL all files are considered; \a recursive is ignored in 91 this case. 92 \param path The path to a file or directory, or \c NULL. 93 \param recursive Non-null to trigger recursive behavior. 94 \param synchronous If non-null update_mime_info() waits until the 95 operation is finished, otherwise it returns immediately and the 96 update is done asynchronously. 97 \param force Specifies how to handle files that already have MIME 98 information: 99 - \c B_UPDATE_MIME_INFO_NO_FORCE: Files that already have a 100 \c BEOS:TYPE attribute won't be updated. 101 - \c B_UPDATE_MIME_INFO_FORCE_KEEP_TYPE: Files that already have a 102 \c BEOS:TYPE attribute will be updated too, but \c BEOS:TYPE 103 itself will remain untouched. 104 - \c B_UPDATE_MIME_INFO_FORCE_UPDATE_ALL: Similar to 105 \c B_UPDATE_MIME_INFO_FORCE_KEEP_TYPE, but the \c BEOS:TYPE 106 attribute will be updated too. 107 \return 108 - \c B_OK: Everything went fine. 109 - An error code otherwise. 110 */ 111 int 112 update_mime_info(const char *path, int recursive, int synchronous, int force) 113 { 114 // Force recursion when given a NULL path 115 if (!path) 116 recursive = true; 117 118 return do_mime_update(B_REG_MIME_UPDATE_MIME_INFO, path, recursive, 119 synchronous, force); 120 } 121 122 // create_app_meta_mime 123 /*! Creates a MIME database entry for one or more applications. 124 If \a path points to an application file, a MIME DB entry is create for the 125 application. If it points to a directory and \a recursive is non-null, 126 entries are created for all application files in the given directory 127 tree. If path is \c NULL all files are considered; \a recursive is 128 ignored in this case. 129 \param path The path to an application file, a directory, or \c NULL. 130 \param recursive Non-null to trigger recursive behavior. 131 \param synchronous If non-null create_app_meta_mime() waits until the 132 operation is finished, otherwise it returns immediately and the 133 operation is done asynchronously. 134 \param force If non-null, entries are created even if they do already 135 exist. 136 \return 137 - \c B_OK: Everything went fine. 138 - An error code otherwise. 139 */ 140 status_t 141 create_app_meta_mime(const char *path, int recursive, int synchronous, 142 int force) 143 { 144 // Force recursion when given a NULL path 145 if (!path) 146 recursive = true; 147 148 return do_mime_update(B_REG_MIME_CREATE_APP_META_MIME, path, recursive, 149 synchronous, force); 150 } 151 152 153 /*! Retrieves an icon associated with a given device. 154 \param dev The path to the device. 155 \param icon A pointer to a buffer the icon data shall be written to. 156 \param size The size of the icon. Currently the sizes 16 (small, i.e 157 \c B_MINI_ICON) and 32 (large, i.e. \c B_LARGE_ICON) are 158 supported. 159 160 \todo The mounted directories for volumes can also have META:X:STD_ICON 161 attributes. Should those attributes override the icon returned 162 by ioctl(,B_GET_ICON,)? 163 164 \return 165 - \c B_OK: Everything went fine. 166 - An error code otherwise. 167 */ 168 status_t 169 get_device_icon(const char *device, void *icon, int32 size) 170 { 171 if (device == NULL || icon == NULL 172 || (size != B_LARGE_ICON && size != B_MINI_ICON)) 173 return B_BAD_VALUE; 174 175 int fd = open(device, O_RDONLY); 176 if (fd < 0) 177 return errno; 178 179 device_icon iconData = {size, icon}; 180 if (ioctl(fd, B_GET_ICON, &iconData) != 0) { 181 // legacy icon was not available, try vector icon 182 close(fd); 183 184 uint8* data; 185 size_t dataSize; 186 type_code type; 187 status_t status = get_device_icon(device, &data, &dataSize, &type); 188 if (status == B_OK) { 189 BBitmap* icon32 = new(std::nothrow) BBitmap( 190 BRect(0, 0, size - 1, size - 1), B_BITMAP_NO_SERVER_LINK, 191 B_RGBA32); 192 BBitmap* icon8 = new(std::nothrow) BBitmap( 193 BRect(0, 0, size - 1, size - 1), B_BITMAP_NO_SERVER_LINK, 194 B_CMAP8); 195 196 ArrayDeleter<uint8> dataDeleter(data); 197 ObjectDeleter<BBitmap> icon32Deleter(icon32); 198 ObjectDeleter<BBitmap> icon8Deleter(icon8); 199 200 if (icon32 == NULL || icon32->InitCheck() != B_OK || icon8 == NULL 201 || icon8->InitCheck() != B_OK) { 202 return B_NO_MEMORY; 203 } 204 205 status = BIconUtils::GetVectorIcon(data, dataSize, icon32); 206 if (status == B_OK) 207 status = BIconUtils::ConvertToCMAP8(icon32, icon8); 208 if (status == B_OK) 209 memcpy(icon, icon8->Bits(), icon8->BitsLength()); 210 211 return status; 212 } 213 return errno; 214 } 215 216 close(fd); 217 return B_OK; 218 } 219 220 221 /*! Retrieves an icon associated with a given device. 222 \param dev The path to the device. 223 \param icon A pointer to a pre-allocated BBitmap of the correct dimension 224 to store the requested icon (16x16 for the mini and 32x32 for the 225 large icon). 226 \param which Specifies the size of the icon to be retrieved: 227 \c B_MINI_ICON for the mini and \c B_LARGE_ICON for the large icon. 228 229 \todo The mounted directories for volumes can also have META:X:STD_ICON 230 attributes. Should those attributes override the icon returned 231 by ioctl(,B_GET_ICON,)? 232 233 \return 234 - \c B_OK: Everything went fine. 235 - An error code otherwise. 236 */ 237 status_t 238 get_device_icon(const char *device, BBitmap *icon, icon_size which) 239 { 240 // check parameters 241 if (device == NULL || icon == NULL) 242 return B_BAD_VALUE; 243 244 uint8* data; 245 size_t size; 246 type_code type; 247 status_t status = get_device_icon(device, &data, &size, &type); 248 if (status == B_OK) { 249 status = BIconUtils::GetVectorIcon(data, size, icon); 250 delete[] data; 251 return status; 252 } 253 254 // Vector icon was not available, try old one 255 256 BRect rect; 257 if (which == B_MINI_ICON) 258 rect.Set(0, 0, 15, 15); 259 else if (which == B_LARGE_ICON) 260 rect.Set(0, 0, 31, 31); 261 262 BBitmap* bitmap = icon; 263 int32 iconSize = which; 264 265 if (icon->ColorSpace() != B_CMAP8 266 || (which != B_MINI_ICON && which != B_LARGE_ICON)) { 267 if (which < B_LARGE_ICON) 268 iconSize = B_MINI_ICON; 269 else 270 iconSize = B_LARGE_ICON; 271 272 bitmap = new(std::nothrow) BBitmap( 273 BRect(0, 0, iconSize - 1, iconSize -1), B_BITMAP_NO_SERVER_LINK, 274 B_CMAP8); 275 if (bitmap == NULL || bitmap->InitCheck() != B_OK) { 276 delete bitmap; 277 return B_NO_MEMORY; 278 } 279 } 280 281 // get the icon, convert temporary data into bitmap if necessary 282 status = get_device_icon(device, bitmap->Bits(), iconSize); 283 if (status == B_OK && icon != bitmap) 284 status = BIconUtils::ConvertFromCMAP8(bitmap, icon); 285 286 if (icon != bitmap) 287 delete bitmap; 288 289 return status; 290 } 291 292 293 status_t 294 get_device_icon(const char *device, uint8** _data, size_t* _size, 295 type_code* _type) 296 { 297 if (device == NULL || _data == NULL || _size == NULL || _type == NULL) 298 return B_BAD_VALUE; 299 300 int fd = open(device, O_RDONLY); 301 if (fd < 0) 302 return errno; 303 304 // Try to get the icon by name first 305 306 char name[B_FILE_NAME_LENGTH]; 307 if (ioctl(fd, B_GET_ICON_NAME, name) >= 0) { 308 status_t status = get_named_icon(name, _data, _size, _type); 309 if (status == B_OK) { 310 close(fd); 311 return B_OK; 312 } 313 } 314 315 // Getting the named icon failed, try vector icon next 316 317 // NOTE: The actual icon size is unknown as of yet. After the first call 318 // to B_GET_VECTOR_ICON, the actual size is known and the final buffer 319 // is allocated with the correct size. If the buffer needed to be 320 // larger, then the temporary buffer above will not yet contain the 321 // valid icon data. In that case, a second call to B_GET_VECTOR_ICON 322 // retrieves it into the final buffer. 323 uint8 data[8192]; 324 device_icon iconData = {sizeof(data), data}; 325 status_t status = ioctl(fd, B_GET_VECTOR_ICON, &iconData, 326 sizeof(device_icon)); 327 if (status != 0) 328 status = errno; 329 330 if (status == B_OK) { 331 *_data = new(std::nothrow) uint8[iconData.icon_size]; 332 if (*_data == NULL) 333 status = B_NO_MEMORY; 334 } 335 336 if (status == B_OK) { 337 if (iconData.icon_size > (int32)sizeof(data)) { 338 // the stack buffer does not contain the data, see NOTE above 339 iconData.icon_data = *_data; 340 status = ioctl(fd, B_GET_VECTOR_ICON, &iconData, 341 sizeof(device_icon)); 342 if (status != 0) 343 status = errno; 344 } else 345 memcpy(*_data, data, iconData.icon_size); 346 347 *_size = iconData.icon_size; 348 *_type = B_VECTOR_ICON_TYPE; 349 } 350 351 // TODO: also support getting the old icon? 352 close(fd); 353 return status; 354 } 355 356 357 status_t 358 get_named_icon(const char* name, BBitmap* icon, icon_size which) 359 { 360 // check parameters 361 if (name == NULL || icon == NULL) 362 return B_BAD_VALUE; 363 364 BRect rect; 365 if (which == B_MINI_ICON) 366 rect.Set(0, 0, 15, 15); 367 else if (which == B_LARGE_ICON) 368 rect.Set(0, 0, 31, 31); 369 else 370 return B_BAD_VALUE; 371 372 if (icon->Bounds() != rect) 373 return B_BAD_VALUE; 374 375 uint8* data; 376 size_t size; 377 type_code type; 378 status_t status = get_named_icon(name, &data, &size, &type); 379 if (status == B_OK) 380 status = BIconUtils::GetVectorIcon(data, size, icon); 381 382 delete[] data; 383 return status; 384 } 385 386 387 status_t 388 get_named_icon(const char* name, uint8** _data, size_t* _size, type_code* _type) 389 { 390 if (name == NULL || _data == NULL || _size == NULL || _type == NULL) 391 return B_BAD_VALUE; 392 393 directory_which kWhich[] = { 394 B_USER_DATA_DIRECTORY, 395 B_COMMON_DATA_DIRECTORY, 396 B_BEOS_DATA_DIRECTORY, 397 }; 398 399 status_t status = B_ENTRY_NOT_FOUND; 400 BFile file; 401 off_t size; 402 403 for (uint32 i = 0; i < sizeof(kWhich) / sizeof(kWhich[0]); i++) { 404 BPath path; 405 if (find_directory(kWhich[i], &path) != B_OK) 406 continue; 407 408 path.Append("icons"); 409 path.Append(name); 410 411 status = file.SetTo(path.Path(), B_READ_ONLY); 412 if (status == B_OK) { 413 status = file.GetSize(&size); 414 if (size > 1024 * 1024) 415 status = B_ERROR; 416 } 417 if (status == B_OK) 418 break; 419 } 420 421 if (status != B_OK) 422 return status; 423 424 *_data = new(std::nothrow) uint8[size]; 425 if (*_data == NULL) 426 return B_NO_MEMORY; 427 428 if (file.Read(*_data, size) != size) { 429 delete[] *_data; 430 return B_ERROR; 431 } 432 433 *_size = size; 434 *_type = B_VECTOR_ICON_TYPE; 435 // TODO: for now 436 437 return B_OK; 438 } 439