#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 #include #include 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; }