spectrum2_manager can run as web-server providing simple web-interface

This commit is contained in:
Jan Kaluza 2012-08-29 10:17:32 +02:00
parent 5daa8bb514
commit 254143e164
8 changed files with 5786 additions and 449 deletions

View file

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 2.6)
FILE(GLOB SRC *.cpp)
FILE(GLOB SRC *.cpp *.c)
ADD_EXECUTABLE(spectrum2_manager ${SRC} ../../src/config.cpp ../../src/util.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../include/transport/protocol.pb.cc)

View file

@ -1,4 +1,6 @@
#include "managerconfig.h"
#include "methods.h"
#include "server.h"
#include "transport/config.h"
#include "transport/protocol.pb.h"
#include "Swiften/Swiften.h"
@ -14,11 +16,6 @@
#include "signal.h"
#include "sys/wait.h"
#define WRAP(MESSAGE, TYPE) pbnetwork::WrapperMessage wrap; \
wrap.set_type(TYPE); \
wrap.set_payload(MESSAGE); \
wrap.SerializeToString(&MESSAGE);
using namespace Transport;
@ -26,447 +23,6 @@ using namespace boost::filesystem;
using namespace boost;
std::string _data;
static std::string searchForBinary(const std::string &binary) {
std::vector<std::string> path_list;
char * env_path = getenv("PATH");
if (env_path != NULL) {
std::string buffer = "";
for (int s = 0; s < strlen(env_path); s++) {
if (env_path[s] == ':') {
path_list.insert(path_list.end(), std::string(buffer));
buffer = "";
}
else {
buffer += env_path[s];
}
}
if (buffer != "") {
path_list.insert(path_list.end(), std::string(buffer));
buffer = "";
}
for (std::vector<std::string>::iterator dit = path_list.begin(); dit < path_list.end(); dit++) {
std::string bpath = *dit;
bpath += "/";
bpath += binary;
path p(bpath);
if (exists(p) && !is_directory(p)) {
return bpath;
}
}
}
return "";
}
// Executes new backend
static unsigned long exec_(std::string path, std::string config, std::string jid = "") {
// fork and exec
pid_t pid = fork();
if ( pid == 0 ) {
// child process
if (jid.empty()) {
exit(execl(path.c_str(), path.c_str(), config.c_str(), NULL));
}
else {
exit(execl(path.c_str(), path.c_str(), "-j", jid.c_str(), config.c_str(), NULL));
}
} else if ( pid < 0 ) {
// fork failed
}
else {
waitpid(pid, 0, 0);
}
return (unsigned long) pid;
}
static int getPort(const std::string &portfile) {
path p(portfile);
if (!exists(p) || is_directory(p)) {
return 0;
}
std::ifstream f(p.string().c_str(), std::ios_base::in);
std::string port;
f >> port;
if (port.empty())
return 0;
return boost::lexical_cast<int>(port);
}
static int isRunning(const std::string &pidfile) {
path p(pidfile);
if (!exists(p) || is_directory(p)) {
return 0;
}
std::ifstream f(p.string().c_str(), std::ios_base::in);
std::string pid;
f >> pid;
if (pid.empty())
return 0;
if (kill(boost::lexical_cast<int>(pid), 0) != 0)
return 0;
return boost::lexical_cast<int>(pid);
}
static void start_instances(ManagerConfig *config, const std::string &_jid = "") {
path p(CONFIG_STRING(config, "service.config_directory"));
try {
if (!exists(p)) {
std::cerr << "Config directory " << CONFIG_STRING(config, "service.config_directory") << " does not exist\n";
exit(6);
}
if (!is_directory(p)) {
std::cerr << "Config directory " << CONFIG_STRING(config, "service.config_directory") << " does not exist\n";
exit(7);
}
std::string spectrum2_binary = searchForBinary("spectrum2");
if (spectrum2_binary.empty()) {
std::cerr << "spectrum2 binary not found in PATH\n";
exit(8);
}
directory_iterator end_itr;
for (directory_iterator itr(p); itr != end_itr; ++itr) {
if (is_regular(itr->path()) && extension(itr->path()) == ".cfg") {
Config cfg;
if (cfg.load(itr->path().string()) == false) {
std::cerr << "Can't load config file " << itr->path().string() << ". Skipping...\n";
continue;
}
std::vector<std::string> vhosts;
if (CONFIG_HAS_KEY(&cfg, "vhosts.vhost"))
vhosts = CONFIG_VECTOR(&cfg, "vhosts.vhost");
vhosts.push_back(CONFIG_STRING(&cfg, "service.jid"));
BOOST_FOREACH(std::string &vhost, vhosts) {
Config vhostCfg;
if (vhostCfg.load(itr->path().string(), vhost) == false) {
std::cerr << "Can't load config file " << itr->path().string() << ". Skipping...\n";
continue;
}
if (!_jid.empty() && _jid != vhost) {
continue;
}
int pid = isRunning(CONFIG_STRING(&vhostCfg, "service.pidfile"));
if (pid == 0) {
std::cout << "Starting " << itr->path() << ": OK\n";
exec_(spectrum2_binary, itr->path().string(), vhost);
}
else {
std::cout << "Starting " << itr->path() << ": Already started (PID=" << pid << ")\n";
}
}
}
}
}
catch (const filesystem_error& ex) {
std::cerr << "boost filesystem error\n";
exit(5);
}
}
static void stop_instances(ManagerConfig *config, const std::string &_jid = "") {
path p(CONFIG_STRING(config, "service.config_directory"));
try {
if (!exists(p)) {
std::cerr << "Config directory " << CONFIG_STRING(config, "service.config_directory") << " does not exist\n";
exit(6);
}
if (!is_directory(p)) {
std::cerr << "Config directory " << CONFIG_STRING(config, "service.config_directory") << " does not exist\n";
exit(7);
}
directory_iterator end_itr;
for (directory_iterator itr(p); itr != end_itr; ++itr) {
if (is_regular(itr->path()) && extension(itr->path()) == ".cfg") {
Config cfg;
if (cfg.load(itr->path().string()) == false) {
std::cerr << "Can't load config file " << itr->path().string() << ". Skipping...\n";
}
std::vector<std::string> vhosts;
if (CONFIG_HAS_KEY(&cfg, "vhosts.vhost"))
vhosts = CONFIG_VECTOR(&cfg, "vhosts.vhost");
vhosts.push_back(CONFIG_STRING(&cfg, "service.jid"));
BOOST_FOREACH(std::string &vhost, vhosts) {
Config vhostCfg;
if (vhostCfg.load(itr->path().string(), vhost) == false) {
std::cerr << "Can't load config file " << itr->path().string() << ". Skipping...\n";
continue;
}
if (!_jid.empty() && _jid != vhost) {
continue;
}
int pid = isRunning(CONFIG_STRING(&vhostCfg, "service.pidfile"));
if (pid) {
std::cout << "Stopping " << itr->path() << ": ";
kill(pid, SIGTERM);
sleep(1);
int count = 20;
while (kill(pid, 0) == 0 && count != 0) {
std::cout << ".";
sleep(1);
count--;
}
if (count == 0) {
std::cout << " ERROR (timeout)\n";
}
else {
std::cout << " OK\n";
}
}
else {
std::cout << "Stopping " << itr->path() << ": Not running\n";
}
}
}
}
}
catch (const filesystem_error& ex) {
std::cerr << "boost filesystem error\n";
exit(5);
}
}
static int show_status(ManagerConfig *config) {
int ret = 0;
path p(CONFIG_STRING(config, "service.config_directory"));
try {
if (!exists(p)) {
std::cerr << "Config directory " << CONFIG_STRING(config, "service.config_directory") << " does not exist\n";
exit(6);
}
if (!is_directory(p)) {
std::cerr << "Config directory " << CONFIG_STRING(config, "service.config_directory") << " does not exist\n";
exit(7);
}
directory_iterator end_itr;
for (directory_iterator itr(p); itr != end_itr; ++itr) {
if (is_regular(itr->path()) && extension(itr->path()) == ".cfg") {
Config cfg;
if (cfg.load(itr->path().string()) == false) {
std::cerr << "Can't load config file " << itr->path().string() << ". Skipping...\n";
}
std::vector<std::string> vhosts;
if (CONFIG_HAS_KEY(&cfg, "vhosts.vhost"))
vhosts = CONFIG_VECTOR(&cfg, "vhosts.vhost");
vhosts.push_back(CONFIG_STRING(&cfg, "service.jid"));
BOOST_FOREACH(std::string &vhost, vhosts) {
Config vhostCfg;
if (vhostCfg.load(itr->path().string(), vhost) == false) {
std::cerr << "Can't load config file " << itr->path().string() << ". Skipping...\n";
continue;
}
int pid = isRunning(CONFIG_STRING(&vhostCfg, "service.pidfile"));
if (pid) {
std::cout << itr->path() << ": " << vhost << " Running\n";
}
else {
ret = 3;
std::cout << itr->path() << ": " << vhost << " Stopped\n";
}
}
}
}
}
catch (const filesystem_error& ex) {
std::cerr << "boost filesystem error\n";
exit(5);
}
return ret;
}
static void handleDataRead(boost::shared_ptr<Swift::Connection> m_conn, boost::shared_ptr<Swift::SafeByteArray> data) {
_data += std::string(data->begin(), data->end());
// Parse data while there are some
while (_data.size() != 0) {
// expected_size of wrapper message
unsigned int expected_size;
// if data is >= 4, we have whole header and we can
// read expected_size.
if (_data.size() >= 4) {
expected_size = *((unsigned int*) &_data[0]);
expected_size = ntohl(expected_size);
// If we don't have whole wrapper message, wait for next
// handleDataRead call.
if (_data.size() - 4 < expected_size)
return;
}
else {
return;
}
// Parse wrapper message and erase it from buffer.
pbnetwork::WrapperMessage wrapper;
if (wrapper.ParseFromArray(&_data[4], expected_size) == false) {
std::cout << "PARSING ERROR " << expected_size << "\n";
_data.erase(_data.begin(), _data.begin() + 4 + expected_size);
continue;
}
_data.erase(_data.begin(), _data.begin() + 4 + expected_size);
if (wrapper.type() == pbnetwork::WrapperMessage_Type_TYPE_QUERY) {
pbnetwork::BackendConfig payload;
if (payload.ParseFromString(wrapper.payload()) == false) {
std::cout << "PARSING ERROR\n";
// TODO: ERROR
continue;
}
std::cout << payload.config() << "\n";
exit(0);
}
}
}
static void handleConnected(boost::shared_ptr<Swift::Connection> m_conn, const std::string &msg, bool error) {
if (error) {
std::cerr << "Can't connect the server\n";
exit(50);
}
else {
pbnetwork::BackendConfig m;
m.set_config(msg);
std::string message;
m.SerializeToString(&message);
WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_QUERY);
uint32_t size = htonl(message.size());
char *header = (char *) &size;
// send header together with wrapper message
m_conn->write(Swift::createSafeByteArray(std::string(header, 4) + message));
}
}
static void ask_local_server(ManagerConfig *config, Swift::BoostNetworkFactories &networkFactories, const std::string &jid, const std::string &message) {
path p(CONFIG_STRING(config, "service.config_directory"));
try {
if (!exists(p)) {
std::cerr << "Config directory " << CONFIG_STRING(config, "service.config_directory") << " does not exist\n";
exit(6);
}
if (!is_directory(p)) {
std::cerr << "Config directory " << CONFIG_STRING(config, "service.config_directory") << " does not exist\n";
exit(7);
}
bool found = false;
directory_iterator end_itr;
for (directory_iterator itr(p); itr != end_itr; ++itr) {
if (is_regular(itr->path()) && extension(itr->path()) == ".cfg") {
Config cfg;
if (cfg.load(itr->path().string()) == false) {
std::cerr << "Can't load config file " << itr->path().string() << ". Skipping...\n";
continue;
}
if (CONFIG_STRING(&cfg, "service.jid") != jid) {
continue;
}
found = true;
boost::shared_ptr<Swift::Connection> m_conn;
m_conn = networkFactories.getConnectionFactory()->createConnection();
m_conn->onDataRead.connect(boost::bind(&handleDataRead, m_conn, _1));
m_conn->onConnectFinished.connect(boost::bind(&handleConnected, m_conn, message, _1));
m_conn->connect(Swift::HostAddressPort(Swift::HostAddress(CONFIG_STRING(&cfg, "service.backend_host")), getPort(CONFIG_STRING(&cfg, "service.portfile"))));
// finished++;
// Swift::Client *client = new Swift::Client(CONFIG_VECTOR(&cfg, "service.admin_jid")[0], CONFIG_STRING(&cfg, "service.admin_password"), &networkFactories);
// client->setAlwaysTrustCertificates();
// client->onConnected.connect(boost::bind(&handleConnected, client, CONFIG_STRING(&cfg, "service.jid")));
// client->onDisconnected.connect(bind(&handleDisconnected, client, _1, CONFIG_STRING(&cfg, "service.jid")));
// client->onMessageReceived.connect(bind(&handleMessageReceived, client, _1, CONFIG_STRING(&cfg, "service.jid")));
// Swift::ClientOptions opt;
// opt.allowPLAINWithoutTLS = true;
// client->connect(opt);
}
}
if (!found) {
std::cerr << "Config file for Spectrum instance with this JID was not found\n";
exit(20);
}
}
catch (const filesystem_error& ex) {
std::cerr << "boost filesystem error\n";
exit(5);
}
}
static void show_list(ManagerConfig *config) {
path p(CONFIG_STRING(config, "service.config_directory"));
try {
if (!exists(p)) {
std::cerr << "Config directory " << CONFIG_STRING(config, "service.config_directory") << " does not exist\n";
exit(6);
}
if (!is_directory(p)) {
std::cerr << "Config directory " << CONFIG_STRING(config, "service.config_directory") << " does not exist\n";
exit(7);
}
bool found = false;
directory_iterator end_itr;
for (directory_iterator itr(p); itr != end_itr; ++itr) {
if (is_regular(itr->path()) && extension(itr->path()) == ".cfg") {
Config cfg;
if (cfg.load(itr->path().string()) == false) {
std::cerr << "Can't load config file " << itr->path().string() << ". Skipping...\n";
continue;
}
std::cout << CONFIG_STRING(&cfg, "service.jid") << "\n";
}
}
}
catch (const filesystem_error& ex) {
std::cerr << "boost filesystem error\n";
exit(5);
}
}
// static void ask_local_servers(ManagerConfig *config, Swift::BoostNetworkFactories &networkFactories, const std::string &message) {
// path p(CONFIG_STRING(config, "service.config_directory"));
@ -579,7 +135,12 @@ int main(int argc, char **argv)
return show_status(&config);
}
else if (command[0] == "list") {
show_list(&config);
std::vector<std::string> list = show_list(&config);
}
else if (command[0] == "server") {
Server server(&config);
server.start(8080, "test", "test");
while (1) { sleep(10); }
}
else {
if (command.size() < 2) {
@ -608,6 +169,23 @@ int main(int argc, char **argv)
// ask_local_server(&config, networkFactories, message);
eventLoop.run();
eventLoop.runUntilEvents();
struct timeval td_start,td_end;
float elapsed = 0;
gettimeofday(&td_start, NULL);
time_t started = time(NULL);
while(get_response().empty()) {
eventLoop.runUntilEvents();
}
if (!get_response().empty()) {
gettimeofday(&td_end, NULL);
elapsed = 1000000.0 * (td_end.tv_sec -td_start.tv_sec); \
elapsed += (td_end.tv_usec - td_start.tv_usec); \
elapsed = elapsed / 1000 / 1000; \
std::cout << "Response received after " << (elapsed) << " seconds\n";
}
}
}

