remove old code

Signed-off-by: Ralf Ramsauer <ralf@binary-kitchen.de>
This commit is contained in:
Ralf Ramsauer 2018-03-23 22:15:48 +00:00
parent dc724b4abb
commit 71bc36a557
67 changed files with 0 additions and 2902 deletions

View File

@ -1,146 +0,0 @@
cmake_minimum_required(VERSION 2.8)
project(doorlockd)
option(USE_COLORIZED_LOGS "Colorized logging" ON)
option(DORLOCKD_CMAKE_DEBUG "Add some debug output" Off)
set(DOORLOCK_VERSION_MAJOR 1)
set(DOORLOCK_VERSION_MINOR 4)
set(DOORLOCK_VERSION_PATCH 0)
set(DOORLOCK_VERSION "${DOORLOCK_VERSION_MAJOR}.${DOORLOCK_VERSION_MINOR}.${DOORLOCK_VERSION_PATCH}")
# Instruct CMake to run moc automatically when needed.
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
if(DORLOCKD_CMAKE_DEBUG)
message(STATUS "[${CMAKE_CURRENT_LIST_DIR}:${CMAKE_CURRENT_LIST_LINE}] "
"doorlockd version: ${DOORLOCK_VERSION}")
endif()
# Get the current working branch
execute_process(
COMMAND git rev-parse --abbrev-ref HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_BRANCH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
set(GIT_BRANCH "\"${GIT_BRANCH}\"")
if(DORLOCKD_CMAKE_DEBUG)
message(STATUS "[${CMAKE_CURRENT_LIST_DIR}:${CMAKE_CURRENT_LIST_LINE}] "
"git branch: ${GIT_BRANCH}")
endif()
# Get the latest abbreviated commit hash of the working branch
execute_process(
COMMAND git log -1 --format=%h
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_COMMIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
set(GIT_COMMIT_HASH "\"${GIT_COMMIT_HASH}\"")
if(DORLOCKD_CMAKE_DEBUG)
message(STATUS "[${CMAKE_CURRENT_LIST_DIR}:${CMAKE_CURRENT_LIST_LINE}] "
"git commit hash: ${GIT_COMMIT_HASH}")
endif()
add_definitions(-std=c++11)
configure_file (
"${PROJECT_SOURCE_DIR}/config.h.in"
"${PROJECT_BINARY_DIR}/config.h"
)
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -ggdb -Wall -pedantic -Weffc++ -Wextra -Wno-unused-result")
set(CMAKE_CXX_FLAGS "-O2 -Wall -pedantic -Wextra -Weffc++ -Wno-unused-result")
set(CMAKE_C_FLAGS_DEBUG "-O0 -ggdb -Wall -pedantic -Wextra -Wno-unused-result")
set(CMAKE_C_FLAGS "-O2 -Wall -pedantic -Wextra -Wno-unused-result")
find_package(Boost 1.55.0 COMPONENTS filesystem program_options system REQUIRED)
set(JSON_INCLUDE_DIR "/usr/include/jsoncpp" CACHE PATH "path to jsoncpp includes")
find_package (Threads)
find_package(Qt5Widgets)
include_directories(
${Boost_INCLUDE_DIRS}
${PROJECT_BINARY_DIR}
${JSON_INCLUDE_DIR})
set(_LIBDOORLOCK_SRCS
lib/clientmessage.cpp
lib/clientmessage.h
lib/door.cpp
lib/door.h
lib/doormessage.cpp
lib/doormessage.h
lib/logger.cpp
lib/logger.h
lib/logic.cpp
lib/logic.h
lib/request.cpp
lib/request.h
lib/response.cpp
lib/response.h
lib/util.cpp
lib/util.h)
set(_DOORLOCKD_SRCS
daemon/doorlockd.cpp)
set(_DOORLOCK_CLIENT_SRCS
client/qrwidget.cpp
client/qrwidget.h
client/doorlock-client.cpp
client/mainwindow.h
client/mainwindow.cpp
client/mainwindow.ui
client/network.cpp
client/network.h
client/wave.h
client/wave.cpp)
add_library(doorlock STATIC ${_LIBDOORLOCK_SRCS})
target_link_libraries(doorlock jsoncpp ldap ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
add_executable(doorlockd ${_DOORLOCKD_SRCS})
target_link_libraries(doorlockd doorlock)
add_executable(doorlock-client ${_DOORLOCK_CLIENT_SRCS})
target_link_libraries(doorlock-client doorlock qrencode Qt5::Widgets)
target_include_directories(doorlock-client PRIVATE ${CMAKE_SOURCE_DIR})
mark_as_advanced(
_LIBDOORLOCK_SRCS
_DOORLOCKD_SRCS
_DOORLOCK_CLIENT_SRCS)
install(TARGETS doorlockd DESTINATION sbin/)
install(TARGETS doorlock-client DESTINATION bin/)
install(DIRECTORY images/ DESTINATION share/doorlockd/images
FILES_MATCHING PATTERN "images/*.png"
PERMISSIONS WORLD_READ OWNER_READ GROUP_READ)
install(DIRECTORY sounds/ DESTINATION share/doorlockd/sounds
FILES_MATCHING PATTERN "sounds/*.wav"
PERMISSIONS WORLD_READ OWNER_READ GROUP_READ)
install(FILES scripts/doorlockd.service DESTINATION /etc/systemd/system/)
install(DIRECTORY scripts/ DESTINATION etc/doorlockd/
FILES_MATCHING PATTERN "scripts/pre_*lock"
PERMISSIONS WORLD_EXECUTE WORLD_READ OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE OWNER_WRITE)
install(DIRECTORY scripts/ DESTINATION etc/doorlockd/
FILES_MATCHING PATTERN "scripts/post_*lock"
PERMISSIONS WORLD_EXECUTE WORLD_READ OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE OWNER_WRITE)
install(DIRECTORY scripts/ DESTINATION etc/doorlockd/
FILES_MATCHING PATTERN "scripts/emergency_unlock"
PERMISSIONS WORLD_EXECUTE WORLD_READ OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE OWNER_WRITE)

View File

@ -1,78 +0,0 @@
#include <exception>
#include <iostream>
#include <string>
#include <thread>
#include <boost/program_options.hpp>
#include <QApplication>
#include "config.h"
#include "mainwindow.h"
// Info about doorlock-client version
const static std::string version =
"doorlock-client-" DOORLOCK_VERSION;
const static std::string gitversion =
DOORLOCK_GIT_BRANCH "-" DOORLOCK_GIT_COMMIT_HASH;
namespace po = boost::program_options;
int main(int argc, char** argv)
{
int retval = 0;
Logger &l = Logger::get();
l((std::string)"Hello, this is " + version + " built on " + gitversion,
LogLevel::info);
try {
QApplication app(argc, argv);
std::string hostname;
unsigned short port;
po::variables_map vm;
qRegisterMetaType<Clientmessage>("Clientmessage");
app.setOrganizationName("Binary Kitchen");
app.setApplicationName("doorlock-client");
po::options_description desc("doorlockd (" + version + " built on " + gitversion + ")");
desc.add_options()
("help,h",
"print help")
("port,p",
po::value<unsigned short>(&port)->default_value(DEFAULT_PORT),
"Port")
("host,c",
po::value<std::string>(&hostname)->default_value("localhost"),
"IP or name of host running doorlockd");
po::store(po::command_line_parser(argc, argv).options(desc).run(), vm);
if (vm.count("help"))
{
std::cout << desc << std::endl;
exit(-1);
}
po::notify(vm);
l(LogLevel::notice, "Starting doorlock-client");
// Start main GUI
MainWindow mainWindow(hostname, port);
mainWindow.showFullScreen();
// This routine will never return under normal conditions
retval = app.exec();
mainWindow.hide();
mainWindow.close();
}
catch(const std::exception &e)
{
l(LogLevel::error, e.what());
retval = -1;
}
l(LogLevel::notice, "Stopping doorlock-client");
return retval;
}

View File

@ -1,85 +0,0 @@
#include "config.h"
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "network.h"
MainWindow::MainWindow(const std::string &hostname,
const unsigned short port,
QWidget* parent) :
QWidget(parent),
ui(new Ui::MainWindow),
_soundLock(Wave(SOUND_LOCK)),
_soundUnlock(Wave(SOUND_UNLOCK)),
_soundEmergencyUnlock(Wave(SOUND_EMERGENCY_UNLOCK)),
_soundZonk(Wave(SOUND_ZONK)),
_soundLockButton(Wave(SOUND_LOCK_BUTTON)),
_soundUnlockButton(Wave(SOUND_UNLOCK_BUTTON))
{
ui->setupUi(this);
_LED(false);
NetworkThread* nw = new NetworkThread(hostname, port);
connect(nw, SIGNAL(new_clientmessage(Clientmessage)),
SLOT(setClientmessage(Clientmessage)));
connect(nw, SIGNAL(finished()),
nw, SLOT(deleteLater()));
nw->start();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::setClientmessage(const Clientmessage &msg)
{
ui->qrwidget->setQRData(msg.web_address());
ui->address->setText(QString::fromStdString(msg.web_address()));
ui->token->setText(QString::fromStdString(msg.token()));
QString statusMessage("");
const auto &doormsg = msg.doormessage();
_LED(msg.isOpen());
if (_oldMessage.isOpen()
&& !msg.isOpen()
&& !doormsg.isLockButton) {
// regular close
statusMessage = "Bye bye. See you next time!";
_soundLock.playAsync();
} else if (!_oldMessage.isOpen() && msg.isOpen()) {
// regular open
statusMessage = "Come in! Happy hacking!";
_soundUnlock.playAsync();
} else {
// no change
}
if (doormsg.isEmergencyUnlock) {
_soundEmergencyUnlock.playAsync();
statusMessage = "!! EMERGENCY UNLOCK !!";
} else if (doormsg.isLockButton) {
_soundLockButton.playAsync();
statusMessage = "!! LOCK BUTTON !!";
} else if (doormsg.isUnlockButton) {
statusMessage = "!! UNLOCK BUTTON !!";
if (msg.isOpen()) {
_soundZonk.playAsync();
} else {
_soundUnlockButton.playAsync();
}
}
ui->message->setText(statusMessage);
_oldMessage = msg;
}
void MainWindow::_LED(const bool on)
{
if (on)
ui->LED->setPixmap(QPixmap(IMAGE_LED_GREEN));
else
ui->LED->setPixmap(QPixmap(IMAGE_LED_RED));
}

View File

@ -1,46 +0,0 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QWidget>
#include "../lib/clientmessage.h"
#include "../lib/logger.h"
#include "wave.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QWidget
{
Q_OBJECT
public:
explicit MainWindow(const std::string &hostname,
const unsigned short port,
QWidget *parent = 0);
MainWindow(const MainWindow &rhs);
MainWindow &operator =(const MainWindow &rhs);
~MainWindow();
public slots:
void setClientmessage(const Clientmessage &msg);
private:
Ui::MainWindow *ui;
Clientmessage _oldMessage = { };
const Wave _soundLock;
const Wave _soundUnlock;
const Wave _soundEmergencyUnlock;
const Wave _soundZonk;
const Wave _soundLockButton;
const Wave _soundUnlockButton;
void _LED(const bool on);
};
#endif // MAINWINDOW_H

View File

@ -1,168 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QWidget" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>400</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" alignment="Qt::AlignHCenter">
<widget class="QLabel" name="welcomeLabel">
<property name="font">
<font>
<pointsize>36</pointsize>
</font>
</property>
<property name="text">
<string>Willkommen in der Binary Kitchen!</string>
</property>
</widget>
</item>
<item row="2" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QRWidget" name="qrwidget" native="true"/>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item alignment="Qt::AlignHCenter">
<widget class="QLabel" name="LED">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item alignment="Qt::AlignHCenter">
<widget class="QLabel" name="message">
<property name="font">
<font>
<pointsize>28</pointsize>
</font>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="token">
<property name="font">
<font>
<pointsize>36</pointsize>
</font>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="address">
<property name="font">
<font>
<pointsize>22</pointsize>
</font>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QRWidget</class>
<extends>QWidget</extends>
<header>client/qrwidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -1,94 +0,0 @@
#include "network.h"
#include "../lib/response.h"
namespace ba = boost::asio;
using ba::ip::tcp;
const std::string NetworkThread::_subscription_command = "{ \"command\": \"subscribe\"}";
NetworkThread::NetworkThread(const std::string &hostname,
const unsigned short port) :
QThread(),
_l(Logger::get()),
_hostname(hostname),
_port(port),
_io_service()
{
}
void NetworkThread::run() {
do {
try {
tcp::resolver resolver(_io_service);
tcp::socket socket(_io_service);
tcp::resolver::query query(_hostname, std::to_string(_port));
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::resolver::iterator end;
boost::system::error_code error = ba::error::host_not_found;
std::vector<char> data;
while (error && endpoint_iterator != end) {
socket.close();
socket.connect(*endpoint_iterator++, error);
}
if (error)
throw boost::system::system_error(error);
// After connection is established, send the subscription command
socket.write_some(ba::buffer(_subscription_command), error);
if (error)
throw boost::system::system_error(error);
data.resize(_SOCKET_BUFFERLENGTH);
std::function<void(void)> receiveMessage = [&] () {
socket.async_read_some(ba::buffer(data),
[&] (const boost::system::error_code &ec,
const size_t length)
{
if (ec) {
throw boost::system::system_error(ec);
}
const auto msg = Clientmessage::fromString(
std::string(data.begin(), data.begin()+length));
// Received valid Clientmessage
// Log it!
const auto& doormessage = msg.doormessage();
_l("Received message", LogLevel::info);
_l((std::string)" token: " + msg.web_address(),
LogLevel::info);
_l((std::string)" open: " + std::to_string(msg.isOpen()),
LogLevel::info);
_l((std::string)" button lock: " + std::to_string(doormessage.isLockButton),
LogLevel::info);
_l((std::string)" button unlock: " + std::to_string(doormessage.isUnlockButton),
LogLevel::info);
_l((std::string)" emergency open: " + std::to_string(doormessage.isEmergencyUnlock),
LogLevel::info);
// Emit the new message
emit new_clientmessage(msg);
// Wait for new message
receiveMessage();
});
};
receiveMessage();
_io_service.run();
}
catch(const Response &err) {
_l(err.message, LogLevel::error);
}
catch(const std::exception &err) {
_l(LogLevel::error, err.what());
}
sleep(1);
} while(1);
_io_service.reset();
}

View File

@ -1,37 +0,0 @@
#ifndef NETWORK_H
#define NETWORK_H
#include <string>
#include <boost/asio.hpp>
#include <QThread>
#include "../lib/clientmessage.h"
#include "../lib/logger.h"
class NetworkThread : public QThread {
Q_OBJECT
public:
NetworkThread(const std::string &hostname,
const unsigned short port);
private:
void run();
Logger &_l;
const std::string _hostname;
const unsigned short _port;
boost::asio::io_service _io_service;
const static std::string _subscription_command;
// The receive buffer length of the TCP socket
constexpr static int _SOCKET_BUFFERLENGTH = {2048};
signals:
void new_clientmessage(Clientmessage msg);
};
#endif

View File

@ -1,63 +0,0 @@
#include <exception>
#include <memory>
#include <QPainter>
#include <QDebug>
#include <qrencode.h>
#include "qrwidget.h"
QRWidget::QRWidget(QWidget* parent) :
QWidget(parent),
_data(" ")
{
}
void QRWidget::setQRData(const std::string &data)
{
_data = data;
update();
}
void QRWidget::paintEvent(QPaintEvent*)
{
QPainter painter(this);
std::unique_ptr<QRcode, void(*)(QRcode*)> qr(
QRcode_encodeString(_data.c_str(), 1, QR_ECLEVEL_L, QR_MODE_8, 1),
[] (QRcode* ptr) {
if (ptr)
QRcode_free(ptr);
});
if (!qr)
throw std::runtime_error("Error generating QR Code");
QColor fg("black");
QColor bg("white");
painter.setBrush(bg);
painter.setPen(Qt::NoPen);
painter.drawRect(0,0,width(),height());
painter.setBrush(fg);
const int s=qr->width>0?qr->width:1;
const double w=width();
const double h=height();
const double aspect=w/h;
const double scale=((aspect>1.0)?h:w)/s;
for(int y=0;y<s;y++) {
const int yy=y*s;
for(int x=0;x<s;x++) {
const int xx=yy+x;
const unsigned char b=qr->data[xx];
if(b &0x01) {
const double rx1=x*scale, ry1=y*scale;
QRectF r(rx1, ry1, scale, scale);
painter.drawRects(&r,1);
}
}
}
}

View File

@ -1,23 +0,0 @@
#ifndef QRWIDGET_H
#define QRWIDGET_H
#include <string>
#include <QWidget>
class QRWidget : public QWidget
{
Q_OBJECT
public:
explicit QRWidget(QWidget *parent = nullptr);
void setQRData(const std::string &data);
private:
std::string _data;
protected:
void paintEvent(QPaintEvent *);
};
#endif

View File

@ -1,30 +0,0 @@
#include <cstring>
#include <stdexcept>
#include <thread>
#include "wave.h"
std::mutex Wave::_playMutex = {};
Wave::Wave(const std::string &filename):
_filename(filename),
_command("aplay " + _filename)
{
}
Wave::~Wave()
{
}
void Wave::play() const
{
std::lock_guard<std::mutex> l(_playMutex);
system(_command.c_str());
}
void Wave::playAsync() const
{
std::thread([this] () {
play();
}).detach();
}

View File

@ -1,25 +0,0 @@
#ifndef WAVE_H
#define WAVE_H
#include <mutex>
#include <string>
class Wave {
public:
Wave(const std::string &filename);
Wave(const Wave &rhs);
~Wave();
Wave &operator=(const Wave &rhs);
void play() const;
void playAsync() const;
private:
static std::mutex _playMutex;
const std::string _filename;
const std::string _command;
};
#endif

View File

@ -1,54 +0,0 @@
#ifndef CONFIG_H
#define CONFIG_H
#cmakedefine USE_COLORIZED_LOGS
#define DOORLOCK_VERSION_MAJOR "@DOORLOCK_VERSION_MAJOR@"
#define DOORLOCK_VERSION_MINOR "@DOORLOCK_VERSION_MINOR@"
#define DOORLOCK_VERSION_PATCH "@DOORLOCK_VERSION_PATCH@"
#define DOORLOCK_VERSION "@DOORLOCK_VERSION@"
#define DOORLOCK_GIT_BRANCH @GIT_BRANCH@
#define DOORLOCK_GIT_COMMIT_HASH @GIT_COMMIT_HASH@
#ifdef DEBUG
#define DEFAULT_LOG_LEVEL LogLevel::debug2
#else
#define DEFAULT_LOG_LEVEL LogLevel::info
#endif
#define DEFAULT_TOKEN_TIMEOUT (60*5)
#define DEFAULT_PORT 5555
#define DEFAULT_WEB_PREFIX "https://lock.binary.kitchen/"
#define DEFAULT_LDAP_URI "ldaps://ldap1.binary.kitchen/ ldaps://ldap2.binary.kitchen/ ldaps://ldapm.binary.kitchen/"
#define DEFAULT_BINDDN "cn=%s,ou=people,dc=binary-kitchen,dc=de"
#define DEFAULT_TOKEN_LENGTH 6
#define DEFAULT_LOG_DIR "/var/log/"
#define DEFAULT_SERIAL_DEVICE "/dev/ttyAMA0"
#define DEFAULT_SERIAL_BAUDRATE 9600UL
#define LOG_FILENAME "doorlockd.log"
#define LOG_SCRIPTS_FILENAME "doorlockd-scripts.log"
#define SHARED_LOCATION "@CMAKE_INSTALL_PREFIX@/share/doorlockd/"
#define IMAGE_LOCATION SHARED_LOCATION "images/"
#define IMAGE_LED_GREEN IMAGE_LOCATION "led-green.png"
#define IMAGE_LED_RED IMAGE_LOCATION "led-red.png"
#define SOUNDS_LOCATION SHARED_LOCATION "sounds/"
#define SOUND_LOCK SOUNDS_LOCATION "lock.wav"
#define SOUND_UNLOCK SOUNDS_LOCATION "unlock.wav"
#define SOUND_EMERGENCY_UNLOCK SOUNDS_LOCATION "emergency_unlock.wav"
#define SOUND_ZONK SOUNDS_LOCATION "zonk.wav"
#define SOUND_LOCK_BUTTON SOUNDS_LOCATION "lock_button.wav"
#define SOUND_UNLOCK_BUTTON SOUNDS_LOCATION "unlock_button.wav"
#define PRE_LOCK_SCRIPT "@CMAKE_INSTALL_PREFIX@/etc/doorlockd/pre_lock"
#define POST_LOCK_SCRIPT "@CMAKE_INSTALL_PREFIX@/etc/doorlockd/post_lock"
#define PRE_UNLOCK_SCRIPT "@CMAKE_INSTALL_PREFIX@/etc/doorlockd/pre_unlock"
#define POST_UNLOCK_SCRIPT "@CMAKE_INSTALL_PREFIX@/etc/doorlockd/post_unlock"
#define EMERGENCY_UNLOCK_SCRIPT "@CMAKE_INSTALL_PREFIX@/etc/doorlockd/emergency_unlock"
#endif

View File

@ -1,278 +0,0 @@
#include <csignal>
#include <iostream>
#include <boost/filesystem.hpp>
#include <boost/program_options.hpp>
#include <boost/asio.hpp>
#include <json/json.h>
#include "../lib/logic.h"
#include "config.h"
namespace po = boost::program_options;
namespace ba = boost::asio;
namespace fs = boost::filesystem;
using ba::ip::tcp;
// Info about doorlockd version
const static std::string version =
"doorlockd-" DOORLOCK_VERSION;
const static std::string gitversion =
DOORLOCK_GIT_BRANCH "-" DOORLOCK_GIT_COMMIT_HASH;
// The receive buffer length of the TCP socket
const int constexpr SOCKET_BUFFERLENGTH = 2048;
static Logger &l = Logger::get();
static std::unique_ptr<Logic> logic = nullptr;
static ba::io_service io_service;
static std::mutex mutex;
static std::condition_variable onClientMessage;
static volatile bool run = true;
static void signal_handler(int signum)
{
l((std::string)"Received Signal " + std::to_string(signum),
LogLevel::warning);
io_service.stop();
run = false;
onClientMessage.notify_all();
}
static Response subscribe(tcp::socket &sock)
{
sock.write_some(ba::buffer(logic->getClientMessage().toJson()));
while (run) {
std::unique_lock<std::mutex> lock(mutex);
onClientMessage.wait(lock);
if (run) {
sock.write_some(ba::buffer(logic->getClientMessage().toJson()));
}
};
return Response(Response::Code::Success);
}
static void session(tcp::socket &&sock)
{
ba::ip::address remoteAddress;
unsigned short remotePort = 0;
Response response;
try {
std::vector<char> data;
data.resize(SOCKET_BUFFERLENGTH);
remoteAddress = sock.remote_endpoint().address();
remotePort = sock.remote_endpoint().port();
l("Incoming TCP connection from " + remoteAddress.to_string() + "("
+ std::to_string(remotePort) + ")",
LogLevel::notice);
size_t length = sock.read_some(ba::buffer(data));
// Get Request
const std::string requestString(data.begin(), data.begin()+length);
l(" Parsing request...", LogLevel::info);
Request request = Request::fromString(requestString);
switch (request.command) {
case Request::Command::Lock:
case Request::Command::Unlock:
response = logic->request(request);
break;
case Request::Command::Subscribe:
if (remoteAddress.is_loopback() == false) {
response.code = Response::Code::AccessDenied;
response.message = "Subscriptions are only allowed from localhost";
} else {
response = subscribe(sock);
}
break;
case Request::Command::Unknown:
default:
response.code = Response::Code::UnknownCommand;
response.message = "Received unknown command ";
break;
}
throw response;
}
catch (const Response &err) {
response = err;
}
catch (const std::exception &err) {
response.code = Response::Code::Fail;
response.message = "Exception in session " + remoteAddress.to_string()
+ ":" + std::to_string(remotePort) + " : " + err.what();
}
catch (...) {
response.code = Response::Code::Fail;
response.message = "Unhandled doorlockd error";
}
if (!response) {
l(response.message, LogLevel::warning);
}
if (sock.is_open()) {
boost::system::error_code ec;
sock.write_some(ba::buffer(response.toJson()), ec);
}
l("Closing TCP connection from " + remoteAddress.to_string(), LogLevel::notice);
}
static void server(unsigned short port)
{
l(LogLevel::info, "Starting TCP Server");
const auto endpoint = tcp::endpoint(ba::ip::address::from_string("127.0.0.1"), port);
tcp::acceptor a(io_service, endpoint);
tcp::socket sock(io_service);
std::function<void(void)> accept_connection = [&] () {
a.async_accept(sock,
[&] (boost::system::error_code ec) {
if (ec)
{
return;
}
std::thread(session, std::move(sock)).detach();
accept_connection();
});
};
accept_connection();
io_service.run();
l(LogLevel::info, "Stopped TCP Server");
}
int main(int argc, char** argv)
{
int retval = -1;
short port;
std::chrono::seconds tokenTimeout;
std::string ldapUri;
std::string bindDN;
std::string lockPagePrefix;
fs::path logdir;
std::string logfile;
std::string logfile_scripts;
unsigned int tokenLength;
std::string serDev;
unsigned int baudrate;
try {
unsigned int timeout;
po::options_description desc("doorlockd (" + version + " built on " + gitversion + ")");
desc.add_options()
("help,h",
"print help")
("tokentimeout,t",
po::value<unsigned int>(&timeout)->default_value(DEFAULT_TOKEN_TIMEOUT),
"Token timeout in seconds")
("port,p",
po::value<short>(&port)->default_value(DEFAULT_PORT),
"Port")
("ldap,s",
po::value<std::string>(&ldapUri)->default_value(DEFAULT_LDAP_URI),
"Ldap Server")
("bidndn,b",
po::value<std::string>(&bindDN)->default_value(DEFAULT_BINDDN),
"Bind DN, %s means username")
("web,w",
po::value<std::string>(&lockPagePrefix)->default_value(DEFAULT_WEB_PREFIX),
"Prefix of the webpage")
("tokenLength,t",
po::value<unsigned int>(&tokenLength)->default_value(DEFAULT_TOKEN_LENGTH),
"Token length")
("logdir,l",
po::value<fs::path>(&logdir)->default_value(DEFAULT_LOG_DIR),
"Log file")
("serial,i",
po::value<std::string>(&serDev)->default_value(DEFAULT_SERIAL_DEVICE),
"Serial port")
("baud,r",
po::value<unsigned int>(&baudrate)->default_value((DEFAULT_SERIAL_BAUDRATE)),
"Serial baudrate");
po::variables_map vm;
po::store(po::command_line_parser(argc, argv).options(desc).run(), vm);
if (vm.count("help"))
{
std::cout << desc << std::endl;
retval = 0;
exit(0);
}
po::notify(vm);
tokenTimeout = std::chrono::seconds(timeout);
logfile = (logdir / LOG_FILENAME).string();
logfile_scripts = (logdir / LOG_SCRIPTS_FILENAME).string();
l.setLogFile(logfile);
l.logFile(true);
}
catch(const std::exception &e)
{
std::cerr << "Error: " << e.what() << "\n";
exit(-1);
}
l((std::string)"Hello, this is " + version + " built on " + gitversion,
LogLevel::info);
l(LogLevel::notice, "Starting doorlockd");
signal(SIGINT, signal_handler);
signal(SIGKILL, signal_handler);
signal(SIGTERM, signal_handler);
signal(SIGUSR1, signal_handler);
signal(SIGUSR2, signal_handler);
l(LogLevel::info, "Starting Doorlock Logic");
retval = 0;
try {
logic = std::unique_ptr<Logic>(new Logic(tokenTimeout,
ldapUri,
bindDN,
lockPagePrefix,
tokenLength,
serDev,
baudrate,
logfile_scripts,
onClientMessage));
server(port);
}
catch (const boost::system::system_error &ex) {
l(LogLevel::error, "Fatal error: %s", ex.what());
retval = -1;
}
catch (const std::exception &ex) {
l(LogLevel::error, "Fatal error: %s", ex.what());
retval = -1;
}
catch (...) {
l(LogLevel::error, "Fatal error, shutting down");
retval = -1;
}
if (logic) {
l(LogLevel::info, "Stopping Doorlock Logic");
logic.reset();
}
l(LogLevel::notice, "Doorlockd stopped");
return retval;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1,120 +0,0 @@
#include "clientmessage.h"
#include "util.h"
#include "response.h"
const std::string Clientmessage::_tokenKey = "token";
const std::string Clientmessage::_unlockButtonKey = "unlockButton";
const std::string Clientmessage::_lockButtonKey = "lockButton";
const std::string Clientmessage::_emergencyUnlockKey = "emergencyUnlock";
const std::string Clientmessage::_isOpenKey = "isOpen";
const std::regex Clientmessage::_token_regex(".*/([0-9a-fA-F]+)");
Clientmessage::Clientmessage(std::string web_address,
bool isOpen,
Doormessage doormessage) :
_web_address(web_address),
_token(),
_isOpen(isOpen),
_doormessage(doormessage)
{
std::smatch match;
if (std::regex_match(_web_address, match, _token_regex)) {
if (match.size() == 2) {
_token = match[1].str();
} else {
_token = "ERROR";
}
} else {
_token = "ERROR";
}
}
Clientmessage::Clientmessage() :
_web_address(),
_token(),
_isOpen(false),
_doormessage()
{
}
Clientmessage &Clientmessage::operator=(const Clientmessage &rhs)
{
// Protect against self assignement
if (this == &rhs) {
return *this;
}
this->_web_address = rhs._web_address;
this->_token = rhs._token;
this->_isOpen = rhs._isOpen;
this->_doormessage = rhs._doormessage;
return *this;
}
std::string Clientmessage::toJson() const
{
Json::StyledWriter writer;
Json::Value message;
message[_tokenKey] = _web_address;
message[_unlockButtonKey] = _doormessage.isUnlockButton;
message[_lockButtonKey] = _doormessage.isLockButton;
message[_emergencyUnlockKey] = _doormessage.isEmergencyUnlock;
message[_isOpenKey] = _isOpen;
return writer.write(message);
}
const std::string& Clientmessage::token() const
{
return _token;
}
const std::string& Clientmessage::web_address() const
{
return _web_address;
}
const Doormessage& Clientmessage::doormessage() const
{
return _doormessage;
}
Clientmessage Clientmessage::fromJson(const Json::Value &root)
{
std::string token;
bool isOpen;
Doormessage doormessage;
try {
token = getJsonOrFail<std::string>(root, _tokenKey);
doormessage.isLockButton = getJsonOrFail<bool>(root, _lockButtonKey);
doormessage.isUnlockButton = getJsonOrFail<bool>(root, _unlockButtonKey);
doormessage.isEmergencyUnlock = getJsonOrFail<bool>(root, _emergencyUnlockKey);
isOpen = getJsonOrFail<bool>(root, _isOpenKey);
}
catch (const std::exception &ex) {
throw Response(Response::Code::JsonError, ex.what());
}
return Clientmessage(token, isOpen, doormessage);
}
Clientmessage Clientmessage::fromString(const std::string &string)
{
Json::Reader reader;
Json::Value root;
if (reader.parse(string, root) == false)
throw Response(Response::Code::NotJson,
"No valid JSON");
return fromJson(root);
}
bool Clientmessage::isOpen() const
{
return _isOpen;
}

View File

@ -1,46 +0,0 @@
#ifndef CLIENTMESSAGE_H
#define CLIENTMESSAGE_H
#include <regex>
#include <string>
#include <json/json.h>
#include "doormessage.h"
class Clientmessage
{
public:
Clientmessage(std::string web_address,
bool isOpen,
Doormessage doormessage);
Clientmessage();
Clientmessage &operator=(const Clientmessage& rhs);
static Clientmessage fromJson(const Json::Value &root);
static Clientmessage fromString(const std::string &json);
std::string toJson() const;
const std::string& web_address() const;
const std::string& token() const;
bool isOpen() const;
const Doormessage& doormessage() const;
private:
std::string _web_address;
std::string _token;
bool _isOpen;
Doormessage _doormessage;
const static std::string _tokenKey;
const static std::string _unlockButtonKey;
const static std::string _lockButtonKey;
const static std::string _emergencyUnlockKey;
const static std::string _isOpenKey;
const static std::regex _token_regex;
};
#endif

View File

@ -1,208 +0,0 @@
#include "config.h"
#include "door.h"
#include "../../doorcmds.h"
Door::Door(const std::string &serDev,
unsigned int baudrate,
const std::string &logfile_scripts) :
_baudrate(baudrate),
_port(_ioService, serDev),
_logfile_scripts(logfile_scripts),
_logger(Logger::get())
{
// Configure serial port
_port.set_option(boost::asio::serial_port_base::baud_rate(baudrate));
_port.set_option(boost::asio::serial_port_base::character_size(8));
_port.set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));
_port.set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none));
_port.set_option(boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::none));
_asyncRead();
_ioThread = std::thread([this] () {
_ioService.run();
});
// TODO Ping device
}
Door::~Door()
{
lock();
_ioService.stop();
_ioService.reset();
_port.cancel();
_port.close();
_ioThread.join();
}
bool Door::_readByte(char &byte, std::chrono::milliseconds timeout)
{
std::unique_lock<std::mutex> lock(_receiveLock);
_receivedCondition.wait_for(lock, timeout);
if (_byteReady) {
byte = recvBuf;
_byteReady = false;
return true;
}
return false;
}
void Door::_asyncRead()
{
_port.async_read_some(
boost::asio::buffer(&recvBuf, sizeof(recvBuf)),
[this] (const boost::system::error_code &ec, size_t bytes_transferred) {
if (ec) {
// Operation canceled occurs on system shutdown
// So we return without invoking an additional asyncRead()
if (ec == boost::system::errc::operation_canceled)
return;
_logger(LogLevel::error, "Serialport error: %s", ec.message().c_str());
goto out;
}
if (bytes_transferred != 1) {
_logger(LogLevel::error, "Fatal serial error");
goto out;
}
if (recvBuf == DOOR_BUTTON_UNLOCK) {
// In case that someone pushed the unlock button - just log it.
// No further actions required
_logger(LogLevel::notice, "Someone pushed the unlock button");
if (_doorCallback) {
_doorCallback(Doormessage(true, false, false));
}
goto out;
} else if (recvBuf == DOOR_BUTTON_LOCK) {
_logger(LogLevel::notice, "Someone pushed the lock button");
_logger(LogLevel::notice, "Locking...");
lock();
if (_doorCallback) {
_doorCallback(Doormessage(false, true, false));
}
goto out;
} else if (recvBuf == DOOR_EMERGENCY_UNLOCK) {
_logger(LogLevel::warning, "Someone did an emergency unlock!");
_exec_and_log(EMERGENCY_UNLOCK_SCRIPT);
if (_doorCallback) {
_doorCallback(Doormessage(false, false, true));
}
goto out;
}
_byteReady = true;
_receivedCondition.notify_one();
out:
_asyncRead();
});
}
Door::State Door::state() const
{
return _state;
}
void Door::lock()
{
_stateMutex.lock();
_logger(LogLevel::notice, "Executing Pre Lock Script");
_exec_and_log(PRE_LOCK_SCRIPT);
if (_state == State::Locked) {
_stateMutex.unlock();
_logger(LogLevel::info, "Door already closed");
goto out;
}
_state = State::Locked;
_stateMutex.unlock();
_heartbeatCondition.notify_one();
_heartbeatThread.join();
_logger(LogLevel::notice , "Door closed");
out:
_logger(LogLevel::notice, "Executing Post Lock Script");
_exec_and_log(POST_LOCK_SCRIPT);
}
void Door::unlock()
{
_stateMutex.lock();
_schnapper = true;
_logger(LogLevel::notice, "Executing Pre Unlock Script");
_exec_and_log(PRE_UNLOCK_SCRIPT);
if(_state == State::Unlocked) {
_stateMutex.unlock();
_logger(LogLevel::info, "Door already opened");
goto out;
}
_state = State::Unlocked;
_stateMutex.unlock();
_heartbeatThread = std::thread([this] () {
std::unique_lock<std::mutex> lock(_heartbeatMutex);
while (_state == State::Unlocked) {
if (_state == State::Unlocked) {
_writeCMD(DOOR_CMD_UNLOCK);
if (_schnapper) {
_schnapper = false;
_writeCMD(DOOR_CMD_SCHNAPER);
}
}
_heartbeatCondition.wait_for(lock, Milliseconds(400));
}
_writeCMD(DOOR_CMD_LOCK);
});
_logger(LogLevel::notice, "Door opened");
out:
_logger(LogLevel::notice, "Executing Post Unlock Script");
_exec_and_log(POST_UNLOCK_SCRIPT);
}
bool Door::_writeCMD(char c)
{
std::lock_guard<std::mutex> l(_serialMutex);
_port.write_some(boost::asio::buffer(&c, sizeof(c)));
char response;
if (_readByte(response, Milliseconds(100)))
{
if (c != response) {
_logger(LogLevel::error, "Sent command '%c' but got response '%c'", c, response);
return false;
}
return true;
}
_logger(LogLevel::error, "Sent Serial command, but got no response!");
return false;
}
void Door::setDoorCallback(DoorCallback doorCallback)
{
_doorCallback = doorCallback;
}
void Door::_exec_and_log(const std::string &filename)
{
const std::string cmd = "nohup " + filename + " &>> " + _logfile_scripts;
::system(cmd.c_str());
}

