Implement the Icinga Studio application

fixes #10042
This commit is contained in:
Gunnar Beutner 2015-08-29 01:16:16 +02:00
parent 9b73205481
commit c37a23ccba
46 changed files with 4134 additions and 125 deletions

View File

@ -16,6 +16,8 @@ addons:
- libmysqlclient-dev - libmysqlclient-dev
- libedit-dev - libedit-dev
- libyajl-dev - libyajl-dev
- libwxbase3.0-dev
- libwxgtk3.0-dev
before_script: before_script:
- mkdir build - mkdir build

View File

@ -37,6 +37,7 @@ option(ICINGA2_WITH_HELLO "Build the hello module" OFF)
option(ICINGA2_WITH_LIVESTATUS "Build the Livestatus module" ON) option(ICINGA2_WITH_LIVESTATUS "Build the Livestatus module" ON)
option(ICINGA2_WITH_NOTIFICATION "Build the notification module" ON) option(ICINGA2_WITH_NOTIFICATION "Build the notification module" ON)
option(ICINGA2_WITH_PERFDATA "Build the perfdata module" ON) option(ICINGA2_WITH_PERFDATA "Build the perfdata module" ON)
option(ICINGA2_WITH_STUDIO "Build the Icinga Studio application" OFF)
file(STRINGS icinga2.spec VERSION_LINE REGEX "^Version: ") file(STRINGS icinga2.spec VERSION_LINE REGEX "^Version: ")
string(REPLACE "Version: " "" ICINGA2_VERSION ${VERSION_LINE}) string(REPLACE "Version: " "" ICINGA2_VERSION ${VERSION_LINE})
@ -244,6 +245,10 @@ add_subdirectory(test)
add_subdirectory(agent) add_subdirectory(agent)
add_subdirectory(plugins) add_subdirectory(plugins)
if(ICINGA2_WITH_STUDIO)
add_subdirectory(icinga-studio)
endif()
set(CPACK_PACKAGE_NAME "Icinga2") set(CPACK_PACKAGE_NAME "Icinga2")
set(CPACK_PACKAGE_VENDOR "Icinga Development Team") set(CPACK_PACKAGE_VENDOR "Icinga Development Team")
set(CPACK_PACKAGE_VERSION ${ICINGA2_VERSION}) set(CPACK_PACKAGE_VERSION ${ICINGA2_VERSION})

View File

@ -0,0 +1,65 @@
# Icinga 2
# Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org)
#
# 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, write to the Free Software Foundation
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
set(wxWidgets_CONFIGURATION mswu)
find_package(wxWidgets COMPONENTS core base propgrid REQUIRED)
include(${wxWidgets_USE_FILE})
if(MSVC)
set(WindowsSources icinga.rc)
else()
set(WindowsSources "")
endif()
add_executable(icinga-studio MACOSX_BUNDLE WIN32 icinga-studio.cpp
forms.cpp aboutform.cpp connectform.cpp mainform.cpp
icinga.icns api.cpp ${WindowsSources})
include_directories(${Boost_INCLUDE_DIRS})
target_link_libraries(icinga-studio ${Boost_LIBRARIES} ${wxWidgets_LIBRARIES} base remote)
if(APPLE)
set_source_files_properties(icinga.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
endif()
set_target_properties (
icinga-studio PROPERTIES
INSTALL_RPATH ${CMAKE_INSTALL_FULL_LIBDIR}/icinga2
FOLDER Bin
OUTPUT_NAME icinga-studio
MACOSX_BUNDLE_INFO_STRING "Icinga Studio"
MACOSX_BUNDLE_BUNDLE_NAME "Icinga Studio"
MACOSX_BUNDLE_GUI_IDENTIFIER "Icinga Studio"
MACOSX_BUNDLE_ICON_FILE icinga.icns
MACOSX_BUNDLE_SHORT_VERSION_STRING "${GIT_VERSION}"
MACOSX_BUNDLE_LONG_VERSION_STRING "${GIT_VERSION}"
MACOSX_BUNDLE_COPYRIGHT "(c) Icinga Development Team"
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/MacOSXBundleInfo.plist.in"
)
if(WIN32)
set(InstallPath "${CMAKE_INSTALL_SBINDIR}")
else()
set(InstallPath "${CMAKE_INSTALL_BINDIR}")
endif()
install(
TARGETS icinga-studio
RUNTIME DESTINATION ${InstallPath}
BUNDLE DESTINATION ${InstallPath}
)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
<key>CFBundleGetInfoString</key>
<string>${MACOSX_BUNDLE_INFO_STRING}</string>
<key>CFBundleIconFile</key>
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
<key>CFBundleIdentifier</key>
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleLongVersionString</key>
<string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string>
<key>CFBundleName</key>
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
<key>CSResourcesFileMapped</key>
<true/>
<key>LSRequiresCarbon</key>
<false/>
<key>NSHumanReadableCopyright</key>
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
<key>NSHighResolutionCapable</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,30 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) *
* *
* 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, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#include "base/application.hpp"
#include "icinga-studio/aboutform.hpp"
using namespace icinga;
AboutForm::AboutForm(wxWindow *parent)
: AboutFormBase(parent)
{
std::string version = "Version " + Application::GetVersion();
m_VersionLabel->SetLabelText(version);
}

View File

@ -0,0 +1,36 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) *
* *
* 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, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#ifndef ABOUTFORM_H
#define ABOUTFORM_H
#include "icinga-studio/forms.h"
namespace icinga
{
class AboutForm : public AboutFormBase
{
public:
AboutForm(wxWindow *parent);
};
}
#endif /* ABOUTFORM_H */

165
icinga-studio/api.cpp Normal file
View File

@ -0,0 +1,165 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) *
* *
* 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, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#include "icinga-studio/api.hpp"
#include "remote/base64.hpp"
#include "base/json.hpp"
#include "base/logger.hpp"
#include "base/exception.hpp"
#include <boost/foreach.hpp>
using namespace icinga;
ApiClient::ApiClient(const String& host, const String& port,
const String& user, const String& password)
: m_Connection(new HttpClientConnection(host, port, true)), m_User(user), m_Password(password)
{
m_Connection->Start();
}
void ApiClient::GetTypes(const TypesCompletionCallback& callback) const
{
boost::shared_ptr<HttpRequest> req = m_Connection->NewRequest();
req->RequestMethod = "GET";
req->RequestUrl = new Url("https://" + m_Connection->GetHost() + ":" + m_Connection->GetPort() + "/v1/types");
req->AddHeader("Authorization", "Basic " + Base64::Encode(m_User + ":" + m_Password));
m_Connection->SubmitRequest(req, boost::bind(TypesHttpCompletionCallback, _1, _2, callback));
}
void ApiClient::TypesHttpCompletionCallback(HttpRequest& request, HttpResponse& response,
const TypesCompletionCallback& callback)
{
Dictionary::Ptr result;
String body;
char buffer[1024];
size_t count;
while ((count = response.ReadBody(buffer, sizeof(buffer))) > 0)
body += String(buffer, buffer + count);
std::vector<ApiType::Ptr> types;
try {
result = JsonDecode(body);
Array::Ptr results = result->Get("results");
ObjectLock olock(results);
BOOST_FOREACH(const Dictionary::Ptr typeInfo, results)
{
ApiType::Ptr type = new ApiType();;
type->Abstract = typeInfo->Get("abstract");
type->BaseName = typeInfo->Get("base");
type->Name = typeInfo->Get("name");
type->PluralName = typeInfo->Get("plural_name");
// TODO: attributes
types.push_back(type);
}
} catch (const std::exception& ex) {
Log(LogCritical, "ApiClient")
<< "Error while decoding response: " << DiagnosticInformation(ex);
}
callback(types);
}
void ApiClient::GetObjects(const String& pluralType, const ObjectsCompletionCallback& callback,
const std::vector<String>& names, const std::vector<String>& attrs) const
{
String url = "https://" + m_Connection->GetHost() + ":" + m_Connection->GetPort() + "/v1/" + pluralType;
String qp;
BOOST_FOREACH(const String& name, names) {
if (!qp.IsEmpty())
qp += "&";
qp += pluralType.ToLower() + "=" + name;
}
BOOST_FOREACH(const String& attr, attrs) {
if (!qp.IsEmpty())
qp += "&";
qp += "attrs[]=" + attr;
}
boost::shared_ptr<HttpRequest> req = m_Connection->NewRequest();
req->RequestMethod = "GET";
req->RequestUrl = new Url(url + "?" + qp);
req->AddHeader("Authorization", "Basic " + Base64::Encode(m_User + ":" + m_Password));
m_Connection->SubmitRequest(req, boost::bind(ObjectsHttpCompletionCallback, _1, _2, callback));
}
void ApiClient::ObjectsHttpCompletionCallback(HttpRequest& request,
HttpResponse& response, const ObjectsCompletionCallback& callback)
{
Dictionary::Ptr result;
String body;
char buffer[1024];
size_t count;
while ((count = response.ReadBody(buffer, sizeof(buffer))) > 0)
body += String(buffer, buffer + count);
std::vector<ApiObject::Ptr> objects;
try {
result = JsonDecode(body);
Array::Ptr results = result->Get("results");
ObjectLock olock(results);
BOOST_FOREACH(const Dictionary::Ptr objectInfo, results)
{
ApiObject::Ptr object = new ApiObject();
Dictionary::Ptr attrs = objectInfo->Get("attrs");
{
ObjectLock olock(attrs);
BOOST_FOREACH(const Dictionary::Pair& kv, attrs)
{
object->Attrs[kv.first] = kv.second;
}
}
Array::Ptr used_by = objectInfo->Get("used_by");
{
ObjectLock olock(used_by);
BOOST_FOREACH(const Dictionary::Ptr& refInfo, used_by)
{
ApiObjectReference ref;
ref.Name = refInfo->Get("name");
ref.Type = refInfo->Get("type");
object->UsedBy.push_back(ref);
}
}
objects.push_back(object);
}
} catch (const std::exception& ex) {
Log(LogCritical, "ApiClient")
<< "Error while decoding response: " << DiagnosticInformation(ex);
}
callback(objects);
}

111
icinga-studio/api.hpp Normal file
View File