View file

@ -0,0 +1,492 @@
#include "methods.h"
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <time.h>
#include <stdarg.h>
#include <pthread.h>
#include <boost/foreach.hpp>
#include <iostream>
#include <fstream>
#include <iterator>
#include <algorithm>
#include <boost/filesystem.hpp>
#include <cstdlib>
#include "signal.h"
#include "sys/wait.h"
#define WRAP(MESSAGE, TYPE) pbnetwork::WrapperMessage wrap; \
wrap.set_type(TYPE); \
wrap.set_payload(MESSAGE); \
wrap.SerializeToString(&MESSAGE);
using namespace Transport;
using namespace boost::filesystem;
using namespace boost;
std::string _data;
static std::string response;
std::string get_response() {
return response;
}
std::string searchForBinary(const std::string &binary) {
std::vector<std::string> path_list;
char * env_path = getenv("PATH");
if (env_path != NULL) {
std::string buffer = "";
for (int s = 0; s < strlen(env_path); s++) {
if (env_path[s] == ':') {
path_list.insert(path_list.end(), std::string(buffer));
buffer = "";
}
else {
buffer += env_path[s];
}
}
if (buffer != "") {
path_list.insert(path_list.end(), std::string(buffer));
buffer = "";
}
for (std::vector<std::string>::iterator dit = path_list.begin(); dit < path_list.end(); dit++) {
std::string bpath = *dit;
bpath += "/";
bpath += binary;
path p(bpath);
if (exists(p) && !is_directory(p)) {
return bpath;
}
}
}
return "";
}
// Executes new backend
unsigned long exec_(std::string path, std::string config, std::string jid) {
// fork and exec
pid_t pid = fork();
if ( pid == 0 ) {
// child process
if (jid.empty()) {
exit(execl(path.c_str(), path.c_str(), config.c_str(), NULL));
}
else {
exit(execl(path.c_str(), path.c_str(), "-j", jid.c_str(), config.c_str(), NULL));
}
} else if ( pid < 0 ) {
// fork failed
}
else {
waitpid(pid, 0, 0);
}
return (unsigned long) pid;
}
int getPort(const std::string &portfile) {
path p(portfile);
if (!exists(p) || is_directory(p)) {
return 0;
}
std::ifstream f(p.string().c_str(), std::ios_base::in);
std::string port;
f >> port;
if (port.empty())
return 0;
return boost::lexical_cast<int>(port);
}
int isRunning(const std::string &pidfile) {
path p(pidfile);
if (!exists(p) || is_directory(p)) {
return 0;
}
std::ifstream f(p.string().c_str(), std::ios_base::in);
std::string pid;
f >> pid;
if (pid.empty())
return 0;
if (kill(boost::lexical_cast<int>(pid), 0) != 0)
return 0;
return boost::lexical_cast<int>(pid);
}
void start_instances(ManagerConfig *config, const std::string &_jid) {
path p(CONFIG_STRING(config, "service.config_directory"));
try {
if (!exists(p)) {
std::cerr << "Config directory " << CONFIG_STRING(config, "service.config_directory") << " does not exist\n";
exit(6);
}
if (!is_directory(p)) {
std::cerr << "Config directory " << CONFIG_STRING(config, "service.config_directory") << " does not exist\n";
exit(7);
}
std::string spectrum2_binary = searchForBinary("spectrum2");
if (spectrum2_binary.empty()) {
std::cerr << "spectrum2 binary not found in PATH\n";
exit(8);
}
directory_iterator end_itr;
for (directory_iterator itr(p); itr != end_itr; ++itr) {
if (is_regular(itr->path()) && extension(itr->path()) == ".cfg") {
Config cfg;
if (cfg.load(itr->path().string()) == false) {
std::cerr << "Can't load config file " << itr->path().string() << ". Skipping...\n";
continue;
}
std::vector<std::string> vhosts;
if (CONFIG_HAS_KEY(&cfg, "vhosts.vhost"))
vhosts = CONFIG_VECTOR(&cfg, "vhosts.vhost");
vhosts.push_back(CONFIG_STRING(&cfg, "service.jid"));
BOOST_FOREACH(std::string &vhost, vhosts) {
Config vhostCfg;
if (vhostCfg.load(itr->path().string(), vhost) == false) {
std::cerr << "Can't load config file " << itr->path().string() << ". Skipping...\n";
continue;
}
if (!_jid.empty() && _jid != vhost) {
continue;
}
int pid = isRunning(CONFIG_STRING(&vhostCfg, "service.pidfile"));
if (pid == 0) {
std::cout << "Starting " << itr->path() << ": OK\n";
exec_(spectrum2_binary, itr->path().string(), vhost);
}
else {
std::cout << "Starting " << itr->path() << ": Already started (PID=" << pid << ")\n";
}
}
}
}
}
catch (const filesystem_error& ex) {
std::cerr << "boost filesystem error\n";
exit(5);
}
}
void stop_instances(ManagerConfig *config, const std::string &_jid) {
path p(CONFIG_STRING(config, "service.config_directory"));
try {
if (!exists(p)) {
std::cerr << "Config directory " << CONFIG_STRING(config, "service.config_directory") << " does not exist\n";
exit(6);
}
if (!is_directory(p)) {
std::cerr << "Config directory " << CONFIG_STRING(config, "service.config_directory") << " does not exist\n";
exit(7);
}
directory_iterator end_itr;
for (directory_iterator itr(p); itr != end_itr; ++itr) {
if (is_regular(itr->path()) && extension(itr->path()) == ".cfg") {
Config cfg;
if (cfg.load(itr->path().string()) == false) {
std::cerr << "Can't load config file " << itr->path().string() << ". Skipping...\n";
}
std::vector<std::string> vhosts;
if (CONFIG_HAS_KEY(&cfg, "vhosts.vhost"))
vhosts = CONFIG_VECTOR(&cfg, "vhosts.vhost");
vhosts.push_back(CONFIG_STRING(&cfg, "service.jid"));
BOOST_FOREACH(std::string &vhost, vhosts) {
Config vhostCfg;
if (vhostCfg.load(itr->path().string(), vhost) == false) {
std::cerr << "Can't load config file " << itr->path().string() << ". Skipping...\n";
continue;
}
if (!_jid.empty() && _jid != vhost) {
continue;
}
int pid = isRunning(CONFIG_STRING(&vhostCfg, "service.pidfile"));
if (pid) {
std::cout << "Stopping " << itr->path() << ": ";
kill(pid, SIGTERM);
sleep(1);
int count = 20;
while (kill(pid, 0) == 0 && count != 0) {
std::cout << ".";
sleep(1);
count--;
}
if (count == 0) {
std::cout << " ERROR (timeout)\n";
}
else {
std::cout << " OK\n";
}
}
else {
std::cout << "Stopping " << itr->path() << ": Not running\n";
}
}
}
}
}
catch (const filesystem_error& ex) {
std::cerr << "boost filesystem error\n";
exit(5);
}
}
int show_status(ManagerConfig *config) {
int ret = 0;
path p(CONFIG_STRING(config, "service.config_directory"));
try {
if (!exists(p)) {
std::cerr << "Config directory " << CONFIG_STRING(config, "service.config_directory") << " does not exist\n";
exit(6);
}
if (!is_directory(p)) {
std::cerr << "Config directory " << CONFIG_STRING(config, "service.config_directory") << " does not exist\n";
exit(7);
}
directory_iterator end_itr;
for (directory_iterator itr(p); itr != end_itr; ++itr) {
if (is_regular(itr->path()) && extension(itr->path()) == ".cfg") {
Config cfg;
if (cfg.load(itr->path().string()) == false) {
std::cerr << "Can't load config file " << itr->path().string() << ". Skipping...\n";
}
std::vector<std::string> vhosts;
if (CONFIG_HAS_KEY(&cfg, "vhosts.vhost"))
vhosts = CONFIG_VECTOR(&cfg, "vhosts.vhost");
vhosts.push_back(CONFIG_STRING(&cfg, "service.jid"));
BOOST_FOREACH(std::string &vhost, vhosts) {
Config vhostCfg;
if (vhostCfg.load(itr->path().string(), vhost) == false) {
std::cerr << "Can't load config file " << itr->path().string() << ". Skipping...\n";
continue;
}
int pid = isRunning(CONFIG_STRING(&vhostCfg, "service.pidfile"));
if (pid) {
std::cout << itr->path() << ": " << vhost << " Running\n";
}
else {
ret = 3;
std::cout << itr->path() << ": " << vhost << " Stopped\n";
}
}
}
}
}
catch (const filesystem_error& ex) {
std::cerr << "boost filesystem error\n";
exit(5);
}
return ret;
}
static void handleDataRead(boost::shared_ptr<Swift::Connection> m_conn, boost::shared_ptr<Swift::SafeByteArray> data) {
_data += std::string(data->begin(), data->end());
// Parse data while there are some
while (_data.size() != 0) {
// expected_size of wrapper message
unsigned int expected_size;
// if data is >= 4, we have whole header and we can
// read expected_size.
if (_data.size() >= 4) {
expected_size = *((unsigned int*) &_data[0]);
expected_size = ntohl(expected_size);
// If we don't have whole wrapper message, wait for next
// handleDataRead call.
if (_data.size() - 4 < expected_size)
return;
}
else {
return;
}
// Parse wrapper message and erase it from buffer.
pbnetwork::WrapperMessage wrapper;
if (wrapper.ParseFromArray(&_data[4], expected_size) == false) {
std::cout << "PARSING ERROR " << expected_size << "\n";
_data.erase(_data.begin(), _data.begin() + 4 + expected_size);
continue;
}
_data.erase(_data.begin(), _data.begin() + 4 + expected_size);
if (wrapper.type() == pbnetwork::WrapperMessage_Type_TYPE_QUERY) {
pbnetwork::BackendConfig payload;
if (payload.ParseFromString(wrapper.payload()) == false) {
std::cout << "PARSING ERROR\n";
// TODO: ERROR
continue;
}
m_conn->onDataRead.disconnect(boost::bind(&handleDataRead, m_conn, _1));
response = payload.config();
std::cout << payload.config() << "\n";
// exit(0);
}
}
}
static void handleConnected(boost::shared_ptr<Swift::Connection> m_conn, const std::string &msg, bool error) {
m_conn->onConnectFinished.disconnect(boost::bind(&handleConnected, m_conn, msg, _1));
if (error) {
std::cerr << "Can't connect the server\n";
response = "Can't connect the server\n";
m_conn->onDataRead.disconnect(boost::bind(&handleDataRead, m_conn, _1));
// exit(50);
}
else {
pbnetwork::BackendConfig m;
m.set_config(msg);
std::string message;
m.SerializeToString(&message);
WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_QUERY);
uint32_t size = htonl(message.size());
char *header = (char *) &size;
// send header together with wrapper message
m_conn->write(Swift::createSafeByteArray(std::string(header, 4) + message));
}
}
void ask_local_server(ManagerConfig *config, Swift::BoostNetworkFactories &networkFactories, const std::string &jid, const std::string &message) {
path p(CONFIG_STRING(config, "service.config_directory"));
try {
if (!exists(p)) {
std::cerr << "Config directory " << CONFIG_STRING(config, "service.config_directory") << " does not exist\n";
exit(6);
}
if (!is_directory(p)) {
std::cerr << "Config directory " << CONFIG_STRING(config, "service.config_directory") << " does not exist\n";
exit(7);
}
bool found = false;
directory_iterator end_itr;
for (directory_iterator itr(p); itr != end_itr; ++itr) {
if (is_regular(itr->path()) && extension(itr->path()) == ".cfg") {
Config cfg;
if (cfg.load(itr->path().string()) == false) {
std::cerr << "Can't load config file " << itr->path().string() << ". Skipping...\n";
continue;
}
if (CONFIG_STRING(&cfg, "service.jid") != jid) {
continue;
}
found = true;
boost::shared_ptr<Swift::Connection> m_conn;
m_conn = networkFactories.getConnectionFactory()->createConnection();
m_conn->onDataRead.connect(boost::bind(&handleDataRead, m_conn, _1));
m_conn->onConnectFinished.connect(boost::bind(&handleConnected, m_conn, message, _1));
m_conn->connect(Swift::HostAddressPort(Swift::HostAddress(CONFIG_STRING(&cfg, "service.backend_host")), getPort(CONFIG_STRING(&cfg, "service.portfile"))));
// finished++;
// Swift::Client *client = new Swift::Client(CONFIG_VECTOR(&cfg, "service.admin_jid")[0], CONFIG_STRING(&cfg, "service.admin_password"), &networkFactories);
// client->setAlwaysTrustCertificates();
// client->onConnected.connect(boost::bind(&handleConnected, client, CONFIG_STRING(&cfg, "service.jid")));
// client->onDisconnected.connect(bind(&handleDisconnected, client, _1, CONFIG_STRING(&cfg, "service.jid")));
// client->onMessageReceived.connect(bind(&handleMessageReceived, client, _1, CONFIG_STRING(&cfg, "service.jid")));
// Swift::ClientOptions opt;
// opt.allowPLAINWithoutTLS = true;
// client->connect(opt);
}
}
if (!found) {
std::cerr << "Config file for Spectrum instance with this JID was not found\n";
exit(20);
}
}
catch (const filesystem_error& ex) {
std::cerr << "boost filesystem error\n";
exit(5);
}
}
std::vector<std::string> show_list(ManagerConfig *config, bool show) {
path p(CONFIG_STRING(config, "service.config_directory"));
std::vector<std::string> list;
try {
if (!exists(p)) {
std::cerr << "Config directory " << CONFIG_STRING(config, "service.config_directory") << " does not exist\n";
exit(6);
}
if (!is_directory(p)) {
std::cerr << "Config directory " << CONFIG_STRING(config, "service.config_directory") << " does not exist\n";
exit(7);
}
bool found = false;
directory_iterator end_itr;
for (directory_iterator itr(p); itr != end_itr; ++itr) {
if (is_regular(itr->path()) && extension(itr->path()) == ".cfg") {
Config cfg;
if (cfg.load(itr->path().string()) == false) {
std::cerr << "Can't load config file " << itr->path().string() << ". Skipping...\n";
continue;
}
if (show) {
std::cout << CONFIG_STRING(&cfg, "service.jid") << "\n";
}
list.push_back(CONFIG_STRING(&cfg, "service.jid"));
}
}
}
catch (const filesystem_error& ex) {
std::cerr << "boost filesystem error\n";
}
return list;
}

