mirror of
https://github.com/owntone/owntone-server.git
synced 2024-12-24 22:25:56 -05:00
Add xml output for daap requests (using output=xml in request)
This commit is contained in:
parent
c86be33245
commit
3b26120a3b
@ -38,7 +38,7 @@ CREATE TABLE config (
|
||||
value VARCHAR(1024) NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_id ON songs(id);
|
||||
CREATE INDEX idx_path on songs(path);
|
||||
#CREATE INDEX idx_id ON songs(id);
|
||||
#CREATE INDEX idx_path on songs(path);
|
||||
|
||||
INSERT INTO config (term, value) VALUES ('version','8');
|
||||
|
145
src/daap-proto.c
145
src/daap-proto.c
@ -333,11 +333,154 @@ DAAP_BLOCK *daap_add_empty(DAAP_BLOCK *parent, char *tag) {
|
||||
return daap_add_formatted(parent,tag,0,NULL);
|
||||
}
|
||||
|
||||
|
||||
DAAP_ITEMS *daap_lookup_tag(char *tag) {
|
||||
DAAP_ITEMS *pitem;
|
||||
|
||||
pitem=taglist;
|
||||
while((pitem->tag) && (strncmp(tag,pitem->tag,4))) {
|
||||
pitem++;
|
||||
}
|
||||
|
||||
if(!pitem->tag)
|
||||
DPRINTF(E_FATAL,L_DAAP,"Unknown daap tag: %c%c%c%c\n",tag[0],tag[1],tag[2],tag[3]);
|
||||
|
||||
return pitem;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* xml entity encoding, stupid style
|
||||
*/
|
||||
char *daap_xml_entity_encode(char *original) {
|
||||
char *new;
|
||||
char *s, *d;
|
||||
int destsize;
|
||||
|
||||
destsize = 6*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;
|
||||
}
|
||||
|
||||
/**
|
||||
* serialize a dmap tree as xml
|
||||
*/
|
||||
int daap_serialize_xml(DAAP_BLOCK *root, int fd) {
|
||||
DAAP_ITEMS *pitem;
|
||||
unsigned char *data;
|
||||
int ivalue;
|
||||
long long lvalue;
|
||||
char *encoded_string;
|
||||
|
||||
while(root) {
|
||||
if(root->free)
|
||||
data=(unsigned char *)root->value;
|
||||
else
|
||||
data=(unsigned char *)root->svalue;
|
||||
|
||||
pitem=daap_lookup_tag(root->tag);
|
||||
r_fdprintf(fd,"<%s>",pitem->description);
|
||||
if(pitem->type != 0x0c) { /* container */
|
||||
switch(pitem->type) {
|
||||
case 0x01: /* byte */
|
||||
r_fdprintf(fd,"%d",*((char *)data));
|
||||
break;
|
||||
case 0x02: /* unsigned byte */
|
||||
r_fdprintf(fd,"%ud",*((char *)data));
|
||||
break;
|
||||
case 0x03: /* short */
|
||||
ivalue = data[0] << 8 | data[1];
|
||||
r_fdprintf(fd,"%d",ivalue);
|
||||
break;
|
||||
case 0x05: /* int */
|
||||
case 0x0A: /* epoch */
|
||||
ivalue = data[0] << 24 |
|
||||
data[1] << 16 |
|
||||
data[2] << 8 |
|
||||
data[3];
|
||||
r_fdprintf(fd,"%d",ivalue);
|
||||
break;
|
||||
case 0x07: /* long long */
|
||||
ivalue = data[0] << 24 |
|
||||
data[1] << 16 |
|
||||
data[2] << 8 |
|
||||
data[3];
|
||||
lvalue=ivalue;
|
||||
ivalue = data[4] << 24 |
|
||||
data[5] << 16 |
|
||||
data[6] << 8 |
|
||||
data[7];
|
||||
lvalue = (lvalue << 32) | ivalue;
|
||||
r_fdprintf(fd,"%ll",ivalue);
|
||||
break;
|
||||
case 0x09: /* string */
|
||||
encoded_string=daap_xml_entity_encode(data);
|
||||
r_fdprintf(fd,"%s",encoded_string);
|
||||
free(encoded_string);
|
||||
break;
|
||||
case 0x0B: /* version? */
|
||||
ivalue=data[0] << 8 | data[1];
|
||||
r_fdprintf(fd,"%d.%d.%d",ivalue,data[2],data[3]);
|
||||
break;
|
||||
default:
|
||||
DPRINTF(E_FATAL,L_DAAP,"Bad dmap type: %d, %s\n",
|
||||
pitem->type, pitem->description);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
daap_serialize_xml(root->children,fd);
|
||||
}
|
||||
r_fdprintf(fd,"</%s>",pitem->description);
|
||||
root=root->next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* daap_serialmem
|
||||
*
|
||||
* Serialize a daap tree to a fd;
|
||||
*/
|
||||
*/
|
||||
int daap_serialize(DAAP_BLOCK *root, int fd, GZIP_STREAM *gz) {
|
||||
char size[4];
|
||||
|
||||
|
@ -74,5 +74,14 @@ DAAP_BLOCK *daap_find(DAAP_BLOCK *parent, char* tag);
|
||||
// search a block's children and change an integer value
|
||||
int daap_set_int(DAAP_BLOCK* parent, char* tag, int value);
|
||||
|
||||
typedef struct tag_daap_items {
|
||||
int type;
|
||||
char *tag;
|
||||
char *description;
|
||||
} DAAP_ITEMS;
|
||||
|
||||
extern DAAP_ITEMS taglist[];
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
16
src/daap.c
16
src/daap.c
@ -41,12 +41,6 @@
|
||||
#include "daapd.h"
|
||||
#include "query.h"
|
||||
|
||||
typedef struct tag_daap_items {
|
||||
int type;
|
||||
char *tag;
|
||||
char *description;
|
||||
} DAAP_ITEMS;
|
||||
|
||||
DAAP_ITEMS taglist[] = {
|
||||
{ 0x05, "miid", "dmap.itemid" },
|
||||
{ 0x09, "minm", "dmap.itemname" },
|
||||
@ -132,6 +126,16 @@ DAAP_ITEMS taglist[] = {
|
||||
{ 0x0C, "arif", "daap.resolveinfo" },
|
||||
{ 0x05, "aeNV", "com.apple.itunes.norm-volume" },
|
||||
{ 0x01, "aeSP", "com.apple.itunes.smart-playlist" },
|
||||
/* iTunes 4.5+ */
|
||||
{ 0x01, "msas", "dmap.authenticationschemes" },
|
||||
{ 0x05, "ascd", "daap.songcodectype" },
|
||||
{ 0x05, "aeSV", "com.apple.itunes.music-sharing-version" },
|
||||
{ 0x05, "aePI", "com.apple.itunes.itms-playlistid" },
|
||||
{ 0x05, "aeCI", "com.apple.iTunes.itms-composerid" },
|
||||
{ 0x05, "aeGI", "com.apple.iTunes.itms-genreid" },
|
||||
{ 0x05, "aeAI", "com.apple.iTunes.itms-artistid" },
|
||||
{ 0x05, "aeSI", "com.apple.iTunes.itms-songid" },
|
||||
{ 0x09, "agrp", "daap.songgrouping" },
|
||||
{ 0x00, NULL, NULL }
|
||||
};
|
||||
|
||||
|
@ -233,7 +233,7 @@ void db_init_once(void) {
|
||||
int db_open(char *parameters, int reload) {
|
||||
char db_path[PATH_MAX + 1];
|
||||
int current_db_version;
|
||||
char *errmsg;
|
||||
char *perr;
|
||||
int err;
|
||||
|
||||
if(pthread_once(&db_initlock,db_init_once))
|
||||
@ -242,14 +242,14 @@ int db_open(char *parameters, int reload) {
|
||||
snprintf(db_path,sizeof(db_path),"%s/%s",parameters,"songs_sqlite.db");
|
||||
|
||||
db_gdbmlock();
|
||||
db_songs=sqlite_open(db_path,0,&errmsg);
|
||||
db_songs=sqlite_open(db_path,0,&perr);
|
||||
if(!db_songs)
|
||||
DPRINTF(E_FATAL,L_DB,"db_open: %s\n",errmsg);
|
||||
DPRINTF(E_FATAL,L_DB,"db_open: %s\n",perr);
|
||||
|
||||
if(reload) {
|
||||
err=sqlite_exec(db_songs,"DELETE FROM songs",NULL,NULL,&errmsg);
|
||||
err=sqlite_exec(db_songs,"DELETE FROM songs",NULL,NULL,&perr);
|
||||
if(err != SQLITE_OK)
|
||||
DPRINTF(E_FATAL,L_DB,"Cannot reload tables: %s\n",errmsg);
|
||||
DPRINTF(E_FATAL,L_DB,"Cannot reload tables: %s\n",perr);
|
||||
}
|
||||
|
||||
db_gdbmunlock();
|
||||
@ -423,6 +423,7 @@ int db_start_initial_update(void) {
|
||||
/* load up the red-black tree with all the current songs in the db */
|
||||
|
||||
db_gdbmlock();
|
||||
sqlite_exec(db_songs,"PRAGMA synchronous=OFF;",NULL,NULL,&perr);
|
||||
err=sqlite_get_table(db_songs,"SELECT id FROM songs",&resarray,
|
||||
&rows, &cols, &perr);
|
||||
db_gdbmunlock();
|
||||
@ -474,6 +475,12 @@ int db_end_initial_update(void) {
|
||||
DB_PLAYLIST *current,*last;
|
||||
DB_PLAYLISTENTRY *pple;
|
||||
|
||||
char *perr;
|
||||
|
||||
db_gdbmlock();
|
||||
sqlite_exec(db_songs,"PRAGMA synchronous=NORMAL;",NULL,NULL,&perr);
|
||||
db_gdbmunlock();
|
||||
|
||||
DPRINTF(E_DBG,L_DB|L_SCAN,"Initial update over. Removing stale items\n");
|
||||
val=rblookup(RB_LUFIRST,NULL,db_removed);
|
||||
|
||||
|
25
src/main.c
25
src/main.c
@ -182,6 +182,7 @@ void daap_handler(WS_CONNINFO *pwsc) {
|
||||
int start_time;
|
||||
int end_time;
|
||||
int bytes_written;
|
||||
int serialize_as_xml;
|
||||
|
||||
MP3FILE *pmp3;
|
||||
int file_fd;
|
||||
@ -256,7 +257,7 @@ void daap_handler(WS_CONNINFO *pwsc) {
|
||||
* /databases/id/containers, which returns a container
|
||||
* /databases/id/containers/id/items, which returns playlist elements
|
||||
* /databases/id/items/id.mp3, to spool an mp3
|
||||
* /databases/id/browse/category
|
||||
* /databases/id/browse/category
|
||||
*/
|
||||
|
||||
uri = strdup(pwsc->uri);
|
||||
@ -342,7 +343,13 @@ void daap_handler(WS_CONNINFO *pwsc) {
|
||||
if(!streaming) {
|
||||
DPRINTF(E_DBG,L_WS,"Satisfying request\n");
|
||||
|
||||
if((config.compress) && ws_testrequestheader(pwsc,"Accept-Encoding","gzip") && root->reported_size >= 1000) {
|
||||
serialize_as_xml=0;
|
||||
if(ws_getvar(pwsc,"output"))
|
||||
serialize_as_xml=1;
|
||||
|
||||
|
||||
if((config.compress) && ws_testrequestheader(pwsc,"Accept-Encoding","gzip") &&
|
||||
(root->reported_size >= 1000) && (!serialize_as_xml)) {
|
||||
compress=1;
|
||||
}
|
||||
|
||||
@ -366,11 +373,21 @@ void daap_handler(WS_CONNINFO *pwsc) {
|
||||
}
|
||||
else {
|
||||
bytes_written = root->reported_size + 8;
|
||||
ws_addresponseheader(pwsc,"Content-Length","%d",bytes_written);
|
||||
if(!serialize_as_xml) {
|
||||
ws_addresponseheader(pwsc,"Content-Length","%d",bytes_written);
|
||||
} else {
|
||||
ws_addresponseheader(pwsc,"Connection","close");
|
||||
pwsc->close=1;
|
||||
}
|
||||
ws_writefd(pwsc,"HTTP/1.1 200 OK\r\n");
|
||||
DPRINTF(E_DBG,L_WS,"Emitting headers\n");
|
||||
ws_emitheaders(pwsc);
|
||||
daap_serialize(root,pwsc->fd,NULL);
|
||||
if(!serialize_as_xml) {
|
||||
daap_serialize(root,pwsc->fd,NULL);
|
||||
} else {
|
||||
ws_writefd(pwsc,"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>");
|
||||
daap_serialize_xml(root,pwsc->fd);
|
||||
}
|
||||
}
|
||||
end_time = time(NULL);
|
||||
DPRINTF(E_DBG,L_WS|L_DAAP,"Sent %d bytes in %d seconds\n",bytes_written,end_time-start_time);
|
||||
|
@ -68,6 +68,18 @@ static int gettimeout(struct timeval end,
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int r_fdprintf(int fd, char *fmt, ...) {
|
||||
char buffer[1024];
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(buffer, 1024, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return r_write(fd,buffer,strlen(buffer));
|
||||
}
|
||||
|
||||
/* Restart versions of traditional functions */
|
||||
|
||||
int r_close(int fildes) {
|
||||
|
@ -43,6 +43,7 @@
|
||||
|
||||
struct timeval add2currenttime(double seconds);
|
||||
int copyfile(int fromfd, int tofd);
|
||||
int r_fdprintf(int fd, char *fmt, ...);
|
||||
int r_close(int fildes);
|
||||
int r_dup2(int fildes, int fildes2);
|
||||
int r_open2(const char *path, int oflag);
|
||||
|
@ -1109,14 +1109,20 @@ int ws_testrequestheader(WS_CONNINFO *pwsc, char *header, char *value) {
|
||||
*/
|
||||
int ws_testarg(ARGLIST *root, char *key, char *value) {
|
||||
char *retval;
|
||||
int result;
|
||||
|
||||
|
||||
DPRINTF(E_DBG,L_WS,"Checking to see if %s matches %s\n",key,value);
|
||||
|
||||
retval=ws_getarg(root,key);
|
||||
if(!retval)
|
||||
if(!retval) {
|
||||
DPRINTF(E_DBG,L_WS,"Nope!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return !strcasecmp(value,retval);
|
||||
result=!strcasecmp(value,retval);
|
||||
DPRINTF(E_DBG,L_WS,"And it %s\n",result ? "DOES!" : "does NOT");
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1187,7 +1193,7 @@ int ws_addarg(ARGLIST *root, char *key, char *fmt, ...) {
|
||||
|
||||
current=root->next;
|
||||
while(current) {
|
||||
if(!strcmp(current->key,key)) {
|
||||
if(!strcasecmp(current->key,key)) {
|
||||
/* got a match! */
|
||||
DPRINTF(E_DBG,L_WS,"Updating %s from %s to %s\n",
|
||||
key,current->value,value);
|
||||
|
Loading…
Reference in New Issue
Block a user