Logo Search packages:      
Sourcecode: icecast-server version File versions  Download package

utility.c

/* utility.c
 * - General Utility Functions *
 * Copyright (c) 1999 Jack Moffitt, Barath Raghavan, and Alexander Havšng
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */

#ifdef HAVE_CONFIG_H
#ifdef _WIN32
#include <win32config.h>
#else
#include <config.h>
#endif
#endif

#include "definitions.h"
#include <stdio.h>
#include "definitions.h"

#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif

#ifdef HAVE_ASSERT_H
#include <assert.h>
#endif
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>

#ifndef _WIN32
# include <netdb.h>
# include <sys/socket.h>
# include <netinet/in.h>
# include <arpa/inet.h>
# include <sys/stat.h>
# include <time.h>
# include <errno.h>
#else
# include <winsock.h>
# include <io.h>
# define access _access
# define open _open
#endif

#include "avl.h"
#include "threads.h"
#include "icecast.h"
#include "icetypes.h"
#include "utility.h"
#include "ice_string.h"
#include "sock.h"
#include "ice_resolv.h"
#include "match.h"
#include "source.h"
#include "client.h"
#include "admin.h"
#include "commands.h"
#include "avl_functions.h"
#include "log.h"
#include "directory.h"
#include "logtime.h"
#include "main.h"
#include "alias.h"
#include "timer.h"
#include "memory.h"
#include "string.h"
#include "vars.h"
#include "connection.h"
#include "relay.h"

extern server_info_t info;
extern int running;

int password_match(const char *crypted, const char *uncrypted)
{
#ifndef USE_CRYPT
      if (!crypted || !uncrypted)   {
            write_log(LOG_DEFAULT, "ERROR: password_match called with NULL arguments");
            return 0;
      }

      if (ice_strcmp(crypted, uncrypted) == 0)
            return 1;

      return 0;
#else
      char salt[3];
      char *test_crypted;
      /* extern char *crypt(const char *, const char *); */

      if (!crypted || !uncrypted)   {
            write_log(LOG_DEFAULT, "ERROR: password_match called with NULL arguments");
            return 0;
      }

      if (ice_strncmp(crypted, "$1$", 3)) {
            salt[0] = crypted[0];
            salt[1] = crypted[1];
      } else {
            salt[0] = crypted[3];
            salt[1] = crypted[4];
      }

      salt[2] = '\0';

      thread_mutex_lock(&info.misc_mutex);
      test_crypted = crypt(uncrypted, salt);
      if (test_crypted == NULL) {
            write_log(LOG_DEFAULT, "WARNING - crypt() failed, refusing access");
            thread_mutex_unlock(&info.misc_mutex);
            return 0;
      }
      if (ice_strcmp(test_crypted, crypted) == 0)     {
            thread_mutex_unlock(&info.misc_mutex);
            return 1;
      }
      
      thread_mutex_unlock(&info.misc_mutex);

      return 0;
#endif
}

void
print_admin (void *data, void *param)
{
      connection_t *con = (connection_t *)data;
      admin_t *admin;
      sock_t *sock = NULL;
      char buf[BUFSIZE], timebuf[BUFSIZE];

      if (!data)
      {
            xa_debug (1, "DEBUG: print_admin() called with NULL pointer");
            return;
      }
      
      admin = con->food.admin;
      if (param)
            sock = (sock_t *)param;

      snprintf (buf, BUFSIZE, "Admin %ld\t[%s] connected for %s. %d commands issued.\tFlags:", con->id, 
             con_host (con), nice_time (get_time () - con->connect_time, timebuf), admin->commands);
      
      if (!param) {
            fd_write (info.statsfile, "%s", buf);
            flags2string (admin, NULL);
            fd_write (info.statsfile, "\n");
      } else {
            sock_write (*sock, "%s", buf);
            flags2string (admin, param);
            if (thread_equal (thread_self (), con->food.admin->thread))
                  sock_write (*sock, " - It's you! -");
            sock_write_line (*sock, "");
      }
}

void
print_connection (void *data, void *param)
{
      char buf[BUFSIZE], timebuf[BUFSIZE];
      connection_t *con = (connection_t *)data;
      sock_t *sock = NULL;
      char *type;

      if (param)
            sock = (sock_t *)param;

      if (con->type == source_e)
            type = "source";
      else if (con->type == client_e)
            type = "client";
      else if (con->type == admin_e)
            type = "admin";
      else
            type = "unknown";

      snprintf (buf, BUFSIZE, "%ld\t [%s] connected for %s. Type: [%s]\r\n", con->id, con_host (con), 
             nice_time (get_time () - con->connect_time, timebuf), type);

      if (!param)
            fd_write (info.statsfile, "%s", buf);
      else
            sock_write (*sock, "%s", buf);
}

/* Print one single source, without audiocast data 
   when param is NULL, output to statsfile */
void 
print_source(void *data, void *param)
{
      connection_t *con = (connection_t *)data;
      source_t *source;
      sock_t *sock = NULL;
      char buf[BUFSIZE], timebuf[BUFSIZE];

      source = con->food.source;
      if (param)
            sock = (sock_t *)param;
      
      snprintf (buf, BUFSIZE, "Source %ld\t[%s] connected for %s, %lu megs transfered, type: %s, streaming to %lu clients.\r\n",
             con->id, con_host (con), nice_time (get_time() - con->connect_time, timebuf), 
             source->stats.read_megs, source->type == encoder_e ? "encoder" : "relay", 
             source->num_clients);
      
      if (!param)
            fd_write (info.statsfile, "%s", buf);
      else
            sock_write (*sock, "%s", buf);
}

/* Print one single source, without audiocast data 
   when param is NULL, output to statsfile */
void 
print_source_verbose (void *data, void *param)
{
      connection_t *con = (connection_t *)data;
      source_t *source;
      sock_t *sock = NULL;
      char buf[BUFSIZE], buf2[2 * BUFSIZE], timebuf[BUFSIZE];
      relay_id_t *rip;
      avl_traverser trav = {0};
      
      source = con->food.source;
      if (param)
            sock = (sock_t *)param;
      
      snprintf (buf, BUFSIZE, "Source %lu\t[%s] connected for %s, %lu megs transfered, type: %s, streaming to %lu clients, priority: %d, ",
             con->id, con_host (con), nice_time (get_time() - con->connect_time, timebuf), source->stats.read_megs, 
             source->type == encoder_e ? "encoder" : "relay", (unsigned long int) avl_count (source->clients), source->priority);

      if (source->dumpfile)
      {
            snprintf (buf2, BUFSIZE, "%sdumping to [%s]\r\n", buf, source->dumpfile);
            strncpy (buf, buf2, BUFSIZE);
      } else {
            strcat (buf, "not dumping to any file\r\n");
      }

      if (!param)
            fd_write (info.statsfile, "%s", buf);
      else
            sock_write (*sock, "%s", buf);

      snprintf (buf, BUFSIZE, "\tStream Title: %s\r\n\tStream URL: %s\tx-audiocast-name: %s\r\n\tx-audiocast-genre: %s\r\n\tx-audiocast-bitrate: %d\r\n\tx-audiocast-url: %s\r\n\tx-audiocast-mount: %s\r\n\tx-audiocast-description: %s\r\n\tx-audiocast-public: %d\r\n",
             source->info.streamtitle ? source->info.streamtitle : "n/a", source->info.streamurl ? source->info.streamurl : "n/a",
             source->audiocast.name, source->audiocast.genre, source->audiocast.bitrate, source->audiocast.url, 
             source->audiocast.mount, source->audiocast.description, source->audiocast.public);
      
      if (!param)
            fd_write (info.statsfile, "%s", buf);
      else
            sock_write (*sock, "%s", buf);

      if (!param)
            fd_write (info.statsfile, "\tdirectory servers: ");
      else
            sock_write (*sock, "\tdirectory servers: ");

      while ((rip = avl_traverse (source->relay_tree, &trav)))
      {
            if (!param)
                  fd_write (info.statsfile, "%s:%d ", rip->host, rip->id);
            else
                  sock_write (*sock, "%s:%d ", rip->host, rip->id);
      }

      if (!param)
            fd_write (info.statsfile, "\n");
      else
            sock_write (*sock, "\r\n");
            
}