View file

@ -0,0 +1,56 @@
/**
* libtransport -- C++ library for easy XMPP Transports development
*
* Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
#pragma once
#include <boost/program_options.hpp>
#include <boost/foreach.hpp>
#include <boost/format.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/assign.hpp>
#include <boost/bind.hpp>
#include <boost/signal.hpp>
#include "managerconfig.h"
#include "transport/config.h"
#include "transport/protocol.pb.h"
#include "Swiften/Swiften.h"
#include "Swiften/EventLoop/SimpleEventLoop.h"
std::string searchForBinary(const std::string &binary);
// Executes new backend
unsigned long exec_(std::string path, std::string config, std::string jid = "");
int getPort(const std::string &portfile);
int isRunning(const std::string &pidfile);
void start_instances(ManagerConfig *config, const std::string &_jid = "");
void stop_instances(ManagerConfig *config, const std::string &_jid = "");
int show_status(ManagerConfig *config);
void ask_local_server(ManagerConfig *config, Swift::BoostNetworkFactories &networkFactories, const std::string &jid, const std::string &message);
std::vector<std::string> show_list(ManagerConfig *config, bool show = true);
std::string get_response();

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,301 @@
// Copyright (c) 2004-2012 Sergey Lyubka
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef MONGOOSE_HEADER_INCLUDED
#define MONGOOSE_HEADER_INCLUDED
#include <stdio.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
struct mg_context; // Handle for the HTTP service itself
struct mg_connection; // Handle for the individual connection
// This structure contains information about the HTTP request.
struct mg_request_info {
void *user_data; // User-defined pointer passed to mg_start()
char *request_method; // "GET", "POST", etc
char *uri; // URL-decoded URI
char *http_version; // E.g. "1.0", "1.1"
char *query_string; // URL part after '?' (not including '?') or NULL
char *remote_user; // Authenticated user, or NULL if no auth used
char *log_message; // Mongoose error log message, MG_EVENT_LOG only
long remote_ip; // Client's IP address
int remote_port; // Client's port
int status_code; // HTTP reply status code, e.g. 200
int is_ssl; // 1 if SSL-ed, 0 if not
int num_headers; // Number of headers
struct mg_header {
char *name; // HTTP header name
char *value; // HTTP header value
} http_headers[64]; // Maximum 64 headers
};
// Various events on which user-defined function is called by Mongoose.
enum mg_event {
MG_NEW_REQUEST, // New HTTP request has arrived from the client
MG_REQUEST_COMPLETE, // Mongoose has finished handling the request
MG_HTTP_ERROR, // HTTP error must be returned to the client
MG_EVENT_LOG, // Mongoose logs an event, request_info.log_message
MG_INIT_SSL // Mongoose initializes SSL. Instead of mg_connection *,
// SSL context is passed to the callback function.
};
// Prototype for the user-defined function. Mongoose calls this function
// on every MG_* event.
//
// Parameters:
// event: which event has been triggered.
// conn: opaque connection handler. Could be used to read, write data to the
// client, etc. See functions below that have "mg_connection *" arg.
//
// Return:
// If handler returns non-NULL, that means that handler has processed the
// request by sending appropriate HTTP reply to the client. Mongoose treats
// the request as served.
// If handler returns NULL, that means that handler has not processed
// the request. Handler must not send any data to the client in this case.
// Mongoose proceeds with request handling as if nothing happened.
typedef void * (*mg_callback_t)(enum mg_event event,
struct mg_connection *conn);
// Start web server.
//
// Parameters:
// callback: user defined event handling function or NULL.
// options: NULL terminated list of option_name, option_value pairs that
// specify Mongoose configuration parameters.
//
// Side-effects: on UNIX, ignores SIGCHLD and SIGPIPE signals. If custom
// processing is required for these, signal handlers must be set up
// after calling mg_start().
//
//
// Example:
// const char *options[] = {
// "document_root", "/var/www",
// "listening_ports", "80,443s",
// NULL
// };
// struct mg_context *ctx = mg_start(&my_func, NULL, options);
//
// Please refer to http://code.google.com/p/mongoose/wiki/MongooseManual
// for the list of valid option and their possible values.
//
// Return:
// web server context, or NULL on error.
struct mg_context *mg_start(mg_callback_t callback, void *user_data,
const char **options);
// Stop the web server.
//
// Must be called last, when an application wants to stop the web server and
// release all associated resources. This function blocks until all Mongoose
// threads are stopped. Context pointer becomes invalid.
void mg_stop(struct mg_context *);
// Get the value of particular configuration parameter.
// The value returned is read-only. Mongoose does not allow changing
// configuration at run time.
// If given parameter name is not valid, NULL is returned. For valid
// names, return value is guaranteed to be non-NULL. If parameter is not
// set, zero-length string is returned.
const char *mg_get_option(const struct mg_context *ctx, const char *name);
// Return array of strings that represent valid configuration options.
// For each option, a short name, long name, and default value is returned.
// Array is NULL terminated.
const char **mg_get_valid_option_names(void);
// Add, edit or delete the entry in the passwords file.
//
// This function allows an application to manipulate .htpasswd files on the
// fly by adding, deleting and changing user records. This is one of the
// several ways of implementing authentication on the server side. For another,
// cookie-based way please refer to the examples/chat.c in the source tree.
//
// If password is not NULL, entry is added (or modified if already exists).
// If password is NULL, entry is deleted.
//
// Return:
// 1 on success, 0 on error.
int mg_modify_passwords_file(const char *passwords_file_name,
const char *domain,
const char *user,
const char *password);
// Return mg_request_info structure associated with the request.
// Always succeeds.
const struct mg_request_info *mg_get_request_info(const struct mg_connection *);
// Send data to the client.
// Return:
// 0 when the connection has been closed
// -1 on error
// number of bytes written on success
int mg_write(struct mg_connection *, const void *buf, size_t len);
// Send data to the browser using printf() semantics.
//
// Works exactly like mg_write(), but allows to do message formatting.
// Below are the macros for enabling compiler-specific checks for
// printf-like arguments.
#undef PRINTF_FORMAT_STRING
#if _MSC_VER >= 1400
#include <sal.h>
#if _MSC_VER > 1400
#define PRINTF_FORMAT_STRING(s) _Printf_format_string_ s
#else
#define PRINTF_FORMAT_STRING(s) __format_string s
#endif
#else
#define PRINTF_FORMAT_STRING(s) s
#endif
#ifdef __GNUC__
#define PRINTF_ARGS(x, y) __attribute__((format(printf, x, y)))
#else
#define PRINTF_ARGS(x, y)
#endif
int mg_printf(struct mg_connection *,
PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3);
// Send contents of the entire file together with HTTP headers.
void mg_send_file(struct mg_connection *conn, const char *path);
// Read data from the remote end, return number of bytes read.
int mg_read(struct mg_connection *, void *buf, size_t len);
// Get the value of particular HTTP header.
//
// This is a helper function. It traverses request_info->http_headers array,
// and if the header is present in the array, returns its value. If it is
// not present, NULL is returned.
const char *mg_get_header(const struct mg_connection *, const char *name);
// Get a value of particular form variable.
//
// Parameters:
// data: pointer to form-uri-encoded buffer. This could be either POST data,
// or request_info.query_string.
// data_len: length of the encoded data.
// var_name: variable name to decode from the buffer
// buf: destination buffer for the decoded variable
// buf_len: length of the destination buffer
//
// Return:
// On success, length of the decoded variable.
// On error:
// -1 (variable not found, or destination buffer is too small).
// -2 (destination buffer is NULL or zero length).
//
// Destination buffer is guaranteed to be '\0' - terminated if it is not
// NULL or zero length. In case of failure, dst[0] == '\0'.
int mg_get_var(const char *data, size_t data_len,
const char *var_name, char *buf, size_t buf_len);
// Fetch value of certain cookie variable into the destination buffer.
//
// Destination buffer is guaranteed to be '\0' - terminated. In case of
// failure, dst[0] == '\0'. Note that RFC allows many occurrences of the same
// parameter. This function returns only first occurrence.
//
// Return:
// On success, value length.
// On error, -1 (either "Cookie:" header is not present at all, or the
// requested parameter is not found, or destination buffer is too small
// to hold the value).
int mg_get_cookie(const struct mg_connection *,
const char *cookie_name, char *buf, size_t buf_len);
// Connect to the remote web server.
// Return:
// On success, valid pointer to the new connection
// On error, NULL
struct mg_connection *mg_connect(struct mg_context *ctx,
const char *host, int port, int use_ssl);
// Close the connection opened by mg_connect().
void mg_close_connection(struct mg_connection *conn);
// Download given URL to a given file.
// url: URL to download
// path: file name where to save the data
// request_info: pointer to a structure that will hold parsed reply headers
// buf, bul_len: a buffer for the reply headers
// Return:
// On error, NULL
// On success, opened file stream to the downloaded contents. The stream
// is positioned to the end of the file. It is the user's responsibility
// to fclose() the opened file stream.
FILE *mg_fetch(struct mg_context *ctx, const char *url, const char *path,
char *buf, size_t buf_len, struct mg_request_info *request_info);
// Convenience function -- create detached thread.
// Return: 0 on success, non-0 on error.
typedef void * (*mg_thread_func_t)(void *);
int mg_start_thread(mg_thread_func_t f, void *p);
// Return builtin mime type for the given file name.
// For unrecognized extensions, "text/plain" is returned.
const char *mg_get_builtin_mime_type(const char *file_name);
// Return Mongoose version.
const char *mg_version(void);
// MD5 hash given strings.
// Buffer 'buf' must be 33 bytes long. Varargs is a NULL terminated list of
// ASCIIz strings. When function returns, buf will contain human-readable
// MD5 hash. Example:
// char buf[33];
// mg_md5(buf, "aa", "bb", NULL);
void mg_md5(char buf[33], ...);
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // MONGOOSE_HEADER_INCLUDED

View file

@ -0,0 +1,365 @@
#include "server.h"
#include "methods.h"
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <time.h>
#include <stdarg.h>
#include <pthread.h>
#define SESSION_TTL 120
static std::string get_header() {
return "\
<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\
\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"> \
<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\" dir=\"ltr\"> \
<head>\
<title>Spectrum 2 web interface</title>\
<meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\"/>\
<style type=\"text/css\">\
body{ background-color: #F9F9F9; color: #444444; font: normal normal 14px \"Helvetica\", \"Arial\", Sans-Serif; }\
\
pre, kbd, var, samp, tt{ font-family: \"Courier\", Monospace; }\
\
pre { font-size: 12px; }\
\
h1, h2, h3, h4, h5, h6, pre{ color: #094776; }\
\
h1{ font-size: 28px; }\
\
h2{ font-size: 24px; font-weight: normal; }\
\
h1, h2, h3, h4, h5, h6{ margin-bottom: 20px; }\
\
h2, h3{ border-bottom: 2px solid #EEEEEE; padding: 0 0 3px; } \
\
h3{ border-color: #E5E5E5; border-width: 1px; }\
\
h4{ font-size: 18px; }\
\
h1 a, h2 a{ font-weight: normal; }\
\
h1 a, h2 a, h3 a{ text-decoration: none; }\
\
h3, h5, h6{ font-size: 18px; }\
\
h4, h5, h6{ font-size: 14px; }\
\
p, dl, ul, ol{ margin: 20px 0; }\
\
p, dl, ul, ol, h3, h4, h5, h6{ margin-left: 20px; }\
\
li > ul,\
li > ol{ margin: 0; margin-left: 40px; }\
\
dl > dd{ margin-left: 20px; }\
\
li > p { margin: 0; }\
\
p, li, dd, dt, pre{ line-height: 1.5; }\
\
table {\
border-collapse: collapse;\
margin-bottom: 20px;\
margin-left:20px;\
}\
\
th {\
padding: 0 0.5em;\
text-align: center;\
}\
\
th {\
border: 1px solid #FB7A31;\
background: #FFC;\
}\
\
td {\
border-bottom: 1px solid #CCC;\
border-right: 1px solid #CCC;\
border-left: 1px solid #CCC;\
padding: 0 0.5em;\
}\
\
\
a:link,\
a:visited{ color: #1A5B8D; }\
\
a:hover,\
a:active{ color: #742CAC; }\
\
a.headerlink{ visibility: hidden; }\
\
:hover > a.headerlink { visibility: visible; }\
\
a img{ \
border: 0;\
outline: 0;\
}\
\
img{ display: block; max-width: 100%; }\
\
code {\
border: 1px solid #FB7A31;\
background: #FFC;\
}\
\
pre {\
white-space: pre-wrap;\
white-space: -moz-pre-wrap;\
white-space: -o-pre-wrap;\
border: 1px solid #FB7A31;\
background: #FFC;\
padding:5px;\
padding-left: 15px;\
}\
\
</style>\
</head><body><h1>Spectrum 2 web interface</h1>";
}
static void get_qsvar(const struct mg_request_info *request_info,
const char *name, char *dst, size_t dst_len) {
const char *qs = request_info->query_string;
mg_get_var(qs, strlen(qs == NULL ? "" : qs), name, dst, dst_len);
}
static void my_strlcpy(char *dst, const char *src, size_t len) {
strncpy(dst, src, len);
dst[len - 1] = '\0';
}
// Generate session ID. buf must be 33 bytes in size.
// Note that it is easy to steal session cookies by sniffing traffic.
// This is why all communication must be SSL-ed.
static void generate_session_id(char *buf, const char *random,
const char *user) {
mg_md5(buf, random, user, NULL);
}
Server::Server(ManagerConfig *config) {
srand((unsigned) time(0));
m_config = config;
}
Server::~Server() {
mg_stop(ctx);
}
static void *_event_handler(enum mg_event event, struct mg_connection *conn) {
const struct mg_request_info *request_info = mg_get_request_info(conn);
return static_cast<Server *>(request_info->user_data)->event_handler(event, conn);
}
bool Server::start(int port, const std::string &user, const std::string &password) {
m_user = user;
m_password = password;
const char *options[] = {
"listening_ports", boost::lexical_cast<std::string>(port).c_str(),
"num_threads", "1",
NULL
};
// Setup and start Mongoose
if ((ctx = mg_start(&_event_handler, this, options)) == NULL) {
return false;
}
return true;
}
bool Server::check_password(const char *user, const char *password) {
return (m_user == user && m_password == password);
}
// Allocate new session object
Server::session *Server::new_session(const char *user) {
Server::session *session = new Server::session;
my_strlcpy(session->user, user, sizeof(session->user));
snprintf(session->random, sizeof(session->random), "%d", rand());
generate_session_id(session->session_id, session->random, session->user);
session->expire = time(0) + SESSION_TTL;
sessions[session->session_id] = session;
return session;
}
// Get session object for the connection. Caller must hold the lock.
Server::session *Server::get_session(const struct mg_connection *conn) {
time_t now = time(NULL);
char session_id[33];
mg_get_cookie(conn, "session", session_id, sizeof(session_id));
if (sessions.find(session_id) == sessions.end()) {
return NULL;
}
if (sessions[session_id]->expire != 0 && sessions[session_id]->expire > now) {
return sessions[session_id];
}
return NULL;
}
void Server::authorize(struct mg_connection *conn, const struct mg_request_info *request_info) {
char user[255], password[255];
Server::session *session;
// Fetch user name and password.
get_qsvar(request_info, "user", user, sizeof(user));
get_qsvar(request_info, "password", password, sizeof(password));
if (check_password(user, password) && (session = new_session(user)) != NULL) {
std::cout << "User authorized\n";
// Authentication success:
// 1. create new session
// 2. set session ID token in the cookie
// 3. remove original_url from the cookie - not needed anymore
// 4. redirect client back to the original URL
//
// The most secure way is to stay HTTPS all the time. However, just to
// show the technique, we redirect to HTTP after the successful
// authentication. The danger of doing this is that session cookie can
// be stolen and an attacker may impersonate the user.
// Secure application must use HTTPS all the time.
mg_printf(conn, "HTTP/1.1 302 Found\r\n"
"Set-Cookie: session=%s; max-age=3600; http-only\r\n" // Session ID
"Set-Cookie: user=%s\r\n" // Set user, needed by Javascript code
"Set-Cookie: original_url=/; max-age=0\r\n" // Delete original_url
"Location: /\r\n\r\n",
session->session_id, session->user);
} else {
// Authentication failure, redirect to login.
redirect_to(conn, request_info, "/login");
}
}
bool Server::is_authorized(const struct mg_connection *conn, const struct mg_request_info *request_info) {
Server::session *session;
char valid_id[33];
bool authorized = false;
// Always authorize accesses to login page and to authorize URI
if (!strcmp(request_info->uri, "/login") ||
!strcmp(request_info->uri, "/authorize")) {
return true;
}
// pthread_rwlock_rdlock(&rwlock);
if ((session = get_session(conn)) != NULL) {
generate_session_id(valid_id, session->random, session->user);
if (strcmp(valid_id, session->session_id) == 0) {
session->expire = time(0) + SESSION_TTL;
authorized = true;
}
}
// pthread_rwlock_unlock(&rwlock);
return authorized;
}
void Server::redirect_to(struct mg_connection *conn, const struct mg_request_info *request_info, const char *where) {
mg_printf(conn, "HTTP/1.1 302 Found\r\n"
"Set-Cookie: original_url=%s\r\n"
"Location: %s\r\n\r\n",
request_info->uri, where);
}
void Server::print_html(struct mg_connection *conn, const struct mg_request_info *request_info, const std::string &html) {
mg_printf(conn,
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html\r\n"
"Content-Length: %d\r\n" // Always set Content-Length
"\r\n"
"%s",
(int) html.size(), html.c_str());
}
void Server::serve_login(struct mg_connection *conn, const struct mg_request_info *request_info) {
std::string html= "\
<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\
\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"> \
<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\" dir=\"ltr\"> \
<head>\
<title>Spectrum 2 web interface</title>\
<meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\"/>\
</head>\
<body>\
<center>\
<h2>Spectrum 2 web interface login</h2>\
<br/>\
<form action=\"/authorize\">\
Username: <input type=\"text\" name=\"user\"></input><br/>\
Password: <input type=\"text\" name=\"password\"></input><br/>\
<input type=\"submit\" value=\"Login\"></input>\
</form>\
</center>\
</body>\
</html>";
print_html(conn, request_info, html);
}
void Server::serve_root(struct mg_connection *conn, const struct mg_request_info *request_info) {
std::vector<std::string> list = show_list(m_config, false);
std::string html= get_header() + "<h2>List of instances</h2><table><tr><th>JID<th>Status</th><th>Command</th></tr>";
BOOST_FOREACH(std::string &instance, list) {
html += "<tr>";
html += "<td>" + instance + "</td>";
Swift::SimpleEventLoop eventLoop;
Swift::BoostNetworkFactories networkFactories(&eventLoop);
ask_local_server(m_config, networkFactories, instance, "status");
eventLoop.runUntilEvents();
while(get_response().empty()) {
eventLoop.runUntilEvents();
}
html += "<td>" + get_response() + "</td>";
if (get_response().find("Running") == 0) {
html += "<td><a href=\"/stop?jid=" + instance + "\">Stop</a></td>";
}
else {
html += "<td><a href=\"/start?jid=" + instance + "\">Start</a></td>";
}
html += "</tr>";
}
html += "</table></body></html>";
print_html(conn, request_info, html);
}
void *Server::event_handler(enum mg_event event, struct mg_connection *conn) {
const struct mg_request_info *request_info = mg_get_request_info(conn);
void *processed = (void *) 0x1;
if (event == MG_NEW_REQUEST) {
if (!is_authorized(conn, request_info)) {
redirect_to(conn, request_info, "/login");
} else if (strcmp(request_info->uri, "/authorize") == 0) {
authorize(conn, request_info);
} else if (strcmp(request_info->uri, "/login") == 0) {
serve_login(conn, request_info);
} else if (strcmp(request_info->uri, "/") == 0) {
serve_root(conn, request_info);
} else {
// No suitable handler found, mark as not processed. Mongoose will
// try to serve the request.
processed = NULL;
}
} else {
processed = NULL;
}
return processed;
}

View file

@ -0,0 +1,75 @@
/**
* libtransport -- C++ library for easy XMPP Transports development
*
* Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
#pragma once
#include <boost/program_options.hpp>
#include <boost/foreach.hpp>
#include <boost/format.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/assign.hpp>
#include <boost/bind.hpp>
#include <boost/signal.hpp>
#include "mongoose.h"
#include "managerconfig.h"
class Server {
public:
struct session {
char session_id[33]; // Session ID, must be unique
char random[20]; // Random data used for extra user validation
char user[255]; // Authenticated user
time_t expire; // Expiration timestamp, UTC
};
/// Constructor.
Server(ManagerConfig *config);
/// Destructor
virtual ~Server();
bool start(int port, const std::string &user, const std::string &password);
void *event_handler(enum mg_event event, struct mg_connection *conn);
private:
void serve_login(struct mg_connection *conn, const struct mg_request_info *request_info);
void serve_root(struct mg_connection *conn, const struct mg_request_info *request_info);
void print_html(struct mg_connection *conn, const struct mg_request_info *request_info, const std::string &html);
private:
bool check_password(const char *user, const char *password);
session *new_session(const char *user);
session *get_session(const struct mg_connection *conn);
void authorize(struct mg_connection *conn, const struct mg_request_info *request_info);
bool is_authorized(const struct mg_connection *conn, const struct mg_request_info *request_info);
void redirect_to(struct mg_connection *conn, const struct mg_request_info *request_info, const char *where);
private:
struct mg_context *ctx;
std::map<std::string, session *> sessions;
std::string m_user;
std::string m_password;
ManagerConfig *m_config;
};