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 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 */ 18/* modified for FLAC support by Steven Richman (2003) */ 19 20#if HAVE_CONFIG_H 21# include <config.h> 22#endif 23 24#include <sys/types.h> 25#include <sys/socket.h> 26#include <sys/time.h> 27#include <netinet/in.h> 28#include <arpa/inet.h> 29#include <netdb.h> 30#include <glib.h> 31#include <string.h> 32#include <fcntl.h> 33#include <unistd.h> 34#include <errno.h> 35#include <stdio.h> 36#include <stdlib.h> 37 38#include <pthread.h> 39 40#include <xmms/util.h> 41#include <xmms/plugin.h> 42 43#include "FLAC/format.h" 44#include "configure.h" 45#include "locale_hack.h" 46#include "plugin.h" 47 48/* on FreeBSD we get socklen_t from <sys/socket.h> */ 49#if (!defined HAVE_SOCKLEN_T) && !defined(__FreeBSD__) 50typedef unsigned int socklen_t; 51#endif 52 53#define min(x,y) ((x)<(y)?(x):(y)) 54#define min3(x,y,z) (min(x,y)<(z)?min(x,y):(z)) 55#define min4(x,y,z,w) (min3(x,y,z)<(w)?min3(x,y,z):(w)) 56 57static gchar *icy_name = NULL; 58static gint icy_metaint = 0; 59 60extern InputPlugin flac_ip; 61 62#undef DEBUG_UDP 63 64/* Static udp channel functions */ 65static int udp_establish_listener (gint *sock); 66static int udp_check_for_data(gint sock); 67 68static char *flac_http_get_title(char *url); 69 70static gboolean prebuffering, going, eof = FALSE; 71static gint sock, rd_index, wr_index, buffer_length, prebuffer_length; 72static guint64 buffer_read = 0; 73static gchar *buffer; 74static guint64 offset; 75static pthread_t thread; 76static GtkWidget *error_dialog = NULL; 77 78static 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. */ 85static 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. */ 122static 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 138static 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 192void 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 202static 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 209static 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 216static void http_wait_for_data(gint bytes) 217{ 218 while ((prebuffering || http_used() < bytes) && !eof && going) 219 xmms_usleep(10000); 220} 221 222static 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 237int 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 313static 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 330gint 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 */ 353static 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 sprintf (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: %llu-\r\n", temp, offset); 491 fprintf (stderr, "%s", temp); 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#ifdef DEBUG_UDP 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 612static 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 713int 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 736char *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 */ 748static int udp_establish_listener(int *sock) 749{ 750 struct sockaddr_in sin; 751 socklen_t sinlen = sizeof (struct sockaddr_in); 752 753#ifdef DEBUG_UDP 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#ifdef DEBUG_UDP 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 799static 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#ifdef DEBUG_UDP 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 sprintf(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#ifdef DEBUG_UDP 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