/* ** 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->hostname, r->server->port, r->uri); ap_rputs("\n", r); if (request->pattern) ap_rprintf(r,"\n", request->pattern); else ap_rputs("\n", r); ap_rputs("\n", r); ap_rputs("
\n", r); ap_rprintf(r, "
\n", r->hostname, r->server->port, r->uri); ap_rputs("\n", r); while ((bank = cfg->dispatch->each(cfg->context, r->pool, info->files, info->token, info->random))) { x++; if (x % 2) ap_rputs("\n", r); } ap_rputs("
\n", r); else ap_rputs("
\n", r); ap_rprintf(r, " %s\n", bank->signature, url, bank->signature, bank->name); ap_rputs("
\n", r); if (request->pattern) ap_rprintf(r, "\n", request->pattern); ap_rputs("
Random Play
\n", r); ap_rputs("
Play
\n", r); ap_rputs("
RSS
\n", r); ap_rputs("
PLS (Version 1)
\n", r); ap_rputs("
PLS (Version 2)
\n", r); ap_rputs("
M3U (Version 1)
\n", r); ap_rputs("
M3U (Version 2)
\n", r); ap_rputs("
RSS 1.0
\n", r); ap_rputs("
Music Brainz
\n", r); ap_rputs("\n", r); ap_rputs("\n", r); ap_rputs("
\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, "\n"); for (x = 0; x < CHILDREN; x++) { if (scfg->board->servers[x].status) { ap_rprintf(r, "",x); ap_rputs("", r); ap_rprintf(r, "\n", scfg->board->servers[x].remote_ip, scfg->board->servers[x].current_file, scfg->board->servers[x].title); } } ap_rputs("
#stream typeConnecting HostSignature of file being sentTitle of file
%d", 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("%s%s%s
\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 };