winamp/Src/Plugins/DSP/sc_serv3/updater.cpp
2024-09-24 14:54:57 +02:00

354 lines
11 KiB
C++

#ifdef _WIN32
#include <winsock2.h>
#endif
#include <stdio.h>
#include "updater.h"
#include "file/fileUtils.h"
#include "stl/stringUtils.h"
#include "aolxml/aolxml.h"
#include "bandwidth.h"
#include "services/stdServiceImpl.h"
#include "./versions.h"
using namespace std;
using namespace uniString;
using namespace stringUtil;
static AOL_namespace::mutex g_UpdaterLock;
static updater *g_Updater = 0;
#define DEBUG_LOG(...) do { if (gOptions.yp2Debug()) DLOG(__VA_ARGS__); } while (0)
///////////////////////////////////////////////////////////////////////////////////////////
void updater::updaterBandWidthSent(webClient::request r) throw()
{
// this effectively works out the size in the same manner
// as webclient::toRequest(..) does to build the request.
size_t total = r.m_content.size() + r.m_contentType.size() +
// 'POST' or 'GET' with '?' on the front
(r.m_method == webClient::request::POST ? 4 : 3) +
64 + r.m_addr.size() + g_userAgent.size() +
(!r.m_content.empty() ? r.m_content.size() : 0);
for (httpHeaderMap_t::const_iterator i = r.m_queryVariables.begin(); i != r.m_queryVariables.end(); ++i)
{
if (i != r.m_queryVariables.begin())
{
++total;
}
total += urlUtils::escapeURI_RFC3986((*i).first).size();
if (!(*i).second.empty())
{
total += 1 + urlUtils::escapeURI_RFC3986((*i).second).size();
}
}
if (!r.m_XFF.empty())
{
total += 18 + r.m_XFF.size();
}
if ((r.m_method == webClient::request::POST) && r.m_content.empty())
{
total += 48;
}
else if (!r.m_contentType.empty())
{
total += 15 + r.m_contentType.size();
}
bandWidth::updateAmount(bandWidth::PRIVATE_WEB, total);
}
void updater::updaterBandWidthReceived(const response r) throw()
{
size_t total = r.m_body.size();
for (httpHeaderMap_t::const_iterator i = r.m_headers.begin(); i != r.m_headers.end(); ++i)
{
total += (*i).first.size() + 1 + (*i).second.size() + eol().size();
}
bandWidth::updateAmount(bandWidth::PRIVATE_WEB, total);
}
size_t updater::requestsInQueue() throw()
{
stackLock sml(g_UpdaterLock);
return (g_Updater ? g_Updater->queueEntries() : 0);
}
updater::updater() throw() : webClient(UPDATER_LOGNAME)
{
stackLock sml(g_UpdaterLock);
g_Updater = this;
m_running = false;
}
updater::~updater() throw()
{
stackLock sml(g_UpdaterLock);
g_Updater = 0;
}
/////////// response handling //////////////
// handle response. retry_exceptions do just that. otherwise retry occurs in yptimeout seconds
void updater::response_updater(const request & /*q*/, const response &r) throw(exception)
{
FILE *f = uniFile::fopen(g_Updater->m_verInfo.fn, "wb");
if (f)
{
if (fwrite(&(r.m_body[0]),1,r.m_body.size(),f) == r.m_body.size())
{
ILOG(UPDATER_LOGNAME "Downloaded update to `" + g_Updater->m_verInfo.fn + "' [" + tos(r.m_body.size()) + " bytes]");
g_Updater->m_verInfo.downloaded = 1;
g_Updater->m_verInfo.needsUpdating = 0;
}
else
{
ILOG(UPDATER_LOGNAME "Error saving the update to `" + g_Updater->m_verInfo.fn + "' [" + tos(r.m_body.size()) + " bytes]");
g_Updater->m_verInfo.fn.clear();
g_Updater->m_verInfo.needsUpdating = 1;
}
::fclose(f);
}
else
{
FILE *f = uniFile::fopen(g_Updater->m_verInfo.fn_alt, "wb");
if (f)
{
if (fwrite(&(r.m_body[0]),1,r.m_body.size(),f) == r.m_body.size())
{
g_Updater->m_verInfo.fn = g_Updater->m_verInfo.fn_alt;
ILOG(UPDATER_LOGNAME "Downloaded update to `" + g_Updater->m_verInfo.fn + "' [" + tos(r.m_body.size()) + " bytes]");
g_Updater->m_verInfo.downloaded = 1;
g_Updater->m_verInfo.needsUpdating = 0;
}
else
{
ILOG(UPDATER_LOGNAME "Error saving the update to `" + g_Updater->m_verInfo.fn_alt + "' [" + tos(r.m_body.size()) + " bytes]");
g_Updater->m_verInfo.fn.clear();
g_Updater->m_verInfo.fn_alt.clear();
g_Updater->m_verInfo.needsUpdating = 1;
}
::fclose(f);
}
else
{
ELOG(UPDATER_LOGNAME "Error creating file: " + errMessage());
g_Updater->m_verInfo.fn.clear();
g_Updater->m_verInfo.fn_alt.clear();
g_Updater->m_verInfo.needsUpdating = 1;
}
}
}
void updater::gotResponse(const request &q, const response &r) throw(exception)
{
stackLock sml(m_serverMapLock);
DEBUG_LOG(UPDATER_LOGNAME + string(__FUNCTION__) + eol() +
"Response body=[" + eol() + utf8(r.m_body.begin(),r.m_body.end()) + "]" +
eol() + "Response code=[" + tos(r.m_resultCode) + "]");
updaterBandWidthReceived(r);
response_updater(q, r);
m_running = false;
}
void updater::gotFailure(const request &/*q*/) throw(std::exception)
{
stackLock sml(g_UpdaterLock);
DEBUG_LOG(UPDATER_LOGNAME + string(__FUNCTION__));
m_running = false;
}
void updater::updateVersion() throw()
{
if (g_Updater)
{
// make sure we've got it and also an url
if (g_Updater->m_verInfo.needsUpdating && !g_Updater->m_verInfo.url.empty() &&
!g_Updater->m_verInfo.fn.empty() && !g_Updater->m_verInfo.fn_alt.empty())
{
try
{
g_Updater->pvt_downloadUpdate();
}
catch(...)
{
}
}
}
}
void updater::pvt_downloadUpdate() throw(exception)
{
if (!m_running)
{
m_running = true;
ILOG(UPDATER_LOGNAME "Preparing to download update package...");
// build request
webClient::request r;
r.m_method = webClient::request::GET;
config::streamConfig::urlObj downloadUrl(g_Updater->m_verInfo.url.hideAsString());
r.m_addr = downloadUrl.server();
r.m_port = downloadUrl.port();
r.m_path = downloadUrl.path();
r.m_nonBlocking = 1;
updaterBandWidthSent(r);
queueRequest(r);
}
}
bool updater::getNewVersion(verInfo &ver) throw()
{
stackLock sml(g_UpdaterLock);
if (g_Updater)
{
// double-check that we've got things correctly
// before we provide the current update status.
bool ret = g_Updater->setNewVersion(g_Updater->m_verInfo, true);
ver = g_Updater->m_verInfo;
return ret;
}
return false;
}
bool updater::setNewVersion(verInfo &ver, bool no_lock) throw()
{
if (!no_lock)
{
stackLock sml(g_UpdaterLock);
}
if (g_Updater)
{
if (g_Updater->m_verInfo.ver != ver.ver ||
g_Updater->m_verInfo.url != ver.url ||
g_Updater->m_verInfo.log != ver.log)
{
g_Updater->m_verInfo = ver;
if (!g_Updater->m_verInfo.ver.empty())
{
const std::vector<uniString::utf8> newVerStr = tokenizer(g_Updater->m_verInfo.ver,'.'),
curVerStr = tokenizer(gOptions.getVersionBuildStrings(),'.');
int newVer[] = {newVerStr[0].toInt(), newVerStr[1].toInt(), newVerStr[2].toInt(), newVerStr[3].toInt()},
curVer[] = {curVerStr[0].toInt(), curVerStr[1].toInt(), curVerStr[2].toInt(), curVerStr[3].toInt()};
g_Updater->m_verInfo.needsUpdating = 0;
// look to compare from major to minor parts of the version strings
// 2.x.x.x vs 3.x.x.x
if (newVer[0] > curVer[0])
{
g_Updater->m_verInfo.needsUpdating = 1;
}
// 2.0.x.x vs 2.2.x.x
else if ((newVer[0] == curVer[0]) && (newVer[1] > curVer[1]))
{
g_Updater->m_verInfo.needsUpdating = 1;
}
// 2.0.0.x vs 2.0.1.x
else if ((newVer[0] == curVer[0]) && (newVer[1] == curVer[1]) && (newVer[2] > curVer[2]))
{
g_Updater->m_verInfo.needsUpdating = 1;
}
// 2.0.0.29 vs 2.0.0.30
else if ((newVer[0] == curVer[0]) && (newVer[1] == curVer[1]) && (newVer[2] == curVer[2]) && (newVer[3] > curVer[3]))
{
g_Updater->m_verInfo.needsUpdating = 1;
}
if (g_Updater->m_verInfo.needsUpdating)
{
#ifdef _WIN32
g_Updater->m_verInfo.fn = gStartupDirectory + "sc_serv_update_" SERV_UPDATE_NAME "_" + tos(newVer[0]) + "_" + tos(newVer[1]) + "_" + tos(newVer[2]) + "_" + tos(newVer[3]) + ".exe";
wchar_t m_fileName[MAX_PATH] = {0};
uniString::utf32 u32("%temp%\\sc_serv_update_" SERV_UPDATE_NAME "_" + tos(newVer[0]) + "_" + tos(newVer[1]) + "_" + tos(newVer[2]) + "_" + tos(newVer[3]) + ".exe");
std::wstring u16;
u32.toUtf16(u16);
ExpandEnvironmentStringsW(u16.c_str(), m_fileName, MAX_PATH);
g_Updater->m_verInfo.fn_alt = utf32(m_fileName).toUtf8();
#else
g_Updater->m_verInfo.fn = gStartupDirectory + "sc_serv_update_" SERV_UPDATE_NAME"_" + tos(newVer[0]) + "_" + tos(newVer[1]) + "_" + tos(newVer[2]) + "_" + tos(newVer[3]) + ".tar.gz";
g_Updater->m_verInfo.fn_alt = "/tmp/sc_serv_update_" SERV_UPDATE_NAME"_" + tos(newVer[0]) + "_" + tos(newVer[1]) + "_" + tos(newVer[2]) + "_" + tos(newVer[3]) + ".tar.gz";
#endif
g_Updater->m_verInfo.downloaded = uniFile::fileExists(g_Updater->m_verInfo.fn);
// if the main file cannot be found, look for the alternate file
if (!g_Updater->m_verInfo.downloaded)
{
g_Updater->m_verInfo.downloaded = uniFile::fileExists(g_Updater->m_verInfo.fn_alt);
if (g_Updater->m_verInfo.downloaded)
{
g_Updater->m_verInfo.fn = g_Updater->m_verInfo.fn_alt;
}
}
}
ULOG(string(YP2_LOGNAME) + "A new DNAS version is now available: " + g_Updater->m_verInfo.ver);
ULOG(string(YP2_LOGNAME) + "The suggested download for your setup is: " + g_Updater->m_verInfo.url);
ULOG(string(YP2_LOGNAME) + "See " + g_Updater->m_verInfo.log + " for more information about this update and alternative download links");
if (!g_Updater->m_verInfo.downloaded && !g_Updater->m_verInfo.fn.empty() && !g_Updater->m_verInfo.fn_alt.empty())
{
g_Updater->updateVersion();
}
}
}
else
{
std::vector<uniString::utf8> newVerStr = tokenizer(g_Updater->m_verInfo.ver,'.');
if (newVerStr.size() == 4)
{
int newVer[] = {newVerStr[0].toInt(), newVerStr[1].toInt(), newVerStr[2].toInt(), newVerStr[3].toInt()};
#ifdef _WIN32
g_Updater->m_verInfo.fn = gStartupDirectory + "sc_serv_update_" SERV_UPDATE_NAME "_" + tos(newVer[0]) + "_" + tos(newVer[1]) + "_" + tos(newVer[2]) + "_" + tos(newVer[3]) + ".exe";
wchar_t m_fileName[MAX_PATH] = {0};
uniString::utf32 u32("%temp%\\sc_serv_update_" SERV_UPDATE_NAME "_" + tos(newVer[0]) + "_" + tos(newVer[1]) + "_" + tos(newVer[2]) + "_" + tos(newVer[3]) + ".exe");
std::wstring u16;
u32.toUtf16(u16);
ExpandEnvironmentStringsW(u16.c_str(), m_fileName, MAX_PATH);
g_Updater->m_verInfo.fn_alt = utf32(m_fileName).toUtf8();
#else
g_Updater->m_verInfo.fn = gStartupDirectory + "sc_serv_update_" SERV_UPDATE_NAME"_" + tos(newVer[0]) + "_" + tos(newVer[1]) + "_" + tos(newVer[2]) + "_" + tos(newVer[3]) + ".tar.gz";
g_Updater->m_verInfo.fn_alt = "/tmp/sc_serv_update_" SERV_UPDATE_NAME"_" + tos(newVer[0]) + "_" + tos(newVer[1]) + "_" + tos(newVer[2]) + "_" + tos(newVer[3]) + ".tar.gz";
#endif
g_Updater->m_verInfo.downloaded = uniFile::fileExists(g_Updater->m_verInfo.fn);
// if the main file cannot be found, look for the alternate file
if (!g_Updater->m_verInfo.downloaded)
{
g_Updater->m_verInfo.downloaded = uniFile::fileExists(g_Updater->m_verInfo.fn_alt);
if (g_Updater->m_verInfo.downloaded)
{
g_Updater->m_verInfo.fn = g_Updater->m_verInfo.fn_alt;
}
}
g_Updater->m_verInfo.needsUpdating = 1;
if (!g_Updater->m_verInfo.downloaded && !g_Updater->m_verInfo.fn.empty())
{
g_Updater->updateVersion();
}
}
}
return (!g_Updater->m_verInfo.ver.empty() && (g_Updater->m_verInfo.needsUpdating || (g_Updater->m_verInfo.downloaded && !g_Updater->m_verInfo.fn.empty())));
}
return false;
}