From c37a23ccba8b620e694b23893e987a381b899377 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Sat, 29 Aug 2015 01:16:16 +0200 Subject: [PATCH] Implement the Icinga Studio application fixes #10042 --- .travis.yml | 2 + CMakeLists.txt | 5 + icinga-studio/CMakeLists.txt | 65 + icinga-studio/IcingaStudio.fbp | 2128 +++++++++++++++++ icinga-studio/MacOSXBundleInfo.plist.in | 38 + icinga-studio/aboutform.cpp | 30 + icinga-studio/aboutform.hpp | 36 + icinga-studio/api.cpp | 165 ++ icinga-studio/api.hpp | 111 + icinga-studio/connectform.cpp | 63 + icinga-studio/connectform.hpp | 39 + icinga-studio/forms.cpp | 242 ++ icinga-studio/forms.h | 105 + icinga-studio/icinga-studio.cpp | 66 + icinga-studio/icinga.icns | Bin 0 -> 224645 bytes icinga-studio/icinga.ico | Bin 0 -> 766 bytes icinga-studio/icinga.rc | 34 + icinga-studio/icinga.xpm | 40 + icinga-studio/mainform.cpp | 258 ++ icinga-studio/mainform.hpp | 53 + lib/base/CMakeLists.txt | 7 + lib/base/exception.hpp | 10 +- lib/base/socketevents.cpp | 10 + lib/base/socketevents.hpp | 3 + lib/base/tlsstream.cpp | 6 +- lib/base/tlsstream.hpp | 2 +- lib/base/tlsutility.cpp | 46 +- lib/base/tlsutility.hpp | 2 +- lib/base/win32.hpp | 4 +- lib/config/CMakeLists.txt | 7 + lib/remote/CMakeLists.txt | 9 +- lib/remote/apilistener.cpp | 8 +- lib/remote/apilistener.hpp | 10 +- lib/remote/httpchunkedencoding.cpp | 58 +- lib/remote/httpclientconnection.cpp | 156 ++ lib/remote/httpclientconnection.hpp | 78 + lib/remote/httprequest.cpp | 111 +- lib/remote/httprequest.hpp | 18 +- lib/remote/httpresponse.cpp | 125 +- lib/remote/httpresponse.hpp | 12 + ...onnection.cpp => httpserverconnection.cpp} | 59 +- ...onnection.hpp => httpserverconnection.hpp} | 12 +- third-party/execvpe/CMakeLists.txt | 6 + third-party/mmatch/CMakeLists.txt | 7 + third-party/socketpair/CMakeLists.txt | 6 + third-party/yajl/src/CMakeLists.txt | 7 + 46 files changed, 4134 insertions(+), 125 deletions(-) create mode 100644 icinga-studio/CMakeLists.txt create mode 100644 icinga-studio/IcingaStudio.fbp create mode 100644 icinga-studio/MacOSXBundleInfo.plist.in create mode 100644 icinga-studio/aboutform.cpp create mode 100644 icinga-studio/aboutform.hpp create mode 100644 icinga-studio/api.cpp create mode 100644 icinga-studio/api.hpp create mode 100644 icinga-studio/connectform.cpp create mode 100644 icinga-studio/connectform.hpp create mode 100644 icinga-studio/forms.cpp create mode 100644 icinga-studio/forms.h create mode 100644 icinga-studio/icinga-studio.cpp create mode 100644 icinga-studio/icinga.icns create mode 100644 icinga-studio/icinga.ico create mode 100644 icinga-studio/icinga.rc create mode 100644 icinga-studio/icinga.xpm create mode 100644 icinga-studio/mainform.cpp create mode 100644 icinga-studio/mainform.hpp create mode 100644 lib/remote/httpclientconnection.cpp create mode 100644 lib/remote/httpclientconnection.hpp rename lib/remote/{httpconnection.cpp => httpserverconnection.cpp} (70%) rename lib/remote/{httpconnection.hpp => httpserverconnection.hpp} (88%) diff --git a/.travis.yml b/.travis.yml index 03ff00591..d054f696a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,8 @@ addons: - libmysqlclient-dev - libedit-dev - libyajl-dev + - libwxbase3.0-dev + - libwxgtk3.0-dev before_script: - mkdir build diff --git a/CMakeLists.txt b/CMakeLists.txt index ca7c2719f..2274a92db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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_NOTIFICATION "Build the notification 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: ") string(REPLACE "Version: " "" ICINGA2_VERSION ${VERSION_LINE}) @@ -244,6 +245,10 @@ add_subdirectory(test) add_subdirectory(agent) add_subdirectory(plugins) +if(ICINGA2_WITH_STUDIO) + add_subdirectory(icinga-studio) +endif() + set(CPACK_PACKAGE_NAME "Icinga2") set(CPACK_PACKAGE_VENDOR "Icinga Development Team") set(CPACK_PACKAGE_VERSION ${ICINGA2_VERSION}) diff --git a/icinga-studio/CMakeLists.txt b/icinga-studio/CMakeLists.txt new file mode 100644 index 000000000..40394a24c --- /dev/null +++ b/icinga-studio/CMakeLists.txt @@ -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} +) + diff --git a/icinga-studio/IcingaStudio.fbp b/icinga-studio/IcingaStudio.fbp new file mode 100644 index 000000000..33038d07a --- /dev/null +++ b/icinga-studio/IcingaStudio.fbp @@ -0,0 +1,2128 @@ + + + + + + C++ + 1 + source_name + 0 + 0 + res + UTF-8 + connect + forms + 1000 + none + 0 + IcingaStudio + + . + + 1 + 1 + 1 + 1 + UI + 1 + 0 + + 0 + wxAUI_MGR_DEFAULT + + wxBOTH + + 1 + 1 + impl_virtual + + + + 0 + wxID_ANY + + 800,569 + MainFormBase + + 800,569 + wxDEFAULT_FRAME_STYLE + + Icinga Studio + + + + wxTAB_TRAVERSAL + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 1 + + + 0 + wxID_ANY + MyMenuBar + + + m_MenuBar + protected + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &File + m_FileMenu + none + + + 0 + 1 + + wxID_EXIT + wxITEM_NORMAL + &Quit + m_QuitMenuItem + none + + + OnQuitClicked + + + + + &Help + m_HelpMenu + none + + + 0 + 1 + + wxID_ABOUT + wxITEM_NORMAL + &About Icinga Studio... + m_AboutMenuItem + none + + + OnAboutClicked + + + + + + + m_DialogSizer + wxVERTICAL + none + + 5 + wxEXPAND + 1 + + + m_ConnectionDetailsSizer + wxHORIZONTAL + none + + 2 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_TypesTree + 1 + + + protected + 1 + + Resizable + 1 + 315,-1 + wxTR_DEFAULT_STYLE|wxTR_HIDE_ROOT + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OnTypeSelected + + + + + + + + 5 + wxEXPAND + 1 + + + m_ObjectDetailsSizer + wxVERTICAL + none + + 2 + wxALL|wxEXPAND + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_ObjectsList + 1 + + + protected + 1 + + Resizable + 1 + + wxLC_REPORT + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OnObjectSelected + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 1 + + 1 + 1 + 1 + 1 + + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + + 1 + + 0 + 0 + wxID_ANY + 1 + + 0 + + + 0 + + 1 + m_PropertyGrid + 1 + + + protected + 1 + + Resizable + 1 + + wxPG_DEFAULT_STYLE + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 1 + + 1 + + 0 + wxID_ANY + + + m_StatusBar + protected + + + wxST_SIZEGRIP + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + wxAUI_MGR_DEFAULT + + wxBOTH + + 1 + 1 + impl_virtual + + + + 0 + wxID_ANY + + + ConnectFormBase + + -1,-1 + wxDEFAULT_DIALOG_STYLE + + Icinga Studio - Connect + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + m_DialogSizer + wxVERTICAL + none + + 5 + wxEXPAND | wxALL + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_ConnectionDetailsPanel + 1 + + + none + 1 + + Resizable + 1 + + + 0 + + + + wxTAB_TRAVERSAL + + + + + + + + + + + + + + + + + + + + + + + + + wxID_ANY + Connection Details + + m_DetailsSizer + wxVERTICAL + none + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Host: + + 0 + + + 0 + + 1 + m_HostLabel + 1 + + + none + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_OK + + 0 + + + + 0 + + 1 + m_HostText + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Port: + + 0 + + + 0 + + 1 + m_PortLabel + 1 + + + none + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_PortText + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + API User: + + 0 + + + 0 + + 1 + m_UserLabel + 1 + + + none + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_UserText + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + API Password: + + 0 + + + 0 + + 1 + m_PasswordLabel + 1 + + + none + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_PasswordText + 1 + + + protected + 1 + + Resizable + 1 + + wxTE_PASSWORD + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + You can find the username and password for the default user in /etc/icinga2/conf.d/api-users.conf. + + 0 + + + 0 + + 1 + m_InfoLabel + 1 + + + none + 1 + + Resizable + 1 + + + + 0 + + + + + 270 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND | wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_ButtonsPanel + 1 + + + none + 1 + + Resizable + 1 + + + 0 + + + + wxTAB_TRAVERSAL + + + + + + + + + + + + + + + + + + + + + + + + + + m_ButtonsSizer + wxHORIZONTAL + none + + 5 + wxEXPAND + 1 + + 0 + 1 + 0 + 0 + 0 + 1 + 0 + 0 + + m_Buttons + none + + + + + + + + + + + + + + + + + 0 + wxAUI_MGR_DEFAULT + + wxBOTH + + 1 + 1 + impl_virtual + + + + 0 + wxID_ANY + + + AboutFormBase + + -1,-1 + wxDEFAULT_DIALOG_STYLE + + About Icinga Studio + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + m_DialogSizer + wxVERTICAL + none + + 5 + wxEXPAND + 1 + + + m_InfoSizer + wxHORIZONTAL + none + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + Load From Embedded File; icinga.xpm + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_ProductIcon + 1 + + + none + 1 + + Resizable + 1 + + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 1 + + + m_AboutInfoSizer + wxVERTICAL + none + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Icinga Studio + + 0 + + + 0 + + 1 + m_ProductNameLabel + 1 + + + none + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Version + + 0 + + + 0 + + 1 + m_VersionLabel + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Copyright (c) 2015 Icinga Development Team + + 0 + + + 0 + + 1 + m_CopyrightLabel + 1 + + + none + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND | wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_ButtonsPanel + 1 + + + none + 1 + + Resizable + 1 + + + 0 + + + + wxTAB_TRAVERSAL + + + + + + + + + + + + + + + + + + + + + + + + + + m_ButtonsSizer + wxVERTICAL + none + + 5 + wxEXPAND + 0 + + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + + m_Buttons + none + + + + + + + + + + + + + + + + + diff --git a/icinga-studio/MacOSXBundleInfo.plist.in b/icinga-studio/MacOSXBundleInfo.plist.in new file mode 100644 index 000000000..681035201 --- /dev/null +++ b/icinga-studio/MacOSXBundleInfo.plist.in @@ -0,0 +1,38 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${MACOSX_BUNDLE_EXECUTABLE_NAME} + CFBundleGetInfoString + ${MACOSX_BUNDLE_INFO_STRING} + CFBundleIconFile + ${MACOSX_BUNDLE_ICON_FILE} + CFBundleIdentifier + ${MACOSX_BUNDLE_GUI_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleLongVersionString + ${MACOSX_BUNDLE_LONG_VERSION_STRING} + CFBundleName + ${MACOSX_BUNDLE_BUNDLE_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + ${MACOSX_BUNDLE_SHORT_VERSION_STRING} + CFBundleSignature + ???? + CFBundleVersion + ${MACOSX_BUNDLE_BUNDLE_VERSION} + CSResourcesFileMapped + + LSRequiresCarbon + + NSHumanReadableCopyright + ${MACOSX_BUNDLE_COPYRIGHT} + NSHighResolutionCapable + + + diff --git a/icinga-studio/aboutform.cpp b/icinga-studio/aboutform.cpp new file mode 100644 index 000000000..d47b22c1b --- /dev/null +++ b/icinga-studio/aboutform.cpp @@ -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); +} diff --git a/icinga-studio/aboutform.hpp b/icinga-studio/aboutform.hpp new file mode 100644 index 000000000..58c6da516 --- /dev/null +++ b/icinga-studio/aboutform.hpp @@ -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 */ \ No newline at end of file diff --git a/icinga-studio/api.cpp b/icinga-studio/api.cpp new file mode 100644 index 000000000..ac9073dd1 --- /dev/null +++ b/icinga-studio/api.cpp @@ -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 + +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 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 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& names, const std::vector& 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 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 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); +} \ No newline at end of file diff --git a/icinga-studio/api.hpp b/icinga-studio/api.hpp new file mode 100644 index 000000000..c3bb0351a --- /dev/null +++ b/icinga-studio/api.hpp @@ -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 + +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 Type; +}; + +class ApiType : public Object +{ +public: + DECLARE_PTR_TYPEDEFS(ApiType); + + String Name; + String PluralName; + String BaseName; + ApiType::Ptr Base; + bool Abstract; + std::map Fields; + std::vector PrototypeKeys; +}; + +struct ApiObjectReference +{ +public: + String Name; + String Type; +}; + +struct ApiObject : public Object +{ +public: + DECLARE_PTR_TYPEDEFS(ApiObject); + + std::map Attrs; + std::vector 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&)> TypesCompletionCallback; + void GetTypes(const TypesCompletionCallback& callback) const; + + typedef boost::function&)> ObjectsCompletionCallback; + void GetObjects(const String& pluralType, const ObjectsCompletionCallback& callback, + const std::vector& names = std::vector(), + const std::vector& attrs = std::vector()) 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 */ \ No newline at end of file diff --git a/icinga-studio/connectform.cpp b/icinga-studio/connectform.cpp new file mode 100644 index 000000000..1a38ee1b3 --- /dev/null +++ b/icinga-studio/connectform.cpp @@ -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 +#include + +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 tokens; + boost::algorithm::split(tokens, authority, boost::is_any_of("@")); + + if (tokens.size() > 1) { + std::vector userinfo; + boost::algorithm::split(userinfo, tokens[0], boost::is_any_of(":")); + + m_UserText->SetValue(userinfo[0]); + m_PasswordText->SetValue(userinfo[1]); + } + + std::vector 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()); +} diff --git a/icinga-studio/connectform.hpp b/icinga-studio/connectform.hpp new file mode 100644 index 000000000..8c0a9a9a5 --- /dev/null +++ b/icinga-studio/connectform.hpp @@ -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 */ \ No newline at end of file diff --git a/icinga-studio/forms.cpp b/icinga-studio/forms.cpp new file mode 100644 index 000000000..ec3b25ff3 --- /dev/null +++ b/icinga-studio/forms.cpp @@ -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() +{ +} diff --git a/icinga-studio/forms.h b/icinga-studio/forms.h new file mode 100644 index 000000000..613b7d469 --- /dev/null +++ b/icinga-studio/forms.h @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +/// 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__ diff --git a/icinga-studio/icinga-studio.cpp b/icinga-studio/icinga-studio.cpp new file mode 100644 index 000000000..ff98cfccd --- /dev/null +++ b/icinga-studio/icinga-studio.cpp @@ -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 +#include +#include + +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); diff --git a/icinga-studio/icinga.icns b/icinga-studio/icinga.icns new file mode 100644 index 0000000000000000000000000000000000000000..67f009e32a7d52e94a60880cc1fa19c3d0a1fafe GIT binary patch literal 224645 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%0A~_%M06GCVFK zg$yzUbHv()9MaR?YS@JFg4aE580nGuC`tZpzb!2!KvhI>{v8<;VC><>5EOE`z8}6W zG%_h|KTq<-+U5#uC#Qvxa*=1B_gMNun+n=K zORvyuwERYFZiiSv3OKF5c3k{zXa_TP!-L^0`=gyh7G;8RIcLEW-D#8BVPeuC?5gDOPta zohtSU1N2=e_Ld!>7-k1Tamsh&%{!)Jt`p5JxR3wdoxWs!_nb zxNnr&g_XnuJsN0LSa-qN@(N=?2(_Hu1=%g3 zZXJUQfK5?Fy_^=r9;+xZqnya2LrHZvSPFgv10q|`FvcfcdW=x@#DvgCynWc;NaWa& zgxecB&L#TJRTrxsSA4LzTU0MvPO9XcKY*ir+Lnv6)NC8nchyLVR4W|ZYQBhsYIgiS z%90gZZ>^EtyiEouTnQ~5O4z)b0`>mRs(6^3^vCRhUt$5-s-ReEm{V@#dF5{?`r;lK z>3j~g)0(#YR#fm)k9vdt64rt$=N@d+vg0hR`{vFBQs8CcLkO>t_%wW~B(T3e^iRWjSN2t zoh8a*e+pm~&td&NurDa8BWLr}HI!cy`6b0-U-j~0<#kI3P3{UWPHhH`m2M5A-ms!- zDz~v}DvmO?2S0PAH(>)6r`GPEvJX)|$_m=mcSCADtv2CCRsH}8;oX>#ZL5=zu zR3Y)zW$F^)b?QOzmv9DE#>h_VV#P3ge;JRXCs4ygCDz1Pvj!@ZGNs`LSbgUWVq0

