winamp/Src/Plugins/Portable/pmp_wifi/post.cpp
2024-09-24 14:54:57 +02:00

519 lines
13 KiB
C++

#include "main.h"
#include "api.h"
#include "resource.h"
#include "../xml/obj_xml.h"
#include "nu/AutoChar.h"
#include "../nu/AutoUrl.h"
#include "../nu/AutoHeader.h"
#include "../../..\Components\wac_network\wac_network_http_receiver_api.h"
#include "../agave/albumart/svc_albumartprovider.h"
#include <api/service/waservicefactory.h>
#include <shlwapi.h>
#include <strsafe.h>
static const GUID internetConfigGroupGUID =
{
0xc0a565dc, 0xcfe, 0x405a, { 0xa2, 0x7c, 0x46, 0x8b, 0xc, 0x8a, 0x3a, 0x5c }
};
#define USER_AGENT_SIZE (10 /*User-Agent*/ + 2 /*: */ + 6 /*Winamp*/ + 1 /*/*/ + 1 /*5*/ + 3/*.21*/ + 1 /*Null*/)
static 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);
}
#define HTTP_BUFFER_SIZE 8192
#define POST_BUFFER_SIZE (128*1024)
int PostFile(const char *base_url, const wchar_t *filename, const itemRecordW *track, obj_xml *parser, int *killswitch,
void (*callback)(void *callbackContext, wchar_t *status), void *context, char *new_item_id, size_t new_item_id_len)
{
//if (!parser)
// return 1;
bool first=true;
char url[2048] = {0};
char *p_url=url;
size_t url_cch=sizeof(url)/sizeof(*url);
FILE *f = _wfopen(filename, L"rb");
if (!f)
return 1;
api_httpreceiver *http = 0;
waServiceFactory *sf = plugin.service->service_getServiceByGuid(httpreceiverGUID);
if (sf)
http = (api_httpreceiver *)sf->getInterface();
if (!http)
return 1;
int use_proxy = 1;
bool proxy80 = AGAVE_API_CONFIG->GetBool(internetConfigGroupGUID, L"proxy80", false);
if (proxy80 && strstr(url, ":") && (!strstr(url, ":80/") && strstr(url, ":80") != (url + strlen(url) - 3)))
use_proxy = 0;
const wchar_t *proxy = use_proxy?AGAVE_API_CONFIG->GetString(internetConfigGroupGUID, L"proxy", 0):0;
fseek(f, 0, SEEK_END);
size_t clen = ftell(f);
size_t transferred=0;
size_t total_clen = clen;
fseek(f, 0, SEEK_SET);
http->open(API_DNS_AUTODNS, HTTP_BUFFER_SIZE, (proxy && proxy[0]) ? (const char *)AutoChar(proxy) : NULL);
http->set_sendbufsize(POST_BUFFER_SIZE);
SetUserAgent(http);
char data[POST_BUFFER_SIZE] = {0};
StringCbCopyExA(p_url, url_cch, base_url, &p_url, &url_cch, 0);
StringCbPrintfA(data, sizeof(data), "Content-Length: %u", clen);
http->addheader(data);
// http->addheader("Content-Type: application/octet-stream");
/* send metadata */
if (track->artist && track->artist[0])
{
if (first)
StringCbPrintfExA(p_url, url_cch, &p_url, &url_cch, 0, "?artist=%s", AutoUrl(track->artist));
else
StringCbPrintfExA(p_url, url_cch, &p_url, &url_cch, 0, "&artist=%s", AutoUrl(track->artist));
first=false;
}
if (track->title && track->title[0])
{
if (first)
StringCbPrintfExA(p_url, url_cch, &p_url, &url_cch, 0, "?title=%s", AutoUrl(track->title));
else
StringCbPrintfExA(p_url, url_cch, &p_url, &url_cch, 0, "&title=%s", AutoUrl(track->title));
first=false;
}
if (track->album && track->album[0])
{
if (first)
StringCbPrintfExA(p_url, url_cch, &p_url, &url_cch, 0, "?album=%s", AutoUrl(track->album));
else
StringCbPrintfExA(p_url, url_cch, &p_url, &url_cch, 0, "&album=%s", AutoUrl(track->album));
first=false;
}
if (track->composer && track->composer[0])
{
if (first)
StringCbPrintfExA(p_url, url_cch, &p_url, &url_cch, 0, "?composer=%s", AutoUrl(track->composer));
else
StringCbPrintfExA(p_url, url_cch, &p_url, &url_cch, 0, "&composer=%s", AutoUrl(track->composer));
first=false;
}
if (track->albumartist && track->albumartist[0])
{
if (first)
StringCbPrintfExA(p_url, url_cch, &p_url, &url_cch, 0, "?albumartist=%s", AutoUrl(track->albumartist));
else
StringCbPrintfExA(p_url, url_cch, &p_url, &url_cch, 0, "&albumartist=%s", AutoUrl(track->albumartist));
first=false;
}
if (track->genre && track->genre[0])
{
if (first)
StringCbPrintfExA(p_url, url_cch, &p_url, &url_cch, 0, "?genre=%s", AutoUrl(track->genre));
else
StringCbPrintfExA(p_url, url_cch, &p_url, &url_cch, 0, "&genre=%s", AutoUrl(track->genre));
first=false;
}
if (track->track > 0)
{
if (first)
StringCbPrintfExA(p_url, url_cch, &p_url, &url_cch, 0, "?track=%d", track->track);
else
StringCbPrintfExA(p_url, url_cch, &p_url, &url_cch, 0, "&track=%d", track->track);
first=false;
}
const wchar_t *ext = PathFindExtension(filename);
if (ext && ext[0])
{
if (ext[0] == '.') ext++;
if (ext[0])
{
if (first)
StringCbPrintfExA(p_url, url_cch, &p_url, &url_cch, 0, "?extension=%s", AutoUrl(ext));
else
StringCbPrintfExA(p_url, url_cch, &p_url, &url_cch, 0, "&extension=%s", AutoUrl(ext));
first=false;
}
}
wchar_t mime_type[128] = {0};
if (AGAVE_API_METADATA->GetExtendedFileInfo(filename, L"mime", mime_type, 128) == 1 && mime_type[0])
{
http->AddHeaderValue("Content-Type", AutoHeader(mime_type));
}
http->AddHeaderValue("X-Winamp-ID", winamp_id_str);
http->AddHeaderValue("X-Winamp-Name", winamp_name);
http->AddHeaderValue("Expect", "100-continue");
/* connect */
callback(context, WASABI_API_LNGSTRINGW(IDS_CONNECTING));
http->connect(url, 0, "POST");
// spin and wait for a 100 response
for (;;)
{
Sleep(55);
if (*killswitch)
goto connection_failed;
int ret = http->run();
if (ret != HTTPRECEIVER_RUN_OK) // connection failed or closed
goto connection_failed;
int reply_code = http->getreplycode();
if (reply_code == 100)
break;
else if (reply_code)
goto connection_failed;
}
/* POST the data */
api_connection *connection = http->GetConnection();
if (connection)
{
if (http->run() == -1)
goto connection_failed;
while (clen)
{
int percent = MulDiv(100, (int)transferred, (int)total_clen);
wchar_t msg[128] = {0};
StringCbPrintfW(msg, sizeof(msg), WASABI_API_LNGSTRINGW(IDS_UPLOADING), percent);
callback(context, msg);
if (*killswitch)
goto connection_failed;
if (http->run() == -1)
goto connection_failed;
int connection_state = connection->get_state();
if (connection_state == CONNECTION_STATE_CLOSED || connection_state == CONNECTION_STATE_ERROR)
goto connection_failed;
size_t lengthToSend = min(clen, connection->GetSendBytesAvailable());
lengthToSend = min(lengthToSend, sizeof(data));
if (lengthToSend)
{
int bytes_read = (int)fread(data, 1, lengthToSend, f);
connection->send(data, bytes_read);
clen-=bytes_read;
transferred+=bytes_read;
}
else
{
Sleep(10);
}
}
int x;
while (x = (int)connection->GetSendBytesInQueue())
{
int connection_state = connection->get_state();
if (connection_state == CONNECTION_STATE_CLOSED || connection_state == CONNECTION_STATE_ERROR)
goto connection_failed;
Sleep(10);
if (*killswitch)
goto connection_failed;
if (http->run() == -1)
goto connection_failed;
}
}
fclose(f);
f=0;
/* retrieve reply */
int ret;
do
{
Sleep(55);
ret = http->run();
if (ret == -1) // connection failed
break;
// ---- check our reply code ----
int status = http->get_status();
switch (status)
{
case HTTPRECEIVER_STATUS_CONNECTING:
case HTTPRECEIVER_STATUS_READING_HEADERS:
break;
case HTTPRECEIVER_STATUS_READING_CONTENT:
{
const char *location = http->getheader("Location");
if (location)
StringCchCopyA(new_item_id, new_item_id_len, location);
else
new_item_id[0]=0;
sf->releaseInterface(http);
return 0;
}
break;
case HTTPRECEIVER_STATUS_ERROR:
default:
sf->releaseInterface(http);
return 1;
}
}
while (ret == HTTPRECEIVER_RUN_OK);
connection_failed:
if (f)
fclose(f);
sf->releaseInterface(http);
return 1;
}
int PostAlbumArt(const char *url, const itemRecordW *track, obj_xml *parser, int *killswitch, void (*callback)(void *callbackContext, wchar_t *status), void *context)
{
void *artData=0;
size_t datalen=0;
wchar_t *mimeType=0;
if (AGAVE_API_ALBUMART->GetAlbumArtData(track->filename, L"cover", &artData, &datalen, &mimeType) != ALBUMART_SUCCESS)
return 1;
api_httpreceiver *http = 0;
waServiceFactory *sf = plugin.service->service_getServiceByGuid(httpreceiverGUID);
if (sf)
http = (api_httpreceiver *)sf->getInterface();
if (!http)
return 1;
int use_proxy = 1;
bool proxy80 = AGAVE_API_CONFIG->GetBool(internetConfigGroupGUID, L"proxy80", false);
if (proxy80 && strstr(url, ":") && (!strstr(url, ":80/") && strstr(url, ":80") != (url + strlen(url) - 3)))
use_proxy = 0;
const wchar_t *proxy = use_proxy?AGAVE_API_CONFIG->GetString(internetConfigGroupGUID, L"proxy", 0):0;
uint8_t *artDataPtr=(uint8_t *)artData;
size_t clen = datalen;
size_t transferred=0;
size_t total_clen = datalen;
http->open(API_DNS_AUTODNS, HTTP_BUFFER_SIZE, (proxy && proxy[0]) ? (const char *)AutoChar(proxy) : NULL);
http->set_sendbufsize(POST_BUFFER_SIZE);
SetUserAgent(http);
char data[POST_BUFFER_SIZE] = {0};
StringCbPrintfA(data, sizeof(data), "Content-Length: %u", datalen);
http->addheader(data);
if (mimeType)
{
StringCbPrintfA(data, sizeof(data), "Content-Type: %s", AutoHeader(mimeType));
http->addheader(data);
}
http->AddHeaderValue("X-Winamp-ID", winamp_id_str);
http->AddHeaderValue("X-Winamp-Name", winamp_name);
/* connect */
http->AddHeaderValue("Expect", "100-continue");
callback(context, WASABI_API_LNGSTRINGW(IDS_CONNECTING));
http->connect(url, 0, "POST");
// spin and wait for a 100 response
for (;;)
{
Sleep(55);
int ret = http->run();
if (ret != HTTPRECEIVER_RUN_OK) // connection failed or closed
goto connection_failed;
if (*killswitch)
goto connection_failed;
int reply_code = http->getreplycode();
if (reply_code == 100)
break;
else if (reply_code)
goto connection_failed;
}
/* POST the data */
api_connection *connection = http->GetConnection();
if (connection)
{
if (http->run() == -1)
goto connection_failed;
while (clen)
{
int percent = MulDiv(100, (int)transferred, (int)total_clen);
wchar_t msg[128] = {0};
StringCbPrintfW(msg, sizeof(msg), L"Uploading Album Art (%d%%)", percent);
callback(context, msg);
if (*killswitch)
goto connection_failed;
if (http->run() == -1)
goto connection_failed;
int connection_state = connection->get_state();
if (connection_state == CONNECTION_STATE_CLOSED || connection_state == CONNECTION_STATE_ERROR)
goto connection_failed;
size_t lengthToSend = min(clen, connection->GetSendBytesAvailable());
if (lengthToSend)
{
connection->send(artDataPtr, (int)lengthToSend);
artDataPtr += lengthToSend;
clen-=lengthToSend;
transferred+=lengthToSend;
}
else
{
Sleep(10);
}
}
int x;
while (x = (int)connection->GetSendBytesInQueue())
{
int connection_state = connection->get_state();
if (connection_state == CONNECTION_STATE_CLOSED || connection_state == CONNECTION_STATE_ERROR)
goto connection_failed;
Sleep(10);
if (*killswitch)
goto connection_failed;
if (http->run() == -1)
goto connection_failed;
}
}
/* retrieve reply */
int ret;
do
{
Sleep(55);
ret = http->run();
if (ret == -1) // connection failed
break;
// ---- check our reply code ----
int status = http->get_status();
switch (status)
{
case HTTPRECEIVER_STATUS_CONNECTING:
case HTTPRECEIVER_STATUS_READING_HEADERS:
break;
case HTTPRECEIVER_STATUS_READING_CONTENT:
{
sf->releaseInterface(http);
WASABI_API_MEMMGR->sysFree(artData);
WASABI_API_MEMMGR->sysFree(mimeType);
return 0;
}
break;
case HTTPRECEIVER_STATUS_ERROR:
default:
sf->releaseInterface(http);
WASABI_API_MEMMGR->sysFree(artData);
WASABI_API_MEMMGR->sysFree(mimeType);
return 1;
}
}
while (ret == HTTPRECEIVER_RUN_OK);
connection_failed:
WASABI_API_MEMMGR->sysFree(artData);
WASABI_API_MEMMGR->sysFree(mimeType);
sf->releaseInterface(http);
return 1;
}
int HTTP_Delete(const char *url)
{
api_httpreceiver *http = 0;
waServiceFactory *sf = plugin.service->service_getServiceByGuid(httpreceiverGUID);
if (sf)
http = (api_httpreceiver *)sf->getInterface();
if (!http)
return 1;
int use_proxy = 1;
bool proxy80 = AGAVE_API_CONFIG->GetBool(internetConfigGroupGUID, L"proxy80", false);
if (proxy80 && strstr(url, ":") && (!strstr(url, ":80/") && strstr(url, ":80") != (url + strlen(url) - 3)))
use_proxy = 0;
const wchar_t *proxy = use_proxy?AGAVE_API_CONFIG->GetString(internetConfigGroupGUID, L"proxy", 0):0;
http->open(API_DNS_AUTODNS, HTTP_BUFFER_SIZE, (proxy && proxy[0]) ? (const char *)AutoChar(proxy) : NULL);
SetUserAgent(http);
http->AddHeaderValue("X-Winamp-ID", winamp_id_str);
http->AddHeaderValue("X-Winamp-Name", winamp_name);
/* connect */
http->connect(url, 0, "DELETE");
/* retrieve reply */
int ret;
do
{
Sleep(55);
ret = http->run();
if (ret == -1) // connection failed
break;
// ---- check our reply code ----
int status = http->get_status();
switch (status)
{
case HTTPRECEIVER_STATUS_CONNECTING:
case HTTPRECEIVER_STATUS_READING_HEADERS:
break;
case HTTPRECEIVER_STATUS_READING_CONTENT:
{
sf->releaseInterface(http);
return 0;
}
break;
case HTTPRECEIVER_STATUS_ERROR:
default:
sf->releaseInterface(http);
return 1;
}
}
while (ret == HTTPRECEIVER_RUN_OK);
sf->releaseInterface(http);
return 1;
}