KiCad PCB EDA Suite
kicad_curl_easy.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2015 Mark Roszko <[email protected]>
5 * Copyright (C) 2015-2021 KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 3
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25// kicad_curl_easy.h **must be** included before any wxWidgets header to avoid conflicts
26// at least on Windows/msys2
27#include <curl/curl.h>
28#include <curl/easy.h>
31
32#include <cstdarg>
33#include <cstddef>
34#include <exception>
35#include <sstream>
36#include <wx/app.h>
37
38#include <build_version.h>
39#include <ki_exception.h> // THROW_IO_ERROR
40#include <kiplatform/app.h>
42#include <pgm_base.h>
43
44
46{
49 curl_off_t m_Last_run_time;
50 curl_off_t m_Interval;
51
52 CURL_PROGRESS( KICAD_CURL_EASY* aCURL, TRANSFER_CALLBACK aCallback, curl_off_t aInterval ) :
53 m_Curl( aCURL ),
54 m_Callback( aCallback ),
55 m_Last_run_time( 0 ),
56 m_Interval( aInterval )
57 {
58 }
59};
60
61
62static size_t write_callback( void* aContents, size_t aSize, size_t aNmemb, void* aUserp )
63{
64 size_t realsize = aSize * aNmemb;
65
66 std::string* p = static_cast<std::string*>( aUserp );
67
68 p->append( static_cast<const char*>( aContents ), realsize );
69
70 return realsize;
71}
72
73
74static size_t stream_write_callback( void* aContents, size_t aSize, size_t aNmemb, void* aUserp )
75{
76 size_t realsize = aSize * aNmemb;
77
78 std::ostream* p = static_cast<std::ostream*>( aUserp );
79
80 p->write( static_cast<const char*>( aContents ), realsize );
81
82 return realsize;
83}
84
85
86static int xferinfo( void* aProgress, curl_off_t aDLtotal, curl_off_t aDLnow, curl_off_t aULtotal,
87 curl_off_t aULnow )
88{
89 CURL_PROGRESS* progress = static_cast<CURL_PROGRESS*>( aProgress );
90 curl_off_t curtime = 0;
91
92 curl_easy_getinfo( progress->m_Curl->GetCurl(), CURLINFO_TOTAL_TIME, &curtime );
93
94 if( curtime - progress->m_Last_run_time >= progress->m_Interval )
95 {
96 progress->m_Last_run_time = curtime;
97 return progress->m_Callback( aDLtotal, aDLnow, aULtotal, aULnow );
98 }
99
100 return CURLE_OK;
101}
102
103
104static int progressinfo( void* aProgress, double aDLtotal, double aDLnow, double aULtotal, double aULnow )
105{
106 return xferinfo( aProgress, static_cast<curl_off_t>( aDLtotal ), static_cast<curl_off_t>( aDLnow ),
107 static_cast<curl_off_t>( aULtotal ), static_cast<curl_off_t>( aULnow ) );
108}
109
110
112 m_headers( nullptr )
113{
114 // Call KICAD_CURL::Init() from in here every time, but only the first time
115 // will incur any overhead. This strategy ensures that libcurl is never loaded
116 // unless it is needed.
117
119
120 m_CURL = curl_easy_init();
121
122 if( !m_CURL )
123 THROW_IO_ERROR( "Unable to initialize CURL session" );
124
125 curl_easy_setopt( m_CURL, CURLOPT_WRITEFUNCTION, write_callback );
126 curl_easy_setopt( m_CURL, CURLOPT_WRITEDATA, static_cast<void*>( &m_buffer ) );
127
128 // Only allow HTTP and HTTPS protocols
129 curl_easy_setopt( m_CURL, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS );
130
131 #ifdef _WIN32
132 // We need this to use the Windows Certificate store
133 curl_easy_setopt( m_CURL, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA );
134 #endif
135
136 wxPlatformInfo platformInfo;
137 wxString application( Pgm().App().GetAppName() );
138 wxString version( GetBuildVersion() );
139 wxString platform = "(" + wxGetOsDescription() + ";" + GetPlatformGetBitnessName();
140
141#if defined( KICAD_BUILD_ARCH_X64 )
142 platform << ";64-bit";
143#elif defined( KICAD_BUILD_ARCH_X86 )
144 platform << ";32-bit";
145#elif defined( KICAD_BUILD_ARCH_ARM )
146 platform << ";ARM 32-bit";
147#elif defined( KICAD_BUILD_ARCH_ARM64 )
148 platform << ";ARM 64-bit";
149#endif
150
151 platform << ")";
152
153 wxString user_agent = "KiCad/" + version + " " + platform + " " + application;
154
155 user_agent << "/" << GetBuildDate();
156 setOption<const char*>( CURLOPT_USERAGENT, user_agent.ToStdString().c_str() );
157 setOption( CURLOPT_ACCEPT_ENCODING, "gzip,deflate" );
158}
159
160
162{
163 if( m_headers )
164 curl_slist_free_all( m_headers );
165
166 curl_easy_cleanup( m_CURL );
167}
168
169
171{
172 if( m_headers )
173 curl_easy_setopt( m_CURL, CURLOPT_HTTPHEADER, m_headers );
174
175 // bonus: retain worst case memory allocation, should re-use occur
176 m_buffer.clear();
177
178 return curl_easy_perform( m_CURL );
179}
180
181
182void KICAD_CURL_EASY::SetHeader( const std::string& aName, const std::string& aValue )
183{
184 std::string header = aName + ':' + aValue;
185 m_headers = curl_slist_append( m_headers, header.c_str() );
186}
187
188
189template <typename T>
190int KICAD_CURL_EASY::setOption( int aOption, T aArg )
191{
192 return curl_easy_setopt( m_CURL, static_cast<CURLoption>( aOption ), aArg );
193}
194
195
196const std::string KICAD_CURL_EASY::GetErrorText( int aCode )
197{
198 return curl_easy_strerror( static_cast<CURLcode>( aCode ) );
199}
200
201
202bool KICAD_CURL_EASY::SetUserAgent( const std::string& aAgent )
203{
204 if( setOption<const char*>( CURLOPT_USERAGENT, aAgent.c_str() ) == CURLE_OK )
205 return true;
206
207 return false;
208}
209
210
211bool KICAD_CURL_EASY::SetURL( const std::string& aURL )
212{
213 if( setOption<const char*>( CURLOPT_URL, aURL.c_str() ) == CURLE_OK )
214 {
216
217 // Unfortunately on Windows land, proxies can be configured depending on destination url
218 // So we also check and set any proxy config here
220 {
221 curl_easy_setopt( m_CURL, CURLOPT_PROXY, static_cast<const char*>( cfg.host.c_str() ) );
222
223 if( !cfg.username.empty() )
224 {
225 curl_easy_setopt( m_CURL, CURLOPT_PROXYUSERNAME,
226 static_cast<const char*>( cfg.username.c_str() ) );
227 }
228
229 if( !cfg.password.empty() )
230 {
231 curl_easy_setopt( m_CURL, CURLOPT_PROXYPASSWORD,
232 static_cast<const char*>( cfg.password.c_str() ) );
233 }
234 }
235
236 return true;
237 }
238
239 return false;
240}
241
242
244{
245 if( setOption<long>( CURLOPT_FOLLOWLOCATION, ( aFollow ? 1 : 0 ) ) == CURLE_OK )
246 return true;
247
248 return false;
249}
250
251
252std::string KICAD_CURL_EASY::Escape( const std::string& aUrl )
253{
254 char* escaped = curl_easy_escape( m_CURL, aUrl.c_str(), aUrl.length() );
255
256 std::string ret( escaped );
257 curl_free( escaped );
258
259 return ret;
260}
261
262
263bool KICAD_CURL_EASY::SetTransferCallback( const TRANSFER_CALLBACK& aCallback, size_t aInterval )
264{
265 progress = std::make_unique<CURL_PROGRESS>( this, aCallback,
266 static_cast<curl_off_t>( aInterval ) );
267#ifdef CURLOPT_XFERINFOFUNCTION
268 setOption( CURLOPT_XFERINFOFUNCTION, xferinfo );
269 setOption( CURLOPT_XFERINFODATA, progress.get() );
270#else
271 setOption( CURLOPT_PROGRESSFUNCTION, progressinfo );
272 setOption( CURLOPT_PROGRESSDATA, progress.get() );
273#endif
274 setOption( CURLOPT_NOPROGRESS, 0L );
275 return true;
276}
277
278
279bool KICAD_CURL_EASY::SetOutputStream( const std::ostream* aOutput )
280{
281 curl_easy_setopt( m_CURL, CURLOPT_WRITEFUNCTION, stream_write_callback );
282 curl_easy_setopt( m_CURL, CURLOPT_WRITEDATA, reinterpret_cast<const void*>( aOutput ) );
283 return true;
284}
285
286
287int KICAD_CURL_EASY::GetTransferTotal( uint64_t& aDownloadedBytes ) const
288{
289#ifdef CURLINFO_SIZE_DOWNLOAD_T
290 curl_off_t dl;
291 int result = curl_easy_getinfo( m_CURL, CURLINFO_SIZE_DOWNLOAD_T, &dl );
292 aDownloadedBytes = static_cast<uint64_t>( dl );
293#else
294 double dl;
295 int result = curl_easy_getinfo( m_CURL, CURLINFO_SIZE_DOWNLOAD, &dl );
296 aDownloadedBytes = static_cast<uint64_t>( dl );
297#endif
298 return result;
299}
wxString GetBuildVersion()
Get the full KiCad version string.
wxString GetPlatformGetBitnessName()
wxString GetBuildDate()
Get the build date as a string.
std::string m_buffer
int Perform()
Equivalent to curl_easy_perform.
std::unique_ptr< CURL_PROGRESS > progress
std::string Escape(const std::string &aUrl)
Escapes a string for use as a URL.
bool SetUserAgent(const std::string &aAgent)
Set the request user agent.
curl_slist * m_headers
void SetHeader(const std::string &aName, const std::string &aValue)
Set an arbitrary header for the HTTP(s) request.
bool SetTransferCallback(const TRANSFER_CALLBACK &aCallback, size_t aInterval)
bool SetURL(const std::string &aURL)
Set the request URL.
bool SetFollowRedirects(bool aFollow)
Enable the following of HTTP(s) and other redirects, by default curl does not follow redirects.
int GetTransferTotal(uint64_t &aDownloadedBytes) const
int setOption(int aOption, T aArg)
Set a curl option, only supports single parameter curl options.
bool SetOutputStream(const std::ostream *aOutput)
const std::string GetErrorText(int aCode)
Fetch CURL's "friendly" error string for a given error code.
static void Init()
Call curl_global_init for the application.
Definition: kicad_curl.cpp:50
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:38
static int progressinfo(void *aProgress, double aDLtotal, double aDLnow, double aULtotal, double aULnow)
static size_t write_callback(void *aContents, size_t aSize, size_t aNmemb, void *aUserp)
static size_t stream_write_callback(void *aContents, size_t aSize, size_t aNmemb, void *aUserp)
static int xferinfo(void *aProgress, curl_off_t aDLtotal, curl_off_t aDLnow, curl_off_t aULtotal, curl_off_t aULnow)
std::function< int(size_t, size_t, size_t, size_t)> TRANSFER_CALLBACK
Wrapper interface around the curl_easy API/.
bool GetSystemProxyConfig(const wxString &aURL, PROXY_CONFIG &aCfg)
Retrieves platform level proxying requirements to reach the given url.
see class PGM_BASE
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:111
curl_off_t m_Interval
CURL_PROGRESS(KICAD_CURL_EASY *aCURL, TRANSFER_CALLBACK aCallback, curl_off_t aInterval)
TRANSFER_CALLBACK m_Callback
KICAD_CURL_EASY * m_Curl
curl_off_t m_Last_run_time