519 lines
13 KiB
C++
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;
|
|
}
|
|
|