View File

@ -1,80 +0,0 @@
#ifndef DOOR_H
#define DOOR_H
#include <chrono>
#include <condition_variable>
#include <functional>
#include <mutex>
#include <string>
#include <thread>
#include <boost/asio.hpp>
#include <boost/asio/serial_port.hpp>
#include "logger.h"
#include "doormessage.h"
class Door final
{
public:
using DoorCallback = std::function<void(Doormessage)>;
enum class State {Unlocked, Locked};
Door(const std::string &serDev,
unsigned int baudrate,
const std::string &logfile_scripts);
~Door();
State state() const;
void setDoorCallback(DoorCallback doorCallback);
void lock();
void unlock();
private:
using Milliseconds = std::chrono::milliseconds;
const unsigned int _baudrate;
// To prevent concurrent writes
std::mutex _serialMutex = { };
boost::asio::io_service _ioService = { };
boost::asio::serial_port _port;
const std::string _logfile_scripts;
std::mutex _stateMutex = { };
volatile State _state = { State::Locked };
std::thread _heartbeatThread = { };
std::mutex _heartbeatMutex = { };
std::condition_variable _heartbeatCondition = { };
std::thread _ioThread = { };
void _asyncRead();
volatile bool _schnapper = { false };
// Indicates if recvBuf contains a valid response from AVR Board
volatile bool _byteReady = { false };
// Actual response
char recvBuf = { };
std::condition_variable _receivedCondition = { };
std::mutex _receiveLock = { };
DoorCallback _doorCallback = { };
Logger &_logger;
// Writed command to AVR board
bool _writeCMD(char c);
// Receives one byte and returns true or returns false on timeout
bool _readByte(char &byte, Milliseconds timeout);
void _exec_and_log(const std::string &filename);
};
#endif

