mirror of
https://github.com/owntone/owntone-server.git
synced 2024-12-26 23:25:56 -05:00
Add rsp files
This commit is contained in:
parent
a2ea8c3de0
commit
553c57ccc1
516
src/plugins/compat.c
Normal file
516
src/plugins/compat.c
Normal file
@ -0,0 +1,516 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#if !HAVE_STRCASESTR
|
||||
/* case-independent string matching, similar to strstr but
|
||||
* matching */
|
||||
char * strcasestr(char* haystack, char* needle) {
|
||||
int i;
|
||||
int nlength = (int) strlen (needle);
|
||||
int hlength = (int) strlen (haystack);
|
||||
|
||||
if (nlength > hlength) return NULL;
|
||||
if (hlength <= 0) return NULL;
|
||||
if (nlength <= 0) return haystack;
|
||||
/* hlength and nlength > 0, nlength <= hlength */
|
||||
for (i = 0; i <= (hlength - nlength); i++) {
|
||||
if (strncasecmp (haystack + i, needle, nlength) == 0) {
|
||||
return haystack + i;
|
||||
}
|
||||
}
|
||||
/* substring not found */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* !HAVE_STRCASESTR */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1994 Powerdog Industries. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. All advertising materials mentioning features or use of this
|
||||
* software must display the following acknowledgement:
|
||||
* This product includes software developed by Powerdog Industries.
|
||||
* 4. The name of Powerdog Industries may not be used to endorse or
|
||||
* promote products derived from this software without specific prior
|
||||
* written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef HAVE_STRPTIME
|
||||
|
||||
#define asizeof(a) (sizeof (a) / sizeof ((a)[0]))
|
||||
|
||||
|
||||
#ifndef sun
|
||||
struct dtconv {
|
||||
char *abbrev_month_names[12];
|
||||
char *month_names[12];
|
||||
char *abbrev_weekday_names[7];
|
||||
char *weekday_names[7];
|
||||
char *time_format;
|
||||
char *sdate_format;
|
||||
char *dtime_format;
|
||||
char *am_string;
|
||||
char *pm_string;
|
||||
char *ldate_format;
|
||||
};
|
||||
#endif
|
||||
|
||||
static struct dtconv En_US = {
|
||||
{ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" },
|
||||
{ "January", "February", "March", "April",
|
||||
"May", "June", "July", "August",
|
||||
"September", "October", "November", "December" },
|
||||
{ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" },
|
||||
{ "Sunday", "Monday", "Tuesday", "Wednesday",
|
||||
"Thursday", "Friday", "Saturday" },
|
||||
"%H:%M:%S",
|
||||
"%m/%d/%y",
|
||||
"%a %b %e %T %Z %Y",
|
||||
"AM",
|
||||
"PM",
|
||||
"%A, %B, %e, %Y"
|
||||
};
|
||||
|
||||
#ifdef SUNOS4
|
||||
extern int strncasecmp();
|
||||
#endif
|
||||
|
||||
void lowercase_string(char *buffer) {
|
||||
while(*buffer) {
|
||||
*buffer = tolower(*buffer);
|
||||
buffer++;
|
||||
}
|
||||
}
|
||||
|
||||
char *strptime(char *buf, char *fmt, struct tm *tm) {
|
||||
char c,
|
||||
*ptr;
|
||||
int i, j,
|
||||
len;
|
||||
ptr = fmt;
|
||||
while (*ptr != 0) {
|
||||
if (*buf == 0)
|
||||
break;
|
||||
|
||||
c = *ptr++;
|
||||
|
||||
if (c != '%') {
|
||||
if (isspace(c))
|
||||
while (*buf != 0 && isspace(*buf))
|
||||
buf++;
|
||||
else if (c != *buf++)
|
||||
return 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
c = *ptr++;
|
||||
switch (c) {
|
||||
case 0:
|
||||
case '%':
|
||||
if (*buf++ != '%')
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
buf = strptime(buf, En_US.ldate_format, tm);
|
||||
if (buf == 0)
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
buf = strptime(buf, "%x %X", tm);
|
||||
if (buf == 0)
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case 'D':
|
||||
buf = strptime(buf, "%m/%d/%y", tm);
|
||||
if (buf == 0)
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case 'R':
|
||||
buf = strptime(buf, "%H:%M", tm);
|
||||
if (buf == 0)
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
buf = strptime(buf, "%I:%M:%S %p", tm);
|
||||
if (buf == 0)
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case 'T':
|
||||
buf = strptime(buf, "%H:%M:%S", tm);
|
||||
if (buf == 0)
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case 'X':
|
||||
buf = strptime(buf, En_US.time_format, tm);
|
||||
if (buf == 0)
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
buf = strptime(buf, En_US.sdate_format, tm);
|
||||
if (buf == 0)
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case 'j':
|
||||
if (!isdigit(*buf))
|
||||
return 0;
|
||||
|
||||
for (i = 0; *buf != 0 && isdigit(*buf); buf++) {
|
||||
i *= 10;
|
||||
i += *buf - '0';
|
||||
}
|
||||
if (i > 365)
|
||||
return 0;
|
||||
|
||||
tm->tm_yday = i;
|
||||
break;
|
||||
|
||||
case 'M':
|
||||
case 'S':
|
||||
if (*buf == 0 || isspace(*buf))
|
||||
break;
|
||||
|
||||
if (!isdigit(*buf))
|
||||
return 0;
|
||||
|
||||
for (j=0,i = 0; *buf != 0 && isdigit(*buf) && j<2; j++,buf++) {
|
||||
i *= 10;
|
||||
i += *buf - '0';
|
||||
}
|
||||
if (i > 59)
|
||||
return 0;
|
||||
|
||||
if (c == 'M')
|
||||
tm->tm_min = i;
|
||||
else
|
||||
tm->tm_sec = i;
|
||||
|
||||
if (*buf != 0 && isspace(*buf))
|
||||
while (*ptr != 0 && !isspace(*ptr))
|
||||
ptr++;
|
||||
break;
|
||||
|
||||
case 'H':
|
||||
case 'I':
|
||||
case 'k':
|
||||
case 'l':
|
||||
if (!isdigit(*buf))
|
||||
return 0;
|
||||
|
||||
for (j=0,i = 0; *buf != 0 && isdigit(*buf) && j<2; j++,buf++) {
|
||||
i *= 10;
|
||||
i += *buf - '0';
|
||||
}
|
||||
if (c == 'H' || c == 'k') {
|
||||
if (i > 23)
|
||||
return 0;
|
||||
} else if (i > 11)
|
||||
return 0;
|
||||
|
||||
tm->tm_hour = i;
|
||||
|
||||
if (*buf != 0 && isspace(*buf))
|
||||
while (*ptr != 0 && !isspace(*ptr))
|
||||
ptr++;
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
len = (int) strlen(En_US.am_string);
|
||||
lowercase_string( buf );
|
||||
|
||||
if (strncmp(buf, En_US.am_string, len) == 0) {
|
||||
if (tm->tm_hour > 12)
|
||||
return 0;
|
||||
if (tm->tm_hour == 12)
|
||||
tm->tm_hour = 0;
|
||||
buf += len;
|
||||
break;
|
||||
}
|
||||
|
||||
len = (int) strlen(En_US.pm_string);
|
||||
|
||||
if (strncmp(buf, En_US.pm_string, len) == 0) {
|
||||
if (tm->tm_hour > 12)
|
||||
return 0;
|
||||
if (tm->tm_hour != 12)
|
||||
tm->tm_hour += 12;
|
||||
buf += len;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
case 'A':
|
||||
case 'a':
|
||||
for (i = 0; i < asizeof(En_US.weekday_names); i++) {
|
||||
len = (int) strlen(En_US.weekday_names[i]);
|
||||
|
||||
lowercase_string( buf );
|
||||
|
||||
if (strncmp(buf,
|
||||
En_US.weekday_names[i],
|
||||
len) == 0)
|
||||
break;
|
||||
|
||||
len = (int) strlen(En_US.abbrev_weekday_names[i]);
|
||||
if (strncmp(buf,
|
||||
En_US.abbrev_weekday_names[i],
|
||||
len) == 0)
|
||||
break;
|
||||
}
|
||||
if (i == asizeof(En_US.weekday_names))
|
||||
return 0;
|
||||
|
||||
tm->tm_wday = i;
|
||||
buf += len;
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
case 'e':
|
||||
if (!isdigit(*buf))
|
||||
return 0;
|
||||
|
||||
for (j=0,i = 0; *buf != 0 && isdigit(*buf) && j<2; j++,buf++) {
|
||||
i *= 10;
|
||||
i += *buf - '0';
|
||||
}
|
||||
if (i > 31)
|
||||
return 0;
|
||||
|
||||
tm->tm_mday = i;
|
||||
|
||||
if (*buf != 0 && isspace(*buf))
|
||||
while (*ptr != 0 && !isspace(*ptr))
|
||||
ptr++;
|
||||
break;
|
||||
|
||||
case 'B':
|
||||
case 'b':
|
||||
case 'h':
|
||||
for (i = 0; i < asizeof(En_US.month_names); i++) {
|
||||
len = (int) strlen(En_US.month_names[i]);
|
||||
|
||||
lowercase_string( buf );
|
||||
if (strncmp(buf, En_US.month_names[i],len) == 0)
|
||||
break;
|
||||
|
||||
len = (int) strlen(En_US.abbrev_month_names[i]);
|
||||
if (strncmp(buf,
|
||||
En_US.abbrev_month_names[i],
|
||||
len) == 0)
|
||||
break;
|
||||
}
|
||||
if (i == asizeof(En_US.month_names))
|
||||
return 0;
|
||||
|
||||
tm->tm_mon = i;
|
||||
buf += len;
|
||||
break;
|
||||
|
||||
case 'm':
|
||||
if (!isdigit(*buf))
|
||||
return 0;
|
||||
|
||||
for (j=0,i = 0; *buf != 0 && isdigit(*buf) && j<2; j++,buf++) {
|
||||
i *= 10;
|
||||
i += *buf - '0';
|
||||
}
|
||||
if (i < 1 || i > 12)
|
||||
return 0;
|
||||
|
||||
tm->tm_mon = i - 1;
|
||||
|
||||
if (*buf != 0 && isspace(*buf))
|
||||
while (*ptr != 0 && !isspace(*ptr))
|
||||
ptr++;
|
||||
break;
|
||||
|
||||
case 'Y':
|
||||
case 'y':
|
||||
if (*buf == 0 || isspace(*buf))
|
||||
break;
|
||||
|
||||
if (!isdigit(*buf))
|
||||
return 0;
|
||||
|
||||
for (j=0,i = 0; *buf != 0 && isdigit(*buf) && j<((c=='Y')?4:2); j++,buf++) {
|
||||
i *= 10;
|
||||
i += *buf - '0';
|
||||
}
|
||||
|
||||
if (c == 'Y')
|
||||
i -= 1900;
|
||||
else if (i < 69) /*c=='y', 00-68 is for 20xx, the rest is for 19xx*/
|
||||
i += 100;
|
||||
|
||||
if (i < 0)
|
||||
return 0;
|
||||
|
||||
tm->tm_year = i;
|
||||
|
||||
if (*buf != 0 && isspace(*buf))
|
||||
while (*ptr != 0 && !isspace(*ptr))
|
||||
ptr++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
#endif /* ndef HAVE_STRPTIME */
|
||||
|
||||
/* Compliments of Jay Freeman <saurik@saurik.com> */
|
||||
|
||||
#if !HAVE_STRSEP
|
||||
char *strsep(char **stringp, const char *delim) {
|
||||
char *ret = *stringp;
|
||||
if (ret == NULL) return(NULL); /* grrr */
|
||||
if ((*stringp = strpbrk(*stringp, delim)) != NULL) {
|
||||
*((*stringp)++) = '\0';
|
||||
}
|
||||
return(ret);
|
||||
}
|
||||
#endif /* !HAVE_STRSEP */
|
||||
|
||||
/* Reentrant string tokenizer. Generic version.
|
||||
Copyright (C) 1991,1996-1999,2001,2004 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
|
||||
|
||||
#ifndef HAVE_STRTOK_R
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/* Parse S into tokens separated by characters in DELIM.
|
||||
If S is NULL, the saved pointer in SAVE_PTR is used as
|
||||
the next starting point. For example:
|
||||
char s[] = "-abc-=-def";
|
||||
char *sp;
|
||||
x = strtok_r(s, "-", &sp); // x = "abc", sp = "=-def"
|
||||
x = strtok_r(NULL, "-=", &sp); // x = "def", sp = NULL
|
||||
x = strtok_r(NULL, "=", &sp); // x = NULL
|
||||
// s = "abc\0-def\0"
|
||||
*/
|
||||
char *strtok_r(char *s, const char *delim, char **last)
|
||||
{
|
||||
char *spanp;
|
||||
int c, sc;
|
||||
char *tok;
|
||||
|
||||
if (s == NULL && (s = *last) == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Skip (span) leading delimiters (s += strspn(s, delim), sort of).
|
||||
*/
|
||||
cont:
|
||||
c = *s++;
|
||||
for (spanp = (char *)delim; (sc = *spanp++) != 0; ) {
|
||||
if (c == sc) {
|
||||
goto cont;
|
||||
}
|
||||
}
|
||||
|
||||
if (c == 0) { /* no non-delimiter characters */
|
||||
*last = NULL;
|
||||
return NULL;
|
||||
}
|
||||
tok = s - 1;
|
||||
|
||||
/*
|
||||
* Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
|
||||
* Note that delim must have one NUL; we stop if we see that, too.
|
||||
*/
|
||||
for (;;) {
|
||||
c = *s++;
|
||||
spanp = (char *)delim;
|
||||
do {
|
||||
if ((sc = *spanp++) == c) {
|
||||
if (c == 0) {
|
||||
s = NULL;
|
||||
}
|
||||
else {
|
||||
char *w = s - 1;
|
||||
*w = '\0';
|
||||
}
|
||||
*last = s;
|
||||
return tok;
|
||||
}
|
||||
}
|
||||
while (sc != 0);
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_TIMEGM
|
||||
time_t timegm(struct tm *tm) {
|
||||
time_t ret;
|
||||
char *tz;
|
||||
char buffer[255];
|
||||
|
||||
tz = getenv("TZ");
|
||||
_putenv("TZ=UTC0");
|
||||
_tzset();
|
||||
ret = mktime(tm);
|
||||
|
||||
if(tz)
|
||||
sprintf(buffer,"TZ=%s",tz);
|
||||
else
|
||||
strcpy(buffer,"TZ=");
|
||||
_putenv(buffer);
|
||||
_tzset();
|
||||
return ret;
|
||||
}
|
||||
#endif
|
25
src/plugins/compat.h
Normal file
25
src/plugins/compat.h
Normal file
@ -0,0 +1,25 @@
|
||||
#ifndef _COMPAT_H_
|
||||
#define _COMPAT_H_
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_STRCASESTR
|
||||
extern char *strcasestr(char* haystack, char* needle);
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_STRPTIME
|
||||
extern char *strptime(char *buf, char *fmt, struct tm *tm );
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_STRTOK_R
|
||||
#undef strtok_r /* defend against win32 pthreads */
|
||||
extern char *strtok_r(char *s, char *delim, char **last);
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_TIMEGM
|
||||
extern time_t timegm(struct tm *tm);
|
||||
#endif
|
||||
|
||||
#endif /* _COMPAT_H_ */
|
521
src/plugins/rsp.c
Normal file
521
src/plugins/rsp.c
Normal file
@ -0,0 +1,521 @@
|
||||
/*
|
||||
* $Id: $
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "mtd-plugins.h"
|
||||
#include "rsp.h"
|
||||
#include "xml-rpc.h"
|
||||
|
||||
/* Forwards */
|
||||
PLUGIN_INFO *plugin_info(void);
|
||||
void plugin_handler(WS_CONNINFO *pwsc);
|
||||
int plugin_auth(WS_CONNINFO *pwsc, char *username, char *pw);
|
||||
void rsp_info(WS_CONNINFO *pwsc, DBQUERYINFO *pqi);
|
||||
void rsp_db(WS_CONNINFO *pwsc, DBQUERYINFO *pqi);
|
||||
void rsp_playlist(WS_CONNINFO *pwsc, DBQUERYINFO *pqi);
|
||||
void rsp_browse(WS_CONNINFO *pwsc, DBQUERYINFO *pqi);
|
||||
void rsp_stream(WS_CONNINFO *pwsc, DBQUERYINFO *pqi);
|
||||
void rsp_error(WS_CONNINFO *pwsc, DBQUERYINFO *pqi, int eno, char *estr);
|
||||
|
||||
/* Globals */
|
||||
PLUGIN_OUTPUT_FN _pofn = { plugin_handler, plugin_auth };
|
||||
PLUGIN_INFO _pi = {
|
||||
PLUGIN_VERSION,
|
||||
PLUGIN_OUTPUT,
|
||||
"rsp/" RSP_VERSION,
|
||||
"/rsp/.*",
|
||||
&_pofn,
|
||||
NULL
|
||||
};
|
||||
|
||||
typedef struct tag_response {
|
||||
char *uri[10];
|
||||
void (*dispatch)(WS_CONNINFO *, DBQUERYINFO *);
|
||||
} PLUGIN_RESPONSE;
|
||||
|
||||
|
||||
PLUGIN_RESPONSE rsp_uri_map[] = {
|
||||
{{"rsp", "info",NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL }, rsp_info },
|
||||
{{"rsp", "db" ,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL }, rsp_db },
|
||||
{{"rsp", "db" , "*",NULL,NULL,NULL,NULL,NULL,NULL,NULL }, rsp_playlist },
|
||||
{{"rsp", "db" , "*", "*",NULL,NULL,NULL,NULL,NULL,NULL }, rsp_browse },
|
||||
{{"rsp","stream", "*",NULL,NULL,NULL,NULL,NULL,NULL,NULL }, rsp_stream }
|
||||
};
|
||||
|
||||
#define E_RSP 0x0000
|
||||
#define E_DB 0x1000
|
||||
|
||||
|
||||
#define T_STRING 0
|
||||
#define T_INT 1
|
||||
#define T_DATE 2
|
||||
|
||||
#define F_FULL 1
|
||||
#define F_BROWSE 2
|
||||
#define F_ID 4
|
||||
|
||||
|
||||
typedef struct tag_fieldspec {
|
||||
char *name;
|
||||
int flags; /* 1: full, 2: browse, 4: id */
|
||||
int type; /* 0: string, 1: int, 2: date */
|
||||
} FIELDSPEC;
|
||||
|
||||
FIELDSPEC rsp_playlist_fields[] = {
|
||||
{ "id" , 7, T_INT },
|
||||
{ "title" , 3, T_STRING },
|
||||
{ "type" , 0, T_INT },
|
||||
{ "items" , 3, T_INT },
|
||||
{ "query" , 0, T_STRING },
|
||||
{ "db_timestamp" , 0, T_DATE },
|
||||
{ "path" , 0, T_STRING },
|
||||
{ "index" , 0, T_INT },
|
||||
{ NULL , 0, 0 }
|
||||
};
|
||||
|
||||
FIELDSPEC rsp_fields[] = {
|
||||
{ "id" , 7, T_INT },
|
||||
{ "path" , 0, T_STRING },
|
||||
{ "fname" , 0, T_STRING },
|
||||
{ "title" , 7, T_STRING },
|
||||
{ "artist" , 3, T_STRING },
|
||||
{ "album" , 3, T_STRING },
|
||||
{ "genre" , 1, T_STRING },
|
||||
{ "comment" , 0, T_STRING },
|
||||
{ "type" , 1, T_STRING },
|
||||
{ "composer" , 1, T_STRING },
|
||||
{ "orchestra" , 1, T_STRING },
|
||||
{ "conductor" , 1, T_STRING },
|
||||
{ "grouping" , 0, T_STRING },
|
||||
{ "url" , 1, T_STRING },
|
||||
{ "bitrate" , 1, T_INT },
|
||||
{ "samplerate" , 1, T_INT },
|
||||
{ "song_length" , 1, T_INT },
|
||||
{ "file_size" , 1, T_INT },
|
||||
{ "year" , 1, T_INT },
|
||||
{ "track" , 3, T_INT },
|
||||
{ "total_tracks" , 1, T_INT },
|
||||
{ "disc" , 3, T_INT },
|
||||
{ "total_discs" , 1, T_INT },
|
||||
{ "bpm" , 1, T_INT },
|
||||
{ "compilation" , 1, T_INT },
|
||||
{ "rating" , 1, T_INT },
|
||||
{ "play_count" , 1, T_INT },
|
||||
{ "data_kind" , 0, T_INT },
|
||||
{ "item_kind" , 0, T_INT },
|
||||
{ "description" , 1, T_STRING },
|
||||
{ "time_added" , 1, T_DATE },
|
||||
{ "time_modified", 1, T_DATE },
|
||||
{ "time_played" , 1, T_DATE },
|
||||
{ "db_timestamp" , 0, T_DATE },
|
||||
{ "disabled" , 1, T_INT },
|
||||
{ "sample_count" , 0, T_INT },
|
||||
{ "force_update" , 0, T_INT },
|
||||
{ "codectype" , 7, T_INT },
|
||||
{ "idx" , 0, T_INT },
|
||||
{ "has_video" , 0, T_INT },
|
||||
{ "contentrating", 0, T_INT },
|
||||
{ NULL , 0 }
|
||||
};
|
||||
|
||||
/**
|
||||
* return info about this plugin module
|
||||
*/
|
||||
PLUGIN_INFO *plugin_info(void) {
|
||||
return &_pi;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* check for auth
|
||||
*/
|
||||
int plugin_auth(WS_CONNINFO *pwsc, char *username, char *pw) {
|
||||
/* disable passwords for now */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* dispatch handler for web stuff
|
||||
*/
|
||||
void plugin_handler(WS_CONNINFO *pwsc) {
|
||||
char *string, *save, *token;
|
||||
DBQUERYINFO *pqi;
|
||||
int elements;
|
||||
int index, part;
|
||||
int found;
|
||||
|
||||
|
||||
string = infn->ws_uri(pwsc);
|
||||
string++;
|
||||
|
||||
pqi = (DBQUERYINFO *)malloc(sizeof(DBQUERYINFO));
|
||||
if(!pqi) {
|
||||
infn->ws_returnerror(pwsc,500,"Malloc error in plugin_handler");
|
||||
return;
|
||||
}
|
||||
memset(pqi,0,sizeof(DBQUERYINFO));
|
||||
|
||||
infn->log(E_DBG,"Tokenizing url\n");
|
||||
while((pqi->uri_count < 10) && (token=strtok_r(string,"/",&save))) {
|
||||
string=NULL;
|
||||
pqi->uri_sections[pqi->uri_count++] = token;
|
||||
}
|
||||
|
||||
elements = sizeof(rsp_uri_map) / sizeof(PLUGIN_RESPONSE);
|
||||
infn->log(E_DBG,"Found %d elements\n",elements);
|
||||
|
||||
index = 0;
|
||||
found = 0;
|
||||
|
||||
while((!found) && (index < elements)) {
|
||||
/* test this set */
|
||||
infn->log(E_DBG,"Checking reponse %d\n",index);
|
||||
part=0;
|
||||
while(part < 10) {
|
||||
if((rsp_uri_map[index].uri[part]) && (!pqi->uri_sections[part]))
|
||||
break;
|
||||
if((pqi->uri_sections[part]) && (!rsp_uri_map[index].uri[part]))
|
||||
break;
|
||||
|
||||
if((rsp_uri_map[index].uri[part]) &&
|
||||
(strcmp(rsp_uri_map[index].uri[part],"*") != 0)) {
|
||||
if(strcmp(rsp_uri_map[index].uri[part],
|
||||
pqi->uri_sections[part])!= 0)
|
||||
break;
|
||||
}
|
||||
part++;
|
||||
}
|
||||
|
||||
if(part == 10) {
|
||||
found = 1;
|
||||
infn->log(E_DBG,"Found it! Index: %d\n",index);
|
||||
} else {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
if(found) {
|
||||
rsp_uri_map[index].dispatch(pwsc, pqi);
|
||||
infn->ws_close(pwsc);
|
||||
return;
|
||||
}
|
||||
|
||||
rsp_error(pwsc, pqi, 1, "Bad path");
|
||||
infn->ws_close(pwsc);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* get server info
|
||||
*/
|
||||
void rsp_info(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
||||
XMLSTRUCT *pxml;
|
||||
char servername[256];
|
||||
int size;
|
||||
|
||||
infn->log(E_DBG,"Starting rsp_info\n");
|
||||
|
||||
pxml = xml_init(pwsc,1);
|
||||
|
||||
xml_push(pxml,"response");
|
||||
xml_push(pxml,"status");
|
||||
xml_output(pxml,"errorcode","0");
|
||||
xml_output(pxml,"errorstring","");
|
||||
xml_output(pxml,"records","0");
|
||||
xml_output(pxml,"totalrecords","0");
|
||||
xml_pop(pxml); /* status */
|
||||
|
||||
/* info block */
|
||||
xml_push(pxml,"info");
|
||||
xml_output(pxml,"count","%d",infn->db_count());
|
||||
xml_output(pxml,"rsp-version",RSP_VERSION);
|
||||
|
||||
xml_output(pxml,"server-version",infn->server_ver());
|
||||
|
||||
size = sizeof(servername);
|
||||
infn->server_name(servername,&size);
|
||||
xml_output(pxml,"name",servername);
|
||||
xml_pop(pxml); /* info */
|
||||
|
||||
xml_pop(pxml); /* response */
|
||||
xml_deinit(pxml);
|
||||
}
|
||||
|
||||
/**
|
||||
* /rsp/db
|
||||
*
|
||||
* dump details about all playlists
|
||||
*/
|
||||
void rsp_db(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
||||
XMLSTRUCT *pxml;
|
||||
char *pe;
|
||||
int err;
|
||||
char **row;
|
||||
int rowindex;
|
||||
|
||||
pqi->want_count = 1;
|
||||
pqi->query_type = queryTypePlaylists;
|
||||
if((err=infn->db_enum_start(&pe,pqi)) != 0) {
|
||||
rsp_error(pwsc, pqi, err | E_DB, pe);
|
||||
return;
|
||||
}
|
||||
|
||||
pxml = xml_init(pwsc,1);
|
||||
|
||||
xml_push(pxml,"response");
|
||||
xml_push(pxml,"status");
|
||||
xml_output(pxml,"errorcode","0");
|
||||
xml_output(pxml,"errorstring","");
|
||||
xml_output(pxml,"records","%d",pqi->specifiedtotalcount);
|
||||
xml_output(pxml,"totalrecords","%d",pqi->specifiedtotalcount);
|
||||
xml_pop(pxml); /* status */
|
||||
|
||||
xml_push(pxml,"playlists");
|
||||
|
||||
while((infn->db_enum_fetch_row(NULL,&row,pqi) == 0) && (row)) {
|
||||
xml_push(pxml,"playlist");
|
||||
rowindex=0;
|
||||
while(rsp_playlist_fields[rowindex].name) {
|
||||
if(rsp_playlist_fields[rowindex].flags & F_FULL) {
|
||||
xml_output(pxml,rsp_playlist_fields[rowindex].name,"%s",
|
||||
row[rowindex]);
|
||||
}
|
||||
rowindex++;
|
||||
}
|
||||
xml_pop(pxml); /* playlist */
|
||||
}
|
||||
|
||||
infn->db_enum_end(NULL);
|
||||
|
||||
xml_pop(pxml); /* playlists */
|
||||
xml_pop(pxml); /* response */
|
||||
xml_deinit(pxml);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* get all items under the playlist
|
||||
*/
|
||||
void rsp_playlist(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
||||
XMLSTRUCT *pxml;
|
||||
char *pe;
|
||||
int err;
|
||||
char **row;
|
||||
int rowindex;
|
||||
int returned;
|
||||
char *browse_type;
|
||||
int type;
|
||||
char *query;
|
||||
char *estr = NULL;
|
||||
|
||||
query = infn->ws_getvar(pwsc,"query");
|
||||
if(query) {
|
||||
pqi->pt = infn->sp_init();
|
||||
if(!infn->sp_parse(pqi->pt,query)) {
|
||||
estr = strdup(infn->sp_get_error(pqi->pt));
|
||||
infn->log(E_LOG,"Ignoring bad query (%s): %s\n",query,estr);
|
||||
infn->sp_dispose(pqi->pt);
|
||||
pqi->pt = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
pqi->index_type = indexTypeNone;
|
||||
if(infn->ws_getvar(pwsc,"offset")) {
|
||||
pqi->index_low = atoi(infn->ws_getvar(pwsc,"offset"));
|
||||
}
|
||||
if(infn->ws_getvar(pwsc,"limit")) {
|
||||
pqi->index_high = pqi->index_low + atoi(infn->ws_getvar(pwsc,"limit")) -1;
|
||||
if(pqi->index_high < pqi->index_low) {
|
||||
pqi->index_high = 999999;
|
||||
}
|
||||
} else {
|
||||
pqi->index_high = 999999; /* FIXME */
|
||||
}
|
||||
pqi->index_type = indexTypeSub;
|
||||
|
||||
browse_type = infn->ws_getvar(pwsc,"type");
|
||||
type = F_FULL;
|
||||
|
||||
if(browse_type) {
|
||||
if(strcasecmp(browse_type,"browse") == 0) {
|
||||
type = F_BROWSE;
|
||||
} else if(strcasecmp(browse_type,"id") == 0) {
|
||||
type = F_ID;
|
||||
}
|
||||
}
|
||||
|
||||
pqi->want_count = 1;
|
||||
pqi->query_type = queryTypePlaylistItems;
|
||||
pqi->playlist_id = atoi(pqi->uri_sections[2]);
|
||||
|
||||
if((err=infn->db_enum_start(&pe,pqi)) != 0) {
|
||||
if(pqi->pt) infn->sp_dispose(pqi->pt);
|
||||
if(estr) free(estr);
|
||||
rsp_error(pwsc, pqi, err | E_DB, pe);
|
||||
return;
|
||||
}
|
||||
|
||||
pxml = xml_init(pwsc,1);
|
||||
|
||||
if(pqi->index_low > pqi->specifiedtotalcount) {
|
||||
returned = 0;
|
||||
} else {
|
||||
returned = pqi->index_high - pqi->index_low + 1;
|
||||
if(returned > (pqi->specifiedtotalcount - pqi->index_low))
|
||||
returned = pqi->specifiedtotalcount - pqi->index_low;
|
||||
}
|
||||
|
||||
xml_push(pxml,"response");
|
||||
xml_push(pxml,"status");
|
||||
xml_output(pxml,"errorcode","0");
|
||||
xml_output(pxml,"errorstring",estr ? estr : "");
|
||||
xml_output(pxml,"records","%d",returned);
|
||||
xml_output(pxml,"totalrecords","%d",pqi->specifiedtotalcount);
|
||||
xml_pop(pxml); /* status */
|
||||
|
||||
xml_push(pxml,"items");
|
||||
|
||||
while((infn->db_enum_fetch_row(NULL,&row,pqi) == 0) && (row)) {
|
||||
xml_push(pxml,"item");
|
||||
rowindex=0;
|
||||
while(rsp_fields[rowindex].name) {
|
||||
if((rsp_fields[rowindex].flags & type) &&
|
||||
(row[rowindex] && strlen(row[rowindex]))) {
|
||||
xml_output(pxml,rsp_fields[rowindex].name,"%s",
|
||||
row[rowindex]);
|
||||
}
|
||||
rowindex++;
|
||||
}
|
||||
xml_pop(pxml); /* item */
|
||||
}
|
||||
|
||||
infn->db_enum_end(NULL);
|
||||
|
||||
xml_pop(pxml); /* items */
|
||||
xml_pop(pxml); /* response */
|
||||
xml_deinit(pxml);
|
||||
|
||||
if(pqi->pt) infn->sp_dispose(pqi->pt);
|
||||
if(estr) free(estr);
|
||||
}
|
||||
|
||||
void rsp_browse(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
||||
XMLSTRUCT *pxml;
|
||||
char *pe;
|
||||
int err;
|
||||
char **row;
|
||||
int returned;
|
||||
char *query;
|
||||
char *estr = NULL;
|
||||
|
||||
/* FIXME */
|
||||
if(!strcmp(pqi->uri_sections[3],"artist")) {
|
||||
pqi->query_type = queryTypeBrowseArtists;
|
||||
} else if(!strcmp(pqi->uri_sections[3],"genre")) {
|
||||
pqi->query_type = queryTypeBrowseGenres;
|
||||
} else if(!strcmp(pqi->uri_sections[3],"album")) {
|
||||
pqi->query_type = queryTypeBrowseAlbums;
|
||||
} else if(!strcmp(pqi->uri_sections[3],"composer")) {
|
||||
pqi->query_type = queryTypeBrowseComposers;
|
||||
} else {
|
||||
rsp_error(pwsc, pqi, 0 | E_RSP, "Unsupported browse type");
|
||||
return;
|
||||
}
|
||||
|
||||
query = infn->ws_getvar(pwsc,"query");
|
||||
if(query) {
|
||||
pqi->pt = infn->sp_init();
|
||||
if(!infn->sp_parse(pqi->pt,query)) {
|
||||
estr = strdup(infn->sp_get_error(pqi->pt));
|
||||
infn->log(E_LOG,"Ignoring bad query (%s): %s\n",query,estr);
|
||||
infn->sp_dispose(pqi->pt);
|
||||
pqi->pt = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
pqi->index_type = indexTypeNone;
|
||||
if(infn->ws_getvar(pwsc,"offset")) {
|
||||
pqi->index_low = atoi(infn->ws_getvar(pwsc,"offset"));
|
||||
}
|
||||
if(infn->ws_getvar(pwsc,"limit")) {
|
||||
pqi->index_high = pqi->index_low + atoi(infn->ws_getvar(pwsc,"limit")) -1;
|
||||
if(pqi->index_high < pqi->index_low) {
|
||||
pqi->index_high = 999999;
|
||||
}
|
||||
} else {
|
||||
pqi->index_high = 999999; /* FIXME */
|
||||
}
|
||||
pqi->index_type = indexTypeSub;
|
||||
pqi->want_count = 1;
|
||||
pqi->playlist_id = atoi(pqi->uri_sections[2]);
|
||||
|
||||
if((err=infn->db_enum_start(&pe,pqi)) != 0) {
|
||||
if(pqi->pt) infn->sp_dispose(pqi->pt);
|
||||
if(estr) free(estr);
|
||||
rsp_error(pwsc, pqi, err | E_DB, pe);
|
||||
return;
|
||||
}
|
||||
|
||||
pxml = xml_init(pwsc,1);
|
||||
|
||||
if(pqi->index_low > pqi->specifiedtotalcount) {
|
||||
returned = 0;
|
||||
} else {
|
||||
returned = pqi->index_high - pqi->index_low + 1;
|
||||
if(returned > (pqi->specifiedtotalcount - pqi->index_low))
|
||||
returned = pqi->specifiedtotalcount - pqi->index_low;
|
||||
}
|
||||
|
||||
xml_push(pxml,"response");
|
||||
xml_push(pxml,"status");
|
||||
xml_output(pxml,"errorcode","0");
|
||||
xml_output(pxml,"errorstring",estr ? estr : "");
|
||||
xml_output(pxml,"records","%d",returned);
|
||||
xml_output(pxml,"totalrecords","%d",pqi->specifiedtotalcount);
|
||||
xml_pop(pxml); /* status */
|
||||
|
||||
xml_push(pxml,"items");
|
||||
|
||||
while((infn->db_enum_fetch_row(NULL,&row,pqi) == 0) && (row)) {
|
||||
xml_output(pxml,"item",row[0]);
|
||||
}
|
||||
|
||||
infn->db_enum_end(NULL);
|
||||
|
||||
xml_pop(pxml); /* items */
|
||||
xml_pop(pxml); /* response */
|
||||
xml_deinit(pxml);
|
||||
|
||||
if(pqi->pt) infn->sp_dispose(pqi->pt);
|
||||
if(estr) free(estr);
|
||||
}
|
||||
|
||||
void rsp_stream(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
||||
infn->stream(pwsc, pqi, pqi->uri_sections[2]);
|
||||
return;
|
||||
}
|
||||
|
||||
void rsp_error(WS_CONNINFO *pwsc, DBQUERYINFO *pqi, int eno, char *estr) {
|
||||
XMLSTRUCT *pxml;
|
||||
|
||||
pxml = xml_init(pwsc, 1);
|
||||
xml_push(pxml,"response");
|
||||
xml_push(pxml,"status");
|
||||
xml_output(pxml,"errorcode","%d",eno);
|
||||
xml_output(pxml,"errorstring","%s",estr);
|
||||
xml_output(pxml,"records","0");
|
||||
xml_output(pxml,"totalrecords","0");
|
||||
xml_pop(pxml); /* status */
|
||||
xml_pop(pxml); /* response */
|
||||
xml_deinit(pxml);
|
||||
infn->ws_close(pwsc);
|
||||
}
|
||||
|
18
src/plugins/rsp.h
Normal file
18
src/plugins/rsp.h
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* $Id: $
|
||||
*/
|
||||
|
||||
#ifndef _RSP_H_
|
||||
#define _RSP_H_
|
||||
|
||||
#define RSP_VERSION "1.0"
|
||||
|
||||
extern PLUGIN_INFO _pi;
|
||||
#define infn ((PLUGIN_INPUT_FN *)(_pi.fn))
|
||||
|
||||
#ifndef TRUE
|
||||
# define TRUE 1
|
||||
# define FALSE 0
|
||||
#endif
|
||||
|
||||
#endif /* _RSP_H_ */
|
380
src/plugins/xml-rpc.c
Normal file
380
src/plugins/xml-rpc.c
Normal file
@ -0,0 +1,380 @@
|
||||
/*
|
||||
* $Id: xml-rpc.c 970 2006-04-20 06:52:21Z rpedde $
|
||||
*
|
||||
* This really isn't xmlrpc. It's xmlrpc-ish. Emphasis on -ish.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "mtd-plugins.h"
|
||||
#include "rsp.h"
|
||||
#include "xml-rpc.h"
|
||||
|
||||
/* typedefs */
|
||||
typedef struct tag_xmlstack {
|
||||
char *tag;
|
||||
struct tag_xmlstack *next;
|
||||
} XMLSTACK;
|
||||
|
||||
#define XML_STREAM_BLOCK 4096
|
||||
typedef struct tag_xml_streambuffer {
|
||||
int fd;
|
||||
z_stream strm;
|
||||
unsigned char *in_buffer;
|
||||
unsigned char *out_buffer;
|
||||
} XML_STREAMBUFFER;
|
||||
|
||||
struct tag_xmlstruct {
|
||||
WS_CONNINFO *pwsc;
|
||||
int stack_level;
|
||||
XMLSTACK stack;
|
||||
XML_STREAMBUFFER *psb;
|
||||
};
|
||||
|
||||
/* Forwards */
|
||||
void xml_get_stats(WS_CONNINFO *pwsc);
|
||||
void xml_set_config(WS_CONNINFO *pwsc);
|
||||
void xml_return_error(WS_CONNINFO *pwsc, int errno, char *errstr);
|
||||
char *xml_entity_encode(char *original);
|
||||
|
||||
XML_STREAMBUFFER *xml_stream_open(int fd);
|
||||
int xml_stream_write(XML_STREAMBUFFER *psb, char *out);
|
||||
int xml_stream_close(XML_STREAMBUFFER *psb);
|
||||
|
||||
void xml_write(XMLSTRUCT *pxml, char *fmt, ...) {
|
||||
char buffer[1024];
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(buffer, 1024, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if(pxml->psb) {
|
||||
infn->log(E_DBG,"Writing %d bytes to output\n",strlen(buffer));
|
||||
xml_stream_write(pxml->psb, buffer);
|
||||
} else {
|
||||
infn->ws_writefd(pxml->pwsc,"%s",buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void xml_return_error(WS_CONNINFO *pwsc, int errno, char *errstr) {
|
||||
XMLSTRUCT *pxml;
|
||||
|
||||
pxml=xml_init(pwsc,TRUE);
|
||||
xml_push(pxml,"results");
|
||||
|
||||
xml_output(pxml,"status","%d",errno);
|
||||
xml_output(pxml,"statusstring","%s",errstr);
|
||||
|
||||
xml_pop(pxml); /* results */
|
||||
xml_deinit(pxml);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* open a gzip stream
|
||||
*/
|
||||
XML_STREAMBUFFER *xml_stream_open(int fd) {
|
||||
XML_STREAMBUFFER *psb;
|
||||
|
||||
psb = (XML_STREAMBUFFER*) malloc(sizeof(XML_STREAMBUFFER));
|
||||
if(!psb) {
|
||||
infn->log(E_FATAL,"xml_stream_open: malloc\n");
|
||||
}
|
||||
|
||||
psb->fd = fd;
|
||||
psb->out_buffer = (unsigned char*) malloc(XML_STREAM_BLOCK);
|
||||
psb->in_buffer = (unsigned char*) malloc(XML_STREAM_BLOCK);
|
||||
|
||||
if((!psb->out_buffer) || (!psb->in_buffer)) {
|
||||
infn->log(E_FATAL,"xml_stream_open: malloc\n");
|
||||
}
|
||||
|
||||
psb->strm.zalloc = Z_NULL;
|
||||
psb->strm.zfree = Z_NULL;
|
||||
psb->strm.opaque = Z_NULL;
|
||||
|
||||
psb->strm.next_in = psb->in_buffer;
|
||||
psb->strm.next_out = psb->out_buffer;
|
||||
|
||||
deflateInit2(&psb->strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
|
||||
24, 8, Z_DEFAULT_STRATEGY);
|
||||
return psb;
|
||||
}
|
||||
|
||||
/**
|
||||
* write a block to the stream
|
||||
*/
|
||||
int xml_stream_write(XML_STREAMBUFFER *psb, char *out) {
|
||||
int bytes = strlen(out);
|
||||
int done = 0;
|
||||
int result;
|
||||
|
||||
if((!out)||(!strlen(out)))
|
||||
return TRUE;
|
||||
|
||||
if(strlen(out) > 1024)
|
||||
return TRUE;
|
||||
|
||||
memcpy(psb->in_buffer,out,strlen(out));
|
||||
psb->strm.avail_in = strlen(out);
|
||||
psb->strm.next_in = psb->in_buffer;
|
||||
psb->strm.next_out = psb->out_buffer;
|
||||
psb->strm.avail_out = XML_STREAM_BLOCK;
|
||||
|
||||
while(!done) {
|
||||
result = deflate(&psb->strm, Z_NO_FLUSH);
|
||||
if(result != Z_OK) {
|
||||
infn->log(E_FATAL,"Error in zlib: %d\n",result);
|
||||
}
|
||||
/* FIXME: error handling */
|
||||
infn->log(E_DBG,"Writing %d bytes compressed info (avail: %d)\n",
|
||||
XML_STREAM_BLOCK-psb->strm.avail_out,
|
||||
psb->strm.avail_out);
|
||||
write(psb->fd,psb->out_buffer,XML_STREAM_BLOCK-psb->strm.avail_out);
|
||||
if(psb->strm.avail_out != 0) {
|
||||
done=1;
|
||||
} else {
|
||||
psb->strm.avail_out = XML_STREAM_BLOCK;
|
||||
psb->strm.next_out = psb->out_buffer;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* close the stream
|
||||
*/
|
||||
int xml_stream_close(XML_STREAMBUFFER *psb) {
|
||||
int done = 0;
|
||||
|
||||
/* flush what's left */
|
||||
while(!done) {
|
||||
psb->strm.avail_out = XML_STREAM_BLOCK;
|
||||
psb->strm.next_out = psb->out_buffer;
|
||||
psb->strm.avail_in = 0;
|
||||
psb->strm.next_in = psb->in_buffer;
|
||||
|
||||
deflate(&psb->strm,Z_FINISH);
|
||||
write(psb->fd,psb->out_buffer,XML_STREAM_BLOCK - psb->strm.avail_out);
|
||||
|
||||
if(psb->strm.avail_out != 0)
|
||||
done=1;
|
||||
}
|
||||
|
||||
infn->log(E_INF,"Done sending xml stream\n");
|
||||
deflateEnd(&psb->strm);
|
||||
if(psb->out_buffer != NULL)
|
||||
free(psb->out_buffer);
|
||||
if(psb->in_buffer != NULL)
|
||||
free(psb->in_buffer);
|
||||
free(psb);
|
||||
}
|
||||
|
||||
/**
|
||||
* create an xml response structure, a helper struct for
|
||||
* building xml responses.
|
||||
*
|
||||
* @param pwsc the pwsc we are emitting to
|
||||
* @param emit_header whether or not to throw out html headers and xml header
|
||||
* @returns XMLSTRUCT on success, or NULL if failure
|
||||
*/
|
||||
XMLSTRUCT *xml_init(WS_CONNINFO *pwsc, int emit_header) {
|
||||
XMLSTRUCT *pxml;
|
||||
char *accept;
|
||||
char *nogzip;
|
||||
|
||||
pxml=(XMLSTRUCT*)malloc(sizeof(XMLSTRUCT));
|
||||
if(!pxml) {
|
||||
infn->log(E_FATAL,"Malloc error\n");
|
||||
}
|
||||
|
||||
memset(pxml,0,sizeof(XMLSTRUCT));
|
||||
|
||||
pxml->pwsc = pwsc;
|
||||
|
||||
/* should we compress output? */
|
||||
nogzip = infn->ws_getvar(pwsc,"nogzip");
|
||||
accept = infn->ws_getrequestheader(pwsc,"accept-encoding");
|
||||
|
||||
if((!nogzip) && (accept) && (strcasestr(accept,"gzip"))) {
|
||||
infn->log(E_LOG,"Gzipping output\n");
|
||||
pxml->psb = xml_stream_open(infn->ws_fd(pwsc));
|
||||
if(pxml->psb) {
|
||||
infn->ws_addresponseheader(pwsc,"Content-Encoding","gzip");
|
||||
infn->ws_addresponseheader(pwsc,"Vary","Accept-Encoding");
|
||||
infn->ws_addresponseheader(pwsc,"Connection","Close");
|
||||
}
|
||||
}
|
||||
|
||||
/* the world would be a wonderful place without ie */
|
||||
infn->ws_addresponseheader(pwsc,"Cache-Control","no-cache");
|
||||
infn->ws_addresponseheader(pwsc,"Expires","-1");
|
||||
|
||||
if(emit_header) {
|
||||
infn->ws_addresponseheader(pwsc,"Content-Type","text/xml; charset=utf-8");
|
||||
infn->ws_writefd(pwsc,"HTTP/1.0 200 OK\r\n");
|
||||
infn->ws_emitheaders(pwsc);
|
||||
|
||||
|
||||
xml_write(pxml,"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>");
|
||||
}
|
||||
|
||||
return pxml;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* push a new term on the stack
|
||||
*
|
||||
* @param pxml xml struct obtained from xml_init
|
||||
* @param term next xlm section to start
|
||||
*/
|
||||
void xml_push(XMLSTRUCT *pxml, char *term) {
|
||||
XMLSTACK *pstack;
|
||||
|
||||
pstack = (XMLSTACK *)malloc(sizeof(XMLSTACK));
|
||||
pstack->next=pxml->stack.next;
|
||||
pstack->tag=strdup(term);
|
||||
pxml->stack.next=pstack;
|
||||
|
||||
pxml->stack_level++;
|
||||
|
||||
xml_write(pxml,"<%s>",term);
|
||||
}
|
||||
|
||||
/**
|
||||
* end an xml section
|
||||
*
|
||||
* @param pxml xml struct we are working with
|
||||
*/
|
||||
void xml_pop(XMLSTRUCT *pxml) {
|
||||
XMLSTACK *pstack;
|
||||
|
||||
pstack=pxml->stack.next;
|
||||
if(!pstack) {
|
||||
infn->log(E_LOG,"xml_pop: tried to pop an empty stack\n");
|
||||
return;
|
||||
}
|
||||
|
||||
pxml->stack.next = pstack->next;
|
||||
|
||||
xml_write(pxml,"</%s>",pstack->tag);
|
||||
free(pstack->tag);
|
||||
free(pstack);
|
||||
|
||||
pxml->stack_level--;
|
||||
}
|
||||
|
||||
/* FIXME: Fixed at 256? And can't I get an expandable sprintf/cat? */
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
output = xml_entity_encode(buf);
|
||||
if(section) {
|
||||
xml_push(pxml,section);
|
||||
}
|
||||
xml_write(pxml,"%s",output);
|
||||
free(output);
|
||||
if(section) {
|
||||
xml_pop(pxml);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* clean up an xml struct
|
||||
*
|
||||
* @param pxml xml struct to clean up
|
||||
*/
|
||||
void xml_deinit(XMLSTRUCT *pxml) {
|
||||
XMLSTACK *pstack;
|
||||
|
||||
if(pxml->stack.next) {
|
||||
infn->log(E_LOG,"xml_deinit: entries still on stack (%s)\n",
|
||||
pxml->stack.next->tag);
|
||||
}
|
||||
|
||||
while((pstack=pxml->stack.next)) {
|
||||
pxml->stack.next=pstack->next;
|
||||
free(pstack->tag);
|
||||
free(pstack);
|
||||
}
|
||||
|
||||
if(pxml->psb) {
|
||||
xml_stream_close(pxml->psb);
|
||||
}
|
||||
|
||||
free(pxml);
|
||||
}
|
||||
|
||||
/**
|
||||
* xml entity encoding, stupid style
|
||||
*/
|
||||
char *xml_entity_encode(char *original) {
|
||||
char *new;
|
||||
char *s, *d;
|
||||
int destsize;
|
||||
|
||||
destsize = 6*(int)strlen(original)+1;
|
||||
new=(char *)malloc(destsize);
|
||||
if(!new) return NULL;
|
||||
|
||||
memset(new,0x00,destsize);
|
||||
|
||||
s=original;
|
||||
d=new;
|
||||
|
||||
while(*s) {
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
||||
return new;
|
||||
}
|
20
src/plugins/xml-rpc.h
Normal file
20
src/plugins/xml-rpc.h
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* $Id: xml-rpc.h 917 2006-04-04 06:31:19Z rpedde $
|
||||
*/
|
||||
|
||||
#ifndef _XMLRPC_H_
|
||||
#define _XMLRPC_H_
|
||||
|
||||
#include "mtd-plugins.h"
|
||||
|
||||
struct tag_xmlstruct;
|
||||
typedef struct tag_xmlstruct XMLSTRUCT;
|
||||
|
||||
extern XMLSTRUCT *xml_init(WS_CONNINFO *pwsc, int emit_header);
|
||||
extern void xml_push(XMLSTRUCT *pxml, char *term);
|
||||
extern void xml_pop(XMLSTRUCT *pxml);
|
||||
extern void xml_output(XMLSTRUCT *pxml, char *section, char *fmt, ...);
|
||||
extern void xml_deinit(XMLSTRUCT *pxml);
|
||||
|
||||
|
||||
#endif /* _XMLRPC_H_ */
|
Loading…
Reference in New Issue
Block a user