@ -0,0 +1,111 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) *
* *
* 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, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#ifndef API_H
#define API_H
#include "remote/httpclientconnection.hpp"
#include "base/value.hpp"
#include <vector>
namespace icinga
{
struct ApiFieldAttributes
{
public:
bool Config;
bool Internal;
bool Required;
bool State;
};
class ApiType;
struct ApiField
{
public:
String Name;
int ID;
int ArrayRank;
ApiFieldAttributes FieldAttributes;
String TypeName;
intrusive_ptr<ApiType> Type;
};
class ApiType : public Object
{
public:
DECLARE_PTR_TYPEDEFS(ApiType);
String Name;
String PluralName;
String BaseName;
ApiType::Ptr Base;
bool Abstract;
std::map<String, ApiField> Fields;
std::vector<String> PrototypeKeys;
};
struct ApiObjectReference
{
public:
String Name;
String Type;
};
struct ApiObject : public Object
{
public:
DECLARE_PTR_TYPEDEFS(ApiObject);
std::map<String, Value> Attrs;
std::vector<ApiObjectReference> UsedBy;
};
class ApiClient : public Object
{
public:
DECLARE_PTR_TYPEDEFS(ApiClient);
ApiClient(const String& host, const String& port,
const String& user, const String& password);
typedef boost::function<void(const std::vector<ApiType::Ptr>&)> TypesCompletionCallback;
void GetTypes(const TypesCompletionCallback& callback) const;
typedef boost::function<void(const std::vector<ApiObject::Ptr>&)> ObjectsCompletionCallback;
void GetObjects(const String& pluralType, const ObjectsCompletionCallback& callback,
const std::vector<String>& names = std::vector<String>(),
const std::vector<String>& attrs = std::vector<String>()) const;
private:
HttpClientConnection::Ptr m_Connection;
String m_User;
String m_Password;
static void TypesHttpCompletionCallback(HttpRequest& request,
HttpResponse& response, const TypesCompletionCallback& callback);
static void ObjectsHttpCompletionCallback(HttpRequest& request,
HttpResponse& response, const ObjectsCompletionCallback& callback);
};
}
#endif /* API_H */

View File

@ -0,0 +1,63 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) *
* *
* 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, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#include "icinga-studio/connectform.hpp"
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
using namespace icinga;
ConnectForm::ConnectForm(wxWindow *parent, const Url::Ptr& url)
: ConnectFormBase(parent)
{
#ifdef _WIN32
SetIcon(wxICON(icinga));
#endif /* _WIN32 */
std::string authority = url->GetAuthority();
std::vector<std::string> tokens;
boost::algorithm::split(tokens, authority, boost::is_any_of("@"));
if (tokens.size() > 1) {
std::vector<std::string> userinfo;
boost::algorithm::split(userinfo, tokens[0], boost::is_any_of(":"));
m_UserText->SetValue(userinfo[0]);
m_PasswordText->SetValue(userinfo[1]);
}
std::vector<std::string> hostport;
boost::algorithm::split(hostport, tokens.size() > 1 ? tokens[1] : tokens[0], boost::is_any_of(":"));
m_HostText->SetValue(hostport[0]);
if (hostport.size() > 1)
m_PortText->SetValue(hostport[1]);
else
m_PortText->SetValue("5665");
}
Url::Ptr ConnectForm::GetUrl(void) const
{
wxString url = "https://" + m_UserText->GetValue() + ":" + m_PasswordText->GetValue()
+ "@" + m_HostText->GetValue() + ":" + m_PortText->GetValue() + "/";
return new Url(url.ToStdString());
}

View File

@ -0,0 +1,39 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) *
* *
* 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, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#ifndef CONNECTFORM_H
#define CONNECTFORM_H
#include "remote/url.hpp"
#include "icinga-studio/forms.h"
namespace icinga
{
class ConnectForm : public ConnectFormBase
{
public:
ConnectForm(wxWindow *parent, const Url::Ptr& url);
Url::Ptr GetUrl(void) const;
};
}
#endif /* CONNECTFORM_H */

242
icinga-studio/forms.cpp Normal file
View File

