real_extractor.c (14930B)
1 /* 2 * This file is part of libextractor. 3 * Copyright (C) 2021 Christian Grothoff 4 * 5 * libextractor is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published 7 * by the Free Software Foundation; either version 3, or (at your 8 * option) any later version. 9 * 10 * libextractor is distributed in the hope that it will be useful, but 11 * WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with libextractor; see the file COPYING. If not, write to the 17 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 * Boston, MA 02110-1301, USA. 19 * 20 */ 21 /** 22 * @file plugins/real_extractor.c 23 * @brief plugin to support REAL files 24 * @author Christian Grothoff 25 */ 26 #include "platform.h" 27 #include "extractor.h" 28 #include "le_architecture.h" 29 30 /* The structs below describe on-disk layout and are overlaid directly 31 on the (unaligned) read buffer, so they must be packed: without that 32 the compiler is entitled to assume natural alignment and emit loads 33 that are undefined for the addresses they are actually used at -- and 34 fatal on a strict-alignment target. The attribute goes on the struct 35 rather than on each member because gcc ignores it on aggregate and 36 array members. */ 37 LE_NETWORK_STRUCT_BEGIN 38 struct MediaProperties 39 { 40 uint32_t object_id; 41 uint32_t size; 42 uint16_t object_version; /* must be 0 */ 43 uint16_t stream_number; 44 uint32_t max_bit_rate; 45 uint32_t avg_bit_rate; 46 uint32_t max_packet_size; 47 uint32_t avg_packet_size; 48 uint32_t start_time; 49 uint32_t preroll; 50 uint32_t duration; 51 uint8_t stream_name_size; 52 uint8_t data[0]; /* variable length section */ 53 /* 54 uint8_t[stream_name_size] stream_name; 55 uint8_t mime_type_size; 56 uint8_t[mime_type_size] mime_type; 57 uint32_t type_specific_len; 58 uint8_t[type_specific_len] type_specific_data; 59 */ 60 } LE_PACKED; 61 LE_NETWORK_STRUCT_END 62 63 LE_NETWORK_STRUCT_BEGIN 64 struct ContentDescription 65 { 66 uint32_t object_id; 67 uint32_t size; 68 uint16_t object_version; /* must be 0 */ 69 uint16_t title_len; 70 uint8_t data[0]; /* variable length section */ 71 /* 72 uint8_t[title_len] title; 73 uint16_t author_len; 74 uint8_t[author_len] author; 75 uint16_t copyright_len; 76 uint8_t[copyright_len] copyright; 77 uint16_t comment_len; 78 uint8_t[comment_len] comment; 79 */ 80 } LE_PACKED; 81 LE_NETWORK_STRUCT_END 82 /* author, copyright and comment are supposed to be ASCII */ 83 84 85 /** 86 * Read a big-endian 16 bit value from @a p, which need not be aligned. 87 * 88 * @param p (unaligned) source 89 * @return the value in host byte order 90 */ 91 static uint16_t 92 read_be16 (const void *p) 93 { 94 uint16_t v; 95 96 memcpy (&v, p, sizeof (v)); 97 return ntohs (v); 98 } 99 100 101 /** 102 * Read a big-endian 32 bit value from @a p, which need not be aligned. 103 * 104 * @param p (unaligned) source 105 * @return the value in host byte order 106 */ 107 static uint32_t 108 read_be32 (const void *p) 109 { 110 uint32_t v; 111 112 memcpy (&v, p, sizeof (v)); 113 return ntohl (v); 114 } 115 116 117 #define REAL_HEADER 0x2E524d46 118 #define MDPR_HEADER 0x4D445052 119 #define CONT_HEADER 0x434F4e54 120 #define RAFF4_HEADER 0x2E7261FD 121 122 123 /** 124 * Give meta data to LE. 125 * 126 * @param s utf-8 string meta data value 127 * @param t type of the meta data 128 */ 129 #define ADD(s,t) do { \ 130 if (0 != ec->proc (ec->cls, "real", t, \ 131 EXTRACTOR_METAFORMAT_C_STRING, \ 132 "text/plain", s, strlen (s) + 1)) \ 133 { return; } \ 134 } while (0) 135 136 137 static void 138 processMediaProperties (const struct MediaProperties *prop, 139 struct EXTRACTOR_ExtractContext *ec) 140 { 141 uint8_t mime_type_size; 142 uint32_t prop_size; 143 144 prop_size = ntohl (prop->size); 145 if (prop_size <= sizeof (struct MediaProperties)) 146 return; 147 if (0 != prop->object_version) 148 return; 149 if (prop_size <= prop->stream_name_size + sizeof (uint8_t) 150 + sizeof (struct MediaProperties)) 151 return; 152 mime_type_size = prop->data[prop->stream_name_size]; 153 if (prop_size > prop->stream_name_size + sizeof (uint8_t) 154 + mime_type_size + sizeof (struct MediaProperties)) 155 { 156 char data[mime_type_size + 1]; 157 158 memcpy (data, 159 &prop->data[prop->stream_name_size + 1], 160 mime_type_size); 161 data[mime_type_size] = '\0'; 162 ADD (data, 163 EXTRACTOR_METATYPE_MIMETYPE); 164 } 165 } 166 167 168 static void 169 processContentDescription (const struct ContentDescription *prop, 170 struct EXTRACTOR_ExtractContext *ec) 171 { 172 uint16_t author_len; 173 uint16_t copyright_len; 174 uint16_t comment_len; 175 uint16_t title_len; 176 uint32_t prop_size; 177 178 prop_size = ntohl (prop->size); 179 if (prop_size <= sizeof (struct ContentDescription)) 180 return; 181 if (0 != prop->object_version) 182 return; 183 title_len = ntohs (prop->title_len); 184 if (prop_size <= 185 title_len 186 + sizeof (struct ContentDescription)) 187 return; 188 if (title_len > 0) 189 { 190 char title[title_len + 1]; 191 192 memcpy (title, 193 &prop->data[0], 194 title_len); 195 title[title_len] = '\0'; 196 ADD (title, 197 EXTRACTOR_METATYPE_TITLE); 198 } 199 if (prop_size <= 200 title_len 201 + sizeof (uint16_t) 202 + sizeof (struct ContentDescription)) 203 return; 204 author_len = read_be16 (&prop->data[title_len]); 205 if (prop_size <= 206 title_len 207 + sizeof (uint16_t) 208 + author_len 209 + sizeof (struct ContentDescription)) 210 return; 211 if (author_len > 0) 212 { 213 char author[author_len + 1]; 214 215 memcpy (author, 216 &prop->data[title_len 217 + sizeof (uint16_t)], 218 author_len); 219 author[author_len] = '\0'; 220 ADD (author, 221 EXTRACTOR_METATYPE_AUTHOR_NAME); 222 } 223 if (prop_size <= 224 title_len 225 + sizeof (uint16_t) 226 + author_len 227 + sizeof (uint16_t) 228 + sizeof (struct ContentDescription)) 229 return; 230 copyright_len = read_be16 (&prop->data[title_len 231 + author_len 232 + sizeof (uint16_t)]); 233 if (prop_size <= 234 title_len 235 + sizeof (uint16_t) 236 + author_len 237 + sizeof (uint16_t) 238 + copyright_len 239 + sizeof (struct ContentDescription)) 240 return; 241 if (copyright_len > 0) 242 { 243 char copyright[copyright_len + 1]; 244 245 memcpy (copyright, 246 &prop->data[title_len 247 + sizeof (uint16_t) * 2 248 + author_len], 249 copyright_len); 250 copyright[copyright_len] = '\0'; 251 ADD (copyright, 252 EXTRACTOR_METATYPE_COPYRIGHT); 253 } 254 255 if (prop_size <= 256 title_len 257 + sizeof (uint16_t) 258 + author_len 259 + sizeof (uint16_t) 260 + copyright_len 261 + sizeof (uint16_t) 262 + sizeof (struct ContentDescription)) 263 return; 264 comment_len = read_be16 (&prop->data[title_len 265 + author_len 266 + copyright_len 267 + 2 * sizeof (uint16_t)]); 268 if (prop_size < 269 title_len 270 + sizeof (uint16_t) 271 + author_len 272 + sizeof (uint16_t) 273 + copyright_len 274 + sizeof (uint16_t) 275 + comment_len 276 + sizeof (struct ContentDescription)) 277 return; 278 279 if (comment_len > 0) 280 { 281 char comment[comment_len + 1]; 282 283 memcpy (comment, 284 &prop->data[title_len 285 + sizeof (uint16_t) * 3 286 + author_len 287 + copyright_len], 288 comment_len); 289 comment[comment_len] = '\0'; 290 ADD (comment, 291 EXTRACTOR_METATYPE_COMMENT); 292 } 293 } 294 295 296 LE_NETWORK_STRUCT_BEGIN 297 struct RAFF_Header 298 { 299 uint16_t version; 300 } LE_PACKED; 301 LE_NETWORK_STRUCT_END 302 303 LE_NETWORK_STRUCT_BEGIN 304 struct RAFF3_Header 305 { 306 uint8_t unknown[10]; 307 uint32_t data_size; 308 /* 309 uint8_t tlen; 310 uint8_t title[tlen]; 311 uint8_t alen; 312 uint8_t author[alen]; 313 uint8_t clen; 314 uint8_t copyright[clen]; 315 uint8_t aplen; 316 uint8_t app[aplen]; */ 317 } LE_PACKED; 318 LE_NETWORK_STRUCT_END 319 320 321 #define RAFF3_HDR_SIZE 14 322 323 324 LE_NETWORK_STRUCT_BEGIN 325 struct RAFF4_Header 326 { 327 uint16_t version; 328 uint16_t revision; 329 uint16_t header_length; 330 uint16_t compression_type; 331 uint32_t granularity; 332 uint32_t total_bytes; 333 uint32_t bytes_per_minute; 334 uint32_t bytes_per_minute2; 335 uint16_t interleave_factor; 336 uint16_t interleave_block_size; 337 uint32_t user_data; 338 float sample_rate; 339 uint16_t sample_size; 340 uint16_t channels; 341 uint8_t interleave_code[5]; 342 uint8_t compression_code[5]; 343 uint8_t is_interleaved; 344 uint8_t copy_byte; 345 uint8_t stream_type; 346 /* 347 uint8_t tlen; 348 uint8_t title[tlen]; 349 uint8_t alen; 350 uint8_t author[alen]; 351 uint8_t clen; 352 uint8_t copyright[clen]; 353 uint8_t aplen; 354 uint8_t app[aplen]; */ 355 } LE_PACKED; 356 LE_NETWORK_STRUCT_END 357 358 #define RAFF4_HDR_SIZE 53 359 360 361 static void 362 extract_raff3 (struct EXTRACTOR_ExtractContext *ec, 363 const void *ptr, 364 size_t size) 365 { 366 const uint8_t *data = ptr; 367 uint8_t tlen; 368 uint8_t alen; 369 uint8_t clen; 370 uint8_t aplen; 371 372 if (size <= RAFF3_HDR_SIZE + 8) 373 return; 374 tlen = data[8 + RAFF3_HDR_SIZE]; 375 if (tlen + RAFF3_HDR_SIZE + 12 > size) 376 return; 377 if (tlen > 0) 378 { 379 char x[tlen + 1]; 380 381 memcpy (x, 382 &data[9 + RAFF3_HDR_SIZE], 383 tlen); 384 x[tlen] = '\0'; 385 ADD (x, 386 EXTRACTOR_METATYPE_TITLE); 387 } 388 alen = data[9 + tlen + RAFF3_HDR_SIZE]; 389 if (tlen + alen + RAFF3_HDR_SIZE + 12 > size) 390 return; 391 if (alen > 0) 392 { 393 char x[alen + 1]; 394 395 memcpy (x, 396 &data[10 + RAFF3_HDR_SIZE + tlen], 397 alen); 398 x[alen] = '\0'; 399 ADD (x, 400 EXTRACTOR_METATYPE_AUTHOR_NAME); 401 } 402 clen = data[10 + tlen + alen + RAFF3_HDR_SIZE]; 403 if (tlen + alen + clen + RAFF3_HDR_SIZE + 12 > size) 404 return; 405 if (clen > 0) 406 { 407 char x[clen + 1]; 408 409 memcpy (x, 410 &data[11 + RAFF3_HDR_SIZE + tlen + alen], 411 clen); 412 x[clen] = '\0'; 413 ADD (x, 414 EXTRACTOR_METATYPE_COPYRIGHT); 415 } 416 aplen = data[11 + tlen + clen + alen + RAFF3_HDR_SIZE]; 417 if (tlen + alen + clen + aplen + RAFF3_HDR_SIZE + 12 > size) 418 return; 419 if (aplen > 0) 420 { 421 char x[aplen + 1]; 422 423 memcpy (x, 424 &data[12 + RAFF3_HDR_SIZE + tlen + alen + clen], 425 aplen); 426 x[aplen] = '\0'; 427 ADD (x, 428 EXTRACTOR_METATYPE_UNKNOWN); 429 } 430 } 431 432 433 static void 434 extract_raff4 (struct EXTRACTOR_ExtractContext *ec, 435 const void *ptr, 436 size_t size) 437 { 438 const uint8_t *data = ptr; 439 uint8_t tlen; 440 uint8_t alen; 441 uint8_t clen; 442 uint8_t aplen; 443 444 if (size <= RAFF4_HDR_SIZE + 16 + 4) 445 return; 446 tlen = data[16 + RAFF4_HDR_SIZE]; 447 if (tlen + RAFF4_HDR_SIZE + 20 > size) 448 return; 449 alen = data[17 + tlen + RAFF4_HDR_SIZE]; 450 if (tlen + alen + RAFF4_HDR_SIZE + 20 > size) 451 return; 452 clen = data[18 + tlen + alen + RAFF4_HDR_SIZE]; 453 if (tlen + alen + clen + RAFF4_HDR_SIZE + 20 > size) 454 return; 455 aplen = data[19 + tlen + clen + alen + RAFF4_HDR_SIZE]; 456 if (tlen + alen + clen + aplen + RAFF4_HDR_SIZE + 20 > size) 457 return; 458 if (tlen > 0) 459 { 460 char x[tlen + 1]; 461 462 memcpy (x, 463 &data[17 + RAFF4_HDR_SIZE], 464 tlen); 465 x[tlen] = '\0'; 466 ADD (x, 467 EXTRACTOR_METATYPE_TITLE); 468 } 469 if (alen > 0) 470 { 471 char x[alen + 1]; 472 473 memcpy (x, 474 &data[18 + RAFF4_HDR_SIZE + tlen], 475 alen); 476 x[alen] = '\0'; 477 ADD (x, 478 EXTRACTOR_METATYPE_AUTHOR_NAME); 479 } 480 if (clen > 0) 481 { 482 char x[clen + 1]; 483 484 memcpy (x, 485 &data[19 + RAFF4_HDR_SIZE + tlen + alen], 486 clen); 487 x[clen] = '\0'; 488 ADD (x, 489 EXTRACTOR_METATYPE_COPYRIGHT); 490 } 491 if (aplen > 0) 492 { 493 char x[aplen + 1]; 494 495 memcpy (x, 496 &data[20 + RAFF4_HDR_SIZE + tlen + alen + clen], 497 aplen); 498 x[aplen] = '\0'; 499 ADD (x, 500 EXTRACTOR_METATYPE_UNKNOWN); 501 } 502 } 503 504 505 static void 506 extract_raff (struct EXTRACTOR_ExtractContext *ec, 507 const void *ptr, 508 size_t size) 509 { 510 const uint8_t *data = ptr; 511 const struct RAFF_Header *hdr; 512 513 /* HELIX */ 514 if (size <= sizeof (*hdr) + 4) 515 return; 516 ADD ("audio/vnd.rn-realaudio", 517 EXTRACTOR_METATYPE_MIMETYPE); 518 hdr = (const struct RAFF_Header *) &data[4]; 519 switch (ntohs (hdr->version)) 520 { 521 case 3: 522 extract_raff3 (ec, 523 ptr, 524 size); 525 break; 526 case 4: 527 extract_raff4 (ec, 528 ptr, 529 size); 530 break; 531 } 532 } 533 534 535 /* old real format */ 536 static void 537 extract_real (struct EXTRACTOR_ExtractContext *ec, 538 const void *data, 539 size_t size) 540 { 541 uint64_t off = 0; 542 size_t pos = 0; 543 544 while (1) 545 { 546 uint32_t length; 547 548 if ( (pos + 8 > size) || 549 (pos + 8 < pos) || 550 (pos + (length = read_be32 ((const char *) data + pos + 4)) > size) ) 551 { 552 uint64_t noff; 553 void *in; 554 ssize_t isize; 555 556 noff = ec->seek (ec->cls, 557 off + pos, 558 SEEK_SET); 559 if (-1 == noff) 560 return; 561 isize = ec->read (ec->cls, 562 &in, 563 32 * 1024); 564 if (isize < 8) 565 return; 566 data = in; 567 size = isize; 568 off = noff; 569 pos = 0; 570 /* re-read the atom header from the freshly filled buffer; 571 the previous value of 'length' refers to the old buffer 572 (or was never assigned due to short-circuit evaluation) */ 573 length = read_be32 ((const char *) data + pos + 4); 574 } 575 if (length <= 8) 576 return; 577 if ( (pos + length > size) || 578 (pos + length < pos) ) 579 return; 580 switch (read_be32 ((const char *) data + pos)) 581 { 582 case MDPR_HEADER: 583 processMediaProperties (data + pos, 584 ec); 585 pos += length; 586 break; 587 case CONT_HEADER: 588 processContentDescription (data + pos, 589 ec); 590 pos += length; 591 break; 592 case REAL_HEADER: /* treat like default */ 593 default: 594 pos += length; 595 break; 596 } 597 } 598 } 599 600 601 /** 602 * "extract" metadata from a REAL file 603 * 604 * @param ec extraction context 605 */ 606 void 607 EXTRACTOR_real_extract_method (struct EXTRACTOR_ExtractContext *ec) 608 { 609 void *data; 610 ssize_t n; 611 612 n = ec->read (ec->cls, 613 &data, 614 sizeof (struct RAFF4_Header) + 4 * 256); 615 if (n < (ssize_t) sizeof (uint32_t)) 616 return; 617 switch (read_be32 (data)) 618 { 619 case RAFF4_HEADER: 620 extract_raff (ec, 621 data, 622 n); 623 break; 624 case REAL_HEADER: 625 extract_real (ec, 626 data, 627 n); 628 break; 629 } 630 } 631 632 633 /* end of real_extractor.c */