mirror of
https://github.com/owntone/owntone-server.git
synced 2025-01-26 06:03:20 -05:00
Remove plugin infrastructure
This commit is contained in:
parent
baf529dbf2
commit
6aca074f82
@ -101,9 +101,6 @@ AC_PROG_GCC_TRADITIONAL
|
||||
AC_TYPE_SIGNAL
|
||||
AC_CHECK_FUNCS(select socket strdup strerror)
|
||||
|
||||
AC_CHECK_LIB(dl, dlsym, LIBDL=-ldl)
|
||||
AC_SUBST(LIBDL)
|
||||
|
||||
dnl check to see if we need -lsocket (solaris)
|
||||
V_NETLIBS=""
|
||||
AC_CHECK_LIB(socket,socket,V_NETLIBS="-lsocket $V_NETLIBS",,)
|
||||
|
@ -10,7 +10,7 @@ MUSEPACKSRC=scan-mpc.c
|
||||
endif
|
||||
|
||||
mt_daapd_CPPFLAGS = -D_GNU_SOURCE @AVAHI_CFLAGS@ @SQLITE3_CFLAGS@ @FFMPEG_CFLAGS@ @CONFUSE_CFLAGS@ @TAGLIB_CFLAGS@ @MINIXML_CFLAGS@
|
||||
mt_daapd_LDADD = @AVAHI_LIBS@ @SQLITE3_LIBS@ @FFMPEG_LIBS@ @CONFUSE_LIBS@ @FLAC_LIBS@ @TAGLIB_LIBS@ @LIBEVENT_LIBS@ @LIBAVL_LIBS@ @MINIXML_LIBS@ @LIBDL@
|
||||
mt_daapd_LDADD = @AVAHI_LIBS@ @SQLITE3_LIBS@ @FFMPEG_LIBS@ @CONFUSE_LIBS@ @FLAC_LIBS@ @TAGLIB_LIBS@ @LIBEVENT_LIBS@ @LIBAVL_LIBS@ @MINIXML_LIBS@
|
||||
mt_daapd_LDFLAGS = -export-dynamic
|
||||
mt_daapd_SOURCES = main.c daapd.h webserver.c \
|
||||
webserver.h configfile.c configfile.h err.c err.h \
|
||||
@ -25,11 +25,11 @@ mt_daapd_SOURCES = main.c daapd.h webserver.c \
|
||||
httpd_daap.c httpd_daap.h \
|
||||
transcode.c transcode.h \
|
||||
misc.c misc.h \
|
||||
db-generic.c db-generic.h ff-plugins.c ff-plugins.h \
|
||||
db-generic.c db-generic.h \
|
||||
scan-wma.c \
|
||||
smart-parser.c smart-parser.h xml-rpc.c xml-rpc.h \
|
||||
util.c util.h \
|
||||
plugin.c plugin.h db-sql-updates.c \
|
||||
db-sql-updates.c \
|
||||
io.h io.c io-errors.h io-plugin.h \
|
||||
db-sql.c db-sql.h db-sql-sqlite3.c db-sql-sqlite3.h\
|
||||
$(FLACSRC) $(MUSEPACKSRC)
|
||||
@ -37,4 +37,4 @@ mt_daapd_SOURCES = main.c daapd.h webserver.c \
|
||||
EXTRA_DIST = scan-mpc.c \
|
||||
scan-flac.c \
|
||||
conf.h \
|
||||
ff-plugins.h ff-dbstruct.h ff-plugin-events.h
|
||||
ff-dbstruct.h
|
||||
|
@ -43,7 +43,6 @@
|
||||
#include "db-generic.h"
|
||||
#include "db-sql.h"
|
||||
#include "smart-parser.h"
|
||||
#include "plugin.h"
|
||||
#include "conf.h" /* FIXME */
|
||||
|
||||
#ifdef HAVE_SQLITE3
|
||||
|
10
src/err.c
10
src/err.c
@ -52,10 +52,6 @@
|
||||
#include "err.h"
|
||||
#include "io.h"
|
||||
|
||||
#ifndef ERR_LEAN
|
||||
# include "plugin.h"
|
||||
#endif
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#ifndef PACKAGE
|
||||
@ -285,12 +281,6 @@ void err_log(int level, unsigned int cat, char *fmt, ...)
|
||||
|
||||
util_mutex_unlock(l_err);
|
||||
|
||||
#ifndef ERR_LEAN
|
||||
if(level < 2) { /* only event level fatals and log level */
|
||||
plugin_event_dispatch(PLUGIN_EVENT_LOG, level, errbuf, (int)strlen(errbuf)+1);
|
||||
}
|
||||
#endif
|
||||
|
||||
util_mutex_lock(l_err_list);
|
||||
__err_thread_del();
|
||||
util_mutex_unlock(l_err_list);
|
||||
|
@ -1,26 +0,0 @@
|
||||
|
||||
|
||||
#ifndef _FF_PLUGIN_EVENTS_H_
|
||||
#define _FF_PLUGIN_EVENTS_H_
|
||||
|
||||
/* Plugin types */
|
||||
#define PLUGIN_OUTPUT 1
|
||||
#define PLUGIN_SCANNER 2
|
||||
#define PLUGIN_DATABASE 4
|
||||
#define PLUGIN_EVENT 8
|
||||
#define PLUGIN_TRANSCODE 16
|
||||
|
||||
/* plugin event types */
|
||||
#define PLUGIN_EVENT_LOG 0
|
||||
#define PLUGIN_EVENT_FULLSCAN_START 1
|
||||
#define PLUGIN_EVENT_FULLSCAN_END 2
|
||||
#define PLUGIN_EVENT_STARTING 3
|
||||
#define PLUGIN_EVENT_SHUTDOWN 4
|
||||
#define PLUGIN_EVENT_STARTSTREAM 5
|
||||
#define PLUGIN_EVENT_ABORTSTREAM 6
|
||||
#define PLUGIN_EVENT_ENDSTREAM 7
|
||||
|
||||
#define PLUGIN_VERSION 2
|
||||
|
||||
|
||||
#endif /* _FF_PLUGIN_EVENTS_ */
|
780
src/ff-plugins.c
780
src/ff-plugins.c
@ -1,780 +0,0 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "conf.h"
|
||||
#include "configfile.h"
|
||||
#include "daapd.h"
|
||||
#include "db-generic.h"
|
||||
#include "err.h"
|
||||
#include "ff-dbstruct.h"
|
||||
#include "ff-plugins.h"
|
||||
#include "io.h"
|
||||
#include "plugin.h"
|
||||
#include "util.h"
|
||||
#include "webserver.h"
|
||||
|
||||
EXPORT char *pi_ws_uri(WS_CONNINFO *pwsc) {
|
||||
ASSERT(pwsc);
|
||||
|
||||
if(!pwsc)
|
||||
return NULL;
|
||||
|
||||
return ws_uri(pwsc);
|
||||
}
|
||||
|
||||
EXPORT void pi_ws_will_close(WS_CONNINFO *pwsc) {
|
||||
ASSERT(pwsc);
|
||||
|
||||
if(!pwsc)
|
||||
return;
|
||||
|
||||
ws_should_close(pwsc,1);
|
||||
}
|
||||
|
||||
EXPORT int pi_ws_returnerror(WS_CONNINFO *pwsc, int ecode, char *msg) {
|
||||
ASSERT(pwsc);
|
||||
|
||||
if(!pwsc)
|
||||
return FALSE;
|
||||
|
||||
return ws_returnerror(pwsc,ecode,msg);
|
||||
}
|
||||
|
||||
EXPORT char *pi_ws_getvar(WS_CONNINFO *pwsc, char *var) {
|
||||
ASSERT(pwsc);
|
||||
ASSERT(var);
|
||||
|
||||
if((!pwsc) || (!var))
|
||||
return NULL;
|
||||
|
||||
return ws_getvar(pwsc,var);
|
||||
}
|
||||
|
||||
EXPORT int pi_ws_writefd(WS_CONNINFO *pwsc, char *fmt, ...) {
|
||||
char *out;
|
||||
va_list ap;
|
||||
int result;
|
||||
|
||||
ASSERT((pwsc) && (fmt));
|
||||
|
||||
if((!pwsc) || (!fmt))
|
||||
return FALSE;
|
||||
|
||||
va_start(ap,fmt);
|
||||
out = util_vasprintf(fmt,ap);
|
||||
va_end(ap);
|
||||
|
||||
result = ws_writefd(pwsc, "%s", out);
|
||||
free(out);
|
||||
return result;
|
||||
}
|
||||
|
||||
EXPORT int pi_ws_addresponseheader(WS_CONNINFO *pwsc, char *header, char *fmt, ...) {
|
||||
char *out;
|
||||
va_list ap;
|
||||
int result;
|
||||
|
||||
ASSERT(pwsc && header && fmt);
|
||||
|
||||
if((!pwsc) || (!header) || (!fmt))
|
||||
return FALSE;
|
||||
|
||||
va_start(ap,fmt);
|
||||
out = util_vasprintf(fmt,ap);
|
||||
va_end(ap);
|
||||
|
||||
result = ws_addresponseheader(pwsc, header, "%s", out);
|
||||
free(out);
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
EXPORT void pi_ws_emitheaders(WS_CONNINFO *pwsc) {
|
||||
ASSERT(pwsc);
|
||||
|
||||
if(!pwsc)
|
||||
return;
|
||||
|
||||
ws_emitheaders(pwsc);
|
||||
}
|
||||
|
||||
EXPORT char *pi_ws_getrequestheader(WS_CONNINFO *pwsc, char *header) {
|
||||
ASSERT((pwsc) && (header));
|
||||
|
||||
if((!pwsc) || (!header))
|
||||
return NULL;
|
||||
|
||||
return ws_getrequestheader(pwsc, header);
|
||||
}
|
||||
|
||||
EXPORT int pi_ws_writebinary(WS_CONNINFO *pwsc, char *data, int len) {
|
||||
ASSERT((pwsc) && (data) && (len));
|
||||
|
||||
if((!pwsc) || (!data) || (!len))
|
||||
return 0;
|
||||
|
||||
return ws_writebinary(pwsc, data, len);
|
||||
}
|
||||
|
||||
EXPORT char *pi_ws_gethostname(WS_CONNINFO *pwsc) {
|
||||
ASSERT(pwsc);
|
||||
|
||||
if(!pwsc)
|
||||
return NULL;
|
||||
|
||||
return ws_hostname(pwsc);
|
||||
}
|
||||
|
||||
EXPORT int pi_ws_matchesrole(WS_CONNINFO *pwsc, char *username,
|
||||
char *password, char *role) {
|
||||
ASSERT((pwsc) && (role));
|
||||
|
||||
if((!pwsc) || (!role))
|
||||
return FALSE;
|
||||
|
||||
return config_matches_role(pwsc, username, password, role);
|
||||
}
|
||||
|
||||
/* misc helpers */
|
||||
EXPORT char *pi_server_ver(void) {
|
||||
return VERSION;
|
||||
}
|
||||
|
||||
EXPORT int pi_server_name(char *name, int *len) {
|
||||
char *servername;
|
||||
|
||||
ASSERT((name) && (len));
|
||||
|
||||
if((!name) || (!len))
|
||||
return FALSE;
|
||||
|
||||
servername = conf_get_servername();
|
||||
|
||||
/* FIXME: this is stupid */
|
||||
if((servername) && (strlen(servername) < (size_t)len)) {
|
||||
strcpy(name,servername);
|
||||
} else {
|
||||
if((size_t)len > strlen("Firefly Media Server"))
|
||||
strcpy(name,"Firefly Media Server");
|
||||
}
|
||||
|
||||
free(servername);
|
||||
return CONF_E_SUCCESS;
|
||||
}
|
||||
|
||||
EXPORT void pi_log(int level, char *fmt, ...) {
|
||||
char *out;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap,fmt);
|
||||
out=util_vasprintf(fmt,ap);
|
||||
va_end(ap);
|
||||
|
||||
DPRINTF(level,L_PLUG,"%s",out);
|
||||
free(out);
|
||||
}
|
||||
|
||||
/**
|
||||
* check to see if we can transcode
|
||||
*
|
||||
* @param codec the codec we are trying to serve
|
||||
* @returns TRUE if we can transcode, FALSE otherwise
|
||||
*/
|
||||
EXPORT int pi_should_transcode(WS_CONNINFO *pwsc, char *codec) {
|
||||
return plugin_ssc_should_transcode(pwsc,codec);
|
||||
}
|
||||
|
||||
|
||||
EXPORT int pi_db_enum_start(char **pe, DB_QUERY *pinfo) {
|
||||
DBQUERYINFO *pqi;
|
||||
int result;
|
||||
|
||||
pqi = (DBQUERYINFO*)malloc(sizeof(DBQUERYINFO));
|
||||
if(!pqi) {
|
||||
if(pe) *pe = strdup("Malloc error");
|
||||
return DB_E_MALLOC;
|
||||
}
|
||||
memset(pqi,0,sizeof(DBQUERYINFO));
|
||||
pinfo->priv = (void*)pqi;
|
||||
|
||||
if(pinfo->filter) {
|
||||
pqi->pt = sp_init();
|
||||
if(!sp_parse(pqi->pt,pinfo->filter,pinfo->filter_type)) {
|
||||
DPRINTF(E_LOG,L_PLUG,"Ignoring bad query (%s): %s\n",
|
||||
pinfo->filter,sp_get_error(pqi->pt));
|
||||
sp_dispose(pqi->pt);
|
||||
pqi->pt = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if((pinfo->limit) || (pinfo->offset)) {
|
||||
pqi->index_low = pinfo->offset;
|
||||
pqi->index_high = pinfo->offset + pinfo->limit - 1;
|
||||
if(pqi->index_high < pqi->index_low)
|
||||
pqi->index_high = 9999999;
|
||||
|
||||
pqi->index_type = indexTypeSub;
|
||||
} else {
|
||||
pqi->index_type = indexTypeNone;
|
||||
}
|
||||
|
||||
pqi->want_count = 1;
|
||||
|
||||
switch(pinfo->query_type) {
|
||||
case QUERY_TYPE_PLAYLISTS:
|
||||
pqi->query_type = queryTypePlaylists;
|
||||
break;
|
||||
case QUERY_TYPE_DISTINCT:
|
||||
if((strcmp(pinfo->distinct_field,"artist") == 0)) {
|
||||
pqi->query_type = queryTypeBrowseArtists;
|
||||
} else if((strcmp(pinfo->distinct_field,"genre") == 0)) {
|
||||
pqi->query_type = queryTypeBrowseGenres;
|
||||
} else if((strcmp(pinfo->distinct_field,"album") == 0)) {
|
||||
pqi->query_type = queryTypeBrowseAlbums;
|
||||
} else if((strcmp(pinfo->distinct_field,"composer") == 0)) {
|
||||
pqi->query_type = queryTypeBrowseComposers;
|
||||
} else {
|
||||
if(pe) *pe = strdup("Unsupported browse type");
|
||||
if(pqi->pt)
|
||||
sp_dispose(pqi->pt);
|
||||
pqi->pt = NULL;
|
||||
return -1; /* not really a db error for this */
|
||||
}
|
||||
break;
|
||||
case QUERY_TYPE_ITEMS:
|
||||
default:
|
||||
pqi->query_type = queryTypePlaylistItems;
|
||||
pqi->correct_order = conf_get_int("scan","correct_order",1);
|
||||
break;
|
||||
}
|
||||
|
||||
pqi->playlist_id = pinfo->playlist_id;
|
||||
result = db_enum_start(pe, pqi);
|
||||
pinfo->totalcount = pqi->specifiedtotalcount;
|
||||
|
||||
return DB_E_SUCCESS;
|
||||
}
|
||||
|
||||
EXPORT int pi_db_enum_fetch_row(char **pe, char ***row, DB_QUERY *pinfo) {
|
||||
return db_enum_fetch_row(pe, (PACKED_MP3FILE**)row,
|
||||
(DBQUERYINFO*)pinfo->priv);
|
||||
}
|
||||
|
||||
EXPORT int pi_db_enum_end(char **pe) {
|
||||
return db_enum_end(pe);
|
||||
}
|
||||
|
||||
EXPORT int pi_db_enum_restart(char **pe, DB_QUERY *pinfo) {
|
||||
DBQUERYINFO *pqi;
|
||||
|
||||
pqi = (DBQUERYINFO*)pinfo->priv;
|
||||
return db_enum_reset(pe,pqi);
|
||||
}
|
||||
|
||||
EXPORT void pi_db_enum_dispose(char **pe, DB_QUERY *pinfo) {
|
||||
DBQUERYINFO *pqi;
|
||||
|
||||
if(!pinfo)
|
||||
return;
|
||||
|
||||
if(pinfo->priv) {
|
||||
pqi = (DBQUERYINFO *)pinfo->priv;
|
||||
if(pqi->pt) {
|
||||
sp_dispose(pqi->pt);
|
||||
pqi->pt = NULL;
|
||||
}
|
||||
free(pqi);
|
||||
pinfo->priv = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT void pi_stream(WS_CONNINFO *pwsc, char *id) {
|
||||
int session = 0;
|
||||
MP3FILE *pmp3;
|
||||
IOHANDLE hfile;
|
||||
uint64_t bytes_copied=0;
|
||||
uint64_t real_len;
|
||||
uint64_t file_len;
|
||||
uint64_t offset=0;
|
||||
int item;
|
||||
char *urltemp;
|
||||
int ret;
|
||||
|
||||
/* stream out the song */
|
||||
ws_should_close(pwsc,1);
|
||||
|
||||
item = atoi(id);
|
||||
|
||||
if(ws_getrequestheader(pwsc,"range")) {
|
||||
offset=(off_t)atol(ws_getrequestheader(pwsc,"range") + 6);
|
||||
}
|
||||
|
||||
/* FIXME: error handling */
|
||||
pmp3=db_fetch_item(NULL,item);
|
||||
if(!pmp3) {
|
||||
DPRINTF(E_LOG,L_DAAP|L_WS|L_DB,"Could not find requested item %lu\n",item);
|
||||
config_set_status(pwsc,session,NULL);
|
||||
ws_returnerror(pwsc,404,"File Not Found");
|
||||
} else if (pi_should_transcode(pwsc,pmp3->codectype)) {
|
||||
/************************
|
||||
* Server side conversion
|
||||
************************/
|
||||
config_set_status(pwsc,session,
|
||||
"Transcoding '%s' (id %d)",
|
||||
pmp3->title,pmp3->id);
|
||||
|
||||
DPRINTF(E_WARN,L_WS,
|
||||
"Session %d: Streaming file '%s' to %s (offset %ld)\n",
|
||||
session,pmp3->fname, ws_hostname(pwsc),(long)offset);
|
||||
|
||||
/* estimate the real length of this thing */
|
||||
bytes_copied = plugin_ssc_transcode(pwsc,pmp3,offset,1);
|
||||
if(bytes_copied != -1)
|
||||
real_len = bytes_copied;
|
||||
|
||||
config_set_status(pwsc,session,NULL);
|
||||
db_dispose_item(pmp3);
|
||||
} else {
|
||||
/**********************
|
||||
* stream file normally
|
||||
**********************/
|
||||
if(pmp3->data_kind != 0) {
|
||||
ws_returnerror(pwsc,500,"Can't stream radio station");
|
||||
return;
|
||||
}
|
||||
|
||||
hfile = io_new();
|
||||
if(!hfile)
|
||||
DPRINTF(E_FATAL,L_WS,"Cannot allocate file handle\n");
|
||||
|
||||
urltemp = io_urlencode(pmp3->path);
|
||||
if (!urltemp)
|
||||
{
|
||||
ws_set_err(pwsc,E_WS_NATIVE);
|
||||
DPRINTF(E_WARN,L_WS,"Thread %d: Error opening %s: out of memory\n",
|
||||
ws_threadno(pwsc),pmp3->path);
|
||||
ws_returnerror(pwsc,404,"Not found");
|
||||
config_set_status(pwsc,session,NULL);
|
||||
db_dispose_item(pmp3);
|
||||
io_dispose(hfile);
|
||||
}
|
||||
|
||||
ret = io_open(hfile, "file://%s", urltemp);
|
||||
free(urltemp);
|
||||
if (!ret) {
|
||||
/* FIXME: ws_set_errstr */
|
||||
ws_set_err(pwsc,E_WS_NATIVE);
|
||||
DPRINTF(E_WARN,L_WS,"Thread %d: Error opening %s: %s\n",
|
||||
ws_threadno(pwsc),pmp3->path,io_errstr(hfile));
|
||||
ws_returnerror(pwsc,404,"Not found");
|
||||
config_set_status(pwsc,session,NULL);
|
||||
db_dispose_item(pmp3);
|
||||
io_dispose(hfile);
|
||||
} else {
|
||||
io_size(hfile,&real_len);
|
||||
file_len = real_len - offset;
|
||||
|
||||
DPRINTF(E_DBG,L_WS,"Thread %d: Length of file (remaining): %lld\n",
|
||||
ws_threadno(pwsc),file_len);
|
||||
|
||||
// DWB: fix content-type to correctly reflect data
|
||||
// content type (dmap tagged) should only be used on
|
||||
// dmap protocol requests, not the actually song data
|
||||
if(pmp3->type)
|
||||
ws_addresponseheader(pwsc,"Content-Type","audio/%s",pmp3->type);
|
||||
|
||||
ws_addresponseheader(pwsc,"Content-Length","%ld",(long)file_len);
|
||||
|
||||
if((ws_getrequestheader(pwsc,"user-agent")) &&
|
||||
(!strncmp(ws_getrequestheader(pwsc,"user-agent"),
|
||||
"Hifidelio",9))) {
|
||||
ws_addresponseheader(pwsc,"Connection","Keep-Alive");
|
||||
ws_should_close(pwsc,0);
|
||||
} else {
|
||||
ws_addresponseheader(pwsc,"Connection","Close");
|
||||
}
|
||||
|
||||
if(!offset)
|
||||
ws_writefd(pwsc,"HTTP/1.1 200 OK\r\n");
|
||||
else {
|
||||
ws_addresponseheader(pwsc,"Content-Range","bytes %ld-%ld/%ld",
|
||||
(long)offset,(long)real_len,
|
||||
(long)real_len+1);
|
||||
ws_writefd(pwsc,"HTTP/1.1 206 Partial Content\r\n");
|
||||
}
|
||||
|
||||
ws_emitheaders(pwsc);
|
||||
|
||||
config_set_status(pwsc,session,"Streaming '%s' (id %d)",
|
||||
pmp3->title, pmp3->id);
|
||||
DPRINTF(E_WARN,L_WS,"Session %d: Streaming file '%s' to %s (offset %d)\n",
|
||||
session,pmp3->fname, ws_hostname(pwsc),(long)offset);
|
||||
|
||||
if(offset) {
|
||||
DPRINTF(E_INF,L_WS,"Seeking to offset %ld\n",(long)offset);
|
||||
io_setpos(hfile,offset,SEEK_SET);
|
||||
}
|
||||
|
||||
if(!ws_copyfile(pwsc,hfile,&bytes_copied)) {
|
||||
/* FIXME: Get webserver error string */
|
||||
DPRINTF(E_INF,L_WS,"Error copying file to remote...\n");
|
||||
ws_should_close(pwsc,1);
|
||||
} else {
|
||||
DPRINTF(E_INF,L_WS,"Finished streaming file to remote: %lld bytes\n",
|
||||
bytes_copied);
|
||||
}
|
||||
|
||||
config_set_status(pwsc,session,NULL);
|
||||
io_close(hfile);
|
||||
io_dispose(hfile);
|
||||
db_dispose_item(pmp3);
|
||||
}
|
||||
}
|
||||
/* update play counts */
|
||||
if(bytes_copied >= (real_len * 80 / 100)) {
|
||||
db_playcount_increment(NULL,pmp3->id);
|
||||
if(!offset)
|
||||
config.stats.songs_served++; /* FIXME: remove stat races */
|
||||
}
|
||||
// free(pqi);
|
||||
}
|
||||
|
||||
EXPORT int pi_db_add_playlist(char **pe, char *name, int type, char *clause,
|
||||
char *path, int index, int *playlistid) {
|
||||
return db_add_playlist(pe, name, type, clause, path, index, playlistid);
|
||||
}
|
||||
|
||||
EXPORT int pi_db_add_playlist_item(char **pe, int playlistid, int songid) {
|
||||
return db_add_playlist_item(pe, playlistid, songid);
|
||||
}
|
||||
|
||||
EXPORT int pi_db_edit_playlist(char **pe, int id, char *name, char *clause) {
|
||||
return db_edit_playlist(pe, id, name, clause);
|
||||
}
|
||||
|
||||
EXPORT int pi_db_delete_playlist(char **pe, int playlistid) {
|
||||
return db_delete_playlist(pe, playlistid);
|
||||
}
|
||||
|
||||
EXPORT int pi_db_delete_playlist_item(char **pe, int playlistid, int songid) {
|
||||
return db_delete_playlist_item(pe, playlistid, songid);
|
||||
}
|
||||
|
||||
EXPORT int pi_db_revision(void) {
|
||||
return db_revision();
|
||||
}
|
||||
|
||||
EXPORT int pi_db_count_items(int what) {
|
||||
int count=0;
|
||||
char *pe = NULL;
|
||||
|
||||
switch(what) {
|
||||
case COUNT_SONGS:
|
||||
db_get_song_count(&pe,&count);
|
||||
break;
|
||||
case COUNT_PLAYLISTS:
|
||||
db_get_playlist_count(&pe,&count);
|
||||
break;
|
||||
}
|
||||
|
||||
if(pe) {
|
||||
DPRINTF(E_LOG,L_DB,"Error getting item count: %s\n",pe);
|
||||
free(pe);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
EXPORT int pi_db_wait_update(WS_CONNINFO *pwsc) {
|
||||
int clientver=1;
|
||||
int lastver=0;
|
||||
IO_WAITHANDLE hwait;
|
||||
uint32_t ms;
|
||||
|
||||
if(ws_getvar(pwsc,"revision-number")) {
|
||||
clientver=atoi(ws_getvar(pwsc,"revision-number"));
|
||||
}
|
||||
|
||||
/* wait for db_version to be stable for 30 seconds */
|
||||
hwait = io_wait_new();
|
||||
if(!hwait)
|
||||
DPRINTF(E_FATAL,L_MISC,"Can't get wait handle in db_wait_update\n");
|
||||
|
||||
/* FIXME: Move this up into webserver to avoid groping around
|
||||
* inside reserved data structures */
|
||||
|
||||
io_wait_add(hwait,pwsc->hclient,IO_WAIT_ERROR);
|
||||
|
||||
while((clientver == db_revision()) ||
|
||||
(lastver && (db_revision() != lastver))) {
|
||||
lastver = db_revision();
|
||||
|
||||
if(!io_wait(hwait,&ms) && (ms != 0)) {
|
||||
/* can't be ready for read, must be error */
|
||||
DPRINTF(E_DBG,L_DAAP,"Update session stopped\n");
|
||||
io_wait_dispose(hwait);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
io_wait_dispose(hwait);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
EXPORT char *pi_conf_alloc_string(char *section, char *key, char *dflt) {
|
||||
return conf_alloc_string(section, key, dflt);
|
||||
}
|
||||
|
||||
EXPORT void pi_conf_dispose_string(char *str) {
|
||||
free(str);
|
||||
}
|
||||
|
||||
EXPORT int pi_conf_get_int(char *section, char *key, int dflt) {
|
||||
return conf_get_int(section, key, dflt);
|
||||
}
|
||||
|
||||
EXPORT void pi_config_set_status(WS_CONNINFO *pwsc, int session, char *fmt, ...) {
|
||||
char *out;
|
||||
va_list ap;
|
||||
|
||||
ASSERT(fmt);
|
||||
if(!fmt)
|
||||
return;
|
||||
|
||||
va_start(ap,fmt);
|
||||
out = util_vasprintf(fmt,ap);
|
||||
va_end(ap);
|
||||
|
||||
config_set_status(pwsc, session, "%s", out);
|
||||
free(out);
|
||||
}
|
||||
|
||||
/**
|
||||
* allocte an io object
|
||||
*
|
||||
* @returns NULL on malloc error, IOHANDLE otherwise
|
||||
*/
|
||||
EXPORT IOHANDLE pi_io_new(void) {
|
||||
return io_new();
|
||||
}
|
||||
|
||||
/**
|
||||
* open an io object given a printf-style URI
|
||||
*
|
||||
* @param io io handle allocated with pi_io_new
|
||||
* @param fmt printf-style format string for URI (%U URI-encodes strings)
|
||||
* @returns TRUE on success, FALSE otherwise. use io_err* to get error info
|
||||
*/
|
||||
EXPORT int pi_io_open(IOHANDLE io, char *fmt, ...) {
|
||||
char uri_copy[4096];
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(uri_copy, sizeof(uri_copy), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return io_open(io, "%s", uri_copy);
|
||||
}
|
||||
|
||||
/**
|
||||
* close an open io_object
|
||||
*
|
||||
* @param io handle to close
|
||||
* @returns TRUE on success, FALSE otherwise
|
||||
*/
|
||||
EXPORT int pi_io_close(IOHANDLE io) {
|
||||
return io_close(io);
|
||||
}
|
||||
|
||||
/**
|
||||
* read from an open io handle
|
||||
*
|
||||
* @param io open handle to read from
|
||||
* @param buf buffer to read into
|
||||
* @param len length to read, on return: length read
|
||||
* @returns TRUE on success, FALSE otherwise, len set with bytes read
|
||||
*/
|
||||
EXPORT int pi_io_read(IOHANDLE io, unsigned char *buf, uint32_t *len){
|
||||
return io_read(io, buf, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* read from an io handle with timeout
|
||||
*
|
||||
* Returns FALSE on read error or timeout. Timeout versus
|
||||
* read error condition can be determined by ms. If ms is 0,
|
||||
* then a timeout condition occurred
|
||||
*
|
||||
* @param io open handle to read from
|
||||
* @param buf buffer to read into
|
||||
* @param len length to read, on return: length read
|
||||
* @param ms time to wait (in ms), on return: time left
|
||||
* @returns TRUE on success, FALSE on failure (or timeout)
|
||||
*/
|
||||
EXPORT int pi_io_read_timeout(IOHANDLE io, unsigned char *buf, uint32_t *len, uint32_t *ms) {
|
||||
return io_read_timeout(io, buf, len, ms);
|
||||
}
|
||||
|
||||
/**
|
||||
* write a block of data to an open io handle
|
||||
*
|
||||
* @param io io handle to write to
|
||||
* @param buf buffer to write from
|
||||
* @param len bytes to write to io handle, on return: bytes written
|
||||
* @returns TRUE on success, FALSE otherwise
|
||||
*/
|
||||
EXPORT int pi_io_write(IOHANDLE io, unsigned char *buf, uint32_t *len) {
|
||||
return io_write(io, buf, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* write a printf formatted string to io handle
|
||||
*
|
||||
* @param io io handle to write to
|
||||
* @param fmt printf style format specifier
|
||||
* @returns TRUE on success, FALSE otherwise
|
||||
*/
|
||||
EXPORT int pi_io_printf(IOHANDLE io, char *fmt, ...) {
|
||||
char *out;
|
||||
va_list ap;
|
||||
int result;
|
||||
|
||||
ASSERT(fmt);
|
||||
if(!fmt)
|
||||
return FALSE;
|
||||
|
||||
va_start(ap,fmt);
|
||||
out = util_vasprintf(fmt,ap);
|
||||
va_end(ap);
|
||||
|
||||
result = io_printf(io,"%s",out);
|
||||
free(out);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the (64-bit) size of a file. Built in URI's use seekability
|
||||
* to determine extent of file, so non-seekable io objects (sockets, etc)
|
||||
* will return IO_E_BADFN.
|
||||
*
|
||||
* @param io io handle to get size of
|
||||
* @param size returns the 64-bit size of file
|
||||
* @returns TRUE on success, FALSE on error (check io_errstr)
|
||||
*/
|
||||
EXPORT int pi_io_size(IOHANDLE io, uint64_t *size) {
|
||||
return io_size(io, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* set the position of file read handle. There are several limitations
|
||||
* to this. Non seekable handles (sockets, etc) won't seek, NOT
|
||||
* EVEN FORWARD!
|
||||
*
|
||||
* FIXME: seeking on buffered file handle makes explode
|
||||
*
|
||||
* @param io handle to set position of
|
||||
* @param offset how far to move
|
||||
* @param whence from where (in lseek style -- SEEK_SET, SEEK_CUR, SEEK_END)
|
||||
* @returns TRUE on success, FALSE otherwise (check io_errstr)
|
||||
*/
|
||||
EXPORT int pi_io_setpos(IOHANDLE io, uint64_t offset, int whence) {
|
||||
return io_setpos(io, offset, whence);
|
||||
}
|
||||
|
||||
/**
|
||||
* get current file position in a stream. Like setpos, this won't work on non-
|
||||
* streamable io handles, and it won't (currently) work on buffered file handles.
|
||||
* in addition, it might behave strangely various filter drivers (ssl, etc)
|
||||
*
|
||||
* @param io io handle to get position for
|
||||
* @param pos on return, the current file position
|
||||
* @returns TRUE on success, FALSE otherwise (see io_errstr)
|
||||
*/
|
||||
EXPORT int pi_io_getpos(IOHANDLE io, uint64_t *pos) {
|
||||
return io_getpos(io, pos);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* turn on buffering for a file handle. This really only makes sense
|
||||
* when doing readlines. Note that you can't currently turn of buffered
|
||||
* mode, so if doing a mix of buffered and unbuffered io, don't buffer the
|
||||
* handle. Also, once the handle is buffered, setpos and getpos won't work
|
||||
* right, and will very likely make Bad Things Happen. You have been
|
||||
* warned.
|
||||
*
|
||||
* @param io handle to buffer
|
||||
* @returns TRUE
|
||||
*/
|
||||
EXPORT int pi_io_buffer(IOHANDLE io) {
|
||||
return io_buffer(io);
|
||||
}
|
||||
|
||||
/**
|
||||
* read a line from the file handle. If the file is opened with
|
||||
* ascii=1, then line ending conversions to/from windows/unix will
|
||||
* take place.
|
||||
*
|
||||
* @param io handle to read line from
|
||||
* @param buf buffer to read line into
|
||||
* @param len size of buffer, on return: bytes read
|
||||
* @returns TRUE on success, FALSE on error (see io_errstr)
|
||||
*/
|
||||
EXPORT int pi_io_readline(IOHANDLE io, unsigned char *buf, uint32_t *len) {
|
||||
return io_readline(io, buf, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* read a line from a file handle with timeout.
|
||||
*
|
||||
* Errors (including timeout) return FALSE. Timeout errors
|
||||
* can be detected because ms=0
|
||||
*
|
||||
* @param io handle to read from
|
||||
* @param buf buffer to read into
|
||||
* @param len size of buffer, on return: bytes read
|
||||
* @param ms timeout, in ms, on return: time remaining
|
||||
* @returns TRUE on success, FALSE otherwise
|
||||
*/
|
||||
EXPORT int pi_io_readline_timeout(IOHANDLE io, unsigned char *buf, uint32_t *len, uint32_t *ms) {
|
||||
return io_readline_timeout(io, buf, len, ms);
|
||||
}
|
||||
|
||||
/**
|
||||
* get error string of last error
|
||||
*
|
||||
* @param io handle to get error of
|
||||
* @returns error string, does not need to be free'd
|
||||
*/
|
||||
EXPORT char* pi_io_errstr(IOHANDLE io) {
|
||||
return io_errstr(io);
|
||||
}
|
||||
|
||||
/**
|
||||
* get native error code
|
||||
*
|
||||
* @param io handle to get error code for
|
||||
* @returns error code (see io-errors.h)
|
||||
*/
|
||||
EXPORT int pi_io_errcode(IOHANDLE io) {
|
||||
return io_errcode(io);
|
||||
}
|
||||
|
||||
/**
|
||||
* dispose of an io handle, freeing up any internally allocated
|
||||
* memory and structs. This implicitly calls io_close, so
|
||||
* you don't *need* to do io_close(hio); io_dispoase(hio);, but I
|
||||
* do anyway. :)
|
||||
*
|
||||
* @param io handle to dispose of
|
||||
*/
|
||||
EXPORT void pi_io_dispose(IOHANDLE io) {
|
||||
return io_dispose(io);
|
||||
}
|
176
src/ff-plugins.h
176
src/ff-plugins.h
@ -1,176 +0,0 @@
|
||||
/*
|
||||
* Public plug-in interface
|
||||
*
|
||||
* Copyright (C) 2006 Ron Pedde (ron@pedde.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _FF_PLUGINS_H_
|
||||
#define _FF_PLUGINS_H_
|
||||
|
||||
#include "ff-dbstruct.h"
|
||||
#include "ff-plugin-events.h"
|
||||
#include "smart-parser.h"
|
||||
|
||||
#define EXPORT
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
#ifndef E_FATAL
|
||||
# define E_FATAL 0
|
||||
# define E_LOG 1
|
||||
# define E_WARN 2
|
||||
# define E_INF 5
|
||||
# define E_DBG 9
|
||||
# define E_SPAM 10
|
||||
#endif
|
||||
|
||||
#define COUNT_SONGS 0
|
||||
#define COUNT_PLAYLISTS 1
|
||||
|
||||
struct tag_ws_conninfo;
|
||||
typedef void* HANDLE;
|
||||
|
||||
/* Functions that must be exported by different plugin types */
|
||||
typedef struct tag_plugin_output_fn {
|
||||
int(*can_handle)(struct tag_ws_conninfo *pwsc);
|
||||
void(*handler)(struct tag_ws_conninfo *pwsc);
|
||||
int(*auth)(struct tag_ws_conninfo *pwsc, char *username, char *pw);
|
||||
} PLUGIN_OUTPUT_FN;
|
||||
|
||||
typedef struct tag_plugin_event_fn {
|
||||
void(*handler)(int event_id, int intval, void *vp, int len);
|
||||
} PLUGIN_EVENT_FN;
|
||||
|
||||
typedef struct tag_plugin_transcode_fn {
|
||||
void *(*ssc_init)(void);
|
||||
void (*ssc_deinit)(void*);
|
||||
int (*ssc_open)(void*, MP3FILE *);
|
||||
int (*ssc_close)(void*);
|
||||
int (*ssc_read)(void*, char*, int);
|
||||
char *(*ssc_error)(void*);
|
||||
} PLUGIN_TRANSCODE_FN;
|
||||
|
||||
/* info for rendezvous advertising */
|
||||
typedef struct tag_plugin_rend_info {
|
||||
char *type;
|
||||
char **txt;
|
||||
} PLUGIN_REND_INFO;
|
||||
|
||||
/* main info struct that plugins must provide */
|
||||
typedef struct tag_plugin_info {
|
||||
int version; /* PLUGIN_VERSION */
|
||||
int type; /* PLUGIN_OUTPUT, etc */
|
||||
char *server; /* Server/version format */
|
||||
PLUGIN_OUTPUT_FN *output_fns; /* functions for different plugin types */
|
||||
PLUGIN_EVENT_FN *event_fns;
|
||||
PLUGIN_TRANSCODE_FN *transcode_fns;
|
||||
PLUGIN_REND_INFO *rend_info; /* array of rend announcements */
|
||||
char *codeclist; /* comma separated list of codecs */
|
||||
} PLUGIN_INFO;
|
||||
|
||||
|
||||
#define QUERY_TYPE_ITEMS 0
|
||||
#define QUERY_TYPE_PLAYLISTS 1
|
||||
#define QUERY_TYPE_DISTINCT 2
|
||||
|
||||
typedef struct tag_db_query {
|
||||
int query_type;
|
||||
char *distinct_field;
|
||||
int filter_type;
|
||||
char *filter;
|
||||
|
||||
int offset;
|
||||
int limit;
|
||||
|
||||
int playlist_id; /* for items query */
|
||||
int totalcount; /* returned total count */
|
||||
void *priv;
|
||||
} DB_QUERY;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* webserver functions */
|
||||
extern EXPORT char *pi_ws_uri(struct tag_ws_conninfo *);
|
||||
extern EXPORT void pi_ws_will_close(struct tag_ws_conninfo *);
|
||||
extern EXPORT int pi_ws_returnerror(struct tag_ws_conninfo *, int, char *);
|
||||
extern EXPORT char *pi_ws_getvar(struct tag_ws_conninfo *, char *);
|
||||
extern EXPORT int pi_ws_writefd(struct tag_ws_conninfo *, char *, ...);
|
||||
extern EXPORT int pi_ws_addresponseheader(struct tag_ws_conninfo *, char *, char *, ...);
|
||||
extern EXPORT void pi_ws_emitheaders(struct tag_ws_conninfo *);
|
||||
extern EXPORT char *pi_ws_getrequestheader(struct tag_ws_conninfo *, char *);
|
||||
extern EXPORT int pi_ws_writebinary(struct tag_ws_conninfo *, char *, int);
|
||||
extern EXPORT char *pi_ws_gethostname(struct tag_ws_conninfo *);
|
||||
extern EXPORT int pi_ws_matchesrole(struct tag_ws_conninfo *, char *, char *, char *);
|
||||
|
||||
/* misc helpers */
|
||||
extern EXPORT char *pi_server_ver(void);
|
||||
extern EXPORT int pi_server_name(char *, int *);
|
||||
extern EXPORT void pi_log(int, char *, ...);
|
||||
extern EXPORT int pi_should_transcode(struct tag_ws_conninfo *, char *);
|
||||
|
||||
/* db functions */
|
||||
extern EXPORT int pi_db_enum_start(char **, DB_QUERY *);
|
||||
extern EXPORT int pi_db_enum_fetch_row(char **, char ***, DB_QUERY *);
|
||||
extern EXPORT int pi_db_enum_end(char **);
|
||||
extern EXPORT int pi_db_enum_restart(char **, DB_QUERY *);
|
||||
extern EXPORT void pi_db_enum_dispose(char **, DB_QUERY*);
|
||||
extern EXPORT void pi_stream(struct tag_ws_conninfo *, char *);
|
||||
|
||||
extern EXPORT int pi_db_add_playlist(char **pe, char *name, int type, char *clause, char *path, int index, int *playlistid);
|
||||
extern EXPORT int pi_db_add_playlist_item(char **pe, int playlistid, int songid);
|
||||
extern EXPORT int pi_db_edit_playlist(char **pe, int id, char *name, char *clause);
|
||||
extern EXPORT int pi_db_delete_playlist(char **pe, int playlistid);
|
||||
extern EXPORT int pi_db_delete_playlist_item(char **pe, int playlistid, int songid);
|
||||
extern EXPORT int pi_db_revision(void);
|
||||
extern EXPORT int pi_db_count_items(int what);
|
||||
extern EXPORT int pi_db_wait_update(struct tag_ws_conninfo *);
|
||||
|
||||
/* config/misc functions */
|
||||
extern EXPORT char *pi_conf_alloc_string(char *section, char *key, char *dflt);
|
||||
extern EXPORT void pi_conf_dispose_string(char *str);
|
||||
extern EXPORT int pi_conf_get_int(char *section, char *key, int dflt);
|
||||
extern EXPORT void pi_config_set_status(struct tag_ws_conninfo *pwsc, int session, char *fmt, ...);
|
||||
|
||||
/* io functions */
|
||||
extern EXPORT HANDLE pi_io_new(void);
|
||||
extern EXPORT int pi_io_open(HANDLE io, char *fmt, ...);
|
||||
extern EXPORT int pi_io_close(HANDLE io);
|
||||
extern EXPORT int pi_io_read(HANDLE io, unsigned char *buf, uint32_t *len);
|
||||
extern EXPORT int pi_io_read_timeout(HANDLE io, unsigned char *buf, uint32_t *len, uint32_t *ms);
|
||||
extern EXPORT int pi_io_write(HANDLE io, unsigned char *buf, uint32_t *len);
|
||||
extern EXPORT int pi_io_printf(HANDLE io, char *fmt, ...);
|
||||
extern EXPORT int pi_io_size(HANDLE io, uint64_t *size);
|
||||
extern EXPORT int pi_io_setpos(HANDLE io, uint64_t offset, int whence);
|
||||
extern EXPORT int pi_io_getpos(HANDLE io, uint64_t *pos);
|
||||
extern EXPORT int pi_io_buffer(HANDLE io); /* unimplemented */
|
||||
extern EXPORT int pi_io_readline(HANDLE io, unsigned char *buf, uint32_t *len);
|
||||
extern EXPORT int pi_io_readline_timeout(HANDLE io, unsigned char *buf, uint32_t *len, uint32_t *ms);
|
||||
extern EXPORT char* pi_io_errstr(HANDLE io);
|
||||
extern EXPORT int pi_io_errcode(HANDLE io);
|
||||
extern EXPORT void pi_io_dispose(HANDLE io);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* _FF_PLUGINS_ */
|
615
src/plugin.c
615
src/plugin.c
@ -1,615 +0,0 @@
|
||||
/*
|
||||
* Simple plug-in api for output, transcode, and scanning plug-ins
|
||||
*
|
||||
* Copyright (C) 2006 Ron Pedde (ron@pedde.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#define _XOPEN_SOURCE 500 /** unix98? pthread_once_t, etc */
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#ifdef HAVE_STDINT_H
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRINGS_H
|
||||
#include <strings.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_SELECT_H
|
||||
#include <sys/select.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "daapd.h"
|
||||
#include "conf.h"
|
||||
#include "configfile.h"
|
||||
#include "db-generic.h"
|
||||
#include "err.h"
|
||||
#include "io.h"
|
||||
#include "plugin.h"
|
||||
#include "smart-parser.h"
|
||||
#include "xml-rpc.h"
|
||||
#include "webserver.h"
|
||||
#include "ff-plugins.h"
|
||||
#include "io.h"
|
||||
|
||||
#include "mdns_avahi.h"
|
||||
|
||||
typedef struct tag_pluginentry {
|
||||
void *phandle;
|
||||
PLUGIN_INFO *pinfo;
|
||||
struct tag_pluginentry *next;
|
||||
} PLUGIN_ENTRY;
|
||||
|
||||
/* Globals */
|
||||
static PLUGIN_ENTRY _plugin_list;
|
||||
static int _plugin_initialized = 0;
|
||||
static char *_plugin_ssc_codecs = NULL;
|
||||
|
||||
static char* _plugin_error_list[] = {
|
||||
"Success.",
|
||||
"Could not load plugin: %s",
|
||||
"Plugin missing required export: plugin_type/plugin_ver"
|
||||
};
|
||||
|
||||
/* Forwards */
|
||||
void _plugin_readlock(void);
|
||||
void _plugin_writelock(void);
|
||||
void _plugin_unlock(void);
|
||||
int _plugin_error(char **pe, int error, ...);
|
||||
void _plugin_free(int *pi);
|
||||
void _plugin_recalc_codecs(void);
|
||||
int _plugin_ssc_transcode(WS_CONNINFO *pwsc, MP3FILE *pmp3, int offset, int headers);
|
||||
|
||||
/**
|
||||
* initialize stuff for plugins
|
||||
*
|
||||
* @returns TRUE on success, FALSE otherwise
|
||||
*/
|
||||
int plugin_init(void) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* free the tls
|
||||
*/
|
||||
void _plugin_free(int *pi) {
|
||||
if(pi)
|
||||
free(pi);
|
||||
}
|
||||
|
||||
/**
|
||||
* deinitialize stuff for plugins
|
||||
*
|
||||
* @returns TRUE on success, FALSE otherwise
|
||||
*/
|
||||
int plugin_deinit(void) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* return the error
|
||||
*
|
||||
* @param pe buffer to store the error string in
|
||||
* @param error error to return
|
||||
* @returns the specified error,to thelp with returns
|
||||
*/
|
||||
int _plugin_error(char **pe, int error, ...) {
|
||||
va_list ap;
|
||||
char errbuf[1024];
|
||||
|
||||
if(!pe)
|
||||
return error;
|
||||
|
||||
va_start(ap, error);
|
||||
vsnprintf(errbuf, sizeof(errbuf), _plugin_error_list[error], ap);
|
||||
va_end(ap);
|
||||
|
||||
DPRINTF(E_SPAM,L_PLUG,"Raising error: %s\n",errbuf);
|
||||
|
||||
*pe = strdup(errbuf);
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* walk through the installed plugins and recalculate
|
||||
* the codec string
|
||||
*/
|
||||
void _plugin_recalc_codecs(void) {
|
||||
PLUGIN_ENTRY *ppi;
|
||||
size_t size=0;
|
||||
|
||||
ppi = _plugin_list.next;
|
||||
while(ppi) {
|
||||
if(ppi->pinfo->type & PLUGIN_TRANSCODE) {
|
||||
if(size) size++;
|
||||
size += strlen(ppi->pinfo->codeclist);
|
||||
}
|
||||
ppi=ppi->next;
|
||||
}
|
||||
|
||||
if(_plugin_ssc_codecs) {
|
||||
free(_plugin_ssc_codecs);
|
||||
}
|
||||
|
||||
_plugin_ssc_codecs = (char*)malloc(size+1);
|
||||
if(!_plugin_ssc_codecs) {
|
||||
DPRINTF(E_FATAL,L_PLUG,"_plugin_recalc_codecs: malloc\n");
|
||||
}
|
||||
|
||||
memset(_plugin_ssc_codecs,0,size+1);
|
||||
|
||||
ppi = _plugin_list.next;
|
||||
while(ppi) {
|
||||
if(ppi->pinfo->type & PLUGIN_TRANSCODE) {
|
||||
if(strlen(_plugin_ssc_codecs)) {
|
||||
strcat(_plugin_ssc_codecs,",");
|
||||
}
|
||||
strcat(_plugin_ssc_codecs,ppi->pinfo->codeclist);
|
||||
}
|
||||
ppi=ppi->next;
|
||||
}
|
||||
|
||||
DPRINTF(E_DBG,L_PLUG,"New transcode codec list: %s\n",_plugin_ssc_codecs);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* plugin_get_description
|
||||
*/
|
||||
char *plugin_get_description(void *which) {
|
||||
PLUGIN_ENTRY *ppi = (PLUGIN_ENTRY *)which;
|
||||
|
||||
return ppi->pinfo->server;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* walk through the loaded plugin list
|
||||
*/
|
||||
void *plugin_enum(void *where) {
|
||||
PLUGIN_ENTRY *ppi = (PLUGIN_ENTRY *)where;
|
||||
|
||||
if(!ppi) {
|
||||
// _plugin_readlock();
|
||||
return (void*) _plugin_list.next;
|
||||
}
|
||||
|
||||
if(!ppi->next) {
|
||||
// _plugin_unlock();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (void*) ppi->next;
|
||||
}
|
||||
|
||||
/**
|
||||
* load a specified plugin.
|
||||
*
|
||||
* @param pe pointer to error string returned (if error)
|
||||
* @param plugin path to plugin to load
|
||||
*
|
||||
* return PLUGIN_E_SUCCESS, or not, with pe set
|
||||
*/
|
||||
int plugin_load(char **pe, char *path) {
|
||||
PLUGIN_ENTRY *ppi;
|
||||
void *phandle;
|
||||
PLUGIN_INFO *(*info_func)(void);
|
||||
PLUGIN_INFO *pinfo;
|
||||
|
||||
DPRINTF(E_DBG,L_PLUG,"Attempting to load plugin %s\n",path);
|
||||
|
||||
phandle = dlopen(path, RTLD_NOW);
|
||||
if (!phandle) {
|
||||
*pe = strdup(dlerror());
|
||||
DPRINTF(E_INF,L_PLUG,"Couldn't get lib handle for %s\n",path);
|
||||
return PLUGIN_E_NOLOAD;
|
||||
}
|
||||
|
||||
ppi = (PLUGIN_ENTRY*)malloc(sizeof(PLUGIN_ENTRY));
|
||||
memset(ppi,0x00,sizeof(PLUGIN_ENTRY));
|
||||
|
||||
ppi->phandle = phandle;
|
||||
|
||||
info_func = (PLUGIN_INFO*(*)(void)) dlsym(phandle, "plugin_info");
|
||||
if(info_func == NULL) {
|
||||
*pe = strdup(dlerror());
|
||||
DPRINTF(E_INF,L_PLUG,"Couldn't get info_func for %s\n",path);
|
||||
dlclose(phandle);
|
||||
free(ppi);
|
||||
return PLUGIN_E_BADFUNCS;
|
||||
}
|
||||
|
||||
pinfo = info_func();
|
||||
ppi->pinfo = pinfo;
|
||||
|
||||
if(!pinfo) {
|
||||
if(pe) *pe = strdup("plugin declined to load");
|
||||
dlclose(phandle);
|
||||
free(ppi);
|
||||
return PLUGIN_E_NOLOAD;
|
||||
}
|
||||
|
||||
if(pinfo->version != PLUGIN_VERSION) {
|
||||
DPRINTF(E_INF,L_PLUG,"Plugin is too old: version %d, expecting %d\n",
|
||||
pinfo->version, PLUGIN_VERSION);
|
||||
dlclose(phandle);
|
||||
free(ppi);
|
||||
return PLUGIN_E_NOLOAD;
|
||||
}
|
||||
|
||||
DPRINTF(E_INF,L_PLUG,"Loaded plugin %s (%s)\n",path,pinfo->server);
|
||||
|
||||
if(!_plugin_initialized) {
|
||||
_plugin_initialized = 1;
|
||||
memset((void*)&_plugin_list,0,sizeof(_plugin_list));
|
||||
}
|
||||
|
||||
ppi->next = _plugin_list.next;
|
||||
_plugin_list.next = ppi;
|
||||
|
||||
_plugin_recalc_codecs();
|
||||
return PLUGIN_E_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* check to see if we want to dispatch a particular url
|
||||
*
|
||||
* @param pwsc the connection info (including uri) to check
|
||||
* @returns TRUE if we want to handle it
|
||||
*/
|
||||
int plugin_url_candispatch(WS_CONNINFO *pwsc) {
|
||||
PLUGIN_ENTRY *ppi;
|
||||
|
||||
ppi = _plugin_list.next;
|
||||
while(ppi) {
|
||||
if(ppi->pinfo->type & PLUGIN_OUTPUT) {
|
||||
if((ppi->pinfo->output_fns)->can_handle(pwsc)) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
ppi = ppi->next;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* actually DISPATCH the hander we said we wanted
|
||||
*
|
||||
* @param pwsc the connection info (including uri) to check
|
||||
* @returns TRUE if we want to handle it
|
||||
*/
|
||||
void plugin_url_handle(WS_CONNINFO *pwsc) {
|
||||
PLUGIN_ENTRY *ppi;
|
||||
void (*disp_fn)(WS_CONNINFO *pwsc);
|
||||
|
||||
ppi = _plugin_list.next;
|
||||
while(ppi) {
|
||||
if(ppi->pinfo->type & PLUGIN_OUTPUT) {
|
||||
if((ppi->pinfo->output_fns)->can_handle(pwsc)) {
|
||||
/* we have a winner */
|
||||
DPRINTF(E_DBG,L_PLUG,"Dispatching %s to %s\n", ws_uri(pwsc),
|
||||
ppi->pinfo->server);
|
||||
|
||||
/* so functions must be a tag_plugin_output_fn */
|
||||
disp_fn=(ppi->pinfo->output_fns)->handler;
|
||||
disp_fn(pwsc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
ppi = ppi->next;
|
||||
}
|
||||
|
||||
/* should 500 here or something */
|
||||
ws_returnerror(pwsc, 500, "Can't find plugin handler");
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* walk through the plugins and register whatever rendezvous
|
||||
* names the clients want
|
||||
*/
|
||||
int plugin_rend_register(char *name, int port, char **txt) {
|
||||
PLUGIN_ENTRY *ppi;
|
||||
PLUGIN_REND_INFO *pri;
|
||||
char **supplied_txt;
|
||||
|
||||
ppi = _plugin_list.next;
|
||||
|
||||
while(ppi) {
|
||||
DPRINTF(E_DBG,L_PLUG,"Checking %s\n",ppi->pinfo->server);
|
||||
if(ppi->pinfo->rend_info) {
|
||||
pri = ppi->pinfo->rend_info;
|
||||
while(pri->type) {
|
||||
supplied_txt = pri->txt;
|
||||
if(!pri->txt)
|
||||
supplied_txt = txt;
|
||||
|
||||
DPRINTF(E_DBG,L_PLUG,"Registering %s\n",pri->type);
|
||||
mdns_register(name, pri->type, port, supplied_txt);
|
||||
pri++;
|
||||
}
|
||||
}
|
||||
ppi=ppi->next;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test password for the handled namespace
|
||||
*
|
||||
* @param pwsc the connection info (including uri) to check
|
||||
* @param username user attempting to login
|
||||
* @param pw password attempting
|
||||
* @returns TRUE if we want to handle it
|
||||
*/
|
||||
int plugin_auth_handle(WS_CONNINFO *pwsc, char *username, char *pw) {
|
||||
PLUGIN_ENTRY *ppi;
|
||||
int (*auth_fn)(WS_CONNINFO *pwsc, char *username, char *pw);
|
||||
int result;
|
||||
|
||||
ppi = _plugin_list.next;
|
||||
while(ppi) {
|
||||
if(ppi->pinfo->type & PLUGIN_OUTPUT) {
|
||||
if((ppi->pinfo->output_fns)->can_handle(pwsc)) {
|
||||
/* we have a winner */
|
||||
DPRINTF(E_DBG,L_PLUG,"Dispatching %s to %s\n", ws_uri(pwsc),
|
||||
ppi->pinfo->server);
|
||||
|
||||
/* so functions must be a tag_plugin_output_fn */
|
||||
auth_fn=(ppi->pinfo->output_fns)->auth;
|
||||
if(auth_fn) {
|
||||
result=auth_fn(pwsc,username,pw);
|
||||
return result;
|
||||
} else {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
ppi = ppi->next;
|
||||
}
|
||||
|
||||
/* should 500 here or something */
|
||||
ws_returnerror(pwsc, 500, "Can't find plugin handler");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* send an event to a plugin... this can be a connection, disconnection, etc.
|
||||
*/
|
||||
void plugin_event_dispatch(int event_id, int intval, void *vp, int len) {
|
||||
PLUGIN_ENTRY *ppi;
|
||||
|
||||
ppi = _plugin_list.next;
|
||||
while(ppi) {
|
||||
if(ppi->pinfo->type & PLUGIN_EVENT) {
|
||||
/* DPRINTF(E_DBG,L_PLUG,"Dispatching event %d to %s\n",
|
||||
event_id,ppi->versionstring); */
|
||||
|
||||
if((ppi->pinfo->event_fns) && (ppi->pinfo->event_fns->handler)) {
|
||||
ppi->pinfo->event_fns->handler(event_id, intval, vp, len);
|
||||
}
|
||||
}
|
||||
ppi=ppi->next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* stupid helper to copy transcode stream to the fd
|
||||
*/
|
||||
int __plugin_ssc_copy(WS_CONNINFO *pwsc, PLUGIN_TRANSCODE_FN *pfn,
|
||||
void *vp,int offset) {
|
||||
int bytes_read;
|
||||
int bytes_to_read;
|
||||
int total_bytes_read = 0;
|
||||
char buffer[1024];
|
||||
|
||||
/* first, skip past the offset */
|
||||
while(offset) {
|
||||
bytes_to_read = sizeof(buffer);
|
||||
if(bytes_to_read > offset)
|
||||
bytes_to_read = offset;
|
||||
|
||||
bytes_read = pfn->ssc_read(vp,buffer,bytes_to_read);
|
||||
if(bytes_read <= 0)
|
||||
return bytes_read;
|
||||
|
||||
offset -= bytes_read;
|
||||
}
|
||||
|
||||
while((bytes_read=pfn->ssc_read(vp,buffer,sizeof(buffer))) > 0) {
|
||||
total_bytes_read += bytes_read;
|
||||
if(ws_writebinary(pwsc,buffer,bytes_read) != bytes_read) {
|
||||
return total_bytes_read;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if(bytes_read < 0) {
|
||||
return bytes_read;
|
||||
}
|
||||
*/
|
||||
|
||||
return total_bytes_read;
|
||||
}
|
||||
|
||||
/**
|
||||
* do the transcode, emitting the headers, content type,
|
||||
* and shoving the file down the wire
|
||||
*
|
||||
* @param pwsc connection to transcode to
|
||||
* @param file file to transcode
|
||||
* @param codec source codec
|
||||
* @param duration time in ms
|
||||
* @returns bytes transferred, or -1 on error
|
||||
*/
|
||||
int plugin_ssc_transcode(WS_CONNINFO *pwsc, MP3FILE *pmp3, int offset, int headers) {
|
||||
PLUGIN_ENTRY *ppi, *ptc=NULL;
|
||||
PLUGIN_TRANSCODE_FN *pfn = NULL;
|
||||
void *vp_ssc;
|
||||
int post_error = 1;
|
||||
int result = -1;
|
||||
|
||||
/* first, find the plugin that will do the conversion */
|
||||
ppi = _plugin_list.next;
|
||||
while((ppi) && (!pfn)) {
|
||||
if(ppi->pinfo->type & PLUGIN_TRANSCODE) {
|
||||
if(strstr(ppi->pinfo->codeclist,pmp3->codectype)) {
|
||||
ptc = ppi;
|
||||
pfn = ppi->pinfo->transcode_fns;
|
||||
}
|
||||
}
|
||||
ppi = ppi->next;
|
||||
}
|
||||
|
||||
if(pfn) {
|
||||
DPRINTF(E_DBG,L_PLUG,"Transcoding %s with %s\n",pmp3->path,
|
||||
ptc->pinfo->server);
|
||||
|
||||
vp_ssc = pfn->ssc_init();
|
||||
if(vp_ssc) {
|
||||
if(pfn->ssc_open(vp_ssc,pmp3)) {
|
||||
/* start reading and throwing */
|
||||
if(headers) {
|
||||
ws_addresponseheader(pwsc,"Content-Type","audio/wav");
|
||||
ws_addresponseheader(pwsc,"Connection","Close");
|
||||
if(!offset) {
|
||||
ws_writefd(pwsc,"HTTP/1.1 200 OK\r\n");
|
||||
} else {
|
||||
ws_addresponseheader(pwsc,"Content-Range",
|
||||
"bytes %ld-*/*",
|
||||
(long)offset);
|
||||
ws_writefd(pwsc,"HTTP/1.1 206 Partial Content\r\n");
|
||||
}
|
||||
ws_emitheaders(pwsc);
|
||||
}
|
||||
|
||||
/* start reading/writing */
|
||||
result = __plugin_ssc_copy(pwsc,pfn,vp_ssc,offset);
|
||||
post_error = 0;
|
||||
pfn->ssc_close(vp_ssc);
|
||||
} else {
|
||||
DPRINTF(E_LOG,L_PLUG,"Error opening %s for ssc: %s\n",
|
||||
pmp3->path,pfn->ssc_error(vp_ssc));
|
||||
}
|
||||
pfn->ssc_deinit(vp_ssc);
|
||||
} else {
|
||||
DPRINTF(E_LOG,L_PLUG,"Error initializing transcoder: %s\n",
|
||||
ptc->pinfo->server);
|
||||
}
|
||||
}
|
||||
|
||||
if(post_error) {
|
||||
pwsc->error = EPERM; /* ?? */
|
||||
ws_returnerror(pwsc,500,"Internal error");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int plugin_ssc_should_transcode(WS_CONNINFO *pwsc, char *codec) {
|
||||
int result;
|
||||
char *native_codecs=NULL;
|
||||
char *user_agent=NULL;
|
||||
char *never_transcode = NULL;
|
||||
char *always_transcode = NULL;
|
||||
|
||||
ASSERT((pwsc) && (codec));
|
||||
|
||||
if(!pwsc)
|
||||
return FALSE;
|
||||
|
||||
if(!codec) {
|
||||
DPRINTF(E_LOG,L_PLUG,"testing transcode on null codec?\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
never_transcode = conf_alloc_string("general","never_transcode",NULL);
|
||||
if(never_transcode) {
|
||||
if(strstr(never_transcode,codec)) {
|
||||
free(never_transcode);
|
||||
return FALSE;
|
||||
}
|
||||
free(never_transcode);
|
||||
}
|
||||
|
||||
if(pwsc) {
|
||||
/* see if the headers give us any guidance */
|
||||
native_codecs = ws_getrequestheader(pwsc,"accept-codecs");
|
||||
if(!native_codecs) {
|
||||
user_agent = ws_getrequestheader(pwsc,"user-agent");
|
||||
if(user_agent) {
|
||||
if(strncmp(user_agent,"iTunes",6)==0) {
|
||||
native_codecs = "mpeg,mp4a,wav,mp4v,alac";
|
||||
} else if(strncmp(user_agent,"Roku",4)==0) {
|
||||
native_codecs = "mpeg,mp4a,wav,wma";
|
||||
} else if(strncmp(user_agent,"Hifidelio",9)==0) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!native_codecs) {
|
||||
native_codecs = "mpeg,wav";
|
||||
}
|
||||
|
||||
/* can't transcode it if we can't transcode it */
|
||||
if(!_plugin_ssc_codecs)
|
||||
return FALSE;
|
||||
|
||||
always_transcode = conf_alloc_string("general","always_transcode",NULL);
|
||||
if(always_transcode) {
|
||||
if(strstr(always_transcode,codec)) {
|
||||
free(always_transcode);
|
||||
return TRUE;
|
||||
}
|
||||
free(always_transcode);
|
||||
}
|
||||
|
||||
if(strstr(native_codecs,codec))
|
||||
return FALSE;
|
||||
|
||||
result = FALSE;
|
||||
if(strstr(_plugin_ssc_codecs,codec)) {
|
||||
result = TRUE;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
52
src/plugin.h
52
src/plugin.h
@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Simple plug-in api for output, transcode, and scanning plug-ins
|
||||
*
|
||||
* Copyright (C) 2006 Ron Pedde (ron@pedde.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _PLUGIN_H_
|
||||
#define _PLUGIN_H_
|
||||
|
||||
#include "webserver.h"
|
||||
#include "xml-rpc.h"
|
||||
#include "db-generic.h"
|
||||
#include "ff-plugin-events.h"
|
||||
|
||||
extern int plugin_init(void);
|
||||
extern int plugin_load(char **pe, char *path);
|
||||
extern int plugin_deinit(void);
|
||||
|
||||
/* Interfaces for web */
|
||||
extern int plugin_url_candispatch(WS_CONNINFO *pwsc);
|
||||
extern void plugin_url_handle(WS_CONNINFO *pwsc);
|
||||
extern int plugin_auth_handle(WS_CONNINFO *pwsc, char *username, char *pw);
|
||||
extern int plugin_rend_register(char *name, int port, char **txt);
|
||||
extern void plugin_event_dispatch(int event_id, int intval, void *vp, int len);
|
||||
|
||||
extern void *plugin_enum(void *);
|
||||
extern char *plugin_get_description(void *);
|
||||
|
||||
extern int plugin_ssc_should_transcode(WS_CONNINFO *pwsc, char *codec);
|
||||
extern int plugin_ssc_transcode(WS_CONNINFO *pwsc, MP3FILE *pmp3, int offset, int headers);
|
||||
|
||||
/* these should really get rows */
|
||||
|
||||
#define PLUGIN_E_SUCCESS 0
|
||||
#define PLUGIN_E_NOLOAD 1
|
||||
#define PLUGIN_E_BADFUNCS 2
|
||||
|
||||
#endif /* _PLUGIN_H_ */
|
@ -33,7 +33,6 @@
|
||||
#include "conf.h"
|
||||
#include "db-generic.h"
|
||||
#include "err.h"
|
||||
#include "plugin.h"
|
||||
#include "webserver.h"
|
||||
#include "xml-rpc.h"
|
||||
|
||||
@ -510,7 +509,6 @@ void xml_get_stats(WS_CONNINFO *pwsc) {
|
||||
WSTHREADENUM wste;
|
||||
int count;
|
||||
XMLSTRUCT *pxml;
|
||||
void *phandle;
|
||||
|
||||
pxml=xml_init(pwsc,1);
|
||||
xml_push(pxml,"status");
|
||||
@ -529,16 +527,6 @@ void xml_get_stats(WS_CONNINFO *pwsc) {
|
||||
|
||||
xml_pop(pxml); /* service_status */
|
||||
|
||||
xml_push(pxml,"plugins");
|
||||
phandle = NULL;
|
||||
while((phandle = plugin_enum(phandle))) {
|
||||
xml_push(pxml,"plugin");
|
||||
xml_output(pxml,"name",plugin_get_description(phandle));
|
||||
xml_pop(pxml); /* plugin */
|
||||
}
|
||||
|
||||
xml_pop(pxml); /* plugins */
|
||||
|
||||
xml_push(pxml,"thread_status");
|
||||
|
||||
pci = ws_thread_enum_first(config.server,&wste);
|
||||
|
Loading…
x
Reference in New Issue
Block a user