void 
print_clients(void *data, void *param)
{
      connection_t *con = (connection_t *)data;
      
      if (param)
      {
            sock_write_line (*((sock_t *)param), "--- Source ---");
            print_source (con, param);
            sock_write_line (*((sock_t *)param), "--- Listeners ---");
      } else {
            fd_write (info.statsfile, "--- Source ---\n");
            print_source (con, param);
            fd_write (info.statsfile, "--- Listeners ---\n");
      }

      thread_mutex_lock (&con->food.source->mutex);
      avl_walk (con->food.source->clients, print_client, param);
      thread_mutex_unlock (&con->food.source->mutex);
}

void 
print_client(void *data, void *param)
{
      connection_t *con = (connection_t *)data;
      client_t *client;
      sock_t *sock = NULL;
      char buf[BUFSIZE], timebuf[BUFSIZE];

      if (param)
            sock = (sock_t *) param;
      
      client = con->food.client;
      
      snprintf (buf, BUFSIZE, "Client %ld\t[%s] connected for %s, %lu bytes transfered. %d errors. User agent: [%s]. Type: %s\r\n",
             con->id, con_host (con), nice_time (get_time () - con->connect_time, timebuf), client->write_bytes, client_errors (client), 
             get_user_agent (con), client->type == listener_e ? "listener" : "relay");
      
      if (!param)
            fd_write(info.statsfile, "%s", buf);
      else
            sock_write (*sock, "%s", buf);
}

void 
print_directory (void *data, void *param)
{
      directory_server_t *dir = (directory_server_t *)data;
      sock_t *sock;

      if (param) {
            sock = (sock_t *)param;
            if (dir->type == icy_e)
                  sock_write_line (*sock, "Server: [%s] id: [%d] touches: [%d]", dir->host, dir->id, dir->counter);
            else
                  sock_write_line (*sock, "Server: [%s:%d%s] id: [%d] touches: [%d]", dir->host, dir->port, dir->path, dir->id, dir->counter);
      }
}


int 
field_ok (char *xac, char *field)
{
      if (!field || !field[0])
        {
          write_log (LOG_DEFAULT, "Header is missing %s field", xac);
          return 0;
        }

      return 1;
}

int find_frame_ofs(source_t *source)
{
      char *buff;
      int pos = 0, cid;
      
      if (!source)
      {
            write_log (LOG_DEFAULT, "ERROR: find_frame_ofs() called with NULL argument");
            return 0;
      }

      if (info.use_meta_data)
        return 0;

      cid = source->cid <= 0 ? CHUNKLEN - 1 : source->cid - 1;
      
      buff = source->chunk[cid].data;
      
      while (pos < source->chunk[cid].len - 1) {
            if ((buff[pos] & 0xFF) == 0xFF && (buff[pos + 1] & 0xF0) == 0xF0)
                  break;
            pos++;
      }
      
      return pos;
}


void
kick_connection_not_me (void *conarg, void *reasonarg)
{
      connection_t *kickcon = (connection_t *)conarg;
      
      if (kickcon->type == admin_e)
      {
            if (thread_equal (kickcon->food.admin->thread, thread_self ()))
                  return;
      } else if (kickcon->type == source_e)
      {
            if (thread_equal (kickcon->food.source->thread, thread_self ()))
                  return;
      }
      kick_connection (conarg, reasonarg);
}
            
void
kick_connection(void *conarg, void *reasonarg)
{
      connection_t *con = (connection_t *)conarg;
      char *reason = (char *)reasonarg;

      char timebuf[BUFSIZE] = "";

      if (!conarg || !reasonarg)
      {
            write_log (LOG_DEFAULT, "WARNING: kick_connection called with NULL pointers");
            return;
      }
      
      switch (con->type)
      {
            case client_e:
                  write_log (LOG_DEFAULT, 
                           "Kicking client %d [%s] [%s] [%s], connected for %s, %lu bytes transfered. %d clients connected",
                           con->id, con_host (con), reason, con->food.client->type == listener_e ? "listener" : "relay",
                           nice_time (get_time () - con->connect_time, timebuf), con->food.client->write_bytes, info.num_clients - 1);
                  con->food.client->alive = CLIENT_DEAD;

                  if (con->food.client->type == pusher_e) {
                        relay_t *rel = relay_find_with_con (con);
                        if (rel) {
                              rel->con = NULL;
                              rel->reconnect_now = 1;
                        }
                        else
                              write_log (LOG_DEFAULT, "WARNING: Pushing relay was not found in the list of relays");
                  }
                  
                  write_clf (con, con->food.client->source);
                  return;
                  break;
            case admin_e:
                  write_log (LOG_DEFAULT, "Kicking admin %d [%s] [%s], %d commands issued, connected for %s. %d admins connected",
                           con->id, con_host (con), reason, con->food.admin->commands, 
                           nice_time (get_time () - con->connect_time, timebuf), info.num_admins - 1);
                  sock_close(con->sock);
                  con->food.admin->alive = 0;
                  return;
                  break;
            case source_e:
                  write_log (LOG_DEFAULT, 
                           "Kicking source %d [%s] [%s] [%s], connected for %s, %lu bytes transfered. %d sources connected",
                           con->id, con_host (con), reason, con->food.source->type == encoder_e ? "encoder" : "relay",
                           nice_time (get_time () - con->connect_time, timebuf), con->food.source->stats.read_bytes, info.num_sources - 1);
                  if (con->food.source->connected == SOURCE_UNUSED)
                        close_connection (con, NULL);
                  else
                  {
                        /* Let the source kill itself */
                        sock_close(con->sock);
                        con->food.source->connected = SOURCE_KILLED;
                  }
                  return;
                  break;
            default:
                  write_log(LOG_DEFAULT, "Kicking unknown %d [%s] [%s], connected for %s", con->id, con_host (con), reason, 
                          nice_time (get_time () - con->connect_time, timebuf));
                  break;
      }
      close_connection(con, &info);
      return;
}

directory_server_t *create_directory()
{
      directory_server_t *dir = nmalloc(sizeof(directory_server_t));
      dir->counter = 0;
      dir->touches = 0;
      dir->id = -1;
      dir->host = NULL;
      dir->port = -1;
      dir->path = NULL;
      return dir;
}

void close_directory(void *data, void *param)
{
      directory_server_t *dir = (directory_server_t *)data;

      if (dir->id >= 0 && dir->type == icy_e)
            directory_remove (dir);
      avl_delete(info.d_servers, dir);
      nfree(dir->host);
      nfree(dir->path);
      nfree(dir);
}

connection_t *
get_admin_with_id(int id)
{
      connection_t con;
      connection_t *res;

      con.type = admin_e;
      con.id = id;
      res = avl_find(info.admins, &con);
      return res;
}

