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