Commit 3111b395 authored by Gabriel Margiani's avatar Gabriel Margiani

push events

parent ab18a48d
......@@ -19,6 +19,7 @@
#include <iostream>
#include <random>
#include <ctime>
#include <future>
#include <boost/algorithm/string.hpp>
......@@ -44,6 +45,7 @@ std::map<std::string, std::string> p3::client::command_shorthands {
{"rr", "reregister"},
{"rl", "reload"},
{"st", "status"},
{"e", "events"},
{"q", "quit"},
{"x", "exit"}
};
......@@ -91,7 +93,7 @@ p3::client::~client() {
delete connection;
}
std::string p3::client::get_input(std::string q, p3::client::inputType type) {
std::string p3::client::get_input(const std::string & q, p3::client::inputType type) {
std::string in;
if (!argv.empty()) {
in = argv.front();
......@@ -118,8 +120,19 @@ std::string p3::client::get_input(std::string q, p3::client::inputType type) {
void p3::client::run() {
bool r = true;
connection->send(p3::protocol::COMMAND, get_input("", COMMAND));
while (r) {
std::string in = get_input("", COMMAND);
if (in == "exit" || in == "quit") {
break;
} else if ("events" == in) {
if(push_event_manager()) {
continue;
} else {
break;
}
} else {
connection->send(p3::protocol::COMMAND, in);
}
p3::mQueueMessage m = connection->receive(-1);
bool print_list = true;
switch (m.get_command()) {
......@@ -160,12 +173,71 @@ void p3::client::run() {
default:
std::cout << "Unknown server command. " << m.get_value() << std::endl;
}
std::string in = get_input("", COMMAND);
if (in == "exit" || in == "quit") {
break;
}
}
bool p3::client::push_event_manager() {
bool ointer = interactive;
interactive = true;
auto res = std::async(std::launch::async, &p3::client::push_event_reader, this);
connection->send(p3::protocol::COMMAND, "startevents");
while (res.wait_for(std::chrono::milliseconds(0)) != std::future_status::ready) {
std::string in("");
std::cin >> in;
boost::trim(in);
boost::to_lower(in);
if (in == "exit" || in == "quit" || in == "q" || in == "x" || !std::cin) {
connection->send(p3::protocol::COMMAND, "stopevents");
res.wait();
return false;
} else if ("events" == in || "e" == in) {
connection->send(p3::protocol::COMMAND, "stopevents");
res.wait();
} else {
connection->send(p3::protocol::COMMAND, in);
std::cout << "No commands supported in push event mode. Use 'event' to stop." << std::endl;
}
}
interactive = ointer;
return res.get();
}
bool p3::client::push_event_reader() {
bool okexit = false;
while (true) {
p3::mQueueMessage m = connection->receive(-1);
bool print_list = true;
switch (m.get_command()) {
case p3::protocol::BEGINTEXT:
m = connection->receive(30);
while (m.get_command() != p3::protocol::ENDTEXT) {
if (print_list) {
std::cout << m.get_value() << std::endl;
}
m = connection->receive(30);
}
std::cout << "---" << std::endl;
break;
case p3::protocol::OK:
if (verbose >= 1) std::cout << m.get_value() << std::endl;
if (okexit) {
return true;
} else {
okexit = true;
}
break;
case p3::protocol::ERROR:
std::cout << "ERROR: " << m.get_value() << std::endl;
return true;
case p3::protocol::FATAL_ERROR:
std::cout << "FATAL ERROR: " << m.get_value() << std::endl;
return true;
case p3::protocol::BYE:
std::cout << "done." << std::endl;
return false;
default:
std::cout << "Unknown server command. " << m.get_value() << std::endl;
}
}
}
......@@ -56,7 +56,10 @@ namespace p3 {
bool interactive;
int verbose;
std::string get_input(std::string question, inputType is_command);
bool push_event_reader(); // Return false if client should exit.
bool push_event_manager(); // Return false if client should exit.
std::string get_input(const std::string & question, inputType is_command);
public:
client(int argc, char *argv[]);
......
......@@ -91,3 +91,20 @@ bool p3::config::get_bool(const std::string& k) {
throw;
}
}
std::list<std::string> p3::config::get_string_list(const std::string& k) {
try {
std::list<std::string> res;
boost::split(res, data.at(k), boost::is_any_of(",;"));
if (res.size() == 1 && (res.front() == "none" || res.front() == "")) {
return std::list<std::string>();
}
for (auto &n : res) {
boost::trim(n);
}
return res;
} catch (std::out_of_range& e) {
std::cout << "!! config::get_string_list(): Invalide configuration key: " << k << std::endl;
throw;
}
}
......@@ -20,6 +20,7 @@
#define __P3_CONFIG_H__
#include <string>
#include <list>
#include <map>
namespace p3 {
......@@ -40,6 +41,7 @@ namespace p3 {
std::string get_string(const std::string& k);
int get_int(const std::string& k);
bool get_bool(const std::string& k);
std::list <std::string> get_string_list(const std::string& k);
};
}
......
......@@ -28,7 +28,7 @@ namespace p3 {
std::string message;
public:
perror(std::string c, std::string e) : context(c), error(e), message(c + ": " + e) {};
perror(const std::string & c, const std::string & e) : context(c), error(e), message(c + ": " + e) {};
~perror() throw () {};
const char * getContext() { return context.c_str(); }
......
......@@ -30,11 +30,22 @@ p3::eventHook::eventHook(config& c, phonebook& p) :
signal(SIGCHLD, SIG_IGN);
}
std::list<p3::eventHook::callback_type>::iterator p3::eventHook::register_callback(callback_type c) {
return callbacks.insert(callbacks.end(), c);
}
void p3::eventHook::unregister_callback(std::list<p3::eventHook::callback_type>::iterator id) {
callbacks.erase(id);
}
void p3::eventHook::run_call_hook(std::string event, int cid, std::string nr, std::string data) {
run_hook(event, std::to_string(cid), book.address_by_nr(nr), data);
}
void p3::eventHook::run_hook(std::string event, const std::string& a2, const std::string& a3, const std::string& a4) {
for (auto c : callbacks) {
c(event, a2, a3, a4);
}
int pid = fork();
if (pid == 0) {
execl(
......
......@@ -20,6 +20,7 @@
#define __P3_EVENT_HOOK_H__
#include <string>
#include <list>
#include "phonebook.h"
#include "config.h"
......@@ -41,18 +42,28 @@ namespace p3 {
*/
class eventHook {
public:
// callback functions need to have same signature as run_hook.
typedef std::function<void(const std::string&, const std::string&, const std::string&, const std::string&)> callback_type;
private:
config& conf;
phonebook& book;
config& conf;
phonebook& book;
std::string command;
std::string command;
std::list<callback_type> callbacks;
public:
explicit eventHook(config& c, phonebook& p);
void run_hook(std::string event, const std::string& a2 = "", const std::string& a3 = "", const std::string& a4 = "");
void run_call_hook(std::string event, int call_id = 0, std::string nr = "", std::string data = "");
std::list<callback_type>::iterator register_callback(callback_type callback);
void unregister_callback(std::list<callback_type>::iterator id);
};
}
......
......@@ -48,12 +48,13 @@ void print_usage() {
<< "p3 h angup [id] - hanup" << std::endl
<< "p3 ha ngupall - hanup all current calls" << std::endl
<< "p3 p ause, hold [id] - pause/hold/reinvite (will play a sound or send HOLD)" << std::endl
<< "p3 m ute [id] - mute ringtone if ringing, else mute microphone." << std::endl
<< "p3 m ute [id] - mute ringtone if ringing, else mute microphone" << std::endl
<< "p3 ma muteall - mute microphone for all calls." << std::endl
<< "p3 j oin - conference current calls" << std::endl
<< "p3 t ransfer [id] [nr] - forward call" << std::endl
<< "p3 d tmf [dgts] [id] - dial dtfm digits" << std::endl
<< "p3 st atus - dump server status" << std::endl
<< "p3 e vents - Toggle push events" << std::endl
<< "-I for an interactive shell" << std::endl
<< "-v for verbose output (not with serve, can be specified multiple times for more output)" << std::endl;
}
......
......@@ -92,7 +92,7 @@ namespace p3 {
class mQueueException : public perror {
public:
mQueueException(std::string c, std::string e) : perror("p3mq:" + c, e) {}
mQueueException(const std::string & c, const std::string & e) : perror("p3mq:" + c, e) {}
};
}
......
......@@ -43,6 +43,7 @@ std::map<std::string, std::string> p3::server::default_config {
{"phone:ringtone", "/usr/share/3phone/rt.wav"},
{"phone:holdmusic", "none"},
{"phone:busy_on_busy", "true"},
{"phone:busyall_exceptions", "none"}
};
......@@ -105,7 +106,7 @@ void p3::server::dispatch_thread(const std::string& id) {
void p3::server::server_thread(const std::string& id) {
phone.register_thread(id);
try {
p3::clientHandler c(id, conf, phone, book);
p3::clientHandler c(id, conf, phone, book, hook);
c.run();
} catch (p3::perror & e) {
std::cout << "Error in client " << id << ": " << e.what() << std::endl;
......@@ -128,10 +129,13 @@ bool p3::server::get_terminate_threads() {
return terminate_threads.load();
}
p3::clientHandler::clientHandler(std::string i, p3::config& c, p3::sipphone& p, p3::phonebook& b) :
conf(c), phone(p), book(b), id(i), connection(i, false), listenTimeout(300) {}
p3::clientHandler::clientHandler(const std::string& i, p3::config& c, p3::sipphone& p, p3::phonebook& b, p3::eventHook& h) :
conf(c), phone(p), book(b), hook(h), id(i), connection(i, false), listenTimeout(300), push_events_enabled(false) {}
p3::clientHandler::~clientHandler() {
if (push_events_enabled) {
hook.unregister_callback(push_event_callback);
}
connection.send(p3::protocol::BYE, "Closing connection");
}
......@@ -141,6 +145,7 @@ void p3::clientHandler::run() {
bool r = true;
while (r && !p3::server::get_terminate_threads()) {
p3::mQueueMessage m = connection.receive(listenTimeout);
std::lock_guard<std::recursive_mutex> g(connection_write_mutex);
switch (m.get_command()) {
case p3::protocol::COMMAND:
parse_command(m.get_value());
......@@ -153,8 +158,8 @@ void p3::clientHandler::run() {
r = false;
break;
default:
std::cout << "Protokol Error, invalid command in " << id << ": " << m.get_value() << std::endl;
connection.send(p3::protocol::ERROR, "PROTOKOLL ERROR, unknown command.");
std::cout << "Protocol Error, invalid command in " << id << ": " << m.get_value() << std::endl;
connection.send(p3::protocol::ERROR, "PROTOCOL ERROR, unknown command.");
}
}
} catch (p3::perror & e) {
......@@ -197,6 +202,10 @@ void p3::clientHandler::parse_command(const std::string& c) {
handle_dtmf();
} else if ("status" == c) {
handle_status();
} else if ("startevents" == c) {
handle_start_push_events();
} else if ("stopevents" == c) {
handle_stop_push_events();
} else {
std::cout << "Unknown command received: " << c << std::endl;
connection.send(p3::protocol::ERROR, "Invalid Command '" + c + "'");
......@@ -284,6 +293,7 @@ void p3::clientHandler::handle_transfer() {
void p3::clientHandler::handle_reload() {
conf.load_config(); // Doesn't reload anythig concerning pjsip for example.
book.load();
phone.load_busyall_exceptions();
connection.send(p3::protocol::OK, "reload OK");
}
......@@ -314,6 +324,57 @@ void p3::clientHandler::handle_status() {
connection.send(p3::protocol::OK, "Status OK");
}
void p3::clientHandler::handle_start_push_events() {
if (push_events_enabled) {
connection.send(p3::protocol::ERROR, "Events already enabled.");
return;
}
push_events_enabled = true;
push_event_callback = hook.register_callback(
std::bind(
&p3::clientHandler::push_event,
this,
std::placeholders::_1,
std::placeholders::_2,
std::placeholders::_3,
std::placeholders::_4
)
);
connection.send(p3::protocol::OK, "Events enabled");
}
void p3::clientHandler::handle_stop_push_events() {
if (!push_events_enabled) {
connection.send(p3::protocol::ERROR, "Events not enabled.");
return;
}
hook.unregister_callback(push_event_callback);
push_events_enabled = false;
connection.send(p3::protocol::OK, "Events disabled");
}
void p3::clientHandler::push_event(
const std::string& e,
const std::string& a2,
const std::string& a3,
const std::string& a4
) {
std::lock_guard<std::recursive_mutex> g(connection_write_mutex);
connection.send(p3::protocol::BEGINTEXT, "Event");
connection.send(p3::protocol::TEXT, e);
if (!a2.empty()) {
connection.send(p3::protocol::TEXT, a2);
}
if (!a3.empty()) {
connection.send(p3::protocol::TEXT, a3);
}
if (!a4.empty()) {
connection.send(p3::protocol::TEXT, a4);
}
connection.send(p3::protocol::ENDTEXT, "End Event");
}
int p3::clientHandler::get_call_id(const std::string& q, p3::callState filter) {
int nr = phone.get_call_count(filter);
if (nr == 1) {
......
......@@ -22,6 +22,7 @@
#include <string>
#include <thread>
#include <atomic>
#include <mutex>
#include <list>
#include <map>
......@@ -101,12 +102,17 @@ namespace p3 {
config & conf;
sipphone & phone;
phonebook & book;
eventHook & hook;
std::string id;
mQueue connection;
std::recursive_mutex connection_write_mutex;
int listenTimeout;
bool push_events_enabled;
std::list<p3::eventHook::callback_type>::iterator push_event_callback;
void parse_command(const std::string& c);
void handle_call();
void handle_hangup();
......@@ -124,6 +130,8 @@ namespace p3 {
void handle_transfer();
void handle_dtmf();
void handle_status();
void handle_start_push_events();
void handle_stop_push_events();
void send_calls_list(
callState filter = callState::NONE,
......@@ -134,8 +142,15 @@ namespace p3 {
callState filter = callState::NONE
);
void push_event(
const std::string& event,
const std::string& a2 = "",
const std::string& a3 = "",
const std::string& a4 = ""
);
public:
clientHandler(std::string i, config & c, sipphone & p, phonebook & b);
clientHandler(const std::string & i, config & c, sipphone & p, phonebook & b, eventHook & h);
~clientHandler();
void run();
......
......@@ -17,6 +17,7 @@
**/
#include <iostream>
#include <algorithm>
#include "error.h"
#include "sipphone.h"
......@@ -31,6 +32,8 @@ p3::sipphone::sipphone(p3::config& c, p3::eventHook& e, p3::phonebook& b) :
always_busy(false),
ring_index(0)
{
load_busyall_exceptions();
// Initiate pj
endpoint = new pj::Endpoint;
......@@ -151,6 +154,12 @@ void p3::sipphone::unregister_thread(const std::string& id) {
pj_thread_destroy(pj_threads[id].first);
}
void p3::sipphone::load_busyall_exceptions() {
for (const std::string &n : conf.get_string_list("phone:busyall_exceptions")) {
busyall_exceptions.push_back(book.find_nr(n));
}
}
void p3::sipphone::start_ringing() {
if (ringtone == nullptr) return;
......@@ -207,8 +216,17 @@ void p3::sipphone::handle_incoming_call(int pjid) {
book.add(c->get_nr());
}
bool isBusy = (get_call_count() > 1 && conf.get_bool("phone:busy_on_busy"));
if ((!isBusy) && always_busy) {
isBusy = std::find(
busyall_exceptions.begin(),
busyall_exceptions.end(),
c->get_nr()
) == busyall_exceptions.end();
}
pj::CallOpParam o;
if (always_busy || (get_call_count() > 1 && conf.get_bool("phone:busy_on_busy"))) {
if (isBusy) {
eventhook.run_hook(
"auto_busy",
book.address_by_nr(c->get_nr()),
......
......@@ -60,6 +60,8 @@ namespace p3 {
bool mute;
bool always_busy;
std::list<std::string> busyall_exceptions;
protected:
eventHook& get_event_hook();
......@@ -115,6 +117,8 @@ namespace p3 {
std::map<std::string, std::string> get_status_dump();
void load_busyall_exceptions();
friend class call;
friend class account;
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment