Define default port in Makefile
[cmus.git] / input.c
blob7861e97d6ffe37256d84c343a139ca2605a502f5
1 /*
2 * Copyright 2004-2005 Timo Hirvonen
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
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., 59 Temple Place - Suite 330, Boston, MA
17 * 02111-1307, USA.
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 "utils.h"
27 #include "cmus.h"
28 #include "list.h"
29 #include "prog.h"
30 #include "misc.h"
31 #include "debug.h"
32 #include "config/libdir.h"
34 #include <unistd.h>
35 #include <string.h>
36 #include <errno.h>
37 #include <stdarg.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <fcntl.h>
41 #include <dirent.h>
42 #include <dlfcn.h>
44 struct input_plugin {
45 const struct input_plugin_ops *ops;
46 struct input_plugin_data data;
47 unsigned int open : 1;
48 unsigned int eof : 1;
49 int http_code;
50 char *http_reason;
52 /* cached duration, -1 = unset */
53 int duration;
56 * pcm is converted to 16-bit signed little-endian stereo
57 * NOTE: no conversion is done if channels > 2 or bits > 16
59 void (*pcm_convert)(char *, const char *, int);
60 void (*pcm_convert_in_place)(char *, int);
62 * 4 if 8-bit mono
63 * 2 if 8-bit stereo or 16-bit mono
64 * 1 otherwise
66 int pcm_convert_scale;
69 struct ip {
70 struct list_head node;
71 char *name;
72 void *handle;
74 const char * const *extensions;
75 const char * const *mime_types;
76 const struct input_plugin_ops *ops;
79 static const char * const plugin_dir = LIBDIR "/cmus/ip";
80 static LIST_HEAD(ip_head);
82 /* timeouts (ms) */
83 static int http_connection_timeout = 5e3;
84 static int http_read_timeout = 5e3;
86 static const char *get_extension(const char *filename)
88 const char *ext;
90 ext = filename + strlen(filename) - 1;
91 while (ext >= filename && *ext != '/') {
92 if (*ext == '.') {
93 ext++;
94 return ext;
96 ext--;
98 return NULL;
101 static const struct input_plugin_ops *get_ops_by_filename(const char *filename)
103 struct ip *ip;
104 const char *ext;
106 ext = get_extension(filename);
107 if (ext == NULL)
108 return NULL;
109 list_for_each_entry(ip, &ip_head, node) {
110 const char * const *exts = ip->extensions;
111 int i;
113 for (i = 0; exts[i]; i++) {
114 if (strcasecmp(ext, exts[i]) == 0)
115 return ip->ops;
118 return NULL;
121 static const struct input_plugin_ops *get_ops_by_mime_type(const char *mime_type)
123 struct ip *ip;
125 list_for_each_entry(ip, &ip_head, node) {
126 const char * const *types = ip->mime_types;
127 int i;
129 for (i = 0; types[i]; i++) {
130 if (strcasecmp(mime_type, types[i]) == 0)
131 return ip->ops;
134 return NULL;
137 static int do_http_get(const char *uri, struct http_header **headersp, int *codep, char **reasonp)
139 struct http_uri u;
140 struct http_header *h;
141 int sock, i, rc, code;
142 char *reason;
144 *headersp = NULL;
145 *codep = -1;
146 *reasonp = NULL;
148 if (http_parse_uri(uri, &u))
149 return -IP_ERROR_INVALID_URI;
151 /* d_print("%s -> '%s':'%s'@'%s':%d'%s'\n", uri, user, pass, host, port, path); */
153 sock = http_open(u.host, u.port, http_connection_timeout);
154 if (sock == -1) {
155 http_free_uri(&u);
156 return -IP_ERROR_ERRNO;
159 h = xnew(struct http_header, 5);
160 i = 0;
161 h[i].key = xstrdup("Host");
162 h[i].val = xstrdup(u.host);
163 i++;
164 h[i].key = xstrdup("User-Agent");
165 h[i].val = xstrdup("cmus/" VERSION);
166 i++;
167 h[i].key = xstrdup("Icy-MetaData");
168 h[i].val = xstrdup("1");
169 i++;
170 if (u.user && u.pass) {
171 char buf[256];
172 char *encoded;
174 snprintf(buf, sizeof(buf), "%s:%s", u.user, u.pass);
175 encoded = base64_encode(buf);
176 if (encoded == NULL) {
177 d_print("couldn't base64 encode '%s'\n", buf);
178 } else {
179 snprintf(buf, sizeof(buf), "Basic %s", encoded);
180 free(encoded);
181 h[i].key = xstrdup("Authorization");
182 h[i].val = xstrdup(buf);
183 i++;
186 h[i].key = NULL;
187 h[i].val = NULL;
188 i++;
190 rc = http_get(sock, u.path, h, &code, &reason, headersp, http_read_timeout);
191 http_headers_free(h);
192 http_free_uri(&u);
193 switch (rc) {
194 case -1:
195 d_print("error: %s\n", strerror(errno));
196 close(sock);
197 return -IP_ERROR_ERRNO;
198 case -2:
199 d_print("error parsing HTTP response\n");
200 close(sock);
201 return -IP_ERROR_HTTP_RESPONSE;
203 d_print("HTTP response: %d %s\n", code, reason);
204 if (code != 200) {
205 *codep = code;
206 *reasonp = reason;
207 close(sock);
208 return -IP_ERROR_HTTP_STATUS;
210 free(reason);
211 return sock;
214 static int setup_remote(struct input_plugin *ip, const struct http_header *headers, int sock)
216 const char *val;
218 val = http_headers_get_value(headers, "Content-Type");
219 if (val) {
220 ip->ops = get_ops_by_mime_type(val);
221 if (ip->ops == NULL) {
222 d_print("unsupported content type: %s\n", val);
223 close(sock);
224 return -IP_ERROR_FILE_FORMAT;
226 } else {
227 const char *type = "audio/mpeg";
229 d_print("assuming %s content type\n", type);
230 ip->ops = get_ops_by_mime_type(type);
231 if (ip->ops == NULL) {
232 d_print("unsupported content type: %s\n", type);
233 close(sock);
234 return -IP_ERROR_FILE_FORMAT;
238 ip->data.fd = sock;
239 ip->data.metadata = (char *)xmalloc(16 * 255 + 1);
241 val = http_headers_get_value(headers, "icy-metaint");
242 if (val) {
243 long int lint;
245 if (str_to_int(val, &lint) == 0 && lint >= 0) {
246 ip->data.metaint = lint;
247 d_print("metaint: %d\n", ip->data.metaint);
250 return 0;
253 static int handle_line(void *data, const char *line)
255 char **firstp = data;
257 *firstp = xstrdup(line);
258 /* ignore other lines */
259 return 1;
262 static int read_playlist(struct input_plugin *ip, int sock)
264 struct http_header *headers;
265 char *body, *reason, *first;
266 int rc, code;
268 rc = http_read_body(sock, &body, http_read_timeout);
269 close(sock);
270 if (rc)
271 return -IP_ERROR_ERRNO;
273 /* get only first URL from the playlist */
274 first = NULL;
275 cmus_playlist_for_each(body, strlen(body), 0, handle_line, &first);
276 free(body);
278 if (first == NULL) {
279 d_print("empty playlist\n");
280 return -IP_ERROR_HTTP_RESPONSE;
283 sock = do_http_get(first, &headers, &code, &reason);
284 free(first);
285 if (sock < 0) {
286 ip->http_code = code;
287 ip->http_reason = reason;
288 /* URI in the _playlist_ is invalid, not our fault */
289 if (sock == -IP_ERROR_INVALID_URI)
290 sock = -IP_ERROR_HTTP_RESPONSE;
291 return sock;
294 rc = setup_remote(ip, headers, sock);
295 http_headers_free(headers);
296 return rc;
299 static int open_remote(struct input_plugin *ip)
301 struct input_plugin_data *d = &ip->data;
302 char *reason;
303 int sock, rc, code;
304 struct http_header *headers;
305 const char *val;
307 sock = do_http_get(d->filename, &headers, &code, &reason);
308 if (sock < 0) {
309 ip->http_code = code;
310 ip->http_reason = reason;
311 return sock;
314 val = http_headers_get_value(headers, "Content-Type");
315 if (val) {
316 d_print("Content-Type: %s\n", val);
317 if (!strcasecmp(val, "audio/x-scpls") || !strcasecmp(val, "audio/m3u")) {
318 http_headers_free(headers);
319 return read_playlist(ip, sock);
323 rc = setup_remote(ip, headers, sock);
324 http_headers_free(headers);
325 return rc;
328 static int open_file(struct input_plugin *ip)
330 ip->ops = get_ops_by_filename(ip->data.filename);
331 if (ip->ops == NULL)
332 return -IP_ERROR_UNRECOGNIZED_FILE_TYPE;
333 ip->data.fd = open(ip->data.filename, O_RDONLY);
334 if (ip->data.fd == -1) {
335 ip->ops = NULL;
336 return -IP_ERROR_ERRNO;
338 return 0;
341 void ip_load_plugins(void)
343 DIR *dir;
344 struct dirent *d;
346 dir = opendir(plugin_dir);
347 if (dir == NULL) {
348 warn_errno("couldn't open directory `%s'", plugin_dir);
349 return;
351 while ((d = readdir(dir)) != NULL) {
352 char filename[256];
353 struct ip *ip;
354 void *so;
355 char *ext;
356 const char *sym;
358 if (d->d_name[0] == '.')
359 continue;
360 ext = strrchr(d->d_name, '.');
361 if (ext == NULL)
362 continue;
363 if (strcmp(ext, ".so"))
364 continue;
366 snprintf(filename, sizeof(filename), "%s/%s", plugin_dir, d->d_name);
368 so = dlopen(filename, RTLD_NOW);
369 if (so == NULL) {
370 warn("%s\n", dlerror());
371 continue;
374 ip = xnew(struct ip, 1);
376 sym = "ip_extensions";
377 if (!(ip->extensions = dlsym(so, sym)))
378 goto sym_err;
380 sym = "ip_mime_types";
381 if (!(ip->mime_types = dlsym(so, sym)))
382 goto sym_err;
384 sym = "ip_ops";
385 if (!(ip->ops = dlsym(so, sym)))
386 goto sym_err;
388 ip->name = xstrndup(d->d_name, ext - d->d_name);
389 ip->handle = so;
391 list_add_tail(&ip->node, &ip_head);
392 continue;
393 sym_err:
394 warn("%s: symbol %s not found\n", filename, sym);
395 free(ip);
396 dlclose(so);
398 closedir(dir);
401 static void ip_init(struct input_plugin *ip, char *filename)
403 memset(ip, 0, sizeof(*ip));
404 ip->http_code = -1;
405 ip->pcm_convert_scale = -1;
406 ip->duration = -1;
407 ip->data.fd = -1;
408 ip->data.filename = filename;
409 ip->data.remote = is_url(filename);
412 struct input_plugin *ip_new(const char *filename)
414 struct input_plugin *ip = xnew(struct input_plugin, 1);
416 ip_init(ip, xstrdup(filename));
417 return ip;
420 void ip_delete(struct input_plugin *ip)
422 if (ip->open)
423 ip_close(ip);
424 free(ip->data.filename);
425 free(ip);
428 int ip_open(struct input_plugin *ip)
430 int rc;
432 BUG_ON(ip->open);
434 /* set fd and ops */
435 if (ip->data.remote) {
436 rc = open_remote(ip);
437 } else {
438 rc = open_file(ip);
441 if (rc) {
442 d_print("opening `%s' failed: %d %s\n", ip->data.filename, rc,
443 rc == -1 ? strerror(errno) : "");
444 return rc;
447 rc = ip->ops->open(&ip->data);
448 if (rc) {
449 d_print("opening file `%s' failed: %d %s\n", ip->data.filename, rc,
450 rc == -1 ? strerror(errno) : "");
451 if (ip->data.fd != -1)
452 close(ip->data.fd);
453 free(ip->data.metadata);
454 ip_init(ip, ip->data.filename);
455 return rc;
457 ip->open = 1;
458 return 0;
461 void ip_setup(struct input_plugin *ip)
463 unsigned int bits, is_signed, channels;
464 sample_format_t sf = ip->data.sf;
466 bits = sf_get_bits(sf);
467 is_signed = sf_get_signed(sf);
468 channels = sf_get_channels(sf);
470 ip->pcm_convert_scale = 1;
471 ip->pcm_convert = NULL;
472 ip->pcm_convert_in_place = NULL;
474 if (bits <= 16 && channels <= 2) {
475 unsigned int mask = ((bits >> 2) & 4) | (is_signed << 1);
477 ip->pcm_convert = pcm_conv[mask | (channels - 1)];
478 ip->pcm_convert_in_place = pcm_conv_in_place[mask | sf_get_bigendian(sf)];
480 ip->pcm_convert_scale = (3 - channels) * (3 - bits / 8);
483 d_print("pcm convert: scale=%d convert=%d convert_in_place=%d\n",
484 ip->pcm_convert_scale,
485 ip->pcm_convert != NULL,
486 ip->pcm_convert_in_place != NULL);
489 int ip_close(struct input_plugin *ip)
491 int rc;
493 rc = ip->ops->close(&ip->data);
494 BUG_ON(ip->data.private);
495 if (ip->data.fd != -1)
496 close(ip->data.fd);
497 free(ip->data.metadata);
498 free(ip->http_reason);
500 ip_init(ip, ip->data.filename);
501 return rc;
504 int ip_read(struct input_plugin *ip, char *buffer, int count)
506 struct timeval tv;
507 fd_set readfds;
508 /* 4608 seems to be optimal for mp3s, 4096 for oggs */
509 char tmp[8 * 1024];
510 char *buf;
511 int rc;
513 BUG_ON(count <= 0);
515 FD_ZERO(&readfds);
516 FD_SET(ip->data.fd, &readfds);
517 /* zero timeout -> return immediately */
518 tv.tv_sec = 0;
519 tv.tv_usec = 50e3;
520 rc = select(ip->data.fd + 1, &readfds, NULL, NULL, &tv);
521 if (rc == -1) {
522 d_print("select: error: %s\n", strerror(errno));
523 if (errno == EINTR)
524 errno = EAGAIN;
525 return -1;
527 if (rc == 0) {
528 errno = EAGAIN;
529 return -1;
532 buf = buffer;
533 if (ip->pcm_convert_scale > 1) {
534 /* use tmp buffer for 16-bit mono and 8-bit */
535 buf = tmp;
536 count /= ip->pcm_convert_scale;
537 if (count > sizeof(tmp))
538 count = sizeof(tmp);
541 rc = ip->ops->read(&ip->data, buf, count);
542 if (rc == 0)
543 ip->eof = 1;
544 if (rc == -1)
545 d_print("error: %s\n", strerror(errno));
547 if (rc > 0) {
548 int sample_size = sf_get_sample_size(ip->data.sf);
550 if (ip->pcm_convert_in_place != NULL)
551 ip->pcm_convert_in_place(buf, rc / sample_size);
552 if (ip->pcm_convert != NULL)
553 ip->pcm_convert(buffer, tmp, rc / sample_size);
554 rc *= ip->pcm_convert_scale;
556 return rc;
559 int ip_seek(struct input_plugin *ip, double offset)
561 int rc;
563 if (ip->data.remote)
564 return -IP_ERROR_FUNCTION_NOT_SUPPORTED;
565 rc = ip->ops->seek(&ip->data, offset);
566 if (rc == 0)
567 ip->eof = 0;
568 return rc;
571 int ip_read_comments(struct input_plugin *ip, struct keyval **comments)
573 return ip->ops->read_comments(&ip->data, comments);
576 int ip_duration(struct input_plugin *ip)
578 if (ip->data.remote)
579 return -1;
580 if (ip->duration == -1)
581 ip->duration = ip->ops->duration(&ip->data);
582 if (ip->duration < 0)
583 return -1;
584 return ip->duration;
587 sample_format_t ip_get_sf(struct input_plugin *ip)
589 BUG_ON(!ip->open);
590 return ip->data.sf;
593 const char *ip_get_filename(struct input_plugin *ip)
595 return ip->data.filename;
598 const char *ip_get_metadata(struct input_plugin *ip)
600 BUG_ON(!ip->open);
601 return ip->data.metadata;
604 int ip_is_remote(struct input_plugin *ip)
606 return ip->data.remote;
609 int ip_metadata_changed(struct input_plugin *ip)
611 int ret = ip->data.metadata_changed;
613 BUG_ON(!ip->open);
614 ip->data.metadata_changed = 0;
615 return ret;
618 int ip_eof(struct input_plugin *ip)
620 BUG_ON(!ip->open);
621 return ip->eof;
624 char *ip_get_error_msg(struct input_plugin *ip, int rc, const char *arg)
626 char buffer[1024];
628 switch (-rc) {
629 case IP_ERROR_ERRNO:
630 snprintf(buffer, sizeof(buffer), "%s: %s", arg, strerror(errno));
631 break;
632 case IP_ERROR_UNRECOGNIZED_FILE_TYPE:
633 snprintf(buffer, sizeof(buffer),
634 "%s: unrecognized filename extension", arg);
635 break;
636 case IP_ERROR_FUNCTION_NOT_SUPPORTED:
637 snprintf(buffer, sizeof(buffer),
638 "%s: function not supported", arg);
639 break;
640 case IP_ERROR_FILE_FORMAT:
641 snprintf(buffer, sizeof(buffer),
642 "%s: file format not supported or corrupted file",
643 arg);
644 break;
645 case IP_ERROR_INVALID_URI:
646 snprintf(buffer, sizeof(buffer), "%s: invalid URI", arg);
647 break;
648 case IP_ERROR_SAMPLE_FORMAT:
649 snprintf(buffer, sizeof(buffer),
650 "%s: input plugin doesn't support the sample format",
651 arg);
652 break;
653 case IP_ERROR_HTTP_RESPONSE:
654 snprintf(buffer, sizeof(buffer), "%s: invalid HTTP response", arg);
655 break;
656 case IP_ERROR_HTTP_STATUS:
657 snprintf(buffer, sizeof(buffer), "%s: %d %s", arg, ip->http_code, ip->http_reason);
658 free(ip->http_reason);
659 ip->http_reason = NULL;
660 ip->http_code = -1;
661 break;
662 case IP_ERROR_INTERNAL:
663 snprintf(buffer, sizeof(buffer), "%s: internal error", arg);
664 break;
665 case IP_ERROR_SUCCESS:
666 default:
667 snprintf(buffer, sizeof(buffer),
668 "%s: this is not an error (%d), this is a bug",
669 arg, rc);
670 break;
672 return xstrdup(buffer);
675 char **ip_get_supported_extensions(void)
677 struct ip *ip;
678 char **exts;
679 int i, size;
680 int count = 0;
682 size = 8;
683 exts = xnew(char *, size);
684 list_for_each_entry(ip, &ip_head, node) {
685 const char * const *e = ip->extensions;
687 for (i = 0; e[i]; i++) {
688 if (count == size - 1) {
689 size *= 2;
690 exts = xrenew(char *, exts, size);
692 exts[count++] = xstrdup(e[i]);
695 exts[count] = NULL;
696 qsort(exts, count, sizeof(char *), strptrcmp);
697 return exts;
700 void ip_dump_plugins(void)
702 struct ip *ip;
703 int i;
705 printf("Input Plugins: %s\n", plugin_dir);
706 list_for_each_entry(ip, &ip_head, node) {
707 printf(" %s:\n File Types:", ip->name);
708 for (i = 0; ip->extensions[i]; i++)
709 printf(" %s", ip->extensions[i]);
710 printf("\n MIME Types:");
711 for (i = 0; ip->mime_types[i]; i++)
712 printf(" %s", ip->mime_types[i]);
713 printf("\n");