void
free_con (connection_t *con)
{
      if (con->sock >= 0)
      {
            if (!(con->host && ice_strcmp (con->host, "icecast console") == 0)) {   
                  sock_close (con->sock);
                  con->sock = -1;
            }
      }

      if (con->host != NULL)
      {
            nfree(con->host);
            con->host = NULL;
      }

      if (con->headervars)
            free_con_variables (con);
            
      if (con->sin)
            nfree (con->sin);
      
      if (con->hostname != NULL)
      {
            nfree(con->hostname);
            con->hostname = NULL;
      }
}

/* Must have the mutex when calling this:
   admin: must have info.admin_mutex
   client: must have the current source mutex
   source: must have its own mutex, and info.source_mutex */
void 
close_connection(void *data, void *param)
{
      connection_t *con = (connection_t *)data;
      
      if (!con) {
            write_log(LOG_DEFAULT, "ERROR: close_connection called with null pointer!");
            return;
      }
      
      xa_debug (2, "DEBUG: Removing connection %d of type %d", con->id, con->type);

      free_con (con); /* Free:s stuff that all connections have */
      
      if (con->type == admin_e) {
            xa_debug (2, "Removing admin %d (%p) from admintree of (%p)", con->id, con, info.admins);
            del_admin ();
            avl_delete (info.admins, con);
            nfree (con->food.admin);
            nfree (con);
            return;
      }
      
      else if (con->type == client_e) {
            source_t *con2 = source_with_client(con);

            if (!con2)
                  xa_debug (2, "DEBUG: Client with NULL source?");

            if (con->food.client->virgin == 0)
                  del_client (con, con2);
            else
                  util_decrease_total_clients ();

            if (con2)
              {
                    con2->stats.client_connect_time += (unsigned long)((get_time () - con->connect_time) / 60.0);
                    info.hourly_stats.client_connect_time += (unsigned long)((get_time() - con->connect_time) / 60.0);

                    xa_debug (2, "DEBUG: Removing client %d (%p) from sourcetree of (%p)", con->id, con, con2);

                    if (!avl_delete(con2->clients, con))
                          xa_debug (2, "DEBUG: Didn't find client in sourcetree, shit!");
              } else {
                    xa_debug (2, "DEBUG: client %d without source?", con->id);
              }
            nfree (con->food.client);
            nfree (con);
            return;
      }

      else if (con->type == source_e) {
            source_t *source = con->food.source;
            connection_t *clicon;

            if (!source)
            {
                  write_log (LOG_DEFAULT, "WARNING!!! - Erroneous source without food");
                  return;
            }

            xa_debug (2, "Removing source %d (%p) from sourcetree of (%p)", con->id, con, info.sources);
            
            if (source && source->clients)
            {
                  avl_traverser trav = {0};
                  relay_id_t *rip;
                  connection_t *smaller = get_twin_mount (source);

                  write_log(LOG_DEFAULT, "Kicking all %d clients for source %d", 
                          source->num_clients, con->id);
                  while ((clicon = avl_traverse (source->clients, &trav)))
                  {
                        if (smaller)
                              move_to_smaller_twin (source, clicon);
                        else
                              kick_connection (clicon, "Stream ended");
                  }
                  kick_dead_clients (source);

                  if (con->food.source->type == puller_e) {
                        relay_t *rel;

                        if (source->connected != SOURCE_KILLED && source->connected != SOURCE_UNUSED && 
                            source->connected != SOURCE_PENDING) {
                              thread_mutex_lock (&info.thread_mutex);
                              rel = relay_find_with_con (con);
                              thread_mutex_unlock (&info.thread_mutex);
                        } else 
                              rel = relay_find_with_con (con);
                        if (rel)
                              rel->con = NULL;
                  }

                  if (source->relay_tree)
                  {
                        while ((rip = avl_get_any_node (source->relay_tree)))
                        {
                              if (!avl_delete (source->relay_tree, rip))
                                    write_log (LOG_DEFAULT, "ERROR: relay id not in relay tree");
                              nfree (rip->host);
                              nfree (rip);
                        }
                        avl_destroy (source->relay_tree, NULL);
                  }

                  avl_destroy (source->clients, NULL);
            }
            
            dispose_audiocast (&source->audiocast);

            /* this was NULL once while debugging
                  Doesn't matter, it's ok to nfree() a null pointer */
            nfree (source->info.streamtitle);
            nfree (source->info.streamurl);
            nfree (source->info.streammsg);

            if (source->dumpfile)
            {
                  nfree (source->dumpfile);
                  source->dumpfile = NULL;
                  fd_close(source->dumpfd);
                  source->dumpfd = -1;
            }

            info.hourly_stats.source_connect_time += ((get_time () - con->connect_time) / 60);


            if (con->food.source->connected != SOURCE_UNUSED)
            {
                  del_source();
                  avl_delete (info.sources, con);
            }
            
            if (source->mutex.thread_id >= 0)
                  thread_mutex_unlock (&source->mutex);
            thread_mutex_destroy (&source->mutex);
            
            nfree(source);
            nfree(con);
            return;
      }

      nfree(con); /* Unknown connection, still remove con */
      return;
}

void
kick_not_connected (connection_t *con, char *reason)
{
      char timebuf[BUFSIZE];
      char typebuf[10];

      if (reason)
            write_log (LOG_DEFAULT, "Kicking %s %d [%s] [%s], connected for %s", type_of_str (con->type, typebuf), con->id, con_host (con), reason, 
                     nice_time (get_time () - con->connect_time, timebuf));
      
      free_con (con);

      if (con->type == source_e)
        {
              avl_destroy (con->food.source->relay_tree, NULL);
              avl_destroy (con->food.source->clients, NULL);
              nfree (con->food.source);
        }
      else if (con->type == client_e)
        {
          nfree (con->food.client);
        }
      else if (con->type == admin_e)
        {
          nfree (con->food.admin);
        }

      nfree (con);
}

void
kick_silently (connection_t *con)
{
      free_con (con);
      
      nfree (con);
}
      

source_t *
source_with_client(connection_t *con)
{
      if (con)
            return (con->food.client->source);
      return NULL;
}  

#ifndef _WIN32
int server_detach()
{
      pid_t icepid;

      write_log(LOG_DEFAULT, "Trying to fork");

      icepid = fork();
      if (icepid == -1) {
            write_log(LOG_DEFAULT, "ERROR: Can't fork dammit!");
            return 1;
      }
  
      if (icepid != 0) {
#if HAVE_SETPGID
            write_log(LOG_DEFAULT, "Detached (pid: %d)", icepid);
            setpgid(icepid, icepid);
#endif
            exit(0);
      } else {
#if HAVE_SETPGID
            setpgid(0, 0);
#endif
            if (NULL == freopen("/dev/null", "r", stdin)) {
                  perror("freopen (stdin)");
                  exit(1);
            }
            if (NULL == freopen("/dev/null", "w", stdout)) {
                  perror("freopen (stdout)");
                  exit(1);
            }
            if (NULL == freopen("/dev/null", "w", stderr)) {
                  perror("freopen (stderr)");
                  exit(1);
            }
            fd_close(0);
            fd_close(1);
            fd_close(2);
      }
      return 1;
}
#endif

