mirror of
https://github.com/owntone/owntone-server.git
synced 2025-01-13 07:53:23 -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
|
value VARCHAR(1024) NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX idx_id ON songs(id);
|
#CREATE INDEX idx_id ON songs(id);
|
||||||
CREATE INDEX idx_path on songs(path);
|
#CREATE INDEX idx_path on songs(path);
|
||||||
|
|
||||||
INSERT INTO config (term, value) VALUES ('version','8');
|
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);
|
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
|
* daap_serialmem
|
||||||
*
|
*
|
||||||
* Serialize a daap tree to a fd;
|
* Serialize a daap tree to a fd;
|
||||||
*/
|
*/
|
||||||
int daap_serialize(DAAP_BLOCK *root, int fd, GZIP_STREAM *gz) {
|
int daap_serialize(DAAP_BLOCK *root, int fd, GZIP_STREAM *gz) {
|
||||||
char size[4];
|
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
|
// search a block's children and change an integer value
|
||||||
int daap_set_int(DAAP_BLOCK* parent, char* tag, int 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
|
#endif
|
||||||
|
|
||||||
|
16
src/daap.c
16
src/daap.c
@ -41,12 +41,6 @@
|
|||||||
#include "daapd.h"
|
#include "daapd.h"
|
||||||
#include "query.h"
|
#include "query.h"
|
||||||
|
|
||||||
typedef struct tag_daap_items {
|
|
||||||
int type;
|
|
||||||
char *tag;
|
|
||||||
char *description;
|
|
||||||
} DAAP_ITEMS;
|
|
||||||
|
|
||||||
DAAP_ITEMS taglist[] = {
|
DAAP_ITEMS taglist[] = {
|
||||||
{ 0x05, "miid", "dmap.itemid" },
|
{ 0x05, "miid", "dmap.itemid" },
|
||||||
{ 0x09, "minm", "dmap.itemname" },
|
{ 0x09, "minm", "dmap.itemname" },
|
||||||
@ -132,6 +126,16 @@ DAAP_ITEMS taglist[] = {
|
|||||||
{ 0x0C, "arif", "daap.resolveinfo" },
|
{ 0x0C, "arif", "daap.resolveinfo" },
|
||||||
{ 0x05, "aeNV", "com.apple.itunes.norm-volume" },
|
{ 0x05, "aeNV", "com.apple.itunes.norm-volume" },
|
||||||
{ 0x01, "aeSP", "com.apple.itunes.smart-playlist" },
|
{ 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 }
|
{ 0x00, NULL, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -233,7 +233,7 @@ void db_init_once(void) {
|
|||||||
int db_open(char *parameters, int reload) {
|
int db_open(char *parameters, int reload) {
|
||||||
char db_path[PATH_MAX + 1];
|
char db_path[PATH_MAX + 1];
|
||||||
int current_db_version;
|
int current_db_version;
|
||||||
char *errmsg;
|
char *perr;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if(pthread_once(&db_initlock,db_init_once))
|
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");
|
snprintf(db_path,sizeof(db_path),"%s/%s",parameters,"songs_sqlite.db");
|
||||||
|
|
||||||
db_gdbmlock();
|
db_gdbmlock();
|
||||||
db_songs=sqlite_open(db_path,0,&errmsg);
|
db_songs=sqlite_open(db_path,0,&perr);
|
||||||
if(!db_songs)
|
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) {
|
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)
|
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();
|
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 */
|
/* load up the red-black tree with all the current songs in the db */
|
||||||
|
|
||||||
db_gdbmlock();
|
db_gdbmlock();
|
||||||
|
sqlite_exec(db_songs,"PRAGMA synchronous=OFF;",NULL,NULL,&perr);
|
||||||
err=sqlite_get_table(db_songs,"SELECT id FROM songs",&resarray,
|
err=sqlite_get_table(db_songs,"SELECT id FROM songs",&resarray,
|
||||||
&rows, &cols, &perr);
|
&rows, &cols, &perr);
|
||||||
db_gdbmunlock();
|
db_gdbmunlock();
|
||||||
@ -474,6 +475,12 @@ int db_end_initial_update(void) {
|
|||||||
DB_PLAYLIST *current,*last;
|
DB_PLAYLIST *current,*last;
|
||||||
DB_PLAYLISTENTRY *pple;
|
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");
|
DPRINTF(E_DBG,L_DB|L_SCAN,"Initial update over. Removing stale items\n");
|
||||||
val=rblookup(RB_LUFIRST,NULL,db_removed);
|
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 start_time;
|
||||||
int end_time;
|
int end_time;
|
||||||
int bytes_written;
|
int bytes_written;
|
||||||
|
int serialize_as_xml;
|
||||||
|
|
||||||
MP3FILE *pmp3;
|
MP3FILE *pmp3;
|
||||||
int file_fd;
|
int file_fd;
|
||||||
@ -256,7 +257,7 @@ void daap_handler(WS_CONNINFO *pwsc) {
|
|||||||
* /databases/id/containers, which returns a container
|
* /databases/id/containers, which returns a container
|
||||||
* /databases/id/containers/id/items, which returns playlist elements
|
* /databases/id/containers/id/items, which returns playlist elements
|
||||||
* /databases/id/items/id.mp3, to spool an mp3
|
* /databases/id/items/id.mp3, to spool an mp3
|
||||||
* /databases/id/browse/category
|
* /databases/id/browse/category
|
||||||
*/
|
*/
|
||||||
|
|
||||||
uri = strdup(pwsc->uri);
|
uri = strdup(pwsc->uri);
|
||||||
@ -342,7 +343,13 @@ void daap_handler(WS_CONNINFO *pwsc) {
|
|||||||
if(!streaming) {
|
if(!streaming) {
|
||||||
DPRINTF(E_DBG,L_WS,"Satisfying request\n");
|
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;
|
compress=1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -366,11 +373,21 @@ void daap_handler(WS_CONNINFO *pwsc) {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
bytes_written = root->reported_size + 8;
|
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");
|
ws_writefd(pwsc,"HTTP/1.1 200 OK\r\n");
|
||||||
DPRINTF(E_DBG,L_WS,"Emitting headers\n");
|
DPRINTF(E_DBG,L_WS,"Emitting headers\n");
|
||||||
ws_emitheaders(pwsc);
|
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);
|
end_time = time(NULL);
|
||||||
DPRINTF(E_DBG,L_WS|L_DAAP,"Sent %d bytes in %d seconds\n",bytes_written,end_time-start_time);
|
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;
|
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 */
|
/* Restart versions of traditional functions */
|
||||||
|
|
||||||
int r_close(int fildes) {
|
int r_close(int fildes) {
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
|
|
||||||
struct timeval add2currenttime(double seconds);
|
struct timeval add2currenttime(double seconds);
|
||||||
int copyfile(int fromfd, int tofd);
|
int copyfile(int fromfd, int tofd);
|
||||||
|
int r_fdprintf(int fd, char *fmt, ...);
|
||||||
int r_close(int fildes);
|
int r_close(int fildes);
|
||||||
int r_dup2(int fildes, int fildes2);
|
int r_dup2(int fildes, int fildes2);
|
||||||
int r_open2(const char *path, int oflag);
|
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) {
|
int ws_testarg(ARGLIST *root, char *key, char *value) {
|
||||||
char *retval;
|
char *retval;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
|
||||||
DPRINTF(E_DBG,L_WS,"Checking to see if %s matches %s\n",key,value);
|
DPRINTF(E_DBG,L_WS,"Checking to see if %s matches %s\n",key,value);
|
||||||
|
|
||||||
retval=ws_getarg(root,key);
|
retval=ws_getarg(root,key);
|
||||||
if(!retval)
|
if(!retval) {
|
||||||
|
DPRINTF(E_DBG,L_WS,"Nope!\n");
|
||||||
return 0;
|
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;
|
current=root->next;
|
||||||
while(current) {
|
while(current) {
|
||||||
if(!strcmp(current->key,key)) {
|
if(!strcasecmp(current->key,key)) {
|
||||||
/* got a match! */
|
/* got a match! */
|
||||||
DPRINTF(E_DBG,L_WS,"Updating %s from %s to %s\n",
|
DPRINTF(E_DBG,L_WS,"Updating %s from %s to %s\n",
|
||||||
key,current->value,value);
|
key,current->value,value);
|
||||||
|
Loading…
Reference in New Issue
Block a user