kicad/pcbnew/github/github_plugin.cpp

319 lines
8.9 KiB
C++
Raw Normal View History

2013-09-21 07:30:23 +00:00
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* Copyright (C) 2013 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 2
* 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
*/
/*
This is a pcbnew PLUGIN which supports some of the PLUGIN::Footprint*() functions
in the PLUGIN interface, and could do so by utilizing the version 3 github.com
API documented here:
http://developer.github.com
https://help.github.com/articles/creating-an-access-token-for-command-line-use
but it does not. Rather it simply reads in a zip file of the repo and unzips it
from RAM as needed. Therefore the PLUGIN is read only for accessing
remote pretty libraries. If you want to support writing to the repo, then you
could use the above API.
*/
#ifdef WIN32
// defines needed by avhttp
// Minimal Windows version is XP: Google for _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#define WINVER 0x0501
#endif
2013-09-21 07:30:23 +00:00
#include <sstream>
#include <boost/ptr_container/ptr_map.hpp>
2013-09-21 07:30:23 +00:00
#include <wx/zipstrm.h>
#include <wx/mstream.h>
#include <wx/uri.h>
#include <fctsys.h>
// 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 <avhttp.hpp> // chinese SSL magic
2013-09-21 07:30:23 +00:00
#include <io_mgr.h>
#include <richio.h>
#include <pcb_parser.h>
#include <class_board.h>
#include <github_plugin.h>
#include <class_module.h>
#include <macros.h>
2013-09-21 07:30:23 +00:00
using namespace std;
2013-10-03 15:12:25 +00:00
typedef boost::ptr_map<string, wxZipEntry> MODULE_MAP;
typedef MODULE_MAP::iterator MODULE_ITER;
typedef MODULE_MAP::const_iterator MODULE_CITER;
2013-09-21 07:30:23 +00:00
/**
* Class GH_CACHE
* assists only within GITHUB_PLUGIN and holds a map of footprint name to wxZipEntry
2013-09-21 07:30:23 +00:00
*/
struct GH_CACHE : public MODULE_MAP
2013-09-21 07:30:23 +00:00
{
// MODULE_MAP is a boost::ptr_map template, made into a class hereby.
};
GITHUB_PLUGIN::GITHUB_PLUGIN() :
m_cache( 0 )
{
}
GITHUB_PLUGIN::~GITHUB_PLUGIN()
{
delete m_cache;
}
const wxString& GITHUB_PLUGIN::PluginName() const
{
static wxString name( wxT( "Github" ) );
return name;
}
const wxString& GITHUB_PLUGIN::GetFileExtension() const
{
static wxString empty_ext;
return empty_ext;
}
wxArrayString GITHUB_PLUGIN::FootprintEnumerate(
const wxString& aLibraryPath, const PROPERTIES* aProperties )
2013-09-21 07:30:23 +00:00
{
//D(printf("%s: this:%p aLibraryPath:'%s'\n", __func__, this, TO_UTF8(aLibraryPath) );)
2013-09-21 07:30:23 +00:00
cacheLib( aLibraryPath );
wxArrayString ret;
for( MODULE_ITER it = m_cache->begin(); it!=m_cache->end(); ++it )
{
ret.Add( FROM_UTF8( it->first.c_str() ) );
}
return ret;
}
MODULE* GITHUB_PLUGIN::FootprintLoad( const wxString& aLibraryPath,
const wxString& aFootprintName, const PROPERTIES* aProperties )
2013-09-21 07:30:23 +00:00
{
// D(printf("%s: this:%p aLibraryPath:'%s'\n", __func__, this, TO_UTF8(aLibraryPath) );)
2013-09-21 07:30:23 +00:00
cacheLib( aLibraryPath );
string fp_name = TO_UTF8( aFootprintName );
MODULE_CITER it = m_cache->find( fp_name );
if( it != m_cache->end() ) // fp_name is present
{
wxMemoryInputStream mis( &m_zip_image[0], m_zip_image.size() );
// This decoder should always be UTF8, since it was saved that way by git.
// That is, since pretty footprints are UTF8, and they were pushed to the
// github repo, they are still UTF8.
wxZipInputStream zis( mis, wxConvUTF8 );
wxZipEntry* entry = (wxZipEntry*) it->second; // remove "const"-ness
if( zis.OpenEntry( *entry ) )
{
INPUTSTREAM_LINE_READER reader( &zis );
PCB_PARSER parser( &reader );
MODULE* ret = (MODULE*) parser.Parse();
// Dude, the footprint name comes from the file name in
// a github library. Zero out the library name, we don't know it here.
// Caller always has to set the library nickname if it knows it.
ret->SetFPID( fp_name );
2013-09-21 07:30:23 +00:00
return ret;
}
}
return NULL; // this API function returns NULL for "not found", per spec.
}
bool GITHUB_PLUGIN::IsFootprintLibWritable( const wxString& aLibraryPath )
{
return false;
}
void GITHUB_PLUGIN::cacheLib( const wxString& aLibraryPath ) throw( IO_ERROR )
{
if( !m_cache || m_lib_path != aLibraryPath )
{
2013-10-03 15:12:25 +00:00
// operator==( wxString, wxChar* ) does not exist, construct wxString once here.
const wxString kicad_mod( wxT( "kicad_mod" ) );
2013-10-03 15:12:25 +00:00
//D(printf("%s: this:%p m_lib_path:'%s' aLibraryPath:'%s'\n", __func__, this, TO_UTF8( m_lib_path), TO_UTF8(aLibraryPath) );)
2013-09-21 07:30:23 +00:00
delete m_cache;
m_cache = new GH_CACHE();
2013-10-03 15:12:25 +00:00
// INIT_LOGGER( "/tmp", "test.log" );
2013-09-21 07:30:23 +00:00
remote_get_zip( aLibraryPath );
2013-10-03 15:12:25 +00:00
// UNINIT_LOGGER();
2013-09-21 07:30:23 +00:00
m_lib_path = aLibraryPath;
wxMemoryInputStream mis( &m_zip_image[0], m_zip_image.size() );
// @todo: generalize this name encoding from a PROPERTY (option) later
wxZipInputStream zis( mis, wxConvUTF8 );
wxZipEntry* entry;
while( ( entry = zis.GetNextEntry() ) != NULL )
2013-09-21 07:30:23 +00:00
{
2013-10-03 15:12:25 +00:00
wxFileName fn( entry->GetName() ); // chop long name into parts
2013-09-21 07:30:23 +00:00
2013-10-03 15:12:25 +00:00
if( fn.GetExt() == kicad_mod )
2013-09-21 07:30:23 +00:00
{
2013-10-03 15:12:25 +00:00
string fp_name = TO_UTF8( fn.GetName() ); // omit extension & path
2013-09-21 07:30:23 +00:00
m_cache->insert( fp_name, entry );
}
else
delete entry;
}
}
}
bool GITHUB_PLUGIN::repoURL_zipURL( const wxString& aRepoURL, string* aZipURL )
{
// e.g. "https://github.com/liftoff-sr/pretty_footprints"
//D(printf("aRepoURL:%s\n", TO_UTF8( aRepoURL ) );)
2013-09-21 07:30:23 +00:00
wxURI repo( aRepoURL );
if( repo.HasServer() && repo.HasPath() )
{
// goal: "https://github.com/liftoff-sr/pretty_footprints/archive/master.zip"
wxString zip_url( wxT( "https://" ) );
2013-09-21 07:30:23 +00:00
zip_url += repo.GetServer();
zip_url += repo.GetPath();
zip_url += wxT( '/' );
2013-09-21 07:30:23 +00:00
zip_url += wxT( "archive/master.zip" );
*aZipURL = zip_url.utf8_str();
return true;
}
return false;
}
void GITHUB_PLUGIN::remote_get_zip( const wxString& aRepoURL ) throw( IO_ERROR )
{
string zip_url;
if( !repoURL_zipURL( aRepoURL, &zip_url ) )
{
wxString msg = wxString::Format( _( "Unable to parse URL:\n'%s'" ), GetChars( aRepoURL ) );
2013-09-21 07:30:23 +00:00
THROW_IO_ERROR( msg );
}
boost::asio::io_service io;
avhttp::http_stream h( io );
avhttp::request_opts options;
options.insert( "Accept", "application/zip" );
options.insert( "User-Agent", "http://kicad-pcb.org" ); // THAT WOULD BE ME.
h.request_options( options );
try
{
ostringstream os;
h.open( zip_url ); // only one file, therefore do it synchronously.
2013-09-21 07:30:23 +00:00
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.
}
2013-10-03 15:12:25 +00:00
catch( boost::system::system_error& e )
2013-09-21 07:30:23 +00:00
{
2013-10-03 15:12:25 +00:00
// https "GET" has faild, report this to API caller.
wxString fmt( _( "Cannot GET zip: '%s'\nfor lib-path: '%s'.\nWhat: '%s'" ) );
string msg = StrPrintf( TO_UTF8( fmt ),
zip_url.c_str(),
TO_UTF8( aRepoURL ),
e.what() );
THROW_IO_ERROR( msg );
2013-09-21 07:30:23 +00:00
}
}
#if 0 && defined(STANDALONE)
int main( int argc, char** argv )
{
INIT_LOGGER( ".", "test.log" );
2013-09-21 07:30:23 +00:00
GITHUB_PLUGIN gh;
try
{
wxArrayString fps = gh.FootprintEnumerate(
wxT( "https://github.com/liftoff-sr/pretty_footprints" ),
NULL
);
for( int i=0; i<(int)fps.Count(); ++i )
{
printf("[%d]:%s\n", i, TO_UTF8( fps[i] ) );
}
}
catch( IO_ERROR ioe )
{
printf( "%s\n", TO_UTF8(ioe.errorText) );
}
UNINIT_LOGGER();
return 0;
}
#endif