@ -0,0 +1,242 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Jun 17 2015)
// http://www.wxformbuilder.org/
//
// PLEASE DO "NOT" EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#include "forms.h"
#include "icinga.xpm"
///////////////////////////////////////////////////////////////////////////
MainFormBase::MainFormBase( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxFrame( parent, id, title, pos, size, style )
{
this->SetSizeHints( wxSize( 800,569 ), wxDefaultSize );
m_MenuBar = new wxMenuBar( 0 );
wxMenu* m_FileMenu;
m_FileMenu = new wxMenu();
wxMenuItem* m_QuitMenuItem;
m_QuitMenuItem = new wxMenuItem( m_FileMenu, wxID_EXIT, wxString( wxT("&Quit") ) , wxEmptyString, wxITEM_NORMAL );
m_FileMenu->Append( m_QuitMenuItem );
m_MenuBar->Append( m_FileMenu, wxT("&File") );
wxMenu* m_HelpMenu;
m_HelpMenu = new wxMenu();
wxMenuItem* m_AboutMenuItem;
m_AboutMenuItem = new wxMenuItem( m_HelpMenu, wxID_ABOUT, wxString( wxT("&About Icinga Studio...") ) , wxEmptyString, wxITEM_NORMAL );
m_HelpMenu->Append( m_AboutMenuItem );
m_MenuBar->Append( m_HelpMenu, wxT("&Help") );
this->SetMenuBar( m_MenuBar );
wxBoxSizer* m_DialogSizer;
m_DialogSizer = new wxBoxSizer( wxVERTICAL );
wxBoxSizer* m_ConnectionDetailsSizer;
m_ConnectionDetailsSizer = new wxBoxSizer( wxHORIZONTAL );
m_TypesTree = new wxTreeCtrl( this, wxID_ANY, wxDefaultPosition, wxSize( 315,-1 ), wxTR_DEFAULT_STYLE|wxTR_HIDE_ROOT );
m_ConnectionDetailsSizer->Add( m_TypesTree, 0, wxALL|wxEXPAND, 2 );
wxBoxSizer* m_ObjectDetailsSizer;
m_ObjectDetailsSizer = new wxBoxSizer( wxVERTICAL );
m_ObjectsList = new wxListCtrl( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT );
m_ObjectDetailsSizer->Add( m_ObjectsList, 1, wxALL|wxEXPAND, 2 );
m_PropertyGrid = new wxPropertyGrid(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxPG_DEFAULT_STYLE);
m_ObjectDetailsSizer->Add( m_PropertyGrid, 1, wxALL|wxEXPAND, 5 );
m_ConnectionDetailsSizer->Add( m_ObjectDetailsSizer, 1, wxEXPAND, 5 );
m_DialogSizer->Add( m_ConnectionDetailsSizer, 1, wxEXPAND, 5 );
this->SetSizer( m_DialogSizer );
this->Layout();
m_StatusBar = this->CreateStatusBar( 1, wxST_SIZEGRIP, wxID_ANY );
this->Centre( wxBOTH );
// Connect Events
this->Connect( m_QuitMenuItem->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainFormBase::OnQuitClicked ) );
this->Connect( m_AboutMenuItem->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainFormBase::OnAboutClicked ) );
m_TypesTree->Connect( wxEVT_COMMAND_TREE_SEL_CHANGED, wxTreeEventHandler( MainFormBase::OnTypeSelected ), NULL, this );
m_ObjectsList->Connect( wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler( MainFormBase::OnObjectSelected ), NULL, this );
}
MainFormBase::~MainFormBase()
{
// Disconnect Events
this->Disconnect( wxID_EXIT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainFormBase::OnQuitClicked ) );
this->Disconnect( wxID_ABOUT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainFormBase::OnAboutClicked ) );
m_TypesTree->Disconnect( wxEVT_COMMAND_TREE_SEL_CHANGED, wxTreeEventHandler( MainFormBase::OnTypeSelected ), NULL, this );
m_ObjectsList->Disconnect( wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler( MainFormBase::OnObjectSelected ), NULL, this );
}
ConnectFormBase::ConnectFormBase( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style )
{
this->SetSizeHints( wxDefaultSize, wxDefaultSize );
wxBoxSizer* m_DialogSizer;
m_DialogSizer = new wxBoxSizer( wxVERTICAL );
wxPanel* m_ConnectionDetailsPanel;
m_ConnectionDetailsPanel = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
wxStaticBoxSizer* m_DetailsSizer;
m_DetailsSizer = new wxStaticBoxSizer( new wxStaticBox( m_ConnectionDetailsPanel, wxID_ANY, wxT("Connection Details") ), wxVERTICAL );
wxStaticText* m_HostLabel;
m_HostLabel = new wxStaticText( m_DetailsSizer->GetStaticBox(), wxID_ANY, wxT("Host:"), wxDefaultPosition, wxDefaultSize, 0 );
m_HostLabel->Wrap( -1 );
m_DetailsSizer->Add( m_HostLabel, 0, wxALL, 5 );
m_HostText = new wxTextCtrl( m_DetailsSizer->GetStaticBox(), wxID_OK, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
m_DetailsSizer->Add( m_HostText, 0, wxALL|wxEXPAND, 5 );
wxStaticText* m_PortLabel;
m_PortLabel = new wxStaticText( m_DetailsSizer->GetStaticBox(), wxID_ANY, wxT("Port:"), wxDefaultPosition, wxDefaultSize, 0 );
m_PortLabel->Wrap( -1 );
m_DetailsSizer->Add( m_PortLabel, 0, wxALL, 5 );
m_PortText = new wxTextCtrl( m_DetailsSizer->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
m_DetailsSizer->Add( m_PortText, 0, wxALL, 5 );
wxStaticText* m_UserLabel;
m_UserLabel = new wxStaticText( m_DetailsSizer->GetStaticBox(), wxID_ANY, wxT("API User:"), wxDefaultPosition, wxDefaultSize, 0 );
m_UserLabel->Wrap( -1 );
m_DetailsSizer->Add( m_UserLabel, 0, wxALL, 5 );
m_UserText = new wxTextCtrl( m_DetailsSizer->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
m_DetailsSizer->Add( m_UserText, 0, wxALL|wxEXPAND, 5 );
wxStaticText* m_PasswordLabel;
m_PasswordLabel = new wxStaticText( m_DetailsSizer->GetStaticBox(), wxID_ANY, wxT("API Password:"), wxDefaultPosition, wxDefaultSize, 0 );
m_PasswordLabel->Wrap( -1 );
m_DetailsSizer->Add( m_PasswordLabel, 0, wxALL, 5 );
m_PasswordText = new wxTextCtrl( m_DetailsSizer->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PASSWORD );
m_DetailsSizer->Add( m_PasswordText, 0, wxALL|wxEXPAND, 5 );
wxStaticText* m_InfoLabel;
m_InfoLabel = new wxStaticText( m_DetailsSizer->GetStaticBox(), wxID_ANY, wxT("You can find the username and password for the default user in /etc/icinga2/conf.d/api-users.conf."), wxDefaultPosition, wxDefaultSize, 0 );
m_InfoLabel->Wrap( 270 );
m_DetailsSizer->Add( m_InfoLabel, 0, wxALL, 5 );
m_ConnectionDetailsPanel->SetSizer( m_DetailsSizer );
m_ConnectionDetailsPanel->Layout();
m_DetailsSizer->Fit( m_ConnectionDetailsPanel );
m_DialogSizer->Add( m_ConnectionDetailsPanel, 1, wxEXPAND | wxALL, 5 );
wxPanel* m_ButtonsPanel;
m_ButtonsPanel = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
wxBoxSizer* m_ButtonsSizer;
m_ButtonsSizer = new wxBoxSizer( wxHORIZONTAL );
wxStdDialogButtonSizer* m_Buttons;
wxButton* m_ButtonsOK;
wxButton* m_ButtonsCancel;
m_Buttons = new wxStdDialogButtonSizer();
m_ButtonsOK = new wxButton( m_ButtonsPanel, wxID_OK );
m_Buttons->AddButton( m_ButtonsOK );
m_ButtonsCancel = new wxButton( m_ButtonsPanel, wxID_CANCEL );
m_Buttons->AddButton( m_ButtonsCancel );
m_Buttons->Realize();
m_ButtonsSizer->Add( m_Buttons, 1, wxEXPAND, 5 );
m_ButtonsPanel->SetSizer( m_ButtonsSizer );
m_ButtonsPanel->Layout();
m_ButtonsSizer->Fit( m_ButtonsPanel );
m_DialogSizer->Add( m_ButtonsPanel, 0, wxEXPAND | wxALL, 5 );
this->SetSizer( m_DialogSizer );
this->Layout();
m_DialogSizer->Fit( this );
this->Centre( wxBOTH );
}
ConnectFormBase::~ConnectFormBase()
{
}
AboutFormBase::AboutFormBase( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style )
{
this->SetSizeHints( wxDefaultSize, wxDefaultSize );
wxBoxSizer* m_DialogSizer;
m_DialogSizer = new wxBoxSizer( wxVERTICAL );
wxBoxSizer* m_InfoSizer;
m_InfoSizer = new wxBoxSizer( wxHORIZONTAL );
wxStaticBitmap* m_ProductIcon;
m_ProductIcon = new wxStaticBitmap( this, wxID_ANY, wxBitmap( icinga_xpm ), wxDefaultPosition, wxDefaultSize, 0 );
m_InfoSizer->Add( m_ProductIcon, 0, wxALL, 5 );
wxBoxSizer* m_AboutInfoSizer;
m_AboutInfoSizer = new wxBoxSizer( wxVERTICAL );
wxStaticText* m_ProductNameLabel;
m_ProductNameLabel = new wxStaticText( this, wxID_ANY, wxT("Icinga Studio"), wxDefaultPosition, wxDefaultSize, 0 );
m_ProductNameLabel->Wrap( -1 );
m_AboutInfoSizer->Add( m_ProductNameLabel, 0, wxALL, 5 );
m_VersionLabel = new wxStaticText( this, wxID_ANY, wxT("Version"), wxDefaultPosition, wxDefaultSize, 0 );
m_VersionLabel->Wrap( -1 );
m_AboutInfoSizer->Add( m_VersionLabel, 0, wxALL, 5 );
wxStaticText* m_CopyrightLabel;
m_CopyrightLabel = new wxStaticText( this, wxID_ANY, wxT("Copyright (c) 2015 Icinga Development Team"), wxDefaultPosition, wxDefaultSize, 0 );
m_CopyrightLabel->Wrap( -1 );
m_AboutInfoSizer->Add( m_CopyrightLabel, 0, wxALL, 5 );
m_InfoSizer->Add( m_AboutInfoSizer, 1, wxEXPAND, 5 );
m_DialogSizer->Add( m_InfoSizer, 1, wxEXPAND, 5 );
wxPanel* m_ButtonsPanel;
m_ButtonsPanel = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
wxBoxSizer* m_ButtonsSizer;
m_ButtonsSizer = new wxBoxSizer( wxVERTICAL );
wxStdDialogButtonSizer* m_Buttons;
wxButton* m_ButtonsOK;
m_Buttons = new wxStdDialogButtonSizer();
m_ButtonsOK = new wxButton( m_ButtonsPanel, wxID_OK );
m_Buttons->AddButton( m_ButtonsOK );
m_Buttons->Realize();
m_ButtonsSizer->Add( m_Buttons, 0, wxEXPAND, 5 );
m_ButtonsPanel->SetSizer( m_ButtonsSizer );
m_ButtonsPanel->Layout();
m_ButtonsSizer->Fit( m_ButtonsPanel );
m_DialogSizer->Add( m_ButtonsPanel, 0, wxEXPAND | wxALL, 5 );
this->SetSizer( m_DialogSizer );
this->Layout();
m_DialogSizer->Fit( this );
this->Centre( wxBOTH );
}
AboutFormBase::~AboutFormBase()
{
}

105
icinga-studio/forms.h Normal file
View File

@ -0,0 +1,105 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Jun 17 2015)
// http://www.wxformbuilder.org/
//
// PLEASE DO "NOT" EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#ifndef __FORMS_H__
#define __FORMS_H__
#include <wx/artprov.h>
#include <wx/xrc/xmlres.h>
#include <wx/string.h>
#include <wx/bitmap.h>
#include <wx/image.h>
#include <wx/icon.h>
#include <wx/menu.h>
#include <wx/gdicmn.h>
#include <wx/font.h>
#include <wx/colour.h>
#include <wx/settings.h>
#include <wx/treectrl.h>
#include <wx/listctrl.h>
#include <wx/propgrid/propgrid.h>
#include <wx/propgrid/advprops.h>
#include <wx/sizer.h>
#include <wx/statusbr.h>
#include <wx/frame.h>
#include <wx/stattext.h>
#include <wx/textctrl.h>
#include <wx/statbox.h>
#include <wx/panel.h>
#include <wx/button.h>
#include <wx/dialog.h>
#include <wx/statbmp.h>
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
/// Class MainFormBase
///////////////////////////////////////////////////////////////////////////////
class MainFormBase : public wxFrame
{
private:
protected:
wxMenuBar* m_MenuBar;
wxTreeCtrl* m_TypesTree;
wxListCtrl* m_ObjectsList;
wxPropertyGrid* m_PropertyGrid;
wxStatusBar* m_StatusBar;
// Virtual event handlers, overide them in your derived class
virtual void OnQuitClicked( wxCommandEvent& event ) { event.Skip(); }
virtual void OnAboutClicked( wxCommandEvent& event ) { event.Skip(); }
virtual void OnTypeSelected( wxTreeEvent& event ) { event.Skip(); }
virtual void OnObjectSelected( wxListEvent& event ) { event.Skip(); }
public:
MainFormBase( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Icinga Studio"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 800,569 ), long style = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL );
~MainFormBase();
};
///////////////////////////////////////////////////////////////////////////////
/// Class ConnectFormBase
///////////////////////////////////////////////////////////////////////////////
class ConnectFormBase : public wxDialog
{
private:
protected:
wxTextCtrl* m_HostText;
wxTextCtrl* m_PortText;
wxTextCtrl* m_UserText;
wxTextCtrl* m_PasswordText;
public:
ConnectFormBase( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Icinga Studio - Connect"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE );
~ConnectFormBase();
};
///////////////////////////////////////////////////////////////////////////////
/// Class AboutFormBase
///////////////////////////////////////////////////////////////////////////////
class AboutFormBase : public wxDialog
{
private:
protected:
wxStaticText* m_VersionLabel;
public:
AboutFormBase( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("About Icinga Studio"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE );
~AboutFormBase();
};
#endif //__FORMS_H__

View File

@ -0,0 +1,66 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) *
* *
* 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, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#include "icinga-studio/connectform.hpp"
#include "icinga-studio/mainform.hpp"
#include "base/application.hpp"
#include <wx/wx.h>
#include <wx/app.h>
#include <wx/config.h>
using namespace icinga;
class IcingaStudio : public wxApp
{
public:
virtual bool OnInit(void) override
{
Application::InitializeBase();
Url::Ptr pUrl;
if (argc < 2) {
wxConfig config("IcingaStudio");
wxString wUrl;
if (!config.Read("url", &wUrl))
wUrl = "https://localhost:5665/";
std::string url = wUrl.ToStdString();
ConnectForm f(NULL, new Url(url));
if (f.ShowModal() != wxID_OK)
return false;
pUrl = f.GetUrl();
url = pUrl->Format();
wUrl = url;
config.Write("url", wUrl);
} else {
pUrl = new Url(argv[1].ToStdString());
}
MainForm *m = new MainForm(NULL, pUrl);
m->Show();
return true;
}
};
wxIMPLEMENT_APP(IcingaStudio);

BIN
icinga-studio/icinga.icns Normal file

Binary file not shown.

BIN
icinga-studio/icinga.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

34
icinga-studio/icinga.rc Normal file
View File

@ -0,0 +1,34 @@
#include <windows.h>
#include "icinga-version.h"
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
icinga ICON "icinga.ico"
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,0
PRODUCTVERSION 1,0,0,0
FILEOS VOS__WINDOWS32
FILETYPE VFT_APP
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904E4"
BEGIN
VALUE "CompanyName", "Icinga Development Team"
VALUE "FileDescription", "Icinga Studio"
VALUE "FileVersion", VERSION
VALUE "InternalName", "icinga-studio.exe"
VALUE "LegalCopyright", "© Icinga Development Team"
VALUE "OriginalFilename", "icinga-studio.exe"
VALUE "ProductName", "Icinga 2"
VALUE "ProductVersion", VERSION
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 0x04E4
END
END

40
icinga-studio/icinga.xpm Normal file
View File

@ -0,0 +1,40 @@
/* XPM */
static const char *icinga_xpm[] = {
"32 32 5 1",
" c None",
". c #808080",
"+ c #000000",
"@ c #C0C0C0",
"# c #FFFFFF",
" ",
" .++++++++++++++++++++++++. ",
" .+++++++++++++++..+++++++++. ",
" .+++++++++++++++@##@+++++++++. ",
" +++++++++++++++.####.+++++++++ ",
" +++++++++++++++.####.+++++++++ ",
" ++++++++++++++++####++++++++++ ",
" ++++++++++++++++@@..++++++++++ ",
" +++++..+++++++++#.++++++++++++ ",
" ++++.##@+++++++@#+++++++++++++ ",
" ++++.###+++++++#.+++++++++.+++ ",
" ++++.###@++.@@@#+++++++++@##.+ ",
" +++++++.@#######.+++++++.###@+ ",
" +++++++++########++++..@####.+ ",
" ++++++++.########@@###@...@.++ ",
" ++++++++.#########@..+++++++++ ",
" ++++++++.########@++++++++++++ ",
" ++++++++.########.++++++++++++ ",
" +++++++++########+++++++++++++ ",
" +++++++++.######.+++++++++++++ ",
" +++++++++.#....#.+++++++++++++ ",
" ++++++++.#.++++.#.++++++++++++ ",
" ++++++++@@++++++##.+++++++++++ ",
" ++++@##@#+++++++##@+++++++++++ ",
" +++@####@+++++++..++++++++++++ ",
" +++######.++++++++++++++++++++ ",
" +++######.++++++++++++++++++++ ",
" +++######+++++++++++++++++++++ ",
" .++.####.++++++++++++++++++++. ",
" .+++..+++++++++++++++++++++. ",
" .++++++++++++++++++++++++. ",
" "};

258
icinga-studio/mainform.cpp Normal file
View File

@ -0,0 +1,258 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) *
* *
* 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, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#include "icinga-studio/mainform.hpp"
#include "icinga-studio/aboutform.hpp"
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/foreach.hpp>
using namespace icinga;
MainForm::MainForm(wxWindow *parent, const Url::Ptr& url)
: MainFormBase(parent)
{
#ifdef _WIN32
SetIcon(wxICON(icinga));
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
#endif /* _WIN32 */
String host, port, user, pass;
std::string authority = url->GetAuthority();
std::vector<std::string> tokens;
boost::algorithm::split(tokens, authority, boost::is_any_of("@"));
if (tokens.size() > 1) {
std::vector<std::string> userinfo;
boost::algorithm::split(userinfo, tokens[0], boost::is_any_of(":"));
user = userinfo[0];
pass = userinfo[1];
}
std::vector<std::string> hostport;
boost::algorithm::split(hostport, tokens.size() > 1 ? tokens[1] : tokens[0], boost::is_any_of(":"));
host = hostport[0];
if (hostport.size() > 1)
port = hostport[1];
else
port = "5665";
m_ApiClient = new ApiClient(host, port, user, pass);
m_ApiClient->GetTypes(boost::bind(&MainForm::TypesCompletionHandler, this, _1, true));
std::string title = host;
if (port != "5665")
title += +":" + port;
title += " - Icinga Studio";
SetTitle(title);
m_ObjectsList->InsertColumn(0, "Name", 0, 300);
}
void MainForm::TypesCompletionHandler(const std::vector<ApiType::Ptr>& types, bool forward)
{
if (forward) {
CallAfter(boost::bind(&MainForm::TypesCompletionHandler, this, types, false));
return;
}
m_TypesTree->DeleteAllItems();
wxTreeItemId rootNode = m_TypesTree->AddRoot("root");
bool all = false;
std::map<String, wxTreeItemId> items;
m_Types.clear();
while (!all) {
all = true;
BOOST_FOREACH(const ApiType::Ptr& type, types) {
std::string name = type->Name;
if (items.find(name) != items.end())
continue;
all = false;
wxTreeItemId parent;
if (type->BaseName.IsEmpty())
parent = rootNode;
else {
std::map<String, wxTreeItemId>::const_iterator it = items.find(type->BaseName);
if (it == items.end())
continue;
parent = it->second;
}
m_Types[name] = type;
items[name] = m_TypesTree->AppendItem(parent, name, 0);
}
}
}
void MainForm::OnTypeSelected(wxTreeEvent& event)
{
wxTreeItemId selectedId = m_TypesTree->GetSelection();
wxString typeName = m_TypesTree->GetItemText(selectedId);
ApiType::Ptr type = m_Types[typeName.ToStdString()];
std::vector<String> attrs;
attrs.push_back(type->Name.ToLower() + ".__name");
m_ApiClient->GetObjects(type->PluralName, boost::bind(&MainForm::ObjectsCompletionHandler, this, _1, true),
std::vector<String>(), attrs);
}
void MainForm::ObjectsCompletionHandler(const std::vector<ApiObject::Ptr>& objects, bool forward)
{
if (forward) {
CallAfter(boost::bind(&MainForm::ObjectsCompletionHandler, this, objects, false));
return;
}
wxTreeItemId selectedId = m_TypesTree->GetSelection();
wxString typeName = m_TypesTree->GetItemText(selectedId);
ApiType::Ptr type = m_Types[typeName.ToStdString()];
String nameAttr = type->Name.ToLower() + ".__name";
m_ObjectsList->DeleteAllItems();
BOOST_FOREACH(const ApiObject::Ptr& object, objects) {
std::map<String, Value>::const_iterator it = object->Attrs.find(nameAttr);
if (it == object->Attrs.end())
continue;
String name = it->second;
m_ObjectsList->InsertItem(0, name.GetData());
}
}
void MainForm::OnObjectSelected(wxListEvent& event)
{
wxTreeItemId selectedId = m_TypesTree->GetSelection();
wxString typeName = m_TypesTree->GetItemText(selectedId);
ApiType::Ptr type = m_Types[typeName.ToStdString()];
long itemIndex = -1;
std::string objectName;
while ((itemIndex = m_ObjectsList->GetNextItem(itemIndex,
wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED)) != wxNOT_FOUND) {
objectName = m_ObjectsList->GetItemText(itemIndex);
break;
}
if (objectName.empty())
return;
std::vector<String> names;
names.push_back(objectName);
m_ApiClient->GetObjects(type->PluralName, boost::bind(&MainForm::ObjectDetailsCompletionHandler, this, _1, true), names);
}
wxPGProperty *MainForm::ValueToProperty(const String& name, const Value& value)
{
wxPGProperty *prop;
if (value.IsNumber()) {
double val = value;
return new wxFloatProperty(name.GetData(), wxPG_LABEL, value);
} else if (value.IsBoolean()) {
bool val = value;
return new wxBoolProperty(name.GetData(), wxPG_LABEL, value);
} else if (value.IsObjectType<Array>()) {
wxArrayString val;
Array::Ptr arr = value;
ObjectLock olock(arr);
BOOST_FOREACH(const Value& aitem, arr)
{
String val1 = aitem;
val.Add(val1.GetData());
}
return new wxArrayStringProperty(name.GetData(), wxPG_LABEL, val);
} else if (value.IsObjectType<Dictionary>()) {
wxStringProperty *prop = new wxStringProperty(name.GetData(), wxPG_LABEL, "<dictionary>");
Dictionary::Ptr dict = value;
ObjectLock olock(dict);
BOOST_FOREACH(const Dictionary::Pair& kv, dict) {
prop->AppendChild(ValueToProperty(kv.first, kv.second));
}
return prop;
} else {
String val = value;
return new wxStringProperty(name.GetData(), wxPG_LABEL, val.GetData());
}
}
void MainForm::ObjectDetailsCompletionHandler(const std::vector<ApiObject::Ptr>& objects, bool forward)
{
if (forward) {
CallAfter(boost::bind(&MainForm::ObjectDetailsCompletionHandler, this, objects, false));
return;
}
wxTreeItemId selectedId = m_TypesTree->GetSelection();
wxString typeName = m_TypesTree->GetItemText(selectedId);
ApiType::Ptr type = m_Types[typeName.ToStdString()];
String nameAttr = type->Name.ToLower() + ".__name";
m_PropertyGrid->Clear();
if (objects.empty())
return;
ApiObject::Ptr object = objects[0];
typedef std::pair<String, Value> kv_pair;
BOOST_FOREACH(const kv_pair& kv, object->Attrs) {
std::vector<String> tokens;
boost::algorithm::split(tokens, kv.first, boost::is_any_of("."));
wxPGProperty *prop = ValueToProperty(tokens[1], kv.second);
m_PropertyGrid->Append(prop);
m_PropertyGrid->SetPropertyReadOnly(prop);
}
}
void MainForm::OnQuitClicked(wxCommandEvent& event)
{
Close();
}
void MainForm::OnAboutClicked(wxCommandEvent& event)
{
AboutForm form(this);
form.ShowModal();
}

View File

@ -0,0 +1,53 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) *
* *
* 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, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#ifndef MAINFORM_H
#define MAINFORM_H
#include "icinga-studio/api.hpp"
#include "remote/url.hpp"
#include "icinga-studio/forms.h"
namespace icinga
{
class MainForm : public MainFormBase
{
public:
MainForm(wxWindow *parent, const Url::Ptr& url);
virtual void OnQuitClicked(wxCommandEvent& event) override;
virtual void OnAboutClicked(wxCommandEvent& event) override;
virtual void OnTypeSelected(wxTreeEvent& event) override;
virtual void OnObjectSelected(wxListEvent& event) override;
private:
ApiClient::Ptr m_ApiClient;
std::map<String, ApiType::Ptr> m_Types;
void TypesCompletionHandler(const std::vector<ApiType::Ptr>& types, bool forward);
void ObjectsCompletionHandler(const std::vector<ApiObject::Ptr>& objects, bool forward);
void ObjectDetailsCompletionHandler(const std::vector<ApiObject::Ptr>& objects, bool forward);
wxPGProperty *ValueToProperty(const String& name, const Value& value);
};
}
#endif /* MAINFORM_H */

View File

@ -86,4 +86,11 @@ install(
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/icinga2 LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/icinga2
) )
if(APPLE)
install(
TARGETS base
LIBRARY DESTINATION ${CMAKE_INSTALL_BINDIR}/icinga-studio.app/Contents
)
endif()
set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${CPACK_NSIS_EXTRA_INSTALL_COMMANDS}" PARENT_SCOPE) set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${CPACK_NSIS_EXTRA_INSTALL_COMMANDS}" PARENT_SCOPE)

