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