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