[xiph-commits] r7096 - icecast/branches/kh/icecast/src

j at dactyl.lonelymoon.com j
Sun Jul 11 11:13:10 PDT 2004


Author: j
Date: Sun Jul 11 11:13:10 2004
New Revision: 7096

Added:
icecast/branches/kh/icecast/src/auth_cmd.c
icecast/branches/kh/icecast/src/auth_cmd.h
icecast/branches/kh/icecast/src/auth_htpasswd.c
icecast/branches/kh/icecast/src/auth_htpasswd.h
icecast/branches/kh/icecast/src/auth_url.c
icecast/branches/kh/icecast/src/auth_url.h
icecast/branches/kh/icecast/src/format_ogg.c
icecast/branches/kh/icecast/src/format_ogg.h
Log:
reimport2: add new files

Added: icecast/branches/kh/icecast/src/auth_cmd.c
===================================================================
--- icecast/branches/kh/icecast/src/auth_cmd.c	2004-07-11 18:09:05 UTC (rev 7095)
+++ icecast/branches/kh/icecast/src/auth_cmd.c	2004-07-11 18:13:09 UTC (rev 7096)
@@ -0,0 +1,185 @@
+/* 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 via command functions
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#ifndef WIN32
+#include <sys/wait.h>
+#endif
+
+#include "auth.h"
+#include "source.h"
+#include "client.h"
+#include "cfgfile.h"
+#include "httpp/httpp.h"
+
+#include "logging.h"
+#define CATMODULE "auth_cmd"
+
+typedef struct {
+    char *filename;
+} auth_cmd;
+
+typedef struct {
+   char *mount;
+   client_t *client;
+   char *cmd;
+} auth_client;
+
+
+static void cmd_clear(auth_t *self)
+{
+    auth_cmd *cmd = self->state;
+    free(cmd->filename);
+    free(cmd);
+}
+
+static void *auth_cmd_thread (void *arg)
+{
+    auth_client *auth_user = arg;
+    int fd[2];
+    int ok = 0;
+    int send_fail = 404;
+    pid_t pid;
+    client_t *client = auth_user->client;
+    int status, len;
+    char str[256];
+
+    DEBUG0("starting auth thread");
+    if (pipe (fd) == 0)
+    {
+        pid = fork();
+        switch (pid)
+        {
+            case 0: /* child */
+                dup2 (fd[0], fileno(stdin));
+                close (fd[0]);
+                close (fd[1]);
+                execl (auth_user->cmd, auth_user->cmd, NULL);
+                exit (EXIT_FAILURE);
+            case -1:
+                break;
+            default: /* parent */
+                close (fd[0]);
+                len = snprintf (str, sizeof(str), "%s\n%s\n%s\n",
+                        auth_user->mount, client->username, client->password);
+                write (fd[1], str, len);
+                close (fd[1]);
+                DEBUG1 ("Waiting on pid %ld", (long)pid);
+                if (waitpid (pid, &status, 0) < 0)
+                {
+                    DEBUG1("waitpid error %s", strerror(errno));
+                    break;
+                }
+                if (WIFEXITED (status))
+                {
+                    DEBUG1("command exited normally with %d", WEXITSTATUS (status));
+                    if (WEXITSTATUS(status) == 0)
+                        ok = 1;
+                    else
+                        send_fail = 401;
+                }
+                break;
+        }
+        /* try to add authenticated client */
+        if (ok)
+        {
+            source_t *source;
+            send_fail = 0;
+            avl_tree_unlock (global.source_tree);
+            source = source_find_mount (auth_user->mount);
+            if (source)
+            {
+                thread_mutex_lock (&source->lock);
+                if (source->running)
+                    add_authenticated_client (source, auth_user->client);
+                else
+                    send_fail = 404;
+                thread_mutex_unlock (&source->lock);
+            }
+            avl_tree_unlock (global.source_tree);
+        }
+    }
+    if (send_fail == 404)
+        client_send_404 (client, "Mount not available");
+    if (send_fail == 401)
+        client_send_401 (client);
+    free (auth_user->mount);
+    free (auth_user->cmd);
+    free (auth_user);
+    return NULL;
+}
+
+
+static auth_result auth_cmd_client (source_t *source, client_t *client)
+{
+    auth_client *auth_user = calloc (1, sizeof (auth_client));
+    auth_cmd *cmd = source->authenticator->state;
+
+    if (auth_user == NULL)
+        return AUTH_FAILED;
+
+    auth_user->cmd = strdup (cmd->filename);
+    auth_user->mount = strdup (source->mount);
+    auth_user->client = client;
+    thread_create("Auth by command thread", auth_cmd_thread, auth_user, THREAD_DETACHED);
+    return AUTH_OK;
+}
+
+static auth_result auth_cmd_adduser(auth_t *auth, const char *username, const char *password)
+{
+    return AUTH_FAILED;
+}
+
+static auth_result auth_cmd_deleteuser (auth_t *auth, const char *username)
+{
+    return AUTH_FAILED;
+}
+
+static auth_result auth_cmd_listuser (auth_t *auth, xmlNodePtr srcnode)
+{
+    return AUTH_FAILED;
+}
+
+auth_t *auth_get_cmd_auth (config_options_t *options)
+{
+    auth_t *authenticator = calloc(1, sizeof(auth_t));
+    auth_cmd *state;
+
+    authenticator->authenticate = auth_cmd_client;
+    authenticator->free = cmd_clear;
+    authenticator->adduser = auth_cmd_adduser;
+    authenticator->deleteuser = auth_cmd_deleteuser;
+    authenticator->listuser = auth_cmd_listuser;
+
+    state = calloc(1, sizeof(auth_cmd));
+
+    while(options) {
+        if(!strcmp(options->name, "filename"))
+            state->filename = strdup(options->value);
+        options = options->next;
+    }
+    authenticator->state = state;
+    INFO0("external command based authentication setup");
+    return authenticator;
+}
+