View File

@ -1,12 +0,0 @@
#include "doormessage.h"
Doormessage::Doormessage()
{
}
Doormessage::Doormessage(bool isUnlockButton, bool isLockButton, bool isEmergencyUnlock) :
isUnlockButton(isUnlockButton),
isLockButton(isLockButton),
isEmergencyUnlock(isEmergencyUnlock)
{
}

View File

@ -1,17 +0,0 @@
#ifndef DOORMESSAGE_H
#define DOORMESSAGE_H
struct Doormessage
{
Doormessage(bool isUnlockButton,
bool isLockButton,
bool isEmergencyUnlock);
Doormessage();
bool isUnlockButton = { false };
bool isLockButton = { false };
bool isEmergencyUnlock = { false };
};
#endif

View File

@ -1,232 +0,0 @@
// (c) 2015 Ralf Ramsauer - Logger class from the sdfs project
#include <iostream>
#include <cstdarg>
#include <ctime>
#include <iomanip>
#include <sstream>
#include <map>
#include <mutex>
#ifdef _WIN32
#include <Windows.h>
#endif
#include "config.h"
#include "logger.h"
#ifdef USE_COLORIZED_LOGS
#ifdef _WIN32 // If Windows, Use Windows Terminal coloring
const static map<LogLevel, WORD> colorAttribute = {
{LogLevel::error, FOREGROUND_RED },
{LogLevel::warning, FOREGROUND_RED | FOREGROUND_GREEN },
{LogLevel::notice, FOREGROUND_BLUE },
{LogLevel::info, FOREGROUND_GREEN },
{LogLevel::debug, FOREGROUND_BLUE | FOREGROUND_RED },
{LogLevel::debug2, FOREGROUND_BLUE | FOREGROUND_RED },
};
#else // Use ANSI Escape sequences
const static std::map<LogLevel, std::string> prefix_ansicolor = {
{LogLevel::error, "\x1b[1m\x1b[31m" },
{LogLevel::warning, "\x1b[1m\x1b[33m" },
{LogLevel::notice, "\x1b[1m\x1b[34m" },
{LogLevel::info, "\x1b[1m\x1b[32m" },
{LogLevel::debug, "\x1b[1m\x1b[35m" },
{LogLevel::debug2, "\x1b[1m\x1b[36m" },
};
const static std::string suffix_ansireset = "\x1b[0m";
#endif
#endif
const static std::map<LogLevel, std::string> logLevel = {
{LogLevel::error, "ERROR " },
{LogLevel::warning, "WARNING" },
{LogLevel::notice, "NOTICE " },
{LogLevel::info, "INFO " },
{LogLevel::debug, "DEBUG " },
{LogLevel::debug2, "DEBUG2 " },
};
Logger::Logger(const LogLevel level) :
_level(level),
_ostreamMutex()
{
}
Logger::~Logger()
{
if (_logFile.is_open())
_logFile.close();
}
Logger &Logger::get()
{
static Logger l(DEFAULT_LOG_LEVEL);
return l;
}
void Logger::operator ()(const std::string &message,
const LogLevel level)
{
if(level > _level)
{
return;
}
std::ostringstream prefix;
auto t = std::time(nullptr);
auto tm = *std::localtime(&t);
#if defined(USE_COLORIZED_LOGS) && !defined(_WIN32)
prefix << prefix_ansicolor.at(level);
#endif
// GCC does not support put_time :-(
/*stringstream ss;
ss << std::put_time(&tm, "%d-%m-%Y %H-%M-%S");
prefix = "[" + ss.str() + "] ";*/
const size_t BUFFER_SIZE = 80;
char timeBuffer[BUFFER_SIZE];
std::strftime(timeBuffer, BUFFER_SIZE, "%Y-%m-%d %H:%M:%S", &tm);
prefix << "[" << timeBuffer << "] -- " << logLevel.at(level) << " :: ";
// Critical section
{
std::lock_guard<std::mutex> lock(_ostreamMutex);
#if defined(USE_COLORIZED_LOGS) && !defined(_WIN32)
if (_consoleActive) {
std::cerr << prefix.str() << message
<< suffix_ansireset << std::endl;
std::cerr.flush();
}
if (_logFileActive && _logFile.is_open()) {
_logFile << prefix.str() << message
<< suffix_ansireset << std::endl;
}
#elif defined(USE_COLORIZED_LOGS) && defined(_WIN32)
if (_consoleActive) {
// taken from GTEST
const HANDLE stdout_handle = GetStdHandle(STD_ERROR_HANDLE);
// Gets the current text color.
CONSOLE_SCREEN_BUFFER_INFO buffer_info;
GetConsoleScreenBufferInfo(stdout_handle, &buffer_info);
const WORD old_color_attrs = buffer_info.wAttributes;
// We need to flush the stream buffers into the console before each
// SetConsoleTextAttribute call lest it affect the text that is already
// printed but has not yet reached the console.
std::cerr.flush();
SetConsoleTextAttribute(stdout_handle,
colorAttribute.at(level) | FOREGROUND_INTENSITY);
std::cerr << prefix.str() << message << std::endl;
std::cerr.flush();
// Restores the text color.
SetConsoleTextAttribute(stdout_handle, old_color_attrs);
}
if (_logFileActive && _logFile.is_open()) {
_logFile << prefix.str() << message << std::endl;
}
#else
if (_consoleActive) {
std::cerr << prefix.str() << message << std::endl;
std::cerr.flush();
}
if (_logFileActive && _logFile.is_open()) {
_logFile << prefix.str() << message << std::endl;
}
#endif
}
}
void Logger::operator ()(const std::ostringstream &message, const LogLevel level)
{
(*this)(message.str(), level);
}
void Logger::operator ()(const LogLevel level, const char* format, ...)
{
if(level > _level)
{
return;
}
va_list argp;
char* message = nullptr;
// determine buffer length
va_start(argp, format);
int size = vsnprintf(nullptr, 0, format, argp) + 1;
va_end(argp);
if (size >= 0)
{
message = (char*)malloc(size);
if (message == nullptr)
{
(*this)("[LOGGER] CRITICAL: MEMORY ALLOCATION ERROR",
LogLevel::error);
}
va_start(argp,format);
vsnprintf(message, size, format, argp);
va_end(argp);
(*this)(std::string(message), level);
free(message);
} else {
(*this)("[LOGGER] CRITICAL: VSNPRINTF ERROR",
LogLevel::error);
}
}
void Logger::level(const LogLevel level)
{
_level = level;
}
LogLevel Logger::level() const
{
return _level;
}
bool Logger::console() const
{
return _consoleActive;
}
void Logger::console(const bool active)
{
std::lock_guard<std::mutex> lock(_ostreamMutex);
_consoleActive = active;
}
bool Logger::logFile() const
{
return _logFileActive;
}
void Logger::logFile(const bool active)
{
std::lock_guard<std::mutex> lock(_ostreamMutex);
_logFileActive = active;
}
void Logger::setLogFile(const std::string &logFile)
{
if (_logFile.is_open())
_logFile.close();
_logFile.open(logFile, std::ofstream::out | std::ofstream::app);
if (!_logFile.is_open())
throw std::runtime_error("Unable to open " + logFile);
}

