[xiph-commits] r9713 - icecast/trunk/icecast/src

karl at svn.xiph.org karl at svn.xiph.org
Sun Aug 7 16:01:14 PDT 2005


Author: karl
Date: 2005-08-07 16:01:04 -0700 (Sun, 07 Aug 2005)
New Revision: 9713

Added:
   icecast/trunk/icecast/src/auth_htpasswd.c
   icecast/trunk/icecast/src/auth_htpasswd.h
Modified:
   icecast/trunk/icecast/src/Makefile.am
   icecast/trunk/icecast/src/admin.c
   icecast/trunk/icecast/src/auth.c
   icecast/trunk/icecast/src/auth.h
   icecast/trunk/icecast/src/cfgfile.c
   icecast/trunk/icecast/src/cfgfile.h
   icecast/trunk/icecast/src/client.c
   icecast/trunk/icecast/src/client.h
   icecast/trunk/icecast/src/connection.c
   icecast/trunk/icecast/src/main.c
   icecast/trunk/icecast/src/source.c
   icecast/trunk/icecast/src/source.h
Log:
Initial auth merge. Add an auth thread (multiple threads can be done later)
which can be used to handle authentication mechanisms without taking locks
for long periods.  Non-authenticated mountpoints bypass the auth thread.

The lookup/checking of the source_t is done after the authentication succeeds
so the fallback mechanism does not affect which authenticator is used. This
can be extended to allow us to authenticate in webroot as well. XML re-read
changes will take effect immediately for new listeners but existing listeners
will use the original auth_t (refcounted) when they exit.

htpasswd access has been seperated out from auth.c, and implements an AVL
tree for a faster username lookup.  The htpasswd file timestamp is checked
just in case there are changes made externally


Modified: icecast/trunk/icecast/src/Makefile.am
===================================================================
--- icecast/trunk/icecast/src/Makefile.am	2005-08-07 17:42:41 UTC (rev 9712)
+++ icecast/trunk/icecast/src/Makefile.am	2005-08-07 23:01:04 UTC (rev 9713)
@@ -7,14 +7,16 @@
 bin_PROGRAMS = icecast
 
 noinst_HEADERS = admin.h cfgfile.h os.h logging.h sighandler.h connection.h \
-    global.h util.h slave.h source.h stats.h refbuf.h client.h format.h \
-    compat.h format_mp3.h fserve.h xslt.h yp.h event.h md5.h \
-    auth.h format_ogg.h \
+    global.h util.h slave.h source.h stats.h refbuf.h client.h \
+    compat.h fserve.h xslt.h yp.h event.h md5.h \
+    auth.h auth_htpasswd.h \
+    format.h format_ogg.h format_mp3.h \
     format_vorbis.h format_theora.h format_flac.h format_speex.h format_midi.h
 icecast_SOURCES = cfgfile.c main.c logging.c sighandler.c connection.c global.c \
     util.c slave.c source.c stats.c refbuf.c client.c \
-    xslt.c fserve.c event.c admin.c auth.c md5.c \
-    format.c format_ogg.c format_mp3.c format_midi.c format_flac.c
+    xslt.c fserve.c event.c admin.c md5.c \
+    format.c format_ogg.c format_mp3.c format_midi.c format_flac.c \
+    auth.c auth_htpasswd.c
 EXTRA_icecast_SOURCES = yp.c \
     format_vorbis.c format_theora.c format_speex.c
     

Modified: icecast/trunk/icecast/src/admin.c
===================================================================
--- icecast/trunk/icecast/src/admin.c	2005-08-07 17:42:41 UTC (rev 9712)
+++ icecast/trunk/icecast/src/admin.c	2005-08-07 23:01:04 UTC (rev 9713)
@@ -229,6 +229,9 @@
 
         if (source->running || source->on_demand)
         {
+            ice_config_t *config;
+            mount_proxy *mountinfo;
+
             srcnode = xmlNewChild(xmlnode, NULL, "source", NULL);
             xmlSetProp(srcnode, "mount", source->mount);
 
@@ -237,6 +240,16 @@
                     source->fallback_mount:"");
             snprintf (buf, sizeof(buf), "%lu", source->listeners);
             xmlNewChild(srcnode, NULL, "listeners", buf);
+            config = config_get_config();
+
+            mountinfo = config_find_mount (config, source->mount);
+            if (mountinfo && mountinfo->auth)
+            {
+                xmlNewChild(srcnode, NULL, "authenticator",
+                        mountinfo->auth->type);
+            }
+            config_release_config();
+
             if (source->running)
             {
                 snprintf (buf, sizeof(buf), "%lu",
@@ -245,10 +258,6 @@
                 xmlNewChild (srcnode, NULL, "content-type", 
                         source->format->contenttype);
             }
-            if (source->authenticator) {
-                xmlNewChild(srcnode, NULL, "authenticator", 
-                    source->authenticator->type);
-            }
         }
         node = avl_get_next(node);
     }
@@ -707,12 +716,21 @@
     char *password = NULL;
     char *message = NULL;
     int ret = AUTH_OK;