Added: icecast/branches/kh/icecast/src/auth_cmd.h
===================================================================
--- icecast/branches/kh/icecast/src/auth_cmd.h	2004-07-11 18:09:05 UTC (rev 7095)
+++ icecast/branches/kh/icecast/src/auth_cmd.h	2004-07-11 18:13:09 UTC (rev 7096)
@@ -0,0 +1,27 @@
+/* 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_CMD_H__
+#define __AUTH_CMD_H__
+
+#include "source.h"
+#include "client.h"
+#include "config.h"
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+auth_t *auth_get_cmd_auth (config_options_t *options);
+
+#endif
+
+

Added: icecast/branches/kh/icecast/src/auth_htpasswd.c
===================================================================
--- icecast/branches/kh/icecast/src/auth_htpasswd.c	2004-07-11 18:09:05 UTC (rev 7095)
+++ icecast/branches/kh/icecast/src/auth_htpasswd.c	2004-07-11 18:13:09 UTC (rev 7096)
@@ -0,0 +1,363 @@
+/* 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 "auth.h"
+#include "source.h"
+#include "client.h"
+#include "cfgfile.h"
+#include "httpp/httpp.h"
+#include "md5.h"
+
+#include "logging.h"
+#define CATMODULE "auth"
+
+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);
+
+typedef struct {
+    char *filename;
+    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);
+}
+
+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
+
+/* Not efficient; opens and scans the entire file for every request */
+static auth_result htpasswd_auth(source_t *source, client_t *client)
+{
+    auth_t *auth = source->authenticator;
+    htpasswd_auth_state *state = auth->state;
+    FILE *passwdfile = fopen(state->filename, "rb");
+    char line[MAX_LINE_LEN];
+    char *sep;
+
+    thread_rwlock_rlock(&state->file_rwlock);
+    if(passwdfile == NULL) {
+        WARN2("Failed to open authentication database \"%s\": %s",
+                state->filename, strerror(errno));
+        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 seperator in line");
+            continue;
+        }
+
+        *sep = 0;
+        if(!strcmp(client->username, line)) {
+            /* Found our user, now: does the hash of password match hash? */
+            char *hash = sep+1;
+            char *hashed_password = get_hash(client->password, strlen(client->password));
+            if(!strcmp(hash, hashed_password)) {
+                fclose(passwdfile);
+                free(hashed_password);
+                thread_rwlock_unlock(&state->file_rwlock);
+                add_authenticated_client (source, client);
+                return AUTH_OK;
+            }
+            free(hashed_password);
+            /* We don't keep searching through the file */
+            break;
+        }
+    }
+
+    fclose(passwdfile);
+
+    thread_rwlock_unlock(&state->file_rwlock);
+    return AUTH_FAILED;
+}
+
+auth_t *auth_get_htpasswd_auth(config_options_t *options)
+{
+    auth_t *authenticator = calloc(1, sizeof(auth_t));
+    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);
+        free(authenticator);
+        ERROR0("No filename given in options for authenticator.");
+        return NULL;
+    }
+
+    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, const char *username)
+{
+    FILE *passwdfile;
+    htpasswd_auth_state *state;
+    int ret = AUTH_OK;
+    char line[MAX_LINE_LEN];
+    char *sep;
+
+    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;
+    }
+    while(get_line(passwdfile, line, MAX_LINE_LEN)) {
+        if(!line[0] || line[0] == '#')
+            continue;
+        sep = strchr(line, ':');
+        if(sep == NULL) {
+            DEBUG0("No seperator in line");
+            continue;
+        }
+        *sep = 0;
+        if (!strcmp(username, line)) {
+            /* We found the user, break out of the loop */
+            ret = AUTH_USEREXISTS;
+            break;
+        }
+    }
+
+    fclose(passwdfile);
+    return ret;
+
+}
+
+
+static int _auth_htpasswd_adduser(auth_t *auth, const char *username, const char *password)
+{
+    FILE *passwdfile;
+    char *hashed_password = NULL;
+    htpasswd_auth_state *state;
+
+    if (auth_htpasswd_existing_user(auth, username) == AUTH_USEREXISTS) {
+        return AUTH_USEREXISTS;
+    }
+    state = auth->state;
+    passwdfile = fopen(state->filename, "ab");
+
+    if(passwdfile == NULL) {
+        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);
+    return AUTH_USERADDED;
+}
+
+
+static auth_result htpasswd_adduser (auth_t *auth, const char *username, const char *password)
+{
+    auth_result ret = 0;
+    htpasswd_auth_state *state;
+
+    state = auth->state;
+    thread_rwlock_wlock (&state->file_rwlock);
+    ret = _auth_htpasswd_adduser (auth, username, password);
+    thread_rwlock_unlock (&state->file_rwlock);
+
+    return ret;
+}
+
+
+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;
+
+    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;
+    }
+    tmpfile_len = strlen(state->filename) + 6;
+    tmpfile = calloc(1, tmpfile_len);
+    sprintf(tmpfile, ".%s.tmp", state->filename);
+
+    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);
+        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 seperator 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);
+
+    return AUTH_USERDELETED;
+}
+
+
+static auth_result htpasswd_userlist(auth_t *auth, xmlNodePtr srcnode)
+{
+    htpasswd_auth_state *state;
+    FILE *passwdfile;
+    char line[MAX_LINE_LEN];
+    char *sep;
+    char *passwd;
+    xmlNodePtr newnode;
+
+    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;
+    }
+
+    while(get_line(passwdfile, line, MAX_LINE_LEN)) {
+        if(!line[0] || line[0] == '#')
+            continue;
+
+        sep = strchr(line, ':');
+        if(sep == NULL) {
+            DEBUG0("No seperator in line");
+            continue;
+        }
+
+        *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;
+}
+