connection_t *
find_con_with_host (const struct sockaddr_in *sin)
{
      connection_t *clicon, *sourcecon;
      avl_traverser trav = {0}, clitrav = {0};
      char hbuf[BUFSIZE];
      char *hostptr;

      if (!sin)
            return NULL;

      hostptr = makeasciihost(&sin->sin_addr, hbuf);

      /* Lock the misc mutex whenever you're about to lock twice */
      thread_mutex_lock (&info.double_mutex);
      thread_mutex_lock (&info.source_mutex);
      
      while ((sourcecon = avl_traverse (info.sources, &trav)))
      {
            zero_trav (&clitrav);

            thread_mutex_lock (&sourcecon->food.source->mutex);
            
            while ((clicon = avl_traverse (sourcecon->food.source->clients, &clitrav)))
            {
                  if (clicon->host && clicon->sin && (ice_strcasecmp (clicon->host, hostptr) == 0) &&
                      clicon->sin->sin_port == sin->sin_port)
                  {
                        thread_mutex_unlock (&sourcecon->food.source->mutex);
                        thread_mutex_unlock (&info.double_mutex);
                        thread_mutex_unlock (&info.source_mutex);
                        return clicon;
                  }
            }
            
            thread_mutex_unlock (&sourcecon->food.source->mutex);
      }
      
      thread_mutex_unlock (&info.double_mutex);
      thread_mutex_unlock (&info.source_mutex);
      
      return NULL;
}

connection_t *
find_con_with_host_and_udpport (const char *hostptr, const int portnr)
{
      connection_t *clicon, *sourcecon;
      avl_traverser trav = {0}, clitrav = {0};

      /* Lock the misc mutex whenever you're about to lock twice */
      thread_mutex_lock (&info.double_mutex);
      thread_mutex_lock (&info.source_mutex);

      while ((sourcecon = avl_traverse (info.sources, &trav)))
      {
            zero_trav (&clitrav);

            thread_mutex_lock (&sourcecon->food.source->mutex);
            
            while ((clicon = avl_traverse (sourcecon->food.source->clients, &clitrav)))
            {
                  if (((clicon->host && ice_strcasecmp (clicon->host, hostptr) == 0)
                       || (clicon->hostname && ice_strcasecmp (clicon->hostname, hostptr) == 0))
                      && (clicon->sin->sin_port == htons (portnr)))
                  {
                        thread_mutex_unlock (&sourcecon->food.source->mutex);
                        thread_mutex_unlock (&info.double_mutex);
                        thread_mutex_unlock (&info.source_mutex);
                        return clicon;
                  }
            }
            
            thread_mutex_unlock (&sourcecon->food.source->mutex);
      }

      thread_mutex_unlock (&info.double_mutex);
      thread_mutex_unlock (&info.source_mutex);
      
      return NULL;

}

connection_t *
find_client_with_id (int id)
{
      connection_t con, *res = NULL, *sourcecon;
      avl_traverser trav = {0};
      con.id = id;
      con.type = client_e;

      /* Lock the misc mutex whenever you're about to lock twice */
      thread_mutex_lock (&info.double_mutex);

      thread_mutex_lock (&info.source_mutex);
      while ((sourcecon = avl_traverse (info.sources, &trav)))
      {
            thread_mutex_lock (&sourcecon->food.source->mutex);
            res = avl_find (sourcecon->food.source->clients, &con);
            thread_mutex_unlock (&sourcecon->food.source->mutex);
            if (res)
                  break;
      }

      thread_mutex_unlock (&info.double_mutex);
      thread_mutex_unlock (&info.source_mutex);
      return res;
}

connection_t *
find_source_with_id (int id)
{
      connection_t con, *res;
      con.id = id;
      con.type = source_e;

      thread_mutex_lock (&info.source_mutex);
      res = avl_find (info.sources, &con);
      thread_mutex_unlock (&info.source_mutex);
      return res;
}

connection_t *
find_source_with_mount (char *mount)
{
      avl_traverser trav = {0};
      connection_t *scon = NULL;

      thread_mutex_lock (&info.source_mutex);
      
      while ((scon = avl_traverse (info.sources, &trav))) {
            if (ice_strcmp (mount, scon->food.source->audiocast.mount) == 0)
                  break;
      }
      
      thread_mutex_unlock (&info.source_mutex);
      
      return scon;
}
      
/* Try to avoid this function if at all possible, 
   since it's locking stuff all over the place */
connection_t *
find_id (int id)
{
  avl_traverser trav = {0};
  avl_traverser trav2 = {0};
  connection_t *con, *con2;
  
  thread_mutex_lock (&info.admin_mutex);
  while ((con = avl_traverse (info.admins, &trav)))
    {
      if (con->id == (unsigned)id)
      {
        thread_mutex_unlock (&info.admin_mutex);
        return con;
      }
    }
  thread_mutex_unlock (&info.admin_mutex);

  zero_trav (&trav);

  /* Lock the misc mutex whenever you're about to lock twice */
  thread_mutex_lock (&info.double_mutex);

  thread_mutex_lock (&info.source_mutex);

  while ((con = avl_traverse (info.sources, &trav)))
    {
      if (con->id == (unsigned)id)
      {
            thread_mutex_unlock (&info.double_mutex);
            thread_mutex_unlock (&info.source_mutex);
            return con;
      }

      zero_trav (&trav2);
      thread_mutex_lock (&(con->food.source->mutex));

      while ((con2 = avl_traverse (con->food.source->clients, &trav2)))
      {
        if (con2->id == (unsigned)id)
          {
                thread_mutex_unlock (&(con->food.source->mutex));
                thread_mutex_unlock (&info.source_mutex);
                thread_mutex_unlock (&info.double_mutex);
                return con2;
          }
      }
      thread_mutex_unlock (&(con->food.source->mutex));
    }
  thread_mutex_unlock (&info.source_mutex);
  
  thread_mutex_unlock (&info.double_mutex);
  return NULL;
}
      
void
kick_everything ()
{
  connection_t *con, *con2;
  
  while ((con = avl_get_any_node (info.sources)))
  {
        while ((con2 = avl_get_any_node (con->food.source->clients)))
              kick_connection (con2, "Masskick by admin");
        kick_connection (con, "Masskick by admin");
  }

  while ((con = avl_get_any_node (info.admins)))
        kick_connection (con, "Masskick by admin");
  
  return;
}

void
kick_if_match (char *pattern)
{
      char buf[BUFSIZE];
      snprintf (buf, BUFSIZE, "Matching %s", pattern);
      do_if_match_all (pattern, kick_connection, buf, 1);
}

void
do_if_match_all (char *pattern, avl_node_func func, void *buf, int destructive)
{
  avl_traverser trav = {0};
  connection_t *con;
  
  thread_mutex_lock (&info.admin_mutex);
  do_if_match_tree (info.admins, pattern, func, buf, destructive);
  thread_mutex_unlock (&info.admin_mutex);

  /* Lock the misc mutex whenever you're about to lock twice */
  thread_mutex_lock (&info.double_mutex);

  thread_mutex_lock (&info.source_mutex);
  while ((con = avl_traverse (info.sources, &trav)))
  {
        thread_mutex_lock (&con->food.source->mutex);
        do_if_match_tree (con->food.source->clients, pattern, func, buf, 0);
        thread_mutex_unlock (&con->food.source->mutex);
  }

  /* Nothing can be destructive with sources */
  do_if_match_tree (info.sources, pattern, func, buf, 0);

  thread_mutex_unlock (&info.source_mutex);

  thread_mutex_unlock (&info.double_mutex);
}