|z&`|ZUb7l>&hVyJp@&nRuO=^vt8-djIx>soL+w79g3-Xa-V<5D8fCM9T+l5Ri* zz~yz&Qkz7EU_v!xH&VDpeU|O)lX^9la8HjpS`DjY;oMhvOu1-H6D3(F~wj#_F3y`4O45PEEt2 z8tCD`URKkF4=?A}TkXsO={?*l|8p8LW^7DcO9n| zxvsLfidg)I%)q)ze^CU{kE<7{uE_WuG|ld6c02(R!5uQ4rkiF4HMFVnFD6lAQYd5_ znNT(*lNO>4N1N^G7fgAr?AN(eztcP;Qq=#zyfMm6&Jt zOwYpR?sv0#M78+bQ(Zu5#$pDPNolo2 zi1>{s#Xuya<{+rbaM{les&Z;Bu!j36j_xXvb)`PtYIF^ux))nYidf1N;}B^WB?XMH zud!)dlrRn4_Fq*4&pA4X&`5|Dcq!BbT42)WZ%r;43pXf|kBK8x!(d6&zbF?Q!|gl5 zVS^BN1qx^X!h7vN;0~x+r8j7-1Njl*$)2i%P%mNe2n6okIGI?yl~c?Mn_GCRj$}DR z26jYt2j2RAp!VN<0`|zM4?MlDXWKft~KQR1bOrYRG0#iMjee@UcN5-hfV0 zXXI?P4JVLhBqZm2nvGQ=A?xFv5c_UI8Pu#CpivK_Cy?I!ZXSY6zic_tqwQ5lityQX`SW0DE63h#dYK)S8?2O#{_KqBLIqX6 z5m4Yf3&F7&uVWxS%;w{+>bZ`0i28g4@Szk(b`D)9w{tinc2&(CE>s&s<4@OSWNR_k zsua7O1#;sf+1XVh*^A09HeRh7u14Gvf)gh7M_f7tZZ7F7LUJYy_SKhwiNE^Nf^=bm zVNTkOaJAinFR~_HLY(jyYL7vWGuBORIvAJ=Q-D-lcd9|rW=H20>#W9d<wJUu=Jvz^-iiQerx&oi1#n4@_4k-3NU^mcn+OT-C3zljruuwa9M{ zKq%!<0U}koUovG#JF7X%mklHdN5=1%dyl8aC7>=Tr3Q8iQZAgdQ43a5gfu(+`7Xq( z0At@H1By4bU8Tv6F@tEJn1M*J(Ju!26QHK1op&kngj2z2?1>?9+|x3wDsfc}1PQrf zAgyw_O7&j@5P`XdZU(%+w0qg*$rEZvbK4O3<-DA$Pn42&Ocmk>bN_JXi!}{jajB#^ z>wqRTUQsI}5~K<3T++3JT*njI>p02Ic8>K~QRK=bBI@BFy2RBQusjM7`NQ+uy(u-T zXMIYzj=kL!+lw)}TZKK*loV7MJ-td7`0#=l*i(42S{_lRVQUq*9ZuMjPx={ZE@K^c zSYt8(5=&6~dZFl$DQ=r}DnLdsTHQNpr<|>XyS-7~I&eFDmQY3IKy?EJEpM83Bm=ll zr&nQLGCfB6ir5#ILwK)L>Z!ayime>pnd5Rb4^6Ye*=2@jBCe5?NPew;GL&l}(9SsF z+#mN;p5PM3B0=aess_mKySr@T7-$H)4e&pdrQn3}s6f}{}aPeKEBcNgklL3U>x zLDL)RQglYS5g#?3nn(FXM0fNceSyjRtW;X}no@?j580{<*D-xDw$Ej5HMtlrXhIq|1l-H zoJq&0e&jxba<|4GCWtZ=apkhCb>!=`e)7>u4xad4))Nk6mLv8c)PpHnXI|AnyKA@G zdzWN^!cIyD7ALCg9g0mH;;qeE(Lp%*T6%f6_DLvANM6pMlQBAk@u1If)Fc6rK?&32 zWx}ZP7GGCETCT5UA&C>)&{V*wwGb#H^zGt3hyeT48|cFjF))5#ZSA-Dia46DaN0;8 zmU;3?{!h;WHt>?1Yr`+I%Hf3l4@ZCV9HNt|D7nH-*~*0Bi1+6tPTQnrJ!Ou}G;_Ox zEq2E9qJx_rhIX1sA1=?4Rr2F`18rLVJFiDz5W{9#)5E$RvC!1fJ4CR^$3Wa-I$3IJ z1i%?v6dIqHdPi!d_5eYn)9>^dO1zm$pO!I{|=g* zZ*@C>!~Auc*E0c!>2^b$CpYY0!im)%>Ky=R^pAtW&qGO8>k@lQDCa=EG7lhQqFojr zi~a6NzrXfL8d8s0bV(Mst%LH+d(NuiB5YmLkD;t%#IN$G#MR6^L3uKR-|3^(@zw#Q zT&k@a8dV-Xrh4aIrF6glK~Aa1OJzkknNfUR`1!EfS$d&A+PibqnGAlVxNj8U`=J{c zbdemk@AZpvhfhho*d}Fg2NvYg*RMsbw!s@e?H}Ns6*Wf^*0Dz?AA?OJeB?~cn(r*W*Ar(qy^?HMg}vHzK8LisxKf{@kZ#Cc zmfh{LCjq1Hb_2NWj5210^fd71j`gj(SIIu|q{oac2FV^sm#S_{rO-zR`(0Ya7_Q|= zBn)hO)3g4OI8BpO+Ul5ICIX&6g7e~KP{>^>g|H7=H!ZaROz1l7SLRFesua=Hr7J;1 z?Cqv*_Q~k(HiIg2C~EpG6?}2opyw~Y97p5Qe52P&;Ks~SGPaW9g7H!eZ}D*ENg93q@-Ol*4z=46%tofwm|8e(nGn{2>^G8dzp=O1fM13|?Dgb1 zIlma>+cZR%bpO41#is7PNy3}9dC>ZXu(rQEbQv?^JA!=eaXS%lG|TsHm1ZMEBEb9K zzTl|qapDiwK66%O$cKUUXruAA5I%r z{TTdXov>H*xx4aeIB&Oem;N7`6p$C8Y8BLm<@%|F?~o67Kv7X@GlDSzyy&fChbh#J zU5^S=E^X3o)jS$`^zx2+%P`~Rs5zUNN>^GBVpyx0hpnqa$xk%9GR)cTe3tA6KTzrU zRJx@_j4-6T1^iD1cZz^_Bv2wQno`kj(5y@O4z;B`IB4_5oAJ~UkSD=V;%b@SzKQoFEG z+7esk&v%rss=kTPqdbqHgrl9Z>mncB-&2x~b`HDo!u0by+6IMbcz6B+D6PgB z^!IM~{|o}JAoDjgy7OcSPEP0)<(w+(o-rfgKM=~W?_{muQaZrIPnQENxk$XVT`okb zkVCZ%wJVJM7#p>$JpE)zq%++7@AeHqV5ht$(^WyS_v5jDHFQIAUn>udk5?CyAhjJb zfriug6tQhJ!bo^Jj3iO}dPJVko3K&4lmXr@P6ZV>A#6U>5c`F0Uc`w1t)9;wbsTv2 z*KnwIJz@3n%0!+_v=CjdiT~4?y0OXpqb7J_=jHpYW>1BUyI7j!=Z_=^c@B?Ckd3}Z zMfDR?1B2Fm#>;-q?y*r2F`Bmz?r$lSuhP)LO zgnS*va})CQnsP+dn$sKcM=&$FLCn)_fQ!<&~&EyzIm#)I5U0<@N*ux=NQ8t6@}#`oGV z(pGU|!a!6bYW-OshG@%hkjV)i54xl|Lt*j0)LLRr$`<0FxP5rbFz|4InLES)@U5Sx zQwvn4K)44*uL6KJz&vfRE}jtiKzkDErN4N(>59SU2O$|X6aH>DF*|eEfm%Tt?jL!i z&ryL~zUp*Tz3>ijwdIkc2}J5Kx*#hubWrq?&=H(^oOqHcxIHS*j068x4aO6g7E7XY zATa8r2vKQpms8^Ci67x;OCC5^H)W!#{9lNtn1PZ_B|b`Hd`j3ita_m&4?Zzzi-VO(vM2p_p~XZv$fdnK>Kad zy0vH4UjOS+!l2ba*=x8j$whiW5giV{jP_{UB$PH!eHgIV3ewg?sk8cz1oH~T52vzz%0!o#~o4xTl1YPY*=P5_g!~`eaX81~zi&V`+MWhm{%v@P180I=VJqSHi zpJP-u&m-l{HqagDq*y5LZ=lOJES|V}$5$ZWWd?1V!4ar5uAFkfUwrV>vAu4bbRDY& zl2=uUypYn|IEii6;*=wCHwkW`l}$?*6jj~u6v$%GD|S@4j2# zwp_SwbjiUMgS#OwWt_A$5*9DrHrD&Fb8elbkzI-m|ChbQhRZ9?4i2+oX>7WpmS(kb zOBlPj)cxz3MD9TIfQMo+A%v*CTQD&j3C%Y?g@1nO>rFSE5Xj;;O;uka{8L(8_p3wmQh^-~C|4s*d8=qxRB z*={RMW>%*SDlOqpnTp~LQN)?zZ2bd+rUa!TQ8#n-?NRnHhCX4Qfz|T*7LMmxuT`fE z*jW{@A8ut)J0{|72j)%gnsn=j!w^I1esN%a()f6NTb=xs_AkXdG8=zno3#e#jndWa zhF4vm*M%?o225o!`QRBg{7aI;3?B^)h=E0&|gYG`BpWz%^1(uIVG|av9 z;W_M`bfQoB@39GAaq;!3Eo?JRFJsh{h8Ng9ZLPeKAZ%`pxxrIa3}y2D1+=Qw~2FAK8+U`6E!^*xv#rpi&27ALg_Zu^FQ?6c{E>=?J&e z^BXi^_cPSRwf)QLqKytwG*}Y(w8~`RE@=4fybg2L|1(}RaW{8IA?AcRp=v@*1LZ`N zz_}_S4U+uO?KO@@p-gYU1IODU=JLDccc1ow7H_ZL>PRZ5A0Os!WIMLa_JXXP>k>UTMQ6M zB`oLx-me`7I+2+7UzwaVU4LfzSk}p%dB}t;jPW#yNvD;?H4M5Jq>Ai_jTjdfmr!@8 z@o=7(hVe=qkkKHxl90h+>;6%CkGJ_8zsW(AAwAF*sg@q7zl%D6T+!XeKkhzkuvqUx z4eMf|>$Zv5kZ}~y=?_@Cq#JBH8{{-h#fOjr6ByB&1(P$cQn9K`z8|*Ro&G#>#)yy{*3BEV5S=M>U}l7~EXBx4R3&o{!3x79%9hBQMu}Nc^SNROAE(TJWn7cA z?%GR(wWJw4O*)=_CUNX5KQtc1_vF>OS!POh=i>#m><*zOGF(!sZ(HKdh?#s~iEX5T z^Zx_>#;sdZF~0iIcjG22tY4rz(4S_`&mmj7Xj2n3PnF5LcSA^f=~>=|3l|nL5|t$n z(q(wQhNo5YqMTr4?f7E1ltWI!^Df9kmyK4@LQ?Jd6np_0jj@6j+l!9065T3Z{V&1f zBdBRVc@guG!K+on4sOf%Ez02Iu(%jQM5%xMXd)fvf+%t zZdIfvjXJ)zWi={5)K1$q${undsoGKrUrSmw$xl{*%?U4tKJ8R%^r8gxqFD`e_U;XPm$ySG!$_ByIg#-Tce-@D`u?s9;jL#x$)3 zf94e8vmeNYqu%Iy146I7q=f*P!fREy74AGTD(ZNp{o|b;#J|M8cm#&L`1-Ff$rW3 zF;`6$B4Bh;CmT(e!fuxtV44%X4iohrdHR~K%T=~jg=L!l`1GvHGAdT%Cic{3rIyxu z3qo`!tY$qrom+PsToH5&UE8X^ z544o+zp4|wf-Wr-+05LFnfR3lSm4teV2#i=|y;XYFe6)(v)CAuAbiwj=Y8Kz@=$udsD5Mc>c{n{gfP7gx=%V zy!8|FLa!gdkk9h|_`YF;g&g47oVx~Zy&OeY=e`^7yzgdAwb|}Me1TzBN4%o>3tw26 zn-{yXU|J|rV+ZiYOz?v=?Tw%L4FenpTID+3m$z*NpHlJSxK(g2hH5`Ap?qBK=A;^Sn+huOxS~kED^lJOYbZjG-D!gTZ1J zfmzD5KKE?gna(k;u@FBR#*+g=|u-MUw8k#Q&*E77k7DV9!FEsEW{BI%K{=}z+w+ti)F~+vWHWV0N zyKc}9z7Vy%|FN*q;6}z8I}{jS%{92o*y|DsjDzpJn^E(gu?@kN=!=FXInuioEWP7#fp$KgCVo&oi53GD2 zEU3%BR5^~I7;@*L+FKJ$<;fZsuqthNxg`c4??mn|cUFLx@x_nMVQHC9F6*W;At6C> z0<=0iQwb{Q364?$nzD{H*g~$FvW%I*XDft=&y z=~?q&1AJ%o^TGEn6>SpUJmMN8tgMFiypERu1>G^%3bQos=>t&g;MOx@27Y~j%>i;f zJ(c5V6p`TVBH|z~bUbgeLea<$BUOu)c@bh>W$cq$YPj$fSv69a&equYb6`g>i%2Yb zZ@bPNC552qCgt)*13QF3F5L=Gw4T989>6ZJcY=pAO8Rc*>pwfbo81N5=hEp-!fQuN zf`nxrCZQ=VX@pcQ2R|I2Zi=K%TbcuvP_J7^_*xx5eN+7pGp%+X;7X~HQlpWVdDO*! zJbt`KpIP+b_=VE@Doq1qb7H;LqVcMCUh{5PdU#MKzS3MTbgOTJm!;u_M~*0molmVd z7?~-uu*s1&UrjDrc+IzC@gX$TVlP&ZaKy8KR7?#Y@)QJSk(m{v^r(mg0AuJK#Va>; z_fH*^Wm0*n1dfQ&D;VA)RDmi>^)8z^FHWz-9M_iqC{9=Nq_gS{Kl+-wj?&63h_6OrM zta;yP{M+<~c^D}zDR0#aCxVFZ^z4)I<&6oF|va+&k z7M|Qfv+l;3tTNlEtzSzQU)utpGCiI?mb;a#ymhF$)oshT7C@3p)#A7*ko>@w*?}Ku zv%{sCibNG%lfIU?O5U8Dl_&)Mk#?lfV1xY%+j0KA_YJVZ9iW!-_NSwbb{e};jFJ?y z4!MR2KOmSK7tSDY+9rGLXm5VR0?(`7)%7jmK9h+H+g{5&1%t9n zRL+{xP}bP7kCuczb?DQ7_l~(>P2cl)_<=#Fmx}!TjB6VwS|u-}hP=aTWq?7T;`k2i z!qk&D<}W)wJUMd(1^Z{FEDL*|$z+DSw|)Eeb*yE|1~YZzGq#x)^qUJV-;7(}iGxgm z7U$k|vBvM69?`)kYl zY5tCUlDua&Gd^m)VwWPuchkCZxxwToaozL!r`Lu^}pjb%=J-6%}wJq8Ku+G zu@#w5e6S3;$;n16i=qK!$MXqI(%$ZU7G(S)LOM-MHp}r!B6wK}zNKj8hUrO8IGL(R z<_K4{smvh=J|+;Y;|E*wW}cIHDi%khP+ZV)1@0*U*L2_)m?*Cof0!DeotIEV_WruV z2It{3*k^Bl+T6(P&d)110q&Vul!dKIm-$GI>KJQtu(j@?PkV|ORvC~Mxvyv1xB8=( z+@9n_mZ?}W9L_?api$J9#m#&aV>NGp~=|pSN zeGcMdhu>O0)!%LO#xUO62Kh>BP1~-MHP2=oa+Lx|m*op~BQ?Bw$})ay+uWXkU`E6_ zns2H#C@DcinZuJSXljL`j?F6o0Gi6M$q(Nbtlw`Y-f#dt^ujARoRX=$P3A%RXuHX%u}tyZL+O_zUU-zocU1(l6HP z_N&`6;s}Px(3r#l@^vIjK=U;(%`V*}d&8qX6h-n^?Uk=7Xg{n7 zruVwXaw`^${|=eUX}<6~Z>aE~-;K@60W8|Sto^2|4ZdMNIK2Fq`wR?p1KK#)AVb}; z13{2NwOw}I3a7Yn%QE6?%xN4^1s1V%ag1Ql)3@m1V0#yAn+3($Nj3LR&*6S-gC0N{ z8*A|8ss)}a5KyJLk)ux>mTe$Ie%nY+Nk#+w$8z;qfw9V-X$!j}sbax3@&URhc|<0F;V42HvfW&@eO|j0L}C>UA!y+-tNg8F&Asm%a+@ z)rLts_WbkBp*pz_A((G_8hoCy)mC^zIMfM2%I4_Fhgez&PqT#W?d`?kjvtprCV_jt z;XhRrO_Q*a1@T_4j1ks0YUfA>ArQ%J3!PaFMmYFuJ`X$~O zMhdmD#`b(O5aPO9*5MtU;HU1hhQ>n+&z2TD5eM9*+Ha_XN=7mtcEoQm1Z4?-afZZc zYLv4@?U8hFTQYmLG}sz>riX*_@_co`z+C_hQd`aGL(GyE*L1*CR0#6LOIq!?boCgh zH6IRT@UOq=Me^U6V=r`l(=7a^`vyU77K#df{K@8@g9Uf|Y_jvb=w}x`NtkThzik=u zII=_u%!p83@mJVQP(mygxM2aOY;D13rU)w6n9Iz_t#ndg7Ph09QG!a{atvv>-_N$V z!!-q}06zS0d=}No*7D6BwTkjV;Yqadv*9bqod;EI?SSJ@*lD9n#fE5Zk zlCU!mxsrr838`Vi&Lg^-1;!F5CMa>LSNG4*A&CgjJOaz9q($A(G=~TerL{m}e%gT_ zjEWw?eus4biBEuTfEd0Ll{xJaDttsYeaY6;)@G;=yOMIKu`Zs2W9K(li%+?S#xT-!uq)z|N8f!-%b9~EXbZ~x)#qYg&c=d0>NrO zei4Vnjh(U6kFkaUU7~nN@Qv~iayzNmjl0ohp(l0>@%tP$iB)(@vcc8DI}w`m`GyD3 zR34V(2gx8b+O@s_yS*O*RDKY@@k^Ei5sE#!7e7oPXE+g~FnRVw5}YmFupWBjxO9qk zG^6{>EUd1E(o$5lBq_miE1Ce#$1?ogATl6gw;8|$b%%q%dwd=^6gpCYTA;`b&tZq$ z@-#+%Y(%B1EdFT#Jc(n@=8_C*G*-KM6*_-Oe#k<;4TuE*!8&}?`6av^Qz}BBWmDAp!VD>EECEW$7?eKoPgV4!=j8dP71wH{$U-Yqo#VY zLsjFkf-2W0x4askmvAAb~UYl}a_cEE!*LZTB7(wI^ne04o zx+0dvCKnflX36tv#U1%-fme7a(FMy>k<5aub%N8dN~=-o^XuZ+#kI%M;Ju&8ndw*plBE-elGW3TG(QaLs3 zo8eOdhttSnwow46E%4;##a57Czpw zq>OdKN#8)zKrHdBD8|7Zq=_~|CMN^)O4>DEPViGClg20vi91ZG7T_gd!fC!CMe4ES z9GtXqjZN!Ay@c%FuHF@aDu3qL7;Mo05}O2j;hF%Kx4+tYz-oFF$E;IT(V25$*KT-* z%49zSUhs=B`f$K~7F^|#Z_-k^#@`dg|9`FJzitA%1r|9a{%MQ^dHkft|=cKrde>JH#ASc47uCrh${XZO za`wy~(MtAkGE#_c{an~Aia~c+uEFz`V5_aD0O!oU2bacQ#}30j1{Wur4PySIS{IlkiNVnB)QrVir&La5KOyi{A@lTCnj zYRK9JCsn@mvod1o^}{8l$EvJ6CCw$NX%F04`cxF)^_g!+SB=Mk;APqefq|}}!gqo1 zXv1{iUsvZn1Mr$uvHF;7qiWGXYgcRsCKRQ3-nhldZ(^kZfoTJdi$W0_WCN^3ew5Rx;ILqgH(sX_-GF!uw_6*I8YB`PuoHqGw85Y{Gg#_o8iHk(xL)=B>O0Q{IKiEK>M`a5{A%CP4HC)`AC;}E#hc8j>MZ% zv$Ne1juv%%#-I`-qLN-_Nq$5_KH!Z~_|`bnvE*5w-prWW_7zIu+x8jcHw}m}<1IdX z$Kc?sMTYRqE|7f5opH$InD7!XTyNQCV6UUhWifYI9R(Kj$37`kvhwS^e93tg5$7<3 zXe*XkcdKIVJ}_=T+yL4JKadl(baDU~2gi#<$Z=}1;snC>97bfdXH_qwqiH~pU{es) z8bW)zF^6X_PuwdJ4s_t<(NB-C)XgzBwUjVmjNEKi(R^9tn^iXmI@qWIg?BcbrfUXk z{1L>>tHM_ssD)o#4S~~JpOzY3`t$b<&^R;m5bK2(1)B$isX_;$7k%-QpA$1rIsM+B zaT}1>{I3KK9O(DnB>bE}!8T{gp!yrIqx&qlK0>8fky8)Y&G&}g2-qO{C7&5T(fXvc4>f^R zMFb{K{J7B#K7Ol@gU#xXwe^^siE*$MIYPYJ!Y<(kT9~K42li^+ffS z?p)%pTo87d(RpoCBHP_kt}Pv1`cPVw!2GgSk$7HD23Wj1m9FEhf+QEJOmE<{-2B_< zB8R*QaJ?(xGbVlr4?|u0k~q*~;wHQThj0^rZZHrczRe7{g#95?rX!f%W)y}K6oJ6c z_LVei&SX^Q`#!n6U2ut`IU|md&|`wLuv#$YVeU2sj8lFrVCmrEUSBKDz!n7Xp;mAv zCCXI5w(s6g!!O zt~rPOE|Yi(`&1Fl*X1F#SXSe~#sPQN!vH9DJVaDFb|P>k#Hyd8#IdE4;uNwmB zJ;W2*IG)WRA7vsK?M#C$2NFMD>D+7EFqU?r3*iD4rxMtAp1bl(A}gbgKnhP(VKhvf zW)FM<6_s@^I9wQ@v;5%iV38X?yHd>z++Zrw?(Q9%1n1bn-!9*c$dW+S(Sac?Qui|I zMI*P>$uvDoXXOTHOM|cdRfB-|0t$$Ar|h4L>y?Y_?%SS*Qp$T7e~683EOA5=3{wdz z?)lesc~!XZ<0GyCLMIQC*ng0_rE!iIr(DMNrSts(&x}2!Xk48i=k7xEcv2Lhp{bBQ z;%K26QH^|tIvcF!6Iuw=D(e;WSQV&y=5m+UOS75Sg+YOkd>D;wG(y3+yg)_Gk-vLH&SWgh+yV4m!AC zcV?bET#&WLpqBC_N+|yP{gp3x?2mJ6-9+g?31mnI<(hW#ed+rS9m-JmfE7fMG{!bkv^dQ`T0RYwz)BvV zlOHbJa6}8o2<5@OI`(EC-8O3aX5qI-STUNXqgSt9wX4@B>xWGOYs>u+45St*&Y_eR zLm&kw+F080X1*GdaP6vJKP-!grTs-`Xb|!4Wd@K`!NTT;LoZ~}dv#iiBd&>wxR&8< z8to?#&}*qGP19g3V(QFV2jmX=|C+Hl1yMzS@FN!pv?J58+JJE3rx1atGiBNep1MG; zOV51-jx%gT7=GhR_HvK)ffBa-;gF4MTH|B3yos?TTYVTH3x*TJCa+lN&NUk&nVl&p z0kTRkf-KHrzF*@oIzJF$T%vE%Xm3?hdx_aRyh3efSEOqzPaz0g7iW`+_=kL}#d^a_>rjs&+0-Q0CcxECk3V_S}ZSm*?eAinz<=su%*RSs^Fl|s+03Rd>-DD5WRUe2b}EE8Id) zZ40X{Miv8wHk5)KM%DLB@sVbO9s3iQv)~CBBZFm}d(bi`B zMq($1>8S0G)6uEH)oS5&npX2iD_*s@4gn;RtsQ zZh*Xq0f})Y@iMl+y`UuX!es&GB7Z*|r%?Z8IBEb@+V~|R6T2o3SDSVHV_gvd?zAo=Es~)e3a-k{%-OvoqH~;l(Ql`C}i<3WMEy0nNbMo(4E8tf{~(971BK z3AHgu>bu4IRw)*vfr1?c3VOEY)%`g%i?yE4$}zB51Qsj41m(*qMYXt6V0SaAvId1H z!G>T@jKi=r2m>R2!>lKeGclBKVdW8WfGmBAW66i9JVKJnrH<`J>OwsPWnCy4e|J#3 z9Xu5`6?58!57a(|ZUwG~m)I9G5-e*%l)Y)l4-c({O|6a#G@#=)qViCY??u7vHQKFF zfliUN?V9FM_v$h9SyLXI2Tep_N^7n=L&%^<-KZ**l&B9^fb$oRc=-x~XU>JjG9~q={4xuz}DBpJTmV&bkX}Q5jjo*w6+Q1 zOW>!YE4&#VF>iG#k2-p0+I*s5OmhNNEYn<0K8i2hAOc`)v zuycG-tIYS80aCim@glny*sg)_VC6X~TjkUu$}j6RH9=POZzd2o+eulPJhfSzl8XX!$!w1n}riFJPB)Y~1=-9U!BQb#!gWwIQ z&;)+kz>pk9aNJv;c7)8Wsr0yDZ|e*_hOHf8;ym1F5=1+E-uN)#^@}}@IpG&UXUu&q z6{|%c45Cz>1KlfB+rI&Xs^3G1Dl7v+JF{lg6P67gJlN9mg1W{~Hs6SsHJ-N#iGIR9Ku{n^!r?Hd6-! z&mb|KO<@BS8Cn~I@2;bEhVCaH69nei_QMQxK6dhl)-cp4)*Wn5+zFi5%&V6nO&e^% zH!(`E>p|%12@nhSQA-1_yHMp-=dmwP>(~uVXNlbP)MC$6Rz;spL4 zpmO^K%dbTRkw?e3L6nY)d>|8{4#K;pwr>Jd-4B;O0`LCO`tJ-9bsI!vcUDVYo6#qIHw)6bKNcP5KJzp=9D>=d z%;hI+zgHsrIxKT?WLHa6UqXY~b4p|@J9?WF z^xt)WuBZVobs*LKcDuoW+~B@5 zcH()wwF%V}%Z`3;0FLA$ApC7lLrt;!*APrkeR08Ei0_rmh-;XDTs#U>5EV!@3qQ50R z<+*Vfn`PGkY=b)=W^Tzs7d`kWz{}r!USFK37(DE#fu4|s1TC~NK{32h(FEmPgi}|{ z-n*$g)p#(WQD(~)Iq}lc(&*-~B;28d5P^@wC9K9&@9;@4<7c`3H5F|avs;Lj6%%fF zM>6WGTzSx-k-`M&08R1tbu;KESP9Yc%EDKYW{SA}89gq2zTdl<^Rv+{OrBdkxzK*) zK)goN27Z)AP$TYmcE+ZH-39o9umUpch?ONU=w;apz?C9e_uZSF9iX*Qyu4?`)z!6Q z06{>a7z~TTgOQ>-Z$by)K%w5t*i0w9EP*9+KAlBns5EsBVpvL9Q%tZX)=8Wv4Poyl zqX_x97lKJ{%0FE)aYr8i9mZX7$h(WBTGV!Zpc$G5pub1~AzwM!&sP~DTD~DteIr3V zOV#YXPkmi}J7Hq&R|jnFKxpqz9t~aw2IeAsXWM(Bp)G@Wt32TWDC2ZTGn9T=5w(); zxxpKAz>{WUHES}|KZus=D;GZ4YPs>IrECh~w3cMb@Dlo3JQREQis-w*;_yH1q1)JS zqa-vB1SQPqG0OWP=F7>7*e0_fIF#l_u}+LRgUXNi6(#iw0(9HSAn57xV|!|K$!{=9 ze&f}KH|Yc;`?ziAI|j+mVNkcY)h9Q+aP8K42t0%Tyi@s^8}T;kCs>8O=;nMe;OXKe z-$Vi)pZUaFS!6z7V;rH5NL7beV(ICNrJ>Lhao|FJgc$cod-2r#y(zW03j; zfN>ckp#9@FR-Uj~1qIpX4T69a)FlycN+bwK!0+JpSTX`53lOPB0( zKa@1Nm}UEVqjpWHZJKBKuF_Z%0$JRfVyyCTyrez+;1vINYHO)#K}^Hd%Vt4zpm<$6 zRnsO~LF`~gbjeig7!zW3=({h^heKn!08?EM#?;03HJ4!1)qZPcz#f^t43=S%)vv1! zjQA%ouD3pIZZ!SY58R!P!~-B!a3g6cB#)~`_<<}3(qd0)}Q1;l0ZMdOiUkBfisw zr_Fu?TlD{)Nj3Xq|8fIJU^>eFH?OCCX>b5QBWTm`T!Rl!p=&>vWwiV@F-_Y9?TlCA zf0+1T&1QrtpKA!*J8?#@z{&dmr+I8nlK#Iv%{;T7n>)`KCB?6N#U^Wtn(X<|I#Fzn zB!@l1(;We@WyMyf-x4E4%Ns-JyK^b-k!J`rZEgwG^|F`}ZH9w*3WU$xNae@(?+x9iQ-Rxi9Hwe9UECfV)B|WEb{<2%| zjfimC_K$Rv0?n$u<`x#EC|!XBGU%=CTDot5=75k3yo0uq5n+`$ydyTA+oWVbI6ULS!DFlv8n}@6O-wTX>b8A_WPyF;6 zFB56O5gnkXIs$w|KyE5tDt5~226|!;+YX_bf-yAH#&Q+$D2xcQE7~fW|AUs@aAt4# zr zcl*~+sBww2wl{t(rK&C~J37A!h)6P3P6wVvk6b@)Am;-2zwsek{9R>Eoj65TlNl(O zUm(5`XvwD_!4IwbIYam9%ccDvi}8DdzR#+S&AZ+UTpD%Zpg0xU!15yk z!qbbgPuH}?rI=GUo(&EQRIkBHn(c<^XUBWEbxFW~S$IU>a1IVT^oRY@L=NXhsI&!9 zB$7r7>EXF>p>gRSTQ*!IQuyrAXmins4Pm3m6Z;5~E=bMQA~v;jH7aF2047nj9V>d; z9o^Fxup7Mb38j;{Ib_>jC+(o@GF5~GPiWU$hTgP~WozZmKU343v_w+=Cs3BtFLO=) ztRDGh;R}&`E40i4K%Ii6d)5|&?T2-{YeKt$rDny6GA)h18t=v#pJm9%A>6Gj%Qa|9 zLJXRpZt0oZZ+6mjt2ZZiX8?#a#dFt4=2Eh=i#tq9!4P4WXJUTBevo&V<4G(Z}~M%5Lt(bL&?8zNOcC&CXe z*lpzd@IMoE|K~bbAxjnH++?6k;fsRvbgL)>zjXh-Hm;-5Pb-O5p&Rq;h#yBdWj@J4 zvCdh{=#~$bgsH3V2I0JU^WSDhQ69Jc zU()`NI4s-x=bJUt{m^YRbqE-jJgsIp#ge$NRnXS8uOOr(3%fLP%1_u2GcTME&+41} z&1-1lJ$jX8_Oh{%r66=drVaXX9K{}x7--LlX`ftP9jR&2IN=6WTwqy?Rcl+`NR^;K zO1AKt_=m-ZYyTdGDZu+Tc-Oh0^G4IO#n3tnd_cY9jqubsU(4$xY{FSyA8j${xvD@> z0qx4@#K9bxG&vqDEgPrp%vFNK0J+3`&eGSv$)?X?Js%1GbNf8 z0egt}^d%9Wt?@5*?D_W2;Fi?mL18y#jjLG>ydTd2H^4#3Mj{TGQ?jvtWfHToYudG5 z32|{9XW$;sLSTX>mKQNpj;{i+4p7%>EJUWSa~kgcG7e6s7C2}s`k9zGsCp=OcImem zMtwm`9COqnMYafT8QeW*+*5W}&+L&ln2MxaFx9{=Aih2=E{#%SeY@0g<*Y|MYsLdw z#1`cO&q5DUTYCrXvy2j{wOS=$k~Yh_H*3EezISRj{+$+S%l#5Ri#d$65)H7_Uj07O zr43qC9v|j+YT_iFtg%95lm|8+7?0Zsy=yM>h651V>M5T1{DFm&*GUsI=J}lm@tgNE z0}AVt%9;tnFAkYJ`2AO!g-`0Aw=98~EZmkQJ~w6Xb?G!u`BLY&eFK5R=eGP+KgZE0 zehcdhF+BoH3X<{4%@-hdz6{(sh(8@61H20)fd&n#kzS)c?KlFR6y#H3?tUSd=}yEy zeV#3^Gov<6N)yC@>HOfqT8pLn_6%ZWsd*l`wtQ(hh!P?H=7hIl0p%12LJ@})t|!54 zQ$)kfijsTK@jiBtG~!{2O6@?VvUsI>W>KaL_orl(Sf)A3p>9z|s=Ybq7jObg2Jf?4 zyXJaUt)X&grXsoIH%&oVbpa{5qvUKy_Wr#FT^7(2279!~Z}Aj^kXQ*UuLIJi^F_QHNBo1_zdJ|E(#cPH_~g+2UG90BSSO*aETKkfVMn&h0D8_+X=D}Rqgp|+09t&Dk` z5q*=F4&qSnL!#p=7@Q+5sKe#tM?bf|un=5GOl&aS)Z&t*=VQuyrxwn^ zF4?_4!{T{VLfyP$lG#`xPUS%YPVF<%8PAH-6s-rTE;dv$I~+I=!kv&ZFr{6QsS_qw z+4P*3RY;*UJ~jpBcjF{7T=bbH>`6nkXRRI)KCB8Wa~xgHapV_*vsw;ekQ?eIX>e%R zEwM)bQZ}$T=Ie?=$1Qu&ys{5187b?=HzbS zUqFwV!%34&mYj*6Oi_CJX8iq_w2O6m`>&FJVp9f)6VEmpHo2Tu;ZzhFf+m zS(3BQD}`3#9v1WAFDcO@Yp1O%h^PpK2<+{qdvZ7M+}3SBST_)gefR$SN$Z#gV?OL= zTDxTA7#(Gv-m*?a`hn|p+Hu3W>Es`{7O8NB-a*u_J~+CAEa$!Y^D}i>2i(Ln*dj2$ zUsfQ&Nk@{^euFBVs{NYzc?Icf_ejb%%dn5f?Jn7ZyEtr?Ym@{9RL#Vq+_bwA9Yog60qXP#P4D zU;Bqd=nec>Kk><$#07OcwrqGvbNzX;5^zpsmnDnSzSJ)bu4E`PteL9-#puKF2X#T}82ZXE5{*ykDU zV}I4)AT8TK0BimaL9KBsIE1kFoMpBK0z^WqM}xi5rZ1>kmFVJc8o2*AU$E(Byk*U~ zARl^C7)C84GR=3$ zEqtl2Dy!>~_&X0Oqfb^SV>Ox_23{oJEmVo5Vsyq%#rso>#ia)mB%`4}_^=X%syX}+ z#@snfuJxn)s{Kx?>Ne||ZMB%>Xxp4$sP0uD;ZRxK#ffcQe`C1int|3I^kV#XrF(7I zB=O_f*ahpR|EDf91Zyhpnz^CpSI_n2hV~Dp{T1r^E_Q``fBh#W*vfpZ)u+jHwK;S7 z@UMbz-{m)HxFe^BYCG8LeJEsm7sz@EM?MIn;T9$fJ|?)zyjVma6Sc(*#DS)q6m#g@5gQTt1AGZ*lxuf%Q9IY}R|ECUWgEiK?yYj! zrYnQ~9gqy6GPC~&wgz{F3fH0Zqvun4op;#}Hm1yH$DZLq&fVi+U3l1+ueYRzH4J82-@gQsa43RY!tN=yWdac0%R;^Ty4A(@!7hjXR`HWMZ{I8-!}Jj#cur)VOZQCNzDl-yGto>DK2Ma|jd$d2D8Q z5Z4WR8j0jS{=DqO>zHJsduUsU`2NBFi@i6GYVzFLhf%Rw2W+*~A}Cg?v{u225F$gg z*3ybVm0AZz2Mov%AV`>!NIggEL7>$N0)c29C=Q??1cVSgDu^+OiV`7^Pyz`ULWm*r z^z8?1D>&gQPJ>v; zjYUV><_zAD{29s_eB&c=$QTKN(2ROT^Z_%>brOARX%^mjm!0oP)q2- zMjE;HyA0F2iO}E9U5sj2_S5^3+8Eq})+l2{PI7aBuo&F~$Vp3Ox`bmQ`S9%u`##U%{!t<-IQt5dKp#r~`vVTdKF9}b04c~-} z9+*`!al^bL#y>ie*gp2h#|hT zN5Cj94&D2j{YlTDeD-DaEOVy~+Ar1z^3IE_k%m5OsDAVw%x@&D*&Om$U=5pwVtC!i zh}VCA9I48`YYZVru+8zrHYfLE!o$Yuhy4znS5sWCzQ&hgCnrBMGgmt^1HwzL&hBNd zk}Fz3baorHZrYd6Zu%2$jX!Ub2w3!(xJ#e>30lS%Z`U=zomnC4r~GN)Uhm|hnYS8V zv97Wz4~Tv`;O`^KFu1SH+L_Sc$ns2i-1{-MzqS9>rmmbh=(X4BAdE1K1Zj0gixI4b zHeWaVe7d{St{1}V@#PWFC1cCujK4=NViw0HdZPK105)oxciy}+=}Yac1uFN={CHdh1__fLWr9Z497pR zqWAgmAG)%TbHQ@|BUOTz+{;ST(4*RFl)d@pN>dF>xSPRpe(CgImuQ;8x(SqcjN7Nb z4@5#LK#GN{7n?TYKb*tUEzn%K{Uo_|+4D}`?@yoq6KWZMD0`#5$k;mf)J$zI-`SiK zun-qYj@9qHa?J6=AB0z8XYYFh3Hl1sCWjAx9%-0(sKcjZ9!-4>1%;4FS$%we%F=WF zDYF8s4VwGnr}M0&n)u)QyXGWREgarZZEyIPB)>%0$zbNmYSOE|R>^u*goo$Ncyz`H z^t#)bPnadVU1M$#o*}Efgb=SIU{LWC-!!el?b4&YV9PzMUTHo=`(HQyk+U~Tw*J9? z3Kzh$~p6 z8%ITnp0~&uA)di=Lf*h}&lrF?qJ(y)`;2T%h4Y1q`^OZw|FwJ7g-nZ_Pb$PZYh=nV z)?1kQ1z`5ZDmN!A-5OkG9@krTJ`{5sH29p=?;3DqeGqffZYZJqm|JI>v4N@mD9yJh zx$3}!MP+GMGiReecXrN=fADRTynlZfiA_0uK0^Y~T6275EUAOvvD;jA77p~IcOO#D zKl*H8VtnO^-S>~~vENkCqXk{T}zm-;R*>dbzZEqRE7;m`7`; z{V{uEwD6DcQ0gs7K7zjv{)xYggNeuQ$}ceGYZ&wAFe+3tkNhH^6??teWwh!FDo;V> z=I6Im_ECA8rKcSC4NZ7x$Va3hPJrib0Sau8R(VHvZ<%M|1SDYah{Vs-H`@k_a+BS2`$;WC#9KhDS$6z!ZyQ^_&%+TD451~CtIo4&~Q3tXl znjHRiO=C=jlJ@LeNT1W;xy=^cF{TGQgPB))=lo?=$1GXqQKSj-lDsFM{6&Y1`%mTM zX$qSo6w!xhy9L`;2;Dtb8zf=o&Xu#!qSYrz4{pYxoi_O^S57GfpItmY5pzyx?*++kul|oA>fZol2JuH}fXx;m-%zmYBsDSb}4!ZCk zI7pX@hpg!T#6klQsylOP;+_bQ20fKaQtX%-2?p%@xSt&*a&X`P3@ysP1POg=_Ps+N?_aqtfI zN=PRlf~&Fe@i&1-W(3lHWvsR3guA(M8k~!Vyv#y$nD;Tr)zbHrex7I|XJ00tNI5eF z9~CBgaKMH&PONQR+(n>}cP0s>(QhQ6GFej(j_6ZED-KY&s8r)=u8&kV_%F#-R>H5r zh+MFGoy{AMgIKP+kT&dzZ-2Vg;@$sO>;zTew7k{Wh)6g5p0(oskR;=Z-dSPya!v|w zOlUrZTJbZY$_1SJ;>ARM^{Yj@})*3 z(r>*Qxr@<)s#ECI1&UX~Z@3k`nzmouZDzjd(Bt<{PNkogzfcAM0g_5QynkL@!0hzN zJ>J)A>L#23;3nXemE@W9GKN{)e;Gu5dkjox#w`suj2v?RuIegN>1Nbkt!O+167~S} zoA!3=haZ(=_M9P`@b%1Jpa3du$IY7O11Ld8+d>dYrve2cjYE`ntbo`z{~2Wdro&bq ziYkDz_wK~+5w(8eu49gq_vA1~zG})1yWKcyy}`rV{vpL&^TXXmwa3F=0?F?acPLmo z-o|tsqr|674y>5d{BRCrE&j2+e(}PbYRUPJLqlYU7cut=z-*A^xPFChrEYE7pX2os^rWJy2lR*A03-ymG=0OZKb+JcSd{1pWZe#c-zfW5f~4|&T)uhmKE0%5UBRE?3S@T%zB|fSrz^jdnN>TgW^cK2yjT7G zoXrjG9@1OpP!9Qeg`1?-uwlv0Y=^A}EC$RhSyK+Tl|apYWU;gNPIj}hxvl zbE<;eqQRbm&ABX@+kK7G!Izn-gcVIfc_iIX?Xx159bCap79zFL@@hj=s3{~{O>hy+G=DqGJJaGJR0f5>==mfv4a1;-R%EW`Iq58M(zJ% zjg{34;)(D4eP+*CI0OD__P5`BwcX0f8h&eSHT@m(5$QEr_Sw-sTB^@oxM#yYWDC zFGqe&`gZ##BUV>x^80SdCR}2~5?7~nPHUEW&sef#iD%-0gX_0{ z_4Vt24uA4m8kv+7zuwuIKp;2~T%2%;Sm!U$XteX1wa#nTu7)dC<5T03_NJ_k!!H~E zl0U!as|fu5#HjeBC|ukUexFHE2mk&_aroDd1rO+q{EhP$ zPHUY1`fm8Ap2(-`0}`Vm;3p&B@BM}6*p(ALd;N1fosl2@#?6dB>DZ_6sNOR?o&Q=k z?-^VF?YR5TCc`O{hPu0R>|4(K9-DTc;J2@yto?Y-Pub`-ZW)sVEKP|hG$Y_BdL(nr zM-SF*miw%eIzJb>7Rd`)Lxc;3uw?|oBABdM&DQEk_|#e1NsVdMj6h>v_Sc`@mc*(g5Rs2nR~OGFZ1no zIGN=jiBweHl#>lDv>LeeqdWs!^w`G~&oX+hc}fgkd!(e#yogF@ol*dvCfT?~)A| zs#=jQ1eu9)V}%1r^T+Xswd&#Spx*1Iv`GnKc3LL#KN}Op?E6#iB`-?4DNt>_)&JwZ z^n;z-$#&+aCD_TU3|yPMtcTIGeLfbRrspCez#hE2KYrp{>p2LSk~aq=IQyn zUOfs(yXfd$3n;R#ptAqgsv2CMA?(osW`S1Z5Lg&&I)S|${nS|O;hP?0!@&m1#g%BU z^aT8m3mF|X)HfiWyK^R~5!XPV-=GoOb~&s~{+4aFJCsW{rIswZ5#kVXuZk!uunMB9 zQ1vV4ytm6~&zu(lJ1p=2B;2R_g`*L_TipL`AcKN;vPl*npR~?vc;&KC-8x%&gksLR zi9@?G^evx=Y^~;*h5a6~j1^6JckzUkreL>H@na3E#_$HTZ<5Y+AJv6dm@X^Ue(5~d z8ExX#m<_4=-e@yuWyn(C&*7~R>6 z$^fpCOv}l8JYkx4`S)>zzE0B_Rp10Q-SHWtc9V_|W0W+URd8_2R&)hUFnw1!E)+&@ z-wO=Tk|sA!8X=KX7HT`}$31pdD8ku8+RcS={$^RF(d=vZRBkMGFZ$=33!(|19MG01 zlvUFAQr~}!oo=Z1_(>4fgtFobzMJ{xg`m{$LpF+b(}5!vpBv%x6!NCkmK$Y3BwyD$ zNAr(qFN!{^?oMB^0dF#|#uIPM!&Xg*G#s|}e7h%(!XoIl-U@Y4>dLq~V*@f~Px_s^ zp=#ztWaEwkHh?Yt$<}$|=e5Tm3VYk-nWEhko+jX60|=4K_lu6!5oDpb@@Kcv&T2sy zSzm`XGuW!;j*O~55W7?q`(5-K4;LKNW9?0K=3;OK-&EkzjBSz&{m9+nq6EHN7*SYp zlM!O!2{K)fSbUbvsgEZ_C{~$jWbhers?=h55_`kB)CJfzeQox=IFh0$BAjfLgymDt zGK^9jmA-o~v!hOrZ#WZJfnMvVoOLA3uyOD}huT2>A>8KbIPvfxQ;a4rwUFP?wt<=BrF_qtun(=Lf7R3O zJ*6kROPBSTS)NM{j!VvfsS^wEka?Claxc#JH99wH_PzY)2>IU#TdHHtt_WjZWfSU_Xw_u1xR1LhGzIQD>$CnFrXAOMPO& z^L_sAI+<~cNAbQ(tEU}oMc1u@!Wa{f7Oi)vFgbi>7Ar5XvTK#L-*wO@iM&Xoa_UA<(_+hq;z0UY8lUdthyX?(U zwR)`qCeW)EI%hBXct9p;HX8W?Gt96KJf!b-TCt%gWwIA0-CLb8=1A6`u7OO9vNh3I zc|D)`WBq-ml9Q)k1Utk^ow)&0#B&JdqkPD%e+X1zi3XbHJ|{Mgo4G(A%lSwBPh^r* zY_5^CTr#MYL- zR75T`)phk=Vq7;p6|}>z8fIeEWwbl%k0=97%0SgKs=V4@hnezg(VL{WvQ^`6&u4C= zKMAiX5}NNXU{>%vV6|W%`_o%y8qd@i*Cmh{jmT*063!H}uWZ_6p9KTj7qI8>9CfHe z(w7X~^NPIug*b^UkyyDSv^=avnYcTiraW9{s)O+|iNsq;HB*E1%&Cvu7|Z!r8ra^F zsVq#QN$|TuBc`JAvw}_FH5SNY1nDc9eyA|A%CS3C=Zc^A%(f8czE@iaCT=Hwd)7p> zNkN)TV~HBeOi{33cxzj2r&HTB#%juu*%%cHTNWhyIaH<9LI&O;!9AjM$(T&m97(#HNZFv7VcZTv*NKGqpAnRLk+W=)u3rs4ci zdYPm`YG<`TSeV#+lejG0R;HT$ujM6a;?T7JGVQ(ARbcf()LFL*J<3xppB9ltrWs`{ zvx$K^DixbT9_l+9@9`Fuarj-0_n9d*9$T0xeAw=_Rij~MWf)VVn{`xLU==H44U^ar zsn~}6d2sIAae80uI6jR3BvVa%r|E)^q(Z;i=WxNd=HbJ_ORwJl5qdaA4Z<*RfmWp_ z|1s}9_s=s)-ZHVeL_mCJ15It~7!qcOQ|3!fLprpMuYJXIhQ5}w)daIuBLI`5)8^z! zdCIm^LdJbEYf_YrsVxR)2&qA3P!{BwXt7evbB@dC>>~=#xUVf3Y8%8dE^)G)PKop) zjW22Wk8XAwC;Rf7H>Q___VXVNbmdWgjn(+FTjy7NdxS7-2)e8ZcML6=Ti1VWm9Qf* zDNi1QHoBB#84DHWyK^wBlr&1?CAvrxYg~Odx*>nrGYYIt`WtHl&QhyP7q3QNVLs3i z%8L|?{)wbfl|J@rf?GN=q#_ z!#X`&WM0c~OD>mRr#!1$JWFVS&>WCxV$Slb7Lx_zzXaGyd3l?VpQoxrvF)-66-iDQ3|lg1%Dm{*P?h zrUAoxvu>!Gi!{Br4+xjR;s{V;1~1)&8r}-FRB}32pGa?$6kTW3nE?9t>##hsvzW0d z(%j9L)DMzhR+}RX^hQcCn;yI(b@=obq=^rKn)lpk&VvNS(}8o4@Ah zzW(b>+RSybKcv;{p63FC2*v!AW}m;@@c1Q25)z`P# z7mK>qIbze_X{sdSO?rZ7{_D>2BY-b_suS>CMN@vweyN-%l&tNmWmC+f8 z6t0UWTbrmGU=z?y8ge@SQ2;OPW-vLHi()FdU`^vL3(eezo<;rL!a>; zMr82PYELq_`E3Jr`Hhp}hkYJvEFByYTunZ;(&CxB5Qwc_W|=kZnK9X6$kr-|-Fj-1 z96H)1)OCz>69gfnMx$B~K+lONWAu0V(P{qC%oAp1ksZ>CwQmF`KQ*N?8>jX7IjEuU zNK7ofihw(P3Lb(Y*0+fIP*^h% z>A}f>oB5kPrT(60RGrcKD#6W%bk4M5LCPc2JdTFO;CW#}e~s3o}LyF>=H zE#PQerkbG8gP2T=t_#U^Qqs1NeeW|&_8`j{M`}*JWs2h%!`?DRc7@dMxq4-O){y#8 zD|_N<9%*Vt>`vwJ<|Yf+!9unc{y)^aj-Nkn&_~ZnVrq9KIyo`5RGSj7bT_o7N^mbQ z!j_j8c(d!;i^RtV8Cg2yIJp(xz1f!%qXcv&cQ(e#0d=%K70gO}D7v;TF2@%iUbw8y zzHrcId`77Mx98A>tUqu$WU~_y@rKe0v^E>5==x`LBEPg)^Z*r?o}J&E``#nZ%aSyu zqU90U8~d!38c?VZq;wXT^r@H8rbPk5DD1-;uIX|KOy(8N95oDSdCE&-as_q$g)uk> zXt(BiE>qbJ=$Fis74_@lqseuo7&A`;H(HC<`P!Ik%!qAwCdJ|FZ5m(T(pT_b`_K@K zvyvuXC5;v0$mS|xrkSy1QEkOb%XKY)Tv+^yY5y_oQV*Yxc&Di>7!~Z&8-1G_IA`a8 za~n9wEYOG?jDAk{@41{@?z2BV!BzqQ*bvgPx+SLdj~AU`y{k+6o~$$A>+buKgN%UZ z;8$#Z|G{tZ-xc0{s)52OTrwG(e)-N^oY6S}f3Q=qefPM>&iUjqMIjnNwLm#yKUYya zhJ|S){HTNHuE6z3tKykut;GV1w}diAb(bZ695cL+a#}X+a7LXs zPZd-ys1XD(f2{Gkd!Ww5Ygfba>xvWM4IJT#X!nBX+(5NMf7(WMF2#&`>1hHG)4w#I z9F*(a+tG+1m{5ID$efi~$&cC4zxvJaV$x*s$35>frP@1M&f=3H;-7SRhlO^Xa=hu3 zquhHFKXHCuO%3JPa`8UqVc6I;_Wu$XFECQyX@VDU$)xIi%Fn|mB7{erypqU#Kd4t^ z06I?1NXMI3Pm36oJ-@n>Y2u~iYo-0QL;Ve&q=t(b_7H-iT86RX0m!OM(OHyF721@$ z`1)u0V+h)0P;m0g2^R`bdYGlSYW|wB}VYzK?3?*EoMzs9;>@GAGln zlNsq%cf6+@gcaVv>+L@u%e!57FDOW|X}eRamV5ywTD``JJZ10yCr}LE39TP0gY=6& ztRZisl3S^JjyG2c&ODUbY~?YYl5l0~q+8KDRJ~=TuVdz%Pu7@EVY(d_a!5N@^GrL` z9@S|sxu0P((&f(rA#CiiwpNvMop>a<^T_cW^W}03LgSo_erVgG%03d&z(akSm-3R1 z8??2OeI7}k=o#bRnT%4wK)~|Y=+^Us8fWxJL56xM8D~

9#SLif?};3|}L=yUUY( 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

E~)9@t%m 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 literal 0 HcmV?d00001 diff --git a/icinga-studio/icinga.ico b/icinga-studio/icinga.ico new file mode 100644 index 0000000000000000000000000000000000000000..6ff7e9029050637e225f83525665b4b78d994f06 GIT binary patch literal 766 zcmcgqF&2U_4E?C@l?^9nMvvi<+{#8b*|-u%VRUqpE&c=og#(y2CI2T)(iWiLsOu|$ z3*IN-M(a+ksSj#F-h@6EjaHo{0h~x_{&1y9O&ErO4zURlwWu6Q>2j2i6HnlYxG@0% zt$a6v0mm%FWH&kDUM4!j8dFM7Tq(IonTWF@fp)vbGpLO*a}!J6leLteEU<;VC&R4a zVt9%UVYrhiHcL_655#_{(hzF>FrS2L(|vSzKeK9Ee|DEalDepiSfMIE4;|%ePgG(V ueb4^Z$@(k!bCp(wujOg13cVg+ncTUi)z_1eQ3G&J{MG)UT5Htnqx=B0zls?E literal 0 HcmV?d00001 diff --git a/icinga-studio/icinga.rc b/icinga-studio/icinga.rc new file mode 100644 index 000000000..abcda3f4e --- /dev/null +++ b/icinga-studio/icinga.rc @@ -0,0 +1,34 @@ +#include +#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 \ No newline at end of file diff --git a/icinga-studio/icinga.xpm b/icinga-studio/icinga.xpm new file mode 100644 index 000000000..271363db7 --- /dev/null +++ b/icinga-studio/icinga.xpm @@ -0,0 +1,40 @@ +/* XPM */ +static const char *icinga_xpm[] = { +"32 32 5 1", +" c None", +". c #808080", +"+ c #000000", +"@ c #C0C0C0", +"# c #FFFFFF", +" ", +" .++++++++++++++++++++++++. ", +" .+++++++++++++++..+++++++++. ", +" .+++++++++++++++@##@+++++++++. ", +" +++++++++++++++.####.+++++++++ ", +" +++++++++++++++.####.+++++++++ ", +" ++++++++++++++++####++++++++++ ", +" ++++++++++++++++@@..++++++++++ ", +" +++++..+++++++++#.++++++++++++ ", +" ++++.##@+++++++@#+++++++++++++ ", +" ++++.###+++++++#.+++++++++.+++ ", +" ++++.###@++.@@@#+++++++++@##.+ ", +" +++++++.@#######.+++++++.###@+ ", +" +++++++++########++++..@####.+ ", +" ++++++++.########@@###@...@.++ ", +" ++++++++.#########@..+++++++++ ", +" ++++++++.########@++++++++++++ ", +" ++++++++.########.++++++++++++ ", +" +++++++++########+++++++++++++ ", +" +++++++++.######.+++++++++++++ ", +" +++++++++.#....#.+++++++++++++ ", +" ++++++++.#.++++.#.++++++++++++ ", +" ++++++++@@++++++##.+++++++++++ ", +" ++++@##@#+++++++##@+++++++++++ ", +" +++@####@+++++++..++++++++++++ ", +" +++######.++++++++++++++++++++ ", +" +++######.++++++++++++++++++++ ", +" +++######+++++++++++++++++++++ ", +" .++.####.++++++++++++++++++++. ", +" .+++..+++++++++++++++++++++. ", +" .++++++++++++++++++++++++. ", +" "}; diff --git a/icinga-studio/mainform.cpp b/icinga-studio/mainform.cpp new file mode 100644 index 000000000..18aea2674 --- /dev/null +++ b/icinga-studio/mainform.cpp @@ -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 +#include +#include + +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 tokens; + boost::algorithm::split(tokens, authority, boost::is_any_of("@")); + + if (tokens.size() > 1) { + std::vector userinfo; + boost::algorithm::split(userinfo, tokens[0], boost::is_any_of(":")); + + user = userinfo[0]; + pass = userinfo[1]; + } + + std::vector 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& 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 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::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 attrs; + attrs.push_back(type->Name.ToLower() + ".__name"); + + m_ApiClient->GetObjects(type->PluralName, boost::bind(&MainForm::ObjectsCompletionHandler, this, _1, true), + std::vector(), attrs); +} + +void MainForm::ObjectsCompletionHandler(const std::vector& 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::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 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()) { + 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()) { + wxStringProperty *prop = new wxStringProperty(name.GetData(), wxPG_LABEL, ""); + + 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& 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 kv_pair; + BOOST_FOREACH(const kv_pair& kv, object->Attrs) { + std::vector 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(); +} diff --git a/icinga-studio/mainform.hpp b/icinga-studio/mainform.hpp new file mode 100644 index 000000000..a961edba0 --- /dev/null +++ b/icinga-studio/mainform.hpp @@ -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 m_Types; + + void TypesCompletionHandler(const std::vector& types, bool forward); + void ObjectsCompletionHandler(const std::vector& objects, bool forward); + void ObjectDetailsCompletionHandler(const std::vector& objects, bool forward); + + wxPGProperty *ValueToProperty(const String& name, const Value& value); +}; + +} + +#endif /* MAINFORM_H */ \ No newline at end of file diff --git a/lib/base/CMakeLists.txt b/lib/base/CMakeLists.txt index 2754ea39a..0adaa2fbd 100644 --- a/lib/base/CMakeLists.txt +++ b/lib/base/CMakeLists.txt @@ -86,4 +86,11 @@ install( 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) diff --git a/lib/base/exception.hpp b/lib/base/exception.hpp index 7ceec1ff3..90e219239 100644 --- a/lib/base/exception.hpp +++ b/lib/base/exception.hpp @@ -147,7 +147,15 @@ typedef boost::error_info errinfo_getadd 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_; diff --git a/lib/base/socketevents.cpp b/lib/base/socketevents.cpp index 045563ba2..8c420e709 100644 --- a/lib/base/socketevents.cpp +++ b/lib/base/socketevents.cpp @@ -207,6 +207,8 @@ void SocketEvents::Register(Object *lifesupportObject) l_SocketIOSockets[m_FD] = desc; + m_Events = true; + /* 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); m_FD = INVALID_SOCKET; + + m_Events = false; } WakeUpThread(true); @@ -244,6 +248,12 @@ void SocketEvents::ChangeEvents(int events) WakeUpThread(); } +bool SocketEvents::IsHandlingEvents(void) const +{ + boost::mutex::scoped_lock lock(l_SocketIOMutex); + return m_Events; +} + void SocketEvents::OnEvent(int revents) { diff --git a/lib/base/socketevents.hpp b/lib/base/socketevents.hpp index 2523d6543..82a565727 100644 --- a/lib/base/socketevents.hpp +++ b/lib/base/socketevents.hpp @@ -42,11 +42,14 @@ public: void ChangeEvents(int events); + bool IsHandlingEvents(void) const; + protected: SocketEvents(const Socket::Ptr& socket, Object *lifesupportObject); private: SOCKET m_FD; + bool m_Events; static void InitializeThread(void); static void ThreadProc(void); diff --git a/lib/base/tlsstream.cpp b/lib/base/tlsstream.cpp index 2430ef29a..9bcb5c6eb 100644 --- a/lib/base/tlsstream.cpp +++ b/lib/base/tlsstream.cpp @@ -191,7 +191,7 @@ void TlsStream::OnEvent(int revents) lock.unlock(); - while (m_RecvQ->IsDataAvailable()) + while (m_RecvQ->IsDataAvailable() && IsHandlingEvents()) SignalDataAvailable(); if (m_Shutdown && !m_SendQ->IsDataAvailable()) @@ -318,6 +318,8 @@ void TlsStream::Close(void) boost::mutex::scoped_lock lock(m_Mutex); + m_Eof = true; + if (!m_SSL) return; @@ -326,8 +328,6 @@ void TlsStream::Close(void) m_Socket->Close(); m_Socket.reset(); - - m_Eof = true; } bool TlsStream::IsEof(void) const diff --git a/lib/base/tlsstream.hpp b/lib/base/tlsstream.hpp index e0e90a57c..669e67c7b 100644 --- a/lib/base/tlsstream.hpp +++ b/lib/base/tlsstream.hpp @@ -48,7 +48,7 @@ class I2_BASE_API TlsStream : public Stream, private SocketEvents public: DECLARE_PTR_TYPEDEFS(TlsStream); - TlsStream(const Socket::Ptr& socket, const String& hostname, ConnectionRole role, const boost::shared_ptr& sslContext); + TlsStream(const Socket::Ptr& socket, const String& hostname, ConnectionRole role, const boost::shared_ptr& sslContext = MakeSSLContext()); ~TlsStream(void); boost::shared_ptr GetClientCertificate(void) const; diff --git a/lib/base/tlsutility.cpp b/lib/base/tlsutility.cpp index 37f3328ce..eccc343fe 100644 --- a/lib/base/tlsutility.cpp +++ b/lib/base/tlsutility.cpp @@ -88,30 +88,34 @@ boost::shared_ptr 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_session_id_context(sslContext.get(), (const unsigned char *)"Icinga 2", 8); - if (!SSL_CTX_use_certificate_chain_file(sslContext.get(), pubkey.CStr())) { - Log(LogCritical, "SSL") - << "Error with public key file '" << pubkey << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\""; - BOOST_THROW_EXCEPTION(openssl_error() - << boost::errinfo_api_function("SSL_CTX_use_certificate_chain_file") - << errinfo_openssl_error(ERR_peek_error()) - << boost::errinfo_file_name(pubkey)); + if (!pubkey.IsEmpty()) { + if (!SSL_CTX_use_certificate_chain_file(sslContext.get(), pubkey.CStr())) { + Log(LogCritical, "SSL") + << "Error with public key file '" << pubkey << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\""; + BOOST_THROW_EXCEPTION(openssl_error() + << boost::errinfo_api_function("SSL_CTX_use_certificate_chain_file") + << errinfo_openssl_error(ERR_peek_error()) + << boost::errinfo_file_name(pubkey)); + } } - if (!SSL_CTX_use_PrivateKey_file(sslContext.get(), privkey.CStr(), SSL_FILETYPE_PEM)) { - Log(LogCritical, "SSL") - << "Error with private key file '" << privkey << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\""; - BOOST_THROW_EXCEPTION(openssl_error() - << boost::errinfo_api_function("SSL_CTX_use_PrivateKey_file") - << errinfo_openssl_error(ERR_peek_error()) - << boost::errinfo_file_name(privkey)); - } + if (!privkey.IsEmpty()) { + if (!SSL_CTX_use_PrivateKey_file(sslContext.get(), privkey.CStr(), SSL_FILETYPE_PEM)) { + Log(LogCritical, "SSL") + << "Error with private key file '" << privkey << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\""; + BOOST_THROW_EXCEPTION(openssl_error() + << boost::errinfo_api_function("SSL_CTX_use_PrivateKey_file") + << errinfo_openssl_error(ERR_peek_error()) + << boost::errinfo_file_name(privkey)); + } - if (!SSL_CTX_check_private_key(sslContext.get())) { - Log(LogCritical, "SSL") - << "Error checking private key '" << privkey << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\""; - BOOST_THROW_EXCEPTION(openssl_error() - << boost::errinfo_api_function("SSL_CTX_check_private_key") - << errinfo_openssl_error(ERR_peek_error())); + if (!SSL_CTX_check_private_key(sslContext.get())) { + Log(LogCritical, "SSL") + << "Error checking private key '" << privkey << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\""; + BOOST_THROW_EXCEPTION(openssl_error() + << boost::errinfo_api_function("SSL_CTX_check_private_key") + << errinfo_openssl_error(ERR_peek_error())); + } } if (!cakey.IsEmpty()) { diff --git a/lib/base/tlsutility.hpp b/lib/base/tlsutility.hpp index d994754c9..68129c3ec 100644 --- a/lib/base/tlsutility.hpp +++ b/lib/base/tlsutility.hpp @@ -38,7 +38,7 @@ namespace icinga { void I2_BASE_API InitializeOpenSSL(void); -boost::shared_ptr I2_BASE_API MakeSSLContext(const String& pubkey, const String& privkey, const String& cakey = String()); +boost::shared_ptr 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& context, const String& crlPath); String I2_BASE_API GetCertificateCN(const boost::shared_ptr& certificate); boost::shared_ptr I2_BASE_API GetX509Certificate(const String& pemfile); diff --git a/lib/base/win32.hpp b/lib/base/win32.hpp index c121911f9..76d3efc41 100644 --- a/lib/base/win32.hpp +++ b/lib/base/win32.hpp @@ -21,10 +21,12 @@ #define WIN32_H #define WIN32_LEAN_AND_MEAN +#ifndef _WIN32_WINNT #define _WIN32_WINNT _WIN32_WINNT_VISTA +#endif /* _WIN32_WINNT */ #define NOMINMAX -#include #include +#include #include #include #include diff --git a/lib/config/CMakeLists.txt b/lib/config/CMakeLists.txt index f60b1b852..048250cf4 100644 --- a/lib/config/CMakeLists.txt +++ b/lib/config/CMakeLists.txt @@ -61,3 +61,10 @@ install( RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/icinga2 ) + +if(APPLE) + install( + TARGETS config + LIBRARY DESTINATION ${CMAKE_INSTALL_BINDIR}/icinga-studio.app/Contents + ) +endif() diff --git a/lib/remote/CMakeLists.txt b/lib/remote/CMakeLists.txt index 0d35f445c..a728a4aa6 100644 --- a/lib/remote/CMakeLists.txt +++ b/lib/remote/CMakeLists.txt @@ -27,7 +27,7 @@ set(remote_SOURCES configfileshandler.cpp configmoduleshandler.cpp configmoduleutility.cpp configobjectutility.cpp configstageshandler.cpp createobjecthandler.cpp deleteobjecthandler.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 messageorigin.cpp modifyobjecthandler.cpp statusqueryhandler.cpp typequeryhandler.cpp url.cpp zone.cpp zone.thpp @@ -55,6 +55,13 @@ install( 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/log\")") install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/api/repository\")") diff --git a/lib/remote/apilistener.cpp b/lib/remote/apilistener.cpp index 3c27a38c5..ed53f2478 100644 --- a/lib/remote/apilistener.cpp +++ b/lib/remote/apilistener.cpp @@ -386,7 +386,7 @@ void ApiListener::NewClientHandlerInternal(const Socket::Ptr& client, const Stri } else { 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(); AddHttpClient(aclient); } @@ -908,19 +908,19 @@ std::set ApiListener::GetAnonymousClients(void) const return m_AnonymousClients; } -void ApiListener::AddHttpClient(const HttpConnection::Ptr& aclient) +void ApiListener::AddHttpClient(const HttpServerConnection::Ptr& aclient) { ObjectLock olock(this); m_HttpClients.insert(aclient); } -void ApiListener::RemoveHttpClient(const HttpConnection::Ptr& aclient) +void ApiListener::RemoveHttpClient(const HttpServerConnection::Ptr& aclient) { ObjectLock olock(this); m_HttpClients.erase(aclient); } -std::set ApiListener::GetHttpClients(void) const +std::set ApiListener::GetHttpClients(void) const { ObjectLock olock(this); return m_HttpClients; diff --git a/lib/remote/apilistener.hpp b/lib/remote/apilistener.hpp index 03559a1e1..4990b4101 100644 --- a/lib/remote/apilistener.hpp +++ b/lib/remote/apilistener.hpp @@ -22,7 +22,7 @@ #include "remote/apilistener.thpp" #include "remote/jsonrpcconnection.hpp" -#include "remote/httpconnection.hpp" +#include "remote/httpserverconnection.hpp" #include "remote/endpoint.hpp" #include "remote/messageorigin.hpp" #include "base/configobject.hpp" @@ -69,9 +69,9 @@ public: void RemoveAnonymousClient(const JsonRpcConnection::Ptr& aclient); std::set GetAnonymousClients(void) const; - void AddHttpClient(const HttpConnection::Ptr& aclient); - void RemoveHttpClient(const HttpConnection::Ptr& aclient); - std::set GetHttpClients(void) const; + void AddHttpClient(const HttpServerConnection::Ptr& aclient); + void RemoveHttpClient(const HttpServerConnection::Ptr& aclient); + std::set GetHttpClients(void) const; static Value ConfigUpdateHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params); @@ -85,7 +85,7 @@ private: boost::shared_ptr m_SSLContext; std::set m_Servers; std::set m_AnonymousClients; - std::set m_HttpClients; + std::set m_HttpClients; Timer::Ptr m_Timer; void ApiTimerHandler(void); diff --git a/lib/remote/httpchunkedencoding.cpp b/lib/remote/httpchunkedencoding.cpp index 4d8aa7ca8..5fd3bc8fd 100644 --- a/lib/remote/httpchunkedencoding.cpp +++ b/lib/remote/httpchunkedencoding.cpp @@ -28,6 +28,7 @@ StreamReadStatus HttpChunkedEncoding::ReadChunkFromStream(const Stream::Ptr& str if (context.LengthIndicator == -1) { String line; StreamReadStatus status = stream->ReadLine(&line, context.StreamContext, may_wait); + may_wait = false; if (status != StatusNewItem) return status; @@ -36,35 +37,36 @@ StreamReadStatus HttpChunkedEncoding::ReadChunkFromStream(const Stream::Ptr& str msgbuf << std::hex << line; msgbuf >> context.LengthIndicator; - return StatusNeedData; - } else { - StreamReadContext& scontext = context.StreamContext; - if (scontext.Eof) - return StatusEof; - - if (scontext.MustRead) { - if (!scontext.FillFromStream(stream, may_wait)) { - scontext.Eof = true; - return StatusEof; - } - - scontext.MustRead = false; - } - - if (scontext.Size < (size_t)context.LengthIndicator) { - scontext.MustRead = true; - return StatusNeedData; - } - - *data = new char[context.LengthIndicator]; - *size = context.LengthIndicator; - memcpy(data, scontext.Buffer, context.LengthIndicator); - - scontext.DropData(context.LengthIndicator); - context.LengthIndicator = -1; - - return StatusNewItem; } + + StreamReadContext& scontext = context.StreamContext; + if (scontext.Eof) + return StatusEof; + + if (scontext.MustRead) { + if (!scontext.FillFromStream(stream, may_wait)) { + scontext.Eof = true; + return StatusEof; + } + + scontext.MustRead = false; + } + + size_t NewlineLength = context.LengthIndicator ? 2 : 0; + + if (scontext.Size < (size_t)context.LengthIndicator + NewlineLength) { + scontext.MustRead = true; + return StatusNeedData; + } + + *data = new char[context.LengthIndicator]; + *size = context.LengthIndicator; + memcpy(*data, scontext.Buffer, context.LengthIndicator); + + scontext.DropData(context.LengthIndicator + NewlineLength); + context.LengthIndicator = -1; + + return StatusNewItem; } void HttpChunkedEncoding::WriteChunkToStream(const Stream::Ptr& stream, const char *data, size_t count) diff --git a/lib/remote/httpclientconnection.cpp b/lib/remote/httpclientconnection.cpp new file mode 100644 index 000000000..4837ed3ec --- /dev/null +++ b/lib/remote/httpclientconnection.cpp @@ -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 + +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, HttpCompletionCallback>& currentRequest = *m_Requests.begin(); + HttpRequest& request = *currentRequest.first.get(); + const HttpCompletionCallback& callback = currentRequest.second; + + if (!m_CurrentResponse) + m_CurrentResponse = boost::make_shared(m_Stream, request); + + boost::shared_ptr 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 HttpClientConnection::NewRequest(void) +{ + Reconnect(); + return boost::make_shared(m_Stream); +} + +void HttpClientConnection::SubmitRequest(const boost::shared_ptr& request, const HttpCompletionCallback& callback) +{ + m_Requests.push_back(std::make_pair(request, callback)); + request->Finish(); +} diff --git a/lib/remote/httpclientconnection.hpp b/lib/remote/httpclientconnection.hpp new file mode 100644 index 000000000..72fcfeef9 --- /dev/null +++ b/lib/remote/httpclientconnection.hpp @@ -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 + +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 NewRequest(void); + + typedef boost::function HttpCompletionCallback; + void SubmitRequest(const boost::shared_ptr& request, const HttpCompletionCallback& callback); + +private: + String m_Host; + String m_Port; + bool m_Tls; + Stream::Ptr m_Stream; + std::deque, HttpCompletionCallback> > m_Requests; + boost::shared_ptr 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 */ diff --git a/lib/remote/httprequest.cpp b/lib/remote/httprequest.cpp index 951169e9d..28634563f 100644 --- a/lib/remote/httprequest.cpp +++ b/lib/remote/httprequest.cpp @@ -19,27 +19,29 @@ #include "remote/httprequest.hpp" #include "base/logger.hpp" +#include "base/application.hpp" #include "base/convert.hpp" #include #include #include +#include +#include using namespace icinga; -HttpRequest::HttpRequest(StreamReadContext& src) +HttpRequest::HttpRequest(const Stream::Ptr& stream) : Complete(false), - ProtocolVersion(HttpVersion10), + ProtocolVersion(HttpVersion11), Headers(new Dictionary()), - m_Context(src), - m_ChunkContext(m_Context), + m_Stream(stream), 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) { String line; - StreamReadStatus srs = stream->ReadLine(&line, src, may_wait); + StreamReadStatus srs = m_Stream->ReadLine(&line, src, may_wait); if (srs != StatusNewItem) return false; @@ -95,9 +97,12 @@ bool HttpRequest::Parse(const Stream::Ptr& stream, StreamReadContext& src, bool } } else if (m_State == HttpRequestBody) { if (Headers->Get("transfer-encoding") == "chunked") { + if (!m_ChunkContext) + m_ChunkContext = boost::make_shared(src); + char *data; 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) return false; @@ -114,27 +119,27 @@ bool HttpRequest::Parse(const Stream::Ptr& stream, StreamReadContext& src, bool return true; } } else { - if (m_Context.Eof) + if (src.Eof) BOOST_THROW_EXCEPTION(std::invalid_argument("Unexpected EOF in HTTP body")); - if (m_Context.MustRead) { - if (!m_Context.FillFromStream(stream, false)) { - m_Context.Eof = true; + if (src.MustRead) { + if (!src.FillFromStream(m_Stream, false)) { + src.Eof = true; 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")); - if (m_Context.Size < length_indicator) { - m_Context.MustRead = true; + if (src.Size < length_indicator) { + src.MustRead = true; return false; } - m_Body->Write(m_Context.Buffer, length_indicator); - m_Context.DropData(length_indicator); + m_Body->Write(src.Buffer, length_indicator); + src.DropData(length_indicator); Complete = true; return true; } @@ -151,3 +156,77 @@ size_t HttpRequest::ReadBody(char *data, size_t count) 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; +} diff --git a/lib/remote/httprequest.hpp b/lib/remote/httprequest.hpp index b04dfd09c..9eb1a0b21 100644 --- a/lib/remote/httprequest.hpp +++ b/lib/remote/httprequest.hpp @@ -40,7 +40,8 @@ enum HttpRequestState { HttpRequestStart, HttpRequestHeaders, - HttpRequestBody + HttpRequestBody, + HttpRequestEnd }; /** @@ -59,17 +60,22 @@ public: Dictionary::Ptr Headers; - HttpRequest(StreamReadContext& ctx); - - bool Parse(const Stream::Ptr& stream, StreamReadContext& src, bool may_wait); + HttpRequest(const Stream::Ptr& stream); + bool Parse(StreamReadContext& src, bool may_wait); 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: - StreamReadContext& m_Context; - ChunkReadContext m_ChunkContext; + Stream::Ptr m_Stream; + boost::shared_ptr m_ChunkContext; HttpRequestState m_State; FIFO::Ptr m_Body; + + void FinishHeaders(void); }; } diff --git a/lib/remote/httpresponse.cpp b/lib/remote/httpresponse.cpp index 631c13a35..fdd5d663d 100644 --- a/lib/remote/httpresponse.cpp +++ b/lib/remote/httpresponse.cpp @@ -20,13 +20,16 @@ #include "remote/httpresponse.hpp" #include "remote/httpchunkedencoding.hpp" #include "base/logger.hpp" +#include +#include #include "base/application.hpp" #include "base/convert.hpp" +#include using namespace icinga; 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) @@ -109,3 +112,123 @@ void HttpResponse::Finish(void) if (m_Request.ProtocolVersion == HttpVersion10 || m_Request.Headers->Get("connection") == "close") 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 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(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); +} \ No newline at end of file diff --git a/lib/remote/httpresponse.hpp b/lib/remote/httpresponse.hpp index 6255ac134..94c11d3f0 100644 --- a/lib/remote/httpresponse.hpp +++ b/lib/remote/httpresponse.hpp @@ -43,8 +43,19 @@ enum HttpResponseState struct I2_REMOTE_API HttpResponse { public: + bool Complete; + + HttpVersion ProtocolVersion; + int StatusCode; + String StatusMessage; + + Dictionary::Ptr Headers; + 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 AddHeader(const String& key, const String& value); void WriteBody(const char *data, size_t count); @@ -52,6 +63,7 @@ public: private: HttpResponseState m_State; + boost::shared_ptr m_ChunkContext; const HttpRequest& m_Request; Stream::Ptr m_Stream; FIFO::Ptr m_Body; diff --git a/lib/remote/httpconnection.cpp b/lib/remote/httpserverconnection.cpp similarity index 70% rename from lib/remote/httpconnection.cpp rename to lib/remote/httpserverconnection.cpp index 48c1ac663..e97d9c2c7 100644 --- a/lib/remote/httpconnection.cpp +++ b/lib/remote/httpserverconnection.cpp @@ -17,7 +17,7 @@ * 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/apilistener.hpp" #include "remote/apifunction.hpp" @@ -33,47 +33,46 @@ using namespace icinga; -static boost::once_flag l_HttpConnectionOnceFlag = BOOST_ONCE_INIT; -static Timer::Ptr l_HttpConnectionTimeoutTimer; +static boost::once_flag l_HttpServerConnectionOnceFlag = BOOST_ONCE_INIT; +static Timer::Ptr l_HttpServerConnectionTimeoutTimer; -HttpConnection::HttpConnection(const String& identity, bool authenticated, const TlsStream::Ptr& stream) - : m_Stream(stream), m_Seen(Utility::GetTime()), - m_CurrentRequest(m_Context), m_PendingRequests(0) +HttpServerConnection::HttpServerConnection(const String& identity, bool authenticated, const TlsStream::Ptr& stream) + : m_Stream(stream), m_CurrentRequest(stream), m_Seen(Utility::GetTime()), m_PendingRequests(0) { - boost::call_once(l_HttpConnectionOnceFlag, &HttpConnection::StaticInitialize); + boost::call_once(l_HttpServerConnectionOnceFlag, &HttpServerConnection::StaticInitialize); if (authenticated) m_ApiUser = ApiUser::GetByClientCN(identity); } -void HttpConnection::StaticInitialize(void) +void HttpServerConnection::StaticInitialize(void) { - l_HttpConnectionTimeoutTimer = new Timer(); - l_HttpConnectionTimeoutTimer->OnTimerExpired.connect(boost::bind(&HttpConnection::TimeoutTimerHandler)); - l_HttpConnectionTimeoutTimer->SetInterval(15); - l_HttpConnectionTimeoutTimer->Start(); + l_HttpServerConnectionTimeoutTimer = new Timer(); + l_HttpServerConnectionTimeoutTimer->OnTimerExpired.connect(boost::bind(&HttpServerConnection::TimeoutTimerHandler)); + l_HttpServerConnectionTimeoutTimer->SetInterval(15); + 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()) DataAvailableHandler(); } -ApiUser::Ptr HttpConnection::GetApiUser(void) const +ApiUser::Ptr HttpServerConnection::GetApiUser(void) const { return m_ApiUser; } -TlsStream::Ptr HttpConnection::GetStream(void) const +TlsStream::Ptr HttpServerConnection::GetStream(void) const { 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(); listener->RemoveHttpClient(this); @@ -81,12 +80,12 @@ void HttpConnection::Disconnect(void) m_Stream->Shutdown(); } -bool HttpConnection::ProcessMessage(void) +bool HttpServerConnection::ProcessMessage(void) { bool res; try { - res = m_CurrentRequest.Parse(m_Stream, m_Context, false); + res = m_CurrentRequest.Parse(m_Context, false); } catch (const std::exception& ex) { HttpResponse response(m_Stream, m_CurrentRequest); response.SetStatus(400, "Bad request"); @@ -99,13 +98,13 @@ bool HttpConnection::ProcessMessage(void) } 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_PendingRequests++; m_CurrentRequest.~HttpRequest(); - new (&m_CurrentRequest) HttpRequest(m_Context); + new (&m_CurrentRequest) HttpRequest(m_Stream); return true; } @@ -113,9 +112,9 @@ bool HttpConnection::ProcessMessage(void) 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"); @@ -169,7 +168,7 @@ void HttpConnection::ProcessMessageAsync(HttpRequest& request) m_PendingRequests--; } -void HttpConnection::DataAvailableHandler(void) +void HttpServerConnection::DataAvailableHandler(void) { boost::mutex::scoped_lock lock(m_DataHandlerMutex); @@ -177,27 +176,27 @@ void HttpConnection::DataAvailableHandler(void) while (ProcessMessage()) ; /* empty loop body */ } catch (const std::exception& ex) { - Log(LogWarning, "HttpConnection") + Log(LogWarning, "HttpServerConnection") << "Error while reading Http request: " << DiagnosticInformation(ex); Disconnect(); } } -void HttpConnection::CheckLiveness(void) +void HttpServerConnection::CheckLiveness(void) { 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."; Disconnect(); } } -void HttpConnection::TimeoutTimerHandler(void) +void HttpServerConnection::TimeoutTimerHandler(void) { ApiListener::Ptr listener = ApiListener::GetInstance(); - BOOST_FOREACH(const HttpConnection::Ptr& client, listener->GetHttpClients()) { + BOOST_FOREACH(const HttpServerConnection::Ptr& client, listener->GetHttpClients()) { client->CheckLiveness(); } } diff --git a/lib/remote/httpconnection.hpp b/lib/remote/httpserverconnection.hpp similarity index 88% rename from lib/remote/httpconnection.hpp rename to lib/remote/httpserverconnection.hpp index a1ca8cf7d..f684d5b6e 100644 --- a/lib/remote/httpconnection.hpp +++ b/lib/remote/httpserverconnection.hpp @@ -17,8 +17,8 @@ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * ******************************************************************************/ -#ifndef HTTPCONNECTION_H -#define HTTPCONNECTION_H +#ifndef HTTPSERVERCONNECTION_H +#define HTTPSERVERCONNECTION_H #include "remote/httprequest.hpp" #include "remote/apiuser.hpp" @@ -34,12 +34,12 @@ namespace icinga * * @ingroup remote */ -class I2_REMOTE_API HttpConnection : public Object +class I2_REMOTE_API HttpServerConnection : public Object { 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); @@ -72,4 +72,4 @@ private: } -#endif /* HTTPCONNECTION_H */ +#endif /* HTTPSERVERCONNECTION_H */ diff --git a/third-party/execvpe/CMakeLists.txt b/third-party/execvpe/CMakeLists.txt index 4d5538006..47d07fe7c 100644 --- a/third-party/execvpe/CMakeLists.txt +++ b/third-party/execvpe/CMakeLists.txt @@ -29,3 +29,9 @@ install( LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/icinga2 ) +if(APPLE) + install( + TARGETS execvpe + LIBRARY DESTINATION ${CMAKE_INSTALL_BINDIR}/icinga-studio.app/Contents + ) +endif() diff --git a/third-party/mmatch/CMakeLists.txt b/third-party/mmatch/CMakeLists.txt index 8298a0424..0cd1cc914 100644 --- a/third-party/mmatch/CMakeLists.txt +++ b/third-party/mmatch/CMakeLists.txt @@ -28,3 +28,10 @@ install( RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/icinga2 ) + +if(APPLE) + install( + TARGETS mmatch + LIBRARY DESTINATION ${CMAKE_INSTALL_BINDIR}/icinga-studio.app/Contents + ) +endif() diff --git a/third-party/socketpair/CMakeLists.txt b/third-party/socketpair/CMakeLists.txt index 5dd113890..9c149cb50 100644 --- a/third-party/socketpair/CMakeLists.txt +++ b/third-party/socketpair/CMakeLists.txt @@ -33,3 +33,9 @@ install( LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/icinga2 ) +if(APPLE) + install( + TARGETS socketpair + LIBRARY DESTINATION ${CMAKE_INSTALL_BINDIR}/icinga-studio.app/Contents + ) +endif() diff --git a/third-party/yajl/src/CMakeLists.txt b/third-party/yajl/src/CMakeLists.txt index 249b4783d..c5de3539d 100644 --- a/third-party/yajl/src/CMakeLists.txt +++ b/third-party/yajl/src/CMakeLists.txt @@ -60,3 +60,10 @@ INCLUDE_DIRECTORIES(${incDir}/..) INSTALL(TARGETS yajl RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/icinga2) + +if(APPLE) + install( + TARGETS yajl + LIBRARY DESTINATION ${CMAKE_INSTALL_BINDIR}/icinga-studio.app/Contents + ) +endif()