owntone-server/src/dispatch.c

1404 lines
46 KiB
C
Raw Normal View History

2005-03-11 06:39:40 +00:00
/*
* $Id$
* daap handler functions and dispatch code
*
* Copyright (C) 2003 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
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
2005-04-16 21:26:35 +00:00
#include <netinet/in.h>
2005-03-11 06:39:40 +00:00
#include "db-generic.h"
#include "configfile.h"
#include "err.h"
#include "mp3-scanner.h"
#include "webserver.h"
#include "ssc.h"
#include "dynamic-art.h"
#include "restart.h"
#include "daapd.h"
2005-03-14 06:17:28 +00:00
#include "query.h"
2005-03-11 06:39:40 +00:00
/* Forwards */
static void dispatch_server_info(WS_CONNINFO *pwsc, DBQUERYINFO *pqi);
static void dispatch_login(WS_CONNINFO *pwsc, DBQUERYINFO *pqi);
static void dispatch_content_codes(WS_CONNINFO *pwsc, DBQUERYINFO *pqi);
static void dispatch_update(WS_CONNINFO *pwsc, DBQUERYINFO *pqi);
static void dispatch_dbinfo(WS_CONNINFO *pwsc, DBQUERYINFO *pqi);
static void dispatch_playlistitems(WS_CONNINFO *pwsc, DBQUERYINFO *pqi);
static void dispatch_stream(WS_CONNINFO *pwsc, DBQUERYINFO *pqi);
static void dispatch_browse(WS_CONNINFO *pwsc, DBQUERYINFO *pqi);
static void dispatch_playlists(WS_CONNINFO *pqsc, DBQUERYINFO *pqi);
static void dispatch_addplaylist(WS_CONNINFO *pwsc, DBQUERYINFO *pqi);
static void dispatch_addplaylistitems(WS_CONNINFO *pwsc, DBQUERYINFO *pqi);
2005-11-03 05:02:17 +00:00
static void dispatch_editplaylist(WS_CONNINFO *pwsc, DBQUERYINFO *pqi);
static void dispatch_deleteplaylist(WS_CONNINFO *pwsc, DBQUERYINFO *pqi);
static void dispatch_deleteplaylistitems(WS_CONNINFO *pwsc, DBQUERYINFO *pqi);
2005-03-11 06:39:40 +00:00
static void dispatch_items(WS_CONNINFO *pwsc, DBQUERYINFO *pqi);
static void dispatch_logout(WS_CONNINFO *pwsc, DBQUERYINFO *pqi);
2005-03-16 11:38:14 +00:00
static int dispatch_output_start(WS_CONNINFO *pwsc, DBQUERYINFO *pqi, int content_length);
2005-05-30 03:41:31 +00:00
static int dispatch_output_write(WS_CONNINFO *pwsc, DBQUERYINFO *pqi, unsigned char *block, int len);
2005-03-16 11:38:14 +00:00
static int dispatch_output_end(WS_CONNINFO *pwsc, DBQUERYINFO *pqi);
2005-03-11 06:39:40 +00:00
2005-03-16 11:38:14 +00:00
static DAAP_ITEMS *dispatch_xml_lookup_tag(char *tag);
static char *dispatch_xml_encode(char *original, int len);
2005-05-30 03:41:31 +00:00
static int dispatch_output_xml_write(WS_CONNINFO *pwsc, DBQUERYINFO *pqi, unsigned char *block, int len);
2005-03-11 06:39:40 +00:00
2005-03-16 11:38:14 +00:00
/**
* Hold the inf for the output serializer
*/
typedef struct tag_xml_stack {
char tag[5];
int bytes_left;
} XML_STACK;
typedef struct tag_output_info {
int xml_output;
int readable;
int browse_response;
int dmap_response_length;
int stack_height;
XML_STACK stack[10];
} OUTPUT_INFO;
2005-03-11 06:39:40 +00:00
/**
* Handles authentication for the daap server. This isn't the
* authenticator for the web admin page, but rather the iTunes
* authentication when trying to connect to the server. Note that most
* of this is actually handled in the web server registration, which
* decides when to apply the authentication or not. If you mess with
* when and where the webserver applies auth or not, you'll likely
* break something. It seems that some requests must be authed, and others
* not. If you apply authentication somewhere that iTunes doesn't expect
* it, it happily disconnects.
*
* \param username The username passed by iTunes
* \param password The password passed by iTunes
* \returns 1 if auth successful, 0 otherwise
*/
int daap_auth(char *username, char *password) {
if((password == NULL) &&
((config.readpassword == NULL) || (strlen(config.readpassword)==0)))
2005-11-03 05:02:17 +00:00
return 1;
2005-03-11 06:39:40 +00:00
if(password == NULL)
2005-11-03 05:02:17 +00:00
return 0;
2005-03-11 06:39:40 +00:00
return !strcasecmp(password,config.readpassword);
}
2005-03-16 11:38:14 +00:00
/**
* decodes the request and hands it off to the appropriate dispatcher
*
* \param pwsc the current web connection info
*/
2005-03-11 06:39:40 +00:00
void daap_handler(WS_CONNINFO *pwsc) {
DBQUERYINFO *pqi;
char *token, *string, *save;
2005-03-14 06:17:28 +00:00
char *query;
2005-03-11 06:39:40 +00:00
pqi=(DBQUERYINFO*)malloc(sizeof(DBQUERYINFO));
if(!pqi) {
2005-11-03 05:02:17 +00:00
ws_returnerror(pwsc,500,"Internal server error: out of memory!");
return;
2005-03-11 06:39:40 +00:00
}
memset(pqi,0x00,sizeof(DBQUERYINFO));
2005-03-14 06:17:28 +00:00
query=ws_getvar(pwsc,"query");
if(!query) query=ws_getvar(pwsc,"filter");
if(query) {
2005-11-03 05:02:17 +00:00
DPRINTF(E_DBG,L_DAAP,"Getting sql clause for %s\n",query);
pqi->whereclause = query_build_sql(query);
2005-03-14 06:17:28 +00:00
}
2005-03-11 06:39:40 +00:00
/* Add some default headers */
ws_addresponseheader(pwsc,"Accept-Ranges","bytes");
ws_addresponseheader(pwsc,"DAAP-Server","mt-daapd/" VERSION);
ws_addresponseheader(pwsc,"Content-Type","application/x-dmap-tagged");
2005-11-11 23:52:42 +00:00
ws_addresponseheader(pwsc,"Cache-Control","no-cache"); /* anti-ie defense */
ws_addresponseheader(pwsc,"Expires","-1");
2005-03-11 06:39:40 +00:00
if(ws_getvar(pwsc,"session-id"))
2005-11-03 05:02:17 +00:00
pqi->session_id = atoi(ws_getvar(pwsc,"session-id"));
2005-03-11 06:39:40 +00:00
/* tokenize the uri for easier decoding */
string=(pwsc->uri)+1;
while((token=strtok_r(string,"/",&save))) {
2005-11-03 05:02:17 +00:00
string=NULL;
pqi->uri_sections[pqi->uri_count++] = token;
2005-03-11 06:39:40 +00:00
}
/* Start dispatching */
if(!strcasecmp(pqi->uri_sections[0],"server-info"))
2005-11-03 05:02:17 +00:00
return dispatch_server_info(pwsc,pqi);
2005-03-11 06:39:40 +00:00
if(!strcasecmp(pqi->uri_sections[0],"content-codes"))
2005-11-03 05:02:17 +00:00
return dispatch_content_codes(pwsc,pqi);
2005-03-11 06:39:40 +00:00
if(!strcasecmp(pqi->uri_sections[0],"login"))
2005-11-03 05:02:17 +00:00
return dispatch_login(pwsc,pqi);
2005-03-11 06:39:40 +00:00
if(!strcasecmp(pqi->uri_sections[0],"update"))
2005-11-03 05:02:17 +00:00
return dispatch_update(pwsc,pqi);
2005-03-11 06:39:40 +00:00
if(!strcasecmp(pqi->uri_sections[0],"logout"))
2005-11-03 05:02:17 +00:00
return dispatch_logout(pwsc,pqi);
2005-03-11 06:39:40 +00:00
/*
* /databases/id/items
* /databases/id/containers
* /databases/id/containers/id/items
* /databases/id/browse/category
* /databases/id/items/id.mp3
*/
if(!strcasecmp(pqi->uri_sections[0],"databases")) {
2005-11-03 05:02:17 +00:00
if(pqi->uri_count == 1) {
return dispatch_dbinfo(pwsc,pqi);
}
pqi->db_id=atoi(pqi->uri_sections[1]);
if(pqi->uri_count == 3) {
if(!strcasecmp(pqi->uri_sections[2],"items"))
/* /databases/id/items */
return dispatch_items(pwsc,pqi);
if(!strcasecmp(pqi->uri_sections[2],"containers"))
/* /databases/id/containers */
return dispatch_playlists(pwsc,pqi);
pwsc->close=1;
free(pqi);
ws_returnerror(pwsc,404,"Page not found");
return;
}
if(pqi->uri_count == 4) {
if(!strcasecmp(pqi->uri_sections[2],"browse"))
/* /databases/id/browse/something */
return dispatch_browse(pwsc,pqi);
if(!strcasecmp(pqi->uri_sections[2],"items"))
/* /databases/id/items/id.mp3 */
return dispatch_stream(pwsc,pqi);
if((!strcasecmp(pqi->uri_sections[2],"containers")) &&
(!strcasecmp(pqi->uri_sections[3],"add")))
/* /databases/id/containers/add */
return dispatch_addplaylist(pwsc,pqi);
if((!strcasecmp(pqi->uri_sections[2],"containers")) &&
(!strcasecmp(pqi->uri_sections[3],"del")))
/* /databases/id/containers/del */
return dispatch_deleteplaylist(pwsc,pqi);
if((!strcasecmp(pqi->uri_sections[2],"containers")) &&
(!strcasecmp(pqi->uri_sections[3],"edit")))
/* /databases/id/contaienrs/edit */
return dispatch_editplaylist(pwsc,pqi);
pwsc->close=1;
free(pqi);
ws_returnerror(pwsc,404,"Page not found");
return;
}
if(pqi->uri_count == 5) {
if((!strcasecmp(pqi->uri_sections[2],"containers")) &&
(!strcasecmp(pqi->uri_sections[4],"items"))) {
pqi->playlist_id=atoi(pqi->uri_sections[3]);
return dispatch_playlistitems(pwsc,pqi);
}
if((!strcasecmp(pqi->uri_sections[2],"containers")) &&
(!strcasecmp(pqi->uri_sections[4],"del"))) {
/* /databases/id/containers/id/del */
pqi->playlist_id=atoi(pqi->uri_sections[3]);
return dispatch_deleteplaylistitems(pwsc,pqi);
}
}
if(pqi->uri_count == 6) {
if((!strcasecmp(pqi->uri_sections[2],"containers")) &&
(!strcasecmp(pqi->uri_sections[4],"items")) &&
(!strcasecmp(pqi->uri_sections[5],"add"))) {
pqi->playlist_id=atoi(pqi->uri_sections[3]);
return dispatch_addplaylistitems(pwsc,pqi);
}
}
2005-03-11 06:39:40 +00:00
}
pwsc->close=1;
free(pqi);
ws_returnerror(pwsc,404,"Page not found");
return;
}
2005-03-16 11:38:14 +00:00
/**
* set up whatever necessary to begin streaming the output
* to the client.
*
* \param pwsc pointer to the current conninfo struct
* \param pqi pointer to the current dbquery struct
* \param content_length content_length (assuming dmap) of the output
*/
int dispatch_output_start(WS_CONNINFO *pwsc, DBQUERYINFO *pqi, int content_length) {
OUTPUT_INFO *poi;
poi=(OUTPUT_INFO*)calloc(1,sizeof(OUTPUT_INFO));
if(!poi) {
2005-11-03 05:02:17 +00:00
DPRINTF(E_LOG,L_DAAP,"Malloc error in dispatch_ouput_start\n");
return -1;
2005-03-16 11:38:14 +00:00
}
pqi->output_info = (void*) poi;
poi->dmap_response_length = content_length;
if(ws_getvar(pwsc,"output")) {
2005-11-03 05:02:17 +00:00
if(strcasecmp(ws_getvar(pwsc,"output"),"readable") == 0)
poi->readable=1;
poi->xml_output=1;
ws_addresponseheader(pwsc,"Content-Type","text/xml");
ws_addresponseheader(pwsc,"Connection","Close");
pwsc->close=1;
ws_writefd(pwsc,"HTTP/1.1 200 OK\r\n");
ws_emitheaders(pwsc);
ws_writefd(pwsc,"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>");
if(poi->readable)
ws_writefd(pwsc,"\n");
return 0;
2005-03-16 11:38:14 +00:00
}
ws_addresponseheader(pwsc,"Content-Length","%d",poi->dmap_response_length);
ws_writefd(pwsc,"HTTP/1.1 200 OK\r\n");
ws_emitheaders(pwsc);
/* I guess now we would start writing the output */
return 0;
}
/**
* write the output to wherever it goes. This expects to be fed
* full dmap blocks. In the simplest case, it just streams those
* dmap blocks out to the client. In more complex cases, it convert
* them to xml, or compresses them.
*
* \param pqi pointer to the current dbquery info struct
* \param pwsc pointer to the current conninfo struct
* \param pblock block of data to write
* \param len length of block to write
*/
2005-05-30 03:41:31 +00:00
int dispatch_output_write(WS_CONNINFO *pwsc, DBQUERYINFO *pqi, unsigned char *block, int len) {
2005-03-16 11:38:14 +00:00
OUTPUT_INFO *poi=(pqi->output_info);
int result;
if(poi->xml_output)
2005-11-03 05:02:17 +00:00
return dispatch_output_xml_write(pwsc, pqi, block, len);
2005-03-16 11:38:14 +00:00
result=r_write(pwsc->fd,block,len);
if(result != len)
2005-11-03 05:02:17 +00:00
return -1;
2005-03-16 11:38:14 +00:00
return 0;
}
/**
* this is the serializer for xml. This assumes that (with the exception of
* containers) blocks are complete dmap blocks
*
* \param pqi pointer to the current dbquery info struct
* \param pwsc pointer to the current conninfo struct
* \param pblock block of data to write
* \param len length of block to write
*/
2005-05-30 03:41:31 +00:00
int dispatch_output_xml_write(WS_CONNINFO *pwsc, DBQUERYINFO *pqi, unsigned char *block, int len) {
2005-03-16 11:38:14 +00:00
OUTPUT_INFO *poi = pqi->output_info;
2005-05-30 03:41:31 +00:00
unsigned char *current=block;
2005-03-16 11:38:14 +00:00
char block_tag[5];
int block_len;
int len_left;
DAAP_ITEMS *pitem;
unsigned char *data;
int ivalue;
long long lvalue;
int block_done=1;
int stack_ptr;
char *encoded_string;
while(current < (block + len)) {
2005-11-03 05:02:17 +00:00
block_done=1;
len_left=(block+len) - current;
if(len_left < 8) {
DPRINTF(E_FATAL,L_DAAP,"Badly formatted dmap block - frag size: %d",len_left);
}
/* set up block */
memcpy(block_tag,current,4);
block_tag[4] = '\0';
block_len = current[4] << 24 | current[5] << 16 |
current[6] << 8 | current[7];
data = &current[8];
if(strncmp(block_tag,"abro",4) ==0 ) {
/* browse queries treat mlit as a string, not container */
poi->browse_response=1;
}
/* lookup and serialize */
DPRINTF(E_SPAM,L_DAAP,"%*s %s: %d\n",poi->stack_height,"",block_tag,block_len);
pitem=dispatch_xml_lookup_tag(block_tag);
if(poi->readable)
r_fdprintf(pwsc->fd,"%*s",poi->stack_height,"");
r_fdprintf(pwsc->fd,"<%s>",pitem->description);
switch(pitem->type) {
case 0x01: /* byte */
if(block_len != 1) {
DPRINTF(E_FATAL,L_DAAP,"tag %s, size %d, wanted 1\n",block_tag, block_len);
}
r_fdprintf(pwsc->fd,"%d",*((char *)data));
break;
case 0x02: /* unsigned byte */
if(block_len != 1) {
DPRINTF(E_FATAL,L_DAAP,"tag %s, size %d, wanted 1\n",block_tag, block_len);
}
r_fdprintf(pwsc->fd,"%ud",*((char *)data));
break;
case 0x03: /* short */
if(block_len != 2) {
DPRINTF(E_FATAL,L_DAAP,"tag %s, size %d, wanted 2\n",block_tag, block_len);
}
ivalue = data[0] << 8 | data[1];
r_fdprintf(pwsc->fd,"%d",ivalue);
break;
case 0x05: /* int */
case 0x0A: /* epoch */
if(block_len != 4) {
DPRINTF(E_FATAL,L_DAAP,"tag %s, size %d, wanted 4\n",block_tag, block_len);
}
ivalue = data[0] << 24 |
data[1] << 16 |
data[2] << 8 |
data[3];
r_fdprintf(pwsc->fd,"%d",ivalue);
break;
case 0x07: /* long long */
if(block_len != 8) {
DPRINTF(E_FATAL,L_DAAP,"tag %s, size %d, wanted 8\n",block_tag, block_len);
}
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(pwsc->fd,"%ll",ivalue);
break;
case 0x09: /* string */
if(block_len) {
encoded_string=dispatch_xml_encode((char*)data,block_len);
r_fdprintf(pwsc->fd,"%s",encoded_string);
free(encoded_string);
}
break;
case 0x0B: /* version? */
if(block_len != 4) {
DPRINTF(E_FATAL,L_DAAP,"tag %s, size %d, wanted 4\n",block_tag, block_len);
}
ivalue=data[0] << 8 | data[1];
r_fdprintf(pwsc->fd,"%d.%d.%d",ivalue,data[2],data[3]);
break;
case 0x0C:
if((poi->browse_response)&&(strcmp(block_tag,"mlit") ==0)) {
if(block_len) {
encoded_string=dispatch_xml_encode((char*)data,block_len);
r_fdprintf(pwsc->fd,"%s",encoded_string);
free(encoded_string);
}
} else {
/* we'll need to stack this up and try and remember where we
* came from. Make it an extra 8 so that it gets fixed to
* the *right* amount when the stacks are juggled below
*/
poi->stack[poi->stack_height].bytes_left=block_len + 8;
memcpy(poi->stack[poi->stack_height].tag,block_tag,5);
poi->stack_height++;
if(poi->stack_height == 10) {
DPRINTF(E_FATAL,L_DAAP,"Stack overflow\n");
}
block_done=0;
}
break;
default:
DPRINTF(E_FATAL,L_DAAP,"Bad dmap type: %d, %s\n",
pitem->type, pitem->description);
break;
}
if(block_done) {
r_fdprintf(pwsc->fd,"</%s>",pitem->description);
if(poi->readable)
r_fdprintf(pwsc->fd,"\n");
block_len += 8;
} else {
/* must be a container */
block_len = 8;
if(poi->readable)
r_fdprintf(pwsc->fd,"\n");
}
current += block_len;
if(poi->stack_height) {
stack_ptr=poi->stack_height;
while(stack_ptr--) {
poi->stack[stack_ptr].bytes_left -= block_len;
if(poi->stack[stack_ptr].bytes_left < 0) {
DPRINTF(E_FATAL,L_DAAP,"negative container\n");
}
if(!poi->stack[stack_ptr].bytes_left) {
poi->stack_height--;
pitem=dispatch_xml_lookup_tag(poi->stack[stack_ptr].tag);
if(poi->readable)
r_fdprintf(pwsc->fd,"%*s",poi->stack_height,"");
r_fdprintf(pwsc->fd,"</%s>",pitem->description);
if(poi->readable)
r_fdprintf(pwsc->fd,"\n");
}
}
}
2005-03-16 11:38:14 +00:00
}
return 0;
}
/**
* finish streaming output to the client, freeing any allocated
* memory, and cleaning up
*
* \param pwsc current conninfo struct
* \param pqi current dbquery struct
*/
int dispatch_output_end(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
OUTPUT_INFO *poi = pqi->output_info;
if((poi) && (poi->xml_output) && (poi->stack_height)) {
2005-11-03 05:02:17 +00:00
DPRINTF(E_LOG,L_DAAP,"Badly formed xml -- still stack\n");
2005-03-16 11:38:14 +00:00
}
config_set_status(pwsc,pqi->session_id,NULL);
free(poi);
free(pqi);
return 0;
}
DAAP_ITEMS *dispatch_xml_lookup_tag(char *tag) {
DAAP_ITEMS *pitem;
pitem=taglist;
while((pitem->tag) && (strncmp(tag,pitem->tag,4))) {
2005-11-03 05:02:17 +00:00
pitem++;
2005-03-16 11:38:14 +00:00
}
if(!pitem->tag)
2005-11-03 05:02:17 +00:00
DPRINTF(E_FATAL,L_DAAP,"Unknown daap tag: %c%c%c%c\n",tag[0],tag[1],tag[2],tag[3]);
2005-03-16 11:38:14 +00:00
return pitem;
}
/**
* xml entity encoding, stupid style
*/
char *dispatch_xml_encode(char *original, int len) {
char *new;
char *s, *d;
int destsize;
int truelen;
/* this is about stupid */
if(len) {
2005-11-03 05:02:17 +00:00
truelen=len;
2005-03-16 11:38:14 +00:00
} else {
2005-11-03 05:02:17 +00:00
truelen=strlen(original);
2005-03-16 11:38:14 +00:00
}
2005-03-16 11:38:14 +00:00
destsize = 6*truelen+1;
new=(char *)malloc(destsize);
if(!new) return NULL;
memset(new,0x00,destsize);
s=original;
d=new;
while(s < (original+truelen)) {
2005-11-03 05:02:17 +00:00
switch(*s) {
case '>':
strcat(d,"&gt;");
d += 4;
s++;
break;
case '<':
strcat(d,"&lt;");
d += 4;
s++;
break;
case '"':
strcat(d,"&quot;");
d += 6;
s++;
break;
case '\'':
strcat(d,"&apos;");
d += 6;
s++;
break;
case '&':
strcat(d,"&amp;");
d += 5;
s++;
break;
default:
*d++ = *s++;
}
2005-03-16 11:38:14 +00:00
}
return new;
}
2005-03-11 06:39:40 +00:00
void dispatch_stream(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
MP3FILE *pmp3;
FILE *file_ptr;
int file_fd;
int bytes_copied;
off_t real_len;
off_t file_len;
off_t offset=0;
long img_size;
struct stat sb;
int img_fd;
int item;
/* stream out the song */
pwsc->close=1;
item=atoi(pqi->uri_sections[3]);
if(ws_getrequestheader(pwsc,"range")) {
2005-11-03 05:02:17 +00:00
offset=(off_t)atol(ws_getrequestheader(pwsc,"range") + 6);
2005-03-11 06:39:40 +00:00
}
pmp3=db_fetch_item(item);
if(!pmp3) {
2005-11-03 05:02:17 +00:00
DPRINTF(E_LOG,L_DAAP|L_WS|L_DB,"Could not find requested item %lu\n",item);
ws_returnerror(pwsc,404,"File Not Found");
2005-11-11 23:52:42 +00:00
} else if (server_side_convert(pmp3->codectype)) {
2005-11-03 05:02:17 +00:00
/************************
* Server side conversion
************************/
DPRINTF(E_WARN,L_WS,"Thread %d: Autoconvert file %s for client\n",
pwsc->threadno,pmp3->path);
file_ptr = server_side_convert_open(pmp3->path,
offset,
pmp3->song_length);
if (file_ptr) {
file_fd = fileno(file_ptr);
} else {
file_fd = -1;
}
if(file_fd == -1) {
if (file_ptr) {
server_side_convert_close(file_ptr);
}
pwsc->error=errno;
DPRINTF(E_WARN,L_WS,
"Thread %d: Error opening %s for conversion\n",
pwsc->threadno,pmp3->path);
ws_returnerror(pwsc,404,"Not found");
config_set_status(pwsc,pqi->session_id,NULL);
db_dispose_item(pmp3);
} else {
// The type should really be determined by the transcoding
// function -- it's possible that you want to transcode
// to a lower-bitrate mp3 or something... but for now,
// we'll just assume .wav
ws_addresponseheader(pwsc,"Content-Type","audio/wav");
/*
if(pmp3->type)
ws_addresponseheader(pwsc,"Content-Type","audio/%s",
pmp3->type);
*/
// Also content-length -heade would be nice, but since
// we don't really know it here, so let's leave it out.
ws_addresponseheader(pwsc,"Connection","Close");
if(!offset)
ws_writefd(pwsc,"HTTP/1.1 200 OK\r\n");
else {
// This is actually against the protocol, since
// range MUST be explicit according to HTTP-standard
// Seems to work at least with iTunes.
ws_addresponseheader(pwsc,
"Content-Range","bytes %ld-*/*",
(long)offset);
ws_writefd(pwsc,"HTTP/1.1 206 Partial Content\r\n");
}
ws_emitheaders(pwsc);
config_set_status(pwsc,pqi->session_id,
"Streaming file via convert filter '%s'",
pmp3->fname);
DPRINTF(E_LOG,L_WS,
"Session %d: Streaming file '%s' to %s (offset %ld)\n",
pqi->session_id,pmp3->fname, pwsc->hostname,(long)offset);
if(!offset)
config.stats.songs_served++; /* FIXME: remove stat races */
if((bytes_copied=copyfile(file_fd,pwsc->fd)) == -1) {
DPRINTF(E_INF,L_WS,
"Error copying converted file to remote... %s\n",
strerror(errno));
} else {
DPRINTF(E_INF,L_WS,
"Finished streaming converted file to remote\n");
}
server_side_convert_close(file_ptr);
config_set_status(pwsc,pqi->session_id,NULL);
db_dispose_item(pmp3);
}
2005-03-11 06:39:40 +00:00
} else {
2005-11-03 05:02:17 +00:00
/**********************
* stream file normally
**********************/
file_fd=r_open2(pmp3->path,O_RDONLY);
if(file_fd == -1) {
pwsc->error=errno;
DPRINTF(E_WARN,L_WS,"Thread %d: Error opening %s: %s\n",
pwsc->threadno,pmp3->path,strerror(errno));
ws_returnerror(pwsc,404,"Not found");
config_set_status(pwsc,pqi->session_id,NULL);
db_dispose_item(pmp3);
} else {
real_len=lseek(file_fd,0,SEEK_END);
lseek(file_fd,0,SEEK_SET);
/* Re-adjust content length for cover art */
if((config.artfilename) &&
((img_fd=da_get_image_fd(pmp3->path)) != -1)) {
fstat(img_fd, &sb);
img_size = sb.st_size;
r_close(img_fd);
if (strncasecmp(pmp3->type,"mp3",4) ==0) {
2005-03-11 06:39:40 +00:00
/*PENDING*/
2005-11-03 05:02:17 +00:00
} else if (strncasecmp(pmp3->type, "m4a", 4) == 0) {
2005-03-11 06:39:40 +00:00
real_len += img_size + 24;
2005-11-03 05:02:17 +00:00
2005-03-11 06:39:40 +00:00
if (offset > img_size + 24) {
2005-11-03 05:02:17 +00:00
offset -= img_size + 24;
2005-03-11 06:39:40 +00:00
}
2005-11-03 05:02:17 +00:00
}
}
file_len = real_len - offset;
DPRINTF(E_DBG,L_WS,"Thread %d: Length of file (remaining) is %ld\n",
pwsc->threadno,(long)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);
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,pqi->session_id,"Streaming file '%s'",pmp3->fname);
DPRINTF(E_LOG,L_WS,"Session %d: Streaming file '%s' to %s (offset %d)\n",
pqi->session_id,pmp3->fname, pwsc->hostname,(long)offset);
if(!offset)
config.stats.songs_served++; /* FIXME: remove stat races */
if((config.artfilename) &&
(!offset) &&
((img_fd=da_get_image_fd(pmp3->path)) != -1)) {
if (strncasecmp(pmp3->type,"mp3",4) ==0) {
DPRINTF(E_INF,L_WS|L_ART,"Dynamic add artwork to %s (fd %d)\n",
pmp3->fname, img_fd);
da_attach_image(img_fd, pwsc->fd, file_fd, offset);
} else if (strncasecmp(pmp3->type, "m4a", 4) == 0) {
DPRINTF(E_INF,L_WS|L_ART,"Dynamic add artwork to %s (fd %d)\n",
pmp3->fname, img_fd);
da_aac_attach_image(img_fd, pwsc->fd, file_fd, offset);
}
} else if(offset) {
DPRINTF(E_INF,L_WS,"Seeking to offset %ld\n",(long)offset);
lseek(file_fd,offset,SEEK_SET);
}
if((bytes_copied=copyfile(file_fd,pwsc->fd)) == -1) {
DPRINTF(E_INF,L_WS,"Error copying file to remote... %s\n",
strerror(errno));
} else {
DPRINTF(E_INF,L_WS,"Finished streaming file to remote: %d bytes\n",
bytes_copied);
}
config_set_status(pwsc,pqi->session_id,NULL);
r_close(file_fd);
db_dispose_item(pmp3);
}
2005-03-11 06:39:40 +00:00
}
free(pqi);
}
/**
* add songs to an existing playlist
*/
void dispatch_addplaylistitems(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
2005-05-30 03:41:31 +00:00
unsigned char playlist_response[20];
unsigned char *current;
char *tempstring;
char *token;
if(!ws_getvar(pwsc,"dmap.itemid")) {
2005-11-03 05:02:17 +00:00
DPRINTF(E_LOG,L_DAAP,"attempt to add playlist items with no dmap.itemid\n");
ws_returnerror(pwsc,500,"no itemid specified");
return;
}
tempstring=strdup(ws_getvar(pwsc,"dmap.itemid"));
2005-05-30 03:41:31 +00:00
current=(unsigned char*)tempstring;
2005-05-30 03:41:31 +00:00
while((token=strsep((char**)&current,","))) {
2005-11-03 05:02:17 +00:00
if(token) {
db_add_playlist_item(pqi->playlist_id,atoi(token));
}
}
free(tempstring);
/* success(ish)... spool out a dmap block */
current = playlist_response;
current += db_dmap_add_container(current,"MAPI",12);
current += db_dmap_add_int(current,"mstt",200); /* 12 */
dispatch_output_start(pwsc,pqi,20);
dispatch_output_write(pwsc,pqi,playlist_response,20);
dispatch_output_end(pwsc,pqi);
pwsc->close=1;
return;
}
/**
* delete a playlist
*/
void dispatch_deleteplaylist(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
2005-05-30 03:41:31 +00:00
unsigned char playlist_response[20];
unsigned char *current;
if(!ws_getvar(pwsc,"dmap.itemid")) {
2005-11-03 05:02:17 +00:00
DPRINTF(E_LOG,L_DAAP,"attempt to delete playlist with no dmap.itemid\n");
ws_returnerror(pwsc,500,"no itemid specified");
return;
}
db_delete_playlist(atoi(ws_getvar(pwsc,"dmap.itemid")));
/* success(ish)... spool out a dmap block */
current = playlist_response;
current += db_dmap_add_container(current,"MDPR",12);
current += db_dmap_add_int(current,"mstt",200); /* 12 */
dispatch_output_start(pwsc,pqi,20);
dispatch_output_write(pwsc,pqi,playlist_response,20);
dispatch_output_end(pwsc,pqi);
pwsc->close=1;
return;
}
/**
* delete a playlist item
*/
void dispatch_deleteplaylistitems(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
2005-05-30 03:41:31 +00:00
unsigned char playlist_response[20];
unsigned char *current;
char *tempstring;
char *token;
if(!ws_getvar(pwsc,"dmap.itemid")) {
2005-11-03 05:02:17 +00:00
DPRINTF(E_LOG,L_DAAP,"attempt to delete playlist items with no dmap.itemid\n");
ws_returnerror(pwsc,500,"no itemid specified");
return;
}
tempstring=strdup(ws_getvar(pwsc,"dmap.itemid"));
2005-05-30 03:41:31 +00:00
current=(unsigned char *)tempstring;
2005-05-30 03:41:31 +00:00
while((token=strsep((char**)&current,","))) {
2005-11-03 05:02:17 +00:00
if(token) {
db_delete_playlist_item(pqi->playlist_id,atoi(token));
}
}
free(tempstring);
/* success(ish)... spool out a dmap block */
current = playlist_response;
current += db_dmap_add_container(current,"MDPI",12);
current += db_dmap_add_int(current,"mstt",200); /* 12 */
dispatch_output_start(pwsc,pqi,20);
dispatch_output_write(pwsc,pqi,playlist_response,20);
dispatch_output_end(pwsc,pqi);
pwsc->close=1;
return;
}
/**
* add a playlist
*/
void dispatch_addplaylist(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
2005-05-30 03:41:31 +00:00
unsigned char playlist_response[32];
unsigned char *current=playlist_response;
char *name, *query;
int type;
int retval, playlistid;
if((!ws_getvar(pwsc,"org.mt-daapd.playlist-type")) ||
(!ws_getvar(pwsc,"dmap.itemname"))) {
2005-11-03 05:02:17 +00:00
DPRINTF(E_LOG,L_DAAP,"attempt to add playlist with invalid type\n");
ws_returnerror(pwsc,500,"bad playlist info specified");
return;
}
type=atoi(ws_getvar(pwsc,"org.mt-daapd.playlist-type"));
name=ws_getvar(pwsc,"dmap.itemname");
query=ws_getvar(pwsc,"org.mt-daapd.smart-playlist-spec");
2005-05-21 05:53:11 +00:00
retval=db_add_playlist(name,type,query,NULL,0,&playlistid);
if(retval != DB_E_SUCCESS) {
2005-11-03 05:02:17 +00:00
DPRINTF(E_LOG,L_DAAP,"error adding playlist. aborting\n");
ws_returnerror(pwsc,500,"error adding playlist");
return;
}
/* success... spool out a dmap block */
current += db_dmap_add_container(current,"MAPR",24);
current += db_dmap_add_int(current,"mstt",200); /* 12 */
current += db_dmap_add_int(current,"miid",playlistid); /* 12 */
dispatch_output_start(pwsc,pqi,32);
dispatch_output_write(pwsc,pqi,playlist_response,32);
dispatch_output_end(pwsc,pqi);
pwsc->close=1;
return;
}
2005-11-03 05:02:17 +00:00
/**
* edit an existing playlist (by id)
*/
void dispatch_editplaylist(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
unsigned char edit_response[20];
unsigned char *current = edit_response;
char *name, *query;
int id;
int retval;
if((!ws_getvar(pwsc,"dmap.itemname")) ||
(!ws_getvar(pwsc,"dmap.itemid"))) {
DPRINTF(E_LOG,L_DAAP,"Missing name on playlist edit");
ws_returnerror(pwsc,500,"missing playlist name");
return;
}
name=ws_getvar(pwsc,"dmap.itemname");
query=ws_getvar(pwsc,"org.mt-daapd.smart-playlist-spec");
id=atoi(ws_getvar(pwsc,"dmap.itemid"));
retval=db_edit_playlist(id,name,query);
if(retval != DB_E_SUCCESS) {
DPRINTF(E_LOG,L_DAAP,"error editing playlist.");
ws_returnerror(pwsc,500,"Error editing playlist");
return;
}
current += db_dmap_add_container(current,"MEPR",12);
current += db_dmap_add_int(current,"mstt",200); /* 12 */
dispatch_output_start(pwsc,pqi,20);
dispatch_output_write(pwsc,pqi,edit_response,20);
dispatch_output_end(pwsc,pqi);
pwsc->close=1;
return;
}
/**
* enumerate and return playlistitems
*/
2005-03-11 06:39:40 +00:00
void dispatch_playlistitems(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
2005-05-30 03:41:31 +00:00
unsigned char items_response[61];
unsigned char *current=items_response;
2005-03-11 06:39:40 +00:00
int song_count;
int list_length;
unsigned char *block;
if(ws_getvar(pwsc,"meta")) {
2005-11-03 05:02:17 +00:00
pqi->meta = db_encode_meta(ws_getvar(pwsc,"meta"));
2005-03-11 06:39:40 +00:00
} else {
2005-11-03 05:02:17 +00:00
pqi->meta = ((1ll << metaItemId) |
(1ll << metaItemName) |
(1ll << metaItemKind) |
(1ll << metaContainerItemId) |
(1ll << metaParentContainerId));
2005-03-11 06:39:40 +00:00
}
pqi->query_type = queryTypePlaylistItems;
pqi->index_type=indexTypeNone;
if(db_enum_start(pqi)) {
2005-11-03 05:02:17 +00:00
DPRINTF(E_LOG,L_DAAP,"Could not start enum\n");
ws_returnerror(pwsc,500,"Internal server error: out of memory!");
return;
2005-03-11 06:39:40 +00:00
}
list_length=db_enum_size(pqi,&song_count);
DPRINTF(E_DBG,L_DAAP,"Item enum: got %d songs, dmap size: %d\n",song_count,list_length);
current += db_dmap_add_container(current,"apso",list_length + 53);
current += db_dmap_add_int(current,"mstt",200); /* 12 */
current += db_dmap_add_char(current,"muty",0); /* 9 */
current += db_dmap_add_int(current,"mtco",song_count); /* 12 */
current += db_dmap_add_int(current,"mrco",song_count); /* 12 */
current += db_dmap_add_container(current,"mlcl",list_length);
2005-03-16 11:38:14 +00:00
dispatch_output_start(pwsc,pqi,61+list_length);
dispatch_output_write(pwsc,pqi,items_response,61);
2005-03-11 06:39:40 +00:00
while((list_length=db_enum_fetch(pqi,&block)) > 0) {
2005-11-03 05:02:17 +00:00
DPRINTF(E_SPAM,L_DAAP,"Got block of size %d\n",list_length);
dispatch_output_write(pwsc,pqi,block,list_length);
free(block);
2005-03-11 06:39:40 +00:00
}
DPRINTF(E_DBG,L_DAAP,"Done enumerating.\n");
db_enum_end();
2005-03-16 11:38:14 +00:00
dispatch_output_end(pwsc,pqi);
2005-03-11 06:39:40 +00:00
return;
}
void dispatch_browse(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
2005-05-30 03:41:31 +00:00
unsigned char browse_response[52];
unsigned char *current=browse_response;
2005-03-13 23:20:25 +00:00
int item_count;
int list_length;
unsigned char *block;
char *response_type;
if(!strcmp(pqi->uri_sections[3],"artists")) {
2005-11-03 05:02:17 +00:00
response_type = "abar";
pqi->query_type=queryTypeBrowseArtists;
2005-03-13 23:20:25 +00:00
} else if(!strcmp(pqi->uri_sections[3],"genres")) {
2005-11-03 05:02:17 +00:00
response_type = "abgn";
pqi->query_type=queryTypeBrowseGenres;
2005-03-13 23:20:25 +00:00
} else if(!strcmp(pqi->uri_sections[3],"albums")) {
2005-11-03 05:02:17 +00:00
response_type = "abal";
pqi->query_type=queryTypeBrowseAlbums;
2005-03-13 23:20:25 +00:00
} else if(!strcmp(pqi->uri_sections[3],"composers")) {
2005-11-03 05:02:17 +00:00
response_type = "abcp";
pqi->query_type=queryTypeBrowseComposers;
2005-03-13 23:20:25 +00:00
} else {
2005-11-03 05:02:17 +00:00
DPRINTF(E_WARN,L_DAAP|L_BROW,"Invalid browse request type %s\n",pqi->uri_sections[3]);
ws_returnerror(pwsc,404,"Invalid browse type");
config_set_status(pwsc,pqi->session_id,NULL);
free(pqi);
return;
2005-03-13 23:20:25 +00:00
}
pqi->index_type = indexTypeNone;
if(db_enum_start(pqi)) {
2005-11-03 05:02:17 +00:00
DPRINTF(E_LOG,L_DAAP|L_BROW,"Could not start enum\n");
ws_returnerror(pwsc,500,"Internal server error: out of memory!\n");
return;
2005-03-13 23:20:25 +00:00
}
DPRINTF(E_DBG,L_DAAP|L_BROW,"Getting enum size.\n");
list_length=db_enum_size(pqi,&item_count);
DPRINTF(E_DBG,L_DAAP|L_BROW,"Item enum: got %d items, dmap size: %d\n",
2005-11-03 05:02:17 +00:00
item_count,list_length);
2005-03-13 23:20:25 +00:00
current += db_dmap_add_container(current,"abro",list_length + 44);
current += db_dmap_add_int(current,"mstt",200); /* 12 */
current += db_dmap_add_int(current,"mtco",item_count); /* 12 */
current += db_dmap_add_int(current,"mrco",item_count); /* 12 */
current += db_dmap_add_container(current,response_type,list_length); /* 8 + length */
2005-03-16 11:38:14 +00:00
dispatch_output_start(pwsc,pqi,52+list_length);
dispatch_output_write(pwsc,pqi,browse_response,52);
2005-03-13 23:20:25 +00:00
while((list_length=db_enum_fetch(pqi,&block)) > 0) {
2005-11-03 05:02:17 +00:00
DPRINTF(E_SPAM,L_DAAP|L_BROW,"Got block of size %d\n",list_length);
dispatch_output_write(pwsc,pqi,block,list_length);
free(block);
2005-03-13 23:20:25 +00:00
}
DPRINTF(E_DBG,L_DAAP|L_BROW,"Done enumerating\n");
db_enum_end();
2005-03-16 11:38:14 +00:00
dispatch_output_end(pwsc,pqi);
2005-03-11 06:39:40 +00:00
return;
}
void dispatch_playlists(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
2005-05-30 03:41:31 +00:00
unsigned char playlist_response[61];
unsigned char *current=playlist_response;
2005-03-11 06:39:40 +00:00
int pl_count;
int list_length;
unsigned char *block;
/* currently, this is ignored for playlist queries */
if(ws_getvar(pwsc,"meta")) {
2005-11-03 05:02:17 +00:00
pqi->meta = db_encode_meta(ws_getvar(pwsc,"meta"));
2005-03-11 06:39:40 +00:00
} else {
2005-11-03 05:02:17 +00:00
pqi->meta = ((1ll << metaItemId) |
(1ll << metaItemName) |
(1ll << metaPersistentId) |
(1ll << metaItunesSmartPlaylist));
2005-03-11 06:39:40 +00:00
}
pqi->query_type = queryTypePlaylists;
2005-03-13 23:20:25 +00:00
pqi->index_type = indexTypeNone;
2005-03-11 06:39:40 +00:00
if(db_enum_start(pqi)) {
2005-11-03 05:02:17 +00:00
DPRINTF(E_LOG,L_DAAP,"Could not start enum\n");
ws_returnerror(pwsc,500,"Internal server error: out of memory!\n");
return;
2005-03-11 06:39:40 +00:00
}
list_length=db_enum_size(pqi,&pl_count);
DPRINTF(E_DBG,L_DAAP,"Item enum: got %d playlists, dmap size: %d\n",pl_count,list_length);
current += db_dmap_add_container(current,"aply",list_length + 53);
current += db_dmap_add_int(current,"mstt",200); /* 12 */
current += db_dmap_add_char(current,"muty",0); /* 9 */
current += db_dmap_add_int(current,"mtco",pl_count); /* 12 */
current += db_dmap_add_int(current,"mrco",pl_count); /* 12 */
current += db_dmap_add_container(current,"mlcl",list_length);
2005-03-16 11:38:14 +00:00
dispatch_output_start(pwsc,pqi,61+list_length);
dispatch_output_write(pwsc,pqi,playlist_response,61);
2005-03-11 06:39:40 +00:00
while((list_length=db_enum_fetch(pqi,&block)) > 0) {
2005-11-03 05:02:17 +00:00
DPRINTF(E_SPAM,L_DAAP,"Got block of size %d\n",list_length);
dispatch_output_write(pwsc,pqi,block,list_length);
free(block);
2005-03-11 06:39:40 +00:00
}
DPRINTF(E_DBG,L_DAAP,"Done enumerating.\n");
db_enum_end();
2005-03-16 11:38:14 +00:00
dispatch_output_end(pwsc,pqi);
2005-03-11 06:39:40 +00:00
return;
}
void dispatch_items(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
2005-05-30 03:41:31 +00:00
unsigned char items_response[61];
unsigned char *current=items_response;
2005-03-11 06:39:40 +00:00
int song_count;
int list_length;
unsigned char *block;
if(ws_getvar(pwsc,"meta")) {
2005-11-03 05:02:17 +00:00
pqi->meta = db_encode_meta(ws_getvar(pwsc,"meta"));
2005-03-11 06:39:40 +00:00
} else {
2005-11-03 05:02:17 +00:00
pqi->meta = (MetaField_t) -1ll;
2005-03-11 06:39:40 +00:00
}
pqi->query_type = queryTypeItems;
pqi->index_type=indexTypeNone;
if(db_enum_start(pqi)) {
2005-11-03 05:02:17 +00:00
DPRINTF(E_LOG,L_DAAP,"Could not start enum\n");
ws_returnerror(pwsc,500,"Internal server error: out of memory!");
return;
2005-03-11 06:39:40 +00:00
}
list_length=db_enum_size(pqi,&song_count);
DPRINTF(E_DBG,L_DAAP,"Item enum: got %d songs, dmap size: %d\n",song_count,list_length);
current += db_dmap_add_container(current,"adbs",list_length + 53);
current += db_dmap_add_int(current,"mstt",200); /* 12 */
current += db_dmap_add_char(current,"muty",0); /* 9 */
current += db_dmap_add_int(current,"mtco",song_count); /* 12 */
current += db_dmap_add_int(current,"mrco",song_count); /* 12 */
current += db_dmap_add_container(current,"mlcl",list_length);
2005-03-16 11:38:14 +00:00
dispatch_output_start(pwsc,pqi,61+list_length);
dispatch_output_write(pwsc,pqi,items_response,61);
2005-03-11 06:39:40 +00:00
while((list_length=db_enum_fetch(pqi,&block)) > 0) {
2005-11-03 05:02:17 +00:00
DPRINTF(E_SPAM,L_DAAP,"Got block of size %d\n",list_length);
dispatch_output_write(pwsc,pqi,block,list_length);
free(block);
2005-03-11 06:39:40 +00:00
}
DPRINTF(E_DBG,L_DAAP,"Done enumerating.\n");
db_enum_end();
2005-03-16 11:38:14 +00:00
dispatch_output_end(pwsc,pqi);
2005-03-11 06:39:40 +00:00
return;
}
void dispatch_update(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
2005-05-30 03:41:31 +00:00
unsigned char update_response[32];
unsigned char *current=update_response;
2005-03-11 06:39:40 +00:00
int clientver=1;
fd_set rset;
struct timeval tv;
int result;
DPRINTF(E_DBG,L_DAAP,"Preparing to send update response\n");
if(ws_getvar(pwsc,"revision-number")) {
2005-11-03 05:02:17 +00:00
clientver=atoi(ws_getvar(pwsc,"revision-number"));
2005-03-11 06:39:40 +00:00
}
while(clientver == db_revision()) {
2005-11-03 05:02:17 +00:00
FD_ZERO(&rset);
FD_SET(pwsc->fd,&rset);
tv.tv_sec=30;
tv.tv_usec=0;
result=select(pwsc->fd+1,&rset,NULL,NULL,&tv);
if(FD_ISSET(pwsc->fd,&rset)) {
/* can't be ready for read, must be error */
DPRINTF(E_DBG,L_DAAP,"Socket closed?\n");
return;
}
2005-03-11 06:39:40 +00:00
}
/* otherwise, send the info about this version */
current += db_dmap_add_container(current,"mupd",24);
current += db_dmap_add_int(current,"mstt",200); /* 12 */
current += db_dmap_add_int(current,"musr",db_revision()); /* 12 */
2005-03-16 11:38:14 +00:00
dispatch_output_start(pwsc,pqi,32);
dispatch_output_write(pwsc,pqi,update_response,32);
dispatch_output_end(pwsc,pqi);
2005-03-11 06:39:40 +00:00
2005-03-16 11:38:14 +00:00
return;
2005-03-11 06:39:40 +00:00
}
void dispatch_dbinfo(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
2005-05-30 03:41:31 +00:00
unsigned char dbinfo_response[255]; /* FIXME */
unsigned char *current = dbinfo_response;
2005-03-11 06:39:40 +00:00
int namelen;
namelen=strlen(config.servername);
current += db_dmap_add_container(current,"avdb",105 + namelen);
current += db_dmap_add_int(current,"mstt",200); /* 12 */
current += db_dmap_add_char(current,"muty",0); /* 9 */
current += db_dmap_add_int(current,"mtco",1); /* 12 */
current += db_dmap_add_int(current,"mrco",1); /* 12 */
current += db_dmap_add_container(current,"mlcl",52 + namelen);
current += db_dmap_add_container(current,"mlit",44 + namelen);
current += db_dmap_add_int(current,"miid",1); /* 12 */
current += db_dmap_add_string(current,"minm",config.servername); /* 8 + namelen */
current += db_dmap_add_int(current,"mimc",db_get_song_count()); /* 12 */
current += db_dmap_add_int(current,"mctc",db_get_playlist_count()); /* 12 */
2005-03-16 11:38:14 +00:00
dispatch_output_start(pwsc,pqi,113+namelen);
dispatch_output_write(pwsc,pqi,dbinfo_response,113+namelen);
dispatch_output_end(pwsc,pqi);
2005-03-11 06:39:40 +00:00
return;
}
void dispatch_logout(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
config_set_status(pwsc,pqi->session_id,NULL);
free(pqi);
ws_returnerror(pwsc,204,"Logout Successful");
}
void dispatch_login(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
2005-05-30 03:41:31 +00:00
unsigned char login_response[32];
unsigned char *current = login_response;
2005-03-11 06:39:40 +00:00
int session;
session = config_get_next_session();
current += db_dmap_add_container(current,"mlog",24);
current += db_dmap_add_int(current,"mstt",200); /* 12 */
current += db_dmap_add_int(current,"mlid",session); /* 12 */
2005-03-16 11:38:14 +00:00
dispatch_output_start(pwsc,pqi,32);
dispatch_output_write(pwsc,pqi,login_response,32);
dispatch_output_end(pwsc,pqi);
2005-03-11 06:39:40 +00:00
return;
}
void dispatch_content_codes(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
2005-05-30 03:41:31 +00:00
unsigned char content_codes[20];
unsigned char *current=content_codes;
unsigned char mdcl[256]; /* FIXME: Don't make this static */
2005-03-11 06:39:40 +00:00
int len;
DAAP_ITEMS *dicurrent;
dicurrent=taglist;
len=0;
while(dicurrent->type) {
2005-11-03 05:02:17 +00:00
len += (8 + 12 + 10 + 8 + strlen(dicurrent->description));
dicurrent++;
2005-03-11 06:39:40 +00:00
}
current += db_dmap_add_container(current,"mccr",len + 12);
current += db_dmap_add_int(current,"mstt",200);
2005-03-16 11:38:14 +00:00
dispatch_output_start(pwsc,pqi,len+20);
dispatch_output_write(pwsc,pqi,content_codes,20);
2005-03-11 06:39:40 +00:00
dicurrent=taglist;
while(dicurrent->type) {
2005-11-03 05:02:17 +00:00
current=mdcl;
len = 12 + 10 + 8 + strlen(dicurrent->description);
current += db_dmap_add_container(current,"mdcl",len);
current += db_dmap_add_string(current,"mcnm",dicurrent->tag); /* 12 */
current += db_dmap_add_string(current,"mcna",dicurrent->description); /* 8 + descr */
current += db_dmap_add_short(current,"mcty",dicurrent->type); /* 10 */
dispatch_output_write(pwsc,pqi,mdcl,len+8);
dicurrent++;
2005-03-11 06:39:40 +00:00
}
2005-03-16 11:38:14 +00:00
dispatch_output_end(pwsc,pqi);
return;
2005-03-11 06:39:40 +00:00
}
void dispatch_server_info(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
2005-05-30 03:41:31 +00:00
unsigned char server_info[256]; /* FIXME: Don't make this static */
unsigned char *current = server_info;
2005-03-11 06:39:40 +00:00
char *client_version;
int mpro = 2 << 16;
int apro = 3 << 16;
int actual_length=130 + strlen(config.servername);
if(actual_length > sizeof(server_info)) {
2005-11-03 05:02:17 +00:00
DPRINTF(E_FATAL,L_DAAP,"Server name too long.\n");
2005-03-11 06:39:40 +00:00
}
client_version=ws_getrequestheader(pwsc,"Client-DAAP-Version");
current += db_dmap_add_container(current,"msrv",actual_length - 8);
current += db_dmap_add_int(current,"mstt",200); /* 12 */
if((client_version) && (!strcmp(client_version,"1.0"))) {
2005-11-03 05:02:17 +00:00
mpro = 1 << 16;
apro = 1 << 16;
2005-03-11 06:39:40 +00:00
}
if((client_version) && (!strcmp(client_version,"2.0"))) {
2005-11-03 05:02:17 +00:00
mpro = 1 << 16;
apro = 2 << 16;
2005-03-11 06:39:40 +00:00
}
current += db_dmap_add_int(current,"mpro",mpro); /* 12 */
current += db_dmap_add_int(current,"apro",apro); /* 12 */
current += db_dmap_add_int(current,"mstm",1800); /* 12 */
current += db_dmap_add_string(current,"minm",config.servername); /* 8 + strlen(name) */
current += db_dmap_add_char(current,"msau", /* 9 */
2005-11-03 05:02:17 +00:00
config.readpassword != NULL ? 2 : 0);
2005-03-11 06:39:40 +00:00
current += db_dmap_add_char(current,"msex",0); /* 9 */
current += db_dmap_add_char(current,"msix",0); /* 9 */
current += db_dmap_add_char(current,"msbr",0); /* 9 */
current += db_dmap_add_char(current,"msqy",0); /* 9 */
current += db_dmap_add_char(current,"msup",0); /* 9 */
current += db_dmap_add_int(current,"msdc",1); /* 12 */
2005-03-16 11:38:14 +00:00
dispatch_output_start(pwsc,pqi,actual_length);
dispatch_output_write(pwsc,pqi,server_info,actual_length);
dispatch_output_end(pwsc,pqi);
2005-03-11 06:39:40 +00:00
2005-03-16 11:38:14 +00:00
return;
2005-03-11 06:39:40 +00:00
}