void
do_if_match_tree_destructive (avl_tree *tree, char *pattern, avl_node_func func, void *buf)
{
      avl_traverser trav = {0};
      connection_t *con;
      
      xa_debug (2, "Destructively traversing tree (%p) with %s", tree, pattern);

      zero_trav (&trav);

      while (42)
      {
            con = avl_traverse (tree, &trav);
            if (!con)
                  break;
            if (wild_match ((unsigned char *)pattern, (unsigned char *)con->host) 
                || (con->hostname && (wild_match ((unsigned char *)pattern, (unsigned char *)con->hostname))))
            {
                  if (con->type == admin_e && thread_equal (con->food.admin->thread, thread_self ()))
                        xa_debug (2, "DEBUG: Skipping myself in destructive tree traversal");
                  else
                  {
                        xa_debug (3, "%s matched, starting over", con->host);
                        (func)(con, buf);
                        zero_trav (&trav); /* Start over */
                  }
            }
      }
}

void
do_if_match_tree (avl_tree *tree, char *pattern, avl_node_func func, void *buf, int destructive)
{
      avl_traverser trav = {0};
      connection_t *con;

      if (destructive)
            do_if_match_tree_destructive (tree, pattern, func, buf);
      else
      {
            while ((con = avl_traverse (tree, &trav)))
            {
                  if (wild_match ((unsigned char *)pattern, (unsigned char *)con->host) 
                      || (con->hostname && wild_match ((unsigned char *)pattern, (unsigned char *)con->hostname)))
                        (func)(con, buf);
            }
      }
}

  
unsigned long int
new_id ()
{
      unsigned long int ret;
      thread_mutex_lock (&info.misc_mutex);
      ret = info.id;
      info.id++;
      thread_mutex_unlock (&info.misc_mutex);
      return ret;
}

void
kill_threads ()
{
      avl_traverser trav = {0};
      connection_t *con;
      mythread_t *mt;

      write_log (LOG_DEFAULT, "Telling threads to die...");

      /* Go through all threads, kill them off */
      while ((mt = avl_traverse (info.threads, &trav))) {
            mt->running = THREAD_KILLED;
      }
      
      write_log (LOG_DEFAULT, "Closing sockets for admins that keep hanging around...");
      /* First all admins */
      thread_mutex_lock (&info.double_mutex);
      thread_mutex_lock (&info.admin_mutex);

      while ((con = avl_traverse (info.admins, &trav))) {
            if (!(con->host && ice_strcmp (con->host, "icecast console") == 0))
                  sock_close(con->sock);
      }

      thread_mutex_unlock (&info.admin_mutex);
      thread_mutex_unlock (&info.double_mutex);
      
      write_log (LOG_DEFAULT, "Closing sockets for sources that keep hanging around...");
      
      zero_trav (&trav);
      
      /* Then all sources */
      thread_mutex_lock (&info.source_mutex);
      while ((con = avl_traverse (info.sources, &trav))) {
            sock_close(con->sock);
            con->food.source->connected = SOURCE_KILLED;
      }
      
      thread_mutex_unlock (&info.source_mutex);
}

time_t
tree_time(avl_tree *tree)
{
      connection_t *con;
      avl_traverser trav = {0};
      time_t t, tc = 0;
      
      t = get_time();
      while ((con = avl_traverse(tree, &trav)))
            tc += (t - con->connect_time);
      return tc / 60;
}
            
void 
write_icecast_header ()
{
      printf("Icecast Version %s Initializing...\n", VERSION);
      printf("Icecast comes with NO WARRANTY, to the extent permitted by law.\nYou may redistribute copies of Icecast under the terms of the\nGNU General Public License.\nFor more information about these matters, see the file named COPYING.\n");
      printf("Starting thread engine...\n");
      write_log(LOG_DEFAULT, "Icecast Version %s Starting..", VERSION);
}

void
print_startup_server_info ()
{
      int i;

      if (info.myhostname && ice_strcmp (info.myhostname, "0.0.0.0"))
            write_log(LOG_DEFAULT, "Listening on host %s...", info.myhostname);

      for (i = 0; i < MAXLISTEN; i++) {
            if (info.port[i] > 0)
                  write_log(LOG_DEFAULT, "Listening on port %i...", info.port[i]);
      }

      if (info.server_name)
            write_log (LOG_DEFAULT, "Using '%s' as servername...", info.server_name);

      write_log(LOG_DEFAULT, "Server limits: %d clients, %d clients per source, %d sources, %d admins", 
              info.max_clients, info.max_clients_per_source, info.max_sources, info.max_admins);

      if (info.allow_http_admin)
            write_log (LOG_DEFAULT, "WWW Admin interface accessible at http://%s:%d/admin", info.server_name, info.port[0]);
      else
            write_log (LOG_DEFAULT,  "Access to WWW Admin interface disabled");
}

void
sanity_check ()
{
      int fd = -1;
      char *file = get_log_file ("stats.log");

      if (!file)
      {
            write_log (LOG_DEFAULT, "ERROR: Could not find a suitable directory for server logfiles");
            fprintf (stderr, "ERROR: Could not find a suitable directory for server logfiles\n");
            clean_shutdown (&info);
      }

      xa_debug (1, "DEBUG: Checking write access for file %s", file);
      if ((fd = open_for_writing (file)) == -1)
      {
            write_log (LOG_DEFAULT, "Damn, could not write log file [%s]. Exiting.", file);
            fprintf (stderr, "Damn, could not write log file [%s]. Exiting.\n", file);
            clean_shutdown (&info);
      } else {
            fd_close (fd);
            remove ("stats.log");
      }

      nfree (file);

      file = get_template ("mountlist.html");

      if (!file)
      {
            write_log (LOG_DEFAULT, "ERROR: Could not find a suitable directory for template files, something might be wrong!");
            return;
      }

      xa_debug (1, "DEBUG: Looking for templates");

      if ((fd = open_for_reading (file)) == -1)
        {
          write_log (LOG_DEFAULT, "WARNING: Could not find template file for mountlist.html, something might be wrong!");
          fd_close (fd);
        }
      
      nfree (file);
}

unsigned long int
transfer_average (unsigned long int megabytes, unsigned long int connections)
{
      if (!connections)
            return megabytes;
      return (unsigned long int)((double) megabytes / (double) connections);
}

char *
connect_average (unsigned long int minutes, unsigned long int connections, char *buf)
{
      if (!connections)
            return nice_time_minutes (minutes, buf);
      return nice_time_minutes ((unsigned long int)((double) minutes  / (double) connections), buf);
}


int
hostname_local (char *name)
{
      char *new;

      if (!name)
      {
            write_log (LOG_DEFAULT, "ERROR: hostname_local called with NULL name");
            return 0;
      }

      if (!name[0])
            return 1;

      if (ice_strcasecmp (name, "localhost") == 0 || ice_strcasecmp (name, "127.0.0.1") == 0)
            return 1;
      if (info.server_name && ice_strcasecmp (name, info.server_name) == 0)
            return 1;
      if (info.myhostname && ice_strcasecmp (name, info.myhostname) == 0)
            return 1;

      /* Search the tree */
      thread_mutex_lock (&info.hostname_mutex);
      
      new = avl_find (info.my_hostnames, name);

      thread_mutex_unlock (&info.hostname_mutex);

      if (new)
            return 1;

      /* Not in the tree, try to reverse it */
      {
            char buf[BUFSIZE], *out;
            char *res = forward (name, buf);

            if (!res)
                  return 0; /* Unresolvable */
                  
            thread_mutex_lock (&info.hostname_mutex);

            out = avl_find (info.my_hostnames, res);
            
            thread_mutex_unlock (&info.hostname_mutex);

            if (out || (info.myhostname && (ice_strcasecmp (res, info.myhostname) == 0)) || (ice_strcmp (res, "127.0.0.1") == 0))
            {
                  thread_mutex_lock (&info.hostname_mutex);
                  avl_insert (info.my_hostnames, nstrdup (name));
                  thread_mutex_unlock (&info.hostname_mutex);
                  return 1;
            }
      }
      return 0;
}