View File

@ -1,75 +0,0 @@
#ifndef LOGGER_H
#define LOGGER_H
#include <fstream>
#include <ostream>
#include <string>
#include <sstream>
#include <mutex>
/**
* @brief The LogLevel enum
*/
enum class LogLevel : unsigned char
{
off, /// disable logging
error, /// error conditions
warning, /// warning conditions
notice, /// normal but significant conditions
info, /// informational messages
debug, /// debug-level messages
debug2 /// more debug-level messages
};
/**
* @brief The Logger class
*
* The logger class is a thread-safe class which is used for formatting and forwarding
* log messages.
*/
class Logger final
{
public:
static Logger &get();
/// Log a string
void operator() (const std::string &message, const LogLevel level = LogLevel::debug);
/// Log a ostringstream
void operator() (const std::ostringstream &message, const LogLevel level = LogLevel::debug);
/// Log a format string
void operator() (const LogLevel level, const char* format, ...);
/// Set minimum required log level to generate output
void level(const LogLevel level);
/// Get minimum required log level to generate output
LogLevel level() const;
/// Getter/Setter for console output
bool console() const;
void console(const bool active);
/// Getter/Setter for logfile output
bool logFile() const;
void logFile(const bool active);
void setLogFile(const std::string &logFile);
private:
/**
* @brief Constructor
* @param level Minimum required log level to generate output
*/
Logger(const LogLevel level);
~Logger();
bool _consoleActive = { true };
bool _logFileActive = { false };
std::ofstream _logFile = {};
LogLevel _level;
mutable std::mutex _ostreamMutex;
};
#endif

