JnetCOM.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. #include "JnetCOM.h"
  2. #include "main.h"
  3. #include "../nu/AutoChar.h"
  4. #include "../nu/AutoWide.h"
  5. #include <algorithm>
  6. #include <map>
  7. #include "config.h"
  8. #include <time.h>
  9. #include "../nu/MediaLibraryInterface.h"
  10. #include "api__ml_online.h"
  11. #include <api/service/waServiceFactory.h>
  12. #include <strsafe.h>
  13. extern C_Config *g_config;
  14. BufferMap buffer_map;
  15. enum
  16. {
  17. DISP_JNET_INIT = 9323,
  18. DISP_JNET_DOWNLOAD,
  19. DISP_JNET_STATUS,
  20. DISP_JNET_SIZE,
  21. DISP_JNET_BUFFER,
  22. DISP_JNET_BUFFER_RAW,
  23. DISP_JNET_POST,
  24. };
  25. HANDLE DuplicateCurrentThread()
  26. {
  27. HANDLE fakeHandle = GetCurrentThread();
  28. HANDLE copiedHandle = 0;
  29. HANDLE processHandle = GetCurrentProcess();
  30. DuplicateHandle(processHandle, fakeHandle, processHandle, &copiedHandle, 0, FALSE, DUPLICATE_SAME_ACCESS);
  31. return copiedHandle;
  32. }
  33. #define CHECK_ID(str, id) if (wcscmp(rgszNames[i], L##str) == 0) { rgdispid[i] = id; continue; }
  34. HRESULT JnetCOM::GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid)
  35. {
  36. bool unknowns = false;
  37. for (unsigned int i = 0;i != cNames;i++)
  38. {
  39. CHECK_ID("Init", DISP_JNET_INIT)
  40. CHECK_ID("Download", DISP_JNET_DOWNLOAD)
  41. CHECK_ID("Status", DISP_JNET_STATUS)
  42. CHECK_ID("Buffer", DISP_JNET_BUFFER)
  43. CHECK_ID("BufferRaw", DISP_JNET_BUFFER_RAW)
  44. CHECK_ID("Size", DISP_JNET_SIZE)
  45. CHECK_ID("Post", DISP_JNET_POST)
  46. rgdispid[i] = DISPID_UNKNOWN;
  47. unknowns = true;
  48. }
  49. if (unknowns)
  50. return DISP_E_UNKNOWNNAME;
  51. else
  52. return S_OK;
  53. }
  54. HRESULT JnetCOM::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo)
  55. {
  56. return E_NOTIMPL;
  57. }
  58. HRESULT JnetCOM::GetTypeInfoCount(unsigned int FAR * pctinfo)
  59. {
  60. return E_NOTIMPL;
  61. }
  62. HRESULT JnetCOM::Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr)
  63. {
  64. switch (dispid)
  65. {
  66. case DISP_JNET_INIT:
  67. {
  68. if (!active && NULL == callback)
  69. {
  70. finalSize = 0;
  71. isBlocking = true;
  72. if ( pdispparams->cArgs == 2 )
  73. {
  74. isBlocking = false;
  75. htmldiv = SysAllocString(pdispparams->rgvarg[0].bstrVal); // HTML DIV
  76. callback = pdispparams->rgvarg[1].pdispVal; // callback object;
  77. if (NULL != callback)
  78. callback->AddRef();
  79. }
  80. }
  81. else
  82. {
  83. if (pvarResult)
  84. {
  85. VariantInit(pvarResult);
  86. V_VT(pvarResult) = VT_I4;
  87. V_I4(pvarResult) = -1;
  88. }
  89. }
  90. return S_OK;
  91. }
  92. break;
  93. case DISP_JNET_DOWNLOAD:
  94. if (pdispparams->cArgs == 1 || pdispparams->cArgs == 2)
  95. {
  96. int result = -1;
  97. if ( !active )
  98. {
  99. time_t ret = 0;
  100. active = 1;
  101. DownloadURL(pdispparams);
  102. result = 1;
  103. }
  104. else result = -1;
  105. if (pvarResult)
  106. {
  107. VariantInit(pvarResult);
  108. V_VT(pvarResult) = VT_I4;
  109. V_I4(pvarResult) = result;
  110. }
  111. return S_OK;
  112. }
  113. break;
  114. case DISP_JNET_POST:
  115. if (pdispparams->cArgs == 2)
  116. {
  117. int result = -1;
  118. if ( !active )
  119. {
  120. time_t ret = 0;
  121. active = 1;
  122. PostURL(pdispparams);
  123. result = 1;
  124. }
  125. else result = -1;
  126. if (pvarResult)
  127. {
  128. VariantInit(pvarResult);
  129. V_VT(pvarResult) = VT_I4;
  130. V_I4(pvarResult) = result;
  131. }
  132. return S_OK;
  133. }
  134. break;
  135. case DISP_JNET_STATUS:
  136. {
  137. if (pvarResult && errorstr && active )
  138. {
  139. AutoWide result(errorstr);
  140. BSTR tag = SysAllocString(result);
  141. VariantInit(pvarResult);
  142. V_VT(pvarResult) = VT_BSTR;
  143. V_BSTR(pvarResult) = tag;
  144. }
  145. return S_OK;
  146. }
  147. break;
  148. case DISP_JNET_SIZE:
  149. {
  150. if (pvarResult && active )
  151. {
  152. VariantInit(pvarResult);
  153. V_VT(pvarResult) = VT_I4;
  154. V_I4(pvarResult) = (INT)finalSize;
  155. }
  156. return S_OK;
  157. }
  158. break;
  159. case DISP_JNET_BUFFER_RAW:
  160. {
  161. if (pvarResult && jnetbuf->get() && active)
  162. {
  163. char dummy[1]={0};
  164. size_t len = jnetbuf->getlen();
  165. char *source = (char *)jnetbuf->get();
  166. if (!len || !source)
  167. {
  168. source=dummy;
  169. len=1;
  170. }
  171. else
  172. {
  173. while (*(source+len-1) == 0 && len)
  174. len--;
  175. }
  176. SAFEARRAY *bufferArray=SafeArrayCreateVector(VT_UI1, 0, (ULONG)len);
  177. void *data;
  178. SafeArrayAccessData(bufferArray, &data);
  179. memcpy(data, source, len);
  180. SafeArrayUnaccessData(bufferArray);
  181. VariantInit(pvarResult);
  182. V_VT(pvarResult) = VT_ARRAY|VT_UI1;
  183. V_ARRAY(pvarResult) = bufferArray;
  184. }
  185. return S_OK;
  186. }
  187. case DISP_JNET_BUFFER:
  188. {
  189. if (pvarResult && jnetbuf->get() && active )
  190. {
  191. AutoWide result((char *)jnetbuf->get());
  192. BSTR tag = SysAllocString(result);
  193. VariantInit(pvarResult);
  194. V_VT(pvarResult) = VT_BSTR;
  195. V_BSTR(pvarResult) = tag;
  196. }
  197. return S_OK;
  198. }
  199. break;
  200. }
  201. return DISP_E_MEMBERNOTFOUND;
  202. }
  203. STDMETHODIMP JnetCOM::QueryInterface(REFIID riid, PVOID *ppvObject)
  204. {
  205. if (!ppvObject)
  206. return E_POINTER;
  207. else if (IsEqualIID(riid, IID_IDispatch))
  208. *ppvObject = (IDispatch *)this;
  209. else if (IsEqualIID(riid, IID_IUnknown))
  210. *ppvObject = this;
  211. else
  212. {
  213. *ppvObject = NULL;
  214. return E_NOINTERFACE;
  215. }
  216. AddRef();
  217. return S_OK;
  218. }
  219. ULONG JnetCOM::AddRef(void)
  220. {
  221. return ++refCount;
  222. }
  223. ULONG JnetCOM::Release(void)
  224. {
  225. if (--refCount)
  226. return refCount;
  227. threadexit = 1; // exit the thread if active
  228. if (!isBlocking)
  229. { // Only if a callback was associated would the thread be running
  230. while (getter) // If there no getter, the thread is gone
  231. Sleep(10);
  232. }
  233. if (!finalSize)
  234. {
  235. BufferMap::iterator buffer_it = buffer_map.find(url);
  236. if (buffer_it != buffer_map.end())
  237. {
  238. jnetbuf = buffer_it->second;
  239. buffer_map.erase(buffer_it->first);
  240. delete jnetbuf;
  241. jnetbuf = NULL;
  242. }
  243. }
  244. Plugin_FreeString(url);
  245. url=0;
  246. if (postData)
  247. {
  248. free(postData);
  249. postData=0;
  250. }
  251. if (NULL != callback)
  252. {
  253. callback->Release();
  254. callback = NULL;
  255. }
  256. delete this;
  257. return 0;
  258. }
  259. static VOID CALLBACK CallbackAPC(ULONG_PTR param)
  260. {
  261. VARIANT arguments[2];
  262. DISPPARAMS params;
  263. unsigned int ret;
  264. JnetCOM *jnet = (JnetCOM *)param;
  265. if (NULL == jnet || NULL == jnet->callback)
  266. return;
  267. VariantInit(&arguments[0]);
  268. VariantInit(&arguments[1]);
  269. V_VT(&arguments[0]) = VT_DISPATCH;
  270. V_DISPATCH(&arguments[0]) = jnet;
  271. V_VT(&arguments[1]) = VT_BSTR;
  272. V_BSTR(&arguments[1]) = jnet->htmldiv;
  273. params.cArgs = ARRAYSIZE(arguments);
  274. params.cNamedArgs = 0;
  275. params.rgdispidNamedArgs = NULL;
  276. params.rgvarg = arguments;
  277. jnet->callback->Invoke(0, GUID_NULL, 0, DISPATCH_METHOD, &params, NULL, NULL, &ret);
  278. V_DISPATCH(&arguments[0]) = NULL;
  279. V_BSTR(&arguments[1]) = NULL;
  280. VariantClear(&arguments[0]);
  281. VariantClear(&arguments[1]);
  282. jnet->Release();
  283. }
  284. void JnetCOM::callBack()
  285. {
  286. AddRef();
  287. if (GetCurrentThreadId() == callingThreadId)
  288. CallbackAPC((ULONG_PTR)this);
  289. else
  290. {
  291. if (NULL == callingThreadHandle ||
  292. 0 == QueueUserAPC(CallbackAPC, callingThreadHandle, (ULONG_PTR)this))
  293. {
  294. Release();
  295. }
  296. }
  297. }
  298. #define USER_AGENT_SIZE (10 /*User-Agent*/ + 2 /*: */ + 6 /*Winamp*/ + 1 /*/*/ + 1 /*5*/ + 3/*.21*/ + 1 /*Null*/)
  299. void SetUserAgent(api_httpreceiver *http)
  300. {
  301. char user_agent[USER_AGENT_SIZE] = {0};
  302. int bigVer = ((winampVersion & 0x0000FF00) >> 12);
  303. int smallVer = ((winampVersion & 0x000000FF));
  304. StringCchPrintfA(user_agent, USER_AGENT_SIZE, "User-Agent: Winamp/%01x.%02x", bigVer, smallVer);
  305. http->addheader(user_agent);
  306. }
  307. int JnetCOM::JthreadProc()
  308. {
  309. int ret;
  310. char temp[WORKSIZE] = {0};
  311. char *proxy = mediaLibrary.GetProxy();
  312. waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(httpreceiverGUID);
  313. if (sf) getter = (api_httpreceiver *)sf->getInterface();
  314. // we're keying off of 'getter' to know if the thread is running or not, so we can release now
  315. Release();
  316. if (!getter)
  317. return -1;
  318. getter->AllowCompression();
  319. getter->open(API_DNS_AUTODNS, WORKSIZE, proxy);
  320. SetUserAgent(getter);
  321. getter->connect(AutoChar(url));
  322. BOOL bDone = FALSE;
  323. while (!bDone && !threadexit)
  324. {
  325. Sleep(10);
  326. ret = getter->run();
  327. if (ret == -1 || ret == 1)
  328. bDone = TRUE;
  329. int bytesReceived = getter->get_bytes(temp, WORKSIZE);
  330. if (bytesReceived)
  331. jnetbuf->add(temp, bytesReceived);
  332. }
  333. if (!threadexit)
  334. {
  335. if (ret == -1) StringCchCopyA(errorstr, 2048, getter->geterrorstr());
  336. else
  337. {
  338. int bytesReceived;
  339. do // flush out the socket
  340. {
  341. bytesReceived = getter->get_bytes(temp, WORKSIZE);
  342. if (bytesReceived)
  343. jnetbuf->add(temp, bytesReceived);
  344. }
  345. while (bytesReceived);
  346. temp[0] = 0;
  347. jnetbuf->add(temp, 1);
  348. }
  349. finalSize = jnetbuf->getlen();
  350. callBack();
  351. }
  352. sf->releaseInterface(getter);
  353. threadexit = 0;
  354. if (callingThreadHandle)
  355. CloseHandle(callingThreadHandle);
  356. getter = NULL;
  357. return ret;
  358. }
  359. int JnetCOM::PostProcedure()
  360. {
  361. int ret;
  362. char temp[WORKSIZE] = {0};
  363. char *proxy = mediaLibrary.GetProxy();
  364. waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(httpreceiverGUID);
  365. if (sf) getter = (api_httpreceiver *)sf->getInterface();
  366. // we're keying off of 'getter' to know if the thread is running or not, so we can release now
  367. Release();
  368. if (!getter)
  369. return -1;
  370. getter->AllowCompression();
  371. getter->open(API_DNS_AUTODNS, WORKSIZE, proxy);
  372. SetUserAgent(getter);
  373. getter->addheader("Content-Type: application/x-www-form-urlencoded");
  374. size_t contentLength = strlen(postData);
  375. char contentLengthHeader[256] = {0};
  376. StringCchPrintfA(contentLengthHeader, 256, "Content-Length: %u", contentLength);
  377. getter->addheader(contentLengthHeader);
  378. char *dataIndex = postData;
  379. bool done=false;
  380. getter->connect(AutoChar(url), 0, "POST");
  381. // time to post data!
  382. api_connection *connection = getter->GetConnection();
  383. while (contentLength && !threadexit)
  384. {
  385. Sleep(1);
  386. getter->run();
  387. size_t lengthToSend = min(contentLength, connection->GetSendBytesAvailable());
  388. if (lengthToSend)
  389. {
  390. connection->send(dataIndex, (int)lengthToSend);
  391. dataIndex+=lengthToSend;
  392. contentLength-=lengthToSend;
  393. }
  394. }
  395. while (!threadexit && !done)
  396. {
  397. Sleep(10);
  398. ret = getter->run();
  399. if (ret == -1)
  400. break;
  401. // ---- check our reply code ----
  402. int replycode = getter->getreplycode();
  403. switch (replycode)
  404. {
  405. case 0:
  406. break;
  407. case 100:
  408. break;
  409. case 200:
  410. {
  411. int bytesReceived = getter->get_bytes(temp, WORKSIZE);
  412. if (bytesReceived)
  413. jnetbuf->add(temp, bytesReceived);
  414. do
  415. {
  416. Sleep(10);
  417. ret = getter->run();
  418. bytesReceived = getter->get_bytes(temp, WORKSIZE);
  419. if (bytesReceived)
  420. jnetbuf->add(temp, bytesReceived);
  421. }
  422. while (ret == HTTPRECEIVER_RUN_OK);
  423. done=true; // just in case
  424. }
  425. break;
  426. default:
  427. done=true;
  428. break;
  429. }
  430. if (ret != HTTPRECEIVER_RUN_OK)
  431. break;
  432. }
  433. if (!threadexit)
  434. {
  435. if (ret == -1)
  436. StringCchCopyA(errorstr, 2048, getter->geterrorstr());
  437. else
  438. {
  439. int bytesReceived;
  440. do // flush out the socket
  441. {
  442. bytesReceived = getter->get_bytes(temp, WORKSIZE);
  443. if (bytesReceived)
  444. jnetbuf->add(temp, bytesReceived);
  445. }
  446. while (bytesReceived && !threadexit);
  447. temp[0] = 0;
  448. jnetbuf->add(temp, 1);
  449. }
  450. if ( !threadexit )
  451. {
  452. finalSize = jnetbuf->getlen();
  453. callBack();
  454. }
  455. }
  456. sf->releaseInterface(getter);
  457. threadexit = 0;
  458. if (callingThreadHandle)
  459. CloseHandle(callingThreadHandle);
  460. getter = NULL;
  461. return ret;
  462. }
  463. void JnetCOM::DownloadURL(DISPPARAMS FAR *pdispparams)
  464. {
  465. Plugin_FreeString(url);
  466. url = Plugin_CopyString(pdispparams->rgvarg[pdispparams->cArgs - 1].bstrVal);
  467. callingThreadId = GetCurrentThreadId();
  468. callingThreadHandle = DuplicateCurrentThread();
  469. BufferMap::iterator buffer_it = buffer_map.find(url);
  470. if (buffer_it != buffer_map.end())
  471. {
  472. time_t check = time(NULL);
  473. jnetbuf = buffer_it->second;
  474. if ( check >= jnetbuf->expire_time)
  475. {
  476. buffer_map.erase(buffer_it->first);
  477. delete jnetbuf;
  478. jnetbuf = NULL;
  479. }
  480. else
  481. {
  482. finalSize = jnetbuf->getlen();
  483. callBack();
  484. }
  485. }
  486. if (!jnetbuf)
  487. {
  488. time_t now = 0;
  489. jnetbuf = buffer_map[url] = new Buffer_GrowBuf;
  490. if ( pdispparams->cArgs == 2 ) // passed in a time from Javascript, or 0 to not cache
  491. {
  492. if ( pdispparams->rgvarg[0].iVal )
  493. {
  494. now = time(NULL);
  495. jnetbuf->expire_time = now + pdispparams->rgvarg[0].iVal;
  496. }
  497. }
  498. else // Use winamp config cache time
  499. {
  500. int when = 0, x = 0;
  501. x = g_config->ReadInt("radio_upd_freq", 0);
  502. switch ( x )
  503. {
  504. case 0:
  505. {
  506. when = 3600; // One Hour
  507. }
  508. break;
  509. case 1:
  510. {
  511. when = 3600 * 24; // One Day aka 24 hours
  512. }
  513. break;
  514. case 2:
  515. {
  516. when = 3600 * 24 * 7; // One week (weak)
  517. }
  518. break;
  519. default:
  520. break;
  521. }
  522. if (when)
  523. now = time(NULL);
  524. jnetbuf->expire_time = now + when;
  525. }
  526. if (isBlocking)
  527. {
  528. AddRef(); // need to call this because JthreadProc does a Release
  529. JthreadProc(); // Call it directly, block until done.
  530. }
  531. else
  532. {
  533. // Launch the thread
  534. DWORD threadId;
  535. AddRef(); // make sure jnetcom object doesn't die while the thread is launching.
  536. jnetThread = CreateThread(NULL, NULL, JThreadProc, (void *)this, NULL, &threadId);
  537. }
  538. }
  539. }
  540. void JnetCOM::PostURL(DISPPARAMS FAR *pdispparams)
  541. {
  542. postData = AutoCharDup(pdispparams->rgvarg[0].bstrVal);
  543. Plugin_FreeString(url);
  544. url = Plugin_CopyString(pdispparams->rgvarg[1].bstrVal);
  545. callingThreadId = GetCurrentThreadId();
  546. callingThreadHandle = DuplicateCurrentThread();
  547. if (!jnetbuf)
  548. {
  549. time_t now = 0;
  550. jnetbuf = buffer_map[url] = new Buffer_GrowBuf;
  551. if (isBlocking)
  552. {
  553. AddRef(); // need to do this beacuse PostProcedure calls Release
  554. PostProcedure(); // Call it directly, block until done.
  555. }
  556. else
  557. {
  558. // Launch the thread
  559. DWORD threadId;
  560. AddRef(); // make sure the jnetcom doesn't get deleted before the thread launches
  561. jnetThread = CreateThread(NULL, NULL, jnetPostProcedure, (void *)this, NULL, &threadId);
  562. }
  563. }
  564. }