From 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d Mon Sep 17 00:00:00 2001 From: Jef Date: Tue, 24 Sep 2024 14:54:57 +0200 Subject: Initial community commit --- Src/Plugins/Library/ml_online/JnetCOM.cpp | 656 ++++++++++++++++++++++++++++++ 1 file changed, 656 insertions(+) create mode 100644 Src/Plugins/Library/ml_online/JnetCOM.cpp (limited to 'Src/Plugins/Library/ml_online/JnetCOM.cpp') diff --git a/Src/Plugins/Library/ml_online/JnetCOM.cpp b/Src/Plugins/Library/ml_online/JnetCOM.cpp new file mode 100644 index 00000000..e93506f3 --- /dev/null +++ b/Src/Plugins/Library/ml_online/JnetCOM.cpp @@ -0,0 +1,656 @@ +#include "JnetCOM.h" +#include "main.h" +#include "../nu/AutoChar.h" +#include "../nu/AutoWide.h" +#include +#include +#include "config.h" +#include +#include "../nu/MediaLibraryInterface.h" +#include "api__ml_online.h" +#include +#include + +extern C_Config *g_config; + +BufferMap buffer_map; + +enum +{ + DISP_JNET_INIT = 9323, + DISP_JNET_DOWNLOAD, + DISP_JNET_STATUS, + DISP_JNET_SIZE, + DISP_JNET_BUFFER, + DISP_JNET_BUFFER_RAW, + DISP_JNET_POST, +}; + +HANDLE DuplicateCurrentThread() +{ + HANDLE fakeHandle = GetCurrentThread(); + HANDLE copiedHandle = 0; + HANDLE processHandle = GetCurrentProcess(); + DuplicateHandle(processHandle, fakeHandle, processHandle, &copiedHandle, 0, FALSE, DUPLICATE_SAME_ACCESS); + return copiedHandle; +} + +#define CHECK_ID(str, id) if (wcscmp(rgszNames[i], L##str) == 0) { rgdispid[i] = id; continue; } +HRESULT JnetCOM::GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid) +{ + bool unknowns = false; + for (unsigned int i = 0;i != cNames;i++) + { + CHECK_ID("Init", DISP_JNET_INIT) + CHECK_ID("Download", DISP_JNET_DOWNLOAD) + CHECK_ID("Status", DISP_JNET_STATUS) + CHECK_ID("Buffer", DISP_JNET_BUFFER) + CHECK_ID("BufferRaw", DISP_JNET_BUFFER_RAW) + CHECK_ID("Size", DISP_JNET_SIZE) + CHECK_ID("Post", DISP_JNET_POST) + + rgdispid[i] = DISPID_UNKNOWN; + unknowns = true; + + } + if (unknowns) + return DISP_E_UNKNOWNNAME; + else + return S_OK; +} + +HRESULT JnetCOM::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo) +{ + return E_NOTIMPL; +} + +HRESULT JnetCOM::GetTypeInfoCount(unsigned int FAR * pctinfo) +{ + return E_NOTIMPL; +} + + +HRESULT JnetCOM::Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr) +{ + switch (dispid) + { + case DISP_JNET_INIT: + { + if (!active && NULL == callback) + { + finalSize = 0; + isBlocking = true; + if ( pdispparams->cArgs == 2 ) + { + isBlocking = false; + htmldiv = SysAllocString(pdispparams->rgvarg[0].bstrVal); // HTML DIV + callback = pdispparams->rgvarg[1].pdispVal; // callback object; + if (NULL != callback) + callback->AddRef(); + } + } + else + { + if (pvarResult) + { + VariantInit(pvarResult); + V_VT(pvarResult) = VT_I4; + V_I4(pvarResult) = -1; + } + } + return S_OK; + } + break; + case DISP_JNET_DOWNLOAD: + if (pdispparams->cArgs == 1 || pdispparams->cArgs == 2) + { + int result = -1; + if ( !active ) + { + time_t ret = 0; + active = 1; + + DownloadURL(pdispparams); + result = 1; + } + else result = -1; + + if (pvarResult) + { + VariantInit(pvarResult); + V_VT(pvarResult) = VT_I4; + V_I4(pvarResult) = result; + } + + return S_OK; + } + break; + case DISP_JNET_POST: + if (pdispparams->cArgs == 2) + { + int result = -1; + if ( !active ) + { + time_t ret = 0; + active = 1; + + PostURL(pdispparams); + result = 1; + } + else result = -1; + + if (pvarResult) + { + VariantInit(pvarResult); + V_VT(pvarResult) = VT_I4; + V_I4(pvarResult) = result; + } + + return S_OK; + } + break; + case DISP_JNET_STATUS: + { + if (pvarResult && errorstr && active ) + { + AutoWide result(errorstr); + BSTR tag = SysAllocString(result); + VariantInit(pvarResult); + V_VT(pvarResult) = VT_BSTR; + V_BSTR(pvarResult) = tag; + } + return S_OK; + } + break; + case DISP_JNET_SIZE: + { + if (pvarResult && active ) + { + VariantInit(pvarResult); + V_VT(pvarResult) = VT_I4; + V_I4(pvarResult) = (INT)finalSize; + } + return S_OK; + } + break; + case DISP_JNET_BUFFER_RAW: + { + if (pvarResult && jnetbuf->get() && active) + { + char dummy[1]={0}; + size_t len = jnetbuf->getlen(); + char *source = (char *)jnetbuf->get(); + if (!len || !source) + { + source=dummy; + len=1; + } + else + { + while (*(source+len-1) == 0 && len) + len--; + } + SAFEARRAY *bufferArray=SafeArrayCreateVector(VT_UI1, 0, (ULONG)len); + void *data; + SafeArrayAccessData(bufferArray, &data); + memcpy(data, source, len); + SafeArrayUnaccessData(bufferArray); + VariantInit(pvarResult); + V_VT(pvarResult) = VT_ARRAY|VT_UI1; + V_ARRAY(pvarResult) = bufferArray; + } + return S_OK; + } + case DISP_JNET_BUFFER: + { + if (pvarResult && jnetbuf->get() && active ) + { + AutoWide result((char *)jnetbuf->get()); + BSTR tag = SysAllocString(result); + VariantInit(pvarResult); + V_VT(pvarResult) = VT_BSTR; + V_BSTR(pvarResult) = tag; + } + return S_OK; + } + break; + } + return DISP_E_MEMBERNOTFOUND; +} + +STDMETHODIMP JnetCOM::QueryInterface(REFIID riid, PVOID *ppvObject) +{ + if (!ppvObject) + return E_POINTER; + + else if (IsEqualIID(riid, IID_IDispatch)) + *ppvObject = (IDispatch *)this; + else if (IsEqualIID(riid, IID_IUnknown)) + *ppvObject = this; + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + +ULONG JnetCOM::AddRef(void) +{ + return ++refCount; +} + +ULONG JnetCOM::Release(void) +{ + if (--refCount) + return refCount; + + threadexit = 1; // exit the thread if active + if (!isBlocking) + { // Only if a callback was associated would the thread be running + while (getter) // If there no getter, the thread is gone + Sleep(10); + } + + if (!finalSize) + { + BufferMap::iterator buffer_it = buffer_map.find(url); + if (buffer_it != buffer_map.end()) + { + jnetbuf = buffer_it->second; + buffer_map.erase(buffer_it->first); + delete jnetbuf; + jnetbuf = NULL; + } + } + Plugin_FreeString(url); + url=0; + if (postData) + { + free(postData); + postData=0; + } + + if (NULL != callback) + { + callback->Release(); + callback = NULL; + } + + delete this; + return 0; +} + +static VOID CALLBACK CallbackAPC(ULONG_PTR param) +{ + VARIANT arguments[2]; + DISPPARAMS params; + unsigned int ret; + + JnetCOM *jnet = (JnetCOM *)param; + if (NULL == jnet || NULL == jnet->callback) + return; + + VariantInit(&arguments[0]); + VariantInit(&arguments[1]); + + V_VT(&arguments[0]) = VT_DISPATCH; + V_DISPATCH(&arguments[0]) = jnet; + + V_VT(&arguments[1]) = VT_BSTR; + V_BSTR(&arguments[1]) = jnet->htmldiv; + + params.cArgs = ARRAYSIZE(arguments); + params.cNamedArgs = 0; + params.rgdispidNamedArgs = NULL; + params.rgvarg = arguments; + + jnet->callback->Invoke(0, GUID_NULL, 0, DISPATCH_METHOD, ¶ms, NULL, NULL, &ret); + + + V_DISPATCH(&arguments[0]) = NULL; + V_BSTR(&arguments[1]) = NULL; + + VariantClear(&arguments[0]); + VariantClear(&arguments[1]); + + jnet->Release(); + + +} + +void JnetCOM::callBack() +{ + AddRef(); + if (GetCurrentThreadId() == callingThreadId) + CallbackAPC((ULONG_PTR)this); + else + { + if (NULL == callingThreadHandle || + 0 == QueueUserAPC(CallbackAPC, callingThreadHandle, (ULONG_PTR)this)) + { + Release(); + } + } +} + +#define USER_AGENT_SIZE (10 /*User-Agent*/ + 2 /*: */ + 6 /*Winamp*/ + 1 /*/*/ + 1 /*5*/ + 3/*.21*/ + 1 /*Null*/) +void SetUserAgent(api_httpreceiver *http) +{ + char user_agent[USER_AGENT_SIZE] = {0}; + int bigVer = ((winampVersion & 0x0000FF00) >> 12); + int smallVer = ((winampVersion & 0x000000FF)); + StringCchPrintfA(user_agent, USER_AGENT_SIZE, "User-Agent: Winamp/%01x.%02x", bigVer, smallVer); + http->addheader(user_agent); +} + +int JnetCOM::JthreadProc() +{ + int ret; + char temp[WORKSIZE] = {0}; + + char *proxy = mediaLibrary.GetProxy(); + + waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(httpreceiverGUID); + if (sf) getter = (api_httpreceiver *)sf->getInterface(); + + // we're keying off of 'getter' to know if the thread is running or not, so we can release now + Release(); + + if (!getter) + return -1; + + getter->AllowCompression(); + getter->open(API_DNS_AUTODNS, WORKSIZE, proxy); + + SetUserAgent(getter); + + getter->connect(AutoChar(url)); + + BOOL bDone = FALSE; + while (!bDone && !threadexit) + { + Sleep(10); + ret = getter->run(); + if (ret == -1 || ret == 1) + bDone = TRUE; + + int bytesReceived = getter->get_bytes(temp, WORKSIZE); + if (bytesReceived) + jnetbuf->add(temp, bytesReceived); + } + + if (!threadexit) + { + + if (ret == -1) StringCchCopyA(errorstr, 2048, getter->geterrorstr()); + else + { + int bytesReceived; + do // flush out the socket + { + bytesReceived = getter->get_bytes(temp, WORKSIZE); + if (bytesReceived) + jnetbuf->add(temp, bytesReceived); + } + while (bytesReceived); + + temp[0] = 0; + jnetbuf->add(temp, 1); + } + + finalSize = jnetbuf->getlen(); + + callBack(); + } + + sf->releaseInterface(getter); + threadexit = 0; + if (callingThreadHandle) + CloseHandle(callingThreadHandle); + getter = NULL; + return ret; +} + +int JnetCOM::PostProcedure() +{ + int ret; + char temp[WORKSIZE] = {0}; + + char *proxy = mediaLibrary.GetProxy(); + + waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(httpreceiverGUID); + if (sf) getter = (api_httpreceiver *)sf->getInterface(); + + // we're keying off of 'getter' to know if the thread is running or not, so we can release now + Release(); + if (!getter) + return -1; + + getter->AllowCompression(); + getter->open(API_DNS_AUTODNS, WORKSIZE, proxy); + + SetUserAgent(getter); + getter->addheader("Content-Type: application/x-www-form-urlencoded"); + + size_t contentLength = strlen(postData); + char contentLengthHeader[256] = {0}; + StringCchPrintfA(contentLengthHeader, 256, "Content-Length: %u", contentLength); + getter->addheader(contentLengthHeader); + + char *dataIndex = postData; + bool done=false; + + getter->connect(AutoChar(url), 0, "POST"); + + // time to post data! + api_connection *connection = getter->GetConnection(); + + while (contentLength && !threadexit) + { + Sleep(1); + getter->run(); + + size_t lengthToSend = min(contentLength, connection->GetSendBytesAvailable()); + if (lengthToSend) + { + connection->send(dataIndex, (int)lengthToSend); + dataIndex+=lengthToSend; + contentLength-=lengthToSend; + } + } + + while (!threadexit && !done) + { + Sleep(10); + ret = getter->run(); + if (ret == -1) + break; + + // ---- check our reply code ---- + int replycode = getter->getreplycode(); + switch (replycode) + { + case 0: + break; + case 100: + break; + case 200: + { + int bytesReceived = getter->get_bytes(temp, WORKSIZE); + if (bytesReceived) + jnetbuf->add(temp, bytesReceived); + do + { + Sleep(10); + ret = getter->run(); + bytesReceived = getter->get_bytes(temp, WORKSIZE); + if (bytesReceived) + jnetbuf->add(temp, bytesReceived); + } + while (ret == HTTPRECEIVER_RUN_OK); + done=true; // just in case + } + break; + default: + done=true; + break; + } + if (ret != HTTPRECEIVER_RUN_OK) + break; + } + if (!threadexit) + { + if (ret == -1) + StringCchCopyA(errorstr, 2048, getter->geterrorstr()); + else + { + int bytesReceived; + do // flush out the socket + { + bytesReceived = getter->get_bytes(temp, WORKSIZE); + if (bytesReceived) + jnetbuf->add(temp, bytesReceived); + } + while (bytesReceived && !threadexit); + + temp[0] = 0; + jnetbuf->add(temp, 1); + } + + if ( !threadexit ) + { + finalSize = jnetbuf->getlen(); + callBack(); + } + + } + sf->releaseInterface(getter); + + threadexit = 0; + if (callingThreadHandle) + CloseHandle(callingThreadHandle); + getter = NULL; + return ret; +} + +void JnetCOM::DownloadURL(DISPPARAMS FAR *pdispparams) +{ + Plugin_FreeString(url); + url = Plugin_CopyString(pdispparams->rgvarg[pdispparams->cArgs - 1].bstrVal); + + callingThreadId = GetCurrentThreadId(); + callingThreadHandle = DuplicateCurrentThread(); + + BufferMap::iterator buffer_it = buffer_map.find(url); + if (buffer_it != buffer_map.end()) + { + time_t check = time(NULL); + jnetbuf = buffer_it->second; + if ( check >= jnetbuf->expire_time) + { + buffer_map.erase(buffer_it->first); + delete jnetbuf; + jnetbuf = NULL; + } + else + { + finalSize = jnetbuf->getlen(); + callBack(); + } + + } + if (!jnetbuf) + { + time_t now = 0; + + jnetbuf = buffer_map[url] = new Buffer_GrowBuf; + + if ( pdispparams->cArgs == 2 ) // passed in a time from Javascript, or 0 to not cache + { + if ( pdispparams->rgvarg[0].iVal ) + { + now = time(NULL); + jnetbuf->expire_time = now + pdispparams->rgvarg[0].iVal; + } + } + else // Use winamp config cache time + { + int when = 0, x = 0; + + x = g_config->ReadInt("radio_upd_freq", 0); + switch ( x ) + { + case 0: + { + when = 3600; // One Hour + } + break; + case 1: + { + when = 3600 * 24; // One Day aka 24 hours + } + break; + case 2: + { + when = 3600 * 24 * 7; // One week (weak) + } + break; + default: + break; + } + if (when) + now = time(NULL); + + jnetbuf->expire_time = now + when; + + } + if (isBlocking) + { + AddRef(); // need to call this because JthreadProc does a Release + JthreadProc(); // Call it directly, block until done. + } + else + { + // Launch the thread + DWORD threadId; + AddRef(); // make sure jnetcom object doesn't die while the thread is launching. + jnetThread = CreateThread(NULL, NULL, JThreadProc, (void *)this, NULL, &threadId); + } + + } +} + +void JnetCOM::PostURL(DISPPARAMS FAR *pdispparams) +{ + postData = AutoCharDup(pdispparams->rgvarg[0].bstrVal); + Plugin_FreeString(url); + url = Plugin_CopyString(pdispparams->rgvarg[1].bstrVal); + + callingThreadId = GetCurrentThreadId(); + callingThreadHandle = DuplicateCurrentThread(); + + if (!jnetbuf) + { + time_t now = 0; + + jnetbuf = buffer_map[url] = new Buffer_GrowBuf; + + if (isBlocking) + { + AddRef(); // need to do this beacuse PostProcedure calls Release + PostProcedure(); // Call it directly, block until done. + } + else + { + // Launch the thread + DWORD threadId; + AddRef(); // make sure the jnetcom doesn't get deleted before the thread launches + jnetThread = CreateThread(NULL, NULL, jnetPostProcedure, (void *)this, NULL, &threadId); + } + + } +} + -- cgit