View File

@ -147,7 +147,15 @@ typedef boost::error_info<struct errinfo_getaddrinfo_error_, int> errinfo_getadd
inline std::string to_string(const errinfo_getaddrinfo_error& e) inline std::string to_string(const errinfo_getaddrinfo_error& e)
{ {
return "[errinfo_getaddrinfo_error] = " + String(gai_strerror(e.value())) + "\n"; String msg;
#ifdef _WIN32
msg = gai_strerrorA(e.value());
#else /* _WIN32 */
msg = gai_strerror(e.value());
#endif /* _WIN32 */
return "[errinfo_getaddrinfo_error] = " + String(msg) + "\n";
} }
struct errinfo_message_; struct errinfo_message_;

View File

@ -207,6 +207,8 @@ void SocketEvents::Register(Object *lifesupportObject)
l_SocketIOSockets[m_FD] = desc; l_SocketIOSockets[m_FD] = desc;
m_Events = true;
/* There's no need to wake up the I/O thread here. */ /* There's no need to wake up the I/O thread here. */
} }
@ -220,6 +222,8 @@ void SocketEvents::Unregister(void)
l_SocketIOSockets.erase(m_FD); l_SocketIOSockets.erase(m_FD);
m_FD = INVALID_SOCKET; m_FD = INVALID_SOCKET;
m_Events = false;
} }
WakeUpThread(true); WakeUpThread(true);
@ -244,6 +248,12 @@ void SocketEvents::ChangeEvents(int events)
WakeUpThread(); WakeUpThread();
} }
bool SocketEvents::IsHandlingEvents(void) const
{
boost::mutex::scoped_lock lock(l_SocketIOMutex);
return m_Events;
}
void SocketEvents::OnEvent(int revents) void SocketEvents::OnEvent(int revents)
{ {

View File

@ -42,11 +42,14 @@ public:
void ChangeEvents(int events); void ChangeEvents(int events);
bool IsHandlingEvents(void) const;
protected: protected:
SocketEvents(const Socket::Ptr& socket, Object *lifesupportObject); SocketEvents(const Socket::Ptr& socket, Object *lifesupportObject);
private: private:
SOCKET m_FD; SOCKET m_FD;
bool m_Events;
static void InitializeThread(void); static void InitializeThread(void);
static void ThreadProc(void); static void ThreadProc(void);

View File

@ -191,7 +191,7 @@ void TlsStream::OnEvent(int revents)
lock.unlock(); lock.unlock();
while (m_RecvQ->IsDataAvailable()) while (m_RecvQ->IsDataAvailable() && IsHandlingEvents())
SignalDataAvailable(); SignalDataAvailable();
if (m_Shutdown && !m_SendQ->IsDataAvailable()) if (m_Shutdown && !m_SendQ->IsDataAvailable())
@ -318,6 +318,8 @@ void TlsStream::Close(void)
boost::mutex::scoped_lock lock(m_Mutex); boost::mutex::scoped_lock lock(m_Mutex);
m_Eof = true;
if (!m_SSL) if (!m_SSL)
return; return;
@ -326,8 +328,6 @@ void TlsStream::Close(void)
m_Socket->Close(); m_Socket->Close();
m_Socket.reset(); m_Socket.reset();
m_Eof = true;
} }
bool TlsStream::IsEof(void) const bool TlsStream::IsEof(void) const

