diff --git a/conf/icecast.xml b/conf/icecast.xml
index 4ac4e776..d343ddff 100644
--- a/conf/icecast.xml
+++ b/conf/icecast.xml
@@ -31,11 +31,13 @@
-
- /usr/local/icecast
- /tmp
- /usr/local/icecast/web
-
+ 1
+
+
+ /usr/local/icecast
+ /tmp
+ /usr/local/icecast/web
+
access.log
diff --git a/src/Makefile.am b/src/Makefile.am
index 29a64718..f2b905e9 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -8,10 +8,10 @@ bin_PROGRAMS = icecast
noinst_HEADERS = config.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 format_vorbis.h\
- compat.h format_mp3.h
+ compat.h format_mp3.h fserve.h
icecast_SOURCES = config.c main.c logging.c sighandler.c connection.c global.c\
util.c slave.c source.c stats.c refbuf.c client.c format.c format_vorbis.c\
- format_mp3.c xslt.c
+ format_mp3.c xslt.c fserve.c
icecast_LDADD = net/libicenet.la thread/libicethread.la httpp/libicehttpp.la\
log/libicelog.la avl/libiceavl.la timing/libicetiming.la
diff --git a/src/config.c b/src/config.c
index 6f37f7c4..2f1a7b75 100644
--- a/src/config.c
+++ b/src/config.c
@@ -16,6 +16,7 @@
#define CONFIG_DEFAULT_SOURCE_PASSWORD "changeme"
#define CONFIG_DEFAULT_RELAY_PASSWORD "changeme"
#define CONFIG_DEFAULT_ICE_LOGIN 0
+#define CONFIG_DEFAULT_FILESERVE 1
#define CONFIG_DEFAULT_TOUCH_FREQ 5
#define CONFIG_DEFAULT_HOSTNAME "localhost"
#define CONFIG_DEFAULT_PORT 8888
@@ -162,6 +163,7 @@ static void _set_defaults(void)
_configuration.source_password = CONFIG_DEFAULT_SOURCE_PASSWORD;
_configuration.relay_password = CONFIG_DEFAULT_RELAY_PASSWORD;
_configuration.ice_login = CONFIG_DEFAULT_ICE_LOGIN;
+ _configuration.fileserve = CONFIG_DEFAULT_FILESERVE;
_configuration.touch_freq = CONFIG_DEFAULT_TOUCH_FREQ;
_configuration.dir_list = NULL;
_configuration.hostname = CONFIG_DEFAULT_HOSTNAME;
@@ -214,6 +216,10 @@ static void _parse_root(xmlDocPtr doc, xmlNodePtr node)
tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
_configuration.ice_login = atoi(tmp);
if (tmp) xmlFree(tmp);
+ } else if (strcmp(node->name, "fileserve") == 0) {
+ tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+ _configuration.fileserve = atoi(tmp);
+ if (tmp) xmlFree(tmp);
} else if (strcmp(node->name, "hostname") == 0) {
if (_configuration.hostname && _configuration.hostname != CONFIG_DEFAULT_HOSTNAME) xmlFree(_configuration.hostname);
_configuration.hostname = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
diff --git a/src/config.h b/src/config.h
index d407e421..5a0fe590 100644
--- a/src/config.h
+++ b/src/config.h
@@ -25,6 +25,7 @@ typedef struct ice_config_tag
int header_timeout;
int source_timeout;
int ice_login;
+ int fileserve;
char *source_password;
char *relay_password;
diff --git a/src/connection.c b/src/connection.c
index 8629a57f..63d38b57 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -34,6 +34,7 @@
#include "format.h"
#include "logging.h"
#include "xslt.h"
+#include "fserve.h"
#include "source.h"
@@ -530,6 +531,18 @@ static void _handle_get_request(connection_t *con,
free(fullpath);
return;
}
+ else if(config_get_config()->fileserve &&
+ stat(fullpath, &statbuf) == 0) {
+ client->respcode = 200;
+ bytes = sock_write(client->con->sock,
+ "HTTP/1.0 200 OK\r\nContent-Type: %s\r\n\r\n",
+ fserve_content_type(fullpath));
+ if(bytes > 0) client->con->sent_bytes = bytes;
+ if(fserve_client_create(client, fullpath) < 0)
+ client_destroy(client);
+ free(fullpath);
+ return;
+ }
free(fullpath);
if(strcmp(util_get_extension(uri), "m3u") == 0) {
diff --git a/src/fserve.c b/src/fserve.c
new file mode 100644
index 00000000..c7a06a76
--- /dev/null
+++ b/src/fserve.c
@@ -0,0 +1,272 @@
+#include
+#include
+#include
+#include
+
+#ifndef _WIN32
+#include
+#include
+#include
+#else
+#include
+#include
+#endif
+
+#include "thread.h"
+#include "avl.h"
+#include "httpp.h"
+#include "sock.h"
+
+#include "connection.h"
+#include "global.h"
+#include "refbuf.h"
+#include "client.h"
+#include "stats.h"
+#include "format.h"
+#include "log.h"
+#include "logging.h"
+#include "config.h"
+#include "util.h"
+
+#include "fserve.h"
+
+#undef CATMODULE
+#define CATMODULE "fserve"
+
+#define BUFSIZE 4096
+
+static avl_tree *client_tree;
+static avl_tree *pending_tree;
+
+static cond_t fserv_cond;
+static thread_t *fserv_thread;
+static int run_fserv;
+
+/* avl tree helper */
+static int _compare_clients(void *compare_arg, void *a, void *b);
+static int _remove_client(void *key);
+static int _free_client(void *key);
+void *fserv_thread_function(void *arg);
+
+void fserve_initialize(void)
+{
+ if(!config_get_config()->fileserve)
+ return;
+
+ client_tree = avl_tree_new(_compare_clients, NULL);
+ pending_tree = avl_tree_new(_compare_clients, NULL);
+ thread_cond_create(&fserv_cond);
+
+ run_fserv = 1;
+
+ fserv_thread = thread_create("File Serving Thread",
+ fserv_thread_function, NULL, THREAD_ATTACHED);
+}
+
+void fserve_shutdown(void)
+{
+ if(!config_get_config()->fileserve)
+ return;
+
+ run_fserv = 0;
+ thread_cond_signal(&fserv_cond);
+ thread_join(fserv_thread);
+
+ thread_cond_destroy(&fserv_cond);
+ avl_tree_free(client_tree, _free_client);
+ avl_tree_free(pending_tree, _free_client);
+}
+
+void *fserv_thread_function(void *arg)
+{
+ avl_node *client_node, *pending_node;
+ fserve_t *client;
+ int sbytes, bytes;
+
+ while (run_fserv) {
+ avl_tree_rlock(client_tree);
+
+ client_node = avl_get_first(client_tree);
+ if(!client_node) {
+ pending_node = avl_get_first(pending_tree);
+ if(!pending_node) {
+ /* There are no current clients. Wait until there are... */
+ avl_tree_unlock(client_tree);
+ thread_cond_wait(&fserv_cond);
+ continue;
+ }
+ }
+
+ while(client_node) {
+ avl_node_wlock(client_node);
+
+ client = (fserve_t *)client_node->key;
+
+ if(client->offset >= client->datasize) {
+ /* Grab a new chunk */
+ bytes = fread(client->buf, 1, BUFSIZE, client->file);
+ if(bytes <= 0) {
+ client->client->con->error = 1;
+ avl_node_unlock(client_node);
+ client_node = avl_get_next(client_node);
+ continue;
+ }
+ client->offset = 0;
+ client->datasize = bytes;
+ }
+
+ /* Now try and send current chunk. */
+ sbytes = sock_write_bytes(client->client->con->sock,
+ &client->buf[client->offset],
+ client->datasize - client->offset);
+
+ // TODO: remove clients if they take too long.
+ if(sbytes >= 0) {
+ client->offset += sbytes;
+ client->client->con->sent_bytes += sbytes;
+ }
+ else if(!sock_recoverable(sock_error())) {
+ DEBUG0("Fileserving client had fatal error, disconnecting");
+ client->client->con->error = 1;
+ }
+ else
+ DEBUG0("Fileserving client had recoverable error");
+
+ avl_node_unlock(client_node);
+ client_node = avl_get_next(client_node);
+ }
+
+ avl_tree_unlock(client_tree);
+
+ /* Now we need a write lock instead, to delete done clients. */
+ avl_tree_wlock(client_tree);
+
+ client_node = avl_get_first(client_tree);
+ while(client_node) {
+ client = (fserve_t *)client_node->key;
+ if(client->client->con->error) {
+ client_node = avl_get_next(client_node);
+ avl_delete(client_tree, (void *)client, _free_client);
+ continue;
+ }
+ client_node = avl_get_next(client_node);
+ }
+
+ avl_tree_wlock(pending_tree);
+
+ /* And now insert new clients. */
+ client_node = avl_get_first(pending_tree);
+ while(client_node) {
+ client = (fserve_t *)client_node->key;
+ avl_insert(client_tree, client);
+ client_node = avl_get_next(client_node);
+
+ }
+
+ /* clear pending */
+ while(avl_get_first(pending_tree)) {
+ avl_delete(pending_tree, avl_get_first(pending_tree)->key,
+ _remove_client);
+ }
+
+ avl_tree_unlock(pending_tree);
+ avl_tree_unlock(client_tree);
+ }
+
+ /* Shutdown path */
+
+ avl_tree_wlock(pending_tree);
+ while(avl_get_first(pending_tree))
+ avl_delete(pending_tree, avl_get_first(pending_tree)->key,
+ _free_client);
+ avl_tree_unlock(pending_tree);
+
+ avl_tree_wlock(client_tree);
+ while(avl_get_first(client_tree))
+ avl_delete(client_tree, avl_get_first(client_tree)->key,
+ _free_client);
+ avl_tree_unlock(client_tree);
+
+ thread_exit(0);
+ return NULL;
+}
+
+char *fserve_content_type(char *path)
+{
+ char *ext = util_get_extension(path);
+
+ if(!strcmp(ext, "ogg"))
+ return "application/x-ogg";
+ else if(!strcmp(ext, "mp3"))
+ return "audio/mpeg";
+ else if(!strcmp(ext, "html"))
+ return "text/html";
+ else if(!strcmp(ext, "txt"))
+ return "text/plain";
+ else
+ return "application/octet-stream";
+ /* TODO Add more types */
+}
+
+static void fserve_client_destroy(fserve_t *client)
+{
+ if(client) {
+ if(client->buf)
+ free(client->buf);
+ if(client->file)
+ fclose(client->file);
+
+ if(client->client)
+ client_destroy(client->client);
+ free(client);
+ }
+}
+
+int fserve_client_create(client_t *httpclient, char *path)
+{
+ fserve_t *client = calloc(1, sizeof(fserve_t));
+
+ client->client = httpclient;
+ client->file = fopen(path, "rb");
+ if(!client->file) {
+ fserve_client_destroy(client);
+ return -1;
+ }
+ client->offset = 0;
+ client->datasize = 0;
+ client->buf = malloc(BUFSIZE);
+
+ avl_tree_wlock(pending_tree);
+ avl_insert(pending_tree, client);
+ avl_tree_unlock(pending_tree);
+
+ thread_cond_signal(&fserv_cond);
+
+ return 0;
+}
+
+static int _compare_clients(void *compare_arg, void *a, void *b)
+{
+ connection_t *cona = (connection_t *)a;
+ connection_t *conb = (connection_t *)b;
+
+ if (cona->id < conb->id) return -1;
+ if (cona->id > conb->id) return 1;
+
+ return 0;
+}
+
+static int _remove_client(void *key)
+{
+ return 1;
+}
+
+static int _free_client(void *key)
+{
+ fserve_t *client = (fserve_t *)key;
+
+ fserve_client_destroy(client);
+
+ return 1;
+}
+
diff --git a/src/fserve.h b/src/fserve.h
new file mode 100644
index 00000000..229e7954
--- /dev/null
+++ b/src/fserve.h
@@ -0,0 +1,24 @@
+#ifndef __FSERVE_H__
+#define __FSERVE_H__
+
+#include
+
+typedef struct
+{
+ client_t *client;
+
+ FILE *file;
+ int offset;
+ int datasize;
+ unsigned char *buf;
+} fserve_t;
+
+void fserve_initialize(void);
+void fserve_shutdown(void);
+char *fserve_content_type(char *path);
+int fserve_client_create(client_t *httpclient, char *path);
+
+
+#endif
+
+
diff --git a/src/main.c b/src/main.c
index 3bca42c1..c0e01ab3 100644
--- a/src/main.c
+++ b/src/main.c
@@ -27,6 +27,7 @@
#include "stats.h"
#include "logging.h"
#include "xslt.h"
+#include "fserve.h"
#ifdef _WIN32
#define snprintf _snprintf
@@ -53,10 +54,13 @@ static void _initialize_subsystems(void)
global_initialize();
refbuf_initialize();
xslt_initialize();
+ DEBUG0("Calling fserve_initialize()");
+ fserve_initialize();
}
static void _shutdown_subsystems(void)
{
+ fserve_shutdown();
xslt_shutdown();
refbuf_shutdown();
stats_shutdown();