From e47bc6883da53af1e953b6973771b935ae17c99d Mon Sep 17 00:00:00 2001 From: Mark Roszko Date: Mon, 21 Dec 2015 15:30:33 -0500 Subject: [PATCH] Replace the avhttp library used by the github plugin with libcurl. --- CMakeLists.txt | 3 + common/CMakeLists.txt | 6 +- common/kicad_curl/kicad_curl.cpp | 54 +++++++++ common/kicad_curl/kicad_curl_easy.cpp | 163 +++++++++++++++++++++++++ common/pgm_base.cpp | 9 ++ include/kicad_curl/kicad_curl.h | 82 +++++++++++++ include/kicad_curl/kicad_curl_easy.h | 166 ++++++++++++++++++++++++++ pcbnew/github/CMakeLists.txt | 28 +---- pcbnew/github/github_getliblist.cpp | 68 ++++++----- pcbnew/github/github_getliblist.h | 4 +- pcbnew/github/github_plugin.cpp | 151 +++++------------------ pcbnew/github/github_plugin.h | 6 +- 12 files changed, 558 insertions(+), 182 deletions(-) create mode 100644 common/kicad_curl/kicad_curl.cpp create mode 100644 common/kicad_curl/kicad_curl_easy.cpp create mode 100644 include/kicad_curl/kicad_curl.h create mode 100644 include/kicad_curl/kicad_curl_easy.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 660711eac3..6993336481 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -457,6 +457,9 @@ endif() # Find GLM library find_package( GLM 0.9.5.4 REQUIRED ) +# Find CURL library +find_package( CURL REQUIRED ) + # Find Cairo library if( NOT CAIRO_FOUND ) find_package( Cairo 1.8.1 QUIET ) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 45a5a24191..9d8b71fc09 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -5,6 +5,7 @@ include_directories( ${CAIRO_INCLUDE_DIR} ${GLEW_INCLUDE_DIR} ${GLM_INCLUDE_DIR} + ${CURL_INCLUDE_DIRS} ../3d-viewer ../pcbnew ../polygon @@ -254,6 +255,9 @@ endif() set( COMMON_SRCS ${COMMON_SRCS} + kicad_curl/kicad_curl.cpp + kicad_curl/kicad_curl_easy.cpp + view/view.cpp view/view_item.cpp view/view_group.cpp @@ -279,7 +283,7 @@ set( COMMON_SRCS add_library( common STATIC ${COMMON_SRCS} ) add_dependencies( common lib-dependencies ) add_dependencies( common version_header ) -target_link_libraries( common ${Boost_LIBRARIES} ) +target_link_libraries( common ${Boost_LIBRARIES} ${CURL_LIBRARIES} ) set( PCB_COMMON_SRCS diff --git a/common/kicad_curl/kicad_curl.cpp b/common/kicad_curl/kicad_curl.cpp new file mode 100644 index 0000000000..7424eec870 --- /dev/null +++ b/common/kicad_curl/kicad_curl.cpp @@ -0,0 +1,54 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015 Mark Roszko + * Copyright (C) 2015 KiCad Developers, see CHANGELOG.TXT for contributors. + * + * 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 + */ + +#include + +bool KICAD_CURL::Init() +{ + if ( curl_global_init( CURL_GLOBAL_ALL ) != CURLE_OK ) + { + return false; + } + else + { + m_initialized = true; + return true; + } +} + + +void KICAD_CURL::Cleanup() +{ + if( m_initialized ) + curl_global_cleanup(); +} + + +std::string KICAD_CURL::GetVersion() +{ + return std::string( curl_version() ); +} + + +bool KICAD_CURL::m_initialized = false; \ No newline at end of file diff --git a/common/kicad_curl/kicad_curl_easy.cpp b/common/kicad_curl/kicad_curl_easy.cpp new file mode 100644 index 0000000000..71a410d0d0 --- /dev/null +++ b/common/kicad_curl/kicad_curl_easy.cpp @@ -0,0 +1,163 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015 Mark Roszko + * Copyright (C) 2015 KiCad Developers, see CHANGELOG.TXT for contributors. + * + * 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 + */ + +#include + +#include +#include +#include +#include +#include + +static size_t write_callback (void *contents, size_t size, size_t nmemb, void *userp); + + +KICAD_CURL_EASY::KICAD_CURL_EASY() + : m_headers( NULL ) +{ + m_CURL = curl_easy_init(); + + if( m_CURL == NULL ) + { + THROW_IO_ERROR( "Unable to initialize CURL session" ); + } + + m_Buffer.payload = (char*)malloc( 1 ); + m_Buffer.size = 0; + + curl_easy_setopt( m_CURL, CURLOPT_WRITEFUNCTION, write_callback ); + curl_easy_setopt( m_CURL, CURLOPT_WRITEDATA, (void *)&m_Buffer ); +} + + +KICAD_CURL_EASY::~KICAD_CURL_EASY() +{ + free(m_Buffer.payload); + curl_easy_cleanup(m_CURL); +} + + +bool KICAD_CURL_EASY::SetURL( const std::string& aURL ) +{ + if( SetOption( CURLOPT_URL, aURL.c_str() ) == CURLE_OK ) + { + return true; + } + return false; +} + + +bool KICAD_CURL_EASY::SetUserAgent( const std::string& aAgent ) +{ + if( SetOption( CURLOPT_USERAGENT, aAgent.c_str() ) == CURLE_OK ) + { + return true; + } + return false; +} + + +bool KICAD_CURL_EASY::SetFollowRedirects( bool aFollow ) +{ + if( SetOption( CURLOPT_FOLLOWLOCATION , (aFollow ? 1 : 0) ) == CURLE_OK ) + { + return true; + } + return false; +} + + +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() ); +} + + +std::string KICAD_CURL_EASY::GetErrorText(CURLcode code) +{ + return curl_easy_strerror(code); +} + + +static size_t write_callback( void *contents, size_t size, size_t nmemb, void *userp ) +{ + /* calculate buffer size */ + size_t realsize = size * nmemb; + + /* cast pointer to fetch struct */ + struct KICAD_EASY_CURL_BUFFER *p = ( struct KICAD_EASY_CURL_BUFFER * ) userp; + + /* expand buffer */ + p->payload = (char *) realloc( p->payload, p->size + realsize + 1 ); + + /* check buffer */ + if ( p->payload == NULL ) + { + wxLogError( wxT( "Failed to expand buffer in curl_callback" ) ); + + /* free buffer */ + free( p->payload ); + + return -1; + } + + /* copy contents to buffer */ + memcpy( &(p->payload[p->size]), contents, realsize ); + + /* set new buffer size */ + p->size += realsize; + + /* ensure null termination */ + p->payload[p->size] = 0; + + /* return size */ + return realsize; +} + + +void KICAD_CURL_EASY::Perform() +{ + if( m_headers != NULL ) + { + curl_easy_setopt( m_CURL, CURLOPT_HTTPHEADER, m_headers ); + } + + if( m_Buffer.size > 0 ) + { + free( m_Buffer.payload ); + m_Buffer.payload = (char*)malloc( 1 ); + m_Buffer.size = 0; + } + + CURLcode res = curl_easy_perform( m_CURL ); + if( res != CURLE_OK ) + { + wxString msg = wxString::Format( + _( "CURL Request Failed: %s" ), + GetErrorText( res ) ); + + THROW_IO_ERROR( msg ); + } +} \ No newline at end of file diff --git a/common/pgm_base.cpp b/common/pgm_base.cpp index 039a8572c4..6606ee1b0a 100644 --- a/common/pgm_base.cpp +++ b/common/pgm_base.cpp @@ -30,6 +30,7 @@ * (locale handling) */ +#include /* Include before any wx file */ #include #include #include @@ -282,6 +283,7 @@ PGM_BASE::PGM_BASE() PGM_BASE::~PGM_BASE() { destroy(); + KICAD_CURL::Cleanup(); } @@ -493,6 +495,13 @@ bool PGM_BASE::initPgm() wxSystemOptions::SetOption( wxOSX_FILEDIALOG_ALWAYS_SHOW_TYPES, 1 ); #endif + // Initialize CURL + wxLogDebug( wxT( "Using %s" ), KICAD_CURL::GetVersion() ); + if( !KICAD_CURL::Init() ) + { + wxLogDebug( wxT( "Error initializing libcurl" ) ); + } + return true; } diff --git a/include/kicad_curl/kicad_curl.h b/include/kicad_curl/kicad_curl.h new file mode 100644 index 0000000000..3639400c92 --- /dev/null +++ b/include/kicad_curl/kicad_curl.h @@ -0,0 +1,82 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015 Mark Roszko + * Copyright (C) 2015 KiCad Developers, see CHANGELOG.TXT for contributors. + * + * 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 + */ +#ifndef KICAD_CURL_H_ +#define KICAD_CURL_H_ + +/* + * KICAD_CURL.h must be included before wxWidgets because on Windows, + * wxWidgets ends up including windows.h before winsocks2.h inside curl + * this causes build warnings + * Because we are before wx, we must explicitly define we are building with unicode. + * wxWidgets defaults to supporting unicode now, so this should be safe. + */ +#if defined(WIN32) + #ifndef UNICODE + # define UNICODE + #endif + + #ifndef _UNICODE + # define _UNICODE + #endif +#endif + +#include +#include + +/** + * Class KICAD_CURL + * simple wrapper class to call curl_global_init and curl_global_cleanup for KiCad. + */ +class KICAD_CURL +{ +public: + /** + * Function Init + * calls curl_global_init for the application. It must be used only once + * and before any curl functions that perform requests. + * + * @return bool - True if successful, false if CURL returned an error + */ + static bool Init(); + + /** + * Function Cleanup + * calls curl_global_cleanup for the application. It must be used only after + * curl_global_init was called. + */ + static void Cleanup(); + + /** + * Function GetVersion + * wrapper for curl_version(). Reports back a short string of loaded libraries. + * + * @return std::string - String reported by libcurl + */ + static std::string GetVersion(); + +private: + static bool m_initialized; +}; + +#endif // KICAD_CURL_H_ \ No newline at end of file diff --git a/include/kicad_curl/kicad_curl_easy.h b/include/kicad_curl/kicad_curl_easy.h new file mode 100644 index 0000000000..a87c3d02b1 --- /dev/null +++ b/include/kicad_curl/kicad_curl_easy.h @@ -0,0 +1,166 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015 Mark Roszko + * Copyright (C) 2015 KiCad Developers, see CHANGELOG.TXT for contributors. + * + * 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 + */ +#ifndef KICAD_CURL_EASY_H_ +#define KICAD_CURL_EASY_H_ + +/* + * KICAD_CURL_EASY.h must included before wxWidgets because on Windows, + * wxWidgets ends up including windows.h before winsocks2.h inside curl + * this causes build warnings + * Because we are before wx, we must explicitly define we are building with unicode + * wxWidgets defaults to supporting unicode now, so this should be safe. + */ +#if defined(WIN32) + #ifndef UNICODE + # define UNICODE + #endif + + #ifndef _UNICODE + # define _UNICODE + #endif +#endif + + +#include +#include + +/** + * Struct KICAD_EASY_CURL_BUFFER + * is a struct used for storing the libcurl received data in its callbacks. + * Do not use directly, KICAD_CURL_EASY uses it. + */ +struct KICAD_EASY_CURL_BUFFER +{ + char* payload; + size_t size; +}; + + +/** + * Class KICAD_CURL_EASY + * wrapper interface around the curl_easy API + * + * Handling of using the curl_easy API to make a request and save the response to + * a memory buffer + * + * Here is a small example usage: + * @code + * KICAD_CURL_EASY curl; + * curl.SetURL("http://github.com"); + * curl.SetUserAgent("KiCad-EDA"); + * curl.SetHeader("Accept", "application/json"); + * curl.Perform(); + * @endcode + */ +class KICAD_CURL_EASY +{ +public: + KICAD_CURL_EASY(); + ~KICAD_CURL_EASY(); + + /** + * Function perform + * equivalent to curl_easy_perform. Executes the request + * that was previously setup. + * + * @throw IO_ERROR, if there is a CURL request error + */ + void Perform(); + + /** + * Function SetHeader + * sets an arbitrary header for the HTTP(s) request. + * + * @param aName is the left hand side of the header, i.e. Accept without the colon + * @param aValue is the right hand side of the header, i.e. application/json + */ + void SetHeader( const std::string& aName, const std::string& aValue ); + + /** + * Function SetUserAgent + * sets the request user agent + * + * @param aAgent is the string to set for the user agent + * @return bool - True if successful, false if not + */ + bool SetUserAgent( const std::string& aAgent ); + + /** + * Function SetURL + * sets the request URL + * + * @param aURL is the URL + * @return bool - True if successful, false if not + */ + bool SetURL( const std::string& aURL ); + + /** + * Function SetFollowRedirects + * enables the following of HTTP(s) and other redirects, by default curl + * does not follow redirects. + * + * @param aFollow is a boolean where true will enable following redirects + * @return bool - True if successful, false if not + */ + bool SetFollowRedirects( bool aFollow ); + + /** + * Function GetErrorText + * fetches CURL's "friendly" error string for a given error code + * + * @param aCode is CURL error code + * @return std::string - the corresponding error string for the given code + */ + std::string GetErrorText( CURLcode aCode ); + + /** + * Function SetOption + * sets a curl option, only supports single parameter curl options + * + * @param aOption is CURL option, see CURL manual for options + * @param aArg is the argument being passed to CURL, ensure it is the right type per manual + * @return CURLcode - CURL error code, will return CURLE_OK unless a problem was encountered + */ + template CURLcode SetOption( CURLoption aOption, T aArg ) + { + return curl_easy_setopt( m_CURL, aOption, aArg ); + } + + /** + * Function GetBuffer + * returns a const pointer to the data buffer + * + * @return KICAD_EASY_CURL_BUFFER* - pointer to buffer + */ + const KICAD_EASY_CURL_BUFFER* GetBuffer() + { + return &m_Buffer; + } +private: + CURL *m_CURL; + struct curl_slist *m_headers; + struct KICAD_EASY_CURL_BUFFER m_Buffer; +}; + +#endif // KICAD_CURL_EASY_H_ \ No newline at end of file diff --git a/pcbnew/github/CMakeLists.txt b/pcbnew/github/CMakeLists.txt index c1a4ee1f0f..758c0fc7a7 100644 --- a/pcbnew/github/CMakeLists.txt +++ b/pcbnew/github/CMakeLists.txt @@ -24,28 +24,15 @@ # Download avhttp and install the headers, not actually compiled ################################################# -include( download_avhttp ) - -find_package( OpenSSL REQUIRED ) -#message( STATUS "OPENSSL_FOUND:${OPENSSL_FOUND} OPENSSL_LIBRARIES:${OPENSSL_LIBRARIES}" ) - -# FindOpenSSL.cmake does not set this var into cache, so is not globally visible, -# do it here incase some other link image needs these libraries -set( OPENSSL_LIBRARIES "${OPENSSL_LIBRARIES}" CACHE FILEPATH "OpenSSL link libraries" ) -set( OPENSSL_INCLUDE_DIR "${OPENSSL_INCLUDE_DIR}" CACHE FILEPATH "OpenSSL include dir" ) # These are additions to any inherited from pcbnew dir: -include_directories( . ${OPENSSL_INCLUDE_DIR} ${AVHTTP_INCLUDE_DIR} ) +include_directories( . ) # Tell AVHTTP we have SSL. -add_definitions( -DAVHTTP_ENABLE_OPENSSL ) - -# tone down the compiler warnings for avhttp header library: -set( CMAKE_CXX_FLAGS - "${CMAKE_CXX_FLAGS} -Wno-sign-compare -Wno-reorder -Wno-unused-variable -Wno-unused-function -Wno-strict-aliasing" ) - set( GITHUB_PLUGIN_SRCS - github_plugin.cpp github_getliblist.cpp html_link_parser.cpp + github_plugin.cpp + github_getliblist.cpp + html_link_parser.cpp ) add_library( github_plugin STATIC ${GITHUB_PLUGIN_SRCS} ) @@ -53,7 +40,7 @@ add_library( github_plugin STATIC ${GITHUB_PLUGIN_SRCS} ) # No, you don't get github without boost and openssl. Boost_LIBRARIES now moved up # into CMakeLists.txt for pcbnew and cvpcb: target_link_libraries( github_plugin - ${OPENSSL_LIBRARIES} + common ) if( MINGW ) @@ -64,9 +51,4 @@ if( MINGW ) endif() add_dependencies( github_plugin boost ) -add_dependencies( github_plugin avhttp ) - -if( MINGW AND NOT OPENSSL_ROOT_DIR ) - add_dependencies( github_plugin openssl ) -endif() diff --git a/pcbnew/github/github_getliblist.cpp b/pcbnew/github/github_getliblist.cpp index 7ef26553b2..4802013946 100644 --- a/pcbnew/github/github_getliblist.cpp +++ b/pcbnew/github/github_getliblist.cpp @@ -41,35 +41,7 @@ * JP Charras. */ - -#if 0 -/* - * FIX ME - * I do not include avhttp.hpp here, because it is already included in - * github_plugin.cpp - * and if it is also included in this file, the link fails (double definiton of modules) - * therefore, the GITHUB_GETLIBLIST method which uses avhttp to download dats from gitub - * is in github_plugin.cpp - */ - -#ifndef WIN32_LEAN_AND_MEAN -// when WIN32_LEAN_AND_MEAN is defined, some useless includes in -// are skipped, and this avoid some compil issues -#define WIN32_LEAN_AND_MEAN -#endif - -#ifdef WIN32 -// defines needed by avhttp -// Minimal Windows version is XP: Google for _WIN32_WINNT - #define _WIN32_WINNT 0x0501 - #define WINVER 0x0501 -#endif - -#include -#include - -#endif - +#include /* Include before any wx file */ #include #include @@ -97,7 +69,7 @@ bool GITHUB_GETLIBLIST::Get3DshapesLibsList( wxArrayString* aList, wxString errorMsg; fullURLCommand = repoURL.utf8_str(); - bool success = remote_get_json( &fullURLCommand, &errorMsg ); + bool success = remoteGetJSON( fullURLCommand, &errorMsg ); if( !success ) { @@ -147,7 +119,7 @@ bool GITHUB_GETLIBLIST::GetFootprintLibraryList( wxArrayString& aList ) while( 1 ) { - bool success = remote_get_json( &fullURLCommand, &errorMsg ); + bool success = remoteGetJSON( fullURLCommand, &errorMsg ); if( !success ) { @@ -232,3 +204,37 @@ bool GITHUB_GETLIBLIST::repoURL2listURL( const wxString& aRepoURL, return false; } + + +bool GITHUB_GETLIBLIST::remoteGetJSON( const std::string& aFullURLCommand, wxString* aMsgError ) +{ + KICAD_CURL_EASY kcurl; + + wxLogDebug( wxT( "Attempting to download: " ) + aFullURLCommand ); + + kcurl.SetURL(aFullURLCommand); + kcurl.SetUserAgent("KiCad-EDA"); + kcurl.SetHeader("Accept", m_option_string); + kcurl.SetFollowRedirects(true); + + try + { + kcurl.Perform(); + m_image.assign(kcurl.GetBuffer()->payload, kcurl.GetBuffer()->size); + return true; + } + catch( const IO_ERROR& ioe ) + { + if( aMsgError ) + { + UTF8 fmt( _( "Error fetching JSON data from URL '%s'.\nReason: '%s'" ) ); + + std::string msg = StrPrintf( fmt.c_str(), + aFullURLCommand.c_str(), + TO_UTF8( ioe.errorText ) ); + + *aMsgError = FROM_UTF8( msg.c_str() ); + } + return false; + } +} \ No newline at end of file diff --git a/pcbnew/github/github_getliblist.h b/pcbnew/github/github_getliblist.h index 6fd059800f..0d38b8aebc 100644 --- a/pcbnew/github/github_getliblist.h +++ b/pcbnew/github/github_getliblist.h @@ -103,7 +103,7 @@ protected: int aItemCountMax, int aPage = 1 ); /** - * Function remote_get_json + * Function remoteGetJSON * Download a json text from a github repo. The text image * is received into the m_input_stream. * @param aFullURLCommand the full command, i.e. the url with options like @@ -111,7 +111,7 @@ protected: * @param aMsgError a pointer to a wxString which can store an error message * @return true if OK, false if error (which an error message in *aMsgError */ - bool remote_get_json( std::string* aFullURLCommand, wxString* aMsgError ); + bool remoteGetJSON( const std::string& aFullURLCommand, wxString* aMsgError ); wxString m_github_path; ///< Something like https://api.github.com/orgs/KiCad std::string m_image; ///< image of the downloaded data in its entirety. diff --git a/pcbnew/github/github_plugin.cpp b/pcbnew/github/github_plugin.cpp index 5da5c40a49..0039734478 100644 --- a/pcbnew/github/github_plugin.cpp +++ b/pcbnew/github/github_plugin.cpp @@ -38,8 +38,7 @@ I have lost my enthusiasm for local caching until a faster time stamp retrieval mechanism can be found, or github gets more servers. But note that the occasionally slow response is the exception rather than the norm. Normally the response is down around a 1/3 of a second. The information we would use is in the header -named "Last-Modified" as seen below. This would need parsing, but avhttp may -offer some help there, if not, then boost async probably does. +named "Last-Modified" as seen below. HTTP/1.1 200 OK @@ -64,20 +63,7 @@ X-GitHub-Request-Id: 411087C2:659E:50FD6E6:52E67F66 Vary: Accept-Encoding */ - - -#ifndef WIN32_LEAN_AND_MEAN -// when WIN32_LEAN_AND_MEAN is defined, some useless includes in -// are skipped, and this avoid some compil issues -#define WIN32_LEAN_AND_MEAN -#endif - -#ifdef WIN32 - // defines needed by avhttp - // Minimal Windows version is XP: Google for _WIN32_WINNT - #define _WIN32_WINNT 0x0501 - #define WINVER 0x0501 -#endif +#include /* Include before any wx file */ #include #include @@ -88,10 +74,6 @@ Vary: Accept-Encoding #include #include -// Under Windows Mingw/msys, avhttp.hpp should be included after fctsys.h -// in fact after wx/wx.h, included by fctsys.h, -// to avoid issues (perhaps due to incompatible defines) -#include // chinese SSL magic #include #include @@ -103,6 +85,7 @@ Vary: Accept-Encoding #include // ExpandSubstitutions() #include + using namespace std; @@ -431,7 +414,7 @@ void GITHUB_PLUGIN::cacheLib( const wxString& aLibraryPath, const PROPERTIES* aP m_gh_cache = new GH_CACHE(); // INIT_LOGGER( "/tmp", "test.log" ); - remote_get_zip( aLibraryPath ); + remoteGetZip( aLibraryPath ); // UNINIT_LOGGER(); m_lib_path = aLibraryPath; @@ -460,7 +443,7 @@ void GITHUB_PLUGIN::cacheLib( const wxString& aLibraryPath, const PROPERTIES* aP } -bool GITHUB_PLUGIN::repoURL_zipURL( const wxString& aRepoURL, string* aZipURL ) +bool GITHUB_PLUGIN::repoURL_zipURL( const wxString& aRepoURL, std::string& aZipURL ) { // e.g. "https://github.com/liftoff-sr/pretty_footprints" //D(printf("aRepoURL:%s\n", TO_UTF8( aRepoURL ) );) @@ -470,12 +453,12 @@ bool GITHUB_PLUGIN::repoURL_zipURL( const wxString& aRepoURL, string* aZipURL ) if( repo.HasServer() && repo.HasPath() ) { // scheme might be "http" or if truly github.com then "https". - wxString zip_url = repo.GetScheme(); - - zip_url += "://"; + wxString zip_url; if( repo.GetServer() == "github.com" ) { + //codeload.github.com only supports https + zip_url = "https://"; #if 0 // A proper code path would be this one, but it is not the fastest. zip_url += repo.GetServer(); zip_url += repo.GetPath(); // path comes with a leading '/' @@ -488,8 +471,6 @@ bool GITHUB_PLUGIN::repoURL_zipURL( const wxString& aRepoURL, string* aZipURL ) // In order to bypass this redirect, saving time, we use the // redirected URL on first attempt to save one HTTP GET hit. - // avhttp would do the redirect behind the scenes normally, but that would - // be slower than doing this bypass. zip_url += "codeload.github.com"; zip_url += repo.GetPath(); // path comes with a leading '/' zip_url += "/zip/master"; @@ -498,9 +479,11 @@ bool GITHUB_PLUGIN::repoURL_zipURL( const wxString& aRepoURL, string* aZipURL ) else { + zip_url = repo.GetScheme(); + zip_url += "://"; + // This is the generic code path for any server which can serve // up zip files. The schemes tested include: http and https. - // (I don't know what the avhttp library supports beyond that.) // zip_url goal: "://[:]/" @@ -526,124 +509,48 @@ bool GITHUB_PLUGIN::repoURL_zipURL( const wxString& aRepoURL, string* aZipURL ) // this code path with the needs of one particular inflexible server. } - *aZipURL = zip_url.utf8_str(); + aZipURL = zip_url.utf8_str(); return true; } return false; } -void GITHUB_PLUGIN::remote_get_zip( const wxString& aRepoURL ) throw( IO_ERROR ) +void GITHUB_PLUGIN::remoteGetZip( const wxString& aRepoURL ) throw( IO_ERROR ) { - string zip_url; + std::string zip_url; - if( !repoURL_zipURL( aRepoURL, &zip_url ) ) + if( !repoURL_zipURL( aRepoURL, zip_url ) ) { wxString msg = wxString::Format( _( "Unable to parse URL:\n'%s'" ), GetChars( aRepoURL ) ); THROW_IO_ERROR( msg ); } - boost::asio::io_service io; - avhttp::http_stream h( io ); - avhttp::request_opts options; + wxLogDebug( wxT( "Attempting to download: " ) + zip_url ); - options.insert( "Accept", "application/zip" ); - options.insert( "User-Agent", "http://kicad-pcb.org" ); // THAT WOULD BE ME. - h.request_options( options ); + KICAD_CURL_EASY kcurl; + + kcurl.SetURL(zip_url.c_str()); + kcurl.SetUserAgent("KiCad-EDA"); + kcurl.SetHeader("Accept", "application/zip"); + kcurl.SetFollowRedirects(true); try { - ostringstream os; - - h.open( zip_url ); // only one file, therefore do it synchronously. - os << &h; - - // Keep zip file byte image in RAM. That plus the MODULE_MAP will constitute - // the cache. We don't cache the MODULEs per se, we parse those as needed from - // this zip file image. - m_zip_image = os.str(); - - // 4 lines, using SSL, top that. + kcurl.Perform(); + m_zip_image.assign(kcurl.GetBuffer()->payload, kcurl.GetBuffer()->size); } - catch( const boost::system::system_error& e ) + catch( const IO_ERROR& ioe ) { - // https "GET" has faild, report this to API caller. - static const char errorcmd[] = "http GET command failed"; // Do not translate this message - UTF8 fmt( _( "%s\nCannot get/download Zip archive: '%s'\nfor library path: '%s'.\nReason: '%s'" ) ); - string msg = StrPrintf( fmt.c_str(), - errorcmd, - // Report both secret zip_url and Lib Path, to user. The secret - // zip_url may go bad at some point in future if github changes - // their server architecture. Then fix repoURL_zipURL() to reflect - // new architecture. - zip_url.c_str(), TO_UTF8( aRepoURL ), - e.what() ); + std::string msg = StrPrintf( fmt.c_str(), + zip_url.c_str(), + TO_UTF8( aRepoURL ), + TO_UTF8( ioe.errorText ) ); THROW_IO_ERROR( msg ); } - catch( const exception& exc ) - { - UTF8 error( _( "Exception '%s' in avhttp while open()-ing URI:'%s'" ) ); - - string msg = StrPrintf( error.c_str(), exc.what(), zip_url.c_str() ); - THROW_IO_ERROR( msg ); - } -} - - -// This GITHUB_GETLIBLIST method should not be here, but in github_getliblist.cpp ! -// However it is here just because we need to include to compile it. -// and when we include avhttp in two .cpp files, the link fails because it detects duplicate -// avhttp functions. -// So until it is fixed, this code is here. -bool GITHUB_GETLIBLIST::remote_get_json( std::string* aFullURLCommand, wxString* aMsgError ) -{ - boost::asio::io_service io; - avhttp::http_stream h( io ); - avhttp::request_opts options; - - - options.insert( "Accept", m_option_string ); - options.insert( "User-Agent", "http://kicad-pcb.org" ); // THAT WOULD BE ME. - h.request_options( options ); - - try - { - std::ostringstream os; - - h.open( *aFullURLCommand ); // only one file, therefore do it synchronously. - os << &h; - - // Keep downloaded text file image in RAM. - m_image = os.str(); - - // 4 lines, using SSL, top that. - } - catch( boost::system::system_error& e ) - { - // https "GET" has faild, report this to API caller. - static const char errorcmd[] = "https GET command failed"; // Do not translate this message - - UTF8 fmt( _( "%s\nCannot get/download data from: '%s'\nReason: '%s'" ) ); - - std::string msg = StrPrintf( fmt.c_str(), - errorcmd, - // Report secret list_url to user. The secret - // list_url may go bad at some point in future if github changes - // their server architecture. Then fix repoURL_zipURL() to reflect - // new architecture. - aFullURLCommand->c_str(), e.what() ); - - if( aMsgError ) - { - *aMsgError = FROM_UTF8( msg.c_str() ); - return false; - } - } - - return true; } #if 0 && defined(STANDALONE) diff --git a/pcbnew/github/github_plugin.h b/pcbnew/github/github_plugin.h index 5a041d0581..8588fc955f 100644 --- a/pcbnew/github/github_plugin.h +++ b/pcbnew/github/github_plugin.h @@ -210,14 +210,14 @@ protected: * @param aZipURL is where to put the zip file URL. * @return bool - true if @a aRepoULR was parseable, else false */ - static bool repoURL_zipURL( const wxString& aRepoURL, std::string* aZipURL ); + static bool repoURL_zipURL( const wxString& aRepoURL, std::string& aZipURL ); /** - * Function remote_get_zip + * Function remoteGetZip * fetches a zip file image from a github repo synchronously. The byte image * is received into the m_input_stream. */ - void remote_get_zip( const wxString& aRepoURL ) throw( IO_ERROR ); + void remoteGetZip( const wxString& aRepoURL ) throw( IO_ERROR ); wxString m_lib_path; ///< from aLibraryPath, something like https://github.com/liftoff-sr/pretty_footprints std::string m_zip_image; ///< byte image of the zip file in its entirety.