/*
** mod_mp3.c
** $Id: mod_mp3.c,v 1.119 2003/06/16 17:41:01 brian Exp $
*/
#include "mod_mp3.h"
/* Setup for our scoreboard */
static key_t shmkey = IPC_PRIVATE;
extern mp3_dispatch internal;
#ifdef MYSQL_ENABLED
extern mp3_dispatch mysql;
#endif
#ifdef PGSQL_ENABLED
extern mp3_dispatch pgsql;
#endif
#ifdef PLAYLIST_ENABLED
extern mp3_dispatch playlist;
#endif
mp3_dispatch *mp3_dispatches[] = {
&internal,
#ifdef MYSQL_ENABLED
&mysql,
#endif
#ifdef PGSQL_ENABLED
&pgsql,
#endif
#ifdef PLAYLIST_ENABLED
&playlist,
#endif
NULL,
};
/* sends out the right headers for data requests */
void send_headers(request_rec *r, mp3_conf* cfg, request_data *request) {
#ifdef DEBUG
printf("TYPE %d\n", request->type);
#endif
if (request->type == ICESTREAM) {
send_icecast_headers(r, cfg, request);
} else if (request->type == SHOUTSTREAM){
send_shout_headers(r, cfg, request);
} else if (request->type == VORBISSTREAM){
send_ogg_headers(r, cfg, request);
} else {
/* Basically we don't know, or we don't support */
r->content_type = cfg->content_type;
ap_send_http_header(r);
}
}
/* Creates the per virtualhost, directory, location information */
static void *mconfig_for_directory(pool *p, char *dir) {
mp3_conf *cfg = ap_pcalloc (p, sizeof (mp3_conf));
cfg->enabled = 0;
cfg->loop = 0;
cfg->limit = DEFAULT_LIMIT;
cfg->random_enabled = 0;
cfg->cache_enabled = 0;
cfg->max_bytes = 0;
cfg->encoder = NULL;
cfg->stream = NULL;
cfg->cast_name = DEFAULT_CAST_NAME;
cfg->genre_name = DEFAULT_GENRE_NAME;
cfg->playlist = NULL;
cfg->content_type = "audio/mpeg";
cfg->default_op = "play";
cfg->connection_limit = 0;
cfg->dispatch = mp3_dispatches[0];
cfg->context = cfg->dispatch->create(p);
return (void *) cfg;
}
static void *mconfig_for_server(pool *p, server_rec *s) {
mp3_server_conf *scfg = ap_pcalloc (p, sizeof (mp3_server_conf));
int shmid = -1;
mp3_scoreboard *shm = NULL;
//if ((shmid = shmget(shmkey, sizeof(scoreboard), IPC_CREAT | 0600 )) < 0) {
if ((shmid = shmget(shmkey, sizeof(mp3_scoreboard), IPC_CREAT | SHM_R | SHM_W )) < 0) {
#ifdef LINUX
if (errno == ENOSYS) {
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, s,
"Your kernel was built without CONFIG_SYSVIPC\n"
"Please consult the Apache FAQ for details");
}
#endif
printf("%s - Cannot create Shared Memory: %s(%d)(%d)\n", __FILE__, strerror(errno),errno, shmid);
exit(1);
}
if (!(shm = get_scoreboard(shmid))) {
printf("%s - Cannot attach to Shared Memory: %s(%d)\n", __FILE__, strerror(errno),errno);
exit(1);
}
// shmctl(shmid, IPC_RMID, 0);
ap_register_cleanup(p, scfg, cleanup_scoreboard, ap_null_cleanup);
scfg->shmid = shmid;
memset(shm, 0, sizeof(mp3_scoreboard));
shm->time = time(NULL);
scfg->generation = time(NULL);
scfg->board = shm;
return (void *) scfg;
}
static int stream_content(request_rec *r, mp3_conf *cfg, mp3_data *content, request_data *request) {
FILE *file = NULL;
int b = 0;
int x = 0;
int bytes_to_send = 0;
int latch = 0;
unsigned char *temp = NULL;
const char *message = NULL;
mp3_server_conf *scfg = ap_get_module_config(r->server->module_config, &mp3_module);
#ifdef DEBUG
printf ("Playing %s \n", content->name);
#endif
if (cfg->log_filename)
write_log(r, cfg, request, content);
if (request->udp) {
message = get_udp_message(r->pool, content->name, content->artist, request->url, cfg->cast_name);
#ifdef DEBUG
printf ("UDP message: %s \n", message);
#endif
send_udp_message(r, request->udp, message);
}
ap_hard_timeout("mod_mp3_write", r);
connection_set_file(r, scfg, content->signature, content->name);
if (content->data) {
/* Check for bytes per track limit */
if (cfg->max_bytes > 0 && cfg->max_bytes > content->size)
bytes_to_send = cfg->max_bytes;
else
bytes_to_send = content->size;
if (request->shout) {
/* Bug in this at the moment */
temp = (unsigned char *)content->data;
for(x = 0; x < bytes_to_send; x++) {
if (shout_write(r, temp[x], content->name, content->artist, request->url, &latch) == -1)
return HTTP_REQUEST_TIME_OUT;
}
} else {
x = ap_send_mmap(content->data, r, 0, bytes_to_send);
if (!x)
return HTTP_REQUEST_TIME_OUT;
}
} else {
if (!(file = open_content(r, cfg, content))) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "File not found: %s(%s)", content->filename, strerror(errno));
return OK;
}
while ((b = fgetc(file)) != EOF) {
x++;
if (request->shout) {
if (shout_write(r,b, content->name, content->artist, request->url, &latch) == -1)
return HTTP_REQUEST_TIME_OUT;
} else {
if (ap_rputc(b,r) == -1)
return HTTP_REQUEST_TIME_OUT;
}
/* Number pulled out of my ass */
if (request->udp && ((x % 800920) == 0)) {
send_udp_message(r, request->udp, message);
}
}
ap_pfclose(r->pool, file);
}
ap_kill_timeout(r);
return OK;
}
static void mp3_child_init(server_rec *s, pool *p) {
/*
mp3_server_conf *scfg = ap_get_module_config(r->server->module_config, &mp3_module);
if (scfg->directory_server) {
}
*/
}
#ifdef SELECT_ENABLED
static int mp3_selection_handler(request_rec *r) {
mp3_conf *cfg = ap_get_module_config(r->per_dir_config, &mp3_module);
request_data *info = ap_get_module_config(r->request_config, &mp3_module);
mp3_data *bank = NULL;
int x = 0;
static char *url = NULL;
request_data *request = ap_get_module_config(r->request_config, &mp3_module);
url = ap_psprintf(r->pool, "http://%s:%d%s?op=play&song=", r->hostname, r->server->port, r->uri);
r->content_type = "text/html";
ap_rprintf(r, "%s
\n%s\n\n", DOCTYPE_HTML_3_2, cfg->cast_name);
ap_rprintf(r, "\n", r);
ap_rprintf(r, "\n", r);
ap_rputs("\n", r);
return OK;
}
#endif
void print_channel(request_rec *r, mp3_conf *cfg) {
char *time_string = NULL;
time_string = ap_pstrdup(r->pool, ap_ht_time(r->pool, r->finfo.st_mtime, TIME_FORMAT, 0));
ap_rprintf(r, "\n");
ap_rprintf(r, "%s\n", cfg->cast_name);
ap_rprintf(r, "%s\n", cfg->genre_name);
ap_rprintf(r, "%s\n", time_string);
ap_rprintf(r, "%s\n", time_string);
ap_rprintf(r, "%s\n", r->server->server_admin);
ap_rprintf(r, "http://%s:%d%s\n\n", r->hostname, r->server->port, r->uri);
ap_rprintf(r, "\n");
}
void print_channel_rdf(request_rec *r, mp3_conf *cfg, array_header *files) {
char *time_string = NULL;
char **signatures = NULL;
int x = 0;
signatures = (char **)files->elts;
time_string = ap_pstrdup(r->pool, ap_ht_time(r->pool, r->finfo.st_mtime, TIME_FORMAT, 0));
ap_rprintf(r, "\t\n", r->hostname, r->server->port, r->uri);
ap_rprintf(r, "\t\t%s\n", cfg->cast_name);
ap_rprintf(r, "\t\t%s\n", cfg->genre_name);
ap_rprintf(r, "\t\t%s\n", time_string);
ap_rprintf(r, "\t\t%s\n", time_string);
ap_rprintf(r, "\t\t%s\n", r->server->server_admin);
ap_rprintf(r, "\t\thttp://%s:%d%s\n\n", r->hostname, r->server->port, r->uri);
ap_rprintf(r, "\t\t\n\t\t\t\n");
for(x = 0; x < files->nelts; x++) {
ap_rprintf(r, "\t\t\t\t\n", r->hostname, r->server->port, r->uri, signatures[x]);
}
ap_rputs("\t\t\t\n\t\t\n",r);
ap_rputs("\t\n", r);
ap_rflush(r);
}
void print_channel_mbm(request_rec *r, mp3_conf *cfg, array_header *files) {
char *time_string = NULL;
char **signatures = NULL;
int x = 0;
signatures = (char **)files->elts;
time_string = ap_pstrdup(r->pool, ap_ht_time(r->pool, r->finfo.st_mtime, TIME_FORMAT, 0));
ap_rprintf(r, "\t\n");
ap_rprintf(r, "\t\tOK\n");
ap_rprintf(r, "\t\t\n");
ap_rprintf(r, "\t\t\t\n");
for(x = 0; x < files->nelts; x++) {
ap_rprintf(r, "\t\t\t\t\n", r->hostname, r->server->port, r->uri, signatures[x]);
}
ap_rputs("\t\t\t\n",r);
ap_rprintf(r, "\t\t\n");
ap_rputs("\t\n\n", r);
ap_rflush(r);
}
static int mp3_rss_handler(request_rec *r) {
mp3_conf *cfg = ap_get_module_config (r->per_dir_config, &mp3_module);
request_data *info = ap_get_module_config(r->request_config, &mp3_module);
mp3_data *bank = NULL;
r->content_type = "text/xml";
ap_send_http_header(r);
if (r->header_only) {
return OK;
}
ap_rputs(RSS09HEADER, r);
print_channel(r, cfg);
/* Brian, do pattern */
while ((bank = cfg->dispatch->each(cfg->context, r->pool, info->files, info->token, info->random))) {
ap_rputs("- \n", r);
ap_rprintf(r, "%s\n", escape_xml(r->pool, bank->name));
ap_rprintf(r, "http://%s:%d%s?op=play&song=%s", r->hostname, r->server->port, r->uri, bank->signature);
if (info->type == 2)
ap_rputs("&type=.ogg\n", r);
ap_rprintf(r, "\n");
ap_rputs("
\n", r);
ap_rputs("\n", r);
}
ap_rputs(RSS09FOOTER, r);
return OK;
}
static int mp3_rdf_handler(request_rec *r) {
mp3_conf *cfg = ap_get_module_config (r->per_dir_config, &mp3_module);
request_data *info = ap_get_module_config(r->request_config, &mp3_module);
array_header *files = NULL;
mp3_data *bank = NULL;
if (info->files) {
files = info->files;
} else {
files = cfg->dispatch->search(cfg->context, r->pool, info->pattern, info->limit);
}
r->content_type = "text/xml";
ap_send_http_header(r);
if (r->header_only) {
return OK;
}
ap_rputs(RSS10HEADER, r);
print_channel_rdf(r, cfg, files);
/* Brian, do pattern */
while ((bank = cfg->dispatch->each(cfg->context, r->pool, files, info->token, info->random))) {
ap_rprintf(r, "\t- \n", r->hostname, r->server->port, r->uri, bank->signature);
ap_rprintf(r, "\t\t%s\n", escape_xml(r->pool, bank->name));
ap_rprintf(r, "\t\thttp://%s:%d%s?op=play&song=%s", r->hostname, r->server->port, r->uri, bank->signature);
if (info->type == 2)
ap_rputs("&type=.ogg\n", r);
ap_rprintf(r, "\n");
if (bank->artist)
ap_rprintf(r, "\t\t%s\n", escape_xml(r->pool, bank->artist));
if (bank->album)
ap_rprintf(r, "\t\t%s\n", escape_xml(r->pool, bank->album));
if (bank->year)
ap_rprintf(r, "\t\t%s\n", bank->year);
if (bank->comment)
ap_rprintf(r, "\t\t%s\n", escape_xml(r->pool, bank->comment));
if (bank->genre)
ap_rprintf(r, "\t\t%s\n", escape_xml(r->pool, bank->genre));
/* This one is more of a note to me -Brian */
if (bank->track)
ap_rprintf(r, "\t\t%s\n", escape_xml(r->pool, bank->track));
if (info->type == 2)
ap_rprintf(r, "\t\t%s\n", "audio/x-ogg");
else
ap_rprintf(r, "\t\t%s\n", "audio/mpeg");
ap_rputs("\t
\n", r);
ap_rputs("\n", r);
ap_rflush(r);
}
ap_rputs(RDFFOOTER, r);
return OK;
}
static int mp3_mbm_handler(request_rec *r) {
mp3_conf *cfg = ap_get_module_config (r->per_dir_config, &mp3_module);
request_data *info = ap_get_module_config(r->request_config, &mp3_module);
array_header *files = NULL;
mp3_data *bank = NULL;
if (info->files) {
files = info->files;
} else {
files = cfg->dispatch->search(cfg->context, r->pool, info->pattern, info->limit);
}
r->content_type = "text/xml";
ap_send_http_header(r);
if (r->header_only) {
return OK;
}
ap_rputs(MBMHEADER, r);
print_channel_mbm(r, cfg, files);
/* Brian, do pattern */
while ((bank = cfg->dispatch->each(cfg->context, r->pool, info->files, info->token, info->random))) {
ap_rprintf(r, "\t\n", r->hostname, r->server->port, r->uri, bank->signature);
ap_rprintf(r, "\t\t%s\n", escape_xml(r->pool, bank->name));
if (bank->artist)
ap_rprintf(r, "\t\t%s\n", escape_xml(r->pool, bank->artist));
if (bank->album)
ap_rprintf(r, "\t\t%s\n", escape_xml(r->pool, bank->album));
if (bank->year)
ap_rprintf(r, "\t\t%s\n", bank->year);
/* The last two are more of a note to me -Brian */
if (bank->track)
ap_rprintf(r, "\t\t%s\n", bank->track);
if (bank->comment)
ap_rprintf(r, "\t\t%s\n", escape_xml(r->pool, bank->comment));
if (info->type == 2)
ap_rprintf(r, "\t\t%s\n", "audio/x-ogg");
else
ap_rprintf(r, "\t\t%s\n", "audio/mpeg");
ap_rputs("\t\n", r);
ap_rputs("\n", r);
ap_rflush(r);
}
ap_rputs(RDFFOOTER, r);
return OK;
}
static int mp3_pls_handler(request_rec *r) {
mp3_conf *cfg = ap_get_module_config (r->per_dir_config, &mp3_module);
request_data *info = ap_get_module_config(r->request_config, &mp3_module);
int version = 0;
int x = 0;
mp3_data *bank = NULL;
r->content_type = "audio/x-scpls";
#ifdef CONTENT_DISPOSITION
ap_table_set(r->headers_out, "Content-Disposition", "attachment; filename=\"mod_mp3.pls\"");
#endif
ap_send_http_header(r);
if (r->header_only) {
return OK;
}
ap_rputs("[playlist]\n", r);
if (!strcmp(info->op,"pls2"))
version = 2;
if (version)
ap_rprintf(r, "numberofentries=%d\n", cfg->dispatch->count(cfg->context, r->pool, info->files, info->token));
while ((bank = cfg->dispatch->each(cfg->context, r->pool, info->files, info->token, info->random))) {
x++;
if (version) {
ap_rprintf(r, "File%d=http://%s:%d%s?op=play&song=%s",x , r->hostname, r->server->port, r->uri, bank->signature);
if (info->type == 2)
ap_rputs("&type=.ogg", r);
ap_rputs("\n", r);
ap_rprintf(r, "Title%d=%s\n", x, bank->name);
ap_rprintf(r, "Length%d=-1\n", x);
} else {
ap_rprintf(r, "http://%s:%d%s?op=play&song=%s", r->hostname, r->server->port, r->uri, bank->signature);
if (info->type == 2)
ap_rputs("&type=.ogg", r);
ap_rputs("\n", r);
}
}
if (version)
ap_rputs("Version=2\n", r);
return OK;
}
static int mp3_m3u_handler(request_rec *r) {
mp3_conf *cfg = ap_get_module_config (r->per_dir_config, &mp3_module);
request_data *info = ap_get_module_config(r->request_config, &mp3_module);
int version = 0;
mp3_data *bank = NULL;
r->content_type = "audio/x-mpegurl";
#ifdef CONTENT_DISPOSITION
ap_table_set(r->headers_out, "Content-Disposition", "attachment; filename=\"mod_mp3.m3u\"");
#endif
ap_send_http_header(r);
if (r->header_only) {
return OK;
}
if (!strcmp(info->op,"m3u2"))
version = 2;
if (version)
ap_rprintf(r, "#EXTM3U\n");
while ((bank = cfg->dispatch->each(cfg->context, r->pool, info->files, info->token, info->random))) {
/* 1st arg is playlength in secs; -1 for "don't know" */
if (version) {
if (bank->artist)
ap_rprintf(r, "#EXTINF:%d,%s - %s\n", (-1), bank->artist, bank->name);
else
ap_rprintf(r, "#EXTINF:%d,%s\n", (-1), bank->name);
}
ap_rprintf(r, "http://%s:%d%s?op=play&song=%s", r->hostname, r->server->port, r->uri, bank->signature);
if (info->type == 2)
ap_rputs("&type=.ogg", r);
ap_rputs("\n", r);
}
return OK;
}
char *table_find(const table *t, const char *key) {
array_header *hdrs_arr;
table_entry *elts;
int x = 0;
if (t == NULL)
return NULL;
hdrs_arr = ap_table_elts(t);
elts = (table_entry *) hdrs_arr->elts;
if (key == NULL)
return NULL;
for (x = 0; x < hdrs_arr->nelts; ++x) {
if (!mp3_match(elts[x].key, key))
return elts[x].val;
}
return NULL;
}
/* This creates the per request content info */
request_data * create_request(request_rec *r, mp3_conf *cfg) {
request_data *request = NULL;
const char *udp = NULL;
const char *limit = NULL;
const char *type = NULL;
const char *agent = ap_table_get(r->headers_in, "user-agent");
const char *handler = NULL;
request = ap_pcalloc (r->pool, sizeof (request_data));
request->url = make_basename(r);
request->op = cfg->default_op;
request->order = NULL;
request->token = NULL;
request->command = NULL;
request->args = NULL;
request->udp = 0;
request->shout = 0;
request->type = HTTPSTREAM;
request->random = cfg->random_enabled;
request->limit = cfg->limit ? cfg->limit : DEFAULT_LIMIT;
request->files = NULL;
request->pattern = NULL;
request->id = (const char *)ap_md5(r->pool, ap_psprintf(r->pool,"%d%s%d", r->connection->child_num, r->connection->remote_ip, (int)r->request_time));
if (agent && cfg->default_handlers) {
handler = table_find(cfg->default_handlers, agent);
if (handler)
request->op = handler;
}
/* This is where we decide what our client is */
if ((udp = ap_table_get(r->headers_in, "x-audiocast-udpport"))) {
request->udp = atoi((char *)udp);
}
if (ap_table_get(r->headers_in, "Icy-MetaData")) {
request->shout = 1;
}
if (request->shout) {
request->type = SHOUTSTREAM;
} else if (request->udp) {
request->type = ICESTREAM;
} else if (!mp3_match(cfg->content_type, "audio/x-ogg")) {
request->type = VORBISSTREAM;
} else {
request->type = HTTPSTREAM;
}
if (r->args) {
request->args = parse_args(r);
request->op = ap_table_get(request->args, "op");
if (ap_table_get(request->args, "limit"))
request->limit = limit ? atoi(limit) : 0;
request->command = ap_table_get(request->args, "command");
request->order = ap_table_get(request->args, "order");
request->pattern = ap_table_get(request->args, "pattern");
request->token = ap_table_get(request->args, "token");
/* I question if this is still needed. -Brian */
if ((type = ap_table_get(request->args, "type"))) {
if (!mp3_match(".ogg", type)) {
request->type = VORBISSTREAM;
}
}
if (!mp3_match("audio/x-ogg", cfg->content_type)) {
request->type = VORBISSTREAM;
}
if (request->pattern) {
request->files = cfg->dispatch->search(cfg->context, r->pool, request->pattern, request->limit);
} else {
request->files = get_songs(r->pool, request->args);
}
}
#ifdef CRAP
/* Just to solve a bug */
if (cfg->cache_enabled) {
request->type = HTTPSTREAM;
}
#endif
if (request->order && (!mp3_match(request->order, "random"))) {
request->random = 1;
}
#ifdef DEBUG
table_list("Args ", request->args);
#endif
ap_set_module_config(r->request_config, &mp3_module, request);
return request;
}
static int mp3_request_init(request_rec *r) {
mp3_conf *cfg = ap_get_module_config(r->per_dir_config, &mp3_module);
if (cfg->dispatch->request)
cfg->dispatch->request(cfg->context, cfg, r->pool);
return DECLINED;
}
static int mp3_status_handler(request_rec *r) {
mp3_server_conf *scfg = ap_get_module_config(r->server->module_config, &mp3_module);
int x = 0;
r->content_type = "text/html";
ap_send_http_header(r);
ap_rprintf(r, "%s\nmod_mp3 status\n\n", DOCTYPE_HTML_3_2);
ap_rprintf(r, "| # | stream type | Connecting Host | Signature of file being sent | Title of file |
\n");
for (x = 0; x < CHILDREN; x++) {
if (scfg->board->servers[x].status) {
ap_rprintf(r, "| %d | ",x);
ap_rputs("", r);
if (scfg->board->servers[x].type == HTTPSTREAM) {
ap_rprintf(r, "HTTP stream\t");
} else if (scfg->board->servers[x].type == ICESTREAM) {
ap_rprintf(r, "Ice Stream\t");
} else if (scfg->board->servers[x].type == SHOUTSTREAM) {
ap_rprintf(r, "Shout stream\t");
} else if (scfg->board->servers[x].type == VORBISSTREAM) {
ap_rprintf(r, "Ogg Vorbis stream\t");
} else {
ap_rprintf(r, "unknown\t");
}
ap_rputs(" | ", r);
ap_rprintf(r, "%s | %s | %s |
\n",
scfg->board->servers[x].remote_ip,
scfg->board->servers[x].current_file, scfg->board->servers[x].title);
}
}
ap_rputs("
\n", r);
return OK;
}
static int mp3_fixup(request_rec *r) {
mp3_conf *cfg = ap_get_module_config(r->per_dir_config, &mp3_module);
request_data *info = NULL;
/* Not sure if this belongs here long time */
if (!cfg->enabled)
return DECLINED;
info = create_request(r, cfg);
ap_bsetflag(r->connection->client, B_CHUNK, 0);
#ifdef STREAM_SUPPORT
if (cfg->stream) {
r->handler = "mp3-stream";
return DECLINED;
}
#endif
#ifdef DEBUG
printf("OP:%s:\n", info->op);
#endif
#ifdef STREAM_SUPPORT
if (!mp3_match(info->op, "stream")) {
r->handler = "mp3-steam";
return DECLINED;
}
#endif
if (!mp3_match(info->op, "play")) {
if (!r->args) /* Basically, no args means this was default so lets go by the httpd.conf */
info->random = cfg->random_enabled;
r->handler = "mp3-play";
#ifdef SELECT_ENABLED
} else if (!mp3_match(info->op, "select")) {
r->handler = "mp3-selection";
#endif
} else if (!mp3_match(info->op, "rdf")) {
r->handler = "mp3-rdf";
} else if (!mp3_match(info->op, "rss")) {
r->handler = "mp3-rss";
} else if (!mp3_match(info->op, "mbm")) {
r->handler = "mp3-mbm";
} else if (!mp3_match(info->op, "m3u")) {
r->handler = "mp3-m3u";
} else if (!mp3_match(info->op, "m3u2")) {
r->handler = "mp3-m3u";
} else if (!mp3_match(info->op, "pls")) {
r->handler = "mp3-pls";
} else if (!mp3_match(info->op, "pls2")) {
r->handler = "mp3-pls";
} else if (!mp3_match(info->op, "list")) {
r->handler = "mp3-rss";
}
return DECLINED;
}
static int mp3_play_handler(request_rec *r) {
int status = OK;
mp3_data *bank = NULL;
mp3_conf *cfg = ap_get_module_config (r->per_dir_config, &mp3_module);
mp3_server_conf *scfg = ap_get_module_config(r->server->module_config, &mp3_module);
request_data *info = ap_get_module_config(r->request_config, &mp3_module);
if ((status = register_connection(r, scfg, cfg->connection_limit, info->type)) != OK) {
return status;
}
#ifdef DEBUG
table_list("HEADERS ", r->headers_in);
#endif
send_headers(r, cfg, info);
do {
#ifdef DEBUG
printf("Using dispatch %s\n", cfg->dispatch->name);
#endif
while ((bank = cfg->dispatch->each(cfg->context, r->pool, info->files, info->token, info->random))) {
if (stream_content(r, cfg, bank, info) != OK)
return OK;
}
} while (cfg->loop);
return OK;
}
static int mp3_handler(request_rec *r) {
int status = OK;
mp3_data *bank = NULL;
mp3_conf *cfg = ap_get_module_config (r->per_dir_config, &mp3_module);
request_data *info = NULL;
cfg->content_type = "audio/mpeg";
info = create_request(r, cfg);
bank = mp3_create_content(r->pool, r->filename, r->uri, NULL, 0);
send_headers(r, cfg, info);
if ((status = stream_content(r, cfg, bank, info)) != OK)
return status;
return OK;
}
static int ogg_handler(request_rec *r) {
int status = OK;
mp3_data *bank = NULL;
mp3_conf *cfg = ap_get_module_config (r->per_dir_config, &mp3_module);
request_data *info = NULL;
cfg->content_type = "audio/x-ogg";
info = create_request(r, cfg);
#ifdef DEBUG
printf("VALUE OF UDP %d\n", info->udp);
#endif
bank = mp3_create_content(r->pool, r->filename, r->uri, NULL, 0);
send_headers(r, cfg, info);
if ((status = stream_content(r, cfg, bank, info)) != OK)
return status;
return OK;
}
/*Yes there is a reason to leave this here,
its handy for someone who just wants to
do a sethandler on a directry and walk
away from it.
*/
static int mp3_random_handler(request_rec *r) {
mp3_data *bank = NULL;
mp3_conf *cfg = ap_get_module_config (r->per_dir_config, &mp3_module);
request_data *info = ap_get_module_config(r->request_config, &mp3_module);
#ifdef DEBUG
table_list("HEADERS ", r->headers_in);
#endif
send_headers(r, cfg, info);
do {
while ((bank = cfg->dispatch->each(cfg->context, r->pool, info->files, info->token, 1))) {
if (stream_content(r, cfg, bank, info) != OK)
return OK;
}
} while (cfg->loop);
return OK;
}
#ifdef STREAM_SUPPORT
static int mp3_stream_handler(request_rec *r) {
mp3_conf *cfg = ap_get_module_config (r->per_dir_config, &mp3_module);
mp3_server_conf *scfg = ap_get_module_config(r->server->module_config, &mp3_module);
int b = 0;
int latch = 0;
struct stat sbuf;
FILE *file;
request_data *info = ap_get_module_config(r->request_config, &mp3_module);
const char *local_stream = ap_psprintf(r->pool, "/tmp/mod_mp3.%d", r->connection->child_num);
/* This should never happen, yeah, right. */
if (!stat(local_stream, &sbuf))
unlink(local_stream);
send_headers(r, cfg, info);
if (mkfifo(local_stream, 0664) == -1) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "Could not make mkfifo stream: %s(%s)", local_stream, strerror(errno));
return HTTP_INTERNAL_SERVER_ERROR;
}
ap_register_cleanup(r->pool, (char *)local_stream, cleanup_file, ap_null_cleanup);
if ((status = register_connection(r, scfg, cfg->connection_limit, 1)) != OK) {
return status;
}
if (!(file = ap_pfopen(r->pool, local_stream, "r"))) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "Could not open file: %s(%s)", local_stream, strerror(errno));
return HTTP_NOT_FOUND;
}
while ((b = fgetc(file))){
while ((b = fgetc(file)) != EOF && (cfg->max_bytes <= 0 || x <= cfg->max_bytes) ) {
if (info->shout) {
if (shout_write(r,b, NULL, NULL, NULL, &latch) == -1) {
return HTTP_REQUEST_TIME_OUT;
}
} else {
if (ap_rputc(b,r) == -1) {
return HTTP_REQUEST_TIME_OUT;
}
}
}
return OK;
}
#endif
MP3_EXPORT(const char *) add_dispatch_agent(cmd_parms *cmd, void *mconfig, char *type) {
mp3_conf *cfg = (mp3_conf *) mconfig;
int x = 0;
cfg->dispatch = NULL;
for (x = 0; mp3_dispatches[x]; x++) {
if (!mp3_match(mp3_dispatches[x]->name, type)) {
cfg->dispatch = mp3_dispatches[x];
cfg->context = cfg->dispatch->create(cmd->pool);
}
}
if (!cfg->dispatch) {
ap_log_error (APLOG_MARK, APLOG_ERR, cmd->server,
"The dispatch you requested doesn't seem to exist");
ap_log_error (APLOG_MARK, APLOG_ERR, cmd->server,
"The following are valid:");
for (x = 0; mp3_dispatches[x]; x++) {
ap_log_error (APLOG_MARK, APLOG_ERR, cmd->server,
"\t%s", mp3_dispatches[x]->name);
}
exit(1);
}
return NULL;
}
static const command_rec mp3_module_cmds[] = {
{"MP3Engine", ap_set_flag_slot, (void *) XtOffsetOf(mp3_conf, enabled), OR_ALL, FLAG, MP3Engine},
{"MP3Loop", ap_set_flag_slot, (void *) XtOffsetOf(mp3_conf, enabled), OR_ALL, FLAG, MP3Loop},
{"MP3Random", ap_set_flag_slot, (void *) XtOffsetOf(mp3_conf, random_enabled), OR_ALL, FLAG, MP3Random},
{"MP3LimitPlayConnections", set_limit_connections, NULL, OR_ALL, TAKE1, MP3LimitPlayConnections},
{"MP3LimitBytesPerTrack", set_limit_bytespertrack, NULL, OR_ALL, TAKE1, MP3LimitBytesPerTrack},
{"MP3Cache", enable_cache, NULL, OR_ALL, FLAG, MP3Cache},
{"MP3", add_mp3, NULL, OR_ALL, TAKE1, MP3},
{"MP3PlayList", add_mp3_playlist, NULL, OR_ALL, TAKE1, MP3PlayList},
{"MP3Genre", ap_set_string_slot, (void *) XtOffsetOf(mp3_conf, genre_name), OR_ALL, TAKE1, MP3Genre},
{"MP3DefaultOperation", add_default_op, NULL, OR_ALL, TAKE12, MP3DefaultOperation},
{"MP3Log", add_log, NULL, OR_ALL, TAKE1, MP3Log},
#ifdef STREAM_SUPPORT
{"MP3Stream", add_stream, NULL, OR_ALL, TAKE1, MP3Stream},
#endif
{"MP3Encoder", ap_set_string_slot, (void *) XtOffsetOf(mp3_conf, encoder), OR_ALL, TAKE1, MP3Encoder},
{"MP3CastName", ap_set_string_slot, (void *) XtOffsetOf(mp3_conf, cast_name), OR_ALL, TAKE1, MP3CastName},
{"MP3MimeType", ap_set_string_slot, (void *) XtOffsetOf(mp3_conf, content_type), OR_ALL, TAKE1, MP3MimeType},
{"MP3Allow", add_mp3_accept, NULL, OR_ALL, TAKE1, MP3Allow},
{"MP3Deny", add_mp3_deny, NULL, OR_ALL, TAKE1, MP3Deny},
{"MP3DispatchAgent", add_dispatch_agent, NULL, OR_ALL, TAKE1, MP3DispatchAgent},
#ifdef YP_ENABLED
{"MP3DirectoryServer", add_directory_server, NULL, OR_ALL, TAKE12, MP3DirectoryServer},
#endif
#ifdef MYSQL_ENABLED
{"MP3MySQLConnectInfo", mysql_add_connect_info, NULL, OR_ALL, TAKE3, MP3MySQLConnectInfo},
{"MP3MySQLInfo", mysql_add_database_info, NULL, OR_ALL, TAKE2, MP3MySQLInfo},
{"MP3MySQLTokenTable", mysql_add_token_table, NULL, OR_ALL, TAKE1, MP3MySQLTokenTable},
#endif
#ifdef PGSQL_ENABLED
{"MP3PgConnectInfo", pgsql_add_connect_info, NULL, OR_ALL, TAKE3, MP3PgConnectInfo},
{"MP3PgInfo", pgsql_add_database_info, NULL, OR_ALL, TAKE2, MP3PgInfo},
{"MP3PgTokenTable", pgsql_add_token_table, NULL, OR_ALL, TAKE1, MP3PgTokenTable},
#endif
{NULL}
};
/* Dispatch list of content handlers */
static const handler_rec mp3_handlers[] = {
{ "mp3-file", mp3_handler },
{ "ogg-file", ogg_handler },
{ "mp3-play", mp3_play_handler },
{ "mp3-status", mp3_status_handler },
{ "mp3-random", mp3_random_handler },
#ifdef SELECT_ENABLED
{ "mp3-selection", mp3_selection_handler },
#endif
{ "mp3-rss", mp3_rss_handler },
{ "mp3-rdf", mp3_rdf_handler },
{ "mp3-m3u", mp3_m3u_handler },
{ "mp3-pls", mp3_pls_handler },
{ "mp3-mbm", mp3_mbm_handler },
#ifdef STREAM_SUPPORT
{ "mp3-stream", mp3_stream_handler },
#endif
{ NULL, NULL }
};
static void mp3_init(server_rec * s, pool * p) {
/* Tell apache we're here */
ap_add_version_component("mod_mp3/"VERSION);
}
/* Dispatch list for API hooks */
module MODULE_VAR_EXPORT mp3_module = {
STANDARD_MODULE_STUFF,
mp3_init, /* module initializer */
mconfig_for_directory, /* create per-dir config structures */
NULL, /* merge per-dir config structures */
mconfig_for_server, /* create per-server config structures */
NULL, /* merge per-server config structures */
mp3_module_cmds, /* table of config file commands */
mp3_handlers, /* [#8] MIME-typed-dispatched handlers */
NULL, /* [#1] URI to filename translation */
NULL, /* [#4] validate user id from request */
NULL, /* [#5] check if the user is ok _here_ */
NULL, /* [#3] check access by host address */
NULL, /* [#6] determine MIME type */
mp3_fixup, /* [#7] pre-run fixups */
NULL, /* [#9] log a transaction */
NULL, /* [#2] header parser */
mp3_child_init, /* child_init */
NULL, /* child_exit */
mp3_request_init /* [#0] post read-request */
#ifdef EAPI
,NULL, /* EAPI: add_module */
NULL, /* EAPI: remove_module */
NULL, /* EAPI: rewrite_command */
NULL /* EAPI: new_connection */
#endif
};