void
build_request (char *line, request_t *req)
{
      char path[BUFSIZE] = "";
      char *ptr, *lineptr;

      if (!line || !req)
      {
            write_log (LOG_DEFAULT, "ERROR: build_request called with NULL pointer");
            return;
      }

      xa_debug (2, "DEBUG: Building request out of [%s]", line);

      if (ice_strncmp (line, "GET", 3) == 0)
      {

            splitc (NULL, line, ' ');
            /* Now we've got either:
               "/ HTTP/1.0"
               " HTTP/1.0"
               "http://whatever.com/ HTTP/1.0"
               "http://whatever.com:8000/ HTTP/1.0"
               "http://whatever.com:8000 HTTP/1.0"
            */

            if ((splitc (path, line, ' ') == NULL) || !path[0] || path[0] == ' ')
            {
                  xa_debug (1, "Empty GET request [%s]", line);
                  strncpy (req->path, "/", BUFSIZE);
                  strncpy (req->host, info.server_name, BUFSIZE);
                  req->port = info.port[0];
                  return;
            }

            if (ice_strncmp (path, "http://", 7) == 0)
            {
                  lineptr = &path[7];

                  xa_debug (2, "DEBUG: Building http request from [%s]", lineptr);

                  ptr = strchr (lineptr, ':');
                  
                  if (ptr) /* port present */
                  {
                        splitc (req->host, lineptr, ':');
                        req->port = atoi (lineptr);

                        lineptr = strchr (lineptr, '/');
                        
                        if (!lineptr)
                              strncpy (req->path, "/", BUFSIZE);
                        else
                        {
                              if (splitc (req->path, lineptr, ' ') == NULL)
                                    strncpy (req->path, lineptr, BUFSIZE);
                        }
                        return;
                  } else {
                        req->port = info.port[0];

                        ptr = strchr (lineptr, '/');

                        if (!ptr)
                        {
                              if (splitc (req->host, lineptr, ' ') == NULL)
                                    strncpy (req->host, lineptr, BUFSIZE);
                              strncpy (req->path, "/", BUFSIZE);
                              return; 
                        } else {
                              if (splitc (req->host, lineptr, '/') == NULL)
                                    strncpy (req->host, lineptr, BUFSIZE);
                              else if (splitc (req->path, lineptr, ' ') == NULL)
                                    strncpy (req->path, lineptr, BUFSIZE);

                              if (req->path[0] != '/')
                                    strncpy (req->path, "/", BUFSIZE);
                              return;
                        }
                  }

            } else {
                  char pathbuf[BUFSIZE];

                  xa_debug (2, "DEBUG: Building clean request from [%s]", path);

                  req->port = info.port[0];
                  if (splitc (pathbuf, path, ' ') == NULL)
                        strncpy (pathbuf, path, BUFSIZE);

                  if (pathbuf[0] == '/')
                        strncpy (req->path, pathbuf, BUFSIZE);
                  else
                        snprintf (req->path, BUFSIZE, "/%s", pathbuf);

                  if (info.server_name)
                        strncpy (req->host, info.server_name, BUFSIZE);
                  else
                        strncpy (req->host, "localhost", BUFSIZE);
                  return;
            }
            
      } else if ((ice_strncmp (line, "HOST:", 5) == 0) || (ice_strncmp (line, "Host:", 5) == 0))
      {
            char hostbuf[BUFSIZE] = "";

            splitc (NULL, line, ':');

            while (*line == ' ')
                  line++;

            ptr = strchr (line, ':');

            if (ptr)
            {
                  splitc (hostbuf, line, ':');
                  req->port = atoi (line);
            } else {
                    if (splitc (hostbuf, line, ' ') == NULL)
                        strncpy (hostbuf, line, BUFSIZE);
            }
            
            strncpy (req->host, hostbuf, BUFSIZE);

            return;
      } else {
            xa_debug (1, "DEBUG: Build request called with invalid line [%s]", line);
      }
}
            
int
mount_exists (char *mount)
{
      avl_traverser trav = {0};
      connection_t *scon;

      thread_mutex_lock (&info.source_mutex);

      while ((scon = avl_traverse (info.sources, &trav)))
      {
            if ((ice_strcmp (mount, scon->food.source->audiocast.mount) == 0) && (scon->food.source->connected != SOURCE_PENDING))
            {
                  thread_mutex_unlock (&info.source_mutex);
                  return 1;
            }
      }

      thread_mutex_unlock (&info.source_mutex);

      return 0;
}

void
generate_request (char *line, request_t *req)
{
      char full[BUFSIZE];

      if (!line || !req)
      {
            write_log (LOG_DEFAULT, "ERROR: generate_request called with NULL pointer");
            return;
      }

      snprintf (full, BUFSIZE, "GET %s HTTP/1.0", line);

      build_request (full, req);

      if (req->path[0])
            xa_debug (2, "DEBUG: Generated request [%s:%d%s]", req->host, req->port, req->path);

}

void
generate_http_request (char *line, request_t *req)
{
      char full[BUFSIZE + 50] = "";
      
      if (!line || !req)
      {
            write_log (LOG_DEFAULT, "ERROR: generate_request called with NULL pointer");
            return;
      }

      if (ice_strncmp (line, "http://", 7) == 0)
      {
            snprintf (full, BUFSIZE, "GET %s HTTP/1.0", line);
      } else {
            snprintf (full, BUFSIZE, "GET http://%s HTTP/1.0", line);
      }

      build_request (full, req);

      xa_debug (2, "DEBUG: Generated http request [%s:%d%s]", req->host, req->port, req->path);
}

void
zero_request (request_t *req)
{
      req->host[0] = '\0';
      req->path[0] = '\0';
      req->port = -1;
}

char *
next_mount_point ()
{
      avl_traverser trav = {0};
      connection_t *sourcecon;
      static char apan[30];
      int count = 0;
      
      while ((sourcecon = avl_traverse (info.sources, &trav)))
      {
            if (ice_strncmp (sourcecon->food.source->audiocast.mount, "/icy_", 5) == 0 && (sourcecon->food.source->connected != SOURCE_PENDING))
                  count++;
      }
      
      snprintf (apan, sizeof(apan), "icy_%d", count);
      
      return &apan[0];
}

void init_thread_tree(int line, char *file)
{
      mythread_t *mt;
      
      if (!file) {
            fprintf (stderr, "WARNING: init_thread_tree() called with file == NULL\n");
            exit (1);
      }

      info.threads = NULL;
      info.threadid = 0;

#ifdef DEBUG_MEMORY
      info.mem = avl_create_nl(compare_mem, &info);
      thread_create_mutex (&info.memory_mutex);
#endif

      mt  = (mythread_t *)nmalloc(sizeof(mythread_t));

      /* Create a tree for all threads */
      info.threads = avl_create(compare_threads, &info);

      /* Some luxury just to make the main thread show up in com_threads() */
      mt->id = 0;
      mt->line = line;
      mt->file = strdup(file);
      mt->thread = thread_self();
      mt->created = get_time();
      mt->name = strdup("Main Thread");

      if (avl_insert(info.threads, mt)) {
            fprintf (stderr, "WARNING: Could not insert main thread into the thread tree, DAMN!\n");
            exit(1);
      }
      
      thread_create_mutex(&info.thread_mutex);

      /* On platforms where it is supported, this enables this thread to be
         cancelable */
      thread_init();

      thread_catch_signals ();

      thread_setup_default_attributes();
}