View File

@ -48,7 +48,7 @@ class I2_BASE_API TlsStream : public Stream, private SocketEvents
public: public:
DECLARE_PTR_TYPEDEFS(TlsStream); DECLARE_PTR_TYPEDEFS(TlsStream);
TlsStream(const Socket::Ptr& socket, const String& hostname, ConnectionRole role, const boost::shared_ptr<SSL_CTX>& sslContext); TlsStream(const Socket::Ptr& socket, const String& hostname, ConnectionRole role, const boost::shared_ptr<SSL_CTX>& sslContext = MakeSSLContext());
~TlsStream(void); ~TlsStream(void);
boost::shared_ptr<X509> GetClientCertificate(void) const; boost::shared_ptr<X509> GetClientCertificate(void) const;

View File

@ -88,6 +88,7 @@ boost::shared_ptr<SSL_CTX> MakeSSLContext(const String& pubkey, const String& pr
SSL_CTX_set_mode(sslContext.get(), SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); SSL_CTX_set_mode(sslContext.get(), SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
SSL_CTX_set_session_id_context(sslContext.get(), (const unsigned char *)"Icinga 2", 8); SSL_CTX_set_session_id_context(sslContext.get(), (const unsigned char *)"Icinga 2", 8);
if (!pubkey.IsEmpty()) {
if (!SSL_CTX_use_certificate_chain_file(sslContext.get(), pubkey.CStr())) { if (!SSL_CTX_use_certificate_chain_file(sslContext.get(), pubkey.CStr())) {
Log(LogCritical, "SSL") Log(LogCritical, "SSL")
<< "Error with public key file '" << pubkey << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\""; << "Error with public key file '" << pubkey << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
@ -96,7 +97,9 @@ boost::shared_ptr<SSL_CTX> MakeSSLContext(const String& pubkey, const String& pr
<< errinfo_openssl_error(ERR_peek_error()) << errinfo_openssl_error(ERR_peek_error())
<< boost::errinfo_file_name(pubkey)); << boost::errinfo_file_name(pubkey));
} }
}
if (!privkey.IsEmpty()) {
if (!SSL_CTX_use_PrivateKey_file(sslContext.get(), privkey.CStr(), SSL_FILETYPE_PEM)) { if (!SSL_CTX_use_PrivateKey_file(sslContext.get(), privkey.CStr(), SSL_FILETYPE_PEM)) {
Log(LogCritical, "SSL") Log(LogCritical, "SSL")
<< "Error with private key file '" << privkey << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\""; << "Error with private key file '" << privkey << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
@ -113,6 +116,7 @@ boost::shared_ptr<SSL_CTX> MakeSSLContext(const String& pubkey, const String& pr
<< boost::errinfo_api_function("SSL_CTX_check_private_key") << boost::errinfo_api_function("SSL_CTX_check_private_key")
<< errinfo_openssl_error(ERR_peek_error())); << errinfo_openssl_error(ERR_peek_error()));
} }
}
if (!cakey.IsEmpty()) { if (!cakey.IsEmpty()) {
if (!SSL_CTX_load_verify_locations(sslContext.get(), cakey.CStr(), NULL)) { if (!SSL_CTX_load_verify_locations(sslContext.get(), cakey.CStr(), NULL)) {

View File

@ -38,7 +38,7 @@ namespace icinga
{ {
void I2_BASE_API InitializeOpenSSL(void); void I2_BASE_API InitializeOpenSSL(void);
boost::shared_ptr<SSL_CTX> I2_BASE_API MakeSSLContext(const String& pubkey, const String& privkey, const String& cakey = String()); boost::shared_ptr<SSL_CTX> I2_BASE_API MakeSSLContext(const String& pubkey = String(), const String& privkey = String(), const String& cakey = String());
void I2_BASE_API AddCRLToSSLContext(const boost::shared_ptr<SSL_CTX>& context, const String& crlPath); void I2_BASE_API AddCRLToSSLContext(const boost::shared_ptr<SSL_CTX>& context, const String& crlPath);
String I2_BASE_API GetCertificateCN(const boost::shared_ptr<X509>& certificate); String I2_BASE_API GetCertificateCN(const boost::shared_ptr<X509>& certificate);
boost::shared_ptr<X509> I2_BASE_API GetX509Certificate(const String& pemfile); boost::shared_ptr<X509> I2_BASE_API GetX509Certificate(const String& pemfile);

View File

@ -21,10 +21,12 @@
#define WIN32_H #define WIN32_H
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#ifndef _WIN32_WINNT
#define _WIN32_WINNT _WIN32_WINNT_VISTA #define _WIN32_WINNT _WIN32_WINNT_VISTA
#endif /* _WIN32_WINNT */
#define NOMINMAX #define NOMINMAX
#include <windows.h>
#include <winsock2.h> #include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h> #include <ws2tcpip.h>
#include <imagehlp.h> #include <imagehlp.h>
#include <shlwapi.h> #include <shlwapi.h>

View File

@ -61,3 +61,10 @@ install(
RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/icinga2 LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/icinga2
) )
if(APPLE)
install(
TARGETS config
LIBRARY DESTINATION ${CMAKE_INSTALL_BINDIR}/icinga-studio.app/Contents
)
endif()

View File

@ -27,7 +27,7 @@ set(remote_SOURCES
configfileshandler.cpp configmoduleshandler.cpp configmoduleutility.cpp configobjectutility.cpp configfileshandler.cpp configmoduleshandler.cpp configmoduleutility.cpp configobjectutility.cpp
configstageshandler.cpp createobjecthandler.cpp deleteobjecthandler.cpp configstageshandler.cpp createobjecthandler.cpp deleteobjecthandler.cpp
endpoint.cpp endpoint.thpp filterutility.cpp endpoint.cpp endpoint.thpp filterutility.cpp
httpchunkedencoding.cpp httpconnection.cpp httphandler.cpp httprequest.cpp httpresponse.cpp httpchunkedencoding.cpp httpclientconnection.cpp httpserverconnection.cpp httphandler.cpp httprequest.cpp httpresponse.cpp
httputility.cpp jsonrpc.cpp jsonrpcconnection.cpp jsonrpcconnection-heartbeat.cpp httputility.cpp jsonrpc.cpp jsonrpcconnection.cpp jsonrpcconnection-heartbeat.cpp
messageorigin.cpp modifyobjecthandler.cpp statusqueryhandler.cpp typequeryhandler.cpp messageorigin.cpp modifyobjecthandler.cpp statusqueryhandler.cpp typequeryhandler.cpp
url.cpp zone.cpp zone.thpp url.cpp zone.cpp zone.thpp
@ -55,6 +55,13 @@ install(
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/icinga2 LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/icinga2
) )
if(APPLE)
install(
TARGETS remote
LIBRARY DESTINATION ${CMAKE_INSTALL_BINDIR}/icinga-studio.app/Contents
)
endif()
#install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/api\")") #install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/api\")")
install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/api/log\")") install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/api/log\")")
install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/api/repository\")") install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/api/repository\")")

View File

@ -386,7 +386,7 @@ void ApiListener::NewClientHandlerInternal(const Socket::Ptr& client, const Stri
} else { } else {
Log(LogInformation, "ApiListener", "New HTTP client"); Log(LogInformation, "ApiListener", "New HTTP client");
HttpConnection::Ptr aclient = new HttpConnection(identity, verify_ok, tlsStream); HttpServerConnection::Ptr aclient = new HttpServerConnection(identity, verify_ok, tlsStream);
aclient->Start(); aclient->Start();
AddHttpClient(aclient); AddHttpClient(aclient);
} }
@ -908,19 +908,19 @@ std::set<JsonRpcConnection::Ptr> ApiListener::GetAnonymousClients(void) const
return m_AnonymousClients; return m_AnonymousClients;
} }
void ApiListener::AddHttpClient(const HttpConnection::Ptr& aclient) void ApiListener::AddHttpClient(const HttpServerConnection::Ptr& aclient)
{ {
ObjectLock olock(this); ObjectLock olock(this);
m_HttpClients.insert(aclient); m_HttpClients.insert(aclient);
} }
void ApiListener::RemoveHttpClient(const HttpConnection::Ptr& aclient) void ApiListener::RemoveHttpClient(const HttpServerConnection::Ptr& aclient)
{ {
ObjectLock olock(this); ObjectLock olock(this);
m_HttpClients.erase(aclient); m_HttpClients.erase(aclient);
} }
std::set<HttpConnection::Ptr> ApiListener::GetHttpClients(void) const std::set<HttpServerConnection::Ptr> ApiListener::GetHttpClients(void) const
{ {
ObjectLock olock(this); ObjectLock olock(this);
return m_HttpClients; return m_HttpClients;

View File

@ -22,7 +22,7 @@
#include "remote/apilistener.thpp" #include "remote/apilistener.thpp"
#include "remote/jsonrpcconnection.hpp" #include "remote/jsonrpcconnection.hpp"
#include "remote/httpconnection.hpp" #include "remote/httpserverconnection.hpp"
#include "remote/endpoint.hpp" #include "remote/endpoint.hpp"
#include "remote/messageorigin.hpp" #include "remote/messageorigin.hpp"
#include "base/configobject.hpp" #include "base/configobject.hpp"
@ -69,9 +69,9 @@ public:
void RemoveAnonymousClient(const JsonRpcConnection::Ptr& aclient); void RemoveAnonymousClient(const JsonRpcConnection::Ptr& aclient);
std::set<JsonRpcConnection::Ptr> GetAnonymousClients(void) const; std::set<JsonRpcConnection::Ptr> GetAnonymousClients(void) const;
void AddHttpClient(const HttpConnection::Ptr& aclient); void AddHttpClient(const HttpServerConnection::Ptr& aclient);
void RemoveHttpClient(const HttpConnection::Ptr& aclient); void RemoveHttpClient(const HttpServerConnection::Ptr& aclient);
std::set<HttpConnection::Ptr> GetHttpClients(void) const; std::set<HttpServerConnection::Ptr> GetHttpClients(void) const;
static Value ConfigUpdateHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params); static Value ConfigUpdateHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params);
@ -85,7 +85,7 @@ private:
boost::shared_ptr<SSL_CTX> m_SSLContext; boost::shared_ptr<SSL_CTX> m_SSLContext;
std::set<TcpSocket::Ptr> m_Servers; std::set<TcpSocket::Ptr> m_Servers;
std::set<JsonRpcConnection::Ptr> m_AnonymousClients; std::set<JsonRpcConnection::Ptr> m_AnonymousClients;
std::set<HttpConnection::Ptr> m_HttpClients; std::set<HttpServerConnection::Ptr> m_HttpClients;
Timer::Ptr m_Timer; Timer::Ptr m_Timer;
void ApiTimerHandler(void); void ApiTimerHandler(void);

