remove old code
Signed-off-by: Ralf Ramsauer <ralf@binary-kitchen.de>
@ -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)
|
@ -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;
|
||||
}
|
@ -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));
|
||||
}
|
@ -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
|
@ -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>
|
@ -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();
|
||||
}
|
@ -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
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
@ -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();
|
||||
}
|
@ -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
|
@ -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
|
@ -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;
|
||||
}
|
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 12 KiB |
@ -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;
|
||||
}
|
@ -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
|
@ -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());
|
||||
}
|
@ -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
|
@ -1,12 +0,0 @@
|
||||
#include "doormessage.h"
|
||||
|
||||
Doormessage::Doormessage()
|
||||
{
|
||||
}
|
||||
|
||||
Doormessage::Doormessage(bool isUnlockButton, bool isLockButton, bool isEmergencyUnlock) :
|
||||
isUnlockButton(isUnlockButton),
|
||||
isLockButton(isLockButton),
|
||||
isEmergencyUnlock(isEmergencyUnlock)
|
||||
{
|
||||
}
|
@ -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
|
@ -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);
|
||||
}
|
@ -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
|
@ -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();
|
||||
}
|
@ -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
|
@ -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);
|
||||
}
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
@ -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
|
@ -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");
|
||||
}
|
@ -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
|
@ -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
|
||||
|
@ -1 +0,0 @@
|
||||
#!/bin/bash
|
@ -1,3 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
mosquitto_pub -t kitchen/frontdoor/status -m "locked"
|
@ -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
|
@ -1 +0,0 @@
|
||||
#!/bin/bash
|
@ -1 +0,0 @@
|
||||
#!/bin/bash
|
@ -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
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 8.2 KiB |
Before Width: | Height: | Size: 869 B After Width: | Height: | Size: 869 B |
@ -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;
|
||||
}
|
||||
?>
|
@ -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 |