View File

@ -1,263 +0,0 @@
#include <errno.h>
#define LDAP_DEPRECATED 1
#include <ldap.h>
#include "logic.h"
#include "util.h"
Logic::Logic(const std::chrono::seconds tokenTimeout,
const std::string &ldapUri,
const std::string &bindDN,
const std::string &webPrefix,
const unsigned int tokenLength,
const std::string &serDev,
const unsigned int baudrate,
const std::string &logfile_scripts,
std::condition_variable &onClientUpdate) :
_logger(Logger::get()),
_door(serDev, baudrate, logfile_scripts),
_tokenTimeout(tokenTimeout),
_onClientUpdate(onClientUpdate),
_ldapUri(ldapUri),
_bindDN(bindDN),
_webPrefix(webPrefix),
_tokenLength(tokenLength)
{
srand(time(NULL));
_createNewToken(false);
_door.setDoorCallback(std::bind(&Logic::_doorCallback,
this,
std::placeholders::_1));
_tokenUpdater = std::thread([this] () {
while (_run)
{
std::unique_lock<std::mutex> l(_mutex);
_tokenCondition.wait_for(l, _tokenTimeout);
if (_run == false)
{
break;
} else {
_createNewToken(true);
}
}
});
}
Logic::~Logic()
{
_run = false;
_tokenCondition.notify_one();
_tokenUpdater.join();
}
Response Logic::processDoor(const DoorCommand &doorCommand)
{
Response response;
switch (doorCommand) {
case DoorCommand::Lock:
response = _lock();
break;
case DoorCommand::Unlock:
response = _unlock();
break;
default:
response.code = Response::Code::UnknownCommand;
response.message = "Unknown DoorCommand";
break;
}
return response;
}
Response Logic::request(const Request &request)
{
std::unique_lock<std::mutex> l(_mutex);
Response response;
DoorCommand doorCommand;
switch (request.command) {
case Request::Command::Lock:
doorCommand = DoorCommand::Lock;
break;
case Request::Command::Unlock:
doorCommand = DoorCommand::Unlock;
break;
default:
response.code = Response::Code::UnknownCommand;
response.message = "Unknown Command";
goto out;
}
response = _checkToken(request.token);
if (!response) {
goto out;
}
_logger(LogLevel::info, " -> Token check successful");
response = _checkLDAP(request.user, request.password);
if (!response) {
goto out;
}
_logger(LogLevel::info, " -> LDAP check successful");
response = processDoor(doorCommand);
_logger(LogLevel::info, " -> Door Command successful");
out:
return response;
}
Response Logic::_lock()
{
Response response;
if (_door.state() == Door::State::Locked)
{
response.code = Response::Code::AlreadyLocked;
response.message = "Unable to lock: already closed";
} else {
_createNewToken(false);
response.code = Response::Code::Success;
response.message = "Success";
}
_door.lock();
return response;
}
Response Logic::_unlock()
{
Response response;
const auto oldState = _door.state();
_door.unlock();
_createNewToken(false);
if (oldState == Door::State::Unlocked)
{
response.code = Response::Code::AlreadyUnlocked;
response.message = "Unable to unlock: already unlocked";
_logger(response.message, LogLevel::info);
} else {
response.code = Response::Code::Success;
response.message = "Success";
}
return response;
}
Response Logic::_checkToken(std::string token) const
{
std::transform(token.begin(),
token.end(),
token.begin(),
::toupper);
if (token == _curToken)
return Response(Response::Code::Success);
if (_prevValid == true && token == _prevToken)
return Response(Response::Code::Success);
_logger("Check Token failed: got \"" + token
+ "\", expected \"" + _curToken +"\"",
LogLevel::error);
return Response(Response::InvalidToken,
"User provided invalid token");
}
Response Logic::_checkLDAP(const std::string &user,
const std::string &password)
{
constexpr int BUFFERSIZE = 1024;
char buffer[BUFFERSIZE];
Response retval;
int rc = -1;
LDAP* ld = nullptr;
unsigned long version = LDAP_VERSION3;
_logger(LogLevel::info, " Trying to authenticate as user \"%s\"", user.c_str());
snprintf(buffer, BUFFERSIZE, _bindDN.c_str(), user.c_str());
rc = ldap_initialize(&ld, _ldapUri.c_str());
if(rc != LDAP_SUCCESS)
{
retval.message = (std::string)"LDAP initialize error: "
+ ldap_err2string(rc);
retval.code = Response::Code::LDAPInit;
goto out2;
}
rc = ldap_set_option(ld,
LDAP_OPT_PROTOCOL_VERSION,
(void*)&version);
if (rc != LDAP_SUCCESS)
{
retval.code = Response::Code::LDAPInit;
retval.message = "LDAP set version failed";
goto out;
}
rc = ldap_simple_bind_s(ld, buffer, password.c_str());
if (rc != LDAP_SUCCESS)
{
retval = Response::Code::InvalidCredentials;
retval.message = "Credential check for user \"" + user
+ "\" failed: " + ldap_err2string(rc);
goto out;
}
retval.code = Response::Code::Success;
retval.message = "";
out:
ldap_unbind(ld);
ld = nullptr;
out2:
return retval;
}
void Logic::_createNewToken(const bool stillValid)
{
// Todo Mutex einführen
_prevToken = _curToken;
_prevValid = stillValid;
_curToken = randHexString(_tokenLength);
std::ostringstream message;
message << "New token: " << _curToken
<< " old token: " << _prevToken << " is "
<< (_prevValid?"still":"not") << " valid";
_logger(message, LogLevel::notice);
_onClientUpdate.notify_all();
}
Clientmessage Logic::getClientMessage()
{
std::lock_guard<std::mutex> l(_mutex);
Clientmessage retval(_webPrefix + _curToken,
_door.state() == Door::State::Unlocked,
_doormessage);
// Reset doormessage
_doormessage = Doormessage();
return retval;
}
void Logic::_doorCallback(Doormessage doormessage)
{
std::lock_guard<std::mutex> l(_mutex);
_doormessage = doormessage;
_onClientUpdate.notify_all();
}