Added: icecast/branches/kh/icecast/src/auth_htpasswd.h
===================================================================
--- icecast/branches/kh/icecast/src/auth_htpasswd.h	2004-07-11 18:09:05 UTC (rev 7095)
+++ icecast/branches/kh/icecast/src/auth_htpasswd.h	2004-07-11 18:13:09 UTC (rev 7096)
@@ -0,0 +1,27 @@
+/* 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__
+
+#include "source.h"
+#include "client.h"
+#include "config.h"
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+auth_t *auth_get_htpasswd_auth(config_options_t *options);
+
+#endif
+
+

Added: icecast/branches/kh/icecast/src/auth_url.c
===================================================================
--- icecast/branches/kh/icecast/src/auth_url.c	2004-07-11 18:09:05 UTC (rev 7095)
+++ icecast/branches/kh/icecast/src/auth_url.c	2004-07-11 18:13:09 UTC (rev 7096)
@@ -0,0 +1,323 @@
+/* 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 via URL functions
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#ifndef WIN32
+#include <sys/wait.h>
+#endif
+
+#include <curl/curl.h>
+
+#include "auth.h"
+#include "source.h"
+#include "client.h"
+#include "cfgfile.h"
+#include "httpp/httpp.h"
+
+#include "logging.h"
+#define CATMODULE "auth_url"
+
+typedef struct {
+    char *addurl;
+    char *removeurl;
+    char *username;
+    char *password;
+} auth_url;
+
+typedef struct {
+    char *mount;
+    client_t *client;
+    CURL *auth_server;
+    char *addurl;
+    char *removeurl;
+    int authenticated;
+    char *username;
+    char *password;
+} auth_client;
+
+
+static void auth_url_clear(auth_t *self)
+{
+    auth_url *url = self->state;
+    free(url->username);
+    free(url->password);
+    free(url->removeurl);
+    free(url->addurl);
+    free(url);
+}
+
+
+static int handle_returned_header (void *ptr, size_t size, size_t nmemb, void *stream)
+{
+    auth_client *auth_user = stream;
+    unsigned bytes = size * nmemb;
+
+    if (strncmp (ptr, "icecast-auth-user: 1", 20) == 0)
+        auth_user->authenticated = 1;
+
+    return (int)bytes;
+}
+
+/* capture returned data, but don't do anything with it */
+static int handle_returned_data (void *ptr, size_t size, size_t nmemb, void *stream)
+{
+    return (int)(size*nmemb);
+}
+
+
+static void *auth_removeurl_thread (void *arg)
+{
+    auth_client *auth_user = arg;
+    client_t *client = auth_user->client;
+    CURL *handle;
+    char post[1024];
+
+    if (auth_user->removeurl)
+    {
+        time_t duration = time(NULL) - client->con->con_time;
+        char *username, *password;
+
+        DEBUG0("starting auth thread");
+        username = util_url_escape (client->username);
+        password = util_url_escape (client->password);
+        snprintf (post, 1024,"id=%ld&mount=%s&user=%s&pass=%s&duration=%ld",
+                client->con->id, auth_user->mount, username, password, duration);
+        free (username);
+        free (password);
+        handle = curl_easy_init ();
+        if (handle)
+        {
+            int res;
+            char errormsg [CURL_ERROR_SIZE];
+            char *userpass = NULL;
+
+            if (auth_user->username && auth_user->password)
+            {
+                unsigned len = strlen (auth_user->username) + strlen (auth_user->password) + 1;
+                userpass = malloc (len);
+                snprintf (userpass, len, "%s:%s", auth_user->username, auth_user->password);
+                curl_easy_setopt (handle, CURLOPT_USERPWD, userpass);
+            }
+            curl_easy_setopt (handle, CURLOPT_URL, auth_user->removeurl);
+            curl_easy_setopt (handle, CURLOPT_POSTFIELDS, post);
+            curl_easy_setopt (handle, CURLOPT_HEADERFUNCTION, handle_returned_header);
+            curl_easy_setopt (handle, CURLOPT_WRITEFUNCTION, handle_returned_data);
+            curl_easy_setopt (handle, CURLOPT_WRITEHEADER, auth_user);
+            curl_easy_setopt (handle, CURLOPT_WRITEDATA, handle);
+            curl_easy_setopt (handle, CURLOPT_NOSIGNAL, 1L);
+            curl_easy_setopt (handle, CURLOPT_TIMEOUT, 15L);
+            curl_easy_setopt (handle, CURLOPT_ERRORBUFFER, errormsg);
+            res = curl_easy_perform (handle);
+            curl_easy_cleanup (handle);
+            free (userpass);
+
+            if (res)
+                WARN2 ("auth to server %s failed with %s", auth_user->removeurl, errormsg);
+        }
+    }
+    auth_close_client (client);
+
+    free (auth_user->username);
+    free (auth_user->password);
+    free (auth_user->mount);
+    free (auth_user->removeurl);
+    free (auth_user);
+    return NULL;
+}
+
+
+static void *auth_url_thread (void *arg)
+{
+    auth_client *auth_user = arg;
+    client_t *client = auth_user->client;
+    int res;
+    char *agent, *user_agent, *username, *password;
+    CURL *handle;
+    char post[1024];
+
+    DEBUG0("starting auth thread");
+    agent = httpp_getvar (client->parser, "user-agent");
+    if (agent == NULL)
+        agent = "-";
+    user_agent = util_url_escape (agent);
+    username  = util_url_escape (client->username);
+    password  = util_url_escape (client->password);
+    snprintf (post, 1024,"id=%ld&mount=%s&user=%s&pass=%s&ip=%s&agent=%s",
+            client->con->id, auth_user->mount, username, password,
+            client->con->ip, user_agent);
+    free (user_agent);
+    free (username);
+    free (password);
+
+    handle = curl_easy_init ();
+    if (handle)
+    {
+        char errormsg [CURL_ERROR_SIZE];
+        char *userpass = NULL;
+
+        if (auth_user->username && auth_user->password)
+        {
+            unsigned len = strlen (auth_user->username) + strlen (auth_user->password) + 1;
+            userpass = malloc (len);
+            snprintf (userpass, len, "%s:%s", auth_user->username, auth_user->password);
+            curl_easy_setopt (handle, CURLOPT_USERPWD, userpass);
+        }
+
+        curl_easy_setopt (handle, CURLOPT_URL, auth_user->addurl);
+        curl_easy_setopt (handle, CURLOPT_POSTFIELDS, post);
+        curl_easy_setopt (handle, CURLOPT_HEADERFUNCTION, handle_returned_header);
+        curl_easy_setopt (handle, CURLOPT_WRITEFUNCTION, handle_returned_data);
+        curl_easy_setopt (handle, CURLOPT_WRITEHEADER, auth_user);
+        curl_easy_setopt (handle, CURLOPT_WRITEDATA, handle);
+        curl_easy_setopt (handle, CURLOPT_NOSIGNAL, 1L);
+        curl_easy_setopt (handle, CURLOPT_TIMEOUT, 15L);
+        curl_easy_setopt (handle, CURLOPT_ERRORBUFFER, errormsg);
+        res = curl_easy_perform (handle);
+        curl_easy_cleanup (handle);
+        free (userpass);
+
+        if (res)
+        {
+            WARN2 ("auth to server %s failed with %s", auth_user->addurl, errormsg);
+            auth_close_client (client);
+        }
+        else
+        {
+            /* we received a response, lets see what it is */
+            if (auth_user->authenticated)
+            {
+                if (auth_postprocess_client (auth_user->mount, client) < 0)
+                {
+                    /* do cleanup, and exit as the remove does cleanup as well */
+                    free (auth_user->addurl);
+                    auth_removeurl_thread (auth_user);
+                    return NULL;
+                }
+            }
+            else
+            {
+                DEBUG0 ("client authentication failed");
+                auth_close_client (client);
+            }
+        }
+    }
+    else
+        auth_close_client (client);
+
+    free (auth_user->username);
+    free (auth_user->password);
+    free (auth_user->mount);
+    free (auth_user->addurl);
+    free (auth_user->removeurl);
+    free (auth_user);
+
+    return NULL;
+}
+
+
+static void auth_remove_url_client (source_t *source, client_t *client)
+{
+    auth_client *auth_user = calloc (1, sizeof (auth_client));
+    auth_url *urlinfo = source->authenticator->state;
+
+    if (auth_user == NULL)
+        return;
+
+    if (urlinfo->username)
+        auth_user->username = strdup (urlinfo->username);
+    if (urlinfo->password)
+        auth_user->password = strdup (urlinfo->password);
+    if (urlinfo->removeurl)
+        auth_user->removeurl = strdup (urlinfo->removeurl);
+    auth_user->mount = strdup (source->mount);
+    auth_user->client = client;
+    thread_create("AuthRemove by URL thread", auth_removeurl_thread,
+            auth_user, THREAD_DETACHED);
+}
+
+
+static auth_result auth_url_client (source_t *source, client_t *client)
+{
+    auth_client *auth_user = calloc (1, sizeof (auth_client));
+    auth_url *urlinfo = source->authenticator->state;
+
+    if (auth_user == NULL)
+        return AUTH_FAILED;
+
+    if (urlinfo->username)
+        auth_user->username = strdup (urlinfo->username);
+    if (urlinfo->password)
+        auth_user->password = strdup (urlinfo->password);
+    auth_user->addurl = strdup (urlinfo->addurl);
+    auth_user->removeurl = strdup (urlinfo->removeurl);
+    auth_user->mount = strdup (source->mount);
+    auth_user->client = client;
+    thread_create("Auth by URL thread", auth_url_thread, auth_user, THREAD_DETACHED);
+    return AUTH_OK;
+}
+
+static auth_result auth_url_adduser(auth_t *auth, const char *username, const char *password)
+{
+    return AUTH_FAILED;
+}
+
+static auth_result auth_url_deleteuser (auth_t *auth, const char *username)
+{
+    return AUTH_FAILED;
+}
+
+static auth_result auth_url_listuser (auth_t *auth, xmlNodePtr srcnode)
+{
+    return AUTH_FAILED;
+}
+
+auth_t *auth_get_url_auth (config_options_t *options)
+{
+    auth_t *authenticator = calloc(1, sizeof(auth_t));
+    auth_url *state;
+
+    authenticator->authenticate = auth_url_client;
+    authenticator->free = auth_url_clear;
+    authenticator->adduser = auth_url_adduser;
+    authenticator->deleteuser = auth_url_deleteuser;
+    authenticator->listuser = auth_url_listuser;
+    authenticator->release_client = auth_remove_url_client;
+
+    state = calloc(1, sizeof(auth_url));
+
+    while(options) {
+        if(!strcmp(options->name, "username"))
+            state->username = strdup(options->value);
+        if(!strcmp(options->name, "password"))
+            state->password = strdup(options->value);
+        if(!strcmp(options->name, "add"))
+            state->addurl = strdup(options->value);
+        if(!strcmp(options->name, "remove"))
+            state->removeurl = strdup(options->value);
+        options = options->next;
+    }
+    authenticator->state = state;
+    INFO0("URL based authentication setup");
+    return authenticator;
+}
+

