123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313 |
- #include "main.h"
- #include "api/service/waservicefactory.h"
- #include "bfc/dispatch.h"
- #include "../Components/wac_network/wac_network_web_server_api.h"
- #include "../Components/wac_network/wac_network_onconncb_api.h"
- #include "../Components/wac_network/wac_network_connection_api.h"
- #include "../Components/wac_network/wac_network_http_server_api.h"
- #include "../Components/wac_network/wac_network_page_generator_api.h"
- #include "api__ml_pmp.h"
- #include "DeviceView.h"
- #include <wchar.h>
- #include "metadata_utils.h"
- #include "nu/AutoWide.h"
- #include "nu/AutoChar.h"
- #include <strsafe.h>
- void url_decode(char *in, char *out, int maxlen)
- {
- while (in && *in && maxlen>1)
- {
- if (*in == '+')
- {
- in++;
- *out++=' ';
- }
- else if (*in == '%' && in[1] != '%' && in[1])
- {
- int a=0;
- int b=0;
- for ( b = 0; b < 2; b ++)
- {
- int r=in[1+b];
- if (r>='0'&&r<='9') r-='0';
- else if (r>='a'&&r<='z') r-='a'-10;
- else if (r>='A'&&r<='Z') r-='A'-10;
- else break;
- a*=16;
- a+=r;
- }
- if (b < 2) *out++=*in++;
- else { *out++=a; in += 3;}
- }
- else *out++=*in++;
- maxlen--;
- }
- *out=0;
- }
- static void callbackThunk(void * callBackContext, wchar_t * status) {
- }
- typedef struct {
- int status;
- wchar_t * device; wchar_t * artist; wchar_t * album; wchar_t * title;
- DeviceView * d; songid_t s;
- } FindTrackStruct;
- static VOID CALLBACK APC_FindTrack(ULONG_PTR dwParam) {
- FindTrackStruct * f = (FindTrackStruct *)dwParam;
- for(int i=0; i<devices.GetSize(); i++) {
- f->d = (DeviceView*)devices.Get(i);
- wchar_t buf[2048] = {0}; int len = 2047;
- f->d->dev->getPlaylistName(0,buf,128);
- if(wcscmp(buf,f->device)) continue;
- int l = f->d->dev->getPlaylistLength(0);
- for(int j=0; j<l; j++) {
- f->s = f->d->dev->getPlaylistTrack(0,j);
- f->d->dev->getTrackArtist(f->s,buf,len);
- if(lstrcmpi(buf,f->artist)) continue;
- f->d->dev->getTrackAlbum(f->s,buf,len);
- if(lstrcmpi(buf,f->album)) continue;
- f->d->dev->getTrackTitle(f->s,buf,len);
- if(lstrcmpi(buf,f->title)) continue;
- f->status = 2;
- return;
- }
- }
- f->status = 1;
- }
- bool findTrack(Device **dev, songid_t *song,wchar_t * device,wchar_t * artist,wchar_t * album,wchar_t * title) {
- FindTrackStruct f = {0,device,artist,album,title,NULL,0};
- SynchronousProcedureCall(APC_FindTrack,(ULONG_PTR)&f);
- *dev = f.d->dev;
- *song = f.s;
- return f.status == 2;
- }
- bool copyFileFromDevice(Device * dev, songid_t song,wchar_t * fn) {
- int k=0;
- if(dev->copyToHardDriveSupported()) {
- if(!dev->copyToHardDrive(song,fn,NULL,callbackThunk,&k))
- return true;
- }
- return false;
- }
- class FilePageGenerator : public api_pagegenerator // public IPageGenerator
- {
- public:
- virtual ~FilePageGenerator();
- FilePageGenerator(wchar_t *fn, api_httpserv *serv);
- size_t GetData(char *buf, int size); // return 0 when done
- int is_error() { return !m_fp; }
- private:
- FILE *m_fp;
- int m_file_pos;
- int m_file_len;
- wchar_t *m_fn;
- protected:
- RECVS_DISPATCH;
- };
- #define CBCLASS FilePageGenerator
- START_DISPATCH;
- CB(API_PAGEGENERATOR_GETDATA, GetData);
- END_DISPATCH;
- #undef CBCLASS
- FilePageGenerator::FilePageGenerator(wchar_t *fn, api_httpserv *serv)
- {
- m_fn = _wcsdup(fn);
- m_file_pos=m_file_len=0;
- m_fp=_wfopen(fn,L"rb");
- if (m_fp)
- {
- int resume_end=0;
- int resume_pos=0;
- char *range = serv->getheader("Range");
- if (range)
- {
- if (!_strnicmp(range,"bytes=",6))
- {
- range+=6;
- char *t=range;
- while (t && *t && (*t < '0' || *t > '9')) t++;
- while (t && *t && *t >= '0' && *t <= '9')
- {
- resume_pos*=10;
- resume_pos+=*t-'0';
- t++;
- }
- if (*t != '-') resume_pos=0;
- else if (t[1])
- {
- resume_end=atoi(t+1);
- }
- }
- }
- fseek(m_fp,0,SEEK_END);
- m_file_len=ftell(m_fp);
- char buf[512] = {0};
- StringCchPrintfA(buf, ARRAYSIZE(buf), "Content-Length: %d",m_file_len);
- serv->set_reply_header(buf);
- int m_file_len_orig=m_file_len;
- if (resume_end && resume_end < m_file_len) m_file_len=resume_end;
- if (resume_pos > 0 && resume_pos < m_file_len) m_file_pos = resume_pos;
- fseek(m_fp,m_file_pos,SEEK_SET);
- StringCchPrintfA(buf, ARRAYSIZE(buf), "Content-Range: bytes=%d-%d/%d",resume_pos,resume_end,m_file_len_orig);
- serv->set_reply_header(buf);
- if (resume_pos != 0) serv->set_reply_string("HTTP/1.1 206 Partial Content");
- }
- }
- FilePageGenerator::~FilePageGenerator()
- {
- if (m_fp) fclose(m_fp);
- if (m_fn) { _wunlink(m_fn); free(m_fn); }
- }
- size_t FilePageGenerator::GetData(char *buf, int size) // return 0 when done
- {
- if (!m_fp) return 0;
- if (m_file_pos+size > m_file_len) size=m_file_len - m_file_pos;
- int l=fread(buf,1,size,m_fp);
- m_file_pos+=l;
- return l;
- }
- class onConnCB : public api_onconncb {
- public:
- api_pagegenerator* onConnection(api_httpserv *serv, int port)
- {
- api_connection *c = serv->get_con();
- if(c && c->GetRemoteAddress() != 0x0100007f) return 0; // if it's not from localhost, disregard.
- serv->set_reply_header("Server:ml_pmp/1.0");
- if (!strcmp(serv->get_request_file(),"/"))
- {
- // find temporary filename
- wchar_t fn[MAX_PATH]={0};
- wchar_t dir[MAX_PATH]={0};
- GetTempPath(MAX_PATH,dir);
- GetTempFileName(dir,L"pmp",0,fn);
- _wunlink(fn);
- {wchar_t * ext = wcsrchr(fn,L'.'); if(ext) *ext=0;}
- // decode the parameters needed to find the track from the URL
- char device[128]={0};
- char artist[2048]={0};
- char album[2048]={0};
- char title[2048]={0};
- char * p;
- p = serv->get_request_parm("d");
- if(p) url_decode(p,device,128);
- p = serv->get_request_parm("a");
- if(p) url_decode(p,artist,2048);
- p = serv->get_request_parm("l");
- if(p) url_decode(p,album,2048);
- p = serv->get_request_parm("t");
- if(p) url_decode(p,title,2048);
- char * sc = strrchr(device,';');
- char * ext = "mp3";
- if(sc) { *sc=0; ext = sc+2; }
- // find the track based on this info
- Device * dev;
- songid_t song;
- if(findTrack(&dev,&song,AutoWide(device,CP_UTF8),AutoWide(artist,CP_UTF8),AutoWide(album,CP_UTF8),AutoWide(title,CP_UTF8))) {
- if(dev->copyToHardDriveSupported()) {
- // track found, can be copied back. Lets reply to the user
- serv->set_reply_string("HTTP/1.1 200 OK");
- char buf[150]="";
- StringCchPrintfA(buf, ARRAYSIZE(buf), "Content-Type:audio/%s",ext);
- serv->set_reply_header(buf);
- wchar_t title[128]={0};
- getTitle(dev,song,fn,title,128);
- StringCchPrintfA(buf, ARRAYSIZE(buf), "icy-name:%s",(char*)AutoChar(title,CP_UTF8));
- serv->set_reply_header(buf);
- serv->send_reply();
- // do the actual copy, and start streaming
- if(copyFileFromDevice(dev,song,fn)) return new FilePageGenerator(fn,serv);
- }
- }
- }
- serv->set_reply_string("HTTP/1.1 404 NOT FOUND");
- serv->send_reply();
- return 0; // no data
- }
- void destroyConnection(api_pagegenerator *conn)
- {
- FilePageGenerator *realObject = static_cast<FilePageGenerator *>(conn);
- delete realObject;
- }
- protected:
- RECVS_DISPATCH;
- };
- #define CBCLASS onConnCB
- START_DISPATCH;
- CB(API_ONCONNCB_ONCONNECTION,onConnection);
- VCB(API_ONCONNCB_DESTROYCONNECTION,destroyConnection);
- END_DISPATCH;
- #undef CBCLASS
- int serverPort = -1;
- static DWORD WINAPI ThreadFunc_Server(LPVOID lpParam) {
- extern bool quitting;
- if(quitting) return 0;
- onConnCB *pOnConnCB;
- int * killswitch = (int*)lpParam;
- api_webserv *server=0;
- waServiceFactory *sf = plugin.service->service_getServiceByGuid(webservGUID);
- if (sf) server = (api_webserv *)sf->getInterface();
- if(!server) return NULL;
- pOnConnCB = new onConnCB;
- server->SetConnectionCallback(pOnConnCB);
- serverPort = 54387;
- while(server->addListenPort(serverPort,0x0100007f) && serverPort < 54397) serverPort++;
- if(serverPort >= 54397) { serverPort=-1; return NULL; }
- while (killswitch && *killswitch == 0)
- {
- server->run();
- server->run();
- Sleep(20);
- }
- server->removeListenPort(serverPort);
- sf->releaseInterface(server);
- serverPort=-1;
- delete pOnConnCB;
- return NULL;
- }
- static int killswitch = 0;
- static HANDLE serverThread = NULL;
- void startServer() {
- if(serverPort == -1) {
- killswitch = 0;
- DWORD dwThreadId;
- serverThread = CreateThread(NULL, 0, ThreadFunc_Server, (LPVOID)&killswitch, 0, &dwThreadId);
- }
- }
- void stopServer() {
- if(serverThread) {
- killswitch = 1;
- WaitForSingleObject(serverThread,INFINITE);
- CloseHandle(serverThread);
- serverThread = NULL;
- }
- }
|