View File

@ -1,105 +0,0 @@
#ifndef LOGIC_H
#define LOGIC_H
#include <condition_variable>
#include <mutex>
#include <string>
#include <thread>
#include "config.h"
#include "clientmessage.h"
#include "door.h"
#include "logger.h"
#include "request.h"
#include "response.h"
/* The "Logic" class
*
* This class is initilized by all settings.
*
* It handles incoming requests and allows modifications of the door
*/
class Logic
{
public:
Logic(const std::chrono::seconds tokenTimeout,
const std::string &ldapUri,
const std::string &bindDN,
const std::string &webPrefix,
const unsigned int tokenLength,
const std::string &serDev,
const unsigned int baudrate,
const std::string &logfile_scripts,
std::condition_variable &onClientUpdate);
~Logic();
// Parse incoming JSON Requests
Response request(const Request &request);
// Send direct command to door without credential checks
enum class DoorCommand { Lock, Unlock };
Response processDoor(const DoorCommand &doorCommand);
// Returns the current Token
Clientmessage getClientMessage();
private:
// Internal lock wrapper
Response _lock();
// Internal unlock wrapper
Response _unlock();
// Checks if the incoming token is valid
Response _checkToken(std::string token) const;
// Checks if incoming credentials against LDAP
Response _checkLDAP(const std::string &user,
const std::string &password);
// Creates a new random token and draws it on the epaper.
// stillValid indicates whether the old (previous) token is still valid
void _createNewToken(const bool stillValid);
void _doorCallback(Doormessage doormessage);
Logger &_logger;
// The door
Door _door;
// The current token
std::string _curToken = {};
// The previous token
std::string _prevToken = {};
// Indicates whether the previous token is valid
bool _prevValid = { false };
// Tokens are refreshed all tokenTimout seconds
const std::chrono::seconds _tokenTimeout;
// Thread for asynchronosly updating tokens
std::thread _tokenUpdater = {};
// Thread can be force-triggered for updates using the condition variable
std::condition_variable _tokenCondition = {};
// stop indicator for the thread
bool _run = true;
// General mutex for concurrent data access
mutable std::mutex _mutex = {};
Doormessage _doormessage = {};
// This variable gets notified on token updates
std::condition_variable &_onClientUpdate;
// The URI of the ldap server
const std::string _ldapUri;
// LDAP bindDN
const std::string _bindDN;
// Prefix of the website
const std::string _webPrefix;
// Length of the token in bytes
const unsigned int _tokenLength;
};
#endif

View File

@ -1,69 +0,0 @@
#include "request.h"
#include "util.h"
#include "logger.h"
const std::string Request::_commandKey = "command";
Request::Command Request::_commandFromString(const std::string &command)
{
Command retval = Command::Unknown;
if (command == "lock")
retval = Command::Lock;
else if (command == "unlock")
retval = Command::Unlock;
else if (command == "subscribe")
retval = Command::Subscribe;
else
retval = Command::Unknown;
return retval;
}
Request Request::fromJson(const Json::Value &root)
{
Request retval;
auto &l = Logger::get();
try {
const auto commandStr =
getJsonOrFail<std::string>(root, _commandKey);
l(" command: " + commandStr, LogLevel::info);
retval.command = _commandFromString(commandStr);
// Stop parsing, if command is unknown
if (retval.command == Command::Unknown)
return retval;
if (retval.command == Command::Lock ||
retval.command == Command::Unlock) {
retval.user = getJsonOrFail<std::string>(root, "user");
l(" user: " + retval.user, LogLevel::info);
retval.password = getJsonOrFail<std::string>(root, "password");
l(" password: XXX", LogLevel::info);
retval.token = getJsonOrFail<std::string>(root, "token");
l(" token: " + retval.token, LogLevel::info);
}
if (retval.command == Command::Subscribe) {
// Nothing to do in this case
}
}
catch (const std::exception &ex) {
throw Response(Response::Code::JsonError, ex.what());
}
return retval;
}
Request Request::fromString(const std::string &string)
{
Json::Reader reader;
Json::Value root;
if (reader.parse(string, root) == false)
throw Response(Response::Code::NotJson,
"No valid JSON");
return fromJson(root);
}

View File

@ -1,29 +0,0 @@
#ifndef REQUEST_H
#define REQUEST_H
#include <string>
#include <json/json.h>
#include "response.h"
class Request
{
public:
static Request fromJson(const Json::Value &root);
static Request fromString(const std::string &string);
enum class Command { Lock, Unlock, Subscribe, Unknown }
command = { Command::Unknown };
std::string user = { };
std::string password = { };
std::string token = { };
private:
static Command _commandFromString(const std::string &command);
const static std::string _commandKey;
};
#endif // REQUEST_H

View File

@ -1,69 +0,0 @@
#include <exception>
#include <json/json.h>
#include "response.h"
#include "util.h"
const std::string Response::_codeKey = "code";
const std::string Response::_messageKey = "message";
Response::Response():
code(Fail),
message("General failure")
{
}
Response::Response(Response::Code code, const std::string &what) :
code(code),
message(what)
{
}
Response::operator bool() const
{
return code == Response::Code::Success;
}
std::string Response::toJson() const
{
Json::Value response;
Json::StyledWriter writer;
response[_codeKey] = code;
response[_messageKey] = message;
return writer.write(response);
}
Response Response::fromJson(const Json::Value &root)
{
Response retval;
try {
retval.message = getJsonOrFail<std::string>(root, _messageKey);
const auto code = getJsonOrFail<int>(root, _codeKey);
if (code > Code::RESPONSE_NUM_ITEMS)
throw std::runtime_error("Error code out of range");
retval.code = static_cast<Code>(code);
}
catch (const std::exception &ex) {
throw Response(Response::Code::JsonError, ex.what());
}
return retval;
}
Response Response::fromString(const std::string &json)
{
Json::Reader reader;
Json::Value root;
if (reader.parse(json, root) == false)
throw Response(Response::Code::NotJson,
"No valid JSON");
return fromJson(root);
}

