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 }