indirectplaybackserver.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. #include "main.h"
  2. #include "api/service/waservicefactory.h"
  3. #include "bfc/dispatch.h"
  4. #include "../Components/wac_network/wac_network_web_server_api.h"
  5. #include "../Components/wac_network/wac_network_onconncb_api.h"
  6. #include "../Components/wac_network/wac_network_connection_api.h"
  7. #include "../Components/wac_network/wac_network_http_server_api.h"
  8. #include "../Components/wac_network/wac_network_page_generator_api.h"
  9. #include "api__ml_pmp.h"
  10. #include "DeviceView.h"
  11. #include <wchar.h>
  12. #include "metadata_utils.h"
  13. #include "nu/AutoWide.h"
  14. #include "nu/AutoChar.h"
  15. #include <strsafe.h>
  16. void url_decode(char *in, char *out, int maxlen)
  17. {
  18. while (in && *in && maxlen>1)
  19. {
  20. if (*in == '+')
  21. {
  22. in++;
  23. *out++=' ';
  24. }
  25. else if (*in == '%' && in[1] != '%' && in[1])
  26. {
  27. int a=0;
  28. int b=0;
  29. for ( b = 0; b < 2; b ++)
  30. {
  31. int r=in[1+b];
  32. if (r>='0'&&r<='9') r-='0';
  33. else if (r>='a'&&r<='z') r-='a'-10;
  34. else if (r>='A'&&r<='Z') r-='A'-10;
  35. else break;
  36. a*=16;
  37. a+=r;
  38. }
  39. if (b < 2) *out++=*in++;
  40. else { *out++=a; in += 3;}
  41. }
  42. else *out++=*in++;
  43. maxlen--;
  44. }
  45. *out=0;
  46. }
  47. static void callbackThunk(void * callBackContext, wchar_t * status) {
  48. }
  49. typedef struct {
  50. int status;
  51. wchar_t * device; wchar_t * artist; wchar_t * album; wchar_t * title;
  52. DeviceView * d; songid_t s;
  53. } FindTrackStruct;
  54. static VOID CALLBACK APC_FindTrack(ULONG_PTR dwParam) {
  55. FindTrackStruct * f = (FindTrackStruct *)dwParam;
  56. for(int i=0; i<devices.GetSize(); i++) {
  57. f->d = (DeviceView*)devices.Get(i);
  58. wchar_t buf[2048] = {0}; int len = 2047;
  59. f->d->dev->getPlaylistName(0,buf,128);
  60. if(wcscmp(buf,f->device)) continue;
  61. int l = f->d->dev->getPlaylistLength(0);
  62. for(int j=0; j<l; j++) {
  63. f->s = f->d->dev->getPlaylistTrack(0,j);
  64. f->d->dev->getTrackArtist(f->s,buf,len);
  65. if(lstrcmpi(buf,f->artist)) continue;
  66. f->d->dev->getTrackAlbum(f->s,buf,len);
  67. if(lstrcmpi(buf,f->album)) continue;
  68. f->d->dev->getTrackTitle(f->s,buf,len);
  69. if(lstrcmpi(buf,f->title)) continue;
  70. f->status = 2;
  71. return;
  72. }
  73. }
  74. f->status = 1;
  75. }
  76. bool findTrack(Device **dev, songid_t *song,wchar_t * device,wchar_t * artist,wchar_t * album,wchar_t * title) {
  77. FindTrackStruct f = {0,device,artist,album,title,NULL,0};
  78. SynchronousProcedureCall(APC_FindTrack,(ULONG_PTR)&f);
  79. *dev = f.d->dev;
  80. *song = f.s;
  81. return f.status == 2;
  82. }
  83. bool copyFileFromDevice(Device * dev, songid_t song,wchar_t * fn) {
  84. int k=0;
  85. if(dev->copyToHardDriveSupported()) {
  86. if(!dev->copyToHardDrive(song,fn,NULL,callbackThunk,&k))
  87. return true;
  88. }
  89. return false;
  90. }
  91. class FilePageGenerator : public api_pagegenerator // public IPageGenerator
  92. {
  93. public:
  94. virtual ~FilePageGenerator();
  95. FilePageGenerator(wchar_t *fn, api_httpserv *serv);
  96. size_t GetData(char *buf, int size); // return 0 when done
  97. int is_error() { return !m_fp; }
  98. private:
  99. FILE *m_fp;
  100. int m_file_pos;
  101. int m_file_len;
  102. wchar_t *m_fn;
  103. protected:
  104. RECVS_DISPATCH;
  105. };
  106. #define CBCLASS FilePageGenerator
  107. START_DISPATCH;
  108. CB(API_PAGEGENERATOR_GETDATA, GetData);
  109. END_DISPATCH;
  110. #undef CBCLASS
  111. FilePageGenerator::FilePageGenerator(wchar_t *fn, api_httpserv *serv)
  112. {
  113. m_fn = _wcsdup(fn);
  114. m_file_pos=m_file_len=0;
  115. m_fp=_wfopen(fn,L"rb");
  116. if (m_fp)
  117. {
  118. int resume_end=0;
  119. int resume_pos=0;
  120. char *range = serv->getheader("Range");
  121. if (range)
  122. {
  123. if (!_strnicmp(range,"bytes=",6))
  124. {
  125. range+=6;
  126. char *t=range;
  127. while (t && *t && (*t < '0' || *t > '9')) t++;
  128. while (t && *t && *t >= '0' && *t <= '9')
  129. {
  130. resume_pos*=10;
  131. resume_pos+=*t-'0';
  132. t++;
  133. }
  134. if (*t != '-') resume_pos=0;
  135. else if (t[1])
  136. {
  137. resume_end=atoi(t+1);
  138. }
  139. }
  140. }
  141. fseek(m_fp,0,SEEK_END);
  142. m_file_len=ftell(m_fp);
  143. char buf[512] = {0};
  144. StringCchPrintfA(buf, ARRAYSIZE(buf), "Content-Length: %d",m_file_len);
  145. serv->set_reply_header(buf);
  146. int m_file_len_orig=m_file_len;
  147. if (resume_end && resume_end < m_file_len) m_file_len=resume_end;
  148. if (resume_pos > 0 && resume_pos < m_file_len) m_file_pos = resume_pos;
  149. fseek(m_fp,m_file_pos,SEEK_SET);
  150. StringCchPrintfA(buf, ARRAYSIZE(buf), "Content-Range: bytes=%d-%d/%d",resume_pos,resume_end,m_file_len_orig);
  151. serv->set_reply_header(buf);
  152. if (resume_pos != 0) serv->set_reply_string("HTTP/1.1 206 Partial Content");
  153. }
  154. }
  155. FilePageGenerator::~FilePageGenerator()
  156. {
  157. if (m_fp) fclose(m_fp);
  158. if (m_fn) { _wunlink(m_fn); free(m_fn); }
  159. }
  160. size_t FilePageGenerator::GetData(char *buf, int size) // return 0 when done
  161. {
  162. if (!m_fp) return 0;
  163. if (m_file_pos+size > m_file_len) size=m_file_len - m_file_pos;
  164. int l=fread(buf,1,size,m_fp);
  165. m_file_pos+=l;
  166. return l;
  167. }
  168. class onConnCB : public api_onconncb {
  169. public:
  170. api_pagegenerator* onConnection(api_httpserv *serv, int port)
  171. {
  172. api_connection *c = serv->get_con();
  173. if(c && c->GetRemoteAddress() != 0x0100007f) return 0; // if it's not from localhost, disregard.
  174. serv->set_reply_header("Server:ml_pmp/1.0");
  175. if (!strcmp(serv->get_request_file(),"/"))
  176. {
  177. // find temporary filename
  178. wchar_t fn[MAX_PATH]={0};
  179. wchar_t dir[MAX_PATH]={0};
  180. GetTempPath(MAX_PATH,dir);
  181. GetTempFileName(dir,L"pmp",0,fn);
  182. _wunlink(fn);
  183. {wchar_t * ext = wcsrchr(fn,L'.'); if(ext) *ext=0;}
  184. // decode the parameters needed to find the track from the URL
  185. char device[128]={0};
  186. char artist[2048]={0};
  187. char album[2048]={0};
  188. char title[2048]={0};
  189. char * p;
  190. p = serv->get_request_parm("d");
  191. if(p) url_decode(p,device,128);
  192. p = serv->get_request_parm("a");
  193. if(p) url_decode(p,artist,2048);
  194. p = serv->get_request_parm("l");
  195. if(p) url_decode(p,album,2048);
  196. p = serv->get_request_parm("t");
  197. if(p) url_decode(p,title,2048);
  198. char * sc = strrchr(device,';');
  199. char * ext = "mp3";
  200. if(sc) { *sc=0; ext = sc+2; }
  201. // find the track based on this info
  202. Device * dev;
  203. songid_t song;
  204. if(findTrack(&dev,&song,AutoWide(device,CP_UTF8),AutoWide(artist,CP_UTF8),AutoWide(album,CP_UTF8),AutoWide(title,CP_UTF8))) {
  205. if(dev->copyToHardDriveSupported()) {
  206. // track found, can be copied back. Lets reply to the user
  207. serv->set_reply_string("HTTP/1.1 200 OK");
  208. char buf[150]="";
  209. StringCchPrintfA(buf, ARRAYSIZE(buf), "Content-Type:audio/%s",ext);
  210. serv->set_reply_header(buf);
  211. wchar_t title[128]={0};
  212. getTitle(dev,song,fn,title,128);
  213. StringCchPrintfA(buf, ARRAYSIZE(buf), "icy-name:%s",(char*)AutoChar(title,CP_UTF8));
  214. serv->set_reply_header(buf);
  215. serv->send_reply();
  216. // do the actual copy, and start streaming
  217. if(copyFileFromDevice(dev,song,fn)) return new FilePageGenerator(fn,serv);
  218. }
  219. }
  220. }
  221. serv->set_reply_string("HTTP/1.1 404 NOT FOUND");
  222. serv->send_reply();
  223. return 0; // no data
  224. }
  225. void destroyConnection(api_pagegenerator *conn)
  226. {
  227. FilePageGenerator *realObject = static_cast<FilePageGenerator *>(conn);
  228. delete realObject;
  229. }
  230. protected:
  231. RECVS_DISPATCH;
  232. };
  233. #define CBCLASS onConnCB
  234. START_DISPATCH;
  235. CB(API_ONCONNCB_ONCONNECTION,onConnection);
  236. VCB(API_ONCONNCB_DESTROYCONNECTION,destroyConnection);
  237. END_DISPATCH;
  238. #undef CBCLASS
  239. int serverPort = -1;
  240. static DWORD WINAPI ThreadFunc_Server(LPVOID lpParam) {
  241. extern bool quitting;
  242. if(quitting) return 0;
  243. onConnCB *pOnConnCB;
  244. int * killswitch = (int*)lpParam;
  245. api_webserv *server=0;
  246. waServiceFactory *sf = plugin.service->service_getServiceByGuid(webservGUID);
  247. if (sf) server = (api_webserv *)sf->getInterface();
  248. if(!server) return NULL;
  249. pOnConnCB = new onConnCB;
  250. server->SetConnectionCallback(pOnConnCB);
  251. serverPort = 54387;
  252. while(server->addListenPort(serverPort,0x0100007f) && serverPort < 54397) serverPort++;
  253. if(serverPort >= 54397) { serverPort=-1; return NULL; }
  254. while (killswitch && *killswitch == 0)
  255. {
  256. server->run();
  257. server->run();
  258. Sleep(20);
  259. }
  260. server->removeListenPort(serverPort);
  261. sf->releaseInterface(server);
  262. serverPort=-1;
  263. delete pOnConnCB;
  264. return NULL;
  265. }
  266. static int killswitch = 0;
  267. static HANDLE serverThread = NULL;
  268. void startServer() {
  269. if(serverPort == -1) {
  270. killswitch = 0;
  271. DWORD dwThreadId;
  272. serverThread = CreateThread(NULL, 0, ThreadFunc_Server, (LPVOID)&killswitch, 0, &dwThreadId);
  273. }
  274. }
  275. void stopServer() {
  276. if(serverThread) {
  277. killswitch = 1;
  278. WaitForSingleObject(serverThread,INFINITE);
  279. CloseHandle(serverThread);
  280. serverThread = NULL;
  281. }
  282. }