1 /* libmpdclient
2 (c) 2003-2019 The Music Player Daemon Project
3 This project's homepage is: http://www.musicpd.org
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
8
9 - Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11
12 - Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
15
16 - Neither the name of the Music Player Daemon nor the names of its
17 contributors may be used to endorse or promote products derived from
18 this software without specific prior written permission.
19
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
24 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include <mpd/song.h>
34 #include <mpd/audio_format.h>
35 #include <mpd/pair.h>
36 #include <mpd/recv.h>
37 #include "internal.h"
38 #include "iso8601.h"
39 #include "uri.h"
40 #include "iaf.h"
41
42 #include <assert.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <errno.h>
46
47 struct mpd_tag_value {
48 struct mpd_tag_value *next;
49
50 char *value;
51 };
52
53 struct mpd_song {
54 char *uri;
55
56 struct mpd_tag_value tags[MPD_TAG_COUNT];
57
58 /**
59 * Duration of the song in seconds, or 0 for unknown.
60 */
61 unsigned duration;
62
63 /**
64 * Duration of the song in milliseconds, or 0 for unknown.
65 */
66 unsigned duration_ms;
67
68 /**
69 * Start of the virtual song within the physical file in
70 * seconds.
71 */
72 unsigned start;
73
74 /**
75 * End of the virtual song within the physical file in
76 * seconds. Zero means that the physical song file is
77 * played to the end.
78 */
79 unsigned end;
80
81 /**
82 * The POSIX UTC time stamp of the last modification, or 0 if
83 * that is unknown.
84 */
85 time_t last_modified;
86
87 /**
88 * The position of this song within the queue.
89 */
90 unsigned pos;
91
92 /**
93 * The id of this song within the queue.
94 */
95 unsigned id;
96
97 /**
98 * The priority of this song within the queue.
99 */
100 unsigned prio;
101
102 #ifndef NDEBUG
103 /**
104 * This flag is used in an assertion: when it is set, you must
105 * not call mpd_song_feed() again. It is a safeguard for
106 * buggy callers.
107 */
108 bool finished;
109 #endif
110
111 /**
112 * The audio format as reported by MPD's decoder plugin.
113 */
114 struct mpd_audio_format audio_format;
115 };
116
117 static struct mpd_song *
mpd_song_new(const char * uri)118 mpd_song_new(const char *uri)
119 {
120 struct mpd_song *song;
121
122 assert(uri != NULL);
123 assert(mpd_verify_uri(uri));
124
125 song = malloc(sizeof(*song));
126 if (song == NULL)
127 /* out of memory */
128 return NULL;
129
130 song->uri = strdup(uri);
131 if (song->uri == NULL) {
132 free(song);
133 return NULL;
134 }
135
136 for (unsigned i = 0; i < MPD_TAG_COUNT; ++i)
137 song->tags[i].value = NULL;
138
139 song->duration = 0;
140 song->duration_ms = 0;
141 song->start = 0;
142 song->end = 0;
143 song->last_modified = 0;
144 song->pos = 0;
145 song->id = 0;
146 song->prio = 0;
147
148 memset(&song->audio_format, 0, sizeof(song->audio_format));
149
150 #ifndef NDEBUG
151 song->finished = false;
152 #endif
153
154 return song;
155 }
156
mpd_song_free(struct mpd_song * song)157 void mpd_song_free(struct mpd_song *song) {
158 assert(song != NULL);
159
160 free(song->uri);
161
162 for (unsigned i = 0; i < MPD_TAG_COUNT; ++i) {
163 struct mpd_tag_value *tag = &song->tags[i], *next;
164
165 if (tag->value == NULL)
166 continue;
167
168 free(tag->value);
169
170 tag = tag->next;
171
172 while (tag != NULL) {
173 assert(tag->value != NULL);
174 free(tag->value);
175
176 next = tag->next;
177 free(tag);
178 tag = next;
179 }
180 }
181
182 free(song);
183 }
184
185 static bool
186 mpd_song_add_tag(struct mpd_song *song,
187 enum mpd_tag_type type, const char *value);
188
189 struct mpd_song *
mpd_song_dup(const struct mpd_song * song)190 mpd_song_dup(const struct mpd_song *song)
191 {
192 struct mpd_song *ret;
193 bool success;
194
195 assert(song != NULL);
196
197 ret = mpd_song_new(song->uri);
198 if (ret == NULL)
199 /* out of memory */
200 return NULL;
201
202 for (unsigned i = 0; i < MPD_TAG_COUNT; ++i) {
203 const struct mpd_tag_value *src_tag = &song->tags[i];
204
205 if (src_tag->value == NULL)
206 continue;
207
208 do {
209 success = mpd_song_add_tag(ret, i, src_tag->value);
210 if (!success) {
211 mpd_song_free(ret);
212 return NULL;
213 }
214
215 src_tag = src_tag->next;
216 } while (src_tag != NULL);
217 }
218
219 ret->duration = song->duration;
220 ret->duration_ms = song->duration_ms;
221 ret->start = song->start;
222 ret->end = song->end;
223 ret->last_modified = song->last_modified;
224 ret->pos = song->pos;
225 ret->id = song->id;
226 ret->prio = song->prio;
227
228 #ifndef NDEBUG
229 ret->finished = true;
230 #endif
231
232 return ret;
233 }
234
235 const char *
mpd_song_get_uri(const struct mpd_song * song)236 mpd_song_get_uri(const struct mpd_song *song)
237 {
238 assert(song != NULL);
239
240 return song->uri;
241 }
242
243
244 /**
245 * Adds a tag value to the song.
246 *
247 * @return true on success, false if the tag is not supported or if no
248 * memory could be allocated
249 */
250 static bool
mpd_song_add_tag(struct mpd_song * song,enum mpd_tag_type type,const char * value)251 mpd_song_add_tag(struct mpd_song *song,
252 enum mpd_tag_type type, const char *value)
253 {
254 struct mpd_tag_value *tag = &song->tags[type], *prev;
255
256 if ((int)type < 0 || type >= MPD_TAG_COUNT)
257 return false;
258
259 if (tag->value == NULL) {
260 tag->next = NULL;
261 tag->value = strdup(value);
262 if (tag->value == NULL)
263 return false;
264 } else {
265 while (tag->next != NULL)
266 tag = tag->next;
267
268 prev = tag;
269 tag = malloc(sizeof(*tag));
270 if (tag == NULL)
271 return NULL;
272
273 tag->value = strdup(value);
274 if (tag->value == NULL) {
275 free(tag);
276 return false;
277 }
278
279 tag->next = NULL;
280 prev->next = tag;
281 }
282
283 return true;
284 }
285
286 #ifdef UNUSED_CODE
287 /**
288 * Removes all values of the specified tag.
289 */
290 static void
mpd_song_clear_tag(struct mpd_song * song,enum mpd_tag_type type)291 mpd_song_clear_tag(struct mpd_song *song, enum mpd_tag_type type)
292 {
293 struct mpd_tag_value *tag = &song->tags[type];
294
295 if ((unsigned)type >= MPD_TAG_COUNT)
296 return;
297
298 if (tag->value == NULL)
299 /* this tag type is empty */
300 return;
301
302 /* free and clear the first value */
303 free(tag->value);
304 tag->value = NULL;
305
306 /* free all other values; no need to clear the "next" pointer,
307 because it is "undefined" as long as value==NULL */
308 while ((tag = tag->next) != NULL)
309 free(tag->value);
310 }
311 #endif
312
313 const char *
mpd_song_get_tag(const struct mpd_song * song,enum mpd_tag_type type,unsigned idx)314 mpd_song_get_tag(const struct mpd_song *song,
315 enum mpd_tag_type type, unsigned idx)
316 {
317 const struct mpd_tag_value *tag = &song->tags[type];
318
319 if ((int)type < 0)
320 return NULL;
321
322 if (tag->value == NULL)
323 return NULL;
324
325 while (idx-- > 0) {
326 tag = tag->next;
327 if (tag == NULL)
328 return NULL;
329 }
330
331 return tag->value;
332 }
333
334 static void
mpd_song_set_duration(struct mpd_song * song,unsigned duration)335 mpd_song_set_duration(struct mpd_song *song, unsigned duration)
336 {
337 song->duration = duration;
338 }
339
340 unsigned
mpd_song_get_duration(const struct mpd_song * song)341 mpd_song_get_duration(const struct mpd_song *song)
342 {
343 assert(song != NULL);
344
345 return song->duration > 0
346 ? song->duration
347 : (song->duration_ms + 500u) / 1000u;
348 }
349
350 static void
mpd_song_set_duration_ms(struct mpd_song * song,unsigned duration_ms)351 mpd_song_set_duration_ms(struct mpd_song *song, unsigned duration_ms)
352 {
353 song->duration_ms = duration_ms;
354 }
355
356 unsigned
mpd_song_get_duration_ms(const struct mpd_song * song)357 mpd_song_get_duration_ms(const struct mpd_song *song)
358 {
359 assert(song != NULL);
360
361 return song->duration_ms > 0
362 ? song->duration_ms
363 : (song->duration * 1000u);
364 }
365
366 unsigned
mpd_song_get_start(const struct mpd_song * song)367 mpd_song_get_start(const struct mpd_song *song)
368 {
369 assert(song != NULL);
370
371 return song->start;
372 }
373
374 unsigned
mpd_song_get_end(const struct mpd_song * song)375 mpd_song_get_end(const struct mpd_song *song)
376 {
377 assert(song != NULL);
378
379 return song->end;
380 }
381
382 static void
mpd_song_set_last_modified(struct mpd_song * song,time_t mtime)383 mpd_song_set_last_modified(struct mpd_song *song, time_t mtime)
384 {
385 song->last_modified = mtime;
386 }
387
388 time_t
mpd_song_get_last_modified(const struct mpd_song * song)389 mpd_song_get_last_modified(const struct mpd_song *song)
390 {
391 assert(song != NULL);
392
393 return song->last_modified;
394 }
395
396 void
mpd_song_set_pos(struct mpd_song * song,unsigned pos)397 mpd_song_set_pos(struct mpd_song *song, unsigned pos)
398 {
399 assert(song != NULL);
400
401 song->pos = pos;
402 }
403
404 unsigned
mpd_song_get_pos(const struct mpd_song * song)405 mpd_song_get_pos(const struct mpd_song *song)
406 {
407 assert(song != NULL);
408
409 return song->pos;
410 }
411
412 static void
mpd_song_set_id(struct mpd_song * song,unsigned id)413 mpd_song_set_id(struct mpd_song *song, unsigned id)
414 {
415 song->id = id;
416 }
417
418 unsigned
mpd_song_get_id(const struct mpd_song * song)419 mpd_song_get_id(const struct mpd_song *song)
420 {
421 assert(song != NULL);
422
423 return song->id;
424 }
425
426 static void
mpd_song_set_prio(struct mpd_song * song,unsigned prio)427 mpd_song_set_prio(struct mpd_song *song, unsigned prio)
428 {
429 song->prio = prio;
430 }
431
432 unsigned
mpd_song_get_prio(const struct mpd_song * song)433 mpd_song_get_prio(const struct mpd_song *song)
434 {
435 assert(song != NULL);
436
437 return song->prio;
438 }
439
440 const struct mpd_audio_format *
mpd_song_get_audio_format(const struct mpd_song * song)441 mpd_song_get_audio_format(const struct mpd_song *song)
442 {
443 assert(song != NULL);
444
445 return !mpd_audio_format_is_empty(&song->audio_format)
446 ? &song->audio_format
447 : NULL;
448 }
449
450 struct mpd_song *
mpd_song_begin(const struct mpd_pair * pair)451 mpd_song_begin(const struct mpd_pair *pair)
452 {
453 assert(pair != NULL);
454 assert(pair->name != NULL);
455 assert(pair->value != NULL);
456
457 if (strcmp(pair->name, "file") != 0 || !mpd_verify_uri(pair->value)) {
458 errno = EINVAL;
459 return NULL;
460 }
461
462 return mpd_song_new(pair->value);
463 }
464
465 static void
mpd_song_parse_range(struct mpd_song * song,const char * value)466 mpd_song_parse_range(struct mpd_song *song, const char *value)
467 {
468 assert(song != NULL);
469 assert(value != NULL);
470
471 char *endptr;
472 double start, end;
473
474 if (*value == '-') {
475 start = 0.0;
476 end = strtod(value + 1, NULL);
477 } else {
478 start = strtod(value, &endptr);
479 if (*endptr != '-')
480 return;
481
482 end = strtod(endptr + 1, NULL);
483 }
484
485 song->start = start > 0.0 ? (unsigned)start : 0;
486
487 if (end > 0.0) {
488 song->end = (unsigned)end;
489 if (song->end == 0)
490 /* round up, because the caller must sees that
491 there's an upper limit */
492 song->end = 1;
493 } else
494 song->end = 0;
495 }
496
497 static void
mpd_song_parse_audio_format(struct mpd_song * song,const char * value)498 mpd_song_parse_audio_format(struct mpd_song *song, const char *value)
499 {
500 assert(song != NULL);
501 assert(value != NULL);
502
503 mpd_parse_audio_format(&song->audio_format, value);
504 }
505
506 bool
mpd_song_feed(struct mpd_song * song,const struct mpd_pair * pair)507 mpd_song_feed(struct mpd_song *song, const struct mpd_pair *pair)
508 {
509 enum mpd_tag_type tag_type;
510
511 assert(song != NULL);
512 assert(!song->finished);
513 assert(pair != NULL);
514 assert(pair->name != NULL);
515 assert(pair->value != NULL);
516
517 if (strcmp(pair->name, "file") == 0) {
518 #ifndef NDEBUG
519 song->finished = true;
520 #endif
521 return false;
522 }
523
524 if (*pair->value == 0)
525 return true;
526
527 tag_type = mpd_tag_name_parse(pair->name);
528 if (tag_type != MPD_TAG_UNKNOWN) {
529 mpd_song_add_tag(song, tag_type, pair->value);
530 return true;
531 }
532
533 if (strcmp(pair->name, "Time") == 0)
534 mpd_song_set_duration(song, atoi(pair->value));
535 else if (strcmp(pair->name, "duration") == 0)
536 mpd_song_set_duration_ms(song, 1000 * atof(pair->value));
537 else if (strcmp(pair->name, "Range") == 0)
538 mpd_song_parse_range(song, pair->value);
539 else if (strcmp(pair->name, "Last-Modified") == 0)
540 mpd_song_set_last_modified(song, iso8601_datetime_parse(pair->value));
541 else if (strcmp(pair->name, "Pos") == 0)
542 mpd_song_set_pos(song, atoi(pair->value));
543 else if (strcmp(pair->name, "Id") == 0)
544 mpd_song_set_id(song, atoi(pair->value));
545 else if (strcmp(pair->name, "Prio") == 0)
546 mpd_song_set_prio(song, atoi(pair->value));
547 else if (strcmp(pair->name, "Format") == 0)
548 mpd_song_parse_audio_format(song, pair->value);
549
550 return true;
551 }
552
553 struct mpd_song *
mpd_recv_song(struct mpd_connection * connection)554 mpd_recv_song(struct mpd_connection *connection)
555 {
556 struct mpd_pair *pair;
557 struct mpd_song *song;
558
559 pair = mpd_recv_pair_named(connection, "file");
560 if (pair == NULL)
561 return NULL;
562
563 song = mpd_song_begin(pair);
564 mpd_return_pair(connection, pair);
565 if (song == NULL) {
566 mpd_error_entity(&connection->error);
567 return NULL;
568 }
569
570 while ((pair = mpd_recv_pair(connection)) != NULL &&
571 mpd_song_feed(song, pair))
572 mpd_return_pair(connection, pair);
573
574 if (mpd_error_is_defined(&connection->error)) {
575 mpd_song_free(song);
576 return NULL;
577 }
578
579 /* unread this pair for the next mpd_recv_song() call */
580 mpd_enqueue_pair(connection, pair);
581
582 return song;
583 }
584