2015-12-21 20:30:33 +00:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2015 Mark Roszko <mark.roszko@gmail.com>
|
2021-07-15 19:26:35 +00:00
|
|
|
* Copyright (C) 2015-2021 KiCad Developers, see AUTHORS.txt for contributors.
|
2015-12-21 20:30:33 +00:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 3
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, you may find one here:
|
|
|
|
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|
|
|
* or you may search the http://www.gnu.org website for the version 2 license,
|
|
|
|
* or you may write to the Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
|
|
*/
|
|
|
|
|
2021-08-28 13:31:45 +00:00
|
|
|
// kicad_curl_easy.h **must be** included before any wxWidgets header to avoid conflicts
|
|
|
|
// at least on Windows/msys2
|
2021-10-17 23:34:53 +00:00
|
|
|
#include <curl/curl.h>
|
|
|
|
#include <curl/easy.h>
|
2021-08-28 13:31:45 +00:00
|
|
|
#include <kicad_curl/kicad_curl.h>
|
2015-12-21 20:30:33 +00:00
|
|
|
#include <kicad_curl/kicad_curl_easy.h>
|
|
|
|
|
2019-12-05 14:03:15 +00:00
|
|
|
#include <cstdarg>
|
2015-12-21 20:30:33 +00:00
|
|
|
#include <cstddef>
|
|
|
|
#include <exception>
|
2019-12-05 14:03:15 +00:00
|
|
|
#include <sstream>
|
2021-08-21 00:23:55 +00:00
|
|
|
#include <wx/app.h>
|
|
|
|
|
|
|
|
#include <build_version.h>
|
|
|
|
#include <ki_exception.h> // THROW_IO_ERROR
|
|
|
|
#include <kiplatform/app.h>
|
2021-11-11 14:29:25 +00:00
|
|
|
#include <kiplatform/environment.h>
|
2021-08-21 00:23:55 +00:00
|
|
|
#include <pgm_base.h>
|
2015-12-21 20:30:33 +00:00
|
|
|
|
|
|
|
|
2020-10-26 10:54:36 +00:00
|
|
|
struct CURL_PROGRESS
|
|
|
|
{
|
|
|
|
KICAD_CURL_EASY* curl;
|
|
|
|
TRANSFER_CALLBACK callback;
|
|
|
|
curl_off_t last_run_time;
|
|
|
|
curl_off_t interval;
|
|
|
|
CURL_PROGRESS( KICAD_CURL_EASY* c, TRANSFER_CALLBACK cb, curl_off_t i ) :
|
|
|
|
curl( c ), callback( cb ), last_run_time( 0 ), interval( i )
|
|
|
|
{
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2016-01-10 21:44:37 +00:00
|
|
|
static size_t write_callback( void* contents, size_t size, size_t nmemb, void* userp )
|
2015-12-21 20:30:33 +00:00
|
|
|
{
|
2016-01-10 21:44:37 +00:00
|
|
|
size_t realsize = size * nmemb;
|
2015-12-21 20:30:33 +00:00
|
|
|
|
2021-11-24 19:23:35 +00:00
|
|
|
std::string* p = static_cast<std::string*>( userp );
|
2015-12-21 20:30:33 +00:00
|
|
|
|
2021-11-24 19:23:35 +00:00
|
|
|
p->append( static_cast<const char*>( contents ), realsize );
|
2015-12-21 20:30:33 +00:00
|
|
|
|
2016-01-10 21:44:37 +00:00
|
|
|
return realsize;
|
2015-12-21 20:30:33 +00:00
|
|
|
}
|
|
|
|
|
2021-09-07 19:11:28 +00:00
|
|
|
|
2020-10-26 10:54:36 +00:00
|
|
|
static size_t stream_write_callback( void* contents, size_t size, size_t nmemb, void* userp )
|
|
|
|
{
|
|
|
|
size_t realsize = size * nmemb;
|
|
|
|
|
|
|
|
std::ostream* p = (std::ostream*) userp;
|
|
|
|
|
2021-11-24 19:23:35 +00:00
|
|
|
p->write( static_cast<const char*>( contents ), realsize );
|
2020-10-26 10:54:36 +00:00
|
|
|
|
|
|
|
return realsize;
|
|
|
|
}
|
|
|
|
|
2015-12-21 20:30:33 +00:00
|
|
|
|
2021-08-28 13:31:45 +00:00
|
|
|
static int xferinfo( void* p, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal,
|
2021-09-07 19:11:28 +00:00
|
|
|
curl_off_t ulnow )
|
2021-08-28 13:31:45 +00:00
|
|
|
{
|
2021-11-24 19:23:35 +00:00
|
|
|
CURL_PROGRESS* progress = static_cast<CURL_PROGRESS*>( p );
|
2021-08-28 13:31:45 +00:00
|
|
|
curl_off_t curtime = 0;
|
|
|
|
|
2021-08-30 12:11:42 +00:00
|
|
|
curl_easy_getinfo( progress->curl->GetCurl(), CURLINFO_TOTAL_TIME, &curtime );
|
2021-08-28 13:31:45 +00:00
|
|
|
|
|
|
|
if( curtime - progress->last_run_time >= progress->interval )
|
|
|
|
{
|
|
|
|
progress->last_run_time = curtime;
|
|
|
|
return progress->callback( dltotal, dlnow, ultotal, ulnow );
|
|
|
|
}
|
|
|
|
|
|
|
|
return CURLE_OK;
|
|
|
|
}
|
|
|
|
|
2021-09-07 19:11:28 +00:00
|
|
|
|
2021-11-24 19:23:35 +00:00
|
|
|
static int progressinfo( void* p, double dltotal, double dlnow, double ultotal, double ulnow )
|
|
|
|
{
|
|
|
|
return xferinfo( p, static_cast<curl_off_t>( dltotal ), static_cast<curl_off_t>( dlnow ),
|
|
|
|
static_cast<curl_off_t>( ultotal ), static_cast<curl_off_t>( ulnow ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-10-26 10:54:36 +00:00
|
|
|
KICAD_CURL_EASY::KICAD_CURL_EASY() : m_headers( nullptr )
|
2015-12-21 20:30:33 +00:00
|
|
|
{
|
2021-09-07 19:11:28 +00:00
|
|
|
// Call KICAD_CURL::Init() from in here every time, but only the first time
|
2016-01-10 21:44:37 +00:00
|
|
|
// will incur any overhead. This strategy ensures that libcurl is never loaded
|
|
|
|
// unless it is needed.
|
2015-12-21 20:30:33 +00:00
|
|
|
|
2016-01-10 21:44:37 +00:00
|
|
|
KICAD_CURL::Init();
|
2015-12-21 20:30:33 +00:00
|
|
|
|
2016-01-14 15:17:13 +00:00
|
|
|
m_CURL = curl_easy_init();
|
2015-12-21 20:30:33 +00:00
|
|
|
|
2016-01-10 21:44:37 +00:00
|
|
|
if( !m_CURL )
|
2015-12-21 20:30:33 +00:00
|
|
|
{
|
2016-01-10 21:44:37 +00:00
|
|
|
THROW_IO_ERROR( "Unable to initialize CURL session" );
|
2015-12-21 20:30:33 +00:00
|
|
|
}
|
|
|
|
|
2016-01-14 15:17:13 +00:00
|
|
|
curl_easy_setopt( m_CURL, CURLOPT_WRITEFUNCTION, write_callback );
|
|
|
|
curl_easy_setopt( m_CURL, CURLOPT_WRITEDATA, (void*) &m_buffer );
|
2021-08-21 00:23:55 +00:00
|
|
|
|
2021-10-17 23:34:53 +00:00
|
|
|
// Only allow HTTP and HTTPS protocols
|
|
|
|
curl_easy_setopt( m_CURL, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS );
|
|
|
|
|
2022-01-22 03:09:34 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
// We need to this to allow using the Windows Certificate store
|
|
|
|
curl_easy_setopt( m_CURL, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA );
|
|
|
|
#endif
|
|
|
|
|
2021-08-21 00:23:55 +00:00
|
|
|
wxPlatformInfo platformInfo;
|
|
|
|
wxString application( Pgm().App().GetAppName() );
|
|
|
|
wxString version( GetBuildVersion() );
|
2021-11-13 08:52:08 +00:00
|
|
|
wxString platform = "(" + wxGetOsDescription() + ";" + GetPlatformGetBitnessName();
|
2021-08-21 00:23:55 +00:00
|
|
|
|
|
|
|
#if defined( KICAD_BUILD_ARCH_X64 )
|
|
|
|
platform << ";64-bit";
|
|
|
|
#elif defined( KICAD_BUILD_ARCH_X86 )
|
|
|
|
platform << ";32-bit";
|
|
|
|
#elif defined( KICAD_BUILD_ARCH_ARM )
|
|
|
|
platform << ";ARM 32-bit";
|
|
|
|
#elif defined( KICAD_BUILD_ARCH_ARM64 )
|
|
|
|
platform << ";ARM 64-bit";
|
|
|
|
#endif
|
|
|
|
|
|
|
|
platform << ")";
|
|
|
|
|
|
|
|
wxString user_agent = "KiCad/" + version + " " + platform + " " + application;
|
|
|
|
|
|
|
|
user_agent << "/" << GetBuildDate();
|
|
|
|
setOption<const char*>( CURLOPT_USERAGENT, user_agent.ToStdString().c_str() );
|
2020-10-26 10:54:36 +00:00
|
|
|
setOption( CURLOPT_ACCEPT_ENCODING, "gzip,deflate" );
|
2015-12-21 20:30:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-01-10 21:44:37 +00:00
|
|
|
KICAD_CURL_EASY::~KICAD_CURL_EASY()
|
2015-12-21 20:30:33 +00:00
|
|
|
{
|
2016-01-10 21:44:37 +00:00
|
|
|
if( m_headers )
|
2016-01-14 15:17:13 +00:00
|
|
|
curl_slist_free_all( m_headers );
|
2015-12-21 20:30:33 +00:00
|
|
|
|
2016-01-14 15:17:13 +00:00
|
|
|
curl_easy_cleanup( m_CURL );
|
2015-12-21 20:30:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-10-26 10:54:36 +00:00
|
|
|
int KICAD_CURL_EASY::Perform()
|
2015-12-21 20:30:33 +00:00
|
|
|
{
|
2016-01-10 21:44:37 +00:00
|
|
|
if( m_headers )
|
2015-12-21 20:30:33 +00:00
|
|
|
{
|
2016-01-14 15:17:13 +00:00
|
|
|
curl_easy_setopt( m_CURL, CURLOPT_HTTPHEADER, m_headers );
|
2015-12-21 20:30:33 +00:00
|
|
|
}
|
|
|
|
|
2016-01-10 21:44:37 +00:00
|
|
|
// bonus: retain worst case memory allocation, should re-use occur
|
|
|
|
m_buffer.clear();
|
|
|
|
|
2016-01-14 15:17:13 +00:00
|
|
|
CURLcode res = curl_easy_perform( m_CURL );
|
2015-12-21 20:30:33 +00:00
|
|
|
|
2020-10-26 10:54:36 +00:00
|
|
|
return res;
|
2016-01-10 21:44:37 +00:00
|
|
|
}
|
2017-09-04 13:42:56 +00:00
|
|
|
|
|
|
|
|
|
|
|
void KICAD_CURL_EASY::SetHeader( const std::string& aName, const std::string& aValue )
|
|
|
|
{
|
|
|
|
std::string header = aName + ':' + aValue;
|
|
|
|
m_headers = curl_slist_append( m_headers, header.c_str() );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-10-26 10:54:36 +00:00
|
|
|
template <typename T>
|
|
|
|
int KICAD_CURL_EASY::setOption( int aOption, T aArg )
|
2017-09-04 13:42:56 +00:00
|
|
|
{
|
2021-11-24 19:23:35 +00:00
|
|
|
return curl_easy_setopt( m_CURL, static_cast<CURLoption>( aOption ), aArg );
|
2017-09-04 13:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const std::string KICAD_CURL_EASY::GetErrorText( int aCode )
|
|
|
|
{
|
2021-11-24 19:23:35 +00:00
|
|
|
return curl_easy_strerror( static_cast<CURLcode>( aCode ) );
|
2017-09-04 13:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool KICAD_CURL_EASY::SetUserAgent( const std::string& aAgent )
|
|
|
|
{
|
|
|
|
if( setOption<const char*>( CURLOPT_USERAGENT, aAgent.c_str() ) == CURLE_OK )
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
2021-07-15 19:26:35 +00:00
|
|
|
|
2017-09-04 13:42:56 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool KICAD_CURL_EASY::SetURL( const std::string& aURL )
|
|
|
|
{
|
2020-10-26 10:54:36 +00:00
|
|
|
if( setOption<const char*>( CURLOPT_URL, aURL.c_str() ) == CURLE_OK )
|
2017-09-04 13:42:56 +00:00
|
|
|
{
|
2021-11-11 14:29:25 +00:00
|
|
|
KIPLATFORM::ENV::PROXY_CONFIG cfg;
|
|
|
|
|
|
|
|
// Unforunately on Windows land, proxies can be configured depending on destination url
|
|
|
|
// So we also check and set any proxy config here
|
|
|
|
if( KIPLATFORM::ENV::GetSystemProxyConfig( aURL, cfg ) )
|
|
|
|
{
|
2021-11-11 15:58:47 +00:00
|
|
|
curl_easy_setopt( m_CURL, CURLOPT_PROXY, static_cast<const char*>( cfg.host.c_str() ) );
|
2021-11-12 03:47:12 +00:00
|
|
|
if( !cfg.username.empty() )
|
2021-11-11 14:29:25 +00:00
|
|
|
{
|
2021-11-11 15:58:47 +00:00
|
|
|
curl_easy_setopt( m_CURL, CURLOPT_PROXYUSERNAME,
|
|
|
|
static_cast<const char*>( cfg.username.c_str() ) );
|
2021-11-11 14:29:25 +00:00
|
|
|
}
|
|
|
|
|
2021-11-12 03:47:12 +00:00
|
|
|
if( !cfg.password.empty() )
|
2021-11-11 14:29:25 +00:00
|
|
|
{
|
2021-11-11 15:58:47 +00:00
|
|
|
curl_easy_setopt( m_CURL, CURLOPT_PROXYPASSWORD,
|
|
|
|
static_cast<const char*>( cfg.password.c_str() ) );
|
2021-11-11 14:29:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-04 13:42:56 +00:00
|
|
|
return true;
|
|
|
|
}
|
2021-07-15 19:26:35 +00:00
|
|
|
|
2017-09-04 13:42:56 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool KICAD_CURL_EASY::SetFollowRedirects( bool aFollow )
|
|
|
|
{
|
2020-10-26 10:54:36 +00:00
|
|
|
if( setOption<long>( CURLOPT_FOLLOWLOCATION, ( aFollow ? 1 : 0 ) ) == CURLE_OK )
|
2017-09-04 13:42:56 +00:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
2021-07-15 19:26:35 +00:00
|
|
|
|
2017-09-04 13:42:56 +00:00
|
|
|
return false;
|
|
|
|
}
|
2020-02-21 01:58:51 +00:00
|
|
|
|
|
|
|
|
|
|
|
std::string KICAD_CURL_EASY::Escape( const std::string& aUrl )
|
|
|
|
{
|
|
|
|
char* escaped = curl_easy_escape( m_CURL, aUrl.c_str(), aUrl.length() );
|
|
|
|
|
|
|
|
std::string ret( escaped );
|
|
|
|
curl_free( escaped );
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2020-10-26 10:54:36 +00:00
|
|
|
|
|
|
|
|
|
|
|
bool KICAD_CURL_EASY::SetTransferCallback( const TRANSFER_CALLBACK& aCallback, size_t aInterval )
|
|
|
|
{
|
2021-11-24 19:23:35 +00:00
|
|
|
progress = std::make_unique<CURL_PROGRESS>( this, aCallback,
|
|
|
|
static_cast<curl_off_t>( aInterval ) );
|
|
|
|
#ifdef CURLOPT_XFERINFOFUNCTION
|
2020-10-26 10:54:36 +00:00
|
|
|
setOption( CURLOPT_XFERINFOFUNCTION, xferinfo );
|
|
|
|
setOption( CURLOPT_XFERINFODATA, progress.get() );
|
2021-11-24 19:23:35 +00:00
|
|
|
#else
|
|
|
|
setOption( CURLOPT_PROGRESSFUNCTION, progressinfo );
|
|
|
|
setOption( CURLOPT_PROGRESSDATA, progress.get() );
|
|
|
|
#endif
|
2020-10-26 10:54:36 +00:00
|
|
|
setOption( CURLOPT_NOPROGRESS, 0L );
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool KICAD_CURL_EASY::SetOutputStream( const std::ostream* aOutput )
|
|
|
|
{
|
|
|
|
curl_easy_setopt( m_CURL, CURLOPT_WRITEFUNCTION, stream_write_callback );
|
2021-11-24 19:49:13 +00:00
|
|
|
curl_easy_setopt( m_CURL, CURLOPT_WRITEDATA, reinterpret_cast<const void*>( aOutput ) );
|
2020-10-26 10:54:36 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int KICAD_CURL_EASY::GetTransferTotal( uint64_t& aDownloadedBytes ) const
|
|
|
|
{
|
2021-11-24 19:23:35 +00:00
|
|
|
#ifdef CURLINFO_SIZE_DOWNLOAD_T
|
2020-10-26 10:54:36 +00:00
|
|
|
curl_off_t dl;
|
|
|
|
int result = curl_easy_getinfo( m_CURL, CURLINFO_SIZE_DOWNLOAD_T, &dl );
|
2021-11-24 19:23:35 +00:00
|
|
|
aDownloadedBytes = static_cast<uint64_t>( dl );
|
|
|
|
#else
|
|
|
|
double dl;
|
|
|
|
int result = curl_easy_getinfo( m_CURL, CURLINFO_SIZE_DOWNLOAD, &dl );
|
|
|
|
aDownloadedBytes = static_cast<uint64_t>( dl );
|
|
|
|
#endif
|
2020-10-26 10:54:36 +00:00
|
|
|
return result;
|
|
|
|
}
|