1 /*
2  * Copyright 2008-2013 Various Authors
3  * Copyright 2004-2005 Timo Hirvonen
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 as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "convert.h"
20 #include "input.h"
21 #include "ip.h"
22 #include "pcm.h"
23 #include "http.h"
24 #include "xmalloc.h"
25 #include "file.h"
26 #include "path.h"
27 #include "utils.h"
28 #include "cmus.h"
29 #include "options.h"
30 #include "list.h"
31 #include "mergesort.h"
32 #include "misc.h"
33 #include "debug.h"
34 #include "ui_curses.h"
35 #include "locking.h"
36 #include "xstrjoin.h"
37 
38 #include <unistd.h>
39 #include <stdbool.h>
40 #include <string.h>
41 #include <errno.h>
42 #include <stdio.h>
43 #include <stdarg.h>
44 #include <sys/select.h>
45 #include <sys/time.h>
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #include <fcntl.h>
49 #include <dirent.h>
50 #include <dlfcn.h>
51 #include <strings.h>
52 
53 struct input_plugin {
54 	const struct input_plugin_ops *ops;
55 	struct input_plugin_data data;
56 	unsigned int open : 1;
57 	unsigned int eof : 1;
58 	int http_code;
59 	char *http_reason;
60 
61 	/* cached duration, -1 = unset */
62 	int duration;
63 	/* cached bitrate, -1 = unset */
64 	long bitrate;
65 	/* cached codec, NULL = unset */
66 	char *codec;
67 	/* cached codec_profile, NULL = unset */
68 	char *codec_profile;
69 
70 	/*
71 	 * pcm is converted to 16-bit signed little-endian stereo
72 	 * NOTE: no conversion is done if channels > 2 or bits > 16
73 	 */
74 	void (*pcm_convert)(void *, const void *, int);
75 	void (*pcm_convert_in_place)(void *, int);
76 	/*
77 	 * 4  if 8-bit mono
78 	 * 2  if 8-bit stereo or 16-bit mono
79 	 * 1  otherwise
80 	 */
81 	int pcm_convert_scale;
82 };
83 
84 struct ip {
85 	struct list_head node;
86 	char *name;
87 	void *handle;
88 
89 	int priority;
90 	const char * const *extensions;
91 	const char * const *mime_types;
92 	const struct input_plugin_ops *ops;
93 	const struct input_plugin_opt *options;
94 };
95 
96 static const char *plugin_dir;
97 static LIST_HEAD(ip_head);
98 
99 /* protects ip->priority and ip_head */
100 static pthread_rwlock_t ip_lock = CMUS_RWLOCK_INITIALIZER;
101 
102 #define ip_rdlock() cmus_rwlock_rdlock(&ip_lock)
103 #define ip_wrlock() cmus_rwlock_wrlock(&ip_lock)
104 #define ip_unlock() cmus_rwlock_unlock(&ip_lock)
105 
106 /* timeouts (ms) */
107 static int http_connection_timeout = 5e3;
108 static int http_read_timeout = 5e3;
109 
110 static const char *pl_mime_types[] = {
111 	"audio/m3u",
112 	"audio/x-scpls",
113 	"audio/x-mpegurl"
114 };
115 
116 static const struct input_plugin_ops *
get_ops_by_extension_locked(const char * ext,struct list_head ** headp)117 get_ops_by_extension_locked(const char *ext, struct list_head **headp)
118 {
119 	struct list_head *node = *headp;
120 
121 	for (node = node->next; node != &ip_head; node = node->next) {
122 		struct ip *ip = list_entry(node, struct ip, node);
123 		const char * const *exts = ip->extensions;
124 		int i;
125 
126 		if (ip->priority <= 0) {
127 			break;
128 		}
129 
130 		for (i = 0; exts[i]; i++) {
131 			if (strcasecmp(ext, exts[i]) == 0 || strcmp("*", exts[i]) == 0) {
132 				*headp = node;
133 				return ip->ops;
134 			}
135 		}
136 	}
137 	return NULL;
138 }
139 
140 static const struct input_plugin_ops *
get_ops_by_extension(const char * ext,struct list_head ** headp)141 get_ops_by_extension(const char *ext, struct list_head **headp)
142 {
143 	ip_rdlock();
144 	const struct input_plugin_ops *rv = get_ops_by_extension_locked(ext,
145 			headp);
146 	ip_unlock();
147 	return rv;
148 }
149 
150 static const struct input_plugin_ops *
get_ops_by_mime_type_locked(const char * mime_type)151 get_ops_by_mime_type_locked(const char *mime_type)
152 {
153 	struct ip *ip;
154 
155 	list_for_each_entry(ip, &ip_head, node) {
156 		const char * const *types = ip->mime_types;
157 		int i;
158 
159 		if (ip->priority <= 0) {
160 			break;
161 		}
162 
163 		for (i = 0; types[i]; i++) {
164 			if (strcasecmp(mime_type, types[i]) == 0)
165 				return ip->ops;
166 		}
167 	}
168 	return NULL;
169 }
170 
171 static const struct input_plugin_ops *
get_ops_by_mime_type(const char * mime_type)172 get_ops_by_mime_type(const char *mime_type)
173 {
174 	ip_rdlock();
175 	const struct input_plugin_ops *rv =
176 		get_ops_by_mime_type_locked(mime_type);
177 	ip_unlock();
178 	return rv;
179 }
180 
keyvals_add_basic_auth(struct growing_keyvals * c,const char * user,const char * pass,const char * header)181 static void keyvals_add_basic_auth(struct growing_keyvals *c,
182 				   const char *user,
183 				   const char *pass,
184 				   const char *header)
185 {
186 	char buf[256];
187 	char *encoded;
188 
189 	snprintf(buf, sizeof(buf), "%s:%s", user, pass);
190 	encoded = base64_encode(buf);
191 	if (encoded == NULL) {
192 		d_print("couldn't base64 encode '%s'\n", buf);
193 	} else {
194 		snprintf(buf, sizeof(buf), "Basic %s", encoded);
195 		free(encoded);
196 		keyvals_add(c, header, xstrdup(buf));
197 	}
198 }
199 
do_http_get(struct http_get * hg,const char * uri,int redirections)200 static int do_http_get(struct http_get *hg, const char *uri, int redirections)
201 {
202 	GROWING_KEYVALS(h);
203 	int i, rc;
204 	const char *val;
205 	char *redirloc;
206 
207 	d_print("%s\n", uri);
208 
209 	hg->headers = NULL;
210 	hg->reason = NULL;
211 	hg->proxy = NULL;
212 	hg->code = -1;
213 	hg->fd = -1;
214 	if (http_parse_uri(uri, &hg->uri))
215 		return -IP_ERROR_INVALID_URI;
216 
217 	if (http_open(hg, http_connection_timeout))
218 		return -IP_ERROR_ERRNO;
219 
220 	keyvals_add(&h, "Host", xstrdup(hg->uri.host));
221 	if (hg->proxy && hg->proxy->user && hg->proxy->pass)
222 		keyvals_add_basic_auth(&h, hg->proxy->user, hg->proxy->pass, "Proxy-Authorization");
223 	keyvals_add(&h, "User-Agent", xstrdup("cmus/" VERSION));
224 	keyvals_add(&h, "Icy-MetaData", xstrdup("1"));
225 	if (hg->uri.user && hg->uri.pass)
226 		keyvals_add_basic_auth(&h, hg->uri.user, hg->uri.pass, "Authorization");
227 	keyvals_terminate(&h);
228 
229 	rc = http_get(hg, h.keyvals, http_read_timeout);
230 	keyvals_free(h.keyvals);
231 	switch (rc) {
232 	case -1:
233 		return -IP_ERROR_ERRNO;
234 	case -2:
235 		return -IP_ERROR_HTTP_RESPONSE;
236 	}
237 
238 	d_print("HTTP response: %d %s\n", hg->code, hg->reason);
239 	for (i = 0; hg->headers[i].key != NULL; i++)
240 		d_print("  %s: %s\n", hg->headers[i].key, hg->headers[i].val);
241 
242 	switch (hg->code) {
243 	case 200: /* OK */
244 		return 0;
245 	/*
246 	 * 3xx Codes (Redirections)
247 	 *     unhandled: 300 Multiple Choices
248 	 */
249 	case 301: /* Moved Permanently */
250 	case 302: /* Found */
251 	case 303: /* See Other */
252 	case 307: /* Temporary Redirect */
253 		val = keyvals_get_val(hg->headers, "location");
254 		if (!val)
255 			return -IP_ERROR_HTTP_RESPONSE;
256 
257 		redirections++;
258 		if (redirections > 2)
259 			return -IP_ERROR_HTTP_REDIRECT_LIMIT;
260 
261 		redirloc = xstrdup(val);
262 		http_get_free(hg);
263 		close(hg->fd);
264 
265 		rc = do_http_get(hg, redirloc, redirections);
266 
267 		free(redirloc);
268 		return rc;
269 	default:
270 		return -IP_ERROR_HTTP_STATUS;
271 	}
272 }
273 
setup_remote(struct input_plugin * ip,const struct keyval * headers,int sock)274 static int setup_remote(struct input_plugin *ip, const struct keyval *headers, int sock)
275 {
276 	const char *val;
277 
278 	val = keyvals_get_val(headers, "Content-Type");
279 	if (val) {
280 		d_print("Content-Type: %s\n", val);
281 		ip->ops = get_ops_by_mime_type(val);
282 		if (ip->ops == NULL) {
283 			d_print("unsupported content type: %s\n", val);
284 			close(sock);
285 			return -IP_ERROR_FILE_FORMAT;
286 		}
287 	} else {
288 		const char *type = "audio/mpeg";
289 
290 		d_print("assuming %s content type\n", type);
291 		ip->ops = get_ops_by_mime_type(type);
292 		if (ip->ops == NULL) {
293 			d_print("unsupported content type: %s\n", type);
294 			close(sock);
295 			return -IP_ERROR_FILE_FORMAT;
296 		}
297 	}
298 
299 	ip->data.fd = sock;
300 	ip->data.metadata = xnew(char, 16 * 255 + 1);
301 
302 	val = keyvals_get_val(headers, "icy-metaint");
303 	if (val) {
304 		long int lint;
305 
306 		if (str_to_int(val, &lint) == 0 && lint >= 0) {
307 			ip->data.metaint = lint;
308 			d_print("metaint: %d\n", ip->data.metaint);
309 		}
310 	}
311 
312 	val = keyvals_get_val(headers, "icy-name");
313 	if (val)
314 		ip->data.icy_name = to_utf8(val, icecast_default_charset);
315 
316 	val = keyvals_get_val(headers, "icy-genre");
317 	if (val)
318 		ip->data.icy_genre = to_utf8(val, icecast_default_charset);
319 
320 	val = keyvals_get_val(headers, "icy-url");
321 	if (val)
322 		ip->data.icy_url = to_utf8(val, icecast_default_charset);
323 
324 	return 0;
325 }
326 
327 struct read_playlist_data {
328 	struct input_plugin *ip;
329 	int rc;
330 	int count;
331 };
332 
handle_line(void * data,const char * uri)333 static int handle_line(void *data, const char *uri)
334 {
335 	struct read_playlist_data *rpd = data;
336 	struct http_get hg;
337 
338 	rpd->count++;
339 	rpd->rc = do_http_get(&hg, uri, 0);
340 	if (rpd->rc) {
341 		rpd->ip->http_code = hg.code;
342 		rpd->ip->http_reason = hg.reason;
343 		if (hg.fd >= 0)
344 			close(hg.fd);
345 
346 		hg.reason = NULL;
347 		http_get_free(&hg);
348 		return 0;
349 	}
350 
351 	rpd->rc = setup_remote(rpd->ip, hg.headers, hg.fd);
352 	http_get_free(&hg);
353 	return 1;
354 }
355 
read_playlist(struct input_plugin * ip,int sock)356 static int read_playlist(struct input_plugin *ip, int sock)
357 {
358 	struct read_playlist_data rpd = { ip, 0, 0 };
359 	char *body;
360 	size_t size;
361 
362 	body = http_read_body(sock, &size, http_read_timeout);
363 	close(sock);
364 	if (!body)
365 		return -IP_ERROR_ERRNO;
366 
367 	cmus_playlist_for_each(body, size, 0, handle_line, &rpd);
368 	free(body);
369 	if (!rpd.count) {
370 		d_print("empty playlist\n");
371 		rpd.rc = -IP_ERROR_HTTP_RESPONSE;
372 	}
373 	return rpd.rc;
374 }
375 
open_remote(struct input_plugin * ip)376 static int open_remote(struct input_plugin *ip)
377 {
378 	struct input_plugin_data *d = &ip->data;
379 	struct http_get hg;
380 	const char *val;
381 	int rc;
382 
383 	rc = do_http_get(&hg, d->filename, 0);
384 	if (rc) {
385 		ip->http_code = hg.code;
386 		ip->http_reason = hg.reason;
387 		hg.reason = NULL;
388 		http_get_free(&hg);
389 		return rc;
390 	}
391 
392 	val = keyvals_get_val(hg.headers, "Content-Type");
393 	if (val) {
394 		int i;
395 
396 		for (i = 0; i < N_ELEMENTS(pl_mime_types); i++) {
397 			if (!strcasecmp(val, pl_mime_types[i])) {
398 				d_print("Content-Type: %s\n", val);
399 				http_get_free(&hg);
400 				return read_playlist(ip, hg.fd);
401 			}
402 		}
403 	}
404 
405 	rc = setup_remote(ip, hg.headers, hg.fd);
406 	http_get_free(&hg);
407 	return rc;
408 }
409 
ip_init(struct input_plugin * ip,char * filename)410 static void ip_init(struct input_plugin *ip, char *filename)
411 {
412 	const struct input_plugin t = {
413 		.http_code          = -1,
414 		.pcm_convert_scale  = -1,
415 		.duration           = -1,
416 		.bitrate            = -1,
417 		.data = {
418 			.fd         = -1,
419 			.filename   = filename,
420 			.remote     = is_http_url(filename),
421 			.channel_map = CHANNEL_MAP_INIT
422 		}
423 	};
424 	*ip = t;
425 }
426 
ip_reset(struct input_plugin * ip,int close_fd)427 static void ip_reset(struct input_plugin *ip, int close_fd)
428 {
429 	int fd = ip->data.fd;
430 	free(ip->data.metadata);
431 	ip_init(ip, ip->data.filename);
432 	if (fd != -1) {
433 		if (close_fd)
434 			close(fd);
435 		else {
436 			lseek(fd, 0, SEEK_SET);
437 			ip->data.fd = fd;
438 		}
439 	}
440 }
441 
open_file_locked(struct input_plugin * ip)442 static int open_file_locked(struct input_plugin *ip)
443 {
444 	const struct input_plugin_ops *ops;
445 	struct list_head *head = &ip_head;
446 	const char *ext;
447 	int rc = 0;
448 
449 	ext = get_extension(ip->data.filename);
450 	if (!ext)
451 		return -IP_ERROR_UNRECOGNIZED_FILE_TYPE;
452 
453 	ops = get_ops_by_extension(ext, &head);
454 	if (!ops)
455 		return -IP_ERROR_UNRECOGNIZED_FILE_TYPE;
456 
457 	ip->data.fd = open(ip->data.filename, O_RDONLY);
458 	if (ip->data.fd == -1)
459 		return -IP_ERROR_ERRNO;
460 
461 	while (1) {
462 		ip->ops = ops;
463 		rc = ip->ops->open(&ip->data);
464 		if (rc != -IP_ERROR_UNSUPPORTED_FILE_TYPE)
465 			break;
466 
467 		ops = get_ops_by_extension(ext, &head);
468 		if (!ops)
469 			break;
470 
471 		ip_reset(ip, 0);
472 		d_print("fallback: try next plugin for `%s'\n", ip->data.filename);
473 	}
474 
475 	return rc;
476 }
477 
open_file(struct input_plugin * ip)478 static int open_file(struct input_plugin *ip)
479 {
480 	ip_rdlock();
481 	int rv = open_file_locked(ip);
482 	ip_unlock();
483 	return rv;
484 }
485 
sort_ip(const struct list_head * a_,const struct list_head * b_)486 static int sort_ip(const struct list_head *a_, const struct list_head *b_)
487 {
488 	const struct ip *a = list_entry(a_, struct ip, node);
489 	const struct ip *b = list_entry(b_, struct ip, node);
490 	return b->priority - a->priority;
491 }
492 
ip_load_plugins(void)493 void ip_load_plugins(void)
494 {
495 	DIR *dir;
496 	struct dirent *d;
497 
498 	plugin_dir = xstrjoin(cmus_lib_dir, "/ip");
499 	dir = opendir(plugin_dir);
500 	if (dir == NULL) {
501 		error_msg("couldn't open directory `%s': %s", plugin_dir, strerror(errno));
502 		return;
503 	}
504 
505 	ip_wrlock();
506 	while ((d = (struct dirent *) readdir(dir)) != NULL) {
507 		char filename[512];
508 		struct ip *ip;
509 		void *so;
510 		char *ext;
511 		const int *priority_ptr;
512 		const unsigned *abi_version_ptr;
513 		bool err = false;
514 
515 		if (d->d_name[0] == '.')
516 			continue;
517 		ext = strrchr(d->d_name, '.');
518 		if (ext == NULL)
519 			continue;
520 		if (strcmp(ext, ".so"))
521 			continue;
522 
523 		snprintf(filename, sizeof(filename), "%s/%s", plugin_dir, d->d_name);
524 
525 		so = dlopen(filename, RTLD_NOW);
526 		if (so == NULL) {
527 			d_print("%s: %s\n", filename, dlerror());
528 			continue;
529 		}
530 
531 		ip = xnew(struct ip, 1);
532 
533 		abi_version_ptr = dlsym(so, "ip_abi_version");
534 		priority_ptr = dlsym(so, "ip_priority");
535 		ip->extensions = dlsym(so, "ip_extensions");
536 		ip->mime_types = dlsym(so, "ip_mime_types");
537 		ip->ops = dlsym(so, "ip_ops");
538 		ip->options = dlsym(so, "ip_options");
539 		if (!priority_ptr || !ip->extensions || !ip->mime_types || !ip->ops || !ip->options) {
540 			error_msg("%s: missing symbol", filename);
541 			err = true;
542 		}
543 		if (!abi_version_ptr || *abi_version_ptr != IP_ABI_VERSION) {
544 			error_msg("%s: incompatible plugin version", filename);
545 			err = true;
546 		}
547 		if (err) {
548 			free(ip);
549 			dlclose(so);
550 			continue;
551 		}
552 		ip->priority = *priority_ptr;
553 
554 		ip->name = xstrndup(d->d_name, ext - d->d_name);
555 		ip->handle = so;
556 
557 		list_add_tail(&ip->node, &ip_head);
558 	}
559 	list_mergesort(&ip_head, sort_ip);
560 	closedir(dir);
561 	ip_unlock();
562 }
563 
ip_new(const char * filename)564 struct input_plugin *ip_new(const char *filename)
565 {
566 	struct input_plugin *ip = xnew(struct input_plugin, 1);
567 
568 	ip_init(ip, xstrdup(filename));
569 	return ip;
570 }
571 
ip_delete(struct input_plugin * ip)572 void ip_delete(struct input_plugin *ip)
573 {
574 	if (ip->open)
575 		ip_close(ip);
576 	free(ip->data.filename);
577 	free(ip);
578 }
579 
ip_open(struct input_plugin * ip)580 int ip_open(struct input_plugin *ip)
581 {
582 	int rc;
583 
584 	BUG_ON(ip->open);
585 
586 	/* set fd and ops, call ops->open */
587 	if (ip->data.remote) {
588 		rc = open_remote(ip);
589 		if (rc == 0)
590 			rc = ip->ops->open(&ip->data);
591 	} else {
592 		if (is_cdda_url(ip->data.filename)) {
593 			ip->ops = get_ops_by_mime_type("x-content/audio-cdda");
594 			rc = ip->ops ? ip->ops->open(&ip->data) : 1;
595 		} else if (is_cue_url(ip->data.filename)) {
596 			ip->ops = get_ops_by_mime_type("application/x-cue");
597 			rc = ip->ops ? ip->ops->open(&ip->data) : 1;
598 		} else
599 			rc = open_file(ip);
600 	}
601 
602 	if (rc) {
603 		d_print("opening `%s' failed: %d %s\n", ip->data.filename, rc,
604 				rc == -1 ? strerror(errno) : "");
605 		ip_reset(ip, 1);
606 		return rc;
607 	}
608 	ip->open = 1;
609 	return 0;
610 }
611 
ip_setup(struct input_plugin * ip)612 void ip_setup(struct input_plugin *ip)
613 {
614 	unsigned int bits, is_signed, channels;
615 	sample_format_t sf = ip->data.sf;
616 
617 	bits = sf_get_bits(sf);
618 	is_signed = sf_get_signed(sf);
619 	channels = sf_get_channels(sf);
620 
621 	ip->pcm_convert_scale = 1;
622 	ip->pcm_convert = NULL;
623 	ip->pcm_convert_in_place = NULL;
624 
625 	if (bits <= 16 && channels <= 2) {
626 		unsigned int mask = ((bits >> 2) & 4) | (is_signed << 1);
627 
628 		ip->pcm_convert = pcm_conv[mask | (channels - 1)];
629 		ip->pcm_convert_in_place = pcm_conv_in_place[mask | sf_get_bigendian(sf)];
630 
631 		ip->pcm_convert_scale = (3 - channels) * (3 - bits / 8);
632 	}
633 
634 	d_print("pcm convert: scale=%d convert=%d convert_in_place=%d\n",
635 			ip->pcm_convert_scale,
636 			ip->pcm_convert != NULL,
637 			ip->pcm_convert_in_place != NULL);
638 }
639 
ip_close(struct input_plugin * ip)640 int ip_close(struct input_plugin *ip)
641 {
642 	int rc;
643 
644 	rc = ip->ops->close(&ip->data);
645 	BUG_ON(ip->data.private);
646 	if (ip->data.fd != -1)
647 		close(ip->data.fd);
648 	free(ip->data.metadata);
649 	free(ip->data.icy_name);
650 	free(ip->data.icy_genre);
651 	free(ip->data.icy_url);
652 	free(ip->http_reason);
653 
654 	ip_init(ip, ip->data.filename);
655 	return rc;
656 }
657 
ip_read(struct input_plugin * ip,char * buffer,int count)658 int ip_read(struct input_plugin *ip, char *buffer, int count)
659 {
660 	struct timeval tv;
661 	fd_set readfds;
662 	/* 4608 seems to be optimal for mp3s, 4096 for oggs */
663 	char tmp[8 * 1024];
664 	char *buf;
665 	int sample_size;
666 	int rc;
667 
668 	BUG_ON(count <= 0);
669 
670 	FD_ZERO(&readfds);
671 	FD_SET(ip->data.fd, &readfds);
672 	/* zero timeout -> return immediately */
673 	tv.tv_sec = 0;
674 	tv.tv_usec = 50e3;
675 	rc = select(ip->data.fd + 1, &readfds, NULL, NULL, &tv);
676 	if (rc == -1) {
677 		if (errno == EINTR)
678 			errno = EAGAIN;
679 		return -1;
680 	}
681 	if (rc == 0) {
682 		errno = EAGAIN;
683 		return -1;
684 	}
685 
686 	buf = buffer;
687 	if (ip->pcm_convert_scale > 1) {
688 		/* use tmp buffer for 16-bit mono and 8-bit */
689 		buf = tmp;
690 		count /= ip->pcm_convert_scale;
691 		if (count > sizeof(tmp))
692 			count = sizeof(tmp);
693 	}
694 
695 	rc = ip->ops->read(&ip->data, buf, count);
696 	if (rc == -1 && (errno == EAGAIN || errno == EINTR)) {
697 		errno = EAGAIN;
698 		return -1;
699 	}
700 	if (rc <= 0) {
701 		ip->eof = 1;
702 		return rc;
703 	}
704 
705 	BUG_ON(rc % sf_get_frame_size(ip->data.sf) != 0);
706 
707 	sample_size = sf_get_sample_size(ip->data.sf);
708 	if (ip->pcm_convert_in_place != NULL)
709 		ip->pcm_convert_in_place(buf, rc / sample_size);
710 	if (ip->pcm_convert != NULL)
711 		ip->pcm_convert(buffer, tmp, rc / sample_size);
712 	return rc * ip->pcm_convert_scale;
713 }
714 
ip_seek(struct input_plugin * ip,double offset)715 int ip_seek(struct input_plugin *ip, double offset)
716 {
717 	int rc;
718 
719 	if (ip->data.remote)
720 		return -IP_ERROR_FUNCTION_NOT_SUPPORTED;
721 	rc = ip->ops->seek(&ip->data, offset);
722 	if (rc == 0)
723 		ip->eof = 0;
724 	return rc;
725 }
726 
ip_read_comments(struct input_plugin * ip,struct keyval ** comments)727 int ip_read_comments(struct input_plugin *ip, struct keyval **comments)
728 {
729 	struct keyval *kv = NULL;
730 	int rc;
731 
732 	rc = ip->ops->read_comments(&ip->data, &kv);
733 
734 	if (ip->data.remote) {
735 		GROWING_KEYVALS(c);
736 
737 		if (kv) {
738 			keyvals_init(&c, kv);
739 			keyvals_free(kv);
740 		}
741 
742 		if (ip->data.icy_name && !keyvals_get_val_growing(&c, "title"))
743 			keyvals_add(&c, "title", xstrdup(ip->data.icy_name));
744 
745 		if (ip->data.icy_genre && !keyvals_get_val_growing(&c, "genre"))
746 			keyvals_add(&c, "genre", xstrdup(ip->data.icy_genre));
747 
748 		if (ip->data.icy_url && !keyvals_get_val_growing(&c, "comment"))
749 			keyvals_add(&c, "comment", xstrdup(ip->data.icy_url));
750 
751 		keyvals_terminate(&c);
752 
753 		kv = c.keyvals;
754 	}
755 
756 	*comments = kv;
757 
758 	return ip->data.remote ? 0 : rc;
759 }
760 
ip_duration(struct input_plugin * ip)761 int ip_duration(struct input_plugin *ip)
762 {
763 	if (ip->data.remote)
764 		return -1;
765 	if (ip->duration == -1)
766 		ip->duration = ip->ops->duration(&ip->data);
767 	if (ip->duration < 0)
768 		return -1;
769 	return ip->duration;
770 }
771 
ip_bitrate(struct input_plugin * ip)772 int ip_bitrate(struct input_plugin *ip)
773 {
774 	if (ip->data.remote)
775 		return -1;
776 	if (ip->bitrate == -1)
777 		ip->bitrate = ip->ops->bitrate(&ip->data);
778 	if (ip->bitrate < 0)
779 		return -1;
780 	return ip->bitrate;
781 }
782 
ip_current_bitrate(struct input_plugin * ip)783 int ip_current_bitrate(struct input_plugin *ip)
784 {
785 	return ip->ops->bitrate_current(&ip->data);
786 }
787 
ip_codec(struct input_plugin * ip)788 char *ip_codec(struct input_plugin *ip)
789 {
790 	if (ip->data.remote)
791 		return NULL;
792 	if (!ip->codec)
793 		ip->codec = ip->ops->codec(&ip->data);
794 	return ip->codec;
795 }
796 
ip_codec_profile(struct input_plugin * ip)797 char *ip_codec_profile(struct input_plugin *ip)
798 {
799 	if (ip->data.remote)
800 		return NULL;
801 	if (!ip->codec_profile)
802 		ip->codec_profile = ip->ops->codec_profile(&ip->data);
803 	return ip->codec_profile;
804 }
805 
ip_get_sf(struct input_plugin * ip)806 sample_format_t ip_get_sf(struct input_plugin *ip)
807 {
808 	BUG_ON(!ip->open);
809 	return ip->data.sf;
810 }
811 
ip_get_channel_map(struct input_plugin * ip,channel_position_t * channel_map)812 void ip_get_channel_map(struct input_plugin *ip, channel_position_t *channel_map)
813 {
814 	BUG_ON(!ip->open);
815 	channel_map_copy(channel_map, ip->data.channel_map);
816 }
817 
ip_get_filename(struct input_plugin * ip)818 const char *ip_get_filename(struct input_plugin *ip)
819 {
820 	return ip->data.filename;
821 }
822 
ip_get_metadata(struct input_plugin * ip)823 const char *ip_get_metadata(struct input_plugin *ip)
824 {
825 	BUG_ON(!ip->open);
826 	return ip->data.metadata;
827 }
828 
ip_is_remote(struct input_plugin * ip)829 int ip_is_remote(struct input_plugin *ip)
830 {
831 	return ip->data.remote;
832 }
833 
ip_metadata_changed(struct input_plugin * ip)834 int ip_metadata_changed(struct input_plugin *ip)
835 {
836 	int ret = ip->data.metadata_changed;
837 
838 	BUG_ON(!ip->open);
839 	ip->data.metadata_changed = 0;
840 	return ret;
841 }
842 
ip_eof(struct input_plugin * ip)843 int ip_eof(struct input_plugin *ip)
844 {
845 	BUG_ON(!ip->open);
846 	return ip->eof;
847 }
848 
option_error(int rc)849 static void option_error(int rc)
850 {
851 	char *msg = ip_get_error_msg(NULL, rc, "setting option");
852 	error_msg("%s", msg);
853 	free(msg);
854 }
855 
set_ip_option(void * data,const char * val)856 static void set_ip_option(void *data, const char *val)
857 {
858 	const struct input_plugin_opt *ipo = data;
859 	int rc;
860 
861 	rc = ipo->set(val);
862 	if (rc)
863 		option_error(rc);
864 }
865 
get_ip_option(void * data,char * buf,size_t size)866 static void get_ip_option(void *data, char *buf, size_t size)
867 {
868 	const struct input_plugin_opt *ipo = data;
869 	char *val = NULL;
870 
871 	ipo->get(&val);
872 	if (val) {
873 		strscpy(buf, val, size);
874 		free(val);
875 	}
876 }
877 
set_ip_priority(void * data,const char * val)878 static void set_ip_priority(void *data, const char *val)
879 {
880 	/* warn only once during the lifetime of the program. */
881 	static bool warned = false;
882 	long tmp;
883 	struct ip *ip = data;
884 
885 	if (str_to_int(val, &tmp) == -1 || tmp < 0 || (long)(int)tmp != tmp) {
886 		error_msg("non-negative integer expected");
887 		return;
888 	}
889 	if (ui_initialized) {
890 		if (!warned) {
891 			static const char *msg =
892 				"Metadata might become inconsistent "
893 				"after this change. Continue? [y/N]";
894 			if (yes_no_query("%s", msg) != UI_QUERY_ANSWER_YES) {
895 				info_msg("Aborted");
896 				return;
897 			}
898 			warned = true;
899 		}
900 		info_msg("Run \":update-cache -f\" to refresh the metadata.");
901 	}
902 
903 	ip_wrlock();
904 	ip->priority = (int)tmp;
905 	list_mergesort(&ip_head, sort_ip);
906 	ip_unlock();
907 }
908 
get_ip_priority(void * data,char * val,size_t size)909 static void get_ip_priority(void *data, char *val, size_t size)
910 {
911 	const struct ip *ip = data;
912 	ip_rdlock();
913 	snprintf(val, size, "%d", ip->priority);
914 	ip_unlock();
915 }
916 
ip_add_options(void)917 void ip_add_options(void)
918 {
919 	struct ip *ip;
920 	const struct input_plugin_opt *ipo;
921 	char key[64];
922 
923 	ip_rdlock();
924 	list_for_each_entry(ip, &ip_head, node) {
925 		for (ipo = ip->options; ipo->name; ipo++) {
926 			snprintf(key, sizeof(key), "input.%s.%s", ip->name,
927 					ipo->name);
928 			option_add(xstrdup(key), ipo, get_ip_option,
929 					set_ip_option, NULL, 0);
930 		}
931 		snprintf(key, sizeof(key), "input.%s.priority", ip->name);
932 		option_add(xstrdup(key), ip, get_ip_priority, set_ip_priority, NULL, 0);
933 	}
934 	ip_unlock();
935 }
936 
ip_get_error_msg(struct input_plugin * ip,int rc,const char * arg)937 char *ip_get_error_msg(struct input_plugin *ip, int rc, const char *arg)
938 {
939 	char buffer[1024];
940 
941 	switch (-rc) {
942 	case IP_ERROR_ERRNO:
943 		snprintf(buffer, sizeof(buffer), "%s: %s", arg, strerror(errno));
944 		break;
945 	case IP_ERROR_UNRECOGNIZED_FILE_TYPE:
946 		snprintf(buffer, sizeof(buffer),
947 				"%s: unrecognized filename extension", arg);
948 		break;
949 	case IP_ERROR_UNSUPPORTED_FILE_TYPE:
950 		snprintf(buffer, sizeof(buffer),
951 				"%s: unsupported file format", arg);
952 		break;
953 	case IP_ERROR_FUNCTION_NOT_SUPPORTED:
954 		snprintf(buffer, sizeof(buffer),
955 				"%s: function not supported", arg);
956 		break;
957 	case IP_ERROR_FILE_FORMAT:
958 		snprintf(buffer, sizeof(buffer),
959 				"%s: file format not supported or corrupted file",
960 				arg);
961 		break;
962 	case IP_ERROR_INVALID_URI:
963 		snprintf(buffer, sizeof(buffer), "%s: invalid URI", arg);
964 		break;
965 	case IP_ERROR_SAMPLE_FORMAT:
966 		snprintf(buffer, sizeof(buffer),
967 				"%s: input plugin doesn't support the sample format",
968 				arg);
969 		break;
970 	case IP_ERROR_WRONG_DISC:
971 		snprintf(buffer, sizeof(buffer), "%s: wrong disc inserted, aborting!", arg);
972 		break;
973 	case IP_ERROR_NO_DISC:
974 		snprintf(buffer, sizeof(buffer), "%s: could not read disc", arg);
975 		break;
976 	case IP_ERROR_HTTP_RESPONSE:
977 		snprintf(buffer, sizeof(buffer), "%s: invalid HTTP response", arg);
978 		break;
979 	case IP_ERROR_HTTP_STATUS:
980 		snprintf(buffer, sizeof(buffer), "%s: %d %s", arg, ip->http_code, ip->http_reason);
981 		free(ip->http_reason);
982 		ip->http_reason = NULL;
983 		ip->http_code = -1;
984 		break;
985 	case IP_ERROR_HTTP_REDIRECT_LIMIT:
986 		snprintf(buffer, sizeof(buffer), "%s: too many HTTP redirections", arg);
987 		break;
988 	case IP_ERROR_NOT_OPTION:
989 		snprintf(buffer, sizeof(buffer),
990 				"%s: no such option", arg);
991 		break;
992 	case IP_ERROR_INTERNAL:
993 		snprintf(buffer, sizeof(buffer), "%s: internal error", arg);
994 		break;
995 	case IP_ERROR_SUCCESS:
996 	default:
997 		snprintf(buffer, sizeof(buffer),
998 				"%s: this is not an error (%d), this is a bug",
999 				arg, rc);
1000 		break;
1001 	}
1002 	return xstrdup(buffer);
1003 }
1004 
ip_get_supported_extensions(void)1005 char **ip_get_supported_extensions(void)
1006 {
1007 	struct ip *ip;
1008 	char **exts;
1009 	int i, size;
1010 	int count = 0;
1011 
1012 	size = 8;
1013 	exts = xnew(char *, size);
1014 	ip_rdlock();
1015 	list_for_each_entry(ip, &ip_head, node) {
1016 		const char * const *e = ip->extensions;
1017 
1018 		for (i = 0; e[i]; i++) {
1019 			if (count == size - 1) {
1020 				size *= 2;
1021 				exts = xrenew(char *, exts, size);
1022 			}
1023 			exts[count++] = xstrdup(e[i]);
1024 		}
1025 	}
1026 	ip_unlock();
1027 	exts[count] = NULL;
1028 	qsort(exts, count, sizeof(char *), strptrcmp);
1029 	return exts;
1030 }
1031 
ip_dump_plugins(void)1032 void ip_dump_plugins(void)
1033 {
1034 	struct ip *ip;
1035 	int i;
1036 
1037 	printf("Input Plugins: %s\n", plugin_dir);
1038 	ip_rdlock();
1039 	list_for_each_entry(ip, &ip_head, node) {
1040 		printf("  %s:\n    Priority: %d\n    File Types:", ip->name, ip->priority);
1041 		for (i = 0; ip->extensions[i]; i++)
1042 			printf(" %s", ip->extensions[i]);
1043 		printf("\n    MIME Types:");
1044 		for (i = 0; ip->mime_types[i]; i++)
1045 			printf(" %s", ip->mime_types[i]);
1046 		printf("\n");
1047 	}
1048 	ip_unlock();
1049 }
1050