void
pending_connection (connection_t *con)
{
      write_log (LOG_DEFAULT, "Lost connection to source on mount %s, waiting %d seconds for timeout", con->food.source->audiocast.mount, info.client_timeout);
      con->food.source->connected = SOURCE_PENDING;

      /* Get the relay reconnector to reconnect this source */
      if (con->food.source->type == puller_e) {
            relay_t *rel;
            thread_mutex_lock (&info.relay_mutex);
            rel = relay_find_with_con (con);
            if (rel) {
                  rel->con = NULL;
                  rel->reconnect_now = 1;
            } /* It is an alias.. don't reconnect */
            thread_mutex_unlock (&info.relay_mutex);
      }
}

int
pending_source_signoff (connection_t *con)
{
      time_t start = get_time ();
      while ((running == SERVER_RUNNING) && con->food.source->connected == SOURCE_PENDING && ((get_time () - start) < info.client_timeout))
            my_sleep(90000);
      if (con->food.source->connected == SOURCE_PENDING)
            return 1;
      return 0;
}
            
connection_t *
get_pending_mount (char *mount)
{
      avl_traverser trav = {0};
      connection_t *sourcecon;

      thread_mutex_lock (&info.source_mutex);

      while ((sourcecon = avl_traverse (info.sources, &trav)))
      {
            if ((ice_strcmp (sourcecon->food.source->audiocast.mount, mount) == 0) && (sourcecon->food.source->connected == SOURCE_PENDING))
            {
                  thread_mutex_unlock (&info.source_mutex);
                  return sourcecon;
            }
      }

      thread_mutex_unlock (&info.source_mutex);
      return NULL;
}

void clear_source_stats (void *data, void *param)
{
        connection_t *con = (connection_t *)data;
        source_t *source;
        source = con->food.source;
      thread_mutex_lock(&source->mutex);         
      zero_stats (&source->stats);
      avl_walk (source->clients, clear_client_stats, param);
      thread_mutex_unlock(&source->mutex);
}

void clear_client_stats (void *data, void *param)
{
      connection_t *con = (connection_t *)data;
        client_t *client;
        client = con->food.client;
      client->write_bytes = 0;
}

void clear_admin_stats (void *data, void *param)
{
      connection_t *con = (connection_t *)data;
        admin_t *admin;
      admin = con->food.admin;
      admin->commands = 0;
}

int
hosteq (connection_t *con, connection_t *con2)
{
      if (con->host && con2->host)
      {
            if (ice_strcasecmp (con->host, con2->host) == 0)
                  return 1;
      }
      
      if (con->hostname && con2->hostname)
      {
            if (ice_strcasecmp (con->hostname, con2->hostname) == 0)
                  return 1;
      }

      return 0;
}

int
hostmatch (const connection_t *con, const char *pattern)
{
      if (!pattern || !con)
            return 0;
      if (con->host && wild_match ((unsigned char *)pattern, (unsigned char *)con->host))
            return 1;
      if (con->hostname && wild_match((unsigned char *)pattern, (unsigned char *)con->hostname))
            return 1;
      return 0;
}

int
open_for_writing (const char *filename)
{
  int fd;

  if (!filename)
        return -1;
  
  fd = open (filename, O_WRONLY|O_CREAT|O_TRUNC, 00644);
  if (fd == -1)
    xa_debug (1, "ERROR: Cannot open file for writing[%s]", filename);
  return fd;
}

int open_for_reading(const char *filename)
{
      int fd;

      if (!filename) {
            xa_debug(1, "ERROR: Cannot open file for reading no file specified");
            return -1;
      }

      fd = open(filename, O_RDONLY);
      if (fd == -1)
            xa_debug(1, "ERROR: Cannot open file for reading [%s]", filename);

      return fd;
}

int
open_for_append (const char *filename)
{
  int fd;

  if (!filename)
        return -1;

  fd = open (filename, O_WRONLY|O_APPEND|O_CREAT, 00644);
  if (fd == -1)
    xa_debug (1, "ERROR: Cannot open file for append [%s]", filename);
  return fd;
}

char *
get_template (const char *filename)
{
      return get_icecast_file (filename, template_file_e, R_OK);
}

char *
get_log_file (const char *filename) 
{
      char logdir[BUFSIZE];

      if (!filename || !info.logdir) {
            fprintf (stderr, "WARNING: get_log_file() called with NULLs\n");
            return NULL;
      }

      snprintf (logdir, BUFSIZE, "%s%c", info.logdir, DIR_DELIMITER);
      xa_debug (1, "DEBUG: Checking directory %s", logdir);

      if (access (info.logdir, R_OK) == 0) {
            return ice_cat (logdir, filename);
      }

      snprintf (logdir, BUFSIZE, "log%c", DIR_DELIMITER);
      xa_debug (1, "DEBUG: Checking directory %s", logdir);
      if (access (logdir, R_OK) == 0) {
            return ice_cat (logdir, filename);
      }

      snprintf (logdir, BUFSIZE, "%s%c", ".", DIR_DELIMITER);
      if (access (logdir, R_OK) == 0) {
            return ice_cat (logdir, filename);
      }

      return NULL;
}

char *
get_icecast_file(const char *filename, filetype_t type, int flags)
{
      char path_and_file [BUFSIZE];

      if (!filename || !info.etcdir || !info.logdir || !info.templatedir || !info.staticdir) {
            xa_debug(1, "ERROR: get_icecast_file(): called with NULL pointer");
            return NULL;
      }

      path_and_file[0] = '\0';

      switch (type) {
            case conf_file_e:
                  snprintf(path_and_file, BUFSIZE, "%s%c%s", info.etcdir, DIR_DELIMITER, filename);
                  break;
            case log_file_e:
                  snprintf(path_and_file, BUFSIZE, "%s%c%s", info.logdir, DIR_DELIMITER, filename);
                  break;
            case template_file_e:
                  snprintf(path_and_file, BUFSIZE, "%s%c%s", info.templatedir, DIR_DELIMITER, filename);
                  break;
            case static_file_e:
                  snprintf(path_and_file, BUFSIZE, "%s%c%s", info.staticdir, DIR_DELIMITER, filename);
                  break;
            default:
                  snprintf(path_and_file, BUFSIZE, "%s", filename);
      }

      xa_debug(3, "DEBUG: get_icecast_file(): Looking for %s", path_and_file);

      if (access(path_and_file, flags) == 0)
            return nstrdup(path_and_file);
      
      switch (type) {
            case conf_file_e:
                  snprintf(path_and_file, BUFSIZE, ".%c%s%c%s", DIR_DELIMITER, "conf", DIR_DELIMITER, filename);
                  break;
            case log_file_e:
                  snprintf(path_and_file, BUFSIZE, ".%c%s%c%s", DIR_DELIMITER, "logs", DIR_DELIMITER, filename);
                  break;
            case template_file_e:
                  snprintf(path_and_file, BUFSIZE, ".%c%s%c%s", DIR_DELIMITER, "templates", DIR_DELIMITER, filename);
                  break;
            default:
                  snprintf(path_and_file, BUFSIZE, "%s", filename);
      }
      
      xa_debug(3, "DEBUG: get_icecast_file(): Looking for %s", path_and_file);

      if (access(path_and_file, flags) == 0)
            return nstrdup(path_and_file);

      switch (type) {
            case conf_file_e:
                  snprintf(path_and_file, BUFSIZE, "..%c%s%c%s", DIR_DELIMITER, "conf", DIR_DELIMITER, filename);
                  break;
            case log_file_e:
                  snprintf(path_and_file, BUFSIZE, "..%c%s%c%s", DIR_DELIMITER, "logs", DIR_DELIMITER, filename);
                  break;
            case template_file_e:
                  snprintf(path_and_file, BUFSIZE, "..%c%s%c%s", DIR_DELIMITER, "templates", DIR_DELIMITER, filename);
                  break;
            default:
                  snprintf(path_and_file, BUFSIZE, "%s", filename);
      }
      
      xa_debug(3, "DEBUG: get_icecast_file(): Looking for %s", path_and_file);

      if (access(path_and_file, flags) == 0)
            return nstrdup(path_and_file);

      xa_debug (2, "DEBUG: get_icecast_file(): Didn't find %s", filename);

      return NULL;
}

