/** * This handles requests that are daap-related. For example, * /server-info, /login, etc. This should really be split up * into multiple functions, and perhaps moved into daap.c * * \param pwsc Webserver connection info, passed from the webserver * * \todo Decomplexify this! */ void daap_handler(WS_CONNINFO *pwsc) { int close; DAAP_BLOCK *root; int clientrev; /* for the /databases URI */ char *uri; unsigned long int db_index; unsigned long int playlist_index; unsigned long int item=0; char *first, *last; char* index = 0; int streaming=0; int compress =0; int start_time; int end_time; int bytes_written; MP3FILE *pmp3; int file_fd; int session_id=0; int img_fd; struct stat sb; long img_size; off_t offset=0; off_t real_len; off_t file_len; int bytes_copied=0; GZIP_STREAM *gz; close=pwsc->close; pwsc->close=1; /* in case we have any errors */ root=NULL; ws_addresponseheader(pwsc,"Accept-Ranges","bytes"); ws_addresponseheader(pwsc,"DAAP-Server","mt-daapd/%s",VERSION); ws_addresponseheader(pwsc,"Content-Type","application/x-dmap-tagged"); if(ws_getvar(pwsc,"session-id")) { session_id=atoi(ws_getvar(pwsc,"session-id")); } if(!strcasecmp(pwsc->uri,"/server-info")) { config_set_status(pwsc,session_id,"Sending server info"); root=daap_response_server_info(config.servername, ws_getrequestheader(pwsc,"Client-DAAP-Version")); } else if (!strcasecmp(pwsc->uri,"/content-codes")) { config_set_status(pwsc,session_id,"Sending content codes"); root=daap_response_content_codes(); } else if (!strcasecmp(pwsc->uri,"/login")) { config_set_status(pwsc,session_id,"Logging in"); root=daap_response_login(pwsc->hostname); } else if (!strcasecmp(pwsc->uri,"/update")) { if(!ws_getvar(pwsc,"delta")) { /* first check */ clientrev=db_version() - 1; config_set_status(pwsc,session_id,"Sending database"); } else { clientrev=atoi(ws_getvar(pwsc,"delta")); config_set_status(pwsc,session_id,"Waiting for DB updates"); } root=daap_response_update(pwsc->fd,clientrev); if((ws_getvar(pwsc,"delta")) && (root==NULL)) { DPRINTF(E_LOG,L_WS,"Client %s disconnected\n",pwsc->hostname); config_set_status(pwsc,session_id,NULL); pwsc->close=1; return; } } else if (!strcasecmp(pwsc->uri,"/logout")) { config_set_status(pwsc,session_id,NULL); ws_returnerror(pwsc,204,"Logout Successful"); return; } else if(strcmp(pwsc->uri,"/databases")==0) { config_set_status(pwsc,session_id,"Sending database info"); root=daap_response_dbinfo(config.servername); if(0 != (index = ws_getvar(pwsc, "index"))) daap_handle_index(root, index); } else if(strncmp(pwsc->uri,"/databases/",11) == 0) { /* the /databases/ uri will either be: * * /databases/id/items, which returns items in a db * /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 */ uri = strdup(pwsc->uri); first=(char*)&uri[11]; last=first; while((*last) && (*last != '/')) { last++; } if(*last) { *last='\0'; db_index=atoll(first); last++; if(strncasecmp(last,"items/",6)==0) { /* streaming */ first=last+6; while((*last) && (*last != '.')) last++; if(*last == '.') { *last='\0'; item=atoll(first); streaming=1; DPRINTF(E_DBG,L_DAAP|L_WS,"Streaming request for id %lu\n",item); } free(uri); } else if (strncasecmp(last,"items",5)==0) { /* songlist */ free(uri); // pass the meta field request for processing // pass the query request for processing root=daap_response_songlist(ws_getvar(pwsc,"meta"), ws_getvar(pwsc,"query")); config_set_status(pwsc,session_id,"Sending songlist"); } else if (strncasecmp(last,"containers/",11)==0) { /* playlist elements */ first=last + 11; last=first; while((*last) && (*last != '/')) { last++; } if(*last) { *last='\0'; playlist_index=atoll(first); // pass the meta list info for processing root=daap_response_playlist_items(playlist_index, ws_getvar(pwsc,"meta"), ws_getvar(pwsc,"query")); } free(uri); config_set_status(pwsc,session_id,"Sending playlist info"); } else if (strncasecmp(last,"containers",10)==0) { /* list of playlists */ free(uri); root=daap_response_playlists(config.servername); config_set_status(pwsc,session_id,"Sending playlist info"); } else if (strncasecmp(last,"browse/",7)==0) { config_set_status(pwsc,session_id,"Compiling browse info"); root = daap_response_browse(last + 7, ws_getvar(pwsc, "filter")); config_set_status(pwsc,session_id,"Sending browse info"); free(uri); } } // prune the full list if an index range was specified if(0 != (index = ws_getvar(pwsc, "index"))) daap_handle_index(root, index); } if((!root)&&(!streaming)) { DPRINTF(E_DBG,L_WS|L_DAAP,"Bad request -- root=%x, streaming=%d\n",root,streaming); ws_returnerror(pwsc,400,"Invalid Request"); config_set_status(pwsc,session_id,NULL); return; } pwsc->close=close; if(!streaming) { DPRINTF(E_DBG,L_WS,"Satisfying request\n"); if((config.compress) && ws_testrequestheader(pwsc,"Accept-Encoding","gzip") && root->reported_size >= 1000) { compress=1; } DPRINTF(E_DBG,L_WS|L_DAAP,"Serializing\n"); start_time = time(NULL); if (compress) { DPRINTF(E_DBG,L_WS|L_DAAP,"Using compression: %s\n", pwsc->uri); gz = gzip_alloc(); daap_serialize(root,pwsc->fd,gz); gzip_compress(gz); bytes_written = gz->bytes_out; ws_writefd(pwsc,"HTTP/1.1 200 OK\r\n"); ws_addresponseheader(pwsc,"Content-Length","%d",bytes_written); ws_addresponseheader(pwsc,"Content-Encoding","gzip"); DPRINTF(E_DBG,L_WS,"Emitting headers\n"); ws_emitheaders(pwsc); if (gzip_close(gz,pwsc->fd) != bytes_written) { DPRINTF(E_LOG,L_WS|L_DAAP,"Error compressing data\n"); } DPRINTF(E_DBG,L_WS|L_DAAP,"Compression ratio: %f\n",((double) bytes_written)/(8.0 + root->reported_size)) } else {
/** * do the transcode, emitting the headers, content type, * and shoving the file down the wire * * @param pwsc connection to transcode to * @param file file to transcode * @param codec source codec * @param duration time in ms * @returns bytes transferred, or -1 on error */ int plugin_ssc_transcode(WS_CONNINFO *pwsc, MP3FILE *pmp3, int offset, int headers) { PLUGIN_ENTRY *ppi, *ptc=NULL; PLUGIN_TRANSCODE_FN *pfn = NULL; void *vp_ssc; int post_error = 1; int result = -1; /* first, find the plugin that will do the conversion */ ppi = _plugin_list.next; while((ppi) && (!pfn)) { if(ppi->pinfo->type & PLUGIN_TRANSCODE) { if(strstr(ppi->pinfo->codeclist,pmp3->codectype)) { ptc = ppi; pfn = ppi->pinfo->transcode_fns; } } ppi = ppi->next; } if(pfn) { DPRINTF(E_DBG,L_PLUG,"Transcoding %s with %s\n",pmp3->path, ptc->pinfo->server); vp_ssc = pfn->ssc_init(); if(vp_ssc) { if(pfn->ssc_open(vp_ssc,pmp3)) { /* start reading and throwing */ if(headers) { ws_addresponseheader(pwsc,"Content-Type","audio/wav"); 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-*/*", (long)offset); ws_writefd(pwsc,"HTTP/1.1 206 Partial Content\r\n"); } ws_emitheaders(pwsc); } /* start reading/writing */ result = __plugin_ssc_copy(pwsc,pfn,vp_ssc,offset); post_error = 0; pfn->ssc_close(vp_ssc); } else { DPRINTF(E_LOG,L_PLUG,"Error opening %s for ssc: %s\n", pmp3->path,pfn->ssc_error(vp_ssc)); } pfn->ssc_deinit(vp_ssc); } else { DPRINTF(E_LOG,L_PLUG,"Error initializing transcoder: %s\n", ptc->pinfo->server); } } if(post_error) { pwsc->error = EPERM; /* ?? */ ws_returnerror(pwsc,500,"Internal error"); } return result; }