1 /* Icecast
2  *
3  * This program is distributed under the GNU General Public License, version 2.
4  * A copy of this license is included with this source.
5  *
6  * Copyright 2000-2004, Jack Moffitt <jack@xiph.org,
7  *                      Michael Smith <msmith@xiph.org>,
8  *                      oddsock <oddsock@xiph.org>,
9  *                      Karl Heyes <karl@xiph.org>
10  *                      and others (see AUTHORS for details).
11  */
12 
13 /* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */
14 #ifdef HAVE_CONFIG_H
15 #include <config.h>
16 #endif
17 
18 #include <stdio.h>
19 #include <string.h>
20 #include <stdlib.h>
21 #include <curl/curl.h>
22 
23 #include "thread/thread.h"
24 
25 #include "connection.h"
26 #include "refbuf.h"
27 #include "client.h"
28 #include "logging.h"
29 #include "format.h"
30 #include "source.h"
31 #include "cfgfile.h"
32 #include "stats.h"
33 
34 #ifdef WIN32
35 #define snprintf _snprintf
36 #endif
37 
38 #define CATMODULE "yp"
39 
40 struct yp_server
41 {
42     char        *url;
43     char        *server_id;
44     unsigned    url_timeout;
45     unsigned    touch_interval;
46     int         remove;
47 
48     CURL *curl;
49     struct ypdata_tag *mounts, *pending_mounts;
50     struct yp_server *next;
51     char curl_error[CURL_ERROR_SIZE];
52 };
53 
54 
55 
56 typedef struct ypdata_tag
57 {
58     int remove;
59     int release;
60     int cmd_ok;
61 
62     char *sid;
63     char *mount;
64     char *url;
65     char *listen_url;
66     char *server_name;
67     char *server_desc;
68     char *server_genre;
69     char *cluster_password;
70     char *bitrate;
71     char *audio_info;
72     char *server_type;
73     char *current_song;
74     char *subtype;
75 
76     struct yp_server *server;
77     time_t      next_update;
78     unsigned    touch_interval;
79     char        *error_msg;
80     int    (*process)(struct ypdata_tag *yp, char *s, unsigned len);
81 
82     struct ypdata_tag *next;
83 } ypdata_t;
84 
85 
86 static rwlock_t yp_lock;
87 static mutex_t yp_pending_lock;
88 
89 static volatile struct yp_server *active_yps = NULL, *pending_yps = NULL;
90 static volatile int yp_update = 0;
91 static int yp_running;
92 static time_t now;
93 static thread_type *yp_thread;
94 static volatile unsigned client_limit = 0;
95 static volatile char *server_version = NULL;
96 
97 static void *yp_update_thread(void *arg);
98 static void add_yp_info (ypdata_t *yp, void *info, int type);
99 static int do_yp_remove (ypdata_t *yp, char *s, unsigned len);
100 static int do_yp_add (ypdata_t *yp, char *s, unsigned len);
101 static int do_yp_touch (ypdata_t *yp, char *s, unsigned len);
102 static void yp_destroy_ypdata(ypdata_t *ypdata);
103 
104 
105 /* curl callback used to parse headers coming back from the YP server */
handle_returned_header(void * ptr,size_t size,size_t nmemb,void * stream)106 static int handle_returned_header (void *ptr, size_t size, size_t nmemb, void *stream)
107 {
108     ypdata_t *yp = stream;
109     unsigned bytes = size * nmemb;
110 
111     /* ICECAST_LOG_DEBUG("header from YP is \"%.*s\"", bytes, ptr); */
112     if (strncasecmp (ptr, "YPResponse: 1", 13) == 0)
113         yp->cmd_ok = 1;
114 
115     if (strncasecmp (ptr, "YPMessage: ", 11) == 0)
116     {
117         unsigned len = bytes - 11;
118         free (yp->error_msg);
119         yp->error_msg = calloc (1, len);
120         if (yp->error_msg)
121             sscanf (ptr + 11, "%[^\r\n]", yp->error_msg);
122     }
123 
124     if (yp->process == do_yp_add)
125     {
126         if (strncasecmp (ptr, "SID: ", 5) == 0)
127         {
128             unsigned len = bytes - 5;
129             free (yp->sid);
130             yp->sid = calloc (1, len);
131             if (yp->sid)
132                 sscanf (ptr + 5, "%[^\r\n]", yp->sid);
133         }
134     }
135     if (strncasecmp (ptr, "TouchFreq: ", 11) == 0)
136     {
137         unsigned secs;
138         if ( sscanf (ptr + 11, "%u", &secs) != 1 )
139             secs = 0;
140         if (secs < 30)
141             secs = 30;
142         ICECAST_LOG_DEBUG("server touch interval is %u", secs);
143         yp->touch_interval = secs;
144     }
145     return (int)bytes;
146 }
147 
148 
149 /* capture returned data, but don't do anything with it, shouldn't be any */
handle_returned_data(void * ptr,size_t size,size_t nmemb,void * stream)150 static int handle_returned_data (void *ptr, size_t size, size_t nmemb, void *stream)
151 {
152     return (int)(size*nmemb);
153 }
154 
155 
156 /* search the active and pending YP server lists */
find_yp_server(const char * url)157 static struct yp_server *find_yp_server (const char *url)
158 {
159     struct yp_server *server;
160 
161     server = (struct yp_server *)active_yps;
162     while (server)
163     {
164         if (strcmp (server->url, url) == 0)
165             return server;
166         server = server->next;
167     }
168     server = (struct yp_server *)pending_yps;
169     while (server)
170     {
171         if (strcmp (server->url, url) == 0)
172             break;
173         server = server->next;
174     }
175     return server;
176 }
177 
178 
destroy_yp_server(struct yp_server * server)179 static void destroy_yp_server (struct yp_server *server)
180 {
181     if (server == NULL)
182         return;
183     ICECAST_LOG_DEBUG("Removing YP server entry for %s", server->url);
184     if (server->curl)
185         curl_easy_cleanup (server->curl);
186     if (server->mounts) ICECAST_LOG_WARN("active ypdata not freed up");
187     if (server->pending_mounts) ICECAST_LOG_WARN("pending ypdata not freed up");
188     free (server->url);
189     free (server->server_id);
190     free (server);
191 }
192 
193 
194 
195 /* search for a ypdata entry corresponding to a specific mountpoint */
find_yp_mount(ypdata_t * mounts,const char * mount)196 static ypdata_t *find_yp_mount (ypdata_t *mounts, const char *mount)
197 {
198     ypdata_t *yp = mounts;
199     while (yp)
200     {
201         if (strcmp (yp->mount, mount) == 0)
202             break;
203         yp = yp->next;
204     }
205     return yp;
206 }
207 
208 
yp_recheck_config(ice_config_t * config)209 void yp_recheck_config (ice_config_t *config)
210 {
211     int i;
212     struct yp_server *server;
213 
214     ICECAST_LOG_DEBUG("Updating YP configuration");
215     thread_rwlock_rlock (&yp_lock);
216 
217     server = (struct yp_server *)active_yps;
218     while (server)
219     {
220         server->remove = 1;
221         server = server->next;
222     }
223     client_limit = config->client_limit;
224     free ((char*)server_version);
225     server_version = strdup (config->server_id);
226     /* for each yp url in config, check to see if one exists
227        if not, then add it. */
228     for (i=0 ; i < config->num_yp_directories; i++)
229     {
230         server = find_yp_server (config->yp_url[i]);
231         if (server == NULL)
232         {
233             server = calloc (1, sizeof (struct yp_server));
234 
235             if (server == NULL)
236             {
237                 destroy_yp_server (server);
238                 break;
239             }
240             server->server_id = strdup ((char *)server_version);
241             server->url = strdup (config->yp_url[i]);
242             server->url_timeout = config->yp_url_timeout[i];
243             server->touch_interval = config->yp_touch_interval[i];
244             server->curl = curl_easy_init();
245             if (server->curl == NULL)
246             {
247                 destroy_yp_server (server);
248                 break;
249             }
250             if (server->url_timeout > 10 || server->url_timeout < 1)
251                 server->url_timeout = 6;
252             if (server->touch_interval < 30)
253                 server->touch_interval = 30;
254             curl_easy_setopt (server->curl, CURLOPT_USERAGENT, server->server_id);
255             curl_easy_setopt (server->curl, CURLOPT_URL, server->url);
256             curl_easy_setopt (server->curl, CURLOPT_HEADERFUNCTION, handle_returned_header);
257             curl_easy_setopt (server->curl, CURLOPT_WRITEFUNCTION, handle_returned_data);
258             curl_easy_setopt (server->curl, CURLOPT_WRITEDATA, server->curl);
259             curl_easy_setopt (server->curl, CURLOPT_TIMEOUT, server->url_timeout);
260             curl_easy_setopt (server->curl, CURLOPT_NOSIGNAL, 1L);
261             curl_easy_setopt (server->curl, CURLOPT_FOLLOWLOCATION, 1L);
262             curl_easy_setopt (server->curl, CURLOPT_MAXREDIRS, 3L);
263             curl_easy_setopt (server->curl, CURLOPT_ERRORBUFFER, &(server->curl_error[0]));
264             server->next = (struct yp_server *)pending_yps;
265             pending_yps = server;
266             ICECAST_LOG_INFO("Adding new YP server \"%s\" (timeout %ds, default interval %ds)",
267                     server->url, server->url_timeout, server->touch_interval);
268         }
269         else
270         {
271             server->remove = 0;
272         }
273     }
274     thread_rwlock_unlock (&yp_lock);
275     yp_update = 1;
276 }
277 
278 
yp_initialize(void)279 void yp_initialize(void)
280 {
281     ice_config_t *config = config_get_config();
282     thread_rwlock_create (&yp_lock);
283     thread_mutex_create (&yp_pending_lock);
284     yp_recheck_config (config);
285     config_release_config ();
286     yp_thread = thread_create("YP Touch Thread", yp_update_thread,
287                             (void *)NULL, THREAD_ATTACHED);
288 }
289 
290 
291 
292 /* handler for curl, checks if successful handling occurred
293  * return 0 for ok, -1 for this entry failed, -2 for server fail.
294  * On failure case, update and process are modified
295  */
send_to_yp(const char * cmd,ypdata_t * yp,char * post)296 static int send_to_yp (const char *cmd, ypdata_t *yp, char *post)
297 {
298     int curlcode;
299     struct yp_server *server = yp->server;
300 
301     /* ICECAST_LOG_DEBUG("send YP (%s):%s", cmd, post); */
302     yp->cmd_ok = 0;
303     curl_easy_setopt (server->curl, CURLOPT_POSTFIELDS, post);
304     curl_easy_setopt (server->curl, CURLOPT_WRITEHEADER, yp);
305     curlcode = curl_easy_perform (server->curl);
306     if (curlcode)
307     {
308         yp->process = do_yp_add;
309         yp->next_update = now + 1200;
310         ICECAST_LOG_ERROR("connection to %s failed with \"%s\"", server->url, server->curl_error);
311         return -2;
312     }
313     if (yp->cmd_ok == 0)
314     {
315         if (yp->error_msg == NULL)
316             yp->error_msg = strdup ("no response from server");
317         if (yp->process == do_yp_add)
318         {
319             ICECAST_LOG_ERROR("YP %s on %s failed: %s", cmd, server->url, yp->error_msg);
320             yp->next_update = now + 7200;
321         }
322         if (yp->process == do_yp_touch)
323         {
324             /* At this point the touch request failed, either because they rejected our session
325              * or the server isn't accessible. This means we have to wait before doing another
326              * add request. We have a minimum delay but we could allow the directory server to
327              * give us a wait time using the TouchFreq header. This time could be given in such
328              * cases as a firewall block or incorrect listenurl.
329              */
330             if (yp->touch_interval < 1200)
331                 yp->next_update = now + 1200;
332             else
333                 yp->next_update = now + yp->touch_interval;
334             ICECAST_LOG_INFO("YP %s on %s failed: %s", cmd, server->url, yp->error_msg);
335         }
336         yp->process = do_yp_add;
337         free (yp->sid);
338         yp->sid = NULL;
339         return -1;
340     }
341     ICECAST_LOG_DEBUG("YP %s at %s succeeded", cmd, server->url);
342     return 0;
343 }
344 
345 
346 /* routines for building and issues requests to the YP server */
do_yp_remove(ypdata_t * yp,char * s,unsigned len)347 static int do_yp_remove (ypdata_t *yp, char *s, unsigned len)
348 {
349     int ret = 0;
350 
351     if (yp->sid)
352     {
353         ret = snprintf (s, len, "action=remove&sid=%s", yp->sid);
354         if (ret >= (signed)len)
355             return ret+1;
356 
357         ICECAST_LOG_INFO("clearing up YP entry for %s", yp->mount);
358         ret = send_to_yp ("remove", yp, s);
359         free (yp->sid);
360         yp->sid = NULL;
361     }
362     yp->remove = 1;
363     yp->process = do_yp_add;
364     yp_update = 1;
365 
366     return ret;
367 }
368 
369 
do_yp_add(ypdata_t * yp,char * s,unsigned len)370 static int do_yp_add (ypdata_t *yp, char *s, unsigned len)
371 {
372     int ret;
373     char *value;
374 
375     value = stats_get_value (yp->mount, "server_type");
376     add_yp_info (yp, value, YP_SERVER_TYPE);
377     free (value);
378 
379     value = stats_get_value (yp->mount, "server_name");
380     add_yp_info (yp, value, YP_SERVER_NAME);
381     free (value);
382 
383     value = stats_get_value (yp->mount, "server_url");
384     add_yp_info (yp, value, YP_SERVER_URL);
385     free (value);
386 
387     value = stats_get_value (yp->mount, "genre");
388     add_yp_info (yp, value, YP_SERVER_GENRE);
389     free (value);
390 
391     value = stats_get_value (yp->mount, "bitrate");
392     if (value == NULL)
393         value = stats_get_value (yp->mount, "ice-bitrate");
394     add_yp_info (yp, value, YP_BITRATE);
395     free (value);
396 
397     value = stats_get_value (yp->mount, "server_description");
398     add_yp_info (yp, value, YP_SERVER_DESC);
399     free (value);
400 
401     value = stats_get_value (yp->mount, "subtype");
402     add_yp_info (yp, value, YP_SUBTYPE);
403     free (value);
404 
405     value = stats_get_value (yp->mount, "audio_info");
406     add_yp_info (yp, value, YP_AUDIO_INFO);
407     free (value);
408 
409     ret = snprintf (s, len, "action=add&sn=%s&genre=%s&cpswd=%s&desc="
410                     "%s&url=%s&listenurl=%s&type=%s&stype=%s&b=%s&%s\r\n",
411                     yp->server_name, yp->server_genre, yp->cluster_password,
412                     yp->server_desc, yp->url, yp->listen_url,
413                     yp->server_type, yp->subtype, yp->bitrate, yp->audio_info);
414     if (ret >= (signed)len)
415         return ret+1;
416     ret = send_to_yp ("add", yp, s);
417     if (ret == 0)
418     {
419         yp->process = do_yp_touch;
420         /* force first touch in 5 secs */
421         yp->next_update = time(NULL) + 5;
422     }
423     return ret;
424 }
425 
426 
do_yp_touch(ypdata_t * yp,char * s,unsigned len)427 static int do_yp_touch (ypdata_t *yp, char *s, unsigned len)
428 {
429     unsigned listeners = 0, max_listeners = 1;
430     char *val, *artist, *title;
431     int ret;
432 
433     artist = (char *)stats_get_value (yp->mount, "artist");
434     title = (char *)stats_get_value (yp->mount, "title");
435     if (artist || title)
436     {
437          char *song;
438          char *separator = " - ";
439          if (artist == NULL)
440          {
441              artist = strdup("");
442              separator = "";
443          }
444          if (title == NULL) title = strdup("");
445          song = malloc (strlen (artist) + strlen (title) + strlen (separator) +1);
446          if (song)
447          {
448              sprintf (song, "%s%s%s", artist, separator, title);
449              add_yp_info(yp, song, YP_CURRENT_SONG);
450              stats_event (yp->mount, "yp_currently_playing", song);
451              free (song);
452          }
453     }
454     free (artist);
455     free (title);
456 
457     val = (char *)stats_get_value (yp->mount, "listeners");
458     if (val)
459     {
460         listeners = atoi (val);
461         free (val);
462     }
463     val = stats_get_value (yp->mount, "max_listeners");
464     if (val == NULL || strcmp (val, "unlimited") == 0 || atoi(val) < 0)
465         max_listeners = client_limit;
466     else
467         max_listeners = atoi (val);
468     free (val);
469 
470     val = stats_get_value (yp->mount, "subtype");
471     if (val)
472     {
473         add_yp_info (yp, val, YP_SUBTYPE);
474         free (val);
475     }
476 
477     ret = snprintf (s, len, "action=touch&sid=%s&st=%s"
478             "&listeners=%u&max_listeners=%u&stype=%s\r\n",
479             yp->sid, yp->current_song, listeners, max_listeners, yp->subtype);
480 
481     if (ret >= (signed)len)
482         return ret+1; /* space required for above text and nul*/
483 
484     if (send_to_yp ("touch", yp, s) == 0)
485     {
486         yp->next_update = now + yp->touch_interval;
487         return 0;
488     }
489     return -1;
490 }
491 
492 
493 
process_ypdata(struct yp_server * server,ypdata_t * yp)494 static int process_ypdata (struct yp_server *server, ypdata_t *yp)
495 {
496     unsigned len = 1024;
497     char *s = NULL, *tmp;
498 
499     if (now < yp->next_update)
500         return 0;
501 
502     /* loop just in case the memory area isn't big enough */
503     while (1)
504     {
505         int ret;
506         if ((tmp = realloc (s, len)) == NULL)
507             return 0;
508         s = tmp;
509 
510         if (yp->release)
511         {
512             yp->process = do_yp_remove;
513             yp->next_update = 0;
514         }
515 
516         ret = yp->process (yp, s, len);
517         if (ret <= 0)
518         {
519            free (s);
520            return ret;
521         }
522         len = ret;
523     }
524     return 0;
525 }
526 
527 
yp_process_server(struct yp_server * server)528 static void yp_process_server (struct yp_server *server)
529 {
530     ypdata_t *yp;
531     int state = 0;
532 
533     /* ICECAST_LOG_DEBUG("processing yp server %s", server->url); */
534     yp = server->mounts;
535     while (yp)
536     {
537         now = time (NULL);
538         /* if one of the streams shows that the server cannot be contacted then mark the
539          * other entries for an update later. Assume YP server is dead and skip it for now
540          */
541         if (state == -2)
542         {
543             ICECAST_LOG_DEBUG("skipping %s on %s", yp->mount, server->url);
544             yp->process = do_yp_add;
545             yp->next_update += 900;
546         }
547         else
548             state = process_ypdata (server, yp);
549         yp = yp->next;
550     }
551 }
552 
553 
554 
create_yp_entry(const char * mount)555 static ypdata_t *create_yp_entry (const char *mount)
556 {
557     ypdata_t *yp;
558     char *s;
559 
560     yp = calloc (1, sizeof (ypdata_t));
561     do
562     {
563         unsigned len = 512;
564         int ret;
565         char *url;
566         mount_proxy *mountproxy = NULL;
567         ice_config_t *config;
568 
569         if (yp == NULL)
570             break;
571         yp->mount = strdup (mount);
572         yp->server_name = strdup ("");
573         yp->server_desc = strdup ("");
574         yp->server_genre = strdup ("");
575         yp->bitrate = strdup ("");
576         yp->server_type = strdup ("");
577         yp->cluster_password = strdup ("");
578         yp->url = strdup ("");
579         yp->current_song = strdup ("");
580         yp->audio_info = strdup ("");
581         yp->subtype = strdup ("");
582         yp->process = do_yp_add;
583 
584         url = malloc (len);
585         if (url == NULL)
586             break;
587         config = config_get_config();
588         ret = snprintf (url, len, "http://%s:%d%s", config->hostname, config->port, mount);
589         if (ret >= (signed)len)
590         {
591             s = realloc (url, ++ret);
592             if (s) url = s;
593             snprintf (url, ret, "http://%s:%d%s", config->hostname, config->port, mount);
594         }
595 
596         mountproxy = config_find_mount (config, mount, MOUNT_TYPE_NORMAL);
597         if (mountproxy && mountproxy->cluster_password)
598             add_yp_info (yp, mountproxy->cluster_password, YP_CLUSTER_PASSWORD);
599         config_release_config();
600 
601         yp->listen_url = util_url_escape (url);
602         free (url);
603         if (yp->listen_url == NULL)
604             break;
605 
606         return yp;
607     } while (0);
608 
609     yp_destroy_ypdata (yp);
610     return NULL;
611 }
612 
613 
614 /* Check for changes in the YP servers configured */
check_servers(void)615 static void check_servers (void)
616 {
617     struct yp_server *server = (struct yp_server *)active_yps,
618                      **server_p = (struct yp_server **)&active_yps;
619 
620     while (server)
621     {
622         if (server->remove)
623         {
624             struct yp_server *to_go = server;
625             ICECAST_LOG_DEBUG("YP server \"%s\"removed", server->url);
626             *server_p = server->next;
627             server = server->next;
628             destroy_yp_server (to_go);
629             continue;
630         }
631         server_p = &server->next;
632         server = server->next;
633     }
634     /* add new server entries */
635     while (pending_yps)
636     {
637         avl_node *node;
638 
639         server = (struct yp_server *)pending_yps;
640         pending_yps = server->next;
641 
642         ICECAST_LOG_DEBUG("Add pending yps %s", server->url);
643         server->next = (struct yp_server *)active_yps;
644         active_yps = server;
645 
646         /* new YP server configured, need to populate with existing sources */
647         avl_tree_rlock (global.source_tree);
648         node = avl_get_first (global.source_tree);
649         while (node)
650         {
651             ypdata_t *yp;
652 
653             source_t *source = node->key;
654             if (source->yp_public && (yp = create_yp_entry (source->mount)) != NULL)
655             {
656                 ICECAST_LOG_DEBUG("Adding existing mount %s", source->mount);
657                 yp->server = server;
658                 yp->touch_interval = server->touch_interval;
659                 yp->next = server->mounts;
660                 server->mounts = yp;
661             }
662             node = avl_get_next (node);
663         }
664         avl_tree_unlock (global.source_tree);
665     }
666 }
667 
668 
add_pending_yp(struct yp_server * server)669 static void add_pending_yp (struct yp_server *server)
670 {
671     ypdata_t *current, *yp;
672     unsigned count = 0;
673 
674     if (server->pending_mounts == NULL)
675         return;
676     current = server->mounts;
677     server->mounts = server->pending_mounts;
678     server->pending_mounts = NULL;
679     yp = server->mounts;
680     while (1)
681     {
682         count++;
683         if (yp->next == NULL)
684             break;
685         yp = yp->next;
686     }
687     yp->next = current;
688     ICECAST_LOG_DEBUG("%u YP entries added to %s", count, server->url);
689 }
690 
691 
delete_marked_yp(struct yp_server * server)692 static void delete_marked_yp (struct yp_server *server)
693 {
694     ypdata_t *yp = server->mounts, **prev = &server->mounts;
695 
696     while (yp)
697     {
698         if (yp->remove)
699         {
700             ypdata_t *to_go = yp;
701             ICECAST_LOG_DEBUG("removed %s from YP server %s", yp->mount, server->url);
702             *prev = yp->next;
703             yp = yp->next;
704             yp_destroy_ypdata (to_go);
705             continue;
706         }
707         prev = &yp->next;
708         yp = yp->next;
709     }
710 }
711 
712 
yp_update_thread(void * arg)713 static void *yp_update_thread(void *arg)
714 {
715     ICECAST_LOG_INFO("YP update thread started");
716 
717     yp_running = 1;
718     while (yp_running)
719     {
720         struct yp_server *server;
721 
722         thread_sleep (200000);
723 
724         /* do the YP communication */
725         thread_rwlock_rlock (&yp_lock);
726         server = (struct yp_server *)active_yps;
727         while (server)
728         {
729             /* ICECAST_LOG_DEBUG("trying %s", server->url); */
730             yp_process_server (server);
731             server = server->next;
732         }
733         thread_rwlock_unlock (&yp_lock);
734 
735         /* update the local YP structure */
736         if (yp_update)
737         {
738             thread_rwlock_wlock (&yp_lock);
739             check_servers ();
740             server = (struct yp_server *)active_yps;
741             while (server)
742             {
743                 /* ICECAST_LOG_DEBUG("Checking yps %s", server->url); */
744                 add_pending_yp (server);
745                 delete_marked_yp (server);
746                 server = server->next;
747             }
748             yp_update = 0;
749             thread_rwlock_unlock (&yp_lock);
750         }
751     }
752     thread_rwlock_destroy (&yp_lock);
753     thread_mutex_destroy (&yp_pending_lock);
754     /* free server and ypdata left */
755     while (active_yps)
756     {
757         struct yp_server *server = (struct yp_server *)active_yps;
758         active_yps = server->next;
759         destroy_yp_server (server);
760     }
761 
762     return NULL;
763 }
764 
765 
766 
yp_destroy_ypdata(ypdata_t * ypdata)767 static void yp_destroy_ypdata(ypdata_t *ypdata)
768 {
769     if (ypdata) {
770         if (ypdata->mount) {
771             free (ypdata->mount);
772         }
773         if (ypdata->url) {
774             free (ypdata->url);
775         }
776         if (ypdata->sid) {
777             free(ypdata->sid);
778         }
779         if (ypdata->server_name) {
780             free(ypdata->server_name);
781         }
782         if (ypdata->server_desc) {
783             free(ypdata->server_desc);
784         }
785         if (ypdata->server_genre) {
786             free(ypdata->server_genre);
787         }
788         if (ypdata->cluster_password) {
789             free(ypdata->cluster_password);
790         }
791         if (ypdata->listen_url) {
792             free(ypdata->listen_url);
793         }
794         if (ypdata->current_song) {
795             free(ypdata->current_song);
796         }
797         if (ypdata->bitrate) {
798             free(ypdata->bitrate);
799         }
800         if (ypdata->server_type) {
801             free(ypdata->server_type);
802         }
803         if (ypdata->audio_info) {
804             free(ypdata->audio_info);
805         }
806         free (ypdata->subtype);
807         free (ypdata->error_msg);
808         free (ypdata);
809     }
810 }
811 
add_yp_info(ypdata_t * yp,void * info,int type)812 static void add_yp_info (ypdata_t *yp, void *info, int type)
813 {
814     char *escaped;
815 
816     if (!info)
817         return;
818 
819     escaped = util_url_escape(info);
820     if (escaped == NULL)
821         return;
822 
823     switch (type)
824     {
825         case YP_SERVER_NAME:
826             free (yp->server_name);
827             yp->server_name = escaped;
828             break;
829         case YP_SERVER_DESC:
830             free (yp->server_desc);
831             yp->server_desc = escaped;
832             break;
833         case YP_SERVER_GENRE:
834             free (yp->server_genre);
835             yp->server_genre = escaped;
836             break;
837         case YP_SERVER_URL:
838             free (yp->url);
839             yp->url = escaped;
840             break;
841         case YP_BITRATE:
842             free (yp->bitrate);
843             yp->bitrate = escaped;
844             break;
845         case YP_AUDIO_INFO:
846             free (yp->audio_info);
847             yp->audio_info = escaped;
848             break;
849         case YP_SERVER_TYPE:
850             free (yp->server_type);
851             yp->server_type = escaped;
852             break;
853         case YP_CURRENT_SONG:
854             free (yp->current_song);
855             yp->current_song = escaped;
856             break;
857         case YP_CLUSTER_PASSWORD:
858             free (yp->cluster_password);
859             yp->cluster_password = escaped;
860             break;
861         case YP_SUBTYPE:
862             free (yp->subtype);
863             yp->subtype = escaped;
864             break;
865         default:
866             free (escaped);
867     }
868 }
869 
870 
871 /* Add YP entries to active servers */
yp_add(const char * mount)872 void yp_add (const char *mount)
873 {
874     struct yp_server *server;
875 
876     /* make sure YP thread is not modifying the lists */
877     thread_rwlock_rlock (&yp_lock);
878 
879     /* make sure we don't race against another yp_add */
880     thread_mutex_lock (&yp_pending_lock);
881     server = (struct yp_server *)active_yps;
882     while (server)
883     {
884         ypdata_t *yp;
885 
886         /* on-demand relays may already have a YP entry */
887         yp = find_yp_mount (server->mounts, mount);
888         if (yp == NULL)
889         {
890             /* add new ypdata to each servers pending yp */
891             yp = create_yp_entry (mount);
892             if (yp)
893             {
894                 ICECAST_LOG_DEBUG("Adding %s to %s", mount, server->url);
895                 yp->server = server;
896                 yp->touch_interval = server->touch_interval;
897                 yp->next = server->pending_mounts;
898                 yp->next_update = time(NULL) + 60;
899                 server->pending_mounts = yp;
900                 yp_update = 1;
901             }
902         }
903         else
904             ICECAST_LOG_DEBUG("YP entry %s already exists", mount);
905         server = server->next;
906     }
907     thread_mutex_unlock (&yp_pending_lock);
908     thread_rwlock_unlock (&yp_lock);
909 }
910 
911 
912 
913 /* Mark an existing entry in the YP list as to be marked for deletion */
yp_remove(const char * mount)914 void yp_remove (const char *mount)
915 {
916     struct yp_server *server = (struct yp_server *)active_yps;
917 
918     thread_rwlock_rlock (&yp_lock);
919     while (server)
920     {
921         ypdata_t *list = server->mounts;
922 
923         while (1)
924         {
925             ypdata_t *yp = find_yp_mount (list, mount);
926             if (yp == NULL)
927                 break;
928             if (yp->release || yp->remove)
929             {
930                 list = yp->next;
931                 continue;   /* search again these are old entries */
932             }
933             ICECAST_LOG_DEBUG("release %s on YP %s", mount, server->url);
934             yp->release = 1;
935             yp->next_update = 0;
936         }
937         server = server->next;
938     }
939     thread_rwlock_unlock (&yp_lock);
940 }
941 
942 
943 /* This is similar to yp_remove, but we force a touch
944  * attempt */
yp_touch(const char * mount)945 void yp_touch (const char *mount)
946 {
947     struct yp_server *server = (struct yp_server *)active_yps;
948     ypdata_t *search_list = NULL;
949 
950     thread_rwlock_rlock (&yp_lock);
951     if (server)
952         search_list = server->mounts;
953 
954     while (server)
955     {
956         ypdata_t *yp = find_yp_mount (search_list, mount);
957         if (yp)
958         {
959             /* we may of found old entries not purged yet, so skip them */
960             if (yp->release != 0 || yp->remove != 0)
961             {
962                 search_list = yp->next;
963                 continue;
964             }
965             /* don't update the directory if there is a touch scheduled soon */
966             if (yp->process == do_yp_touch && now + yp->touch_interval - yp->next_update > 60)
967                 yp->next_update = now + 3;
968         }
969         server = server->next;
970         if (server)
971             search_list = server->mounts;
972     }
973     thread_rwlock_unlock (&yp_lock);
974 }
975 
976 
yp_shutdown(void)977 void yp_shutdown (void)
978 {
979     yp_running = 0;
980     yp_update = 1;
981     if (yp_thread)
982         thread_join (yp_thread);
983     curl_global_cleanup();
984     free ((char*)server_version);
985     server_version = NULL;
986     ICECAST_LOG_INFO("YP thread down");
987 }
988 
989