#define MEG (1024*1024)
void
stat_add_read (statistics_t *stat, int len)
{
      while (stat->read_bytes + len >= MEG)
      {
            stat->read_megs++;
            len -= MEG;
      }

      stat->read_bytes += len;
}

void
stat_add_write (statistics_t *stat, int len)
{
      while (stat->write_bytes + len >= MEG)
      {
            stat->write_megs++;
            len -= MEG;
      }

      stat->write_bytes += len;
}

char *
type_of_str (contype_t type, char *buf)
{
      if (type == admin_e)
            sprintf (buf, "admin");
      else if (type == client_e)
            sprintf (buf, "client");
      else if (type == source_e)
            sprintf (buf, "source");
      else
            sprintf (buf, "unknown");

      return buf;
}

void
zero_song_info (songinfo_t *si)
{
      si->streamurl = NULL;
      si->streammsg = NULL;
      si->streamtitle = NULL;
      si->streamlength = -1;
      si->udpseqnr = 0;
}

void
my_sleep (int microseconds)
{
#ifdef _WIN32
      Sleep (microseconds/1000); /* Does this really work? */
#else
# ifdef HAVE_NANOSLEEP
        struct timespec req, rem;
      long nanoseconds;

      req.tv_sec = 0;
      req.tv_nsec = 0;
      
      while (microseconds > 999999) {
            req.tv_sec++;
            microseconds -= 1000000;
      }

      nanoseconds = microseconds * 1000;
 
        while (nanoseconds > 999999999)
        {
                req.tv_sec++;
                nanoseconds -= 1000000000;
        }

        req.tv_nsec = nanoseconds;

/*    xa_debug (1, "DEBUG: Will sleep for %d seconds and %d nanoseconds", req.tv_sec, req.tv_nsec); */

        switch (nanosleep (&req, &rem)) {
            case EINTR:
                  xa_debug (4, "WARNING: nanosleep() was interupted by nonblocked signal");
                  break;
            case EINVAL:
                  xa_debug (1, "WARNING: nanosleep() was passed invalid or negative sleep value %ld+%ld",
                          req.tv_sec, req.tv_nsec);
                  break;
      }

# elif HAVE_SELECT
        struct timeval sleeper;
        sleeper.tv_sec = 0;
        sleeper.tv_usec = microseconds;
        select (1, NULL, NULL, NULL, &sleeper);
# else
        usleep (microseconds);
# endif
#endif
}

void
show_runtime_configuration ()
{
      xa_debug (1, "Runtime Configuration:");
#if defined(HAVE_NANOSLEEP)
      xa_debug (1, "Using nanosleep() as sleep method");
#elif defined(HAVE_SELECT)
      xa_debug (1, "Using select() as sleep method");
#else
#ifdef _WIN32
      xa_debug (1, "Using Sleep() as sleep method");
#else
      xa_debug (1, "Using usleep() as sleep method - THIS MAY BE UNSAFE");
#endif
#endif

#ifdef HAVE_SIGACTION
      xa_debug (1, "Using posix signal interface to block all signals in threads that don't want them");
#endif

      xa_debug (1, "Using %d chunks of %d bytes for client backlog", CHUNKLEN, SOURCE_BUFFSIZE);

      switch (info.resolv_type)
      {
            case solaris_gethostbyname_r_e:
                  xa_debug (1, "Using solaris own gethostbyname_r() and getaddrbyname_r(), which is good.");
                  break;
            case linux_gethostbyname_r_e:
                  xa_debug (1, "Using linux own gethostbyname_r() and getaddrbyname_r(), which is good.");
                  break;
            case standard_gethostbyname_e:
                  xa_debug (1, "Using standard gethostbyname() and getaddrbyname(), which might be dangerous cause it's not threadsafe!");
                  break;
      }

#ifdef PTHREAD_THREADS_MAX
      xa_debug (1, "System can create max %d threads", PTHREAD_THREADS_MAX);
#endif
}

int
is_recoverable (int error)
{
#ifdef _WIN32
      if ((WSAGetLastError() == WSAEWOULDBLOCK) || (WSAGetLastError() == WSAEINTR) || (WSAGetLastError() == WSAEINPROGRESS)) 
            return 1;
#else
      if ((error == EAGAIN) || (error == EINTR) || (error == EINPROGRESS))
            return 1;
#endif

#ifdef SOLARIS
      if ((error == EWOULDBLOCK))
        return 1;
#endif

#ifdef LINUX
      if (error == EIO) /* Works around a very very weird error with gdb and linux */
            return 1;
#endif

      return 0;
}

void
set_run_path (char **argv)
{
      char *pos;
      int i;

      if (!argv || !argv[0]) {
            fprintf (stderr, "WARNING: Weird NULL pointer in argv\n");
            return;
      }

      /* Where are we running from? */
      info.runpath = strdup (argv[0]);    
      running = SERVER_INITIALIZING;
      pos = strrchr (info.runpath, DIR_DELIMITER);
      if (pos) {
            *(pos + 1) = '\0';
            i = strlen(info.runpath) - 1;
            if ((i >= 3) && 
                (info.runpath[i-1] == 'n' || info.runpath[i-1] == 'N') &&
                (info.runpath[i-2] == 'i' || info.runpath[i-3] == 'I') &&
                (info.runpath[i-3] == 'b' || info.runpath[i-4] == 'B')) {
                  info.runpath[i-3] = '\0';
            }
      }
}

void
zero_audiocast (audiocast_t *au)
{
      au->name = NULL;
      au->genre = NULL;
      au->bitrate = -1;
      au->url = NULL;
      au->mount = NULL;
      au->description = NULL;
      au->streammimetype = NULL;
      au->contentid = NULL;
      au->public = -1;
}

void
dispose_audiocast (audiocast_t *au)
{
      if (!au)
            return;
      nfree (au->name);
      nfree (au->genre);
      nfree (au->url);
      nfree (au->mount);
      nfree (au->description);
      nfree (au->streammimetype);
      nfree (au->contentid);
}

int
is_valid_http_request (request_t *req)
{
      if (!req->path[0])
            return 0;
      return 1;
}


      

Generated by  Doxygen 1.6.0   Back to index