/src/flac/src/metaflac/operations_shorthand_picture.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* metaflac - Command-line FLAC metadata editor |
2 | | * Copyright (C) 2001-2009 Josh Coalson |
3 | | * Copyright (C) 2011-2023 Xiph.Org Foundation |
4 | | * |
5 | | * This program is free software; you can redistribute it and/or |
6 | | * modify it under the terms of the GNU General Public License |
7 | | * as published by the Free Software Foundation; either version 2 |
8 | | * of the License, or (at your option) any later version. |
9 | | * |
10 | | * This program is distributed in the hope that it will be useful, |
11 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | | * GNU General Public License for more details. |
14 | | * |
15 | | * You should have received a copy of the GNU General Public License along |
16 | | * with this program; if not, write to the Free Software Foundation, Inc., |
17 | | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
18 | | */ |
19 | | |
20 | | #ifdef HAVE_CONFIG_H |
21 | | # include <config.h> |
22 | | #endif |
23 | | |
24 | | #include <errno.h> |
25 | | #include <string.h> |
26 | | #include "options.h" |
27 | | #include "utils.h" |
28 | | #include "FLAC/assert.h" |
29 | | #include "share/grabbag.h" /* for grabbag__picture_parse_specification() etc */ |
30 | | |
31 | | #include "operations_shorthand.h" |
32 | | |
33 | | static FLAC__bool import_pic_from(const char *filename, FLAC__StreamMetadata **picture, const char *specification, FLAC__bool *needs_write); |
34 | | static FLAC__bool export_pic_to(const char *filename, const FLAC__StreamMetadata *picture, const char *pic_filename); |
35 | | |
36 | | FLAC__bool do_shorthand_operation__picture(const char *filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write) |
37 | 1.05k | { |
38 | 1.05k | FLAC__bool ok = true, has_type1 = false, has_type2 = false; |
39 | 1.05k | FLAC__StreamMetadata *picture = 0; |
40 | 1.05k | FLAC__Metadata_Iterator *iterator = FLAC__metadata_iterator_new(); |
41 | | |
42 | 1.05k | if(0 == iterator) |
43 | 0 | die("out of memory allocating iterator"); |
44 | | |
45 | 1.05k | FLAC__metadata_iterator_init(iterator, chain); |
46 | | |
47 | 1.05k | switch(operation->type) { |
48 | 1.02k | case OP__IMPORT_PICTURE_FROM: |
49 | 1.02k | ok = import_pic_from(filename, &picture, operation->argument.specification.value, needs_write); |
50 | 1.02k | if(ok) { |
51 | | /* append PICTURE block */ |
52 | 1.94k | while(FLAC__metadata_iterator_next(iterator)) |
53 | 1.50k | ; |
54 | 439 | if(!FLAC__metadata_iterator_insert_block_after(iterator, picture)) { |
55 | 0 | print_error_with_chain_status(chain, "%s: ERROR: adding new PICTURE block to metadata", filename); |
56 | 0 | FLAC__metadata_object_delete(picture); |
57 | 0 | ok = false; |
58 | 0 | } |
59 | 439 | } |
60 | 1.02k | if(ok) { |
61 | | /* check global PICTURE constraints (max 1 block each of type=1 and type=2) */ |
62 | 2.38k | while(FLAC__metadata_iterator_prev(iterator)) |
63 | 1.94k | ; |
64 | 2.38k | do { |
65 | 2.38k | FLAC__StreamMetadata *block = FLAC__metadata_iterator_get_block(iterator); |
66 | 2.38k | if(block->type == FLAC__METADATA_TYPE_PICTURE) { |
67 | 1.56k | if(block->data.picture.type == FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD) { |
68 | 135 | if(has_type1) { |
69 | 103 | print_error_with_chain_status(chain, "%s: ERROR: FLAC stream can only have one 32x32 standard icon (type=1) PICTURE block", filename); |
70 | 103 | ok = false; |
71 | 103 | } |
72 | 135 | has_type1 = true; |
73 | 135 | } |
74 | 1.42k | else if(block->data.picture.type == FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON) { |
75 | 73 | if(has_type2) { |
76 | 13 | print_error_with_chain_status(chain, "%s: ERROR: FLAC stream can only have one icon (type=2) PICTURE block", filename); |
77 | 13 | ok = false; |
78 | 13 | } |
79 | 73 | has_type2 = true; |
80 | 73 | } |
81 | 1.56k | } |
82 | 2.38k | } while(FLAC__metadata_iterator_next(iterator)); |
83 | 439 | } |
84 | 1.02k | break; |
85 | 32 | case OP__EXPORT_PICTURE_TO: |
86 | 32 | { |
87 | 32 | const Argument_BlockNumber *a = operation->argument.export_picture_to.block_number_link; |
88 | 32 | int block_number = (a && a->num_entries > 0)? (int)a->entries[0] : -1; |
89 | 32 | unsigned i = 0; |
90 | 269 | do { |
91 | 269 | FLAC__StreamMetadata *block = FLAC__metadata_iterator_get_block(iterator); |
92 | 269 | if(block->type == FLAC__METADATA_TYPE_PICTURE && (block_number < 0 || i == (unsigned)block_number)) |
93 | 19 | picture = block; |
94 | 269 | i++; |
95 | 269 | } while(FLAC__metadata_iterator_next(iterator) && 0 == picture); |
96 | 32 | if(0 == picture) { |
97 | 13 | if(block_number < 0) |
98 | 13 | flac_fprintf(stderr, "%s: ERROR: FLAC file has no PICTURE block\n", filename); |
99 | 0 | else |
100 | 0 | flac_fprintf(stderr, "%s: ERROR: FLAC file has no PICTURE block at block #%d\n", filename, block_number); |
101 | 13 | ok = false; |
102 | 13 | } |
103 | 19 | else |
104 | 19 | ok = export_pic_to(filename, picture, operation->argument.filename.value); |
105 | 32 | } |
106 | 32 | break; |
107 | 0 | default: |
108 | 0 | ok = false; |
109 | 0 | FLAC__ASSERT(0); |
110 | 0 | break; |
111 | 1.05k | }; |
112 | | |
113 | 1.05k | FLAC__metadata_iterator_delete(iterator); |
114 | 1.05k | return ok; |
115 | 1.05k | } |
116 | | |
117 | | /* |
118 | | * local routines |
119 | | */ |
120 | | |
121 | | FLAC__bool import_pic_from(const char *filename, FLAC__StreamMetadata **picture, const char *specification, FLAC__bool *needs_write) |
122 | 1.02k | { |
123 | 1.02k | const char *error_message; |
124 | | |
125 | 1.02k | if(0 == specification || strlen(specification) == 0) { |
126 | 0 | flac_fprintf(stderr, "%s: ERROR: empty picture specification\n", filename); |
127 | 0 | return false; |
128 | 0 | } |
129 | | |
130 | 1.02k | *picture = grabbag__picture_parse_specification(specification, &error_message); |
131 | | |
132 | 1.02k | if(0 == *picture) { |
133 | 548 | flac_fprintf(stderr, "%s: ERROR: while parsing picture specification \"%s\": %s\n", filename, specification, error_message); |
134 | 548 | return false; |
135 | 548 | } |
136 | | |
137 | 478 | if(!FLAC__format_picture_is_legal(&(*picture)->data.picture, &error_message)) { |
138 | 39 | flac_fprintf(stderr, "%s: ERROR: new PICTURE block for \"%s\" is illegal: %s\n", filename, specification, error_message); |
139 | 39 | FLAC__metadata_object_delete(*picture); |
140 | 39 | *picture = 0; |
141 | 39 | return false; |
142 | 39 | } |
143 | | |
144 | 439 | *needs_write = true; |
145 | 439 | return true; |
146 | 478 | } |
147 | | |
148 | | FLAC__bool export_pic_to(const char *filename, const FLAC__StreamMetadata *picture, const char *pic_filename) |
149 | 19 | { |
150 | 19 | FILE *f; |
151 | 19 | const FLAC__uint32 len = picture->data.picture.data_length; |
152 | | |
153 | 19 | if(0 == pic_filename || strlen(pic_filename) == 0) { |
154 | 0 | flac_fprintf(stderr, "%s: ERROR: empty export file name\n", filename); |
155 | 0 | return false; |
156 | 0 | } |
157 | 19 | if(0 == strcmp(pic_filename, "-")) |
158 | 3 | f = grabbag__file_get_binary_stdout(); |
159 | 16 | else |
160 | 16 | f = flac_fopen(pic_filename, "wb"); |
161 | | |
162 | 19 | if(0 == f) { |
163 | 1 | flac_fprintf(stderr, "%s: ERROR: can't open export file %s: %s\n", filename, pic_filename, strerror(errno)); |
164 | 1 | return false; |
165 | 1 | } |
166 | | |
167 | 18 | if(fwrite(picture->data.picture.data, 1, len, f) != len) { |
168 | 0 | flac_fprintf(stderr, "%s: ERROR: writing PICTURE data to file\n", filename); |
169 | 0 | if(f != stdout) |
170 | 0 | fclose(f); |
171 | 0 | return false; |
172 | 0 | } |
173 | | |
174 | 18 | if(f != stdout) |
175 | 15 | fclose(f); |
176 | | |
177 | 18 | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
178 | | /* Delete output file when fuzzing */ |
179 | 18 | if(f != stdout) |
180 | 15 | flac_unlink(pic_filename); |
181 | 18 | #endif |
182 | | |
183 | 18 | return true; |
184 | 18 | } |