22#include <wx/datetime.h>
24#include <wx/stdpaths.h>
37#include <api/common/envelope.pb.h>
39using kiapi::common::ApiRequest, kiapi::common::ApiResponse, kiapi::common::ApiStatusCode;
50 m_token(
KIID().AsStdString() ),
51 m_readyToReply( false )
56 if( !
Pgm().GetCommonSettings()->m_Api.enable_server )
58 wxLogTrace(
traceApi,
"Server: disabled by user preferences." );
78 socket.AssignDir( wxS(
"/tmp" ) );
80 socket.AssignDir( wxStandardPaths::Get().GetTempDir() );
82 socket.AppendDir( wxS(
"kicad" ) );
83 socket.SetFullName( wxS(
"api.sock" ) );
87 wxLogTrace(
traceApi, wxString::Format(
"Server: socket path %s could not be created",
92 if( socket.FileExists() )
94 socket.SetFullName( wxString::Format( wxS(
"api-%ul.sock" ), ::wxGetProcessId() ) );
96 if( socket.FileExists() )
98 wxLogTrace(
traceApi, wxString::Format(
"Server: PID socket path %s already exists!",
99 socket.GetFullPath() ) );
104 m_server = std::make_unique<KINNG_REQUEST_SERVER>(
105 fmt::format(
"ipc://{}", socket.GetFullPath().ToStdString() ) );
114 log( fmt::format(
"--- KiCad API server started at {} ---\n",
SocketPath() ) );
128 wxLogTrace(
traceApi,
"Stopping server" );
144 wxCHECK( aHandler, );
165 ApiResponse notHandled;
166 notHandled.mutable_status()->set_status( ApiStatusCode::AS_NOT_READY );
167 notHandled.mutable_status()->set_error_message(
"KiCad is not ready to reply" );
168 m_server->Reply( notHandled.SerializeAsString() );
169 log(
"Got incoming request but was not yet ready to reply." );
173 wxCommandEvent* evt =
new wxCommandEvent( API_REQUEST_EVENT );
176 evt->SetClientData(
static_cast<void*
>( aRequest ) );
185 std::string& requestString = *
static_cast<std::string*
>( aEvent.GetClientData() );
188 if( !request.ParseFromString( requestString ) )
191 error.mutable_header()->set_kicad_token(
m_token );
192 error.mutable_status()->set_status( ApiStatusCode::AS_BAD_REQUEST );
193 error.mutable_status()->set_error_message(
"request could not be parsed" );
194 m_server->Reply( error.SerializeAsString() );
197 log(
"Response (ERROR): " + error.Utf8DebugString() );
201 log(
"Request: " + request.Utf8DebugString() );
203 if( !request.header().kicad_token().empty() &&
204 request.header().kicad_token().compare(
m_token ) != 0 )
207 error.mutable_header()->set_kicad_token(
m_token );
208 error.mutable_status()->set_status( ApiStatusCode::AS_TOKEN_MISMATCH );
209 error.mutable_status()->set_error_message(
210 "the provided kicad_token did not match this KiCad instance's token" );
211 m_server->Reply( error.SerializeAsString() );
214 log(
"Response (ERROR): " + error.Utf8DebugString() );
221 result = handler->Handle( request );
223 if( result.has_value() )
225 else if( result.error().status() != ApiStatusCode::AS_UNHANDLED )
231 if( result.has_value() )
233 result->mutable_header()->set_kicad_token(
m_token );
234 m_server->Reply( result->SerializeAsString() );
237 log(
"Response: " + result->Utf8DebugString() );
242 error.mutable_status()->CopyFrom( result.error() );
243 error.mutable_header()->set_kicad_token(
m_token );
245 if( result.error().status() == ApiStatusCode::AS_UNHANDLED )
247 std::string type =
"<unparseable Any>";
248 google::protobuf::Any::ParseAnyTypeUrl( request.message().type_url(), &type );
249 std::string msg = fmt::format(
"no handler available for request of type {}", type );
250 error.mutable_status()->set_error_message( msg );
253 m_server->Reply( error.SerializeAsString() );
256 log(
"Response (ERROR): " + error.Utf8DebugString() );
263 FILE* fp = wxFopen(
m_logFilePath.GetFullPath(), wxT(
"a" ) );
269 wxDateTime now = wxDateTime::Now();
271 fprintf( fp,
"%s",
TO_UTF8( out.Format( wxS(
"%s: %s" ),
272 now.FormatISOCombined(), aOutput ) ) );
tl::expected< ApiResponse, ApiResponseStatus > API_RESULT
wxDEFINE_EVENT(API_REQUEST_EVENT, wxCommandEvent)
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
void handleApiEvent(wxCommandEvent &aEvent)
Event handler that receives the event on the main thread sent by onApiRequest.
std::unique_ptr< API_HANDLER_COMMON > m_commonHandler
static wxString s_logFileName
void RegisterHandler(API_HANDLER *aHandler)
Adds a new request handler to the server.
void onApiRequest(std::string *aRequest)
Callback that executes on the server thread and generates an event that will be handled by the wxWidg...
std::set< API_HANDLER * > m_handlers
std::string SocketPath() const
void log(const std::string &aOutput)
void DeregisterHandler(API_HANDLER *aHandler)
std::unique_ptr< KINNG_REQUEST_SERVER > m_server
static wxString GetLogsPath()
Gets a path to use for user-visible log files.
static bool EnsurePathExists(const wxString &aPath)
Attempts to create a given path if it does not exist.
bool m_EnableAPILogging
Log IPC API requests and responses.
const wxChar *const traceApi
Flag to enable debug output related to the API plugin system.
PGM_BASE & Pgm()
The global Program "get" accessor.
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.