libextractor

GNU libextractor
Log | Files | Refs | Submodules | README | LICENSE

flacfile.c (12409B)


      1 /*
      2 flacfile.c - FLAC file attachment utility
      3 Copyright Copyright (C) 2005 Ian Gulliver
      4 
      5 This program is free software; you can redistribute it and/or modify
      6 it under the terms of version 2 of the GNU General Public License as
      7 published by the Free Software Foundation.
      8 
      9 This program is distributed in the hope that it will be useful,
     10 but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12 GNU General Public License for more details.
     13 
     14 You should have received a copy of the GNU General Public License
     15 along with this program; if not, write to the Free Software
     16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
     17 */
     18 
     19 #include "firemake.h"
     20 #include <FLAC/metadata.h>
     21 #include <sys/types.h>
     22 #include <sys/stat.h>
     23 #include <fcntl.h>
     24 #include <unistd.h>
     25 #include <stdlib.h>
     26 #include <stdio.h>
     27 #include <string.h>
     28 
     29 const char *
     30 get_mime_type (const char *filename)
     31 {
     32   magic_t magic_flags;
     33   const char *mime_type;
     34 
     35   magic_flags = magic_open (MAGIC_MIME);
     36   if (magic_flags == NULL)
     37     {
     38       perror ("flacfile: magic_open");
     39       exit (1);
     40     }
     41 
     42   if (magic_load (magic_flags, NULL) != 0)
     43     {
     44       perror ("flacfile: magic_load");
     45       exit (1);
     46     }
     47 
     48   mime_type = magic_file (magic_flags, filename);
     49   if (mime_type == NULL)
     50     {
     51       fprintf (stderr,
     52                "flacfile: Unable to determine MIME type automatically; please specify one.\n");
     53     }
     54 
     55   return mime_type;
     56 }
     57 
     58 FLAC__StreamMetadata *
     59 new_block (const char *filename, const char *description, const char *mime)
     60 {
     61   // block size is mime length + description length + file length + 2
     62   size_t desc_len = strlen (description);
     63   size_t mime_len = strlen (mime);
     64   struct stat s;
     65   int f = open (filename, O_RDONLY);
     66   static FLAC__StreamMetadata meta;
     67   char *block;
     68   int pos = 0;
     69 
     70   if (desc_len > 255 || mime_len > 255)
     71     {
     72       fprintf (stderr,
     73                "flacfile: Description and MIME type must both be shorter than 256 characters.\n");
     74       exit (1);
     75     }
     76 
     77   if (f == -1)
     78     {
     79       perror ("flacfile: open(FLAC file)");
     80       exit (1);
     81     }
     82 
     83   if (fstat (f, &s) != 0)
     84     {
     85       perror ("flacfile: fstat(FLAC file)");
     86       exit (1);
     87     }
     88 
     89   block = malloc (desc_len + mime_len + s.st_size + 2);
     90   if (block == NULL)
     91     {
     92       perror ("flacfile: malloc");
     93       exit (1);
     94     }
     95 
     96   block[pos++] = (char) desc_len;
     97   strcpy (&block[pos], description);
     98   pos += desc_len;
     99 
    100   block[pos++] = (char) mime_len;
    101   strcpy (&block[pos], mime);
    102   pos += mime_len;
    103 
    104   if (read (f, &block[pos], s.st_size) != s.st_size)
    105     {
    106       perror ("flacfile: read(FLAC file)");
    107       exit (1);
    108     }
    109 
    110   meta.is_last = false;
    111   meta.length = desc_len + mime_len + s.st_size + 6;
    112   meta.type = FLAC__METADATA_TYPE_APPLICATION;
    113   memcpy (meta.data.application.id, "ATCH", 4);
    114   meta.data.application.data = block;
    115 
    116   return &meta;
    117 }
    118 
    119 int
    120 add (FLAC__Metadata_SimpleIterator * iter, char *description, char *attach,
    121      const char *mime_type)
    122 {
    123   if (mime_type == NULL)
    124     mime_type = get_mime_type (attach);
    125 
    126   if (FLAC__metadata_simple_iterator_insert_block_after
    127       (iter, new_block (attach, description, mime_type), true) != true)
    128     {
    129       perror ("flacfile: FLAC__metadata_simple_iterator_insert_block_after");
    130       exit (1);
    131     }
    132 
    133   return 0;
    134 }
    135 
    136 int
    137 list (FLAC__Metadata_SimpleIterator * iter)
    138 {
    139   while (1)
    140     {
    141       int pos;
    142       FLAC__StreamMetadata *meta;
    143       if (FLAC__metadata_simple_iterator_get_block_type (iter) !=
    144           FLAC__METADATA_TYPE_APPLICATION)
    145         {
    146           if (FLAC__metadata_simple_iterator_next (iter) == false)
    147             return 0;
    148           continue;
    149         }
    150 
    151       meta = FLAC__metadata_simple_iterator_get_block (iter);
    152       if (memcmp (meta->data.application.id, "ATCH", 4) != 0)
    153         {
    154           if (FLAC__metadata_simple_iterator_next (iter) == false)
    155             return 0;
    156           FLAC__metadata_object_delete (meta);
    157           continue;
    158         }
    159 
    160       /* this is one of our blocks */
    161       write (1, "Description: '", 14);
    162       if (meta->length < 1
    163           || meta->length < meta->data.application.data[0] + 1)
    164         {
    165           write (1, "(invalid block)\n", 16);
    166           if (FLAC__metadata_simple_iterator_next (iter) == false)
    167             return 0;
    168           FLAC__metadata_object_delete (meta);
    169           continue;
    170         }
    171       write (1, &meta->data.application.data[1],
    172              meta->data.application.data[0]);
    173       write (1, "', ", 3);
    174       pos = meta->data.application.data[0] + 1;
    175 
    176       write (1, "MIME: '", 7);
    177       if (meta->length < pos + 1
    178           || meta->length < meta->data.application.data[pos] + pos + 1)
    179         {
    180           write (1, "(invalid block)\n", 16);
    181           if (FLAC__metadata_simple_iterator_next (iter) == false)
    182             return 0;
    183           FLAC__metadata_object_delete (meta);
    184           continue;
    185         }
    186       write (1, &meta->data.application.data[pos + 1],
    187              meta->data.application.data[pos]);
    188       write (1, "', ", 3);
    189       pos += meta->data.application.data[pos] + 1;
    190 
    191       printf ("Length: %d\n", meta->length - pos - 4);
    192 
    193       FLAC__metadata_object_delete (meta);
    194 
    195       if (FLAC__metadata_simple_iterator_next (iter) == false)
    196         return 0;
    197     }
    198 }
    199 
    200 int
    201 extract (FLAC__Metadata_SimpleIterator * iter, char *description,
    202          char *filename, char *mime)
    203 {
    204   while (1)
    205     {
    206       int pos;
    207       int o;
    208       FLAC__StreamMetadata *meta;
    209 
    210       if (FLAC__metadata_simple_iterator_get_block_type (iter) !=
    211           FLAC__METADATA_TYPE_APPLICATION)
    212         {
    213           if (FLAC__metadata_simple_iterator_next (iter) == false)
    214             return 1;
    215           continue;
    216         }
    217 
    218       meta = FLAC__metadata_simple_iterator_get_block (iter);
    219       if (memcmp (meta->data.application.id, "ATCH", 4) != 0)
    220         {
    221           if (FLAC__metadata_simple_iterator_next (iter) == false)
    222             return 1;
    223           FLAC__metadata_object_delete (meta);
    224           continue;
    225         }
    226 
    227       /* this is one of our blocks */
    228       if (meta->length < 1
    229           || meta->length < meta->data.application.data[0] + 1)
    230         {
    231           if (FLAC__metadata_simple_iterator_next (iter) == false)
    232             return 1;
    233           FLAC__metadata_object_delete (meta);
    234           continue;
    235         }
    236       if (meta->data.application.data[0] != strlen (description)
    237           || memcmp (&meta->data.application.data[1], description,
    238                      meta->data.application.data[0]) != 0)
    239         {
    240           if (FLAC__metadata_simple_iterator_next (iter) == false)
    241             return 1;
    242           FLAC__metadata_object_delete (meta);
    243           continue;
    244         }
    245       pos = meta->data.application.data[0] + 1;
    246 
    247       if (meta->length < pos + 1
    248           || meta->length < meta->data.application.data[pos] + pos + 1)
    249         {
    250           if (FLAC__metadata_simple_iterator_next (iter) == false)
    251             return 1;
    252           FLAC__metadata_object_delete (meta);
    253           continue;
    254         }
    255 
    256       if (mime != NULL &&
    257           (meta->data.application.data[pos] != strlen (mime)
    258            || memcmp (&meta->data.application.data[pos + 1], mime,
    259                       meta->data.application.data[pos]) != 0))
    260         {
    261           if (FLAC__metadata_simple_iterator_next (iter) == false)
    262             return 1;
    263           FLAC__metadata_object_delete (meta);
    264           continue;
    265         }
    266       pos += meta->data.application.data[pos] + 1;
    267 
    268       o = open (filename, O_WRONLY | O_CREAT, 0644);
    269       if (o == -1)
    270         {
    271           perror ("flacfile: open");
    272           exit (1);
    273         }
    274 
    275       if (write
    276           (o, &meta->data.application.data[pos], meta->length - pos - 4));
    277 
    278       FLAC__metadata_object_delete (meta);
    279 
    280       return 0;
    281     }
    282 }
    283 
    284 int
    285 my_remove (FLAC__Metadata_SimpleIterator * iter, char *description,
    286            char *mime)
    287 {
    288   while (1)
    289     {
    290       int pos;
    291       FLAC__StreamMetadata *meta;
    292 
    293       if (FLAC__metadata_simple_iterator_get_block_type (iter) !=
    294           FLAC__METADATA_TYPE_APPLICATION)
    295         {
    296           if (FLAC__metadata_simple_iterator_next (iter) == false)
    297             return 1;
    298           continue;
    299         }
    300 
    301       meta = FLAC__metadata_simple_iterator_get_block (iter);
    302       if (memcmp (meta->data.application.id, "ATCH", 4) != 0)
    303         {
    304           if (FLAC__metadata_simple_iterator_next (iter) == false)
    305             return 1;
    306           FLAC__metadata_object_delete (meta);
    307           continue;
    308         }
    309 
    310       /* this is one of our blocks */
    311       if (meta->length < 1
    312           || meta->length < meta->data.application.data[0] + 1)
    313         {
    314           if (FLAC__metadata_simple_iterator_next (iter) == false)
    315             return 1;
    316           FLAC__metadata_object_delete (meta);
    317           continue;
    318         }
    319       if (meta->data.application.data[0] != strlen (description)
    320           || memcmp (&meta->data.application.data[1], description,
    321                      meta->data.application.data[0]) != 0)
    322         {
    323           if (FLAC__metadata_simple_iterator_next (iter) == false)
    324             return 1;
    325           FLAC__metadata_object_delete (meta);
    326           continue;
    327         }
    328       pos = meta->data.application.data[0] + 1;
    329 
    330       if (meta->length < pos + 1
    331           || meta->length < meta->data.application.data[pos] + pos + 1)
    332         {
    333           if (FLAC__metadata_simple_iterator_next (iter) == false)
    334             return 1;
    335           FLAC__metadata_object_delete (meta);
    336           continue;
    337         }
    338 
    339       if (mime != NULL &&
    340           (meta->data.application.data[pos] != strlen (mime)
    341            || memcmp (&meta->data.application.data[pos + 1], mime,
    342                       meta->data.application.data[pos]) != 0))
    343         {
    344           if (FLAC__metadata_simple_iterator_next (iter) == false)
    345             return 1;
    346           FLAC__metadata_object_delete (meta);
    347           continue;
    348         }
    349       pos += meta->data.application.data[pos] + 1;
    350 
    351       if (FLAC__metadata_simple_iterator_delete_block (iter, false) != true)
    352         {
    353           perror ("flacfile: FLAC__metadata_simple_iterator_delete_block");
    354           exit (1);
    355         }
    356 
    357       FLAC__metadata_object_delete (meta);
    358 
    359       return 0;
    360     }
    361 }
    362 
    363 void
    364 usage ()
    365 {
    366   fprintf (stderr, "flacfile v" VERSION "\n\n");
    367   fprintf (stderr,
    368            "Usage: flacfile add <FLAC filename> <description> <filename to attach> [<MIME type>]\n");
    369   fprintf (stderr, "       flacfile list <FLAC filename>\n");
    370   fprintf (stderr,
    371            "       flacfile extract <FLAC filename> <description> <filename to save as> [<MIME type>]\n");
    372   fprintf (stderr,
    373            "       flacfile remove <FLAC filename> <description> [<MIME type>]\n");
    374   exit (1);
    375 }
    376 
    377 int
    378 main (int argc, char *argv[])
    379 {
    380   FLAC__Metadata_SimpleIterator *iter;
    381 
    382   if (argc < 3 || argc > 6)
    383     usage ();
    384 
    385   iter = FLAC__metadata_simple_iterator_new ();
    386   if (iter == NULL)
    387     {
    388       perror ("flacfile: FLAC__metadata_simple_iterator_new");
    389       exit (1);
    390     }
    391 
    392   if (FLAC__metadata_simple_iterator_init (iter, argv[2], false, false) !=
    393       true)
    394     {
    395       perror ("flacfile: FLAC__metadata_simple_iterator_init");
    396       exit (1);
    397     }
    398 
    399   if (strcmp (argv[1], "add") == 0)
    400     {
    401       if (argc < 5)
    402         usage ();
    403       if (strcmp (argv[2], argv[4]) == 0)
    404         {
    405           fprintf (stderr,
    406                    "flacfile: You didn't really want to attach a file to itself.\n");
    407           exit (1);
    408         }
    409       return add (iter, argv[3], argv[4], argc == 6 ? argv[5] : NULL);
    410     }
    411   else if (strcmp (argv[1], "list") == 0)
    412     {
    413       if (argc > 3)
    414         usage ();
    415       return list (iter);
    416     }
    417   else if (strcmp (argv[1], "extract") == 0)
    418     {
    419       int r;
    420       if (argc < 5)
    421         usage ();
    422       if (strcmp (argv[2], argv[4]) == 0)
    423         {
    424           fprintf (stderr,
    425                    "flacfile: You didn't really want to extract a file onto itself.\n");
    426           exit (1);
    427         }
    428       r = extract (iter, argv[3], argv[4], argc == 6 ? argv[5] : NULL);
    429       if (r != 0)
    430         fprintf (stderr, "flacfile: requested block not found\n");
    431       return r;
    432     }
    433   else if (strcmp (argv[1], "remove") == 0)
    434     {
    435       int r;
    436       if (argc < 4)
    437         usage ();
    438       r = my_remove (iter, argv[3], argc == 5 ? argv[4] : NULL);
    439       if (r != 0)
    440         fprintf (stderr, "flacfile: requested block not found\n");
    441       return r;
    442     }
    443   else
    444     {
    445       usage ();
    446       return 1;
    447     }
    448 }