View File

@ -28,6 +28,7 @@ StreamReadStatus HttpChunkedEncoding::ReadChunkFromStream(const Stream::Ptr& str
if (context.LengthIndicator == -1) { if (context.LengthIndicator == -1) {
String line; String line;
StreamReadStatus status = stream->ReadLine(&line, context.StreamContext, may_wait); StreamReadStatus status = stream->ReadLine(&line, context.StreamContext, may_wait);
may_wait = false;
if (status != StatusNewItem) if (status != StatusNewItem)
return status; return status;
@ -36,8 +37,8 @@ StreamReadStatus HttpChunkedEncoding::ReadChunkFromStream(const Stream::Ptr& str
msgbuf << std::hex << line; msgbuf << std::hex << line;
msgbuf >> context.LengthIndicator; msgbuf >> context.LengthIndicator;
return StatusNeedData; }
} else {
StreamReadContext& scontext = context.StreamContext; StreamReadContext& scontext = context.StreamContext;
if (scontext.Eof) if (scontext.Eof)
return StatusEof; return StatusEof;
@ -51,20 +52,21 @@ StreamReadStatus HttpChunkedEncoding::ReadChunkFromStream(const Stream::Ptr& str
scontext.MustRead = false; scontext.MustRead = false;
} }
if (scontext.Size < (size_t)context.LengthIndicator) { size_t NewlineLength = context.LengthIndicator ? 2 : 0;
if (scontext.Size < (size_t)context.LengthIndicator + NewlineLength) {
scontext.MustRead = true; scontext.MustRead = true;
return StatusNeedData; return StatusNeedData;
} }
*data = new char[context.LengthIndicator]; *data = new char[context.LengthIndicator];
*size = context.LengthIndicator; *size = context.LengthIndicator;
memcpy(data, scontext.Buffer, context.LengthIndicator); memcpy(*data, scontext.Buffer, context.LengthIndicator);
scontext.DropData(context.LengthIndicator); scontext.DropData(context.LengthIndicator + NewlineLength);
context.LengthIndicator = -1; context.LengthIndicator = -1;
return StatusNewItem; return StatusNewItem;
}
} }
void HttpChunkedEncoding::WriteChunkToStream(const Stream::Ptr& stream, const char *data, size_t count) void HttpChunkedEncoding::WriteChunkToStream(const Stream::Ptr& stream, const char *data, size_t count)

View File

@ -0,0 +1,156 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) *
* *
* 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, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#include "remote/httpclientconnection.hpp"
#include "remote/base64.hpp"
#include "base/configtype.hpp"
#include "base/objectlock.hpp"
#include "base/utility.hpp"
#include "base/logger.hpp"
#include "base/exception.hpp"
#include "base/convert.hpp"
#include "base/tcpsocket.hpp"
#include "base/tlsstream.hpp"
#include "base/networkstream.hpp"
#include <boost/smart_ptr/make_shared.hpp>
using namespace icinga;
HttpClientConnection::HttpClientConnection(const String& host, const String& port, bool tls)
: m_Host(host), m_Port(port), m_Tls(tls)
{ }
void HttpClientConnection::Start(void)
{
/* Nothing to do here atm. */
}
void HttpClientConnection::Reconnect(void)
{
if (m_Stream)
m_Stream->Close();
m_Context.~StreamReadContext();
new (&m_Context) StreamReadContext();
TcpSocket::Ptr socket = new TcpSocket();
socket->Connect(m_Host, m_Port);
if (m_Tls)
m_Stream = new TlsStream(socket, m_Host, RoleClient);
else
ASSERT(!"Non-TLS HTTP connections not supported.");
//m_Stream = new NetworkStream(socket); -- does not currently work because the NetworkStream class doesn't support async I/O
m_Stream->RegisterDataHandler(boost::bind(&HttpClientConnection::DataAvailableHandler, this));
if (m_Stream->IsDataAvailable())
DataAvailableHandler();
}
Stream::Ptr HttpClientConnection::GetStream(void) const
{
return m_Stream;
}
String HttpClientConnection::GetHost(void) const
{
return m_Host;
}
String HttpClientConnection::GetPort(void) const
{
return m_Port;
}
bool HttpClientConnection::GetTls(void) const
{
return m_Tls;
}
void HttpClientConnection::Disconnect(void)
{
Log(LogDebug, "HttpClientConnection", "Http client disconnected");
m_Stream->Shutdown();
}
bool HttpClientConnection::ProcessMessage(void)
{
bool res;
if (m_Requests.empty())
return false;
const std::pair<boost::shared_ptr<HttpRequest>, HttpCompletionCallback>& currentRequest = *m_Requests.begin();
HttpRequest& request = *currentRequest.first.get();
const HttpCompletionCallback& callback = currentRequest.second;
if (!m_CurrentResponse)
m_CurrentResponse = boost::make_shared<HttpResponse>(m_Stream, request);
boost::shared_ptr<HttpResponse> currentResponse = m_CurrentResponse;
HttpResponse& response = *currentResponse.get();
try {
res = response.Parse(m_Context, false);
} catch (const std::exception& ex) {
callback(request, response);
m_Stream->Shutdown();
return false;
}
if (response.Complete) {
callback(request, response);
m_Requests.pop_front();
m_CurrentResponse.reset();
return true;
}
return res;
}
void HttpClientConnection::DataAvailableHandler(void)
{
boost::mutex::scoped_lock lock(m_DataHandlerMutex);
try {
while (ProcessMessage())
; /* empty loop body */
} catch (const std::exception& ex) {
Log(LogWarning, "HttpClientConnection")
<< "Error while reading Http request: " << DiagnosticInformation(ex);
Disconnect();
}
}
boost::shared_ptr<HttpRequest> HttpClientConnection::NewRequest(void)
{
Reconnect();
return boost::make_shared<HttpRequest>(m_Stream);
}
void HttpClientConnection::SubmitRequest(const boost::shared_ptr<HttpRequest>& request, const HttpCompletionCallback& callback)
{
m_Requests.push_back(std::make_pair(request, callback));
request->Finish();
}

View File

@ -0,0 +1,78 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) *
* *
* 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, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#ifndef HTTPCLIENTCONNECTION_H
#define HTTPCLIENTCONNECTION_H
#include "remote/httprequest.hpp"
#include "remote/httpresponse.hpp"
#include "base/stream.hpp"
#include "base/timer.hpp"
#include <deque>
namespace icinga
{
/**
* An HTTP client connection.
*
* @ingroup remote
*/
class I2_REMOTE_API HttpClientConnection : public Object
{
public:
DECLARE_PTR_TYPEDEFS(HttpClientConnection);
HttpClientConnection(const String& host, const String& port, bool tls = true);
void Start(void);
Stream::Ptr GetStream(void) const;
String GetHost(void) const;
String GetPort(void) const;
bool GetTls(void) const;
void Disconnect(void);
boost::shared_ptr<HttpRequest> NewRequest(void);
typedef boost::function<void(HttpRequest&, HttpResponse&)> HttpCompletionCallback;
void SubmitRequest(const boost::shared_ptr<HttpRequest>& request, const HttpCompletionCallback& callback);
private:
String m_Host;
String m_Port;
bool m_Tls;
Stream::Ptr m_Stream;
std::deque<std::pair<boost::shared_ptr<HttpRequest>, HttpCompletionCallback> > m_Requests;
boost::shared_ptr<HttpResponse> m_CurrentResponse;
boost::mutex m_DataHandlerMutex;
StreamReadContext m_Context;
void Reconnect(void);
bool ProcessMessage(void);
void DataAvailableHandler(void);
void ProcessMessageAsync(HttpRequest& request);
};
}
#endif /* HTTPCLIENTCONNECTION_H */

View File