View File

@ -1,44 +0,0 @@
#ifndef RESPONSE_H
#define RESPONSE_H
#include <string>
#include <json/json.h>
class Response
{
public:
enum Code {
Success = 0, // Request successful
Fail, // General non-specified error
AlreadyUnlocked, // Authentication successful, but door is already unlocked
AlreadyLocked, // Authentication successful, but door is already locked
NotJson, // Request is not a valid JSON object
JsonError, // Request is valid JSON, but does not contain necessary material
InvalidToken, // Request contains invalid token
InvalidCredentials, // Invalid LDAP credentials
InvalidIP, // IP check failure
UnknownCommand, // Unknown action
LDAPInit, // Ldap initialization failed
AccessDenied, // Access denied
RESPONSE_NUM_ITEMS
} code;
std::string message;
Response();
Response(Response::Code code, const std::string &message = "");
static Response fromJson(const Json::Value &root);
static Response fromString(const std::string &json);
std::string toJson() const;
// Returns true if code is success
operator bool() const;
private:
const static std::string _codeKey;
const static std::string _messageKey;
};
#endif // RESPONSE_H

View File

@ -1,76 +0,0 @@
#include "util.h"
template <>
int getJson(const Json::Value &root, const std::string &key)
{
auto val = root.get(key, Json::Value());
if (val.isInt())
return val.asInt();
throw std::runtime_error("Json Type error");
}
template <>
std::string getJson(const Json::Value &root, const std::string &key)
{
auto val = root.get(key, Json::Value());
if (val.isString())
return val.asString();
throw std::runtime_error("Json Type error");
}
template <>
size_t getJson(const Json::Value &root, const std::string &key)
{
auto val = root.get(key, Json::Value());
if (val.isInt())
return val.asUInt64();
throw std::runtime_error("Json Type error");
}
template <>
bool getJson(const Json::Value &root, const std::string &key)
{
auto val = root.get(key, Json::Value());
if (val.isBool())
return val.asBool();
throw std::runtime_error("Json Type error");
}
template <>
Json::Value getJson(const Json::Value &root, const std::string &key)
{
auto val = root.get(key, Json::Value());
return val;
}
static char nibble2hex(unsigned char input)
{
input &= 0xf;
if(input <= 9)
{
return input + '0';
}
return input - 0xA + 'A';
}
std::string randHexString(unsigned int len)
{
std::string retval;
while (len--)
retval += nibble2hex(rand() & 0xF);
return retval;
}
unsigned char hex2uchar(const char input)
{
if(input >= '0' && input <= '9') {
return input - '0';
} else if(input >= 'A' && input <= 'F') {
return input - 'A' + 10;
} else if(input >= 'a' && input <= 'f') {
return input - 'a' + 10;
}
throw std::runtime_error("Malformed hexadecimal input");
}

View File

@ -1,28 +0,0 @@
#ifndef UTIL_H
#define UTIL_H
#include <algorithm>
#include <exception>
#include <json/json.h>
template <typename T>
T getJson(const Json::Value &root, const std::string &key);
template <typename T>
static T getJsonOrFail(const Json::Value &root, const std::string &key)
{
if (root.isObject() == false)
{
throw std::runtime_error("Invalid Json Object");
}
if (root.isMember(key) == false) {
throw std::runtime_error("Json key \"" + key + "\" not existing");
}
return getJson<T>(root, key);
}
std::string randHexString(unsigned int len);
#endif

View File

@ -1,13 +0,0 @@
[Unit]
Description=Binary Kitchen doorlockd service
After=network.target
[Service]
User=root
Group=root
EnvironmentFile=-/etc/sysconfig/doorlockd
ExecStart=/usr/local/sbin/doorlockd
[Install]
WantedBy=multi-user.target

View File

@ -1 +0,0 @@
#!/bin/bash

View File

@ -1,3 +0,0 @@
#!/bin/bash
mosquitto_pub -t kitchen/frontdoor/status -m "locked"

View File

@ -1,8 +0,0 @@
#!/bin/bash
mosquitto_pub -t kitchen/frontdoor/status -m "unlocked"
# Wakeup cashdesk
wol -i 172.23.2.44 00:0b:ca:94:13:f1
wol -i 172.23.2.44 00:0b:ca:94:13:f1
wol -i 172.23.2.44 00:0b:ca:94:13:f1

View File

@ -1 +0,0 @@
#!/bin/bash

View File

@ -1 +0,0 @@
#!/bin/bash

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,23 +0,0 @@
#!/bin/bash
echo -n "Cmd: "
read CMD
echo -n "User: "
read USER
echo -n "Password: "
read -s PW
echo
echo -n "Token: "
read TOKEN
cat << EOF | nc localhost 5555
{
"command" : "$CMD",
"user" : "$USER",
"password" : "$PW",
"token" : "$TOKEN"
}
EOF

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

Before

Width:  |  Height:  |  Size: 869 B

After

Width:  |  Height:  |  Size: 869 B

View File

@ -1,171 +0,0 @@
<?php
function tellLock($pCommand, $pUser, $pPass, $pToken, $pIp){
$json = '{
"user":' . json_encode( $pUser ) . ',
"password":' . json_encode( $pPass ) . ',
"command":' . json_encode( $pCommand ) . ',
"token":' . json_encode( $pToken ) . ',
"ip":' . json_encode( $pIp ) . '
}'."\n";
$address = "127.0.0.1";
$port = "5555";
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false) {
echo "socket_create() failed: " . socket_strerror(socket_last_error()) . "\n";
}
$result = socket_connect($socket, $address, $port);
if ($result === false) {
echo "socket_connect() failed: ($result) " . socket_strerror(socket_last_error($socket)) . "\n";
}
socket_write($socket, $json, strlen($json));
$result = socket_read($socket, 1024);
socket_close($socket);
return $result;
}
$showLoginForm = false;
$showSuccess = false;
$showFailure = false;
$showTokenForm = false;
$isApi = false;
$pIp = $_SERVER[ 'REMOTE_ADDR' ];
if( $_SERVER[ 'REQUEST_METHOD' ] == "POST" ) {
if (array_key_exists("user", $_POST)
&& array_key_exists('pass', $_POST)
&& array_key_exists('token', $_POST)
&& array_key_exists('command', $_POST)
&& array_key_exists('api', $_POST))
{
$pUser = $_POST[ 'user' ];
$pPass = $_POST[ 'pass' ];
$pToken = $_POST[ 'token' ];
$pCommand = $_POST[ 'command' ];
$pApi = $_POST[ 'api' ];
if ($pApi == "true")
{
$isApi = true;
}
$jsonResponse = json_decode(tellLock($pCommand, $pUser, $pPass, $pToken, $pIp), true);
if ($jsonResponse == null || !isset($jsonResponse['message']) || !isset($jsonResponse['code'])) {
$showFailure = true;
$failureMsg = 'Error parsing JSON response';
} else {
$failureMsg = $jsonResponse['message'];
$code = $jsonResponse['code'];
$showSuccess = ($code == 0);
$showFailure = !$showSuccess;
}
} else {
$failureMsg = 'Invalid Request';
$showFailure = true;
}
} else {
// This is done by apache mod_rewrite
$pToken = $_GET['token'];
$lToken = $str= ltrim ($pToken, '/');
if(strlen($lToken) == 0) {
$showLoginForm = true;
$showTokenForm = true;
} else {
$showLoginForm = true;
}
}
if ($isApi == false) {
?>
<!DOCTYPE html>
<html>
<head>
<title>Login</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
</head>
<body>
<style>
* {
font: normal 30px Arial,sans-serif;
}
body {
background-color: #037;
color: white;
background-image: url('logo.svg' );
background-repeat: repeat;
background-size: 300%;
background-position: -200px -100px;
}
form {
position: relative;
display: block;
width: auto;
text-align: center;
}
input {
position: relative;
display: block;
width: auto;
width: 100%;
}
button {
width: 100%;
margin-top: 40px;
}
</style>
<?php if ($showLoginForm): ?>
<form name="login" method="post" action="/">
<?php if ($showTokenForm): ?>
<label for="token">Token</label>
<input type="text" name="token" value="">
<?php else: ?>
<input type="hidden" name="token" value="<?php echo $lToken;?>">
<?php endif; ?>
<input type="hidden" name="api" value="false">
<label for="user">User</label>
<input id="user" type="text" name="user">
<label for="pass">Pass</label>
<input id="pass" type="password" name="pass">
<button name="command" value="unlock">Open</button>
<hr/>
<button name="command" value="lock">Lock</button>
</form>
<?php elseif( $showSuccess ): ?>
<h1>Welcome Cpt. Cook</h1>
<?php elseif( $showFailure ): ?>
<h1>Something went wrong: <?php echo $failureMsg; ?></h1>
<?php endif; ?>
</body>
</html>
<?php
} else {
echo $code;
}
?>

View File

