• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*  XMMS - Cross-platform multimedia player
2  *  Copyright (C) 1998-2000  Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License along
15  *  with this program; if not, write to the Free Software Foundation, Inc.,
16  *  with this program; if not, write to the Free Software Foundation, Inc.,
17  */
18 /* modified for FLAC support by Steven Richman (2003) */
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #include "plugin.h"
25 
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <sys/time.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31 #include <netdb.h>
32 #include <glib.h>
33 #include <string.h>
34 #include <fcntl.h>
35 #include <unistd.h>
36 #include <errno.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <inttypes.h>
40 
41 #include <pthread.h>
42 
43 #include <xmms/util.h>
44 #include <xmms/plugin.h>
45 
46 #include "FLAC/format.h"
47 #include "configure.h"
48 #include "locale_hack.h"
49 
50 /* on FreeBSD we get socklen_t from <sys/socket.h> */
51 #if (!defined HAVE_SOCKLEN_T) && !defined(__FreeBSD__)
52 typedef uint32_t socklen_t;
53 #endif
54 
55 #define min(x,y) ((x)<(y)?(x):(y))
56 #define min3(x,y,z) (min(x,y)<(z)?min(x,y):(z))
57 #define min4(x,y,z,w) (min3(x,y,z)<(w)?min3(x,y,z):(w))
58 
59 static gchar *icy_name = NULL;
60 static gint icy_metaint = 0;
61 
62 extern InputPlugin flac_ip;
63 
64 /* Static udp channel functions */
65 static int udp_establish_listener (gint *sock);
66 static int udp_check_for_data(gint sock);
67 
68 static char *flac_http_get_title(char *url);
69 
70 static gboolean prebuffering, going, eof = FALSE;
71 static gint sock, rd_index, wr_index, buffer_length, prebuffer_length;
72 static guint64 buffer_read = 0;
73 static gchar *buffer;
74 static guint64 offset;
75 static pthread_t thread;
76 static GtkWidget *error_dialog = NULL;
77 
78 static FILE *output_file = NULL;
79 
80 #define BASE64_LENGTH(len) (4 * (((len) + 2) / 3))
81 
82 /* Encode the string S of length LENGTH to base64 format and place it
83    to STORE.  STORE will be 0-terminated, and must point to a writable
84    buffer of at least 1+BASE64_LENGTH(length) bytes.  */
base64_encode(const gchar * s,gchar * store,gint length)85 static void base64_encode (const gchar *s, gchar *store, gint length)
86 {
87 	/* Conversion table.  */
88 	static gchar tbl[64] = {
89 		'A','B','C','D','E','F','G','H',
90 		'I','J','K','L','M','N','O','P',
91 		'Q','R','S','T','U','V','W','X',
92 		'Y','Z','a','b','c','d','e','f',
93 		'g','h','i','j','k','l','m','n',
94 		'o','p','q','r','s','t','u','v',
95 		'w','x','y','z','0','1','2','3',
96 		'4','5','6','7','8','9','+','/'
97 	};
98 	gint i;
99 	guchar *p = (guchar *)store;
100 
101 	/* Transform the 3x8 bits to 4x6 bits, as required by base64.  */
102 	for (i = 0; i < length; i += 3)
103 	{
104 		*p++ = tbl[s[0] >> 2];
105 		*p++ = tbl[((s[0] & 3) << 4) + (s[1] >> 4)];
106 		*p++ = tbl[((s[1] & 0xf) << 2) + (s[2] >> 6)];
107 		*p++ = tbl[s[2] & 0x3f];
108 		s += 3;
109 	}
110 	/* Pad the result if necessary...  */
111 	if (i == length + 1)
112 		*(p - 1) = '=';
113 	else if (i == length + 2)
114 		*(p - 1) = *(p - 2) = '=';
115 	/* ...and zero-terminate it.  */
116 	*p = '\0';
117 }
118 
119 /* Create the authentication header contents for the `Basic' scheme.
120    This is done by encoding the string `USER:PASS' in base64 and
121    prepending `HEADER: Basic ' to it.  */
basic_authentication_encode(const gchar * user,const gchar * passwd,const gchar * header)122 static gchar *basic_authentication_encode (const gchar *user, const gchar *passwd, const gchar *header)
123 {
124 	gchar *t1, *t2, *res;
125 	gint len1 = strlen (user) + 1 + strlen (passwd);
126 	gint len2 = BASE64_LENGTH (len1);
127 
128 	t1 = g_strdup_printf("%s:%s", user, passwd);
129 	t2 = g_malloc0(len2 + 1);
130 	base64_encode (t1, t2, len1);
131 	res = g_strdup_printf("%s: Basic %s\r\n", header, t2);
132 	g_free(t2);
133 	g_free(t1);
134 
135 	return res;
136 }
137 
parse_url(const gchar * url,gchar ** user,gchar ** pass,gchar ** host,int * port,gchar ** filename)138 static void parse_url(const gchar * url, gchar ** user, gchar ** pass, gchar ** host, int *port, gchar ** filename)
139 {
140 	gchar *h, *p, *pt, *f, *temp, *ptr;
141 
142 	temp = g_strdup(url);
143 	ptr = temp;
144 
145 	if (!strncasecmp("http://", ptr, 7))
146 		ptr += 7;
147 	h = strchr(ptr, '@');
148 	f = strchr(ptr, '/');
149 	if (h != NULL && (!f || h < f))
150 	{
151 		*h = '\0';
152 		p = strchr(ptr, ':');
153 		if (p != NULL && p < h)
154 		{
155 			*p = '\0';
156 			p++;
157 			*pass = g_strdup(p);
158 		}
159 		else
160 			*pass = NULL;
161 		*user = g_strdup(ptr);
162 		h++;
163 		ptr = h;
164 	}
165 	else
166 	{
167 		*user = NULL;
168 		*pass = NULL;
169 		h = ptr;
170 	}
171 	pt = strchr(ptr, ':');
172 	if (pt != NULL && (f == NULL || pt < f))
173 	{
174 		*pt = '\0';
175 		*port = atoi(pt + 1);
176 	}
177 	else
178 	{
179 		if (f)
180 			*f = '\0';
181 		*port = 80;
182 	}
183 	*host = g_strdup(h);
184 
185 	if (f)
186 		*filename = g_strdup(f + 1);
187 	else
188 		*filename = NULL;
189 	g_free(temp);
190 }
191 
flac_http_close(void)192 void flac_http_close(void)
193 {
194 	going = FALSE;
195 
196 	pthread_join(thread, NULL);
197 	g_free(icy_name);
198 	icy_name = NULL;
199 }
200 
201 
http_used(void)202 static gint http_used(void)
203 {
204 	if (wr_index >= rd_index)
205 		return wr_index - rd_index;
206 	return buffer_length - (rd_index - wr_index);
207 }
208 
http_free(void)209 static gint http_free(void)
210 {
211 	if (rd_index > wr_index)
212 		return (rd_index - wr_index) - 1;
213 	return (buffer_length - (wr_index - rd_index)) - 1;
214 }
215 
http_wait_for_data(gint bytes)216 static void http_wait_for_data(gint bytes)
217 {
218 	while ((prebuffering || http_used() < bytes) && !eof && going)
219 		xmms_usleep(10000);
220 }
221 
show_error_message(gchar * error)222 static void show_error_message(gchar *error)
223 {
224 	if(!error_dialog)
225 	{
226 		GDK_THREADS_ENTER();
227 		error_dialog = xmms_show_message(_("Error"), error, _("Ok"), FALSE,
228 						 NULL, NULL);
229 		gtk_signal_connect(GTK_OBJECT(error_dialog),
230 				   "destroy",
231 				   GTK_SIGNAL_FUNC(gtk_widget_destroyed),
232 				   &error_dialog);
233 		GDK_THREADS_LEAVE();
234 	}
235 }
236 
flac_http_read(gpointer data,gint length)237 int flac_http_read(gpointer data, gint length)
238 {
239 	gint len, cnt, off = 0, meta_len, meta_off = 0, i;
240 	gchar *meta_data, **tags, *temp, *title;
241 	if (length > buffer_length) {
242 		length = buffer_length;
243 	}
244 
245 	http_wait_for_data(length);
246 
247 	if (!going)
248 		return 0;
249 	len = min(http_used(), length);
250 
251 	while (len && http_used())
252 	{
253 		if ((flac_cfg.stream.cast_title_streaming) && (icy_metaint > 0) && (buffer_read % icy_metaint) == 0 && (buffer_read > 0))
254 		{
255 			meta_len = *((guchar *) buffer + rd_index) * 16;
256 			rd_index = (rd_index + 1) % buffer_length;
257 			if (meta_len > 0)
258 			{
259 				http_wait_for_data(meta_len);
260 				meta_data = g_malloc0(meta_len);
261 				if (http_used() >= meta_len)
262 				{
263 					while (meta_len)
264 					{
265 						cnt = min(meta_len, buffer_length - rd_index);
266 						memcpy(meta_data + meta_off, buffer + rd_index, cnt);
267 						rd_index = (rd_index + cnt) % buffer_length;
268 						meta_len -= cnt;
269 						meta_off += cnt;
270 					}
271 					tags = g_strsplit(meta_data, "';", 0);
272 
273 					for (i = 0; tags[i]; i++)
274 					{
275 						if (!strncasecmp(tags[i], "StreamTitle=", 12))
276 						{
277 							temp = g_strdup(tags[i] + 13);
278 							title = g_strdup_printf("%s (%s)", temp, icy_name);
279 							set_track_info(title, -1);
280 							g_free(title);
281 							g_free(temp);
282 						}
283 
284 					}
285 					g_strfreev(tags);
286 
287 				}
288 				g_free(meta_data);
289 			}
290 			if (!http_used())
291 				http_wait_for_data(length - off);
292 			cnt = min3(len, buffer_length - rd_index, http_used());
293 		}
294 		else if ((icy_metaint > 0) && (flac_cfg.stream.cast_title_streaming))
295 			cnt = min4(len, buffer_length - rd_index, http_used(), icy_metaint - (gint) (buffer_read % icy_metaint));
296 		else
297 			cnt = min3(len, buffer_length - rd_index, http_used());
298 		if (output_file)
299 			fwrite(buffer + rd_index, 1, cnt, output_file);
300 
301 		memcpy((gchar *)data + off, buffer + rd_index, cnt);
302 		rd_index = (rd_index + cnt) % buffer_length;
303 		buffer_read += cnt;
304 		len -= cnt;
305 		off += cnt;
306 	}
307 	if (!off) {
308 		fprintf(stderr, "returning zero\n");
309 	}
310 	return off;
311 }
312 
http_check_for_data(void)313 static gboolean http_check_for_data(void)
314 {
315 
316 	fd_set set;
317 	struct timeval tv;
318 	gint ret;
319 
320 	tv.tv_sec = 0;
321 	tv.tv_usec = 20000;
322 	FD_ZERO(&set);
323 	FD_SET(sock, &set);
324 	ret = select(sock + 1, &set, NULL, NULL, &tv);
325 	if (ret > 0)
326 		return TRUE;
327 	return FALSE;
328 }
329 
flac_http_read_line(gchar * buf,gint size)330 gint flac_http_read_line(gchar * buf, gint size)
331 {
332 	gint i = 0;
333 
334 	while (going && i < size - 1)
335 	{
336 		if (http_check_for_data())
337 		{
338 			if (read(sock, buf + i, 1) <= 0)
339 				return -1;
340 			if (buf[i] == '\n')
341 				break;
342 			if (buf[i] != '\r')
343 				i++;
344 		}
345 	}
346 	if (!going)
347 		return -1;
348 	buf[i] = '\0';
349 	return i;
350 }
351 
352 /* returns the file descriptor of the socket, or -1 on error */
http_connect(gchar * url_,gboolean head,guint64 offset)353 static int http_connect (gchar *url_, gboolean head, guint64 offset)
354 {
355 	gchar line[1024], *user, *pass, *host, *filename,
356 	     *status, *url, *temp, *file;
357 	gchar *chost;
358 	gint cnt, error, port, cport;
359 	socklen_t err_len;
360 	gboolean redirect;
361 	int udp_sock = 0;
362 	fd_set set;
363 	struct hostent *hp;
364 	struct sockaddr_in address;
365 	struct timeval tv;
366 
367 	url = g_strdup (url_);
368 
369 	do
370 	{
371 		redirect=FALSE;
372 
373 		g_strstrip(url);
374 
375 		parse_url(url, &user, &pass, &host, &port, &filename);
376 
377 		if ((!filename || !*filename) && url[strlen(url) - 1] != '/')
378 			temp = g_strconcat(url, "/", NULL);
379 		else
380 			temp = g_strdup(url);
381 		g_free(url);
382 		url = temp;
383 
384 		chost = flac_cfg.stream.use_proxy ? flac_cfg.stream.proxy_host : host;
385 		cport = flac_cfg.stream.use_proxy ? flac_cfg.stream.proxy_port : port;
386 
387 		sock = socket(AF_INET, SOCK_STREAM, 0);
388 		fcntl(sock, F_SETFL, O_NONBLOCK);
389 		address.sin_family = AF_INET;
390 
391 		status = g_strdup_printf(_("LOOKING UP %s"), chost);
392 		flac_ip.set_info_text(status);
393 		g_free(status);
394 
395 		if (!(hp = gethostbyname(chost)))
396 		{
397 			status = g_strdup_printf(_("Couldn't look up host %s"), chost);
398 			show_error_message(status);
399 			g_free(status);
400 
401 			flac_ip.set_info_text(NULL);
402 			eof = TRUE;
403 		}
404 
405 		if (!eof)
406 		{
407 			memcpy(&address.sin_addr.s_addr, *(hp->h_addr_list), sizeof (address.sin_addr.s_addr));
408 			address.sin_port = (gint) g_htons(cport);
409 
410 			status = g_strdup_printf(_("CONNECTING TO %s:%d"), chost, cport);
411 			flac_ip.set_info_text(status);
412 			g_free(status);
413 			if (connect(sock, (struct sockaddr *) &address, sizeof (struct sockaddr_in)) == -1)
414 			{
415 				if (errno != EINPROGRESS)
416 				{
417 					status = g_strdup_printf(_("Couldn't connect to host %s"), chost);
418 					show_error_message(status);
419 					g_free(status);
420 
421 					flac_ip.set_info_text(NULL);
422 					eof = TRUE;
423 				}
424 			}
425 			while (going)
426 			{
427 				tv.tv_sec = 0;
428 				tv.tv_usec = 10000;
429 				FD_ZERO(&set);
430 				FD_SET(sock, &set);
431 				if (select(sock + 1, NULL, &set, NULL, &tv) > 0)
432 				{
433 					err_len = sizeof (error);
434 					getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &err_len);
435 					if (error)
436 					{
437 						status = g_strdup_printf(_("Couldn't connect to host %s"),
438 									 chost);
439 						show_error_message(status);
440 						g_free(status);
441 
442 						flac_ip.set_info_text(NULL);
443 						eof = TRUE;
444 
445 					}
446 					break;
447 				}
448 			}
449 			if (!eof)
450 			{
451 				gchar *auth = NULL, *proxy_auth = NULL;
452 				gchar udpspace[30];
453 				int udp_port;
454 
455 				if (flac_cfg.stream.use_udp_channel)
456 				{
457 					udp_port = udp_establish_listener (&udp_sock);
458 					if (udp_port > 0)
459 						flac_snprintf (udpspace, sizeof (udpspace), "x-audiocast-udpport: %d\r\n", udp_port);
460 					else
461 						udp_sock = 0;
462 				}
463 
464 				if(user && pass)
465 					auth = basic_authentication_encode(user, pass, "Authorization");
466 
467 				if (flac_cfg.stream.use_proxy)
468 				{
469 					file = g_strdup(url);
470 					if(flac_cfg.stream.proxy_use_auth && flac_cfg.stream.proxy_user && flac_cfg.stream.proxy_pass)
471 					{
472 						proxy_auth = basic_authentication_encode(flac_cfg.stream.proxy_user,
473 											 flac_cfg.stream.proxy_pass,
474 											 "Proxy-Authorization");
475 					}
476 				}
477 				else
478 					file = g_strconcat("/", filename, NULL);
479 
480 				temp = g_strdup_printf("GET %s HTTP/1.0\r\n"
481 						       "Host: %s\r\n"
482 						       "User-Agent: %s/%s\r\n"
483 						       "%s%s%s%s",
484 						       file, host, "Reference FLAC Player", FLAC__VERSION_STRING,
485 						       proxy_auth ? proxy_auth : "", auth ? auth : "",
486 						       flac_cfg.stream.cast_title_streaming ?  "Icy-MetaData:1\r\n" : "",
487 						       flac_cfg.stream.use_udp_channel ? udpspace : "");
488 				if (offset && !head) {
489 					gchar *temp_dead = temp;
490 					temp = g_strdup_printf ("%sRange: %" PRIu64 "-\r\n", temp, offset);
491 					fputs (temp, stderr);
492 					g_free (temp_dead);
493 				}
494 
495 				g_free(file);
496 				if(proxy_auth)
497 					g_free(proxy_auth);
498 				if(auth)
499 					g_free(auth);
500 				write(sock, temp, strlen(temp));
501 				write(sock, "\r\n", 2);
502 				g_free(temp);
503 				flac_ip.set_info_text(_("CONNECTED: WAITING FOR REPLY"));
504 				while (going && !eof)
505 				  {
506 					if (http_check_for_data())
507 					{
508 						if (flac_http_read_line(line, 1024))
509 						{
510 							status = strchr(line, ' ');
511 							if (status)
512 							{
513 								if (status[1] == '2')
514 									break;
515 								else if(status[1] == '3' && status[2] == '0' && status[3] == '2')
516 								{
517 									while(going)
518 									{
519 										if(http_check_for_data())
520 										{
521 											if((cnt = flac_http_read_line(line, 1024)) != -1)
522 											{
523 												if(!cnt)
524 													break;
525 												if(!strncmp(line, "Location:", 9))
526 												{
527 													g_free(url);
528 													url = g_strdup(line+10);
529 												}
530 											}
531 											else
532 											{
533 												eof=TRUE;
534 												flac_ip.set_info_text(NULL);
535 												break;
536 											}
537 										}
538 									}
539 									redirect=TRUE;
540 									break;
541 								}
542 								else
543 								{
544 									status = g_strdup_printf(_("Couldn't connect to host %s\nServer reported: %s"), chost, status);
545 									show_error_message(status);
546 									g_free(status);
547 									break;
548 								}
549 							}
550 						}
551 						else
552 						{
553 							eof = TRUE;
554 							flac_ip.set_info_text(NULL);
555 						}
556 					}
557 				}
558 
559 				while (going && !redirect)
560 				{
561 					if (http_check_for_data())
562 					{
563 						if ((cnt = flac_http_read_line(line, 1024)) != -1)
564 						{
565 							if (!cnt)
566 								break;
567 							if (!strncmp(line, "icy-name:", 9))
568 								icy_name = g_strdup(line + 9);
569 							else if (!strncmp(line, "x-audiocast-name:", 17))
570 								icy_name = g_strdup(line + 17);
571 							if (!strncmp(line, "icy-metaint:", 12))
572 								icy_metaint = atoi(line + 12);
573 							if (!strncmp(line, "x-audiocast-udpport:", 20)) {
574 #ifndef NDEBUG
575 								fprintf (stderr, "Server wants udp messages on port %d\n", atoi (line + 20));
576 #endif
577 								/*udp_serverport = atoi (line + 20);*/
578 							}
579 
580 						}
581 						else
582 						{
583 							eof = TRUE;
584 							flac_ip.set_info_text(NULL);
585 							break;
586 						}
587 					}
588 				}
589 			}
590 		}
591 
592 		if(redirect)
593 		{
594 			if (output_file)
595 			{
596 				fclose(output_file);
597 				output_file = NULL;
598 			}
599 			close(sock);
600 		}
601 
602 		g_free(user);
603 		g_free(pass);
604 		g_free(host);
605 		g_free(filename);
606 	} while(redirect);
607 
608 	g_free(url);
609 	return eof ? -1 : sock;
610 }
611 
http_buffer_loop(void * arg)612 static void *http_buffer_loop(void *arg)
613 {
614 	gchar *status, *url, *temp, *file;
615 	gint cnt, written;
616 	int udp_sock = 0;
617 
618 	url = (gchar *) arg;
619 	sock = http_connect (url, false, offset);
620 
621 	if (sock >= 0 && flac_cfg.stream.save_http_stream) {
622 		gchar *output_name;
623 		file = flac_http_get_title(url);
624 		output_name = file;
625 		if (!strncasecmp(output_name, "http://", 7))
626 			output_name += 7;
627 		temp = strrchr(output_name, '.');
628 		if (temp && (!strcasecmp(temp, ".fla") || !strcasecmp(temp, ".flac")))
629 			*temp = '\0';
630 
631 		while ((temp = strchr(output_name, '/')))
632 			*temp = '_';
633 		output_name = g_strdup_printf("%s/%s.flac", flac_cfg.stream.save_http_path, output_name);
634 
635 		g_free(file);
636 
637 		output_file = fopen(output_name, "wb");
638 		g_free(output_name);
639 	}
640 
641 	while (going)
642 	{
643 
644 		if (!http_used() && !flac_ip.output->buffer_playing())
645 			prebuffering = TRUE;
646 		if (http_free() > 0 && !eof)
647 		{
648 			if (http_check_for_data())
649 			{
650 				cnt = min(http_free(), buffer_length - wr_index);
651 				if (cnt > 1024)
652 					cnt = 1024;
653 				written = read(sock, buffer + wr_index, cnt);
654 				if (written <= 0)
655 				{
656 					eof = TRUE;
657 					if (prebuffering)
658 					{
659 						prebuffering = FALSE;
660 
661 						flac_ip.set_info_text(NULL);
662 					}
663 
664 				}
665 				else
666 					wr_index = (wr_index + written) % buffer_length;
667 			}
668 
669 			if (prebuffering)
670 			{
671 				if (http_used() > prebuffer_length)
672 				{
673 					prebuffering = FALSE;
674 					flac_ip.set_info_text(NULL);
675 				}
676 				else
677 				{
678 					status = g_strdup_printf(_("PRE-BUFFERING: %dKB/%dKB"), http_used() / 1024, prebuffer_length / 1024);
679 					flac_ip.set_info_text(status);
680 					g_free(status);
681 				}
682 
683 			}
684 		}
685 		else
686 			xmms_usleep(10000);
687 
688 		if (flac_cfg.stream.use_udp_channel && udp_sock != 0)
689 			if (udp_check_for_data(udp_sock) < 0)
690 			{
691 				close(udp_sock);
692 				udp_sock = 0;
693 			}
694 	}
695 	if (output_file)
696 	{
697 		fclose(output_file);
698 		output_file = NULL;
699 	}
700 	if (sock >= 0) {
701 		close(sock);
702 	}
703 	if (udp_sock != 0)
704 		close(udp_sock);
705 
706 	g_free(buffer);
707 	g_free(url);
708 
709 	pthread_exit(NULL);
710 	return NULL; /* avoid compiler warning */
711 }
712 
flac_http_open(const gchar * _url,guint64 _offset)713 int flac_http_open(const gchar * _url, guint64 _offset)
714 {
715 	gchar *url;
716 
717 	url = g_strdup(_url);
718 
719 	rd_index = 0;
720 	wr_index = 0;
721 	buffer_length = flac_cfg.stream.http_buffer_size * 1024;
722 	prebuffer_length = (buffer_length * flac_cfg.stream.http_prebuffer) / 100;
723 	buffer_read = 0;
724 	icy_metaint = 0;
725 	prebuffering = TRUE;
726 	going = TRUE;
727 	eof = FALSE;
728 	buffer = g_malloc(buffer_length);
729 	offset = _offset;
730 
731 	pthread_create(&thread, NULL, http_buffer_loop, url);
732 
733 	return 0;
734 }
735 
flac_http_get_title(char * url)736 char *flac_http_get_title(char *url)
737 {
738 	if (icy_name)
739 		return g_strdup(icy_name);
740 	if (g_basename(url) && strlen(g_basename(url)) > 0)
741 		return g_strdup(g_basename(url));
742 	return g_strdup(url);
743 }
744 
745 /* Start UDP Channel specific stuff */
746 
747 /* Find a good local udp port and bind udp_sock to it, return the port */
udp_establish_listener(int * sock)748 static int udp_establish_listener(int *sock)
749 {
750 	struct sockaddr_in sin;
751 	socklen_t sinlen = sizeof (struct sockaddr_in);
752 
753 #ifndef NDEBUG
754 	fprintf (stderr,"Establishing udp listener\n");
755 #endif
756 
757 	if ((*sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
758 	{
759 		g_log(NULL, G_LOG_LEVEL_CRITICAL,
760 		      "udp_establish_listener(): unable to create socket");
761 		return -1;
762 	}
763 
764 	memset(&sin, 0, sinlen);
765 	sin.sin_family = AF_INET;
766 	sin.sin_addr.s_addr = g_htonl(INADDR_ANY);
767 
768 	if (bind(*sock, (struct sockaddr *)&sin, sinlen) < 0)
769 	{
770 		g_log(NULL, G_LOG_LEVEL_CRITICAL,
771 		      "udp_establish_listener():  Failed to bind socket to localhost: %s", strerror(errno));
772 		close(*sock);
773 		return -1;
774 	}
775 	if (fcntl(*sock, F_SETFL, O_NONBLOCK) < 0)
776 	{
777 		g_log(NULL, G_LOG_LEVEL_CRITICAL,
778 		      "udp_establish_listener():  Failed to set flags: %s", strerror(errno));
779 		close(*sock);
780 		return -1;
781 	}
782 
783 	memset(&sin, 0, sinlen);
784 	if (getsockname(*sock, (struct sockaddr *)&sin, &sinlen) < 0)
785 	{
786 		g_log(NULL, G_LOG_LEVEL_CRITICAL,
787 		      "udp_establish_listener():  Failed to retrieve socket info: %s", strerror(errno));
788 		close(*sock);
789 		return -1;
790 	}
791 
792 #ifndef NDEBUG
793 	fprintf (stderr,"Listening on local %s:%d\n", inet_ntoa(sin.sin_addr), g_ntohs(sin.sin_port));
794 #endif
795 
796 	return g_ntohs(sin.sin_port);
797 }
798 
udp_check_for_data(int sock)799 static int udp_check_for_data(int sock)
800 {
801 	char buf[1025], **lines;
802 	char *valptr;
803 	gchar *title;
804 	gint len, i;
805 	struct sockaddr_in from;
806 	socklen_t fromlen;
807 
808 	fromlen = sizeof(struct sockaddr_in);
809 
810 	if ((len = recvfrom(sock, buf, 1024, 0, (struct sockaddr *)&from, &fromlen)) < 0)
811 	{
812 		if (errno != EAGAIN)
813 		{
814 			g_log(NULL, G_LOG_LEVEL_CRITICAL,
815 			      "udp_read_data(): Error reading from socket: %s", strerror(errno));
816 			return -1;
817 		}
818 		return 0;
819 	}
820 	buf[len] = '\0';
821 #ifndef NDEBUG
822 	fprintf (stderr,"Received: [%s]\n", buf);
823 #endif
824 	lines = g_strsplit(buf, "\n", 0);
825 	if (!lines)
826 		return 0;
827 
828 	for (i = 0; lines[i]; i++)
829 	{
830 		while ((lines[i][strlen(lines[i]) - 1] == '\n') ||
831 		       (lines[i][strlen(lines[i]) - 1] == '\r'))
832 			lines[i][strlen(lines[i]) - 1] = '\0';
833 
834 		valptr = strchr(lines[i], ':');
835 
836 		if (!valptr)
837 			continue;
838 		else
839 			valptr++;
840 
841 		g_strstrip(valptr);
842 		if (!strlen(valptr))
843 			continue;
844 
845 		if (strstr(lines[i], "x-audiocast-streamtitle") != NULL)
846 		{
847 			title = g_strdup_printf ("%s (%s)", valptr, icy_name);
848 			if (going)
849 				set_track_info(title, -1);
850 			g_free (title);
851 		}
852 
853 #if 0
854 		else if (strstr(lines[i], "x-audiocast-streamlength") != NULL)
855 		{
856 			if (atoi(valptr) != -1)
857 				set_track_info(NULL, atoi(valptr));
858 		}
859 #endif
860 
861 		else if (strstr(lines[i], "x-audiocast-streammsg") != NULL)
862 		{
863 			/* set_track_info(title, -1); */
864 /*  			xmms_show_message(_("Message"), valptr, _("Ok"), */
865 /*  					  FALSE, NULL, NULL); */
866 			g_message("Stream_message: %s", valptr);
867 		}
868 
869 #if 0
870 		/* Use this to direct your webbrowser.. yeah right.. */
871 		else if (strstr(lines[i], "x-audiocast-streamurl") != NULL)
872 		{
873 			if (lasturl && g_strcmp (valptr, lasturl))
874 			{
875 				c_message (stderr, "Song URL: %s\n", valptr);
876 				g_free (lasturl);
877 				lasturl = g_strdup (valptr);
878 			}
879 		}
880 #endif
881 		else if (strstr(lines[i], "x-audiocast-udpseqnr:") != NULL)
882 		{
883 			gchar obuf[60];
884 			flac_snprintf(obuf, sizeof (obuf), "x-audiocast-ack: %ld \r\n", atol(valptr));
885 			if (sendto(sock, obuf, strlen(obuf), 0, (struct sockaddr *) &from, fromlen) < 0)
886 			{
887 				g_log(NULL, G_LOG_LEVEL_WARNING,
888 				      "udp_check_for_data(): Unable to send ack to server: %s", strerror(errno));
889 			}
890 #ifndef NDEBUG
891 			else
892 				fprintf(stderr,"Sent ack: %s", obuf);
893 			fprintf (stderr,"Remote: %s:%d\n", inet_ntoa(from.sin_addr), g_ntohs(from.sin_port));
894 #endif
895 		}
896 	}
897 	g_strfreev(lines);
898 	return 0;
899 }
900