@ -19,27 +19,29 @@
#include "remote/httprequest.hpp" #include "remote/httprequest.hpp"
#include "base/logger.hpp" #include "base/logger.hpp"
#include "base/application.hpp"
#include "base/convert.hpp" #include "base/convert.hpp"
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/split.hpp> #include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp> #include <boost/algorithm/string/classification.hpp>
#include <boost/smart_ptr/make_shared.hpp>
#include <boost/foreach.hpp>
using namespace icinga; using namespace icinga;
HttpRequest::HttpRequest(StreamReadContext& src) HttpRequest::HttpRequest(const Stream::Ptr& stream)
: Complete(false), : Complete(false),
ProtocolVersion(HttpVersion10), ProtocolVersion(HttpVersion11),
Headers(new Dictionary()), Headers(new Dictionary()),
m_Context(src), m_Stream(stream),
m_ChunkContext(m_Context),
m_State(HttpRequestStart) m_State(HttpRequestStart)
{ } { }
bool HttpRequest::Parse(const Stream::Ptr& stream, StreamReadContext& src, bool may_wait) bool HttpRequest::Parse(StreamReadContext& src, bool may_wait)
{ {
if (m_State != HttpRequestBody) { if (m_State != HttpRequestBody) {
String line; String line;
StreamReadStatus srs = stream->ReadLine(&line, src, may_wait); StreamReadStatus srs = m_Stream->ReadLine(&line, src, may_wait);
if (srs != StatusNewItem) if (srs != StatusNewItem)
return false; return false;
@ -95,9 +97,12 @@ bool HttpRequest::Parse(const Stream::Ptr& stream, StreamReadContext& src, bool
} }
} else if (m_State == HttpRequestBody) { } else if (m_State == HttpRequestBody) {
if (Headers->Get("transfer-encoding") == "chunked") { if (Headers->Get("transfer-encoding") == "chunked") {
if (!m_ChunkContext)
m_ChunkContext = boost::make_shared<ChunkReadContext>(src);
char *data; char *data;
size_t size; size_t size;
StreamReadStatus srs = HttpChunkedEncoding::ReadChunkFromStream(stream, &data, &size, m_ChunkContext, false); StreamReadStatus srs = HttpChunkedEncoding::ReadChunkFromStream(m_Stream, &data, &size, *m_ChunkContext.get(), may_wait);
if (srs != StatusNewItem) if (srs != StatusNewItem)
return false; return false;
@ -114,27 +119,27 @@ bool HttpRequest::Parse(const Stream::Ptr& stream, StreamReadContext& src, bool
return true; return true;
} }
} else { } else {
if (m_Context.Eof) if (src.Eof)
BOOST_THROW_EXCEPTION(std::invalid_argument("Unexpected EOF in HTTP body")); BOOST_THROW_EXCEPTION(std::invalid_argument("Unexpected EOF in HTTP body"));
if (m_Context.MustRead) { if (src.MustRead) {
if (!m_Context.FillFromStream(stream, false)) { if (!src.FillFromStream(m_Stream, false)) {
m_Context.Eof = true; src.Eof = true;
BOOST_THROW_EXCEPTION(std::invalid_argument("Unexpected EOF in HTTP body")); BOOST_THROW_EXCEPTION(std::invalid_argument("Unexpected EOF in HTTP body"));
} }
m_Context.MustRead = false; src.MustRead = false;
} }
size_t length_indicator = Convert::ToLong(Headers->Get("content-length")); size_t length_indicator = Convert::ToLong(Headers->Get("content-length"));
if (m_Context.Size < length_indicator) { if (src.Size < length_indicator) {
m_Context.MustRead = true; src.MustRead = true;
return false; return false;
} }
m_Body->Write(m_Context.Buffer, length_indicator); m_Body->Write(src.Buffer, length_indicator);
m_Context.DropData(length_indicator); src.DropData(length_indicator);
Complete = true; Complete = true;
return true; return true;
} }
@ -151,3 +156,77 @@ size_t HttpRequest::ReadBody(char *data, size_t count)
return m_Body->Read(data, count, true); return m_Body->Read(data, count, true);
} }
void HttpRequest::AddHeader(const String& key, const String& value)
{
ASSERT(m_State == HttpRequestStart || m_State == HttpRequestHeaders);
Headers->Set(key.ToLower(), value);
}
void HttpRequest::FinishHeaders(void)
{
if (m_State == HttpRequestStart) {
String rqline = RequestMethod + " " + RequestUrl->Format() + " HTTP/1." + (ProtocolVersion == HttpVersion10 ? "0" : "1") + "\n";
m_Stream->Write(rqline.CStr(), rqline.GetLength());
m_State = HttpRequestHeaders;
}
if (m_State == HttpRequestHeaders) {
AddHeader("User-Agent", "Icinga/" + Application::GetVersion());
if (ProtocolVersion == HttpVersion11)
AddHeader("Transfer-Encoding", "chunked");
ObjectLock olock(Headers);
BOOST_FOREACH(const Dictionary::Pair& kv, Headers)
{
String header = kv.first + ": " + kv.second + "\n";
m_Stream->Write(header.CStr(), header.GetLength());
}
m_Stream->Write("\n", 1);
m_State = HttpRequestBody;
}
}
void HttpRequest::WriteBody(const char *data, size_t count)
{
ASSERT(m_State == HttpRequestStart || m_State == HttpRequestHeaders || m_State == HttpRequestBody);
if (ProtocolVersion == HttpVersion10) {
if (!m_Body)
m_Body = new FIFO();
m_Body->Write(data, count);
} else {
FinishHeaders();
HttpChunkedEncoding::WriteChunkToStream(m_Stream, data, count);
}
}
void HttpRequest::Finish(void)
{
ASSERT(m_State != HttpRequestEnd);
if (ProtocolVersion == HttpVersion10) {
if (m_Body)
AddHeader("Content-Length", Convert::ToString(m_Body->GetAvailableBytes()));
FinishHeaders();
while (m_Body && m_Body->IsDataAvailable()) {
char buffer[1024];
size_t rc = m_Body->Read(buffer, sizeof(buffer), true);
m_Stream->Write(buffer, rc);
}
} else {
if (m_State == HttpRequestStart || m_State == HttpRequestHeaders)
FinishHeaders();
WriteBody(NULL, 0);
m_Stream->Write("\r\n", 2);
}
m_State = HttpRequestEnd;
}

View File

@ -40,7 +40,8 @@ enum HttpRequestState
{ {
HttpRequestStart, HttpRequestStart,
HttpRequestHeaders, HttpRequestHeaders,
HttpRequestBody HttpRequestBody,
HttpRequestEnd
}; };
/** /**
@ -59,17 +60,22 @@ public:
Dictionary::Ptr Headers; Dictionary::Ptr Headers;
HttpRequest(StreamReadContext& ctx); HttpRequest(const Stream::Ptr& stream);
bool Parse(const Stream::Ptr& stream, StreamReadContext& src, bool may_wait);
bool Parse(StreamReadContext& src, bool may_wait);
size_t ReadBody(char *data, size_t count); size_t ReadBody(char *data, size_t count);
void AddHeader(const String& key, const String& value);
void WriteBody(const char *data, size_t count);
void Finish(void);
private: private:
StreamReadContext& m_Context; Stream::Ptr m_Stream;
ChunkReadContext m_ChunkContext; boost::shared_ptr<ChunkReadContext> m_ChunkContext;
HttpRequestState m_State; HttpRequestState m_State;
FIFO::Ptr m_Body; FIFO::Ptr m_Body;
void FinishHeaders(void);
}; };
} }

View File

@ -20,13 +20,16 @@
#include "remote/httpresponse.hpp" #include "remote/httpresponse.hpp"
#include "remote/httpchunkedencoding.hpp" #include "remote/httpchunkedencoding.hpp"
#include "base/logger.hpp" #include "base/logger.hpp"
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
#include "base/application.hpp" #include "base/application.hpp"
#include "base/convert.hpp" #include "base/convert.hpp"
#include <boost/smart_ptr/make_shared.hpp>
using namespace icinga; using namespace icinga;
HttpResponse::HttpResponse(const Stream::Ptr& stream, const HttpRequest& request) HttpResponse::HttpResponse(const Stream::Ptr& stream, const HttpRequest& request)
: m_State(HttpResponseStart), m_Request(request), m_Stream(stream) : Complete(false), m_State(HttpResponseStart), m_Request(request), m_Stream(stream)
{ } { }
void HttpResponse::SetStatus(int code, const String& message) void HttpResponse::SetStatus(int code, const String& message)
@ -109,3 +112,123 @@ void HttpResponse::Finish(void)
if (m_Request.ProtocolVersion == HttpVersion10 || m_Request.Headers->Get("connection") == "close") if (m_Request.ProtocolVersion == HttpVersion10 || m_Request.Headers->Get("connection") == "close")
m_Stream->Shutdown(); m_Stream->Shutdown();
} }
bool HttpResponse::Parse(StreamReadContext& src, bool may_wait)
{
if (m_State != HttpResponseBody) {
String line;
StreamReadStatus srs = m_Stream->ReadLine(&line, src, may_wait);
if (srs != StatusNewItem)
return false;
if (m_State == HttpResponseStart) {
/* ignore trailing new-lines */
if (line == "")
return true;
std::vector<String> tokens;
boost::algorithm::split(tokens, line, boost::is_any_of(" "));
Log(LogDebug, "HttpRequest")
<< "line: " << line << ", tokens: " << tokens.size();
if (tokens.size() < 3)
BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid HTTP request"));
if (tokens[0] == "HTTP/1.0")
ProtocolVersion = HttpVersion10;
else if (tokens[0] == "HTTP/1.1") {
ProtocolVersion = HttpVersion11;
} else
BOOST_THROW_EXCEPTION(std::invalid_argument("Unsupported HTTP version"));
StatusCode = Convert::ToLong(tokens[1]);
StatusMessage = tokens[2]; // TODO: Join tokens[2..end]
m_State = HttpResponseHeaders;
} else if (m_State == HttpResponseHeaders) {
if (!Headers)
Headers = new Dictionary();
if (line == "") {
m_State = HttpResponseBody;
/* we're done if the request doesn't contain a message body */
if (!Headers->Contains("content-length") && !Headers->Contains("transfer-encoding"))
Complete = true;
else
m_Body = new FIFO();
return true;
} else {
String::SizeType pos = line.FindFirstOf(":");
if (pos == String::NPos)
BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid HTTP request"));
String key = line.SubStr(0, pos).ToLower().Trim();
String value = line.SubStr(pos + 1).Trim();
Headers->Set(key, value);
}
} else {
VERIFY(!"Invalid HTTP request state.");
}
} else if (m_State == HttpResponseBody) {
if (Headers->Get("transfer-encoding") == "chunked") {
if (!m_ChunkContext)
m_ChunkContext = boost::make_shared<ChunkReadContext>(src);
char *data;
size_t size;
StreamReadStatus srs = HttpChunkedEncoding::ReadChunkFromStream(m_Stream, &data, &size, *m_ChunkContext.get(), may_wait);
if (srs != StatusNewItem)
return false;
Log(LogInformation, "HttpResponse")
<< "Read " << size << " bytes";
m_Body->Write(data, size);
delete[] data;
if (size == 0) {
Complete = true;
return true;
}
} else {
if (src.Eof)
BOOST_THROW_EXCEPTION(std::invalid_argument("Unexpected EOF in HTTP body"));
if (src.MustRead) {
if (!src.FillFromStream(m_Stream, false)) {
src.Eof = true;
BOOST_THROW_EXCEPTION(std::invalid_argument("Unexpected EOF in HTTP body"));
}
src.MustRead = false;
}
size_t length_indicator = Convert::ToLong(Headers->Get("content-length"));
if (src.Size < length_indicator) {
src.MustRead = true;
return false;
}
m_Body->Write(src.Buffer, length_indicator);
src.DropData(length_indicator);
Complete = true;
return true;
}
}
return true;
}
size_t HttpResponse::ReadBody(char *data, size_t count)
{
if (!m_Body)
return 0;
else
return m_Body->Read(data, count, true);
}

View File