Added: icecast/branches/kh/icecast/src/auth_url.h
===================================================================
--- icecast/branches/kh/icecast/src/auth_url.h	2004-07-11 18:09:05 UTC (rev 7095)
+++ icecast/branches/kh/icecast/src/auth_url.h	2004-07-11 18:13:09 UTC (rev 7096)
@@ -0,0 +1,28 @@
+/* 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_URL_H__
+#define __AUTH_URL_H__
+#ifdef HAVE_AUTH_URL
+
+#include "source.h"
+#include "client.h"
+#include "config.h"
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+auth_t *auth_get_url_auth (config_options_t *options);
+#endif
+#endif
+
+

Added: icecast/branches/kh/icecast/src/format_ogg.c
===================================================================
--- icecast/branches/kh/icecast/src/format_ogg.c	2004-07-11 18:09:05 UTC (rev 7095)
+++ icecast/branches/kh/icecast/src/format_ogg.c	2004-07-11 18:13:09 UTC (rev 7096)
@@ -0,0 +1,798 @@
+/* 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).
+ */
+
+/* format_ogg.c
+**
+** format plugin for Ogg
+**
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <ogg/ogg.h>
+#include <vorbis/codec.h>
+#ifdef HAVE_THEORA
+#include <theora/theora.h>
+#endif
+#ifdef HAVE_SPEEX
+#include <speex_header.h>
+#endif
+
+#include "refbuf.h"
+#include "source.h"
+#include "client.h"
+
+#include "stats.h"
+#include "format.h"
+
+#define CATMODULE "format-ogg"
+#include "logging.h"
+
+struct _ogg_state_tag;
+
+/* per codec/logical structure */
+typedef struct _ogg_codec_tag
+{
+    ogg_stream_state os;
+    unsigned headers;
+    void *specific;
+    struct _ogg_state_tag *feed;
+    struct _ogg_codec_tag *next;
+    refbuf_t *(*process_page)(struct _ogg_codec_tag *codec, ogg_page *page);
+    void (*codec_free)(struct _ogg_codec_tag *codec);
+    refbuf_t        *possible_start;
+} ogg_codec_t;
+
+
+typedef struct _ogg_state_tag
+{
+    char *mount;
+    ogg_sync_state oy;
+
+    ogg_codec_t *codecs;
+    char *artist;
+    char *title;
+    int send_yp_info;
+    refbuf_t *file_headers;
+    refbuf_t *header_pages;
+    refbuf_t *header_pages_tail;
+    int headers_completed;
+    long bitrate;
+    ogg_codec_t *codec_sync;
+} ogg_state_t;
+
+
+struct client_vorbis
+{
+    refbuf_t *headers;
+    refbuf_t *header_page;
+    unsigned pos;
+    int headers_sent;
+};
+
+
+void refbuf_page_prerelease (struct source_tag *source, refbuf_t *refbuf)
+{
+    ogg_state_t *ogg_info = source->format->_state;
+
+    /* only theora will be marking refbufs as sync that are behind in the
+     * queue, here we just make sure that it isn't the one we are going to
+     * remove. */
+    if (ogg_info->codec_sync)
+    {
+        if (ogg_info->codec_sync->possible_start == refbuf)
+            ogg_info->codec_sync->possible_start = NULL;
+    }
+}
+
+
+static refbuf_t *make_refbuf_with_page (ogg_page *page)
+{
+    refbuf_t *refbuf = refbuf_new (page->header_len + page->body_len);
+
+    refbuf->idx = ogg_page_pageno (page);
+    memcpy (refbuf->data, page->header, page->header_len);
+    memcpy (refbuf->data+page->header_len, page->body, page->body_len);
+    refbuf->len = page->header_len + page->body_len;
+    return refbuf;
+}
+
+
+void format_ogg_attach_header (ogg_state_t *ogg_info, ogg_page *page)
+{
+    refbuf_t *refbuf = make_refbuf_with_page (page);
+
+    if (ogg_info->header_pages_tail)
+        ogg_info->header_pages_tail->next = refbuf;
+    ogg_info->header_pages_tail = refbuf;
+
+    if (ogg_info->header_pages == NULL)
+        ogg_info->header_pages = refbuf;
+}
+
+
+
+/**** vorbis ****/
+typedef struct _vorbis_codec_tag
+{
+    vorbis_info vi;
+    vorbis_comment vc;
+} vorbis_codec_t;
+
+
+static void vorbis_codec_free (ogg_codec_t *codec)
+{
+    vorbis_codec_t *vorbis = codec->specific;
+
+    codec->feed->artist = NULL;
+    codec->feed->title = NULL;
+    vorbis_info_clear (&vorbis->vi);
+    vorbis_comment_clear (&vorbis->vc);
+    ogg_stream_clear (&codec->os);
+    free (vorbis);
+    free (codec);
+}
+
+static refbuf_t *process_vorbis_page (ogg_codec_t *codec, ogg_page *page)
+{
+    refbuf_t *refbuf;
+
+    if (ogg_page_granulepos (page) == 0)
+    {
+        vorbis_codec_t *vorbis = codec->specific;
+        ogg_packet packet;
+
+        ogg_stream_pagein (&codec->os, page);
+        while (ogg_stream_packetout (&codec->os, &packet) > 0)
+        {
+           if (vorbis_synthesis_headerin (&vorbis->vi, &vorbis->vc, &packet) < 0)
+           {
+               /* set some error code */
+               return NULL;
+           }
+           codec->headers++;
+        }
+        /* add header page to associated list */
+        format_ogg_attach_header (codec->feed, page);
+        if (codec->headers == 3)
+        {
+            ogg_state_t *ogg_info = codec->feed;
+
+            ogg_info->send_yp_info = 1;
+            ogg_info->title  = vorbis_comment_query (&vorbis->vc, "TITLE", 0);
+            ogg_info->artist = vorbis_comment_query (&vorbis->vc, "ARTIST", 0);
+            ogg_info->bitrate += vorbis->vi.bitrate_nominal;
+            stats_event_args (codec->feed->mount, "audio-samplerate", "%ld", (long)vorbis->vi.rate);
+            stats_event_args (codec->feed->mount, "audio-channels", "%ld", (long)vorbis->vi.channels);
+            stats_event_args (codec->feed->mount, "audio-bitrate", "%ld", (long)vorbis->vi.bitrate_nominal);
+        }
+        return NULL;
+    }
+    refbuf = make_refbuf_with_page (page);
+    if (codec->feed->codec_sync == NULL)
+        refbuf->sync_point = 1;
+    return refbuf;
+}
+
+
+static ogg_codec_t *initial_vorbis_page (ogg_page *page)
+{
+    ogg_codec_t *codec = calloc (1, sizeof (ogg_codec_t));
+    ogg_packet packet;
+
+    vorbis_codec_t *vorbis_codec = calloc (1, sizeof (vorbis_codec_t));
+
+    ogg_stream_init (&codec->os, ogg_page_serialno (page));
+    ogg_stream_pagein (&codec->os, page);
+
+    vorbis_info_init (&vorbis_codec->vi);
+    vorbis_comment_init (&vorbis_codec->vc);
+
+    ogg_stream_packetout (&codec->os, &packet);
+
+    if (vorbis_synthesis_headerin (&vorbis_codec->vi, &vorbis_codec->vc, &packet) < 0)
+    {
+        ogg_stream_clear (&codec->os);
+        vorbis_info_clear (&vorbis_codec->vi);
+        vorbis_comment_clear (&vorbis_codec->vc);
+        free (vorbis_codec);
+        free (codec);
+        return NULL;
+    }
+    INFO0 ("seen initial vorbis header");
+    codec->specific = vorbis_codec;
+    codec->process_page = process_vorbis_page;
+    codec->codec_free = vorbis_codec_free;
+    codec->headers = 1;
+    return codec;
+}
+
+#ifdef HAVE_SPEEX
+
+static void speex_codec_free (ogg_codec_t *codec)
+{
+    ogg_stream_clear (&codec->os);
+    free (codec);
+}
+
+
+static refbuf_t *process_speex_page (ogg_codec_t *codec, ogg_page *page)
+{
+    refbuf_t *refbuf;
+
+    if (codec->headers < 2)
+    {
+        ogg_packet packet;
+
+        ogg_stream_pagein (&codec->os, page);
+        while (ogg_stream_packetout (&codec->os, &packet) > 0)
+        {
+           /* first time around (normal case) yields comments */
+           codec->headers++;
+        }
+        /* add header page to associated list */
+        format_ogg_attach_header (codec->feed, page);
+        return NULL;
+    }
+    refbuf = make_refbuf_with_page (page);
+    if (codec->feed->codec_sync == NULL)
+        refbuf->sync_point = 1;
+    return refbuf;
+}
+
+
+static ogg_codec_t *initial_speex_page (ogg_page *page)
+{
+    ogg_codec_t *codec = calloc (1, sizeof (ogg_codec_t));
+    ogg_packet packet;
+    SpeexHeader *header;
+
+    ogg_stream_init (&codec->os, ogg_page_serialno (page));
+    ogg_stream_pagein (&codec->os, page);
+
+    ogg_stream_packetout (&codec->os, &packet);
+
+    header = speex_packet_to_header (packet.packet, packet.bytes);
+    if (header == NULL)
+    {
+        ogg_stream_clear (&codec->os);
+        free (header);
+        free (codec);
+        return NULL;
+    }
+    INFO0 ("seen initial speex header");
+    codec->process_page = process_speex_page;
+    codec->codec_free = speex_codec_free;
+    codec->headers = 1;
+    free (header);
+    return codec;
+}
+#endif
+
+#ifdef HAVE_THEORA
+
+typedef struct _theora_codec_tag
+{
+    theora_info     ti;
+    theora_comment  tc;
+    int             granule_shift;
+    ogg_int64_t     last_iframe;
+    ogg_int64_t     prev_granulepos;
+} theora_codec_t;
+
+
+static void theora_codec_free (ogg_codec_t *codec)
+{
+    theora_codec_t *theora = codec->specific;
+
+    theora_info_clear (&theora->ti);
+    theora_comment_clear (&theora->tc);
+    ogg_stream_clear (&codec->os);
+    free (theora);
+    free (codec);
+}
+
+
+static int _ilog (unsigned int v)
+{
+  int ret=0;
+  while(v){
+    ret++;
+    v>>=1;
+  }
+  return ret;
+}
+
+
+static refbuf_t *process_theora_page (ogg_codec_t *codec, ogg_page *page)
+{
+    refbuf_t *refbuf;
+    theora_codec_t *theora = codec->specific;
+    ogg_int64_t granulepos;
+
+    granulepos = ogg_page_granulepos (page);
+    // printf ("   granulepos of page %ld is %lld\n", ogg_page_pageno (page), granulepos);
+    if (granulepos == 0)
+    {
+        ogg_packet packet;
+        ogg_state_t *ogg_info = codec->feed;
+
+        ogg_stream_pagein (&codec->os, page);
+        // printf ("page %ld processing\n", ogg_page_pageno (page));
+        while (ogg_stream_packetout (&codec->os, &packet) > 0)
+        {
+           if (theora_decode_header (&theora->ti, &theora->tc, &packet) < 0)
+           {
+               /* set some error code */
+               WARN0 ("problem with theora header");
+               return NULL;
+           }
+           codec->headers++;
+           // printf ("header packets: %d\n", codec->headers);
+           if (codec->headers == 3)
+           {
+               theora->granule_shift = _ilog (theora->ti.keyframe_frequency_force - 1);
+               DEBUG1 ("granule shift is %lu", theora->granule_shift);
+               theora->last_iframe = (ogg_int64_t)-1;
+               codec->possible_start = NULL;
+               ogg_info->bitrate += theora->ti.target_bitrate;
+               stats_event_args (codec->feed->mount, "video_bitrate",
+                       "%ld", (long)theora->ti.target_bitrate);
+               stats_event_args (codec->feed->mount, "frame_size",
+                       "%ld x %ld", (long)theora->ti.frame_width, (long)theora->ti.frame_height);
+               stats_event_args (codec->feed->mount, "framerate",
+                       "%.2f", (float)theora->ti.fps_numerator/theora->ti.fps_denominator);
+           }
+        }
+        /* add page to associated list */
+        format_ogg_attach_header (ogg_info, page);
+
+        return NULL;
+    }
+    refbuf = make_refbuf_with_page (page);
+    refbuf->idx = ogg_page_pageno (page);
+
+    // DEBUG2 ("granulepos is %lld,  %p", granulepos, refbuf);
+    if (granulepos == -1 || granulepos == theora->prev_granulepos)
+    {
+        if (codec->possible_start == NULL)
+        {
+            // DEBUG1 ("granulepos is unset, possible beginning %p", refbuf);
+            codec->possible_start = refbuf;
+        }
+        // DEBUG1 ("possible start is %p", codec->possible_start);
+    }
+    else
+    {
+        if ((granulepos >> theora->granule_shift) != theora->last_iframe)
+        {
+            theora->last_iframe = (granulepos >> theora->granule_shift);
+            // DEBUG2 ("iframe changed to %lld (%p)", theora->last_iframe, refbuf);
+            if (codec->possible_start == NULL)
+                codec->possible_start = refbuf;
+            codec->possible_start->sync_point = 1;
+            // DEBUG1 ("possible start is %p", codec->possible_start);
+        }
+        else
+        {
+            if (theora->prev_granulepos != -1)
+                codec->possible_start = refbuf;
+        }
+    }
+    theora->prev_granulepos = granulepos;
+
+    return refbuf;
+}
+
+
+static ogg_codec_t *initial_theora_page (ogg_page *page)
+{
+    ogg_codec_t *codec = calloc (1, sizeof (ogg_codec_t));
+    ogg_packet packet;
+
+    theora_codec_t *theora_codec = calloc (1, sizeof (theora_codec_t));
+
+    ogg_stream_init (&codec->os, ogg_page_serialno (page));
+    ogg_stream_pagein (&codec->os, page);
+
+    theora_info_init (&theora_codec->ti);
+    theora_comment_init (&theora_codec->tc);
+
+    ogg_stream_packetout (&codec->os, &packet);
+
+    if (theora_decode_header (&theora_codec->ti, &theora_codec->tc, &packet) < 0)
+    {
+        theora_info_clear (&theora_codec->ti);
+        theora_comment_clear (&theora_codec->tc);
+        ogg_stream_clear (&codec->os);
+        free (theora_codec);
+        free (codec);
+        return NULL;
+    }
+    INFO0 ("seen initial theora header");
+    // printf ("initial page %ld processing\n", ogg_page_pageno (page));
+    codec->specific = theora_codec;
+    codec->process_page = process_theora_page;
+    codec->codec_free = theora_codec_free;
+    codec->headers = 1;
+    return codec;
+}
+#endif /* THEORA */
+
+static void format_ogg_free_plugin (format_plugin_t *plugin);
+static int  create_ogg_client_data(source_t *source, client_t *client);
+static void free_ogg_client_data (client_t *client);
+
+static void write_ogg_to_file (struct source_tag *source, refbuf_t *refbuf);
+static refbuf_t *ogg_get_buffer (source_t *source);
+static int write_buf_to_client (format_plugin_t *self, client_t *client);
+
+
+static void free_ogg_codecs (ogg_state_t *ogg_info)
+{
+    ogg_codec_t *codec;
+
+    if (ogg_info == NULL)
+        return;
+    codec = ogg_info->codecs;
+    while (codec)
+    {
+        ogg_codec_t *next = codec->next;
+        codec->codec_free (codec);
+        codec = next;
+    }
+    ogg_info->codecs = NULL;
+    ogg_info->headers_completed = 0;
+}
+
+
+int format_ogg_get_plugin (source_t *source)
+{
+    format_plugin_t *plugin;
+    ogg_state_t *state = calloc (1, sizeof (ogg_state_t));
+
+    plugin = (format_plugin_t *)malloc(sizeof(format_plugin_t));
+
+    plugin->type = FORMAT_TYPE_OGG;
+    plugin->format_description = "Ogg Vorbis";
+    plugin->get_buffer = ogg_get_buffer;
+    plugin->write_buf_to_client = write_buf_to_client;
+    plugin->write_buf_to_file = write_ogg_to_file;
+    plugin->create_client_data = create_ogg_client_data;
+    plugin->free_plugin = format_ogg_free_plugin;
+    plugin->set_tag = NULL;
+    plugin->prerelease = refbuf_page_prerelease;
+
+    ogg_sync_init (&state->oy);
+
+    plugin->_state = state;
+    source->format = plugin;
+    state->mount = source->mount;
+
+    return 0;
+}
+
+
+void format_ogg_free_plugin (format_plugin_t *plugin)
+{
+    ogg_state_t *state = plugin->_state;
+
+    /* free memory associated with this plugin instance */
+    free_ogg_codecs (state);
+
+    /* free state memory */
+    ogg_sync_clear (&state->oy);
+    free (state);
+
+    /* free the plugin instance */
+    free (plugin);
+}
+
+
+
+static int process_initial_page (ogg_state_t *ogg_info, ogg_page *page)
+{
+    ogg_codec_t *codec;
+
+    if (ogg_info->headers_completed)
+    {
+        ogg_info->bitrate = 0;
+        ogg_info->codec_sync = NULL;
+        /* need to zap old list of codecs when next group of BOS pages appear */
+        free_ogg_codecs (ogg_info);
+        ogg_info->header_pages = NULL;
+        ogg_info->header_pages_tail = NULL;
+    }
+    do
+    {
+        codec = initial_vorbis_page (page);
+        if (codec)
+            break;
+#ifdef HAVE_THEORA
+        codec = initial_theora_page (page);
+        if (codec)
+        {
+            ogg_info->codec_sync = codec;
+            break;
+        }
+#endif
+#ifdef HAVE_SPEEX
+        codec = initial_speex_page (page);
+        if (codec)
+            break;
+#endif
+
+        /* any others */
+        INFO0 ("Seen BOS page with unknown type");
+        return -1;
+    } while (0);
+
+    if (codec)
+    {
+        /* add codec to list */
+        codec->next = ogg_info->codecs;
+        ogg_info->codecs = codec;
+
+        codec->feed = ogg_info;
+        format_ogg_attach_header (ogg_info, page);
+        // printf ("initial header page stored at %p\n", ogg_info->header_pages);
+    }
+
+    return 0;
+}
+
+
+static refbuf_t *process_ogg_page (ogg_state_t *ogg_info, ogg_page *page)
+{
+    ogg_codec_t *codec = ogg_info->codecs;
+
+    while (codec)
+    {
+        if (ogg_page_serialno (page) == codec->os.serialno)
+        {
+            refbuf_t *refbuf = codec->process_page (codec, page);
+            if (refbuf)
+                refbuf->associated = ogg_info->header_pages;
+            return refbuf;
+        }
+        codec = codec->next;
+    }
+    return NULL;
+}
+
+
+static refbuf_t *ogg_get_buffer (source_t *source)
+{
+    ogg_state_t *ogg_info = source->format->_state;
+    char *data = NULL;
+    int bytes;
+    ogg_page page;
+    refbuf_t *refbuf = NULL;
+
+    while (1)
+    {
+        while (1)
+        {
+            if (ogg_sync_pageout (&ogg_info->oy, &page) > 0)
+            {
+                if (ogg_page_bos (&page))
+                {
+                    process_initial_page (ogg_info, &page);
+                    continue;
+                }
+                // printf ("finished with BOS pages\n");
+                ogg_info->headers_completed = 1;
+                /* process the extracted page */
+                refbuf = process_ogg_page (ogg_info, &page);
+
+                if (ogg_info->send_yp_info)
+                {
+                    char *tag;
+                    tag = ogg_info->title;
+                    if (tag == NULL)
+                        tag = "unknown";
+                    stats_event (source->mount, "title", tag);
+                    INFO1("Updating title \"%s\"", tag);
+
+                    tag = ogg_info->artist;
+                    if (tag == NULL)
+                        tag = "unknown";
+                    stats_event (source->mount, "artist", tag);
+                    if (ogg_info->bitrate)
+                        stats_event_args (source->mount, "ice-bitrate", "%u", ogg_info->bitrate/1000);
+
+                    INFO1("Updating artist \"%s\"", tag);
+                    ogg_info->send_yp_info = 0;
+                    yp_touch (source->mount);
+                }
+                if (refbuf)
+                    return refbuf;
+                continue;
+            }
+            break;
+        }
+        /* we need more data to continue getting pages */
+        data = ogg_sync_buffer (&ogg_info->oy, 4096);
+
+        bytes = sock_read_bytes (source->con->sock, data, 4096);
+        if (bytes < 0)
+        {
+            if (sock_recoverable (sock_error()))
+                return NULL;
+            WARN0 ("source connection has died");
+            ogg_sync_wrote (&ogg_info->oy, 0);
+            source->running = 0;
+            return NULL;
+        }
+        if (bytes == 0)
+        {
+            INFO1 ("End of Stream %s", source->mount);
+            ogg_sync_wrote (&ogg_info->oy, 0);
+            source->running = 0;
+            return NULL;
+        }
+        ogg_sync_wrote (&ogg_info->oy, bytes);
+    }
+}
+
+
+static int create_ogg_client_data (source_t *source, client_t *client)
+{
+    struct client_vorbis *client_data = calloc (1, sizeof (struct client_vorbis));
+    int ret = -1;
+
+    if (client_data)
+    {
+        client_data->headers_sent = 1;
+        client->format_data = client_data;
+        client->free_client_data = free_ogg_client_data;
+        ret = 0;
+    }
+    return ret;
+}
+
+
+static void free_ogg_client_data (client_t *client)
+{
+    free (client->format_data);
+    client->format_data = NULL;
+}
+
+
+
+static int send_ogg_headers (client_t *client, refbuf_t *headers)
+{
+    struct client_vorbis *client_data = client->format_data;
+    refbuf_t *refbuf;
+    int written = 0;
+
+    if (client_data->headers_sent)
+    {
+        client_data->header_page = headers;
+        client_data->pos = 0;
+        client_data->headers_sent = 0;
+    }
+    refbuf = client_data->header_page;
+    while (refbuf)
+    {
+        char *data = refbuf->data + client_data->pos;
+        unsigned len = refbuf->len - client_data->pos;
+        int ret;
+
+        ret = client_send_bytes (client, data, len);
+        if (ret > 0)
+           written += ret;
+        if (ret < (int)len)
+            return written ? written : -1;
+        client_data->pos += ret;
+        if (client_data->pos == refbuf->len)
+        {
+            refbuf = refbuf->next;
+            client_data->header_page = refbuf;
+            client_data->pos = 0;
+        }
+    }
+    client_data->headers_sent = 1;
+    client_data->headers = headers;
+    return written;
+}
+
+
+static int write_buf_to_client (format_plugin_t *self, client_t *client)
+{
+    refbuf_t *refbuf = client->refbuf;
+    char *buf;
+    unsigned len;
+    struct client_vorbis *client_data = client->format_data;
+    int ret, written = 0;
+
+    /* rare but the listener could connect before audio is ready */
+    if (refbuf == NULL)
+        return 0;
+    if (refbuf->next == NULL && client->pos == refbuf->len)
+        return 0;
+
+    if (refbuf->next && client->pos == refbuf->len)
+    {
+        client->refbuf = refbuf->next;
+        client->pos = 0;
+    }
+    refbuf = client->refbuf;
+    buf = refbuf->data + client->pos;
+    len = refbuf->len - client->pos;
+    do
+    {
+        if (client_data->headers != refbuf->associated)
+        {
+            ret = send_ogg_headers (client, refbuf->associated);
+            if (client_data->headers_sent == 0)
+                break;
+            written += ret;
+        }
+        ret = client_send_bytes (client, buf, len);
+
+        if (ret > 0)
+            client->pos += ret;
+
+        if (ret < (int)len)
+            break;
+        written += ret;
+        /* we have now written the page(s) */
+        ret = 0;
+    } while (0);
+
+    if (ret > 0)
+       written += ret;
+    return written ? written : -1;
+}
+
+
+static int write_ogg_data (struct source_tag *source, refbuf_t *refbuf)
+{
+    int ret = 1;
+
+    if (fwrite (refbuf->data, 1, refbuf->len, source->dumpfile) != refbuf->len)
+    {
+        WARN0 ("Write to dump file failed, disabling");
+        fclose (source->dumpfile);
+        source->dumpfile = NULL;
+        ret = 0;
+    }
+    return ret;
+}
+
+
+static void write_ogg_to_file (struct source_tag *source, refbuf_t *refbuf)
+{
+    ogg_state_t *ogg_info = source->format->_state;
+
+    if (ogg_info->file_headers != refbuf->associated)
+    {
+        refbuf_t *header = refbuf->associated;
+        while (header)
+        {
+            if (write_ogg_data (source, header) == 0)
+                return;
+            header = header->next;
+        }
+        ogg_info->file_headers = refbuf->associated;
+    }
+    write_ogg_data (source, refbuf);
+}
+
+

Added: icecast/branches/kh/icecast/src/format_ogg.h
===================================================================
--- icecast/branches/kh/icecast/src/format_ogg.h	2004-07-11 18:09:05 UTC (rev 7095)
+++ icecast/branches/kh/icecast/src/format_ogg.h	2004-07-11 18:13:09 UTC (rev 7096)
@@ -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).
+ */
+
+/* format_ogg.h
+**
+** vorbis format plugin header
+**
+*/
+#ifndef __FORMAT_OGG_H__
+#define __FORMAT_OGG_H__
+
+int format_ogg_get_plugin (source_t *source);
+#define format_ogg_initialise() do{}while(0)
+
+#endif  /* __FORMAT_OGG_H__ */



More information about the commits mailing list