2005-02-16 23:24:16 -05:00
|
|
|
/*
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
* This really isn't xmlrpc. It's xmlrpc-ish. Emphasis on -ish.
|
|
|
|
*/
|
|
|
|
|
2006-02-26 03:46:24 -05:00
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
# include "config.h"
|
|
|
|
#endif
|
|
|
|
|
2005-02-16 23:24:16 -05:00
|
|
|
#include <stdio.h>
|
2005-11-20 04:50:50 -05:00
|
|
|
#include <stdarg.h>
|
2005-02-16 23:24:16 -05:00
|
|
|
#include <stdlib.h>
|
2005-11-07 00:58:05 -05:00
|
|
|
#include <string.h>
|
|
|
|
#include <time.h>
|
2005-02-16 23:24:16 -05:00
|
|
|
|
2005-11-07 00:58:05 -05:00
|
|
|
#include "configfile.h"
|
2005-11-22 23:11:04 -05:00
|
|
|
#include "db-generic.h"
|
2005-11-07 00:58:05 -05:00
|
|
|
#include "daapd.h"
|
2005-02-16 23:24:16 -05:00
|
|
|
#include "err.h"
|
2005-02-18 19:50:29 -05:00
|
|
|
#include "mp3-scanner.h"
|
2005-11-22 23:11:04 -05:00
|
|
|
#include "rend.h"
|
2005-02-16 23:24:16 -05:00
|
|
|
#include "webserver.h"
|
|
|
|
|
2005-11-11 18:52:42 -05:00
|
|
|
/* typedefs */
|
2005-11-20 04:50:50 -05:00
|
|
|
typedef struct tag_xmlstack {
|
|
|
|
char *tag;
|
|
|
|
struct tag_xmlstack *next;
|
|
|
|
} XMLSTACK;
|
|
|
|
|
2005-11-11 18:52:42 -05:00
|
|
|
typedef struct tag_xmlstruct {
|
2005-11-20 04:50:50 -05:00
|
|
|
WS_CONNINFO *pwsc;
|
|
|
|
int stack_level;
|
2006-01-04 15:30:44 -05:00
|
|
|
XMLSTACK stack;
|
2005-11-11 18:52:42 -05:00
|
|
|
} XMLSTRUCT;
|
|
|
|
|
2005-02-16 23:24:16 -05:00
|
|
|
/* Forwards */
|
2005-11-07 00:58:05 -05:00
|
|
|
void xml_get_stats(WS_CONNINFO *pwsc);
|
2005-02-16 23:24:16 -05:00
|
|
|
char *xml_entity_encode(char *original);
|
|
|
|
|
2006-03-29 23:13:20 -05:00
|
|
|
XMLSTRUCT *xml_init(WS_CONNINFO *pwsc, int emit_header);
|
|
|
|
void xml_push(XMLSTRUCT *pxml, char *term);
|
|
|
|
void xml_pop(XMLSTRUCT *pxml);
|
|
|
|
void xml_output(XMLSTRUCT *pxml, char *section, char *fmt, ...);
|
|
|
|
void xml_deinit(XMLSTRUCT *pxml);
|
|
|
|
|
2005-11-11 18:52:42 -05:00
|
|
|
/**
|
|
|
|
* create an xml response structure, a helper struct for
|
|
|
|
* building xml responses.
|
|
|
|
*
|
2005-11-20 04:50:50 -05:00
|
|
|
* @param pwsc the pwsc we are emitting to
|
|
|
|
* @param emit_header whether or not to throw out html headers and xml header
|
2005-11-11 18:52:42 -05:00
|
|
|
* @returns XMLSTRUCT on success, or NULL if failure
|
|
|
|
*/
|
2006-03-29 23:13:20 -05:00
|
|
|
XMLSTRUCT *xml_init(WS_CONNINFO *pwsc, int emit_header) {
|
2005-11-20 04:50:50 -05:00
|
|
|
XMLSTRUCT *pxml;
|
2006-01-04 15:30:44 -05:00
|
|
|
|
2005-11-20 04:50:50 -05:00
|
|
|
pxml=(XMLSTRUCT*)malloc(sizeof(XMLSTRUCT));
|
|
|
|
if(!pxml) {
|
|
|
|
DPRINTF(E_FATAL,L_XML,"Malloc error\n");
|
|
|
|
}
|
2006-01-04 15:30:44 -05:00
|
|
|
|
2005-11-20 04:50:50 -05:00
|
|
|
memset(pxml,0,sizeof(XMLSTRUCT));
|
2006-01-04 15:30:44 -05:00
|
|
|
|
2005-11-20 04:50:50 -05:00
|
|
|
pxml->pwsc = pwsc;
|
2006-01-04 15:30:44 -05:00
|
|
|
|
2006-03-31 17:23:18 -05:00
|
|
|
/* the world would be a wonderful place without ie */
|
|
|
|
ws_addresponseheader(pwsc,"Cache-Control","no-cache");
|
|
|
|
ws_addresponseheader(pwsc,"Expires","-1");
|
|
|
|
|
2005-11-20 04:50:50 -05:00
|
|
|
if(emit_header) {
|
2006-03-29 23:13:20 -05:00
|
|
|
ws_addresponseheader(pwsc,"Content-Type","text/xml; charset=utf-8");
|
2005-11-20 04:50:50 -05:00
|
|
|
ws_writefd(pwsc,"HTTP/1.0 200 OK\r\n");
|
|
|
|
ws_emitheaders(pwsc);
|
|
|
|
|
2006-03-29 23:13:20 -05:00
|
|
|
ws_writefd(pwsc,"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>");
|
2005-11-20 04:50:50 -05:00
|
|
|
}
|
2006-01-04 15:30:44 -05:00
|
|
|
|
2005-11-20 04:50:50 -05:00
|
|
|
return pxml;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* push a new term on the stack
|
|
|
|
*
|
|
|
|
* @param pxml xml struct obtained from xml_init
|
2006-01-04 15:30:44 -05:00
|
|
|
* @param term next xlm section to start
|
2005-11-20 04:50:50 -05:00
|
|
|
*/
|
2006-03-29 23:13:20 -05:00
|
|
|
void xml_push(XMLSTRUCT *pxml, char *term) {
|
2005-11-20 04:50:50 -05:00
|
|
|
XMLSTACK *pstack;
|
2006-01-04 15:30:44 -05:00
|
|
|
|
2005-11-20 04:50:50 -05:00
|
|
|
pstack = (XMLSTACK *)malloc(sizeof(XMLSTACK));
|
|
|
|
pstack->next=pxml->stack.next;
|
|
|
|
pstack->tag=strdup(term);
|
|
|
|
pxml->stack.next=pstack;
|
2006-01-04 15:30:44 -05:00
|
|
|
|
2006-03-28 02:49:39 -05:00
|
|
|
pxml->stack_level++;
|
2006-03-29 23:13:20 -05:00
|
|
|
|
|
|
|
ws_writefd(pxml->pwsc,"<%s>",term);
|
2005-11-20 04:50:50 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* end an xml section
|
|
|
|
*
|
|
|
|
* @param pxml xml struct we are working with
|
|
|
|
*/
|
2006-03-29 23:13:20 -05:00
|
|
|
void xml_pop(XMLSTRUCT *pxml) {
|
2005-11-20 04:50:50 -05:00
|
|
|
XMLSTACK *pstack;
|
2006-01-04 15:30:44 -05:00
|
|
|
|
2005-11-20 04:50:50 -05:00
|
|
|
pstack=pxml->stack.next;
|
|
|
|
if(!pstack) {
|
|
|
|
DPRINTF(E_LOG,L_XML,"xml_pop: tried to pop an empty stack\n");
|
|
|
|
return;
|
|
|
|
}
|
2006-01-04 15:30:44 -05:00
|
|
|
|
2005-11-20 04:50:50 -05:00
|
|
|
pxml->stack.next = pstack->next;
|
2006-01-04 15:30:44 -05:00
|
|
|
|
2006-03-29 23:13:20 -05:00
|
|
|
ws_writefd(pxml->pwsc,"</%s>",pstack->tag);
|
2005-11-20 04:50:50 -05:00
|
|
|
free(pstack->tag);
|
|
|
|
free(pstack);
|
2006-03-29 23:13:20 -05:00
|
|
|
|
|
|
|
pxml->stack_level--;
|
2005-11-20 04:50:50 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* output a string
|
|
|
|
*/
|
|
|
|
void xml_output(XMLSTRUCT *pxml, char *section, char *fmt, ...) {
|
|
|
|
va_list ap;
|
|
|
|
char buf[256];
|
|
|
|
char *output;
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
vsnprintf(buf, sizeof(buf), fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
|
2006-03-28 02:49:39 -05:00
|
|
|
output = xml_entity_encode(buf);
|
2006-03-29 23:13:20 -05:00
|
|
|
if(section) {
|
|
|
|
xml_push(pxml,section);
|
2006-03-28 02:49:39 -05:00
|
|
|
}
|
2006-03-29 23:13:20 -05:00
|
|
|
ws_writefd(pxml->pwsc,"%s",output);
|
2005-11-20 04:50:50 -05:00
|
|
|
free(output);
|
|
|
|
if(section) {
|
2006-03-29 23:13:20 -05:00
|
|
|
xml_pop(pxml);
|
2005-11-20 04:50:50 -05:00
|
|
|
}
|
2005-11-11 18:52:42 -05:00
|
|
|
}
|
|
|
|
|
2005-11-20 04:50:50 -05:00
|
|
|
/**
|
|
|
|
* clean up an xml struct
|
|
|
|
*
|
|
|
|
* @param pxml xml struct to clean up
|
|
|
|
*/
|
|
|
|
void xml_deinit(XMLSTRUCT *pxml) {
|
|
|
|
XMLSTACK *pstack;
|
2006-01-04 15:30:44 -05:00
|
|
|
|
2005-11-20 04:50:50 -05:00
|
|
|
if(pxml->stack.next) {
|
|
|
|
DPRINTF(E_LOG,L_XML,"xml_deinit: entries still on stack (%s)\n",pxml->stack.next->tag);
|
|
|
|
}
|
2006-01-04 15:30:44 -05:00
|
|
|
|
2005-11-20 04:50:50 -05:00
|
|
|
while((pstack=pxml->stack.next)) {
|
|
|
|
pxml->stack.next=pstack->next;
|
|
|
|
free(pstack->tag);
|
|
|
|
free(pstack);
|
|
|
|
}
|
|
|
|
free(pxml);
|
|
|
|
}
|
2005-11-11 18:52:42 -05:00
|
|
|
|
2005-02-16 23:24:16 -05:00
|
|
|
/**
|
|
|
|
* main entrypoint for the xmlrpc functions.
|
|
|
|
*
|
|
|
|
* @arg pwsc Pointer to the web request structure
|
|
|
|
*/
|
|
|
|
void xml_handle(WS_CONNINFO *pwsc) {
|
|
|
|
char *method;
|
|
|
|
|
|
|
|
if((method=ws_getvar(pwsc,"method")) == NULL) {
|
2005-11-07 00:58:05 -05:00
|
|
|
ws_returnerror(pwsc,500,"no method specified");
|
|
|
|
return;
|
2005-02-16 23:24:16 -05:00
|
|
|
}
|
|
|
|
|
2005-11-07 00:58:05 -05:00
|
|
|
if(strcasecmp(method,"stats") == 0) {
|
|
|
|
xml_get_stats(pwsc);
|
|
|
|
return;
|
2005-02-16 23:24:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
ws_returnerror(pwsc,500,"Invalid method");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* return xml file of all playlists
|
|
|
|
*/
|
2005-11-07 00:58:05 -05:00
|
|
|
void xml_get_stats(WS_CONNINFO *pwsc) {
|
|
|
|
int r_secs, r_days, r_hours, r_mins;
|
|
|
|
char buf[80];
|
|
|
|
WS_CONNINFO *pci;
|
|
|
|
SCAN_STATUS *pss;
|
|
|
|
WSTHREADENUM wste;
|
2006-01-04 15:30:44 -05:00
|
|
|
int count;
|
2005-11-20 04:50:50 -05:00
|
|
|
XMLSTRUCT *pxml;
|
2006-01-04 15:30:44 -05:00
|
|
|
|
2006-03-29 23:13:20 -05:00
|
|
|
pxml=xml_init(pwsc,1);
|
|
|
|
xml_push(pxml,"status");
|
|
|
|
|
|
|
|
xml_push(pxml,"service_status");
|
2005-02-16 23:24:16 -05:00
|
|
|
|
2006-03-29 23:13:20 -05:00
|
|
|
xml_push(pxml,"service");
|
2005-11-22 23:11:04 -05:00
|
|
|
|
|
|
|
xml_output(pxml,"name","Rendezvous");
|
2006-01-04 15:30:44 -05:00
|
|
|
|
2005-11-22 23:11:04 -05:00
|
|
|
#ifndef WITHOUT_MDNS
|
|
|
|
if(config.use_mdns) {
|
|
|
|
xml_output(pxml,"status",rend_running() ? "Stopped" : "Running"); /* ??? */
|
|
|
|
} else {
|
|
|
|
xml_output(pxml,"status","Disabled");
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
|
|
|
|
ws_writefd(pwsc,"<td>No Support</td><td> </td></tr>\n");
|
|
|
|
#endif
|
2006-03-29 23:13:20 -05:00
|
|
|
xml_pop(pxml); /* service */
|
2005-11-22 23:11:04 -05:00
|
|
|
|
2006-03-29 23:13:20 -05:00
|
|
|
xml_push(pxml,"service");
|
2005-11-22 23:11:04 -05:00
|
|
|
xml_output(pxml,"name","DAAP Server");
|
|
|
|
xml_output(pxml,"status",config.stop ? "Stopping" : "Running");
|
2006-03-29 23:13:20 -05:00
|
|
|
xml_pop(pxml); /* service */
|
2005-11-22 23:11:04 -05:00
|
|
|
|
2006-03-29 23:13:20 -05:00
|
|
|
xml_push(pxml,"service");
|
2005-11-22 23:11:04 -05:00
|
|
|
xml_output(pxml,"name","File Scanner");
|
|
|
|
xml_output(pxml,"status",config.reload ? "Running" : "Idle");
|
2006-03-29 23:13:20 -05:00
|
|
|
xml_pop(pxml); /* service */
|
2005-11-22 23:11:04 -05:00
|
|
|
|
2006-03-29 23:13:20 -05:00
|
|
|
xml_pop(pxml); /* service_status */
|
2006-01-04 15:30:44 -05:00
|
|
|
|
2006-03-29 23:13:20 -05:00
|
|
|
xml_push(pxml,"thread_status");
|
2005-11-07 00:58:05 -05:00
|
|
|
|
|
|
|
pci = ws_thread_enum_first(config.server,&wste);
|
|
|
|
while(pci) {
|
|
|
|
pss = ws_get_local_storage(pci);
|
|
|
|
if(pss) {
|
2006-03-29 23:13:20 -05:00
|
|
|
xml_push(pxml,"thread");
|
2005-11-20 04:50:50 -05:00
|
|
|
xml_output(pxml,"id","%d",pss->thread);
|
|
|
|
xml_output(pxml,"sourceip","%s",pss->host);
|
|
|
|
xml_output(pxml,"action","%s",pss->what);
|
2006-03-29 23:13:20 -05:00
|
|
|
xml_pop(pxml); /* thread */
|
2005-11-07 00:58:05 -05:00
|
|
|
}
|
|
|
|
pci=ws_thread_enum_next(config.server,&wste);
|
2005-02-16 23:24:16 -05:00
|
|
|
}
|
2006-01-04 15:30:44 -05:00
|
|
|
|
2006-03-29 23:13:20 -05:00
|
|
|
xml_pop(pxml); /* thread_status */
|
2005-02-16 23:24:16 -05:00
|
|
|
|
2006-03-29 23:13:20 -05:00
|
|
|
xml_push(pxml,"statistics");
|
2006-01-04 15:30:44 -05:00
|
|
|
|
2006-02-26 03:46:24 -05:00
|
|
|
r_secs=(int)(time(NULL)-config.stats.start_time);
|
2005-02-16 23:24:16 -05:00
|
|
|
|
2005-11-07 00:58:05 -05:00
|
|
|
r_days=r_secs/(3600 * 24);
|
|
|
|
r_secs -= ((3600 * 24) * r_days);
|
2005-02-16 23:24:16 -05:00
|
|
|
|
2005-11-07 00:58:05 -05:00
|
|
|
r_hours=r_secs/3600;
|
|
|
|
r_secs -= (3600 * r_hours);
|
2005-02-18 19:50:29 -05:00
|
|
|
|
2005-11-07 00:58:05 -05:00
|
|
|
r_mins=r_secs/60;
|
|
|
|
r_secs -= 60 * r_mins;
|
2005-02-18 19:50:29 -05:00
|
|
|
|
2005-11-07 00:58:05 -05:00
|
|
|
memset(buf,0x0,sizeof(buf));
|
2006-01-04 15:30:44 -05:00
|
|
|
if(r_days)
|
|
|
|
sprintf((char*)&buf[strlen(buf)],"%d day%s, ", r_days,
|
|
|
|
r_days == 1 ? "" : "s");
|
2005-02-18 19:50:29 -05:00
|
|
|
|
2006-01-04 15:30:44 -05:00
|
|
|
if(r_days || r_hours)
|
|
|
|
sprintf((char*)&buf[strlen(buf)],"%d hour%s, ", r_hours,
|
|
|
|
r_hours == 1 ? "" : "s");
|
2005-11-07 00:58:05 -05:00
|
|
|
|
|
|
|
if(r_days || r_hours || r_mins)
|
2006-01-04 15:30:44 -05:00
|
|
|
sprintf((char*)&buf[strlen(buf)],"%d minute%s, ", r_mins,
|
|
|
|
r_mins == 1 ? "" : "s");
|
2005-11-07 00:58:05 -05:00
|
|
|
|
|
|
|
sprintf((char*)&buf[strlen(buf)],"%d second%s ", r_secs,
|
2006-01-04 15:30:44 -05:00
|
|
|
r_secs == 1 ? "" : "s");
|
|
|
|
|
2006-03-29 23:13:20 -05:00
|
|
|
xml_push(pxml,"stat");
|
2005-11-20 04:50:50 -05:00
|
|
|
xml_output(pxml,"name","Uptime");
|
|
|
|
xml_output(pxml,"value","%s",buf);
|
2006-03-29 23:13:20 -05:00
|
|
|
xml_pop(pxml); /* stat */
|
2006-01-04 15:30:44 -05:00
|
|
|
|
2006-03-29 23:13:20 -05:00
|
|
|
xml_push(pxml,"stat");
|
2005-11-22 23:11:04 -05:00
|
|
|
xml_output(pxml,"name","Songs");
|
2006-01-04 15:30:44 -05:00
|
|
|
db_get_song_count(NULL,&count);
|
|
|
|
xml_output(pxml,"value","%d",count);
|
2006-03-29 23:13:20 -05:00
|
|
|
xml_pop(pxml); /* stat */
|
2006-01-04 15:30:44 -05:00
|
|
|
|
2006-03-29 23:13:20 -05:00
|
|
|
xml_push(pxml,"stat");
|
2005-11-22 23:11:04 -05:00
|
|
|
xml_output(pxml,"name","Songs Served");
|
|
|
|
xml_output(pxml,"value","%d",config.stats.songs_served);
|
2006-03-29 23:13:20 -05:00
|
|
|
xml_pop(pxml); /* stat */
|
2006-01-04 15:30:44 -05:00
|
|
|
|
2006-03-29 23:13:20 -05:00
|
|
|
xml_pop(pxml); /* statistics */
|
|
|
|
xml_pop(pxml); /* status */
|
2005-11-20 04:50:50 -05:00
|
|
|
|
|
|
|
xml_deinit(pxml);
|
2005-02-18 19:50:29 -05:00
|
|
|
return;
|
2005-02-16 23:24:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* xml entity encoding, stupid style
|
|
|
|
*/
|
|
|
|
char *xml_entity_encode(char *original) {
|
|
|
|
char *new;
|
|
|
|
char *s, *d;
|
|
|
|
int destsize;
|
|
|
|
|
2006-02-26 03:46:24 -05:00
|
|
|
destsize = 6*(int)strlen(original)+1;
|
2005-02-16 23:24:16 -05:00
|
|
|
new=(char *)malloc(destsize);
|
|
|
|
if(!new) return NULL;
|
|
|
|
|
|
|
|
memset(new,0x00,destsize);
|
|
|
|
|
|
|
|
s=original;
|
|
|
|
d=new;
|
|
|
|
|
|
|
|
while(*s) {
|
2005-11-07 00:58:05 -05:00
|
|
|
switch(*s) {
|
|
|
|
case '>':
|
|
|
|
strcat(d,">");
|
|
|
|
d += 4;
|
|
|
|
s++;
|
|
|
|
break;
|
|
|
|
case '<':
|
|
|
|
strcat(d,"<");
|
|
|
|
d += 4;
|
|
|
|
s++;
|
|
|
|
break;
|
|
|
|
case '"':
|
|
|
|
strcat(d,""");
|
|
|
|
d += 6;
|
|
|
|
s++;
|
|
|
|
break;
|
|
|
|
case '\'':
|
|
|
|
strcat(d,"'");
|
|
|
|
d += 6;
|
|
|
|
s++;
|
|
|
|
break;
|
|
|
|
case '&':
|
|
|
|
strcat(d,"&");
|
|
|
|
d += 5;
|
|
|
|
s++;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
*d++ = *s++;
|
|
|
|
}
|
2005-02-16 23:24:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return new;
|
|
|
|
}
|