@ -1,81 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="200"
height="180"
id="svg4662"
version="1.1"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="New document 4">
<defs
id="defs4664" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.8"
inkscape:cx="150.41655"
inkscape:cy="115.14291"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1031"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1" />
<metadata
id="metadata4667">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-872.36218)">
<g
id="g5022"
transform="matrix(1.2255371,0,0,1.2255371,-230.84023,1130.0139)">
<path
inkscape:connector-curvature="0"
id="path3021"
d="m 244.28546,-205.07285 c -13.55524,0.0103 -24.54012,7.56438 -24.53305,16.88361 0.006,7.85919 7.80317,14.5031 18.40741,16.23346 2.21111,0.3608 3.4598,0.3416 4.77455,0.40635 0.27656,0.0138 0.50974,0.29954 0.49777,0.54856 -0.012,0.24902 -0.272,0.52169 -0.54857,0.50793 -1.96704,-0.0979 -2.21207,-0.0854 -3.67741,-0.22349 l 0,4.96757 -2.48886,0.0101 c -0.96509,0.004 -1.80211,0.7736 -1.74729,1.73712 l 2.05204,36.06308 c 0.0548,0.96352 0.78464,1.66853 1.74728,1.73712 16.97546,1.20947 34.16131,1.28134 51.56512,-0.0406 0.96231,-0.0731 1.68392,-0.7735 1.73713,-1.73712 l 1.99108,-36.06307 c 0.0532,-0.96362 -0.77204,-1.73713 -1.73712,-1.73713 l -1.99109,0 -0.0101,-6.41008 c -0.59278,0.0685 -1.30174,0.1021 -2.28569,0.10158 -0.28484,-1.4e-4 -0.55101,-0.28356 -0.54856,-0.5384 0.003,-0.25484 0.27393,-0.52379 0.55872,-0.51809 1.08555,0.0217 1.80456,0.0155 2.37712,-0.1219 7.72295,-1.8535 13.20034,-6.75436 13.19604,-12.43415 -0.006,-7.34241 -9.17359,-13.28587 -20.46961,-13.2773 -5.9236,0.004 -11.26017,1.64843 -14.99411,4.26661 0.71202,0.9635 1.31636,1.69784 1.99109,2.19426 0.68758,0.50589 1.45609,0.794 2.65139,0.85333 0.27658,0.0137 0.50975,0.29955 0.49778,0.54856 -0.012,0.24902 -0.272,0.52167 -0.54857,0.50793 -1.35281,-0.0672 -2.42214,-0.38317 -3.23044,-1.05649 -0.98588,-0.82125 -1.47763,-1.14899 -2.34664,-2.6514 -3.62458,-6.26641 -12.45917,-10.76586 -22.88737,-10.75795 z m 9.38656,47.75563 3.62662,0.0406 c 0.8264,0.01 1.48105,0.67707 1.473,1.50347 l -0.21333,22.39974 c -0.008,0.82641 -0.68724,1.49312 -1.51363,1.48316 l -3.61647,-0.0406 c -0.82639,-0.01 -1.48104,-0.67707 -1.473,-1.50348 l 0.21334,-22.39974 c 0.008,-0.82641 0.67708,-1.49311 1.50347,-1.48316 z m 19.08803,0.19302 c 4.59464,0.0456 8.26648,5.6641 8.198,12.55604 -0.0685,6.89195 -3.83701,12.43916 -8.43164,12.39351 -4.59463,-0.0456 -8.26648,-5.6641 -8.19801,-12.55604 0.0685,-6.89195 3.83702,-12.43916 8.43165,-12.39351 z"
style="fill:#ccbbbb;fill-opacity:1;fill-rule:nonzero;stroke:none" />
<path
inkscape:connector-curvature="0"
id="use3078"
d="m 200.30883,-134.33843 c -1.39853,0.0404 -3.12049,0.56027 -3.67742,1.91997 l -3.37266,8.22848 c -0.84129,2.05396 1.37784,4.43092 2.53965,4.90661 l 35.56531,14.55729 c 0.38962,-0.3884 0.93039,-0.63562 1.52379,-0.65015 1.22875,-0.0301 2.2556,0.96549 2.28569,2.19426 0.15008,0.0661 0.30962,0.14098 0.43682,0.19302 1.16194,0.47535 2.48075,-0.0876 2.95615,-1.24951 l 1.43237,-3.50473 16.57885,6.78596 4.97772,-2.1841 -20.06326,-8.21832 1.43237,-3.50472 c 0.4754,-1.16193 -0.0761,-2.49428 -1.23935,-2.96632 l -0.43682,-0.17269 c -0.39626,0.46577 -0.98624,0.76607 -1.6457,0.78221 -1.22876,0.0301 -2.29115,-1.05984 -2.26537,-2.39743 -12.25687,-5.07793 -24.37983,-9.98145 -35.46372,-14.5065 -0.36093,-0.14735 -0.92873,-0.23168 -1.56442,-0.21333 z m -0.0813,4.15487 c 0.68786,-0.0126 1.39645,0.0653 1.94029,0.28444 11.52178,4.64268 22.39838,9.14152 33.89929,13.83603 0.33307,0.14692 0.49168,0.61064 0.36571,0.91428 -0.12597,0.30363 -0.56708,0.52359 -0.90411,0.38602 l -33.76723,-13.77508 -0.0101,0 c -1.42005,-0.57618 -2.57387,-0.25535 -3.23044,0.57905 -0.2481,0.31529 -0.77308,0.39811 -1.04634,0.17269 -0.27326,-0.22541 -0.2957,-0.76564 -0.0305,-1.06665 0.83707,-0.94999 1.88567,-1.31433 2.78346,-1.33078 z m -1.09713,6.70468 c 0.0935,-0.006 0.18575,0.0105 0.28444,0.0508 11.49444,4.69486 22.39412,9.17072 33.88913,13.87666 0.34309,0.14046 0.52212,0.61406 0.39619,0.92444 -0.12594,0.31037 -0.58137,0.52653 -0.92444,0.38602 l -33.82818,-13.85635 c -0.38694,-0.15848 -0.54846,-0.61414 -0.42666,-0.93459 0.087,-0.22894 0.32917,-0.42985 0.60952,-0.44698 z m 82.01048,28.36285 -5.31296,2.041882 32.64979,13.368734 c 0.25004,1.079745 3.14683,3.021327 5.22152,3.972018 1.30859,0.599644 2.67177,1.086854 4.0025,1.635536 0.91278,-2.256918 1.83518,-4.51491 2.76314,-6.765636 -1.33528,-0.537581 -2.63001,-1.16066 -3.99234,-1.625378 -1.73375,-0.59141 -5.10658,-1.391879 -6.52183,-0.833006 l -28.80982,-11.79415 z"
style="fill:#ccbbbb;fill-opacity:1;fill-rule:nonzero;stroke:none" />
<g
transform="matrix(0.32303566,-0.14109669,0.14109669,0.32303566,99.129267,-226.69717)"
id="use3083">
<path
style="fill:#ccbbbb;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 900.1875,443.75 -10.0625,0.0312 c -2.06742,0.006 -3.71875,1.65132 -3.71875,3.71875 l 0,6.25 -14.875,9.1875 C 822.66476,464.22979 769.15433,465.92087 718,467.4687 c -3.86018,0.1168 -6.954,3.13809 -6.9375,7 l 0.0312,7.3125 c -4.39379,0.1562 -8.77864,0.31969 -13.125,0.40625 0.97303,-5.55653 -0.58392,-11.07117 -3.84375,-15.125 -3.90015,-4.85012 -9.63745,-7.59553 -16.6875,-8.375 -1.77081,-0.19579 -3.69096,-0.27617 -5.65625,-0.15625 -1.96529,0.11992 -3.96613,0.45055 -5.90625,1.0625 -3.88023,1.22388 -7.73646,3.96623 -9.0625,8.53125 -1.13246,3.89857 -1.08251,8.27896 1.21875,12 2.30126,3.72104 6.66466,6.31703 12.8125,7.25 5.20014,0.78916 10.38378,1.20262 15.5625,1.375 -0.42767,0.29707 -0.71554,0.66894 -1.1875,0.9375 -3.16064,1.79846 -7.2586,2.82575 -11.21875,3 -16.17998,0.71196 -33.27007,1.52537 -49.75,0.90625 l -0.21875,6.5 c 16.86139,0.63344 34.08845,-0.19511 50.25,-0.90625 4.85233,-0.21352 9.86708,-1.40312 14.15625,-3.84375 2.80877,-1.59825 5.26565,-3.83995 7.0625,-6.625 5.2263,-0.0543 10.41482,-0.22176 15.59375,-0.40625 l 0.0312,4.875 c 0.0248,3.86187 3.07592,6.91592 6.9375,6.96875 50.79894,0.69498 105.04842,1.35105 153.5625,3.96875 l 14.875,9.125 0.0312,6.25 c 0.0103,2.06745 1.65133,3.72517 3.71875,3.71875 l 10.0625,-0.0312 c 2.06742,-0.006 3.75283,-1.65128 3.75,-3.71875 l -0.0312,-22.84375 127.21875,-0.25 c 2.4965,-0.005 4.5,-2.00347 4.5,-4.5 l 0,-4.78125 31.7188,-0.0625 c 2.1455,-0.004 8.1914,-1.72948 8.1874,-3.875 0,-2.14552 -6.0732,-3.84798 -8.2187,-3.84375 l -31.7187,0.0625 0,-4.78125 c 0,-2.49653 -2.0348,-4.50429 -4.5313,-4.5 L 904,470.3125 903.9375,447.46875 c -0.006,-2.06743 -1.68258,-3.72517 -3.75,-3.71875 z M 674.5,465.03125 c 0.76399,0.0155 1.53642,0.0461 2.25,0.125 5.62398,0.62179 9.55318,2.43804 12.375,6.0625 2.29548,2.94841 3.4456,6.73828 2.3125,10.96875 -6.61366,-0.0371 -13.17428,-0.27106 -19.625,-1.25 -4.90375,-0.74418 -7.17641,-2.43226 -8.28125,-4.21875 -1.10484,-1.78649 -1.24006,-4.12594 -0.46875,-6.78125 0.62042,-2.13582 2.1484,-3.33567 4.75,-4.15625 1.3008,-0.41029 2.83201,-0.6234 4.375,-0.71875 0.77149,-0.0477 1.54851,-0.0468 2.3125,-0.0312 z"
transform="matrix(0.92218368,0.00169429,-0.00169429,0.92218368,-452.50678,78.944729)"
id="path3858"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sssccssccasssssscssccssccssccsssscsscscscsscssssacssssss" />
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 8.2 KiB