@ -43,8 +43,19 @@ enum HttpResponseState
struct I2_REMOTE_API HttpResponse struct I2_REMOTE_API HttpResponse
{ {
public: public:
bool Complete;
HttpVersion ProtocolVersion;
int StatusCode;
String StatusMessage;
Dictionary::Ptr Headers;
HttpResponse(const Stream::Ptr& stream, const HttpRequest& request); HttpResponse(const Stream::Ptr& stream, const HttpRequest& request);
bool Parse(StreamReadContext& src, bool may_wait);
size_t ReadBody(char *data, size_t count);
void SetStatus(int code, const String& message); void SetStatus(int code, const String& message);
void AddHeader(const String& key, const String& value); void AddHeader(const String& key, const String& value);
void WriteBody(const char *data, size_t count); void WriteBody(const char *data, size_t count);
@ -52,6 +63,7 @@ public:
private: private:
HttpResponseState m_State; HttpResponseState m_State;
boost::shared_ptr<ChunkReadContext> m_ChunkContext;
const HttpRequest& m_Request; const HttpRequest& m_Request;
Stream::Ptr m_Stream; Stream::Ptr m_Stream;
FIFO::Ptr m_Body; FIFO::Ptr m_Body;

View File

@ -17,7 +17,7 @@
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/ ******************************************************************************/
#include "remote/httpconnection.hpp" #include "remote/httpserverconnection.hpp"
#include "remote/httphandler.hpp" #include "remote/httphandler.hpp"
#include "remote/apilistener.hpp" #include "remote/apilistener.hpp"
#include "remote/apifunction.hpp" #include "remote/apifunction.hpp"
@ -33,47 +33,46 @@
using namespace icinga; using namespace icinga;
static boost::once_flag l_HttpConnectionOnceFlag = BOOST_ONCE_INIT; static boost::once_flag l_HttpServerConnectionOnceFlag = BOOST_ONCE_INIT;
static Timer::Ptr l_HttpConnectionTimeoutTimer; static Timer::Ptr l_HttpServerConnectionTimeoutTimer;
HttpConnection::HttpConnection(const String& identity, bool authenticated, const TlsStream::Ptr& stream) HttpServerConnection::HttpServerConnection(const String& identity, bool authenticated, const TlsStream::Ptr& stream)
: m_Stream(stream), m_Seen(Utility::GetTime()), : m_Stream(stream), m_CurrentRequest(stream), m_Seen(Utility::GetTime()), m_PendingRequests(0)
m_CurrentRequest(m_Context), m_PendingRequests(0)
{ {
boost::call_once(l_HttpConnectionOnceFlag, &HttpConnection::StaticInitialize); boost::call_once(l_HttpServerConnectionOnceFlag, &HttpServerConnection::StaticInitialize);
if (authenticated) if (authenticated)
m_ApiUser = ApiUser::GetByClientCN(identity); m_ApiUser = ApiUser::GetByClientCN(identity);
} }
void HttpConnection::StaticInitialize(void) void HttpServerConnection::StaticInitialize(void)
{ {
l_HttpConnectionTimeoutTimer = new Timer(); l_HttpServerConnectionTimeoutTimer = new Timer();
l_HttpConnectionTimeoutTimer->OnTimerExpired.connect(boost::bind(&HttpConnection::TimeoutTimerHandler)); l_HttpServerConnectionTimeoutTimer->OnTimerExpired.connect(boost::bind(&HttpServerConnection::TimeoutTimerHandler));
l_HttpConnectionTimeoutTimer->SetInterval(15); l_HttpServerConnectionTimeoutTimer->SetInterval(15);
l_HttpConnectionTimeoutTimer->Start(); l_HttpServerConnectionTimeoutTimer->Start();
} }
void HttpConnection::Start(void) void HttpServerConnection::Start(void)
{ {
m_Stream->RegisterDataHandler(boost::bind(&HttpConnection::DataAvailableHandler, this)); m_Stream->RegisterDataHandler(boost::bind(&HttpServerConnection::DataAvailableHandler, this));
if (m_Stream->IsDataAvailable()) if (m_Stream->IsDataAvailable())
DataAvailableHandler(); DataAvailableHandler();
} }
ApiUser::Ptr HttpConnection::GetApiUser(void) const ApiUser::Ptr HttpServerConnection::GetApiUser(void) const
{ {
return m_ApiUser; return m_ApiUser;
} }
TlsStream::Ptr HttpConnection::GetStream(void) const TlsStream::Ptr HttpServerConnection::GetStream(void) const
{ {
return m_Stream; return m_Stream;
} }
void HttpConnection::Disconnect(void) void HttpServerConnection::Disconnect(void)
{ {
Log(LogDebug, "HttpConnection", "Http client disconnected"); Log(LogDebug, "HttpServerConnection", "Http client disconnected");
ApiListener::Ptr listener = ApiListener::GetInstance(); ApiListener::Ptr listener = ApiListener::GetInstance();
listener->RemoveHttpClient(this); listener->RemoveHttpClient(this);
@ -81,12 +80,12 @@ void HttpConnection::Disconnect(void)
m_Stream->Shutdown(); m_Stream->Shutdown();
} }
bool HttpConnection::ProcessMessage(void) bool HttpServerConnection::ProcessMessage(void)
{ {
bool res; bool res;
try { try {
res = m_CurrentRequest.Parse(m_Stream, m_Context, false); res = m_CurrentRequest.Parse(m_Context, false);
} catch (const std::exception& ex) { } catch (const std::exception& ex) {
HttpResponse response(m_Stream, m_CurrentRequest); HttpResponse response(m_Stream, m_CurrentRequest);
response.SetStatus(400, "Bad request"); response.SetStatus(400, "Bad request");
@ -99,13 +98,13 @@ bool HttpConnection::ProcessMessage(void)
} }
if (m_CurrentRequest.Complete) { if (m_CurrentRequest.Complete) {
m_RequestQueue.Enqueue(boost::bind(&HttpConnection::ProcessMessageAsync, HttpConnection::Ptr(this), m_CurrentRequest)); m_RequestQueue.Enqueue(boost::bind(&HttpServerConnection::ProcessMessageAsync, HttpServerConnection::Ptr(this), m_CurrentRequest));
m_Seen = Utility::GetTime(); m_Seen = Utility::GetTime();
m_PendingRequests++; m_PendingRequests++;
m_CurrentRequest.~HttpRequest(); m_CurrentRequest.~HttpRequest();
new (&m_CurrentRequest) HttpRequest(m_Context); new (&m_CurrentRequest) HttpRequest(m_Stream);
return true; return true;
} }
@ -113,9 +112,9 @@ bool HttpConnection::ProcessMessage(void)
return res; return res;
} }
void HttpConnection::ProcessMessageAsync(HttpRequest& request) void HttpServerConnection::ProcessMessageAsync(HttpRequest& request)
{ {
Log(LogInformation, "HttpConnection", "Processing Http message"); Log(LogInformation, "HttpServerConnection", "Processing Http message");
String auth_header = request.Headers->Get("authorization"); String auth_header = request.Headers->Get("authorization");
@ -169,7 +168,7 @@ void HttpConnection::ProcessMessageAsync(HttpRequest& request)
m_PendingRequests--; m_PendingRequests--;
} }
void HttpConnection::DataAvailableHandler(void) void HttpServerConnection::DataAvailableHandler(void)
{ {
boost::mutex::scoped_lock lock(m_DataHandlerMutex); boost::mutex::scoped_lock lock(m_DataHandlerMutex);
@ -177,27 +176,27 @@ void HttpConnection::DataAvailableHandler(void)
while (ProcessMessage()) while (ProcessMessage())
; /* empty loop body */ ; /* empty loop body */
} catch (const std::exception& ex) { } catch (const std::exception& ex) {
Log(LogWarning, "HttpConnection") Log(LogWarning, "HttpServerConnection")
<< "Error while reading Http request: " << DiagnosticInformation(ex); << "Error while reading Http request: " << DiagnosticInformation(ex);
Disconnect(); Disconnect();
} }
} }
void HttpConnection::CheckLiveness(void) void HttpServerConnection::CheckLiveness(void)
{ {
if (m_Seen < Utility::GetTime() - 10 && m_PendingRequests == 0) { if (m_Seen < Utility::GetTime() - 10 && m_PendingRequests == 0) {
Log(LogInformation, "HttpConnection") Log(LogInformation, "HttpServerConnection")
<< "No messages for Http connection have been received in the last 10 seconds."; << "No messages for Http connection have been received in the last 10 seconds.";
Disconnect(); Disconnect();
} }
} }
void HttpConnection::TimeoutTimerHandler(void) void HttpServerConnection::TimeoutTimerHandler(void)
{ {
ApiListener::Ptr listener = ApiListener::GetInstance(); ApiListener::Ptr listener = ApiListener::GetInstance();
BOOST_FOREACH(const HttpConnection::Ptr& client, listener->GetHttpClients()) { BOOST_FOREACH(const HttpServerConnection::Ptr& client, listener->GetHttpClients()) {
client->CheckLiveness(); client->CheckLiveness();
} }
} }

View File

@ -17,8 +17,8 @@
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/ ******************************************************************************/
#ifndef HTTPCONNECTION_H #ifndef HTTPSERVERCONNECTION_H
#define HTTPCONNECTION_H #define HTTPSERVERCONNECTION_H
#include "remote/httprequest.hpp" #include "remote/httprequest.hpp"
#include "remote/apiuser.hpp" #include "remote/apiuser.hpp"
@ -34,12 +34,12 @@ namespace icinga
* *
* @ingroup remote * @ingroup remote
*/ */
class I2_REMOTE_API HttpConnection : public Object class I2_REMOTE_API HttpServerConnection : public Object
{ {
public: public:
DECLARE_PTR_TYPEDEFS(HttpConnection); DECLARE_PTR_TYPEDEFS(HttpServerConnection);
HttpConnection(const String& identity, bool authenticated, const TlsStream::Ptr& stream); HttpServerConnection(const String& identity, bool authenticated, const TlsStream::Ptr& stream);
void Start(void); void Start(void);
@ -72,4 +72,4 @@ private:
} }
#endif /* HTTPCONNECTION_H */ #endif /* HTTPSERVERCONNECTION_H */

View File

@ -29,3 +29,9 @@ install(
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/icinga2 LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/icinga2
) )
if(APPLE)
install(
TARGETS execvpe
LIBRARY DESTINATION ${CMAKE_INSTALL_BINDIR}/icinga-studio.app/Contents
)
endif()

View File

@ -28,3 +28,10 @@ install(
RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/icinga2 LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/icinga2
) )
if(APPLE)
install(
TARGETS mmatch
LIBRARY DESTINATION ${CMAKE_INSTALL_BINDIR}/icinga-studio.app/Contents
)
endif()

View File

@ -33,3 +33,9 @@ install(
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/icinga2 LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/icinga2
) )
if(APPLE)
install(
TARGETS socketpair
LIBRARY DESTINATION ${CMAKE_INSTALL_BINDIR}/icinga-studio.app/Contents
)
endif()

View File

@ -60,3 +60,10 @@ INCLUDE_DIRECTORIES(${incDir}/..)
INSTALL(TARGETS yajl INSTALL(TARGETS yajl
RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/icinga2) LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/icinga2)
if(APPLE)
install(
TARGETS yajl
LIBRARY DESTINATION ${CMAKE_INSTALL_BINDIR}/icinga-studio.app/Contents
)
endif()