mirror of
https://github.com/cookiengineer/audacity
synced 2025-04-29 23:29:41 +02:00
💩 Remove Update Check
This commit is contained in:
parent
bbf352d36d
commit
5728dd542f
@ -151,10 +151,6 @@ cmd_option( ${_OPT}obey_system_dependencies
|
||||
Off
|
||||
)
|
||||
|
||||
cmd_option( ${_OPT}has_networking
|
||||
"Build networking features into Audacity"
|
||||
Off)
|
||||
|
||||
include( AudacityDependencies )
|
||||
|
||||
# Pull all the modules we'll need
|
||||
@ -170,24 +166,6 @@ include( CMakePushCheckState )
|
||||
include( GNUInstallDirs )
|
||||
include( TestBigEndian )
|
||||
|
||||
set_from_env(CRASH_REPORT_URL)
|
||||
|
||||
cmake_dependent_option(
|
||||
${_OPT}has_crashreports
|
||||
"Enables crash reporting for Audacity"
|
||||
On
|
||||
"${_OPT}has_networking;DEFINED CRASH_REPORT_URL"
|
||||
Off
|
||||
)
|
||||
|
||||
cmake_dependent_option(
|
||||
${_OPT}has_updates_check
|
||||
"Build updates checking features into Audacity"
|
||||
On
|
||||
"${_OPT}has_networking"
|
||||
Off
|
||||
)
|
||||
|
||||
# Determine 32-bit or 64-bit target
|
||||
if( CMAKE_C_COMPILER_ID MATCHES "MSVC" AND CMAKE_VS_PLATFORM_NAME MATCHES "Win64|x64" )
|
||||
set( IS_64BIT ON )
|
||||
|
@ -120,23 +120,6 @@ add_conan_lib(
|
||||
ALWAYS_ALLOW_CONAN_FALLBACK
|
||||
)
|
||||
|
||||
if( ${_OPT}has_networking )
|
||||
|
||||
add_conan_lib(
|
||||
CURL
|
||||
libcurl/7.75.0
|
||||
REQUIRED
|
||||
OPTION_NAME curl
|
||||
PKG_CONFIG "libcurl >= 7.68.0"
|
||||
INTERFACE_NAME CURL::libcurl
|
||||
FIND_PACKAGE_OPTIONS
|
||||
CONAN_OPTIONS
|
||||
libcurl:with_ssl=${curl_ssl}
|
||||
libcurl:shared=True
|
||||
)
|
||||
|
||||
endif()
|
||||
|
||||
if( NOT CMAKE_SYSTEM_NAME MATCHES "Darwin|Windows")
|
||||
add_conan_lib(
|
||||
libuuid
|
||||
|
@ -1,43 +0,0 @@
|
||||
set(TARGET lib-network-manager)
|
||||
set( TARGET_ROOT ${CMAKE_CURRENT_SOURCE_DIR} )
|
||||
|
||||
def_vars()
|
||||
|
||||
set( SOURCES
|
||||
IResponse.h
|
||||
IResponseFactory.h
|
||||
|
||||
HeadersList.h
|
||||
HeadersList.cpp
|
||||
|
||||
CookiesList.h
|
||||
CookiesList.cpp
|
||||
|
||||
Request.h
|
||||
Request.cpp
|
||||
|
||||
NetworkManager.h
|
||||
NetworkManager.cpp
|
||||
|
||||
curl/CurlResponse.h
|
||||
curl/CurlResponse.cpp
|
||||
curl/CurlResponseFactory.h
|
||||
curl/CurlResponseFactory.cpp
|
||||
curl/CurlStringList.h
|
||||
curl/CurlStringList.cpp
|
||||
curl/CurlHandleManager.h
|
||||
curl/CurlHandleManager.cpp
|
||||
)
|
||||
|
||||
|
||||
set ( LIBRARIES PRIVATE
|
||||
CURL::libcurl
|
||||
ThreadPool::ThreadPool
|
||||
lib-string-utils
|
||||
wxwidgets::base
|
||||
)
|
||||
|
||||
set ( DEFINES INTERFACE "HAS_NETWORKING" )
|
||||
|
||||
|
||||
audacity_library( ${TARGET} "${SOURCES}" "${LIBRARIES}" "${DEFINES}" "" )
|
@ -1,215 +0,0 @@
|
||||
/*!********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
@file CookiesList.cpp
|
||||
@brief Define HTTP cookies list class.
|
||||
|
||||
Dmitry Vedenko
|
||||
**********************************************************************/
|
||||
|
||||
/*!********************************************************************
|
||||
|
||||
@class Cookie
|
||||
@brief A struct, representing a Cookie object.
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
/*!********************************************************************
|
||||
|
||||
@class CookiesList
|
||||
@brief A class, representing a list of HTTP cookies.
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#include "CookiesList.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
|
||||
#include "DateTimeConversions.h"
|
||||
|
||||
namespace audacity
|
||||
{
|
||||
namespace network_manager
|
||||
{
|
||||
|
||||
Cookie Cookie::Parse (const std::string& cookieString)
|
||||
{
|
||||
const size_t equalsPosition = cookieString.find ('=');
|
||||
|
||||
if (equalsPosition == std::string::npos)
|
||||
return { cookieString, std::string () };
|
||||
|
||||
std::string name = cookieString.substr (0, equalsPosition);
|
||||
|
||||
const size_t firstValueIndex = equalsPosition + 1;
|
||||
|
||||
const size_t semicolonPosition = cookieString.find (';', firstValueIndex);
|
||||
|
||||
Cookie cookie;
|
||||
|
||||
cookie.Name = std::move (name);
|
||||
cookie.Value = semicolonPosition == std::string::npos ?
|
||||
cookieString.substr (firstValueIndex) :
|
||||
cookieString.substr (firstValueIndex, semicolonPosition - firstValueIndex);
|
||||
|
||||
size_t expiresPosition = cookieString.find("Expires=");
|
||||
|
||||
if (expiresPosition != std::string::npos)
|
||||
{
|
||||
expiresPosition += std::strlen ("Expires=");
|
||||
|
||||
const size_t trailingSemicolon = cookieString.find (';', expiresPosition);
|
||||
|
||||
std::string expiresValue =
|
||||
trailingSemicolon == std::string::npos ?
|
||||
cookieString.substr (expiresPosition) :
|
||||
cookieString.substr (expiresPosition, trailingSemicolon - expiresPosition);
|
||||
|
||||
// Hack around Yandex violating RFC
|
||||
std::replace (expiresValue.begin(), expiresValue.end(), '-', ' ');
|
||||
ParseRFC822Date (expiresValue, &cookie.Expires);
|
||||
}
|
||||
|
||||
return cookie;
|
||||
}
|
||||
|
||||
bool Cookie::isSession () const noexcept
|
||||
{
|
||||
return Expires == ExpiresTime {};
|
||||
}
|
||||
|
||||
std::string Cookie::toString (bool fullString) const
|
||||
{
|
||||
std::ostringstream stream;
|
||||
|
||||
stream << Name << "=" << Value;
|
||||
|
||||
if (fullString)
|
||||
{
|
||||
if (!isSession())
|
||||
stream << "; " << "Expires=" << SerializeRFC822Date(Expires);
|
||||
}
|
||||
|
||||
return stream.str ();
|
||||
}
|
||||
|
||||
bool Cookie::isExpired () const noexcept
|
||||
{
|
||||
return !isSession() &&
|
||||
std::chrono::system_clock::now() >= Expires;
|
||||
}
|
||||
|
||||
void CookiesList::setCookie (const Cookie& cookie)
|
||||
{
|
||||
setCookie (cookie.Name, cookie.Value);
|
||||
}
|
||||
|
||||
void CookiesList::setCookie (const std::string& cookieName, std::string cookieValue)
|
||||
{
|
||||
Cookie* item = getCookie (cookieName);
|
||||
|
||||
if (item != nullptr)
|
||||
item->Value = std::move (cookieValue);
|
||||
else
|
||||
mCookies.push_back ({ cookieName, std::move (cookieValue) });
|
||||
}
|
||||
|
||||
void CookiesList::addCookie (Cookie cookie)
|
||||
{
|
||||
addCookie (std::move (cookie.Name), std::move (cookie.Value));
|
||||
}
|
||||
|
||||
void CookiesList::addCookie (std::string cookieName, std::string cookieValue)
|
||||
{
|
||||
mCookies.push_back ({ std::move (cookieName), std::move (cookieValue) });
|
||||
}
|
||||
|
||||
bool CookiesList::hasCookie (const std::string& cookieName) const noexcept
|
||||
{
|
||||
return getCookie (cookieName) != nullptr;
|
||||
}
|
||||
|
||||
std::string CookiesList::getCookieValue (const std::string& cookieName) const
|
||||
{
|
||||
const Cookie* item = getCookie (cookieName);
|
||||
|
||||
if (item != nullptr)
|
||||
return item->Value;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
const Cookie* CookiesList::getCookie (size_t idx) const noexcept
|
||||
{
|
||||
return const_cast<CookiesList*>(this)->getCookie(idx);
|
||||
}
|
||||
|
||||
const Cookie* CookiesList::getCookie (const std::string& name) const noexcept
|
||||
{
|
||||
return const_cast<CookiesList*>(this)->getCookie (name);
|
||||
}
|
||||
|
||||
size_t CookiesList::getCookiesCount () const noexcept
|
||||
{
|
||||
return mCookies.size ();
|
||||
}
|
||||
|
||||
std::string CookiesList::getCookiesString () const
|
||||
{
|
||||
std::string result;
|
||||
|
||||
for (const Cookie& cookie : mCookies)
|
||||
{
|
||||
if (!result.empty ())
|
||||
result.push_back (';');
|
||||
|
||||
result += cookie.Name + "=" + cookie.Value;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
CookiesList::CookiesIterator CookiesList::begin () noexcept
|
||||
{
|
||||
return mCookies.begin ();
|
||||
}
|
||||
|
||||
CookiesList::CookiesIterator CookiesList::end () noexcept
|
||||
{
|
||||
return mCookies.end ();
|
||||
}
|
||||
|
||||
CookiesList::CookiesConstIterator CookiesList::begin () const noexcept
|
||||
{
|
||||
return mCookies.begin ();
|
||||
}
|
||||
|
||||
CookiesList::CookiesConstIterator CookiesList::end () const noexcept
|
||||
{
|
||||
return mCookies.end ();
|
||||
}
|
||||
|
||||
Cookie* CookiesList::getCookie (size_t idx) noexcept
|
||||
{
|
||||
if (idx < mCookies.size ())
|
||||
return &mCookies[idx];
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Cookie* CookiesList::getCookie (const std::string& cookieName) noexcept
|
||||
{
|
||||
for (Cookie& cookie : mCookies)
|
||||
{
|
||||
if (cookie.Name == cookieName)
|
||||
return &cookie;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
/*!********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
@file CookiesList.h
|
||||
@brief Define HTTP cookies list class.
|
||||
|
||||
Dmitry Vedenko
|
||||
**********************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
|
||||
#include "NetworkManagerApi.h"
|
||||
|
||||
namespace audacity
|
||||
{
|
||||
namespace network_manager
|
||||
{
|
||||
|
||||
using ExpiresTime = std::chrono::system_clock::time_point;
|
||||
|
||||
struct NETWORK_MANAGER_API Cookie final
|
||||
{
|
||||
std::string Name;
|
||||
std::string Value;
|
||||
|
||||
ExpiresTime Expires {};
|
||||
|
||||
static Cookie Parse (const std::string& cookieString);
|
||||
|
||||
bool isSession () const noexcept;
|
||||
bool isExpired () const noexcept;
|
||||
|
||||
std::string toString(bool fullString) const;
|
||||
};
|
||||
|
||||
class NETWORK_MANAGER_API CookiesList final
|
||||
{
|
||||
using CookiesStorageType = std::vector<Cookie>;
|
||||
public:
|
||||
using CookiesIterator = CookiesStorageType::iterator;
|
||||
using CookiesConstIterator = CookiesStorageType::const_iterator;
|
||||
|
||||
void setCookie (const Cookie& cookie);
|
||||
void setCookie (const std::string& cookieName, std::string cookieValue);
|
||||
|
||||
void addCookie (Cookie cookie);
|
||||
void addCookie (std::string cookieName, std::string cookieValue);
|
||||
|
||||
bool hasCookie (const std::string& cookieName) const noexcept;
|
||||
|
||||
std::string getCookieValue (const std::string& cookieName) const;
|
||||
|
||||
const Cookie* getCookie (size_t idx) const noexcept;
|
||||
const Cookie* getCookie (const std::string& name) const noexcept;
|
||||
|
||||
size_t getCookiesCount () const noexcept;
|
||||
|
||||
std::string getCookiesString () const;
|
||||
|
||||
CookiesIterator begin () noexcept;
|
||||
CookiesIterator end () noexcept;
|
||||
|
||||
CookiesConstIterator begin () const noexcept;
|
||||
CookiesConstIterator end () const noexcept;
|
||||
private:
|
||||
Cookie* getCookie (size_t idx) noexcept;
|
||||
Cookie* getCookie (const std::string& cookieName) noexcept;
|
||||
|
||||
CookiesStorageType mCookies;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
@ -1,158 +0,0 @@
|
||||
/*!********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
@file HeadersList.cpp
|
||||
@brief Define HTTP headers list class.
|
||||
|
||||
Dmitry Vedenko
|
||||
**********************************************************************/
|
||||
|
||||
/*!********************************************************************
|
||||
|
||||
@class Header
|
||||
@brief A string pair, representing HTTP header.
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
/*!********************************************************************
|
||||
|
||||
@class HeadersList
|
||||
@brief A class, representing a list of HTTP headers.
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#include "HeadersList.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
|
||||
namespace audacity
|
||||
{
|
||||
namespace network_manager
|
||||
{
|
||||
|
||||
bool Header::hasSameName (const Header& header) const
|
||||
{
|
||||
return hasSameName (header.Name);
|
||||
}
|
||||
|
||||
bool Header::hasSameName (const std::string& name) const
|
||||
{
|
||||
return std::equal (
|
||||
name.begin (), name.end (),
|
||||
Name.begin (), Name.end (),
|
||||
[](const char leftChar, const char rightChar) {
|
||||
return std::tolower (leftChar) == std::tolower (rightChar);
|
||||
});
|
||||
}
|
||||
|
||||
Header Header::Parse (const std::string& header)
|
||||
{
|
||||
const size_t colonPosition = header.find (": ");
|
||||
|
||||
if (colonPosition == std::string::npos) // This can happen when we receieve the first line of the response
|
||||
return { header, std::string () };
|
||||
|
||||
return {
|
||||
header.substr (0, colonPosition),
|
||||
header.substr (colonPosition + 2)
|
||||
};
|
||||
}
|
||||
|
||||
void HeadersList::setHeader (const Header& header)
|
||||
{
|
||||
setHeader (header.Name, header.Value);
|
||||
}
|
||||
|
||||
void HeadersList::setHeader (const std::string& headerName, std::string headerValue)
|
||||
{
|
||||
Header* item = getHeader (headerName);
|
||||
|
||||
if (item != nullptr)
|
||||
item->Value = std::move (headerValue);
|
||||
else
|
||||
mHeaders.push_back ({ headerName, std::move (headerValue) });
|
||||
}
|
||||
|
||||
void HeadersList::addHeader (Header header)
|
||||
{
|
||||
addHeader (std::move (header.Name), std::move (header.Value));
|
||||
}
|
||||
|
||||
void HeadersList::addHeader (std::string headerName, std::string headerValue)
|
||||
{
|
||||
mHeaders.push_back ({ std::move (headerName), std::move (headerValue) });
|
||||
}
|
||||
|
||||
bool HeadersList::hasHeader (const std::string& headerName) const noexcept
|
||||
{
|
||||
return getHeader (headerName) != nullptr;
|
||||
}
|
||||
|
||||
std::string HeadersList::getHeaderValue (const std::string& headerName) const
|
||||
{
|
||||
const Header* header = getHeader (headerName);
|
||||
|
||||
if (header != nullptr)
|
||||
return header->Value;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
const Header* HeadersList::getHeader (size_t idx) const noexcept
|
||||
{
|
||||
return const_cast<HeadersList*>(this)->getHeader (idx);
|
||||
}
|
||||
|
||||
const Header* HeadersList::getHeader (const std::string& name) const noexcept
|
||||
{
|
||||
return const_cast<HeadersList*>(this)->getHeader (name);
|
||||
}
|
||||
|
||||
size_t HeadersList::getHeadersCount () const noexcept
|
||||
{
|
||||
return mHeaders.size ();
|
||||
}
|
||||
|
||||
HeadersList::HeadersIterator HeadersList::begin () noexcept
|
||||
{
|
||||
return mHeaders.begin ();
|
||||
}
|
||||
|
||||
HeadersList::HeadersIterator HeadersList::end () noexcept
|
||||
{
|
||||
return mHeaders.end ();
|
||||
}
|
||||
|
||||
HeadersList::HeadersConstIterator HeadersList::begin () const noexcept
|
||||
{
|
||||
return mHeaders.begin ();
|
||||
}
|
||||
|
||||
HeadersList::HeadersConstIterator HeadersList::end () const noexcept
|
||||
{
|
||||
return mHeaders.end ();
|
||||
}
|
||||
|
||||
Header* HeadersList::getHeader (size_t idx) noexcept
|
||||
{
|
||||
if (idx < mHeaders.size ())
|
||||
return &mHeaders[idx];
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Header* HeadersList::getHeader (const std::string& headerName) noexcept
|
||||
{
|
||||
for (Header& header : mHeaders)
|
||||
{
|
||||
if (header.hasSameName (headerName))
|
||||
return &header;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
/*!********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
@file HeadersList.h
|
||||
@brief Declare HTTP headers list class.
|
||||
|
||||
Dmitry Vedenko
|
||||
**********************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "NetworkManagerApi.h"
|
||||
|
||||
namespace audacity
|
||||
{
|
||||
namespace network_manager
|
||||
{
|
||||
|
||||
struct NETWORK_MANAGER_API Header final
|
||||
{
|
||||
std::string Name;
|
||||
std::string Value;
|
||||
|
||||
bool hasSameName (const Header& header) const;
|
||||
bool hasSameName (const std::string& name) const;
|
||||
|
||||
static Header Parse (const std::string& header);
|
||||
};
|
||||
|
||||
class NETWORK_MANAGER_API HeadersList final
|
||||
{
|
||||
using HeadersStorageType = std::vector<Header>;
|
||||
public:
|
||||
using HeadersIterator = HeadersStorageType::iterator;
|
||||
using HeadersConstIterator = HeadersStorageType::const_iterator;
|
||||
|
||||
void setHeader (const Header& header);
|
||||
void setHeader (const std::string& headerName, std::string headerValue);
|
||||
|
||||
void addHeader (Header header);
|
||||
void addHeader (std::string headerName, std::string headerValue);
|
||||
|
||||
bool hasHeader (const std::string& headerName) const noexcept;
|
||||
|
||||
std::string getHeaderValue (const std::string& headerName) const;
|
||||
|
||||
const Header* getHeader (size_t idx) const noexcept;
|
||||
const Header* getHeader (const std::string& name) const noexcept;
|
||||
|
||||
size_t getHeadersCount () const noexcept;
|
||||
|
||||
HeadersIterator begin () noexcept;
|
||||
HeadersIterator end () noexcept;
|
||||
|
||||
HeadersConstIterator begin () const noexcept;
|
||||
HeadersConstIterator end () const noexcept;
|
||||
private:
|
||||
Header* getHeader (size_t idx) noexcept;
|
||||
Header* getHeader (const std::string& headerName) noexcept;
|
||||
|
||||
HeadersStorageType mHeaders;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
@ -1,109 +0,0 @@
|
||||
/*!********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
@file IResponse.h
|
||||
@brief Declare an interface for HTTP response.
|
||||
|
||||
Dmitry Vedenko
|
||||
**********************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
#include "NetworkManagerApi.h"
|
||||
|
||||
namespace audacity
|
||||
{
|
||||
namespace network_manager
|
||||
{
|
||||
|
||||
class Request;
|
||||
class HeadersList;
|
||||
class CookiesList;
|
||||
|
||||
enum class NetworkError
|
||||
{
|
||||
NoError,
|
||||
BadURL,
|
||||
ConnectionFailed,
|
||||
ConnectionRefused,
|
||||
RemoteHostClosed,
|
||||
HostNotFound,
|
||||
Timeout,
|
||||
OperationCancelled,
|
||||
SSLHandshakeFailed,
|
||||
TooManyRedirects,
|
||||
ProxyConnectionFailed,
|
||||
ProxyNotFound,
|
||||
UnknownError,
|
||||
HTTPError
|
||||
};
|
||||
|
||||
class NETWORK_MANAGER_API IResponse
|
||||
{
|
||||
public:
|
||||
using RequestCallback = std::function<void (IResponse*)>;
|
||||
|
||||
virtual ~IResponse () = default;
|
||||
|
||||
virtual bool isFinished () const noexcept = 0;
|
||||
|
||||
virtual unsigned getHTTPCode () const noexcept = 0;
|
||||
|
||||
virtual NetworkError getError () const noexcept = 0;
|
||||
virtual std::string getErrorString () const = 0;
|
||||
|
||||
virtual bool headersReceived () const noexcept = 0;
|
||||
|
||||
virtual bool hasHeader (const std::string& headerName) const noexcept = 0;
|
||||
virtual std::string getHeader (const std::string& headerName) const = 0;
|
||||
|
||||
virtual const HeadersList& getHeaders () const noexcept = 0;
|
||||
virtual const CookiesList& getCookies () const noexcept = 0;
|
||||
|
||||
virtual const Request& getRequest () const noexcept = 0;
|
||||
virtual std::string getURL () const = 0;
|
||||
|
||||
virtual void abort () noexcept = 0;
|
||||
|
||||
virtual void setOnDataReceivedCallback (RequestCallback callback) = 0;
|
||||
virtual void setRequestFinishedCallback (RequestCallback callback) = 0;
|
||||
|
||||
// The total bytes available to read by readData
|
||||
virtual uint64_t getBytesAvailable () const noexcept = 0;
|
||||
|
||||
// Reads at max maxBytesCount into the buffer, returns the actual count of bytes read
|
||||
virtual uint64_t readData (void* buffer, uint64_t maxBytesCount) = 0;
|
||||
|
||||
template<typename RetVal = std::vector<uint8_t>>
|
||||
RetVal readAll ()
|
||||
{
|
||||
RetVal result;
|
||||
|
||||
constexpr uint64_t bufferSize = 4 * 1024;
|
||||
uint8_t buffer[bufferSize];
|
||||
|
||||
while (uint64_t bytesRead = readData (buffer, bufferSize))
|
||||
{
|
||||
using PtrType = typename RetVal::pointer;
|
||||
|
||||
PtrType begin = reinterpret_cast<PtrType>(buffer);
|
||||
PtrType end = reinterpret_cast<PtrType>(buffer + bytesRead);
|
||||
|
||||
result.insert (result.end (), begin, end);
|
||||
|
||||
if (bytesRead < bufferSize)
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
/*!********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
@file IResponse.h
|
||||
@brief Declare an interface for HTTP response factory.
|
||||
|
||||
Dmitry Vedenko
|
||||
**********************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace audacity
|
||||
{
|
||||
namespace network_manager
|
||||
{
|
||||
|
||||
class IResponse;
|
||||
class Request;
|
||||
|
||||
using ResponsePtr = std::shared_ptr<IResponse>;
|
||||
|
||||
enum class RequestVerb
|
||||
{
|
||||
Head,
|
||||
Get,
|
||||
Post,
|
||||
Put,
|
||||
Delete
|
||||
};
|
||||
|
||||
class IResponseFactory
|
||||
{
|
||||
public:
|
||||
virtual ~IResponseFactory () = default;
|
||||
|
||||
virtual void setProxy (const std::string& proxy) = 0;
|
||||
|
||||
virtual ResponsePtr performRequest (RequestVerb verb, const Request& request) = 0;
|
||||
virtual ResponsePtr performRequest (RequestVerb verb, const Request& request, const void* data, size_t size) = 0;
|
||||
|
||||
virtual void terminate () = 0;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
@ -1,79 +0,0 @@
|
||||
/*!********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
@file NetworkManager.cpp
|
||||
@brief Define a class for preforming HTTP requests.
|
||||
|
||||
Dmitry Vedenko
|
||||
**********************************************************************/
|
||||
|
||||
/*!********************************************************************
|
||||
|
||||
@class NetworkManager
|
||||
@brief Class for preforming HTTP requests.
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#include "NetworkManager.h"
|
||||
|
||||
#include "IResponseFactory.h"
|
||||
#include "curl/CurlResponseFactory.h"
|
||||
|
||||
namespace audacity
|
||||
{
|
||||
namespace network_manager
|
||||
{
|
||||
|
||||
NetworkManager::NetworkManager ()
|
||||
{
|
||||
mResponseFactory = std::make_unique<CurlResponseFactory> ();
|
||||
}
|
||||
|
||||
NetworkManager::~NetworkManager ()
|
||||
{}
|
||||
|
||||
NetworkManager& NetworkManager::GetInstance ()
|
||||
{
|
||||
static NetworkManager instance;
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void NetworkManager::Terminate ()
|
||||
{
|
||||
GetInstance ().mResponseFactory->terminate ();
|
||||
}
|
||||
|
||||
ResponsePtr NetworkManager::doGet (const Request& request)
|
||||
{
|
||||
return mResponseFactory->performRequest (RequestVerb::Get, request);
|
||||
}
|
||||
|
||||
ResponsePtr NetworkManager::doHead (const Request& request)
|
||||
{
|
||||
return mResponseFactory->performRequest (RequestVerb::Head, request);
|
||||
}
|
||||
|
||||
ResponsePtr NetworkManager::doDelete (const Request& request)
|
||||
{
|
||||
return mResponseFactory->performRequest (RequestVerb::Delete, request);
|
||||
}
|
||||
|
||||
ResponsePtr NetworkManager::doPost (const Request& request, const void* data, size_t size)
|
||||
{
|
||||
return mResponseFactory->performRequest (RequestVerb::Post, request, data, size);
|
||||
}
|
||||
|
||||
ResponsePtr NetworkManager::doPut (const Request& request, const void* data, size_t size)
|
||||
{
|
||||
return mResponseFactory->performRequest (RequestVerb::Put, request, data, size);
|
||||
}
|
||||
|
||||
void NetworkManager::setProxy (const std::string& proxy)
|
||||
{
|
||||
mResponseFactory->setProxy (proxy);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
/*!********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
@file NetworkManager.h
|
||||
@brief Declare a class for preforming HTTP requests.
|
||||
|
||||
Dmitry Vedenko
|
||||
**********************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "NetworkManagerApi.h"
|
||||
|
||||
namespace audacity
|
||||
{
|
||||
namespace network_manager
|
||||
{
|
||||
|
||||
class Request;
|
||||
class IResponse;
|
||||
class IResponseFactory;
|
||||
|
||||
using ResponsePtr = std::shared_ptr<IResponse>;
|
||||
|
||||
class NETWORK_MANAGER_API NetworkManager final
|
||||
{
|
||||
NetworkManager ();
|
||||
~NetworkManager ();
|
||||
public:
|
||||
static NetworkManager& GetInstance();
|
||||
static void Terminate ();
|
||||
|
||||
ResponsePtr doGet (const Request& request);
|
||||
ResponsePtr doHead (const Request& request);
|
||||
ResponsePtr doDelete (const Request& request);
|
||||
ResponsePtr doPost (const Request& request, const void* data, size_t size);
|
||||
ResponsePtr doPut (const Request& request, const void* data, size_t size);
|
||||
|
||||
void setProxy (const std::string& proxy);
|
||||
private:
|
||||
std::unique_ptr<IResponseFactory> mResponseFactory;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
/*!********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
@file NetworkManagerApi.h
|
||||
@brief Declare macros for the Network Manager library DLL API
|
||||
|
||||
Dmitry Vedenko
|
||||
**********************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* The dynamic library import and export for Microsoft Windows.
|
||||
* Supported by Visual Studio and for GCC 4+ */
|
||||
#if defined _WIN32 || (defined __CYGWIN__ && defined __GNUC__)
|
||||
# ifndef NETWORK_MANAGER_API
|
||||
# ifdef BUILDING_LIB_NETWORK_MANAGER
|
||||
# define NETWORK_MANAGER_API __declspec(dllexport)
|
||||
# else
|
||||
# ifdef _DLL
|
||||
# define NETWORK_MANAGER_API __declspec(dllimport)
|
||||
# else
|
||||
# define NETWORK_MANAGER_API
|
||||
# endif
|
||||
# endif
|
||||
# endif
|
||||
#else
|
||||
# ifndef NETWORK_MANAGER_API
|
||||
# define NETWORK_MANAGER_API __attribute__((visibility("default")))
|
||||
# endif
|
||||
#endif //_WIN32 || (__CYGWIN__ && __GNUC__)
|
@ -1,115 +0,0 @@
|
||||
/*!********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
@file Request.cpp
|
||||
@brief Define a class for constructing HTTP requests.
|
||||
|
||||
Dmitry Vedenko
|
||||
**********************************************************************/
|
||||
|
||||
/*!********************************************************************
|
||||
|
||||
@class Request
|
||||
@brief Class to construct the HTTP request.
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#include "Request.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <algorithm>
|
||||
|
||||
namespace audacity
|
||||
{
|
||||
namespace network_manager
|
||||
{
|
||||
|
||||
|
||||
Request::Request(std::string url) noexcept
|
||||
: mUrl (std::move (url))
|
||||
{
|
||||
}
|
||||
|
||||
Request& Request::setURL(std::string url) noexcept
|
||||
{
|
||||
mUrl = std::move (url);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
const std::string& Request::getURL () const noexcept
|
||||
{
|
||||
return mUrl;
|
||||
}
|
||||
|
||||
Request& Request::setHeader (const std::string& name, std::string value)
|
||||
{
|
||||
mHeaders.setHeader (name, std::move (value));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::string Request::getHeader (const std::string& name) const
|
||||
{
|
||||
return mHeaders.getHeaderValue (name);
|
||||
}
|
||||
|
||||
const HeadersList& Request::getHeaders () const noexcept
|
||||
{
|
||||
return mHeaders;
|
||||
}
|
||||
|
||||
Request& Request::setCookie (const std::string& name, std::string value)
|
||||
{
|
||||
mCookies.setCookie (name, std::move (value));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::string Request::getCookie (const std::string& name) const
|
||||
{
|
||||
return mCookies.getCookieValue (name);
|
||||
}
|
||||
|
||||
const CookiesList& Request::getCookies () noexcept
|
||||
{
|
||||
return mCookies;
|
||||
}
|
||||
|
||||
Request& Request::setMaxRedirects (size_t redirects) noexcept
|
||||
{
|
||||
mMaxRedirects = redirects;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
size_t Request::getMaxRedirects () const noexcept
|
||||
{
|
||||
return mMaxRedirects;
|
||||
}
|
||||
|
||||
Request& Request::setTimeout (Timeout timeout) noexcept
|
||||
{
|
||||
mTimeout = timeout;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Request::Timeout Request::getTimeout () const noexcept
|
||||
{
|
||||
return mTimeout;
|
||||
}
|
||||
|
||||
Request& Request::appendCookies (const CookiesList& list)
|
||||
{
|
||||
for (const Cookie& cookie : list)
|
||||
{
|
||||
if (!cookie.isExpired())
|
||||
mCookies.setCookie (cookie);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
/*!********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
@file Request.h
|
||||
@brief Declare a class for constructing HTTP requests.
|
||||
|
||||
Dmitry Vedenko
|
||||
**********************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <numeric>
|
||||
|
||||
#include "NetworkManagerApi.h"
|
||||
|
||||
#include "HeadersList.h"
|
||||
#include "CookiesList.h"
|
||||
|
||||
namespace audacity
|
||||
{
|
||||
namespace network_manager
|
||||
{
|
||||
|
||||
class NETWORK_MANAGER_API Request final
|
||||
{
|
||||
public:
|
||||
using Timeout = std::chrono::milliseconds;
|
||||
|
||||
static constexpr size_t INFINITE_REDIRECTS = std::numeric_limits<size_t>::max();
|
||||
|
||||
Request() = default;
|
||||
explicit Request(std::string url) noexcept;
|
||||
|
||||
Request(const Request&) = default;
|
||||
Request(Request&&) = default;
|
||||
Request& operator = (const Request&) = default;
|
||||
Request& operator = (Request&&) = default;
|
||||
|
||||
Request& setURL(std::string url) noexcept;
|
||||
const std::string& getURL() const noexcept;
|
||||
|
||||
Request& setHeader(const std::string& name, std::string value);
|
||||
std::string getHeader(const std::string& name) const;
|
||||
|
||||
const HeadersList& getHeaders () const noexcept;
|
||||
|
||||
Request& setCookie(const std::string& name, std::string value);
|
||||
Request& appendCookies (const CookiesList& list);
|
||||
|
||||
std::string getCookie(const std::string& name) const;
|
||||
|
||||
const CookiesList& getCookies () noexcept;
|
||||
|
||||
Request& setMaxRedirects(size_t redirects) noexcept;
|
||||
size_t getMaxRedirects() const noexcept;
|
||||
|
||||
|
||||
Request& setTimeout(Timeout timeout) noexcept;
|
||||
Timeout getTimeout() const noexcept;
|
||||
private:
|
||||
std::string mUrl;
|
||||
|
||||
HeadersList mHeaders;
|
||||
CookiesList mCookies;
|
||||
|
||||
size_t mMaxRedirects { INFINITE_REDIRECTS };
|
||||
|
||||
Timeout mTimeout { std::chrono::seconds (5) };
|
||||
};
|
||||
|
||||
}
|
||||
}
|
@ -1,349 +0,0 @@
|
||||
/*!********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
@file CurlHandleManager.cpp
|
||||
@brief Define a class responsible for reuse of CURL hanldes.
|
||||
|
||||
Dmitry Vedenko
|
||||
**********************************************************************/
|
||||
|
||||
#include "CurlHandleManager.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
#include <wx/platinfo.h>
|
||||
|
||||
namespace audacity
|
||||
{
|
||||
namespace network_manager
|
||||
{
|
||||
namespace
|
||||
{
|
||||
|
||||
void GetOSString (std::ostringstream& output, const wxPlatformInfo& platformInfo)
|
||||
{
|
||||
const wxOperatingSystemId osID = platformInfo.GetOperatingSystemId ();
|
||||
|
||||
if (osID & wxOS_WINDOWS)
|
||||
output << "Windows ";
|
||||
else if (osID & wxOS_MAC)
|
||||
output << "MacOS ";
|
||||
else if (osID & wxOS_UNIX_LINUX)
|
||||
output << "Linux ";
|
||||
else if (osID & wxOS_UNIX_FREEBSD)
|
||||
output << "FreeBSD ";
|
||||
else if (osID & wxOS_UNIX_OPENBSD)
|
||||
output << "OpenBSD ";
|
||||
else
|
||||
output << "Other ";
|
||||
|
||||
output <<
|
||||
platformInfo.GetOSMajorVersion () <<
|
||||
"_" <<
|
||||
platformInfo.GetOSMinorVersion () <<
|
||||
"_" <<
|
||||
platformInfo.GetOSMicroVersion() <<
|
||||
"; ";
|
||||
|
||||
#if defined(__amd64__) || defined(__x86_64__) || defined(_M_X64)
|
||||
output << "x64";
|
||||
#elif defined(__i386__) || defined(i386) || defined(_M_IX86) || defined(_X86_) || defined(__THW_INTEL)
|
||||
output << "x86";
|
||||
#elif defined(__arm64__) || defined(__aarch64__)
|
||||
output << "arm64";
|
||||
#elif defined(arm) || defined(__arm__) || defined(ARM) || defined(_ARM_)
|
||||
output << "arm";
|
||||
#else
|
||||
output << "unknown";
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
constexpr std::chrono::milliseconds CurlHandleManager::KEEP_ALIVE_IDLE;
|
||||
constexpr std::chrono::milliseconds CurlHandleManager::KEEP_ALIVE_PROBE;
|
||||
|
||||
CurlHandleManager::Handle::Handle(CurlHandleManager* owner, CURL* handle, RequestVerb verb, std::string url) noexcept
|
||||
: mHandle (handle),
|
||||
mOwner (owner),
|
||||
mVerb (verb),
|
||||
mUrl (std::move (url)),
|
||||
mHandleFromCache (handle != nullptr)
|
||||
{
|
||||
if (mHandle == nullptr)
|
||||
mHandle = curl_easy_init ();
|
||||
|
||||
setOption (CURLOPT_URL, mUrl);
|
||||
|
||||
switch (verb)
|
||||
{
|
||||
case RequestVerb::Head:
|
||||
setOption (CURLOPT_NOBODY, 1);
|
||||
break;
|
||||
case RequestVerb::Get:
|
||||
// This is a default, no additional setup is needed
|
||||
// We cache handles by the verb, so there is no need to
|
||||
// reset the handle state
|
||||
break;
|
||||
case RequestVerb::Post:
|
||||
setOption (CURLOPT_POST, 1);
|
||||
break;
|
||||
case RequestVerb::Put:
|
||||
setOption (CURLOPT_UPLOAD, 1);
|
||||
break;
|
||||
case RequestVerb::Delete:
|
||||
setOption (CURLOPT_CUSTOMREQUEST, "DELETE");
|
||||
break;
|
||||
}
|
||||
|
||||
setOption (CURLOPT_NOSIGNAL, 1L);
|
||||
|
||||
setOption (CURLOPT_SSL_VERIFYPEER, 1L);
|
||||
setOption (CURLOPT_SSL_VERIFYHOST, 2L);
|
||||
|
||||
setOption (CURLOPT_ACCEPT_ENCODING, "");
|
||||
}
|
||||
|
||||
CurlHandleManager::Handle::Handle (Handle&& rhs) noexcept
|
||||
{
|
||||
*this = std::move (rhs);
|
||||
}
|
||||
|
||||
CurlHandleManager::Handle::~Handle () noexcept
|
||||
{
|
||||
if (mReuse)
|
||||
mOwner->cacheHandle (*this);
|
||||
else
|
||||
curl_easy_cleanup (mHandle);
|
||||
}
|
||||
|
||||
CurlHandleManager::Handle& CurlHandleManager::Handle::operator=(Handle&& rhs) noexcept
|
||||
{
|
||||
std::swap (mHandle, rhs.mHandle);
|
||||
std::swap (mOwner, rhs.mOwner);
|
||||
|
||||
std::swap (mVerb, rhs.mVerb);
|
||||
|
||||
mUrl = std::move (rhs.mUrl);
|
||||
mHeaders = std::move (rhs.mHeaders);
|
||||
|
||||
mReuse = rhs.mReuse;
|
||||
rhs.mReuse = false;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CURLcode CurlHandleManager::Handle::setOption (CURLoption option, const std::string& value) noexcept
|
||||
{
|
||||
return setOption (option, value.c_str ());
|
||||
}
|
||||
|
||||
CURLcode CurlHandleManager::Handle::appendCookie (const Cookie& cookie) noexcept
|
||||
{
|
||||
return setOption(CURLOPT_COOKIE, "Set-Cookie: " + cookie.Name + "=" + cookie.Value);
|
||||
}
|
||||
|
||||
CURLcode CurlHandleManager::Handle::appendCookies (const CookiesList& cookies)noexcept
|
||||
{
|
||||
for (const Cookie& cookie : cookies)
|
||||
{
|
||||
const CURLcode result = appendCookie (cookie);
|
||||
|
||||
if (result != CURLE_OK)
|
||||
return result;
|
||||
}
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
void CurlHandleManager::Handle::appendHeader (const Header& header)
|
||||
{
|
||||
if (header.hasSameName ("User-Agent"))
|
||||
mUserAgentSet = true;
|
||||
|
||||
mHeaders.append(header.Name + ": " + header.Value);
|
||||
}
|
||||
|
||||
void CurlHandleManager::Handle::appendHeaders (const HeadersList& headers)
|
||||
{
|
||||
for (const Header& header : headers)
|
||||
appendHeader (header);
|
||||
}
|
||||
|
||||
CurlHandleManager::Handle::Result CurlHandleManager::Handle::perform ()
|
||||
{
|
||||
if (!mUserAgentSet)
|
||||
mHeaders.append ("User-Agent: " + mOwner->getUserAgent ());
|
||||
|
||||
CURLcode result = setOption (CURLOPT_HTTPHEADER, mHeaders.getCurlList ());
|
||||
|
||||
if (result != CURLE_OK)
|
||||
return { result, std::string () };
|
||||
|
||||
char currentError[CURL_ERROR_SIZE] = {};
|
||||
setOption(CURLOPT_ERRORBUFFER, currentError);
|
||||
|
||||
result = curl_easy_perform (mHandle);
|
||||
|
||||
mReuse = mReuse && result == CURLE_OK;
|
||||
|
||||
return { result, std::string (currentError) };
|
||||
}
|
||||
|
||||
void CurlHandleManager::Handle::markKeepAlive ()
|
||||
{
|
||||
mReuse = true;
|
||||
}
|
||||
|
||||
bool CurlHandleManager::Handle::isHandleFromCache () const noexcept
|
||||
{
|
||||
return mHandleFromCache;
|
||||
}
|
||||
|
||||
unsigned CurlHandleManager::Handle::getHTTPCode () const noexcept
|
||||
{
|
||||
long code = 0;
|
||||
|
||||
if (CURLE_OK != curl_easy_getinfo(mHandle, CURLINFO_RESPONSE_CODE, &code))
|
||||
return 0;
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
void CurlHandleManager::Handle::reset () noexcept
|
||||
{
|
||||
setOption (CURLOPT_COOKIELIST, nullptr);
|
||||
setOption (CURLOPT_PROXY, nullptr);
|
||||
setOption (CURLOPT_SSL_OPTIONS, 0);
|
||||
|
||||
mUserAgentSet = false;
|
||||
}
|
||||
|
||||
CurlHandleManager::CurlHandleManager ()
|
||||
{
|
||||
std::ostringstream ss;
|
||||
|
||||
ss << "Audacity/" <<
|
||||
AUDACITY_VERSION << "." <<
|
||||
AUDACITY_RELEASE << "." <<
|
||||
AUDACITY_REVISION <<
|
||||
" (";
|
||||
|
||||
GetOSString (ss, wxPlatformInfo::Get ());
|
||||
|
||||
ss << ")";
|
||||
|
||||
mUserAgent = ss.str ();
|
||||
}
|
||||
|
||||
CurlHandleManager::~CurlHandleManager ()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (mHandleCacheLock);
|
||||
|
||||
for (auto& cachedHandle : mHandleCache)
|
||||
curl_easy_cleanup (cachedHandle.Handle);
|
||||
}
|
||||
|
||||
void CurlHandleManager::setProxy (std::string proxy)
|
||||
{
|
||||
mProxy = std::move (proxy);
|
||||
}
|
||||
|
||||
CurlHandleManager::Handle CurlHandleManager::getHandle (RequestVerb verb, const std::string& url)
|
||||
{
|
||||
Handle handle (this, getCurlHandleFromCache (verb, url), verb, url);
|
||||
|
||||
if (!mProxy.empty ())
|
||||
{
|
||||
handle.setOption (CURLOPT_PROXY, mProxy);
|
||||
// If we use proxy, checking the CRL will likely break the SSL proxying
|
||||
handle.setOption (CURLOPT_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE);
|
||||
}
|
||||
|
||||
handle.setOption (CURLOPT_TCP_KEEPALIVE, 1L);
|
||||
|
||||
handle.setOption (CURLOPT_TCP_KEEPIDLE,
|
||||
std::chrono::duration_cast<std::chrono::seconds> (KEEP_ALIVE_IDLE).count ()
|
||||
);
|
||||
|
||||
handle.setOption (CURLOPT_TCP_KEEPINTVL,
|
||||
std::chrono::duration_cast<std::chrono::seconds> (KEEP_ALIVE_PROBE).count ()
|
||||
);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
std::string CurlHandleManager::getUserAgent () const
|
||||
{
|
||||
return mUserAgent;
|
||||
}
|
||||
|
||||
CURL* CurlHandleManager::getCurlHandleFromCache (RequestVerb verb, const std::string& url)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (mHandleCacheLock);
|
||||
|
||||
cleanupHandlesCache ();
|
||||
|
||||
const std::string schemeAndDomain = GetSchemeAndDomain (url);
|
||||
|
||||
auto it = std::find_if (mHandleCache.begin (), mHandleCache.end (), [verb, schemeAndDomain](const CachedHandle& handle) {
|
||||
return handle.Verb == verb && handle.SchemeAndDomain == schemeAndDomain;
|
||||
});
|
||||
|
||||
if (it == mHandleCache.end ())
|
||||
return nullptr;
|
||||
|
||||
CURL* handle = it->Handle;
|
||||
|
||||
mHandleCache.erase (it);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
void CurlHandleManager::cacheHandle (Handle& handle)
|
||||
{
|
||||
// Reset the state to the safe defaults
|
||||
handle.reset ();
|
||||
|
||||
std::lock_guard<std::mutex> lock (mHandleCacheLock);
|
||||
|
||||
cleanupHandlesCache ();
|
||||
|
||||
mHandleCache.push_back ({
|
||||
handle.mVerb,
|
||||
GetSchemeAndDomain (handle.mUrl),
|
||||
handle.mHandle,
|
||||
RequestClock::now ()
|
||||
});
|
||||
}
|
||||
|
||||
void CurlHandleManager::cleanupHandlesCache ()
|
||||
{
|
||||
const RequestTimePoint timePoint = RequestClock::now ();
|
||||
|
||||
mHandleCache.erase (std::remove_if (mHandleCache.begin (), mHandleCache.end (), [timePoint](const CachedHandle& cachedHandle) {
|
||||
return (timePoint - cachedHandle.RequestTime) >= KEEP_ALIVE_IDLE;
|
||||
}), mHandleCache.end ());
|
||||
}
|
||||
|
||||
std::string CurlHandleManager::GetSchemeAndDomain (const std::string& url)
|
||||
{
|
||||
const size_t schemeEndPosition = url.find ("://");
|
||||
|
||||
if (schemeEndPosition == std::string::npos) // Is url even valid?
|
||||
return url;
|
||||
|
||||
const size_t domainStartPosition = schemeEndPosition + 3;
|
||||
|
||||
const size_t slashLocation = url.find ('/', domainStartPosition);
|
||||
|
||||
if (slashLocation == std::string::npos)
|
||||
return url;
|
||||
|
||||
return url.substr (domainStartPosition, slashLocation - domainStartPosition);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,127 +0,0 @@
|
||||
/*!********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
@file CurlHandleManager.h
|
||||
@brief Declare a class responsible for reuse of CURL hanldes.
|
||||
|
||||
Dmitry Vedenko
|
||||
**********************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include "CurlStringList.h"
|
||||
#include "../IResponseFactory.h"
|
||||
|
||||
#include "../CookiesList.h"
|
||||
#include "../HeadersList.h"
|
||||
|
||||
namespace audacity
|
||||
{
|
||||
namespace network_manager
|
||||
{
|
||||
|
||||
class CurlHandleManager final
|
||||
{
|
||||
public:
|
||||
class Handle final
|
||||
{
|
||||
friend class CurlHandleManager;
|
||||
Handle (CurlHandleManager* owner, CURL* handle, RequestVerb verb, std::string url) noexcept;
|
||||
public:
|
||||
Handle (Handle&& rhs) noexcept;
|
||||
~Handle () noexcept;
|
||||
|
||||
Handle& operator = (Handle&& rhs) noexcept;
|
||||
|
||||
template<typename... Args>
|
||||
CURLcode setOption (CURLoption option, Args... value) noexcept
|
||||
{
|
||||
return curl_easy_setopt (mHandle, option, value...);
|
||||
}
|
||||
|
||||
CURLcode setOption (CURLoption option, const std::string& value) noexcept;
|
||||
|
||||
CURLcode appendCookie (const Cookie& cookie) noexcept;
|
||||
CURLcode appendCookies (const CookiesList& cookie) noexcept;
|
||||
|
||||
void appendHeader (const Header& header);
|
||||
void appendHeaders (const HeadersList& headers);
|
||||
|
||||
struct Result final
|
||||
{
|
||||
CURLcode Code;
|
||||
std::string Message;
|
||||
};
|
||||
|
||||
Result perform ();
|
||||
|
||||
void markKeepAlive ();
|
||||
|
||||
bool isHandleFromCache () const noexcept;
|
||||
|
||||
unsigned getHTTPCode () const noexcept;
|
||||
|
||||
void reset () noexcept;
|
||||
private:
|
||||
CURL* mHandle { nullptr };
|
||||
CurlHandleManager* mOwner { nullptr };
|
||||
|
||||
RequestVerb mVerb;
|
||||
std::string mUrl;
|
||||
|
||||
CurlStringList mHeaders;
|
||||
|
||||
bool mUserAgentSet { false };
|
||||
bool mReuse { false };
|
||||
bool mHandleFromCache { false };
|
||||
};
|
||||
|
||||
CurlHandleManager ();
|
||||
~CurlHandleManager ();
|
||||
|
||||
void setProxy (std::string proxy);
|
||||
|
||||
Handle getHandle (RequestVerb verb, const std::string& url);
|
||||
private:
|
||||
using RequestClock = std::chrono::steady_clock;
|
||||
using RequestTimePoint = RequestClock::time_point;
|
||||
|
||||
static constexpr std::chrono::milliseconds KEEP_ALIVE_IDLE { std::chrono::seconds (120) };
|
||||
static constexpr std::chrono::milliseconds KEEP_ALIVE_PROBE { std::chrono::seconds (60) };
|
||||
|
||||
struct CachedHandle final
|
||||
{
|
||||
RequestVerb Verb;
|
||||
std::string SchemeAndDomain;
|
||||
|
||||
CURL* Handle { nullptr };
|
||||
|
||||
RequestTimePoint RequestTime;
|
||||
};
|
||||
|
||||
std::string getUserAgent () const;
|
||||
|
||||
CURL* getCurlHandleFromCache (RequestVerb verb, const std::string& url);
|
||||
void cacheHandle (Handle& handle);
|
||||
|
||||
void cleanupHandlesCache ();
|
||||
|
||||
static std::string GetSchemeAndDomain (const std::string& url);
|
||||
|
||||
std::string mProxy;
|
||||
std::string mUserAgent;
|
||||
|
||||
std::mutex mHandleCacheLock;
|
||||
std::vector<CachedHandle> mHandleCache;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
@ -1,382 +0,0 @@
|
||||
/*!********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
@file CurlResponse.cpp
|
||||
@brief Define an implementation of IResponse using libcurl.
|
||||
|
||||
Dmitry Vedenko
|
||||
**********************************************************************/
|
||||
|
||||
#include "CurlResponse.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
|
||||
namespace audacity
|
||||
{
|
||||
namespace network_manager
|
||||
{
|
||||
namespace
|
||||
{
|
||||
|
||||
static const std::map<CURLcode, NetworkError> errorsMap = {
|
||||
{ CURLE_OK, NetworkError::NoError },
|
||||
{ CURLE_URL_MALFORMAT, NetworkError::BadURL },
|
||||
{ CURLE_COULDNT_RESOLVE_PROXY, NetworkError::ProxyNotFound },
|
||||
{ CURLE_COULDNT_RESOLVE_HOST, NetworkError::HostNotFound },
|
||||
{ CURLE_COULDNT_CONNECT, NetworkError::ConnectionRefused },
|
||||
{ CURLE_HTTP_RETURNED_ERROR, NetworkError::HTTPError },
|
||||
{ CURLE_WRITE_ERROR, NetworkError::OperationCancelled },
|
||||
{ CURLE_READ_ERROR, NetworkError::OperationCancelled },
|
||||
{ CURLE_OPERATION_TIMEDOUT, NetworkError::Timeout },
|
||||
{ CURLE_RANGE_ERROR, NetworkError::HTTPError },
|
||||
{ CURLE_HTTP_POST_ERROR, NetworkError::HTTPError },
|
||||
{ CURLE_SSL_CONNECT_ERROR, NetworkError::SSLHandshakeFailed },
|
||||
{ CURLE_ABORTED_BY_CALLBACK, NetworkError::OperationCancelled },
|
||||
{ CURLE_TOO_MANY_REDIRECTS, NetworkError::OperationCancelled },
|
||||
{ CURLE_PEER_FAILED_VERIFICATION, NetworkError::SSLHandshakeFailed },
|
||||
{ CURLE_GOT_NOTHING, NetworkError::RemoteHostClosed },
|
||||
{ CURLE_SSL_ENGINE_NOTFOUND, NetworkError::SSLHandshakeFailed },
|
||||
{ CURLE_SSL_ENGINE_SETFAILED, NetworkError::SSLHandshakeFailed },
|
||||
{ CURLE_SEND_ERROR, NetworkError::RemoteHostClosed },
|
||||
{ CURLE_RECV_ERROR, NetworkError::RemoteHostClosed },
|
||||
{ CURLE_SSL_CERTPROBLEM, NetworkError::SSLHandshakeFailed },
|
||||
{ CURLE_SSL_CIPHER, NetworkError::SSLHandshakeFailed },
|
||||
{ CURLE_SSL_CACERT, NetworkError::SSLHandshakeFailed },
|
||||
{ CURLE_USE_SSL_FAILED, NetworkError::SSLHandshakeFailed },
|
||||
{ CURLE_SSL_ENGINE_INITFAILED, NetworkError::SSLHandshakeFailed },
|
||||
{ CURLE_SSL_CACERT_BADFILE, NetworkError::SSLHandshakeFailed },
|
||||
{ CURLE_SSL_SHUTDOWN_FAILED, NetworkError::SSLHandshakeFailed },
|
||||
{ CURLE_SSL_CRL_BADFILE, NetworkError::SSLHandshakeFailed },
|
||||
{ CURLE_SSL_ISSUER_ERROR, NetworkError::SSLHandshakeFailed },
|
||||
{ CURLE_CHUNK_FAILED, NetworkError::HTTPError },
|
||||
{ CURLE_NO_CONNECTION_AVAILABLE, NetworkError::ConnectionFailed },
|
||||
{ CURLE_SSL_PINNEDPUBKEYNOTMATCH, NetworkError::SSLHandshakeFailed },
|
||||
{ CURLE_SSL_INVALIDCERTSTATUS, NetworkError::SSLHandshakeFailed },
|
||||
{ CURLE_PARTIAL_FILE, NetworkError::RemoteHostClosed }
|
||||
};
|
||||
|
||||
struct DataStream final
|
||||
{
|
||||
const char* Buffer;
|
||||
size_t Size;
|
||||
|
||||
size_t Offset { 0 };
|
||||
};
|
||||
|
||||
size_t DataStreamRead (char* ptr, size_t size, size_t nmemb, DataStream* stream) noexcept
|
||||
{
|
||||
size = std::min (size * nmemb, stream->Size - stream->Offset);
|
||||
|
||||
const char* start = stream->Buffer + stream->Offset;
|
||||
const char* end = start + size;
|
||||
|
||||
std::copy (start, end, ptr);
|
||||
|
||||
stream->Offset += size;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
int DataStreamSeek (DataStream* stream, curl_off_t offs, int origin) noexcept
|
||||
{
|
||||
int64_t offset = offs;
|
||||
|
||||
switch (origin)
|
||||
{
|
||||
case SEEK_CUR:
|
||||
offset += stream->Offset;
|
||||
break;
|
||||
case SEEK_END:
|
||||
offset += stream->Size;
|
||||
break;
|
||||
}
|
||||
|
||||
if (offset < 0 || offset >= stream->Size)
|
||||
return CURL_SEEKFUNC_FAIL;
|
||||
|
||||
stream->Offset = offset;
|
||||
|
||||
return CURL_SEEKFUNC_OK;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
CurlResponse::CurlResponse (RequestVerb verb, const Request& request, CurlHandleManager* handleManager) noexcept
|
||||
: mVerb(verb),
|
||||
mRequest(request),
|
||||
mHandleManager (handleManager)
|
||||
{
|
||||
}
|
||||
|
||||
bool CurlResponse::isFinished () const noexcept
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (mStatusMutex);
|
||||
return mRequestFinished;
|
||||
}
|
||||
|
||||
unsigned CurlResponse::getHTTPCode () const noexcept
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (mStatusMutex);
|
||||
return mHttpCode;
|
||||
}
|
||||
|
||||
NetworkError CurlResponse::getError () const noexcept
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (mStatusMutex);
|
||||
return mNetworkError;
|
||||
}
|
||||
|
||||
std::string CurlResponse::getErrorString () const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (mStatusMutex);
|
||||
return mErrorString;
|
||||
}
|
||||
|
||||
bool CurlResponse::headersReceived () const noexcept
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (mStatusMutex);
|
||||
return mHeadersReceived;
|
||||
}
|
||||
|
||||
bool CurlResponse::hasHeader (const std::string& headerName) const noexcept
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (mHeadersMutex);
|
||||
return mResponseHeaders.hasHeader (headerName);
|
||||
}
|
||||
|
||||
std::string CurlResponse::getHeader (const std::string& headerName) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (mHeadersMutex);
|
||||
return mResponseHeaders.getHeaderValue (headerName);
|
||||
}
|
||||
|
||||
const HeadersList& CurlResponse::getHeaders () const noexcept
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (mHeadersMutex);
|
||||
return mResponseHeaders;
|
||||
}
|
||||
|
||||
const CookiesList& CurlResponse::getCookies () const noexcept
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (mHeadersMutex);
|
||||
return mResponseCookies;
|
||||
}
|
||||
|
||||
const Request& CurlResponse::getRequest () const noexcept
|
||||
{
|
||||
return mRequest;
|
||||
}
|
||||
|
||||
std::string CurlResponse::getURL () const
|
||||
{
|
||||
return mRequest.getURL ();
|
||||
}
|
||||
|
||||
void CurlResponse::abort () noexcept
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (mStatusMutex);
|
||||
mAbortRequested = true;
|
||||
}
|
||||
|
||||
void CurlResponse::setOnDataReceivedCallback (RequestCallback callback)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (mCallbackMutex);
|
||||
|
||||
mOnDataReceivedCallback = std::move (callback);
|
||||
|
||||
if (mOnDataReceivedCallback && getBytesAvailable () > 0)
|
||||
mOnDataReceivedCallback (this);
|
||||
}
|
||||
|
||||
void CurlResponse::setRequestFinishedCallback (RequestCallback callback)
|
||||
{
|
||||
std::lock_guard<std::mutex> callbackLock (mCallbackMutex);
|
||||
|
||||
mRequestFinishedCallback = std::move (callback);
|
||||
|
||||
std::lock_guard<std::mutex> statusLock (mStatusMutex);
|
||||
|
||||
if (mRequestFinishedCallback && mRequestFinished)
|
||||
mRequestFinishedCallback (this);
|
||||
}
|
||||
|
||||
uint64_t CurlResponse::getBytesAvailable () const noexcept
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (mDataBufferMutex);
|
||||
return mDataBuffer.size ();
|
||||
}
|
||||
|
||||
uint64_t CurlResponse::readData (void* buffer, uint64_t maxBytesCount)
|
||||
{
|
||||
if (buffer == nullptr || maxBytesCount == 0)
|
||||
return 0;
|
||||
|
||||
std::lock_guard<std::mutex> lock (mDataBufferMutex);
|
||||
|
||||
if (mDataBuffer.empty ())
|
||||
return 0;
|
||||
|
||||
maxBytesCount = std::min<uint64_t> (maxBytesCount, mDataBuffer.size ());
|
||||
|
||||
const auto begin = mDataBuffer.begin ();
|
||||
const auto end = begin + maxBytesCount;
|
||||
|
||||
std::copy (begin, end, static_cast<uint8_t*> (buffer));
|
||||
|
||||
mDataBuffer.erase (begin, end);
|
||||
|
||||
return maxBytesCount;
|
||||
}
|
||||
|
||||
void CurlResponse::perform (const void* ptr, size_t size)
|
||||
{
|
||||
CurlHandleManager::Handle handle = mHandleManager->getHandle (mVerb, mRequest.getURL ());
|
||||
|
||||
handle.setOption (CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||
handle.setOption (CURLOPT_WRITEDATA, this);
|
||||
|
||||
handle.setOption (CURLOPT_HEADERFUNCTION, HeaderCallback);
|
||||
handle.setOption (CURLOPT_HEADERDATA, this);
|
||||
|
||||
handle.setOption (CURLOPT_FOLLOWLOCATION, mRequest.getMaxRedirects () == 0 ? 0 : 1);
|
||||
handle.setOption (CURLOPT_MAXREDIRS, mRequest.getMaxRedirects ());
|
||||
|
||||
handle.setOption (CURLOPT_CONNECTTIMEOUT_MS,
|
||||
std::chrono::duration_cast<std::chrono::milliseconds> (mRequest.getTimeout()).count ()
|
||||
);
|
||||
|
||||
handle.appendCookies (mRequest.getCookies ());
|
||||
|
||||
DataStream ds { reinterpret_cast<const char*>(ptr), size };
|
||||
|
||||
if (ptr != nullptr && size != 0)
|
||||
{
|
||||
handle.appendHeader ({ "Transfer-Encoding", std::string () });
|
||||
handle.appendHeader ({ "Content-Length", std::to_string (size) });
|
||||
|
||||
if (mVerb == RequestVerb::Post)
|
||||
handle.setOption (CURLOPT_POSTFIELDSIZE_LARGE, size);
|
||||
else
|
||||
handle.setOption (CURLOPT_INFILESIZE_LARGE, size);
|
||||
|
||||
handle.setOption (CURLOPT_READFUNCTION, DataStreamRead);
|
||||
handle.setOption (CURLOPT_READDATA, &ds);
|
||||
|
||||
handle.setOption (CURLOPT_SEEKFUNCTION, DataStreamSeek);
|
||||
handle.setOption (CURLOPT_SEEKDATA, &ds);
|
||||
}
|
||||
|
||||
handle.appendHeaders (mRequest.getHeaders ());
|
||||
|
||||
mCurrentHandle = &handle;
|
||||
const auto result = handle.perform ();
|
||||
mCurrentHandle = nullptr;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (mStatusMutex);
|
||||
|
||||
if (result.Code != CURLE_OK)
|
||||
{
|
||||
const auto it = errorsMap.find (result.Code);
|
||||
|
||||
mNetworkError = it != errorsMap.end () ? it->second : NetworkError::UnknownError;
|
||||
mErrorString = result.Message;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mHttpCode == 0)
|
||||
mHttpCode = handle.getHTTPCode ();
|
||||
}
|
||||
|
||||
mRequestFinished = true;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock (mCallbackMutex);
|
||||
|
||||
if (mRequestFinishedCallback)
|
||||
mRequestFinishedCallback (this);
|
||||
|
||||
mRequestFinishedCallback = {};
|
||||
mOnDataReceivedCallback = {};
|
||||
}
|
||||
|
||||
|
||||
size_t CurlResponse::WriteCallback (const uint8_t* ptr, size_t size, size_t nmemb, CurlResponse* request) noexcept
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (request->mStatusMutex);
|
||||
|
||||
if (request->mAbortRequested)
|
||||
return 0;
|
||||
|
||||
if (!request->mHeadersReceived)
|
||||
{
|
||||
request->mHeadersReceived = true;
|
||||
|
||||
// WriteCallback is called by the handle
|
||||
assert (request->mCurrentHandle != nullptr);
|
||||
|
||||
if (request->mCurrentHandle != nullptr && request->mHttpCode == 0)
|
||||
request->mHttpCode = request->mCurrentHandle->getHTTPCode ();
|
||||
}
|
||||
}
|
||||
|
||||
size *= nmemb;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (request->mDataBufferMutex);
|
||||
request->mDataBuffer.insert (request->mDataBuffer.end (), ptr, ptr + size);
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock (request->mCallbackMutex);
|
||||
|
||||
if (request->mOnDataReceivedCallback)
|
||||
request->mOnDataReceivedCallback (request);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t CurlResponse::HeaderCallback (const char* buffer, size_t size, size_t nitems, CurlResponse* request) noexcept
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (request->mStatusMutex);
|
||||
|
||||
if (request->mAbortRequested)
|
||||
return 0;
|
||||
|
||||
// HeaderCallback is called by the handle
|
||||
assert (request->mCurrentHandle != nullptr);
|
||||
|
||||
if (request->mCurrentHandle != nullptr && request->mHttpCode == 0)
|
||||
request->mHttpCode = request->mCurrentHandle->getHTTPCode ();
|
||||
}
|
||||
|
||||
size = size * nitems;
|
||||
|
||||
if (size < 2)
|
||||
return 0;
|
||||
|
||||
const Header header = Header::Parse (std::string (buffer, size - 2));
|
||||
|
||||
std::lock_guard<std::mutex> lock (request->mHeadersMutex);
|
||||
|
||||
if (header.hasSameName ("Set-Cookie"))
|
||||
{
|
||||
request->mResponseCookies.addCookie (Cookie::Parse (header.Value));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (header.hasSameName ("Keep-Alive"))
|
||||
request->mCurrentHandle->markKeepAlive ();
|
||||
|
||||
request->mResponseHeaders.addHeader (header);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,98 +0,0 @@
|
||||
/*!********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
@file CurlResponse.h
|
||||
@brief Declare an implementation of IResponse using libcurl.
|
||||
|
||||
Dmitry Vedenko
|
||||
**********************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <cstdint>
|
||||
|
||||
#include "../IResponse.h"
|
||||
|
||||
#include "../HeadersList.h"
|
||||
#include "../CookiesList.h"
|
||||
#include "../Request.h"
|
||||
|
||||
#include "CurlHandleManager.h"
|
||||
|
||||
namespace audacity
|
||||
{
|
||||
namespace network_manager
|
||||
{
|
||||
|
||||
class CurlResponse final : public IResponse
|
||||
{
|
||||
public:
|
||||
CurlResponse (RequestVerb verb, const Request& request, CurlHandleManager* handleManager) noexcept;
|
||||
|
||||
bool isFinished () const noexcept override;
|
||||
unsigned getHTTPCode () const noexcept override;
|
||||
|
||||
NetworkError getError () const noexcept override;
|
||||
std::string getErrorString () const override;
|
||||
|
||||
bool headersReceived () const noexcept override;
|
||||
|
||||
bool hasHeader (const std::string& headerName) const noexcept override;
|
||||
std::string getHeader (const std::string& headerName) const override;
|
||||
|
||||
const HeadersList& getHeaders () const noexcept override;
|
||||
const CookiesList& getCookies () const noexcept override;
|
||||
|
||||
const Request& getRequest () const noexcept override;
|
||||
|
||||
std::string getURL () const override;
|
||||
|
||||
void abort () noexcept override;
|
||||
|
||||
void setOnDataReceivedCallback (RequestCallback callback) override;
|
||||
|
||||
void setRequestFinishedCallback (RequestCallback callback) override;
|
||||
|
||||
uint64_t getBytesAvailable () const noexcept override;
|
||||
uint64_t readData (void* buffer, uint64_t maxBytesCount) override;
|
||||
|
||||
void perform (const void* ptr, size_t size);
|
||||
private:
|
||||
RequestVerb mVerb;
|
||||
|
||||
Request mRequest;
|
||||
CurlHandleManager* mHandleManager;
|
||||
|
||||
CurlHandleManager::Handle* mCurrentHandle { nullptr };
|
||||
|
||||
mutable std::mutex mCallbackMutex;
|
||||
RequestCallback mOnDataReceivedCallback;
|
||||
RequestCallback mRequestFinishedCallback;
|
||||
|
||||
mutable std::mutex mHeadersMutex;
|
||||
HeadersList mResponseHeaders;
|
||||
CookiesList mResponseCookies;
|
||||
|
||||
mutable std::mutex mDataBufferMutex;
|
||||
std::deque<uint8_t> mDataBuffer;
|
||||
|
||||
mutable std::mutex mStatusMutex;
|
||||
|
||||
NetworkError mNetworkError { NetworkError::NoError };
|
||||
std::string mErrorString;
|
||||
|
||||
unsigned mHttpCode { 0 };
|
||||
|
||||
bool mHeadersReceived { false };
|
||||
bool mRequestFinished { false };
|
||||
bool mAbortRequested { false };
|
||||
|
||||
static size_t WriteCallback (const uint8_t* ptr, size_t size, size_t nmemb, CurlResponse* userdata) noexcept;
|
||||
static size_t HeaderCallback (const char* buffer, size_t size, size_t nitems, CurlResponse* userdata) noexcept;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
/*!********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
@file CurlResponseFactory.cpp
|
||||
@brief Define an implementation of IResponseFactory using libcurl.
|
||||
|
||||
Dmitry Vedenko
|
||||
**********************************************************************/
|
||||
|
||||
#include "CurlResponseFactory.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "CurlResponse.h"
|
||||
|
||||
namespace audacity
|
||||
{
|
||||
namespace network_manager
|
||||
{
|
||||
|
||||
constexpr decltype(std::thread::hardware_concurrency ()) MIN_CURL_THREADS = 6;
|
||||
|
||||
CurlResponseFactory::CurlResponseFactory ()
|
||||
: mThreadPool (std::make_unique<ThreadPool>(
|
||||
std::max (
|
||||
MIN_CURL_THREADS,
|
||||
std::thread::hardware_concurrency ()
|
||||
)))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void CurlResponseFactory::setProxy (const std::string& proxy)
|
||||
{
|
||||
mHandleManager->setProxy (proxy);
|
||||
}
|
||||
|
||||
ResponsePtr CurlResponseFactory::performRequest (RequestVerb verb, const Request& request)
|
||||
{
|
||||
return performRequest (verb, request, nullptr, 0);
|
||||
}
|
||||
|
||||
ResponsePtr CurlResponseFactory::performRequest (RequestVerb verb, const Request& request, const void* data, size_t size)
|
||||
{
|
||||
if (!mThreadPool)
|
||||
return {};
|
||||
|
||||
std::shared_ptr<CurlResponse> response = std::make_shared<CurlResponse> (
|
||||
verb, request, mHandleManager.get ()
|
||||
);
|
||||
|
||||
std::vector<uint8_t> buffer;
|
||||
|
||||
if (data != nullptr && size != 0)
|
||||
{
|
||||
const uint8_t* start = static_cast<const uint8_t*>(data);
|
||||
const uint8_t* end = static_cast<const uint8_t*>(data) + size;
|
||||
|
||||
buffer.insert (buffer.begin (), start, end);
|
||||
}
|
||||
|
||||
mThreadPool->enqueue ([response, dataBuffer = std::move (buffer)]() {
|
||||
if (!dataBuffer.empty())
|
||||
response->perform (dataBuffer.data (), dataBuffer.size ());
|
||||
else
|
||||
response->perform (nullptr, 0);
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
void CurlResponseFactory::terminate ()
|
||||
{
|
||||
mThreadPool.reset ();
|
||||
mHandleManager.reset ();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
/*!********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
@file CurlResponseFactory.h
|
||||
@brief Declare an implementation of IResponseFactory using libcurl.
|
||||
|
||||
Dmitry Vedenko
|
||||
**********************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include "../IResponseFactory.h"
|
||||
|
||||
#include "CurlHandleManager.h"
|
||||
#include "ThreadPool/ThreadPool.h"
|
||||
|
||||
namespace audacity
|
||||
{
|
||||
namespace network_manager
|
||||
{
|
||||
|
||||
class CurlResponseFactory final : public IResponseFactory
|
||||
{
|
||||
public:
|
||||
CurlResponseFactory ();
|
||||
|
||||
void setProxy (const std::string& proxy) override;
|
||||
|
||||
ResponsePtr performRequest (RequestVerb verb, const Request& request) override;
|
||||
ResponsePtr performRequest (RequestVerb verb, const Request& request, const void* data, size_t size) override;
|
||||
|
||||
void terminate () override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<CurlHandleManager> mHandleManager { std::make_unique<CurlHandleManager> () };
|
||||
std::unique_ptr<ThreadPool> mThreadPool;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
/*!********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
@file CurlStringList.cpp
|
||||
@brief Define a RAII wrapper for the curl_slist.
|
||||
|
||||
Dmitry Vedenko
|
||||
**********************************************************************/
|
||||
|
||||
#include "CurlStringList.h"
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
|
||||
namespace audacity
|
||||
{
|
||||
namespace network_manager
|
||||
{
|
||||
|
||||
CurlStringList::CurlStringList (CurlStringList&& rhs) noexcept
|
||||
: mList (rhs.mList)
|
||||
{
|
||||
rhs.mList = nullptr;
|
||||
}
|
||||
|
||||
CurlStringList::~CurlStringList () noexcept
|
||||
{
|
||||
curl_slist_free_all (mList);
|
||||
}
|
||||
|
||||
CurlStringList& CurlStringList::operator= (CurlStringList&& rhs) noexcept
|
||||
{
|
||||
std::swap (mList, rhs.mList);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void CurlStringList::append (const std::string& string) noexcept
|
||||
{
|
||||
mList = curl_slist_append (mList, string.c_str ());
|
||||
}
|
||||
|
||||
void CurlStringList::append (const char* string) noexcept
|
||||
{
|
||||
mList = curl_slist_append (mList, string);
|
||||
}
|
||||
|
||||
curl_slist* CurlStringList::getCurlList () const noexcept
|
||||
{
|
||||
return mList;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
/*!********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
@file CurlStringList.h
|
||||
@brief Declare a RAII wrapper for the curl_slist.
|
||||
|
||||
Dmitry Vedenko
|
||||
**********************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
struct curl_slist;
|
||||
|
||||
namespace audacity
|
||||
{
|
||||
namespace network_manager
|
||||
{
|
||||
|
||||
class CurlStringList final
|
||||
{
|
||||
public:
|
||||
CurlStringList () = default;
|
||||
CurlStringList (CurlStringList&& rhs) noexcept;
|
||||
|
||||
~CurlStringList () noexcept;
|
||||
|
||||
CurlStringList& operator = (CurlStringList&& rhs) noexcept;
|
||||
|
||||
void append (const std::string& string) noexcept;
|
||||
void append (const char* string) noexcept;
|
||||
|
||||
curl_slist* getCurlList () const noexcept;
|
||||
|
||||
private:
|
||||
curl_slist* mList { nullptr };
|
||||
};
|
||||
|
||||
}
|
||||
}
|
@ -28,7 +28,7 @@ cmake_options=(
|
||||
-Daudacity_use_mad=system # This library has bugs, that are fixed in *local* version
|
||||
-Daudacity_use_nyquist=local # This library is not available
|
||||
-Daudacity_use_vamp=local # The dev package for this library is not available
|
||||
-Daudacity_use_ogg=system
|
||||
-Daudacity_use_ogg=system
|
||||
-Daudacity_use_vorbis=system
|
||||
-Daudacity_use_flac=system
|
||||
-Daudacity_use_lv2=system
|
||||
@ -38,7 +38,6 @@ cmake_options=(
|
||||
-Daudacity_use_sbsms=local # We prefer using the latest version of sbsms
|
||||
-Daudacity_use_soundtouch=system
|
||||
-Daudacity_use_twolame=system
|
||||
-Daudacity_has_networking=yes
|
||||
-Daudacity_use_curl=system
|
||||
)
|
||||
|
||||
|
@ -11,8 +11,6 @@ cmake_args=(
|
||||
-B build
|
||||
-G "${AUDACITY_CMAKE_GENERATOR}"
|
||||
-D audacity_use_pch=no
|
||||
-D audacity_has_networking=yes
|
||||
-D audacity_has_updates_check=yes
|
||||
-D CMAKE_BUILD_TYPE="${AUDACITY_BUILD_TYPE}"
|
||||
-D CMAKE_INSTALL_PREFIX="${AUDACITY_INSTALL_PREFIX}"
|
||||
)
|
||||
|
@ -111,11 +111,6 @@ It handles initialization and termination by subclassing wxApp.
|
||||
#include "tracks/ui/Scrubbing.h"
|
||||
#include "widgets/FileConfig.h"
|
||||
#include "widgets/FileHistory.h"
|
||||
#include "update/UpdateManager.h"
|
||||
|
||||
#ifdef HAS_NETWORKING
|
||||
#include "NetworkManager.h"
|
||||
#endif
|
||||
|
||||
#ifdef EXPERIMENTAL_EASY_CHANGE_KEY_BINDINGS
|
||||
#include "prefs/KeyConfigPrefs.h"
|
||||
@ -1490,10 +1485,6 @@ bool AudacityApp::InitPart2()
|
||||
SplashDialog::DoHelpWelcome(*project);
|
||||
}
|
||||
|
||||
#if defined(HAVE_UPDATES_CHECK)
|
||||
UpdateManager::Start();
|
||||
#endif
|
||||
|
||||
#ifdef USE_FFMPEG
|
||||
FFmpegStartup();
|
||||
#endif
|
||||
@ -2243,10 +2234,6 @@ int AudacityApp::OnExit()
|
||||
// Terminate the PluginManager (must be done before deleting the locale)
|
||||
PluginManager::Get().Terminate();
|
||||
|
||||
#ifdef HAS_NETWORKING
|
||||
audacity::network_manager::NetworkManager::GetInstance().Terminate();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -980,21 +980,6 @@ list( APPEND SOURCES
|
||||
xml/XMLWriter.h
|
||||
xml/audacityproject.dtd
|
||||
|
||||
# Update version
|
||||
$<$<BOOL:${${_OPT}has_updates_check}>:
|
||||
update/VersionId.h
|
||||
update/VersionId.cpp
|
||||
update/VersionPatch.h
|
||||
update/UpdateDataParser.h
|
||||
update/UpdateDataParser.cpp
|
||||
update/UpdateManager.h
|
||||
update/UpdateManager.cpp
|
||||
update/UpdatePopupDialog.h
|
||||
update/UpdatePopupDialog.cpp
|
||||
prefs/ApplicationPrefs.h
|
||||
prefs/ApplicationPrefs.cpp
|
||||
>
|
||||
|
||||
Experimental.cmake
|
||||
)
|
||||
|
||||
@ -1051,9 +1036,6 @@ list( APPEND DEFINES
|
||||
__STDC_CONSTANT_MACROS
|
||||
STRICT
|
||||
>
|
||||
$<$<BOOL:${USE_UPDATE_CHECK}>:
|
||||
HAVE_UPDATES_CHECK
|
||||
>
|
||||
)
|
||||
|
||||
# If we have cmake 3.16 or higher, we can use precompiled headers, but
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
|
||||
#include "ApplicationPrefs.h"
|
||||
#include "update/UpdateManager.h"
|
||||
|
||||
#include <wx/defs.h>
|
||||
|
||||
|
@ -1,153 +0,0 @@
|
||||
/*!********************************************************************
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
@file UpdateDataParser.cpp
|
||||
@brief Declare a class that parses update server data format.
|
||||
|
||||
Anton Gerasimov
|
||||
**********************************************************************/
|
||||
|
||||
#include "UpdateDataParser.h"
|
||||
|
||||
#include "xml/XMLFileReader.h"
|
||||
#include "MemoryX.h"
|
||||
|
||||
UpdateDataParser::UpdateDataParser()
|
||||
{}
|
||||
|
||||
UpdateDataParser::~UpdateDataParser()
|
||||
{}
|
||||
|
||||
bool UpdateDataParser::Parse(const VersionPatch::UpdateDataFormat& updateData, VersionPatch* versionPatch)
|
||||
{
|
||||
XMLFileReader xmlReader;
|
||||
|
||||
ValueRestorer<VersionPatch*> setter{ mVersionPatch, versionPatch };
|
||||
|
||||
return xmlReader.ParseString(this, updateData);
|
||||
}
|
||||
|
||||
wxArrayString UpdateDataParser::SplitChangelogSentences(const wxString& changelogContent)
|
||||
{
|
||||
wxArrayString changelogSentenceList;
|
||||
|
||||
size_t pos = 0;
|
||||
std::string s(changelogContent.ToStdString());
|
||||
std::string token;
|
||||
const std::string delimiter(". ");
|
||||
|
||||
while ((pos = s.find(delimiter)) != std::string::npos)
|
||||
{
|
||||
token = s.substr(0, pos + 1);
|
||||
changelogSentenceList.Add(wxString(token).Trim());
|
||||
|
||||
s.erase(0, pos + delimiter.length());
|
||||
}
|
||||
changelogSentenceList.Add(wxString(s).Trim());
|
||||
|
||||
return changelogSentenceList;
|
||||
}
|
||||
|
||||
bool UpdateDataParser::HandleXMLTag(const wxChar* tag, const wxChar** attrs)
|
||||
{
|
||||
if (wxStrcmp(tag, mXmlTagNames[XmlParsedTags::kDescriptionTag]) == 0)
|
||||
{
|
||||
mXmlParsingState = XmlParsedTags::kDescriptionTag;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (wxStrcmp(tag, mXmlTagNames[XmlParsedTags::kWindowsTag]) == 0)
|
||||
{
|
||||
if (wxPlatformInfo::Get().GetOperatingSystemId() & wxOS_WINDOWS)
|
||||
mXmlParsingState = XmlParsedTags::kOsTag;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (wxStrcmp(tag, mXmlTagNames[XmlParsedTags::kMacosTag]) == 0)
|
||||
{
|
||||
if (wxPlatformInfo::Get().GetOperatingSystemId() & wxOS_MAC)
|
||||
mXmlParsingState = XmlParsedTags::kOsTag;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (wxStrcmp(tag, mXmlTagNames[XmlParsedTags::kLinuxTag]) == 0)
|
||||
{
|
||||
if (wxPlatformInfo::Get().GetOperatingSystemId() & wxOS_UNIX_LINUX)
|
||||
mXmlParsingState = XmlParsedTags::kOsTag;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (wxStrcmp(tag, mXmlTagNames[XmlParsedTags::kVersionTag]) == 0)
|
||||
{
|
||||
if (mXmlParsingState == XmlParsedTags::kOsTag)
|
||||
mXmlParsingState = XmlParsedTags::kVersionTag;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (wxStrcmp(tag, mXmlTagNames[XmlParsedTags::kLinkTag]) == 0)
|
||||
{
|
||||
if (mXmlParsingState == XmlParsedTags::kOsTag)
|
||||
mXmlParsingState = XmlParsedTags::kLinkTag;
|
||||
return true;
|
||||
}
|
||||
|
||||
for (auto& xmlTag : mXmlTagNames)
|
||||
{
|
||||
if (wxStrcmp(tag, xmlTag.second) == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void UpdateDataParser::HandleXMLEndTag(const wxChar* tag)
|
||||
{
|
||||
if (mXmlParsingState == XmlParsedTags::kDescriptionTag ||
|
||||
mXmlParsingState == XmlParsedTags::kLinkTag)
|
||||
mXmlParsingState = XmlParsedTags::kNotUsedTag;
|
||||
|
||||
// If it is our working OS, using "kOsTag" for keeping ready for parse state for both tags:
|
||||
// <Version> and <Link>, that ordered one after another.
|
||||
if (mXmlParsingState == XmlParsedTags::kVersionTag)
|
||||
mXmlParsingState = XmlParsedTags::kOsTag;
|
||||
}
|
||||
|
||||
void UpdateDataParser::HandleXMLContent(const wxString& content)
|
||||
{
|
||||
if (mVersionPatch == nullptr)
|
||||
return;
|
||||
|
||||
wxString trimmedContent(content);
|
||||
|
||||
switch (mXmlParsingState)
|
||||
{
|
||||
case XmlParsedTags::kDescriptionTag:
|
||||
trimmedContent.Trim(true).Trim(false);
|
||||
mVersionPatch->changelog = SplitChangelogSentences(trimmedContent);
|
||||
break;
|
||||
|
||||
case XmlParsedTags::kVersionTag:
|
||||
trimmedContent.Trim(true).Trim(false);
|
||||
mVersionPatch->version = VersionId::ParseFromString(trimmedContent);
|
||||
break;
|
||||
|
||||
case XmlParsedTags::kLinkTag:
|
||||
trimmedContent.Trim(true).Trim(false);
|
||||
mVersionPatch->download = trimmedContent;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
XMLTagHandler* UpdateDataParser::HandleXMLChild(const wxChar* tag)
|
||||
{
|
||||
for (auto& xmlTag : mXmlTagNames)
|
||||
{
|
||||
if (wxStrcmp(tag, xmlTag.second) == 0)
|
||||
return this;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
/*!********************************************************************
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
@file UpdateDataParser.h
|
||||
@brief Declare a class that parses update server data format.
|
||||
|
||||
Anton Gerasimov
|
||||
**********************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include "VersionPatch.h"
|
||||
|
||||
#include "xml/XMLTagHandler.h"
|
||||
|
||||
#include <wx/arrstr.h>
|
||||
#include <map>
|
||||
|
||||
/// A class that parses update server data format.
|
||||
class UpdateDataParser final : public XMLTagHandler
|
||||
{
|
||||
public:
|
||||
UpdateDataParser();
|
||||
~UpdateDataParser();
|
||||
|
||||
//! Parsing from update data format to VersionPatch fields.
|
||||
/*!
|
||||
@param updateData InputData.
|
||||
@param versionPath Parsed output data.
|
||||
@return True if success.
|
||||
*/
|
||||
bool Parse(const VersionPatch::UpdateDataFormat& updateData, VersionPatch* versionPatch);
|
||||
|
||||
private:
|
||||
enum class XmlParsedTags : int {
|
||||
kNotUsedTag,
|
||||
kUpdateTag,
|
||||
kDescriptionTag,
|
||||
kOsTag,
|
||||
kWindowsTag,
|
||||
kMacosTag,
|
||||
kLinuxTag,
|
||||
kVersionTag,
|
||||
kLinkTag
|
||||
};
|
||||
XmlParsedTags mXmlParsingState{ XmlParsedTags::kNotUsedTag };
|
||||
|
||||
std::map<XmlParsedTags, wxString> mXmlTagNames{
|
||||
{ XmlParsedTags::kUpdateTag, wxT("Updates") },
|
||||
{ XmlParsedTags::kDescriptionTag, wxT("Description") },
|
||||
{ XmlParsedTags::kOsTag, wxT("OS") },
|
||||
{ XmlParsedTags::kWindowsTag, wxT("Windows") },
|
||||
{ XmlParsedTags::kMacosTag, wxT("Macos") },
|
||||
{ XmlParsedTags::kLinuxTag, wxT("Linux") },
|
||||
{ XmlParsedTags::kVersionTag, wxT("Version") },
|
||||
{ XmlParsedTags::kLinkTag, wxT("Link") },
|
||||
};
|
||||
|
||||
bool HandleXMLTag(const wxChar* tag, const wxChar** attrs) override;
|
||||
void HandleXMLEndTag(const wxChar* tag) override;
|
||||
void HandleXMLContent(const wxString& content) override;
|
||||
XMLTagHandler* HandleXMLChild(const wxChar* tag) override;
|
||||
|
||||
wxArrayString SplitChangelogSentences(const wxString& changelogContent);
|
||||
|
||||
VersionPatch* mVersionPatch{ nullptr };
|
||||
};
|
@ -1,153 +0,0 @@
|
||||
/*!********************************************************************
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
@file UpdateManager.cpp
|
||||
@brief Declare a class that handles managing of updates.
|
||||
|
||||
Anton Gerasimov
|
||||
**********************************************************************/
|
||||
|
||||
#include "UpdateManager.h"
|
||||
#include "UpdatePopupDialog.h"
|
||||
|
||||
#include "NetworkManager.h"
|
||||
#include "IResponse.h"
|
||||
#include "Request.h"
|
||||
|
||||
#include "widgets/ErrorDialog.h"
|
||||
|
||||
#include <wx/platinfo.h>
|
||||
#include <wx/utils.h>
|
||||
#include <wx/frame.h>
|
||||
|
||||
#include <mutex>
|
||||
|
||||
BoolSetting UpdatesCheckingSettings::DefaultUpdatesCheckingFlag{
|
||||
L"/Update/DefaultUpdatesChecking", true };
|
||||
|
||||
static const char* prefsUpdateScheduledTime = "/Update/UpdateScheduledTime";
|
||||
|
||||
enum { ID_TIMER = wxID_HIGHEST + 1 };
|
||||
|
||||
BEGIN_EVENT_TABLE(UpdateManager, wxEvtHandler)
|
||||
EVT_TIMER(ID_TIMER, UpdateManager::OnTimer)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
UpdateManager::UpdateManager()
|
||||
: mUpdateCheckingInterval(
|
||||
std::chrono::milliseconds(std::chrono::hours(12)).count())
|
||||
{}
|
||||
|
||||
UpdateManager::~UpdateManager()
|
||||
{}
|
||||
|
||||
UpdateManager& UpdateManager::GetInstance()
|
||||
{
|
||||
static UpdateManager updateManager;
|
||||
|
||||
return updateManager;
|
||||
}
|
||||
|
||||
void UpdateManager::Start()
|
||||
{
|
||||
auto& instance = GetInstance();
|
||||
|
||||
static std::once_flag flag;
|
||||
std::call_once(flag, [&instance] {
|
||||
instance.mTimer.SetOwner(&instance, ID_TIMER);
|
||||
instance.mTimer.StartOnce(1);
|
||||
});
|
||||
}
|
||||
|
||||
VersionPatch UpdateManager::GetVersionPatch() const
|
||||
{
|
||||
return mVersionPatch;
|
||||
}
|
||||
|
||||
void UpdateManager::GetUpdates()
|
||||
{
|
||||
const audacity::network_manager::Request request("https://updates.audacityteam.org/feed/latest.xml");
|
||||
auto response = audacity::network_manager::NetworkManager::GetInstance().doGet(request);
|
||||
|
||||
response->setRequestFinishedCallback([response, this](audacity::network_manager::IResponse*) {
|
||||
|
||||
if (response->getError() != audacity::network_manager::NetworkError::NoError)
|
||||
{
|
||||
wxTheApp->CallAfter([] {ShowExceptionDialog(nullptr,
|
||||
XC("Error checking for update", "update dialog"),
|
||||
XC("Unable to connect to Audacity update server.", "update dialog"),
|
||||
wxString());
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mUpdateDataParser.Parse(response->readAll<VersionPatch::UpdateDataFormat>(), &mVersionPatch))
|
||||
{
|
||||
wxTheApp->CallAfter([] {ShowExceptionDialog(nullptr,
|
||||
XC("Error checking for update", "update dialog"),
|
||||
XC("Update data was corrupted.", "update dialog"),
|
||||
wxString());
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (mVersionPatch.version > CurrentBuildVersion())
|
||||
{
|
||||
wxTheApp->CallAfter([this] {
|
||||
UpdatePopupDialog dlg(nullptr, mVersionPatch);
|
||||
const int code = dlg.ShowModal();
|
||||
|
||||
if (code == wxID_YES)
|
||||
{
|
||||
if (!wxLaunchDefaultBrowser(mVersionPatch.download))
|
||||
{
|
||||
ShowExceptionDialog(nullptr,
|
||||
XC("Error downloading update.", "update dialog"),
|
||||
XC("Can't open the Audacity download link.", "update dialog"),
|
||||
wxString());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void UpdateManager::OnTimer(wxTimerEvent& WXUNUSED(event))
|
||||
{
|
||||
bool updatesCheckingEnabled = UpdatesCheckingSettings::DefaultUpdatesCheckingFlag.Read();
|
||||
|
||||
if (updatesCheckingEnabled && IsTimeForUpdatesChecking())
|
||||
GetUpdates();
|
||||
|
||||
mTimer.StartOnce(mUpdateCheckingInterval);
|
||||
}
|
||||
|
||||
bool UpdateManager::IsTimeForUpdatesChecking()
|
||||
{
|
||||
long long nextUpdatesCheckingTime = std::stoll(
|
||||
gPrefs->Read(prefsUpdateScheduledTime, "0").ToStdString());
|
||||
|
||||
// Get current time in milliseconds
|
||||
auto now_ms = std::chrono::time_point_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now());
|
||||
|
||||
auto currentTimeInMillisec = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now_ms.time_since_epoch()).count();
|
||||
|
||||
// If next update time 0 or less then current time -> show update dialog,
|
||||
// else this condition allow us to avoid from duplicating update notifications.
|
||||
if (nextUpdatesCheckingTime < currentTimeInMillisec)
|
||||
{
|
||||
nextUpdatesCheckingTime = currentTimeInMillisec + mUpdateCheckingInterval;
|
||||
|
||||
gPrefs->Write(prefsUpdateScheduledTime,
|
||||
wxString(std::to_string(nextUpdatesCheckingTime)));
|
||||
gPrefs->Flush();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
/*!********************************************************************
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
@file UpdateManager.h
|
||||
@brief Declare a class that handles managing of updates.
|
||||
|
||||
Anton Gerasimov
|
||||
**********************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include "VersionId.h"
|
||||
#include "VersionPatch.h"
|
||||
#include "UpdateDataParser.h"
|
||||
|
||||
#include "Prefs.h"
|
||||
|
||||
#include <wx/string.h>
|
||||
#include <wx/event.h>
|
||||
#include <wx/timer.h>
|
||||
|
||||
namespace UpdatesCheckingSettings {
|
||||
extern AUDACITY_DLL_API BoolSetting DefaultUpdatesCheckingFlag;
|
||||
}
|
||||
|
||||
/// A class that managing of updates.
|
||||
/**
|
||||
Opt-in request and show update dialog by the scheduled time.
|
||||
Have a built-in check that allows avoiding multiplying update notifications
|
||||
when multiple Audacity windows are shown.
|
||||
*/
|
||||
class UpdateManager final : public wxEvtHandler
|
||||
{
|
||||
public:
|
||||
UpdateManager();
|
||||
~UpdateManager();
|
||||
|
||||
static UpdateManager& GetInstance();
|
||||
static void Start();
|
||||
|
||||
void GetUpdates();
|
||||
|
||||
VersionPatch GetVersionPatch() const;
|
||||
|
||||
private:
|
||||
UpdateDataParser mUpdateDataParser;
|
||||
VersionPatch mVersionPatch;
|
||||
|
||||
wxTimer mTimer;
|
||||
const int mUpdateCheckingInterval;
|
||||
|
||||
void OnTimer(wxTimerEvent& event);
|
||||
|
||||
/// Scheduling update time for avoiding multiplying update notifications.
|
||||
bool IsTimeForUpdatesChecking();
|
||||
|
||||
public:
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
@ -1,136 +0,0 @@
|
||||
/*!********************************************************************
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
@file UpdatePopupDialog.cpp
|
||||
@brief Define a dialog for notifying users about new version available.
|
||||
|
||||
Anton Gerasimov
|
||||
**********************************************************************/
|
||||
|
||||
#include "UpdatePopupDialog.h"
|
||||
#include "UpdateManager.h"
|
||||
|
||||
#include "ShuttleGui.h"
|
||||
#include "widgets/HelpSystem.h"
|
||||
|
||||
#include <wx/debug.h>
|
||||
#include <wx/sstream.h>
|
||||
#include <wx/txtstrm.h>
|
||||
|
||||
enum { DontShowID = wxID_HIGHEST + 1 };
|
||||
|
||||
BEGIN_EVENT_TABLE (UpdatePopupDialog, wxDialogWrapper)
|
||||
EVT_BUTTON (wxID_YES, UpdatePopupDialog::OnUpdate)
|
||||
EVT_BUTTON (wxID_NO, UpdatePopupDialog::OnSkip)
|
||||
EVT_CHECKBOX (DontShowID, UpdatePopupDialog::OnDontShow)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
IMPLEMENT_CLASS (UpdatePopupDialog, wxDialogWrapper)
|
||||
|
||||
UpdatePopupDialog::UpdatePopupDialog (wxWindow* parent, const VersionPatch& versionPatch)
|
||||
: wxDialogWrapper (parent, -1, XC("Update Audacity", "update dialog"),
|
||||
wxDefaultPosition, wxDefaultSize,
|
||||
wxCAPTION),
|
||||
mVersionPatch(versionPatch)
|
||||
{
|
||||
ShuttleGui S (this, eIsCreating);
|
||||
S.SetBorder (5);
|
||||
S.StartVerticalLay (wxEXPAND, 1);
|
||||
{
|
||||
S.AddWindow (AddHtmlContent (S.GetParent()));
|
||||
|
||||
S.StartHorizontalLay (wxEXPAND, 0);
|
||||
{
|
||||
S.SetBorder (5);
|
||||
|
||||
S.Id (DontShowID).AddCheckBox (
|
||||
XO ("Don't show this again at start up"),
|
||||
!UpdatesCheckingSettings::DefaultUpdatesCheckingFlag.Read());
|
||||
|
||||
S.Prop(1).AddSpace(1, 0, 1);
|
||||
|
||||
S.Id (wxID_NO).AddButton (XC ("&Skip", "update dialog"));
|
||||
S.Id (wxID_YES).AddButton (XC("&Install update", "update dialog"));
|
||||
|
||||
S.SetBorder (5);
|
||||
}
|
||||
S.EndHorizontalLay();
|
||||
}
|
||||
S.EndVerticalLay();
|
||||
|
||||
Layout();
|
||||
Fit();
|
||||
Center();
|
||||
}
|
||||
|
||||
UpdatePopupDialog::~UpdatePopupDialog()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
void UpdatePopupDialog::OnUpdate (wxCommandEvent&)
|
||||
{
|
||||
EndModal (wxID_YES);
|
||||
}
|
||||
|
||||
void UpdatePopupDialog::OnSkip (wxCommandEvent&)
|
||||
{
|
||||
EndModal (wxID_NO);
|
||||
}
|
||||
|
||||
void UpdatePopupDialog::OnDontShow (wxCommandEvent& event)
|
||||
{
|
||||
UpdatesCheckingSettings::DefaultUpdatesCheckingFlag.Write(!event.IsChecked());
|
||||
}
|
||||
|
||||
HtmlWindow* UpdatePopupDialog::AddHtmlContent (wxWindow* parent)
|
||||
{
|
||||
wxStringOutputStream o;
|
||||
wxTextOutputStream informationStr (o);
|
||||
|
||||
informationStr
|
||||
<< wxT("<html><body><h3>")
|
||||
// i18n-hint Substitution of version number for %s.
|
||||
<< XC("Audacity %s is available!", "update dialog").Format(mVersionPatch.version.GetString()).Translation()
|
||||
<< wxT("</h3><h5>")
|
||||
<< XC("Changelog", "update dialog")
|
||||
<< wxT("</h5><p>");
|
||||
|
||||
informationStr << wxT("<ul>");
|
||||
for (auto& logLine : mVersionPatch.changelog)
|
||||
{
|
||||
informationStr << wxT("<li>");
|
||||
// We won't to translate downloaded text.
|
||||
informationStr << logLine;
|
||||
informationStr << wxT("</li>");
|
||||
}
|
||||
informationStr << wxT("</ul></p>");
|
||||
|
||||
informationStr << wxT("<p>");
|
||||
informationStr << wxT("<a href = \"https://github.com/audacity/audacity/releases\">");
|
||||
informationStr << XC("Read more on GitHub", "update dialog");
|
||||
informationStr << wxT("</a>");
|
||||
informationStr << wxT("</p>");
|
||||
|
||||
informationStr << wxT("</body></html>");
|
||||
|
||||
HtmlWindow* html = safenew LinkingHtmlWindow (parent, -1,
|
||||
wxDefaultPosition,
|
||||
wxSize (500, -1),
|
||||
wxHW_SCROLLBAR_AUTO | wxSUNKEN_BORDER);
|
||||
|
||||
html->SetBorders (20);
|
||||
html->SetPage (o.GetString());
|
||||
|
||||
wxHtmlContainerCell* cell = html->GetInternalRepresentation();
|
||||
|
||||
cell->Layout (500);
|
||||
|
||||
const wxSize size = wxSize (500, cell->GetHeight());
|
||||
|
||||
html->SetMinSize (size);
|
||||
html->SetMaxSize (size);
|
||||
html->SetSize (size);
|
||||
|
||||
return html;
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
/*!********************************************************************
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
@file UpdatePopupDialog.h
|
||||
@brief Define a dialog for notifying users about new version available.
|
||||
|
||||
Anton Gerasimov
|
||||
**********************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "widgets/wxPanelWrapper.h"
|
||||
#include "wx/string.h"
|
||||
|
||||
#include "VersionPatch.h"
|
||||
|
||||
class HtmlWindow;
|
||||
class wxWindow;
|
||||
|
||||
/// Show dialog window with update information for the user.
|
||||
class UpdatePopupDialog final : public wxDialogWrapper
|
||||
{
|
||||
DECLARE_DYNAMIC_CLASS (AboutDialog)
|
||||
public:
|
||||
explicit UpdatePopupDialog (wxWindow* parent, const VersionPatch& versionPatch);
|
||||
virtual ~UpdatePopupDialog();
|
||||
|
||||
void OnUpdate (wxCommandEvent& event);
|
||||
void OnSkip (wxCommandEvent& event);
|
||||
void OnDontShow (wxCommandEvent& event);
|
||||
|
||||
DECLARE_EVENT_TABLE()
|
||||
|
||||
private:
|
||||
HtmlWindow* AddHtmlContent (wxWindow* parent);
|
||||
|
||||
const VersionPatch& mVersionPatch;
|
||||
};
|
@ -1,71 +0,0 @@
|
||||
/*!********************************************************************
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
@file VersionId.h
|
||||
@brief Declare a class with version number manipulation.
|
||||
|
||||
Anton Gerasimov
|
||||
**********************************************************************/
|
||||
#include "VersionId.h"
|
||||
|
||||
VersionId::VersionId(int version, int release, int revision)
|
||||
: mVersion(version),
|
||||
mRelease(release),
|
||||
mRevision(revision)
|
||||
{}
|
||||
|
||||
wxString VersionId::MakeString(int version, int release, int revision)
|
||||
{
|
||||
return std::to_string(version)
|
||||
+ "." + std::to_string(release)
|
||||
+ "." + std::to_string(revision);
|
||||
}
|
||||
|
||||
VersionId VersionId::ParseFromString(wxString& versionString)
|
||||
{
|
||||
auto versionStringParts = wxSplit(versionString, '.');
|
||||
|
||||
// If we have corrupted version string,
|
||||
// then return the zero version number, that not allow us to update.
|
||||
if (versionStringParts.size() != 3)
|
||||
return VersionId{};
|
||||
|
||||
for (auto& v : versionStringParts)
|
||||
{
|
||||
if (v.empty() || !v.IsNumber())
|
||||
return VersionId{};
|
||||
}
|
||||
|
||||
return VersionId(
|
||||
std::stoi(versionStringParts[0].ToStdString()),
|
||||
std::stoi(versionStringParts[1].ToStdString()),
|
||||
std::stoi(versionStringParts[2].ToStdString())
|
||||
);
|
||||
}
|
||||
|
||||
wxString VersionId::GetString() const
|
||||
{
|
||||
return MakeString(mVersion, mRelease, mRevision);
|
||||
}
|
||||
|
||||
bool VersionId::operator== (const VersionId& other)
|
||||
{
|
||||
return std::tie(mVersion, mRelease, mRevision) ==
|
||||
std::tie(other.mVersion, other.mRelease, other.mRevision);
|
||||
}
|
||||
|
||||
bool VersionId::operator!= (const VersionId& other)
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool VersionId::operator< (const VersionId& other)
|
||||
{
|
||||
return std::tie(mVersion, mRelease, mRevision) <
|
||||
std::tie(other.mVersion, other.mRelease, other.mRevision);
|
||||
}
|
||||
|
||||
bool VersionId::operator> (const VersionId& other)
|
||||
{
|
||||
return !(*this < other) && (*this != other);
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
/*!********************************************************************
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
@file VersionId.h
|
||||
@brief Declare a class with version number manipulation.
|
||||
|
||||
Anton Gerasimov
|
||||
**********************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include <wx/arrstr.h>
|
||||
|
||||
/// A class, that supports base manipulation with version number.
|
||||
/**
|
||||
By default initialized by zero version number (Version = 0, Release = 0, Revision = 0),
|
||||
that not allow us to update.
|
||||
*/
|
||||
class VersionId final
|
||||
{
|
||||
public:
|
||||
/// Creates an zero version object.
|
||||
VersionId() = default;
|
||||
VersionId(int version, int release, int revision);
|
||||
|
||||
/// Creates version string like "1.2.3" by parameters.
|
||||
static wxString MakeString(int version, int release, int revision);
|
||||
/// Parse and return version object from version string like "1.2.3".
|
||||
static VersionId ParseFromString(wxString& versionString);
|
||||
|
||||
/// Make string with version by MakeString() from instance values.
|
||||
wxString GetString() const;
|
||||
|
||||
bool operator== (const VersionId& other);
|
||||
bool operator!= (const VersionId& other);
|
||||
|
||||
bool operator< (const VersionId& other);
|
||||
bool operator> (const VersionId& other);
|
||||
|
||||
private:
|
||||
int mVersion{ 0 };
|
||||
int mRelease{ 0 };
|
||||
int mRevision{ 0 };
|
||||
};
|
||||
|
||||
/// Return version (VersionId) object with current Audacity build version.
|
||||
static inline VersionId CurrentBuildVersion()
|
||||
{
|
||||
return VersionId{ AUDACITY_VERSION, AUDACITY_RELEASE, AUDACITY_REVISION };
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
/*!********************************************************************
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
@file VersionPatch.h
|
||||
@brief Declare a structure that describes patch fields.
|
||||
|
||||
Anton Gerasimov
|
||||
**********************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include "VersionId.h"
|
||||
|
||||
/// A structure that describes patch fields.
|
||||
struct VersionPatch final
|
||||
{
|
||||
using UpdateDataFormat = std::string;
|
||||
|
||||
VersionPatch() = default;
|
||||
|
||||
VersionId version;
|
||||
wxArrayString changelog;
|
||||
wxString download;
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user