+    ice_config_t *config = config_get_config ();
+    mount_proxy *mountinfo = config_find_mount (config, source->mount);
 
     if((COMMAND_OPTIONAL(client, "action", action))) {
+        if (mountinfo == NULL || mountinfo->auth == NULL)
+        {
+            WARN1 ("manage auth request for %s but no facility available", source->mount);
+            config_release_config ();
+            client_send_404 (client, "no such auth facility");
+            return;
+        }
         if (!strcmp(action, "add")) {
             COMMAND_REQUIRE(client, "username", username);
             COMMAND_REQUIRE(client, "password", password);
-            ret = auth_adduser(source, username, password);
+            ret = mountinfo->auth->adduser(mountinfo->auth, username, password);
             if (ret == AUTH_FAILED) {
                 message = strdup("User add failed - check the icecast error log");
             }
@@ -725,7 +743,7 @@
         }
         if (!strcmp(action, "delete")) {
             COMMAND_REQUIRE(client, "username", username);
-            ret = auth_deleteuser(source, username);
+            ret = mountinfo->auth->deleteuser(mountinfo->auth, username);
             if (ret == AUTH_FAILED) {
                 message = strdup("User delete failed - check the icecast error log");
             }
@@ -747,8 +765,11 @@
 
     xmlDocSetRootElement(doc, node);
 
-    auth_get_userlist(source, srcnode);
+    if (mountinfo && mountinfo->auth && mountinfo->auth->listuser)
+        mountinfo->auth->listuser (mountinfo->auth, srcnode);
 
+    config_release_config ();
+
     admin_send_response(doc, client, response, 
         MANAGEAUTH_TRANSFORMED_REQUEST);
     if (message) {

Modified: icecast/trunk/icecast/src/auth.c
===================================================================
--- icecast/trunk/icecast/src/auth.c	2005-08-07 17:42:41 UTC (rev 9712)
+++ icecast/trunk/icecast/src/auth.c	2005-08-07 23:01:04 UTC (rev 9713)
@@ -22,487 +22,534 @@
 #include <string.h>
 #include <errno.h>
 #include <stdio.h>
-#include <sys/types.h>
-#include <sys/stat.h>
 
 #include "auth.h"
+#include "auth_htpasswd.h"
 #include "source.h"
 #include "client.h"
 #include "cfgfile.h"
+#include "stats.h"
 #include "httpp/httpp.h"
-#include "md5.h"
 
 #include "logging.h"
 #define CATMODULE "auth"
-
-#ifdef _WIN32
-#define snprintf _snprintf
-#endif
 
-int auth_is_listener_connected(source_t *source, char *username)
+
+static volatile auth_client *clients_to_auth;
+static volatile unsigned int auth_pending_count;
+static volatile int auth_running;
+static mutex_t auth_lock;
+static thread_type *auth_thread;
+
+
+static void auth_client_setup (mount_proxy *mountinfo, client_t *client)
 {
-    client_t *client;
-    avl_node *client_node;
+    /* This will look something like "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" */
+    char *header = httpp_getvar(client->parser, "authorization");
+    char *userpass, *tmp;
+    char *username, *password;
 
-    avl_tree_rlock(source->client_tree);
+    do
+    {
+        if (header == NULL)
+            break;
 
-    client_node = avl_get_first(source->client_tree);
-    while(client_node) {
-        client = (client_t *)client_node->key;
-        if (client->username) {
-            if (!strcmp(client->username, username)) {
-                avl_tree_unlock(source->client_tree);
-                return 1;
+        if (strncmp(header, "Basic ", 6) == 0)
+        {
+            userpass = util_base64_decode (header+6);
+            if (userpass == NULL)
+            {
+                WARN1("Base64 decode of Authorization header \"%s\" failed",
+                        header+6);
+                break;
             }
+
+            tmp = strchr(userpass, ':');
+            if (tmp == NULL)
+            { 
+                free (userpass);
+                break;
+            }
+
+            *tmp = 0;
+            username = userpass;
+            password = tmp+1;
+            client->username = strdup (username);
+            client->password = strdup (password);
+            free (userpass);
+            break;
         }
-        client_node = avl_get_next(client_node);
-    }
+        INFO1 ("unhandled authorization header: %s", header);
 
-    avl_tree_unlock(source->client_tree);
-    return 0;
+    } while (0);
 
+    client->auth = mountinfo->auth;
+    client->auth->refcount++;
 }
 
-auth_result auth_check_client(source_t *source, client_t *client)
+
+static void queue_auth_client (auth_client *auth_user)
 {
-    auth_t *authenticator = source->authenticator;
-    auth_result result;
+    thread_mutex_lock (&auth_lock);
+    auth_user->next = (auth_client *)clients_to_auth;
+    clients_to_auth = auth_user;
+    auth_pending_count++;
+    thread_mutex_unlock (&auth_lock);
+}
 
-    if(authenticator) {
-        /* This will look something like "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" */
-        char *header = httpp_getvar(client->parser, "authorization");
-        char *userpass, *tmp;
-        char *username, *password;
-    
-        if(header == NULL)
-            return AUTH_FAILED;
-    
-        if(strncmp(header, "Basic ", 6)) {
-            INFO0("Authorization not using Basic");
-            return 0;
-        }
-        
-        userpass = util_base64_decode(header+6);
-        if(userpass == NULL) {
-            WARN1("Base64 decode of Authorization header \"%s\" failed",
-                    header+6);
-            return AUTH_FAILED;
-        }
-        
-        tmp = strchr(userpass, ':');
-        if(!tmp) { 
-            free(userpass);
-            return AUTH_FAILED;
-        }
 
-        *tmp = 0;
-        username = userpass;
-        password = tmp+1;
+/* release the auth. It is referred to by multiple structures so this is
+ * refcounted and only actual freed after the last use
+ */
+void auth_release (auth_t *authenticator)
+{
+    if (authenticator == NULL)
+        return;
 
-        result = authenticator->authenticate(
-                authenticator, source, username, password);
+    authenticator->refcount--;
+    if (authenticator->refcount)
+        return;
 
-        if(result == AUTH_OK)
-            client->username = strdup(username);
+    if (authenticator->free)
+        authenticator->free (authenticator);
+    free (authenticator->type);
+    free (authenticator);
+}
 
-        free(userpass);
 
-        return result;
+void auth_client_free (auth_client *auth_user)
+{
+    if (auth_user == NULL)
+        return;
+    if (auth_user->client)
+    {
+        client_t *client = auth_user->client;
+
+        if (client->respcode)
+            client_destroy (client);
+        else
+            client_send_401 (client);
+        auth_user->client = NULL;
     }
-    else
-        return AUTH_FAILED;
+    free (auth_user->mount);
+    free (auth_user);
 }
 
-static auth_t *auth_get_htpasswd_auth(config_options_t *options);
 
-auth_t *auth_get_authenticator(char *type, config_options_t *options)
+/* wrapper function for auth thread to authenticate new listener
+ * connection details
+ */
+static void auth_new_listener (auth_client *auth_user)
 {
-    auth_t *auth = NULL;
-    if(!strcmp(type, "htpasswd")) {
-        auth = auth_get_htpasswd_auth(options);
-        auth->type = strdup(type);
+    client_t *client = auth_user->client;
+
+    if (client->auth->authenticate)
+    {
+        if (client->auth->authenticate (auth_user) != AUTH_OK)
+            return;
     }
-    else {
-        ERROR1("Unrecognised authenticator type: \"%s\"", type);
-        return NULL;
-    }
-
-    if(!auth)
-        ERROR1("Couldn't configure authenticator of type \"%s\"", type);
-    
-    return auth;
+    if (auth_postprocess_client (auth_user) < 0)
+        INFO1 ("client %lu failed", client->con->id);
 }
 
-typedef struct {
-    char *filename;
-    int allow_duplicate_users;
-    rwlock_t file_rwlock;
-} htpasswd_auth_state;
 
-static void htpasswd_clear(auth_t *self) {
-    htpasswd_auth_state *state = self->state;
-    free(state->filename);
-    thread_rwlock_destroy(&state->file_rwlock);
-    free(state);
-    free(self->type);
-    free(self);
-}
+/* wrapper function are auth thread to authenticate new listener
+ * connections
+ */
+static void auth_remove_listener (auth_client *auth_user)
+{
+    client_t *client = auth_user->client;
 
-static int get_line(FILE *file, char *buf, int len)
-{
-    if(fgets(buf, len, file)) {
-        int len = strlen(buf);
-        if(len > 0 && buf[len-1] == '\n') {
-            buf[--len] = 0;
-            if(len > 0 && buf[len-1] == '\r')
-                buf[--len] = 0;
-        }
-        return 1;
-    }
-    return 0;
+    if (client->auth->release_client)
+        client->auth->release_client (auth_user);
+    auth_release (client->auth);
+    client->auth = NULL;
+    return;
 }
 
-/* md5 hash */
-static char *get_hash(char *data, int len)
+
+/* The auth thread main loop. */
+static void *auth_run_thread (void *arg)
 {
-    struct MD5Context context;
-    unsigned char digest[16];
+    INFO0 ("Authentication thread started");
+    while (1)
+    {
+        if (clients_to_auth)
+        {
+            auth_client *auth_user;
 
-    MD5Init(&context);
+            thread_mutex_lock (&auth_lock);
+            auth_user = (auth_client*)clients_to_auth;
+            clients_to_auth = auth_user->next;
+            auth_pending_count--;
+            thread_mutex_unlock (&auth_lock);
+            auth_user->next = NULL;
 
-    MD5Update(&context, data, len);
+            if (auth_user->process)
+                auth_user->process (auth_user);
+            else
+                ERROR0 ("client auth process not set");
 
-    MD5Final(digest, &context);
+            auth_client_free (auth_user);
 
-    return util_bin_to_hex(digest, 16);
+            continue;
+        }
+        /* is there a request to shutdown */
+        if (auth_running == 0)
+            break;
+        thread_sleep (150000);
+    }
+    INFO0 ("Authenication thread shutting down");
+    return NULL;
 }
 
-#define MAX_LINE_LEN 512
 
-/* Not efficient; opens and scans the entire file for every request */
-static auth_result htpasswd_auth(auth_t *auth, source_t *source, char *username, char *password)
+/* Check whether this client is currently on this mount, the client may be
+ * on either the active or pending lists.
+ * return 1 if ok to add or 0 to prevent
+ */
+static int check_duplicate_logins (source_t *source, client_t *client)
 {
-    htpasswd_auth_state *state = auth->state;
-    FILE *passwdfile = NULL;
-    char line[MAX_LINE_LEN];
-    char *sep;
+    auth_t *auth = client->auth;
 
-    thread_rwlock_rlock(&state->file_rwlock);
-    if (!state->allow_duplicate_users) {
-        if (auth_is_listener_connected(source, username)) {
-            thread_rwlock_unlock(&state->file_rwlock);
-            return AUTH_FORBIDDEN;
-        }
-    }
-    passwdfile = fopen(state->filename, "rb");
-    if(passwdfile == NULL) {
-        WARN2("Failed to open authentication database \"%s\": %s", 
-                state->filename, strerror(errno));
-        thread_rwlock_unlock(&state->file_rwlock);
-        return AUTH_FAILED;
-    }
+    /* allow multiple authenticated relays */
+    if (client->username == NULL)
+        return 1;
 
-    while(get_line(passwdfile, line, MAX_LINE_LEN)) {
-        if(!line[0] || line[0] == '#')
-            continue;
+    if (auth && auth->allow_duplicate_users == 0)
+    {
+        avl_node *node;
 
-        sep = strchr(line, ':');
-        if(sep == NULL) {
-            DEBUG0("No separator in line");
-            continue;
-        }
+        avl_tree_rlock (source->client_tree);
+        node = avl_get_first (source->client_tree);
+        while (node)
+        {   
+            client_t *client = (client_t *)node->key;
+            if (client->username && strcmp (client->username, client->username) == 0)
+            {
+                avl_tree_unlock (source->client_tree);
+                return 0;
+            }
+            node = avl_get_next (node);
+        }       
+        avl_tree_unlock (source->client_tree);
 
-        *sep = 0;
-        if(!strcmp(username, line)) {
-            /* Found our user, now: does the hash of password match hash? */
-            char *hash = sep+1;
-            char *hashed_password = get_hash(password, strlen(password));
-            if(!strcmp(hash, hashed_password)) {
-                fclose(passwdfile);
-                free(hashed_password);
-                thread_rwlock_unlock(&state->file_rwlock);
-                return AUTH_OK;
+        avl_tree_rlock (source->pending_tree);
+        node = avl_get_first (source->pending_tree);
+        while (node)
+        {
+            client_t *client = (client_t *)node->key;
+            if (client->username && strcmp (client->username, client->username) == 0)
+            {
+                avl_tree_unlock (source->pending_tree);
+                return 0;
             }
-            free(hashed_password);
-            /* We don't keep searching through the file */
-            break; 
+            node = avl_get_next (node);
         }
+        avl_tree_unlock (source->pending_tree);
     }
+    return 1;
+}
 
-    fclose(passwdfile);
 
-    thread_rwlock_unlock(&state->file_rwlock);
-    return AUTH_FAILED;
-}
-
-static auth_t *auth_get_htpasswd_auth(config_options_t *options)
+/* if 0 is returned then the client should not be touched, however if -1
+ * is returned then the caller is responsible for handling the client
+ */
+static int add_client_to_source (source_t *source, client_t *client)
 {
-    auth_t *authenticator = calloc(1, sizeof(auth_t));
-    htpasswd_auth_state *state;
+    do
+    {
+        DEBUG3 ("max on %s is %ld (cur %lu)", source->mount,
+                source->max_listeners, source->listeners);
+        if (source->max_listeners == -1)
+            break;
+        if (source->listeners < (unsigned long)source->max_listeners)
+            break;
 
-    authenticator->authenticate = htpasswd_auth;
-    authenticator->free = htpasswd_clear;
+        /* now we fail the client */
+        return -1;
 
-    state = calloc(1, sizeof(htpasswd_auth_state));
+    } while (1);
+    /* lets add the client to the active list */
+    avl_tree_wlock (source->pending_tree);
+    avl_insert (source->pending_tree, client);
+    avl_tree_unlock (source->pending_tree);
+    stats_event_inc (NULL, "listener_connections");
 
-    state->allow_duplicate_users = 1;
-    while(options) {
-        if(!strcmp(options->name, "filename"))
-            state->filename = strdup(options->value);
-        if(!strcmp(options->name, "allow_duplicate_users"))
-            state->allow_duplicate_users = atoi(options->value);
-        options = options->next;
-    }
+    client->write_to_client = format_generic_write_to_client;
+    client->check_buffer = format_check_http_buffer;
+    client->refbuf->len = PER_CLIENT_REFBUF_SIZE;
+    memset (client->refbuf->data, 0, PER_CLIENT_REFBUF_SIZE);
 
-    if(!state->filename) {
-        free(state);
-        free(authenticator);
-        ERROR0("No filename given in options for authenticator.");
-        return NULL;
+    if (source->running == 0 && source->on_demand)
+    {
+        /* enable on-demand relay to start, wake up the slave thread */
+        DEBUG0("kicking off on-demand relay");
+        source->on_demand_req = 1;
+        slave_rescan ();
     }
+    DEBUG1 ("Added client to %s", source->mount);
+    return 0;
+}
 
-    authenticator->state = state;
-    DEBUG1("Configured htpasswd authentication using password file %s", 
-            state->filename);
 
-    thread_rwlock_create(&state->file_rwlock);
-
-    return authenticator;
-}
-
-int auth_htpasswd_existing_user(auth_t *auth, char *username)
+/* Add listener to the pending lists of either the  source or fserve thread.
+ * This can be run from the connection or auth thread context
+ */
+static int add_authenticated_client (const char *mount, mount_proxy *mountinfo, client_t *client)
 {
-    FILE *passwdfile;
-    htpasswd_auth_state *state;
-    int ret = AUTH_OK;
-    char line[MAX_LINE_LEN];
-    char *sep;
+    int ret = 0;
+    source_t *source = NULL;
 
-    state = auth->state;
-    passwdfile = fopen(state->filename, "rb");
+    avl_tree_rlock (global.source_tree);
+    source = source_find_mount (mount);
 
-    if(passwdfile == NULL) {
-        WARN2("Failed to open authentication database \"%s\": %s", 
-                state->filename, strerror(errno));
-        return AUTH_FAILED;
-    }
-    while(get_line(passwdfile, line, MAX_LINE_LEN)) {
-        if(!line[0] || line[0] == '#')
-            continue;
-        sep = strchr(line, ':');
-        if(sep == NULL) {
-            DEBUG0("No separator in line");
-            continue;
+    if (source)
+    {
+        if (client->auth && check_duplicate_logins (source, client) == 0)
+        {
+            avl_tree_unlock (global.source_tree);
+            return -1;
         }
-        *sep = 0;
-        if (!strcmp(username, line)) {
-            /* We found the user, break out of the loop */
-            ret = AUTH_USEREXISTS;
-            break;
-        }
+        ret = add_client_to_source (source, client);
+        avl_tree_unlock (global.source_tree);
+        if (ret == 0)
+            DEBUG0 ("client authenticated, passed to source");
     }
-
-    fclose(passwdfile);
     return ret;
+}
 
-}
-int auth_htpasswd_adduser(auth_t *auth, char *username, char *password)
+
+int auth_postprocess_client (auth_client *auth_user)
 {
-    FILE *passwdfile;
-    char *hashed_password = NULL;
-    htpasswd_auth_state *state;
+    int ret;
+    ice_config_t *config = config_get_config();
 
-    if (auth_htpasswd_existing_user(auth, username) == AUTH_USEREXISTS) {
-        return AUTH_USEREXISTS;
-    }
-    state = auth->state;
-    passwdfile = fopen(state->filename, "ab");
+    mount_proxy *mountinfo = config_find_mount (config, auth_user->mount);
+    auth_user->client->authenticated = 1;
 
-    if(passwdfile == NULL) {
-        WARN2("Failed to open authentication database \"%s\": %s", 
-                state->filename, strerror(errno));
-        return AUTH_FAILED;
-    }
+    ret = add_authenticated_client (auth_user->mount, mountinfo, auth_user->client);
+    config_release_config();
 
-    hashed_password = get_hash(password, strlen(password));
-    if (hashed_password) {
-        fprintf(passwdfile, "%s:%s\n", username, hashed_password);
-        free(hashed_password);
-    }
+    if (ret < 0)
+        client_send_401 (auth_user->client);
+    auth_user->client = NULL;
 
-    fclose(passwdfile);
-    return AUTH_USERADDED;
+    return ret;
 }
 
-int auth_adduser(source_t *source, char *username, char *password)
+
+/* Add a listener. Check for any mount information that states any
+ * authentication to be used.
+ */
+void add_client (const char *mount, client_t *client)
 {
-    int ret = 0;
-    htpasswd_auth_state *state;
+    mount_proxy *mountinfo; 
+    ice_config_t *config = config_get_config();
 
-    if (source->authenticator) {
-        if (!strcmp(source->authenticator->type, "htpasswd")) {
-            state = source->authenticator->state;
-            thread_rwlock_wlock(&state->file_rwlock);
-            ret = auth_htpasswd_adduser(source->authenticator, username, password);
-            thread_rwlock_unlock(&state->file_rwlock);
+    mountinfo = config_find_mount (config, mount);
+    if (mountinfo && mountinfo->no_mount)
+    {
+        config_release_config ();
+        client_send_404 (client, "mountpoint unavailable");
+        return;
+    }
+    if (mountinfo && mountinfo->auth)
+    {
+        auth_client *auth_user;
+
+        if (auth_pending_count > 30)
+        {
+            config_release_config ();
+            WARN0 ("too many clients awaiting authentication");
+            client_send_404 (client, "busy, please try again later");
+            return;
         }
+        auth_client_setup (mountinfo, client);
+        config_release_config ();
+
+        if (client->auth == NULL)
+        {
+            client_send_401 (client);
+            return;
+        }
+        auth_user = calloc (1, sizeof (auth_client));
+        if (auth_user == NULL)
+        {
+            client_send_401 (client);
+            return;
+        }
+        auth_user->mount = strdup (mount);
+        auth_user->process = auth_new_listener;
+        auth_user->client = client;
+
+        INFO0 ("adding client for authentication");
+        queue_auth_client (auth_user);
     }
-    return ret;
+    else
+    {
+        int ret = add_authenticated_client (mount, mountinfo, client);
+        config_release_config ();
+        if (ret < 0)
+            client_send_404 (client, "stream full");
+    }
 }
 
-int auth_htpasswd_deleteuser(auth_t *auth, char *username)
+
+/* determine whether we need to process this client further. This
+ * involves any auth exit, typically for external auth servers.
+ */
+int release_client (client_t *client)
 {
-    FILE *passwdfile;
-    FILE *tmp_passwdfile;
-    htpasswd_auth_state *state;
-    char line[MAX_LINE_LEN];
-    char *sep;
-    char *tmpfile = NULL;
-    int tmpfile_len = 0;
-    struct stat file_info;
+    if (client->auth)
+    {
+        auth_client *auth_user = calloc (1, sizeof (auth_client));
+        if (auth_user == NULL)
+            return 0;
 
-    state = auth->state;
-    passwdfile = fopen(state->filename, "rb");
+        auth_user->mount = strdup (httpp_getvar (client->parser, HTTPP_VAR_URI));
+        auth_user->process = auth_remove_listener;
+        auth_user->client = client;
 
-    if(passwdfile == NULL) {
-        WARN2("Failed to open authentication database \"%s\": %s", 
-                state->filename, strerror(errno));
-        return AUTH_FAILED;
+        queue_auth_client (auth_user);
+        return 1;
     }
-    tmpfile_len = strlen(state->filename) + 5;
-    tmpfile = calloc(1, tmpfile_len);
-    snprintf (tmpfile, tmpfile_len, "%s.tmp", state->filename);
-    if (stat (tmpfile, &file_info) == 0)
+    return 0;
+}
+
+
+static void get_authenticator (auth_t *auth, config_options_t *options)
+{
+    do
     {
-        WARN1 ("temp file \"%s\" exists, rejecting operation", tmpfile);
-        free (tmpfile);
-        fclose (passwdfile);
-        return AUTH_FAILED;
-    }
-    tmp_passwdfile = fopen(tmpfile, "wb");
+        DEBUG1 ("type is %s", auth->type);
+        if (strcmp (auth->type, "htpasswd") == 0)
+        {
+            auth_get_htpasswd_auth (auth, options);
+            break;
+        }
+        
+        ERROR1("Unrecognised authenticator type: \"%s\"", auth->type);
+        return;
+    } while (0);
 
-    if(tmp_passwdfile == NULL) {
-        WARN2("Failed to open temporary authentication database \"%s\": %s", 
-                tmpfile, strerror(errno));
-        fclose(passwdfile);
-        free(tmpfile);
-        return AUTH_FAILED;
+    auth->refcount = 1;
+    while (options)
+    {
+        if (strcmp(options->name, "allow_duplicate_users") == 0)
+            auth->allow_duplicate_users = atoi (options->value);
+        options = options->next;
     }
+}
 
 
-    while(get_line(passwdfile, line, MAX_LINE_LEN)) {
-        if(!line[0] || line[0] == '#')
-            continue;
+auth_t *auth_get_authenticator (xmlNodePtr node)
+{
+    auth_t *auth = calloc (1, sizeof (auth_t));
+    config_options_t *options = NULL, **next_option = &options;
+    xmlNodePtr option;
 
-        sep = strchr(line, ':');
-        if(sep == NULL) {
-            DEBUG0("No separator in line");
-            continue;
-        }
+    if (auth == NULL)
+        return NULL;
 
-        *sep = 0;
-        if (strcmp(username, line)) {
-            /* We did not match on the user, so copy it to the temp file */
-            /* and put the : back in */
-            *sep = ':';
-            fprintf(tmp_passwdfile, "%s\n", line);
+    option = node->xmlChildrenNode;
+    while (option)
+    {
+        xmlNodePtr current = option;
+        option = option->next;
+        if (strcmp (current->name, "option") == 0)
+        {
+            config_options_t *opt = calloc (1, sizeof (config_options_t));
+            opt->name = xmlGetProp (current, "name");
+            if (opt->name == NULL)
+            {
+                free(opt);
+                continue;
+            }
+            opt->value = xmlGetProp (current, "value");
+            if (opt->value == NULL)
+            {
+                xmlFree (opt->name);
+                free (opt);
+                continue;
+            }
+            *next_option = opt;
+            next_option = &opt->next;
         }
+        else
+            if (strcmp (current->name, "text") != 0)
+                WARN1 ("unknown auth setting (%s)", current->name);
     }
-
-    fclose(tmp_passwdfile);
-    fclose(passwdfile);
-
-    /* Now move the contents of the tmp file to the original */
-#ifdef _WIN32
-    /* Windows won't let us rename a file if the destination file
-       exists...so, lets remove the original first */
-    if (remove(state->filename) != 0) {
-        ERROR3("Problem moving temp authentication file to original \"%s\" - \"%s\": %s", 
-                tmpfile, state->filename, strerror(errno));
+    auth->type = xmlGetProp (node, "type");
+    get_authenticator (auth, options);
+    while (options)
+    {
+        config_options_t *opt = options;
+        options = opt->next;
+        xmlFree (opt->name);
+        xmlFree (opt->value);
+        free (opt);
     }
-    else {
-#endif
-        if (rename(tmpfile, state->filename) != 0) {
-            ERROR3("Problem moving temp authentication file to original \"%s\" - \"%s\": %s", 
-                tmpfile, state->filename, strerror(errno));
-	}
-#ifdef _WIN32
-    }
-#endif
+    return auth;
+}
 
-    free(tmpfile);
 
-    return AUTH_USERDELETED;
-}
-int auth_deleteuser(source_t *source, char *username)
+/* called when the stream starts, so that authentication engine can do any
+ * cleanup/initialisation.
+ */
+void auth_stream_start (mount_proxy *mountinfo, const char *mount)
 {
-    htpasswd_auth_state *state;
+    if (mountinfo && mountinfo->auth && mountinfo->auth->stream_start)
+    {
+        auth_client *auth_user = calloc (1, sizeof (auth_client));
+        if (auth_user)
+        {
+            auth_user->mount = strdup (mount);
+            auth_user->process = mountinfo->auth->stream_start;
 
-    int ret = 0;
-    if (source->authenticator) {
-        if (!strcmp(source->authenticator->type, "htpasswd")) {
-            state = source->authenticator->state;
-            thread_rwlock_wlock(&state->file_rwlock);
-            ret = auth_htpasswd_deleteuser(source->authenticator, username);
-            thread_rwlock_unlock(&state->file_rwlock);
+            queue_auth_client (auth_user);
         }
     }
-    return ret;
 }
 
-int auth_get_htpasswd_userlist(auth_t *auth, xmlNodePtr srcnode)
+
+/* Called when the stream ends so that the authentication engine can do
+ * any authentication cleanup
+ */
+void auth_stream_end (mount_proxy *mountinfo, const char *mount)
 {
-    htpasswd_auth_state *state;
-    FILE *passwdfile;
-    char line[MAX_LINE_LEN];
-    char *sep;
-    char *passwd;
-    xmlNodePtr newnode;
+    if (mountinfo && mountinfo->auth && mountinfo->auth->stream_end)
+    {
+        auth_client *auth_user = calloc (1, sizeof (auth_client));
+        if (auth_user)
+        {
+            auth_user->mount = strdup (mount);
+            auth_user->process = mountinfo->auth->stream_end;
 
-    state = auth->state;
-
-    passwdfile = fopen(state->filename, "rb");
-
-    if(passwdfile == NULL) {
-        WARN2("Failed to open authentication database \"%s\": %s", 
-                state->filename, strerror(errno));
-        return AUTH_FAILED;
+            queue_auth_client (auth_user);
+        }
     }
+}
 
-    while(get_line(passwdfile, line, MAX_LINE_LEN)) {
-        if(!line[0] || line[0] == '#')
-            continue;
 
-        sep = strchr(line, ':');
-        if(sep == NULL) {
-            DEBUG0("No separator in line");
-            continue;
-        }
+/* these are called at server start and termination */
 
-        *sep = 0;
-        newnode = xmlNewChild(srcnode, NULL, "User", NULL);
-        xmlNewChild(newnode, NULL, "username", line);
-        passwd = sep+1;
-        xmlNewChild(newnode, NULL, "password", passwd);
-    }
-
-    fclose(passwdfile);
-    return AUTH_OK;
+void auth_initialise ()
+{
+    clients_to_auth = NULL;
+    auth_pending_count = 0;
+    auth_running = 1;
+    thread_mutex_create (&auth_lock);
+    auth_thread = thread_create ("auth thread", auth_run_thread, NULL, THREAD_ATTACHED);
 }
 
-int auth_get_userlist(source_t *source, xmlNodePtr srcnode)
+void auth_shutdown ()
 {
-    int ret = 0;
-    htpasswd_auth_state *state;
-
-    if (source->authenticator) {
-        if (!strcmp(source->authenticator->type, "htpasswd")) {
-            state = source->authenticator->state;
-            thread_rwlock_rlock(&state->file_rwlock);
-            ret = auth_get_htpasswd_userlist(source->authenticator, srcnode);
-            thread_rwlock_unlock(&state->file_rwlock);
-        }
+    if (auth_thread)
+    {
+        auth_running = 0;
+        thread_join (auth_thread);
+        INFO0 ("Auth thread has terminated");
     }
-    return ret;
 }
 

Modified: icecast/trunk/icecast/src/auth.h
===================================================================
--- icecast/trunk/icecast/src/auth.h	2005-08-07 17:42:41 UTC (rev 9712)
+++ icecast/trunk/icecast/src/auth.h	2005-08-07 23:01:04 UTC (rev 9713)
@@ -17,14 +17,19 @@
 #include <config.h>
 #endif
 
+struct source_tag;
+struct auth_tag;
+
 #include <libxml/xmlmemory.h>
 #include <libxml/parser.h>
 #include <libxml/tree.h>
-#include "source.h"
+#include "cfgfile.h"
 #include "client.h"
+#include "thread/thread.h"
 
 typedef enum
 {
+    AUTH_UNDEFINED,
     AUTH_OK,
     AUTH_FAILED,
     AUTH_FORBIDDEN,
@@ -33,24 +38,58 @@
     AUTH_USERDELETED,
 } auth_result;
 
+typedef struct auth_client_tag
+{
+    char        *mount;
+    client_t    *client;
+    void        (*process)(struct auth_client_tag *auth_user);
+    struct auth_client_tag *next;
+} auth_client;
+
+
 typedef struct auth_tag
 {
+    char *mount;
+
     /* Authenticate using the given username and password */
-    auth_result (*authenticate)(struct auth_tag *self, 
-            source_t *source, char *username, char *password);
+    auth_result (*authenticate)(auth_client *aclient);
+    auth_result (*release_client)(auth_client *auth_user);
+
+    /* callbacks to specific auth for notifying auth server on source
+     * startup or shutdown
+     */
+    void (*stream_start)(auth_client *auth_user);
+    void (*stream_end)(auth_client *auth_user);
+
     void (*free)(struct auth_tag *self);
+    auth_result (*adduser)(struct auth_tag *auth, const char *username, const char *password);
+    auth_result (*deleteuser)(struct auth_tag *auth, const char *username);
+    auth_result (*listuser)(struct auth_tag *auth, xmlNodePtr srcnode);
+
+    int refcount;
+    int allow_duplicate_users;
+
     void *state;
     char *type;
 } auth_t;
 
-auth_result auth_check_client(source_t *source, client_t *client);
+void add_client (const char *mount, client_t *client);
+int  release_client (client_t *client);
 
-auth_t *auth_get_authenticator(char *type, config_options_t *options);
-void *auth_clear(auth_t *authenticator);
-int auth_get_userlist(source_t *source, xmlNodePtr srcnode);
-int auth_adduser(source_t *source, char *username, char *password);
-int auth_deleteuser(source_t *source, char *username);
+void auth_initialise ();
+auth_t  *auth_get_authenticator (xmlNodePtr node);
+void    auth_release (auth_t *authenticator);
 
+/* call to send a url request when source starts */
+void auth_stream_start (struct _mount_proxy *mountinfo, const char *mount);
+
+/* call to send a url request when source ends */
+void auth_stream_end (struct _mount_proxy *mountinfo, const char *mount);
+
+/* called from auth thread, after the client has successfully authenticated
+ * and requires adding to source or fserve. */
+int auth_postprocess_client (auth_client *auth_user);
+
 #endif
 
 

Added: icecast/trunk/icecast/src/auth_htpasswd.c
===================================================================
--- icecast/trunk/icecast/src/auth_htpasswd.c	2005-08-07 17:42:41 UTC (rev 9712)
+++ icecast/trunk/icecast/src/auth_htpasswd.c	2005-08-07 23:01:04 UTC (rev 9713)
@@ -0,0 +1,410 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+/** 
+ * Client authentication functions
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "auth.h"
+#include "source.h"
+#include "client.h"
+#include "cfgfile.h"
+#include "httpp/httpp.h"
+#include "md5.h"
+
+#include "logging.h"
+#define CATMODULE "auth_htpasswd"
+
+#ifdef WIN32
+#define snprintf _snprintf
+#endif
+
+static auth_result htpasswd_adduser (auth_t *auth, const char *username, const char *password);
+static auth_result htpasswd_deleteuser(auth_t *auth, const char *username);
+static auth_result htpasswd_userlist(auth_t *auth, xmlNodePtr srcnode);
+static int _free_user (void *key);
+
+typedef struct
+{
+    char *name;
+    char *pass;
+} htpasswd_user;
+
+typedef struct {
+    char *filename;
+    rwlock_t file_rwlock;
+    avl_tree *users;
+    time_t mtime;
+} htpasswd_auth_state;
+
+static void htpasswd_clear(auth_t *self) {
+    htpasswd_auth_state *state = self->state;
+    free(state->filename);
+    if (state->users)
+        avl_tree_free (state->users, _free_user);
+    thread_rwlock_destroy(&state->file_rwlock);
+    free(state);
+}
+
+static int get_line(FILE *file, char *buf, int len)
+{
+    if(fgets(buf, len, file)) {
+        int len = strlen(buf);
+        if(len > 0 && buf[len-1] == '\n') {
+            buf[--len] = 0;
+            if(len > 0 && buf[len-1] == '\r')
+                buf[--len] = 0;
+        }
+        return 1;
+    }
+    return 0;
+}
+
+/* md5 hash */
+static char *get_hash(const char *data, int len)
+{
+    struct MD5Context context;
+    unsigned char digest[16];
+
+    MD5Init(&context);
+
+    MD5Update(&context, data, len);
+
+    MD5Final(digest, &context);
+
+    return util_bin_to_hex(digest, 16);
+}
+
+#define MAX_LINE_LEN 512
+
+
+static int compare_users (void *arg, void *a, void *b)
+{
+    htpasswd_user *user1 = (htpasswd_user *)a;
+    htpasswd_user *user2 = (htpasswd_user *)b;
+
+    return strcmp (user1->name, user2->name);
+}
+
+
+static int _free_user (void *key)
+{
+    htpasswd_user *user = (htpasswd_user *)key;
+
+    free (user->name); /* ->pass is part of same buffer */
+    free (user);
+    return 1;
+}
+
+
+static void htpasswd_recheckfile (htpasswd_auth_state *htpasswd)
+{
+    FILE *passwdfile;
+    avl_tree *new_users;
+    int num = 0;
+    struct stat file_stat;
+    char *sep;
+    char line [MAX_LINE_LEN];
+
+    if (stat (htpasswd->filename, &file_stat) < 0)
+    {
+        WARN1 ("failed to check status of %s", htpasswd->filename);
+        return;
+    }
+    if (file_stat.st_mtime == htpasswd->mtime)
+    {
+        /* common case, no update to file */
+        return;
+    }
+    INFO1 ("re-reading htpasswd file \"%s\"", htpasswd->filename);
+    passwdfile = fopen (htpasswd->filename, "rb");
+    if (passwdfile == NULL)
+    {
+        WARN2("Failed to open authentication database \"%s\": %s", 
+                htpasswd->filename, strerror(errno));
+        return;
+    }
+    htpasswd->mtime = file_stat.st_mtime;
+
+    new_users = avl_tree_new (compare_users, NULL);
+
+    while (get_line(passwdfile, line, MAX_LINE_LEN))
+    {
+        int len;
+        htpasswd_user *entry;
+
+        num++;
+        if(!line[0] || line[0] == '#')
+            continue;
+
+        sep = strrchr (line, ':');
+        if (sep == NULL)
+        {
+            WARN2("No separator on line %d (%s)", num, htpasswd->filename);
+            continue;
+        }
+        entry = calloc (1, sizeof (htpasswd_user));
+        len = strlen (line) + 1;
+        entry->name = malloc (len);
+        *sep = 0;
+        memcpy (entry->name, line, len);
+        entry->pass = entry->name + (sep-line) + 1;
+        avl_insert (new_users, entry);
+    }
+    fclose (passwdfile);
+
+    thread_rwlock_wlock (&htpasswd->file_rwlock);
+    if (htpasswd->users)
+        avl_tree_free (htpasswd->users, _free_user);
+    htpasswd->users = new_users;
+    thread_rwlock_unlock (&htpasswd->file_rwlock);
+}
+
+
+static auth_result htpasswd_auth (auth_client *auth_user)
+{
+    auth_t *auth = auth_user->client->auth;
+    htpasswd_auth_state *htpasswd = auth->state;
+    client_t *client = auth_user->client;
+    htpasswd_user entry;
+    void *result;
+
+    if (client->username == NULL || client->password == NULL)
+        return AUTH_FAILED;
+
+    htpasswd_recheckfile (htpasswd);
+
+    thread_rwlock_rlock (&htpasswd->file_rwlock);
+    entry.name = client->username;
+    if (avl_get_by_key (htpasswd->users, &entry, &result) == 0)
+    {
+        htpasswd_user *found = result;
+        char *hashed_pw;
+
+        thread_rwlock_unlock (&htpasswd->file_rwlock);
+        hashed_pw = get_hash (client->password, strlen (client->password));
+        if (strcmp (found->pass, hashed_pw) == 0)
+        {
+            free (hashed_pw);
+            return AUTH_OK;
+        }
+        free (hashed_pw);
+        DEBUG0 ("incorrect password for client");
+        return AUTH_FAILED;
+    }
+    DEBUG1 ("no such username: %s", client->username);
+    thread_rwlock_unlock (&htpasswd->file_rwlock);
+    return AUTH_FAILED;
+}
+
+
+int  auth_get_htpasswd_auth (auth_t *authenticator, config_options_t *options)
+{
+    htpasswd_auth_state *state;
+
+    authenticator->authenticate = htpasswd_auth;
+    authenticator->free = htpasswd_clear;
+    authenticator->adduser = htpasswd_adduser;
+    authenticator->deleteuser = htpasswd_deleteuser;
+    authenticator->listuser = htpasswd_userlist;
+
+    state = calloc(1, sizeof(htpasswd_auth_state));
+
+    while(options) {
+        if(!strcmp(options->name, "filename"))
+            state->filename = strdup(options->value);
+        options = options->next;
+    }
+
+    if(!state->filename) {
+        free(state);
+        ERROR0("No filename given in options for authenticator.");
+        return -1;
+    }
+
+    authenticator->state = state;
+    DEBUG1("Configured htpasswd authentication using password file %s", 
+            state->filename);
+
+    thread_rwlock_create(&state->file_rwlock);
+    htpasswd_recheckfile (state);
+
+    return 0;
+}
+
+
+static auth_result htpasswd_adduser (auth_t *auth, const char *username, const char *password)
+{
+    FILE *passwdfile;
+    char *hashed_password = NULL;
+    htpasswd_auth_state *state = auth->state;
+    htpasswd_user entry;
+    void *result;
+
+    htpasswd_recheckfile (state);
+
+    thread_rwlock_wlock (&state->file_rwlock);
+
+    entry.name = (char*)username;
+    if (avl_get_by_key (state->users, &entry, &result) == 0)
+    {
+        thread_rwlock_unlock (&state->file_rwlock);
+        return AUTH_USEREXISTS;
+    }
+
+    passwdfile = fopen(state->filename, "ab");
+
+    if (passwdfile == NULL)
+    {
+        thread_rwlock_unlock (&state->file_rwlock);
+        WARN2("Failed to open authentication database \"%s\": %s", 
+                state->filename, strerror(errno));
+        return AUTH_FAILED;
+    }
+
+    hashed_password = get_hash(password, strlen(password));
+    if (hashed_password) {
+        fprintf(passwdfile, "%s:%s\n", username, hashed_password);
+        free(hashed_password);
+    }
+
+    fclose(passwdfile);
+    thread_rwlock_unlock (&state->file_rwlock);
+
+    return AUTH_USERADDED;
+}
+
+
+static auth_result htpasswd_deleteuser(auth_t *auth, const char *username)
+{
+    FILE *passwdfile;
+    FILE *tmp_passwdfile;
+    htpasswd_auth_state *state;
+    char line[MAX_LINE_LEN];
+    char *sep;
+    char *tmpfile = NULL;
+    int tmpfile_len = 0;
+    struct stat file_info;
+
+    state = auth->state;
+    thread_rwlock_wlock (&state->file_rwlock);
+    passwdfile = fopen(state->filename, "rb");
+
+    if(passwdfile == NULL) {
+        WARN2("Failed to open authentication database \"%s\": %s", 
+                state->filename, strerror(errno));
+        thread_rwlock_unlock (&state->file_rwlock);
+        return AUTH_FAILED;
+    }
+    tmpfile_len = strlen(state->filename) + 6;
+    tmpfile = calloc(1, tmpfile_len);
+    snprintf (tmpfile, tmpfile_len, "%s.tmp", state->filename);
+    if (stat (tmpfile, &file_info) == 0)
+    {
+        WARN1 ("temp file \"%s\" exists, rejecting operation", tmpfile);
+        free (tmpfile);
+        fclose (passwdfile);
+        thread_rwlock_unlock (&state->file_rwlock);
+        return AUTH_FAILED;
+    }
+
+    tmp_passwdfile = fopen(tmpfile, "wb");
+
+    if(tmp_passwdfile == NULL) {
+        WARN2("Failed to open temporary authentication database \"%s\": %s", 
+                tmpfile, strerror(errno));
+        fclose(passwdfile);
+        free(tmpfile);
+        thread_rwlock_unlock (&state->file_rwlock);
+        return AUTH_FAILED;
+    }
+
+
+    while(get_line(passwdfile, line, MAX_LINE_LEN)) {
+        if(!line[0] || line[0] == '#')
+            continue;
+
+        sep = strchr(line, ':');
+        if(sep == NULL) {
+            DEBUG0("No separator in line");
+            continue;
+        }
+
+        *sep = 0;
+        if (strcmp(username, line)) {
+            /* We did not match on the user, so copy it to the temp file */
+            /* and put the : back in */
+            *sep = ':';
+            fprintf(tmp_passwdfile, "%s\n", line);
+        }
+    }
+
+    fclose(tmp_passwdfile);
+    fclose(passwdfile);
+
+    /* Now move the contents of the tmp file to the original */
+    /* Windows won't let us rename a file if the destination file
+       exists...so, lets remove the original first */
+    if (remove(state->filename) != 0) {
+        ERROR3("Problem moving temp authentication file to original \"%s\" - \"%s\": %s", 
+                tmpfile, state->filename, strerror(errno));
+    }
+    else {
+        if (rename(tmpfile, state->filename) != 0) {
+            ERROR3("Problem moving temp authentication file to original \"%s\" - \"%s\": %s", 
+                    tmpfile, state->filename, strerror(errno));
+        }
+    }
+    free(tmpfile);
+    thread_rwlock_unlock (&state->file_rwlock);
+    htpasswd_recheckfile (state);
+
+    return AUTH_USERDELETED;
+}
+
+
+static auth_result htpasswd_userlist(auth_t *auth, xmlNodePtr srcnode)
+{
+    htpasswd_auth_state *state;
+    xmlNodePtr newnode;
+    avl_node *node;
+
+    state = auth->state;
+
+    htpasswd_recheckfile (state);
+
+    thread_rwlock_rlock (&state->file_rwlock);
+    node = avl_get_first (state->users);
+    while (node)
+    {
+        htpasswd_user *user = (htpasswd_user *)node->key;
+        newnode = xmlNewChild (srcnode, NULL, "User", NULL);
+        xmlNewChild(newnode, NULL, "username", user->name);
+        xmlNewChild(newnode, NULL, "password", user->pass);
+        node = avl_get_next (node);
+    }
+    thread_rwlock_unlock (&state->file_rwlock);
+
+    return AUTH_OK;
+}
+

Added: icecast/trunk/icecast/src/auth_htpasswd.h
===================================================================
--- icecast/trunk/icecast/src/auth_htpasswd.h	2005-08-07 17:42:41 UTC (rev 9712)
+++ icecast/trunk/icecast/src/auth_htpasswd.h	2005-08-07 23:01:04 UTC (rev 9713)
@@ -0,0 +1,24 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2000-2004, Jack Moffitt <jack at xiph.org, 
+ *                      Michael Smith <msmith at xiph.org>,
+ *                      oddsock <oddsock at xiph.org>,
+ *                      Karl Heyes <karl at xiph.org>
+ *                      and others (see AUTHORS for details).
+ */
+
+#ifndef __AUTH_HTPASSWD_H__
+#define __AUTH_HTPASSWD_H__
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+void auth_get_htpasswd_auth (auth_t *auth, config_options_t *options);
+
+#endif
+
+

Modified: icecast/trunk/icecast/src/cfgfile.c
===================================================================
--- icecast/trunk/icecast/src/cfgfile.c	2005-08-07 17:42:41 UTC (rev 9712)
+++ icecast/trunk/icecast/src/cfgfile.c	2005-08-07 23:01:04 UTC (rev 9713)
@@ -522,8 +522,6 @@
     mount_proxy *mount = calloc(1, sizeof(mount_proxy));
     mount_proxy *current = configuration->mounts;
     mount_proxy *last=NULL;
-    xmlNodePtr option;
-    config_options_t *last_option;
     
     while(current) {
         last = current;
@@ -601,35 +599,7 @@
             if(tmp) xmlFree(tmp);
         }
         else if (strcmp(node->name, "authentication") == 0) {
-            mount->auth_type = xmlGetProp(node, "type");
-            option = node->xmlChildrenNode;
-            last_option = NULL;
-            while(option != NULL) {
-                if(strcmp(option->name, "option") == 0) {
-                    config_options_t *opt = malloc(sizeof(config_options_t));
-                    opt->name = xmlGetProp(option, "name");
-                    if(!opt->name) {
-                        free(opt);
-                        option = option->next;
-                        continue;
-                    }
-                    opt->value = xmlGetProp(option, "value");
-                    if(!opt->value) {
-                        free(opt->name);
-                        free(opt);
-                        option = option->next;
-                        continue;
-                    }
-                    opt->next = NULL;
-
-                    if(last_option)
-                        last_option->next = opt;
-                    else
-                        mount->auth_options = opt;
-                    last_option = opt;
-                }
-                option = option->next;
-            }
+            mount->auth = auth_get_authenticator (node);
         }
         else if (strcmp(node->name, "on-connect") == 0) {
             mount->on_connect = (char *)xmlNodeListGetString(

Modified: icecast/trunk/icecast/src/cfgfile.h
===================================================================
--- icecast/trunk/icecast/src/cfgfile.h	2005-08-07 17:42:41 UTC (rev 9712)
+++ icecast/trunk/icecast/src/cfgfile.h	2005-08-07 23:01:04 UTC (rev 9713)
@@ -20,9 +20,11 @@
 
 #define MAX_YP_DIRECTORIES 25
 
+struct _mount_proxy;
 
 #include "thread/thread.h"
 #include "avl/avl.h"
+#include "auth.h"
 #include "global.h"
 
 typedef struct ice_config_dir_tag
@@ -63,6 +65,7 @@
     int mp3_meta_interval; /* outgoing per-stream metadata interval */
 
     char *auth_type; /* Authentication type */
+    struct auth_tag *auth;
     char *cluster_password;
     config_options_t *auth_options; /* Options for this type */
     char *on_connect;

Modified: icecast/trunk/icecast/src/client.c
===================================================================
--- icecast/trunk/icecast/src/client.c	2005-08-07 17:42:41 UTC (rev 9712)
+++ icecast/trunk/icecast/src/client.c	2005-08-07 23:01:04 UTC (rev 9713)
@@ -72,6 +72,10 @@
 {
     if (client == NULL)
         return;
+
+    if (release_client (client))
+        return;
+
     /* write log entry if ip is set (some things don't set it, like outgoing 
      * slave requests
      */

Modified: icecast/trunk/icecast/src/client.h
===================================================================
--- icecast/trunk/icecast/src/client.h	2005-08-07 17:42:41 UTC (rev 9712)
+++ icecast/trunk/icecast/src/client.h	2005-08-07 23:01:04 UTC (rev 9713)
@@ -32,6 +32,9 @@
     /* http response code for this client */
     int respcode;
 
+    /* auth completed, 0 not yet, 1 passed */
+    int authenticated;
+
     /* is client getting intro data */
     long intro_offset;
 
@@ -41,9 +44,15 @@
     /* position in first buffer */
     unsigned long pos;
 
+    /* auth used for this client */
+    struct auth_tag *auth;
+
     /* Client username, if authenticated */
     char *username;
 
+    /* Client password, if authenticated */
+    char *password;
+
     /* Format-handler-specific data for this client */
     void *format_data;
 

Modified: icecast/trunk/icecast/src/connection.c
===================================================================
--- icecast/trunk/icecast/src/connection.c	2005-08-07 17:42:41 UTC (rev 9712)
+++ icecast/trunk/icecast/src/connection.c	2005-08-07 23:01:04 UTC (rev 9713)
@@ -758,7 +758,6 @@
 
 static void _handle_get_request (client_t *client, char *passed_uri)
 {
-    source_t *source;
     int fileserve;
     int port;
     int i;
@@ -766,7 +765,6 @@
     int serverport = 0;
     aliases *alias;
     ice_config_t *config;
-    int ret;
     char *uri = passed_uri;
 
     config = config_get_config();
@@ -831,95 +829,14 @@
         return;
     }
 
-    avl_tree_rlock(global.source_tree);
-    source = source_find_mount(uri);
-    if (source) {
-        DEBUG0("Source found for client");
+    sock_set_blocking(client->con->sock, SOCK_NONBLOCK);
+    sock_set_nodelay(client->con->sock);
 
-        /* The source may not be the requested source - it might have gone
-         * via one or more fallbacks. We only reject it for no-mount if it's
-         * the originally requested source
-         */
-        if(strcmp(uri, source->mount) == 0 && source->no_mount) {
-            avl_tree_unlock(global.source_tree);
-            client_send_404(client, "This mount is unavailable.");
-            if (uri != passed_uri) free (uri);
-            return;
-        }
-        if (source->running == 0 && source->on_demand == 0)
-        {
-            avl_tree_unlock(global.source_tree);
-            DEBUG0("inactive source, client dropped");
-            client_send_404(client, "This mount is unavailable.");
-            if (uri != passed_uri) free (uri);
-            return;
-        }
+    client->write_to_client = format_generic_write_to_client;
+    client->check_buffer = format_check_http_buffer;
+    client->refbuf = refbuf_new (PER_CLIENT_REFBUF_SIZE);
 
-        /* Check for any required authentication first */
-        if(source->authenticator != NULL) {
-            ret = auth_check_client(source, client);
-            if(ret != AUTH_OK) {
-                avl_tree_unlock(global.source_tree);
-                if (ret == AUTH_FORBIDDEN) {
-                    INFO1("Client attempted to log multiple times to source "
-                        "(\"%s\")", uri);
-                    client_send_403(client);
-                }
-                else {
-                /* If not FORBIDDEN, default to 401 */
-                    INFO1("Client attempted to log in to source (\"%s\")with "
-                        "incorrect or missing password", uri);
-                    client_send_401(client);
-                }
-                if (uri != passed_uri) free (uri);
-                return;
-            }
-        }
-
-        global_lock();
-        /* Early-out for per-source max listeners. This gets checked again
-         * by the source itself, later. This route gives a useful message to
-         * the client, also.
-         */
-        if (source->max_listeners != -1 && 
-                source->listeners >= (unsigned long)source->max_listeners) 
-        {
-            global_unlock();
-            avl_tree_unlock(global.source_tree);
-            client_send_404(client, 
-                    "Too many clients on this mountpoint. Try again later.");
-            if (uri != passed_uri) free (uri);
-            return;
-        }
-        global_unlock();
-                        
-        sock_set_blocking(client->con->sock, SOCK_NONBLOCK);
-        sock_set_nodelay(client->con->sock);
-
-        client->write_to_client = format_generic_write_to_client;
-        client->check_buffer = format_check_http_buffer;
-        client->refbuf = refbuf_new (PER_CLIENT_REFBUF_SIZE);
-
-        avl_tree_wlock(source->pending_tree);
-        avl_insert(source->pending_tree, (void *)client);
-        avl_tree_unlock(source->pending_tree);
-        stats_event_inc (NULL, "listener_connections");
-
-        if (source->running == 0 && source->on_demand)
-        {
-            /* enable on-demand relay to start, wake up the slave thread */
-            DEBUG0("kicking off on-demand relay");
-            source->on_demand_req = 1;
-            slave_rescan ();
-        }
-    }
-                    
-    avl_tree_unlock(global.source_tree);
-                    
-    if (!source) {
-        DEBUG0("Source not found for client");
-        client_send_404(client, "The source you requested could not be found.");
-    }
+    add_client (uri, client);
     if (uri != passed_uri) free (uri);
 }
 

Modified: icecast/trunk/icecast/src/main.c
===================================================================
--- icecast/trunk/icecast/src/main.c	2005-08-07 17:42:41 UTC (rev 9712)
+++ icecast/trunk/icecast/src/main.c	2005-08-07 23:01:04 UTC (rev 9713)
@@ -505,6 +505,7 @@
 
     /* Do this after logging init */
     slave_initialize();
+    auth_initialise ();
 
     _server_proc();
 

Modified: icecast/trunk/icecast/src/source.c
===================================================================
--- icecast/trunk/icecast/src/source.c	2005-08-07 17:42:41 UTC (rev 9712)
+++ icecast/trunk/icecast/src/source.c	2005-08-07 23:01:04 UTC (rev 9713)
@@ -245,7 +245,6 @@
     source->queue_size = 0;
     source->queue_size_limit = 0;
     source->listeners = 0;
-    source->no_mount = 0;
     source->shoutcast_compat = 0;
     source->max_listeners = -1;
     source->hidden = 0;
@@ -614,6 +613,7 @@
     {
         if (mountinfo->on_connect)
             source_run_script (mountinfo->on_connect, source->mount);
+        auth_stream_start (mountinfo, source->mount);
     }
     config_release_config();
 
@@ -817,6 +817,7 @@
     {
         if (mountinfo->on_disconnect)
             source_run_script (mountinfo->on_disconnect, source->mount);
+        auth_stream_end (mountinfo, source->mount);
     }
     config_release_config();
 
@@ -930,7 +931,6 @@
     {
         source->max_listeners = mountinfo->max_listeners;
         source->fallback_override = mountinfo->fallback_override;
-        source->no_mount = mountinfo->no_mount;
         source->hidden = mountinfo->hidden;
     }
 
@@ -1062,6 +1062,11 @@
     if (mountinfo && mountinfo->subtype)
         stats_event (source->mount, "subtype", mountinfo->subtype);
 
+    if (mountinfo && mountinfo->auth)
+        stats_event (source->mount, "authenticator", mountinfo->auth->type);
+    else
+        stats_event (source->mount, "authenticator", NULL);
+
     if (mountinfo && mountinfo->fallback_mount)
     {
         char *mount = source->fallback_mount;
@@ -1071,13 +1076,6 @@
     else
         source->fallback_mount = NULL;
 
-    if (mountinfo && mountinfo->auth_type != NULL && source->authenticator == NULL)
-    {
-        source->authenticator = auth_get_authenticator(
-                mountinfo->auth_type, mountinfo->auth_options);
-        stats_event(source->mount, "authenticator", mountinfo->auth_type);
-    }
-
     if (mountinfo && mountinfo->dumpfile)
     {
         char *filename = source->dumpfilename;

Modified: icecast/trunk/icecast/src/source.h
===================================================================
--- icecast/trunk/icecast/src/source.h	2005-08-07 17:42:41 UTC (rev 9712)
+++ icecast/trunk/icecast/src/source.h	2005-08-07 23:01:04 UTC (rev 9713)
@@ -20,8 +20,6 @@
 
 #include <stdio.h>
 
-struct auth_tag;
-
 typedef struct source_tag
 {
     client_t *client;
@@ -55,9 +53,7 @@
     unsigned long listeners;
     long max_listeners;
     int yp_public;
-    struct auth_tag *authenticator;
     int fallback_override;
-    int no_mount;
     int shoutcast_compat;
 
     /* per source burst handling for connecting clients */



More information about the commits mailing list