yate/modules/server/regfile.cpp

520 lines
14 KiB
C++

/**
* regfile.cpp
* This file is part of the YATE Project http://YATE.null.ro
*
* Ask for a registration from this module.
*
* Yet Another Telephony Engine - a fully featured software PBX and IVR
* Copyright (C) 2004-2013 Null Team
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
* information for this specific distribution.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
*
* 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.
*/
#include <yatengine.h>
using namespace TelEngine;
namespace { // anonymous
class RegfilePlugin : public Plugin
{
public:
RegfilePlugin();
~RegfilePlugin();
virtual void initialize();
void populate(bool first);
private:
bool m_init;
};
Mutex s_mutex(false,"RegFile");
static Configuration s_cfg(Engine::configFile("regfile"));
static Configuration s_accounts;
static bool s_create = false;
static const String s_general = "general";
static ObjList s_expand;
static int s_count = 0;
INIT_PLUGIN(RegfilePlugin);
class AuthHandler : public MessageHandler
{
public:
AuthHandler(const char *name, unsigned prio = 100)
: MessageHandler(name,prio,__plugin.name())
{ }
virtual bool received(Message &msg);
};
class RegistHandler : public MessageHandler
{
public:
RegistHandler(const char *name, unsigned prio = 100)
: MessageHandler(name,prio,__plugin.name())
{ }
virtual bool received(Message &msg);
};
class UnRegistHandler : public MessageHandler
{
public:
UnRegistHandler(const char *name, unsigned prio = 100)
: MessageHandler(name,prio,__plugin.name())
{ }
virtual bool received(Message &msg);
};
class RouteHandler : public MessageHandler
{
public:
RouteHandler(const char *name, unsigned prio = 100);
~RouteHandler();
virtual bool received(Message &msg);
private:
ObjList* m_skip;
};
class StatusHandler : public MessageHandler
{
public:
StatusHandler(const char *name, unsigned prio = 100)
: MessageHandler(name,prio,__plugin.name())
{ }
virtual bool received(Message &msg);
};
class CommandHandler : public MessageHandler
{
public:
CommandHandler(const char *name, unsigned prio = 100)
: MessageHandler(name,prio,__plugin.name())
{ }
virtual bool received(Message &msg);
};
class ExpireHandler : public MessageHandler
{
public:
ExpireHandler()
: MessageHandler("engine.timer",100,__plugin.name())
{ }
virtual bool received(Message &msg);
};
class ExpandedUser : public ObjList
{
public:
inline ExpandedUser(const String& username)
: m_username(username) {}
virtual const String& toString () const
{ return m_username; }
private:
String m_username;
};
static void clearListParams(NamedList& list, const char* name)
{
if (TelEngine::null(name))
return;
NamedString* param = list.getParam(name);
if (!param)
return;
ObjList* l = param->split(',',false);
for (ObjList* o = l->skipNull(); o; o = o->skipNext())
list.clearParam(o->get()->toString());
list.clearParam(param);
TelEngine::destruct(l);
}
bool expired(const NamedList& list, unsigned int time)
{
// If eTime is 0 the registration will never expire
unsigned int eTime = list.getIntValue("expires",0);
return eTime && eTime < time;
}
// Copy list parameters
static inline void regfileCopyParams(NamedList& dest, NamedList& src, const String& params,
const String& extra)
{
String s = params;
s.append(extra,",");
dest.copyParams(src,s);
}
bool AuthHandler::received(Message &msg)
{
if (!msg.getBoolValue(YSTRING("auth_regfile"),true))
return false;
String username(msg.getValue("username"));
if (username.null() || username == s_general)
return false;
Lock lock(s_mutex);
const NamedList* usr = s_cfg.getSection(username);
if (!usr)
return false;
const String* pass = usr->getParam("password");
if (!pass)
return false;
msg.retValue() = *pass;
Debug(&__plugin,DebugAll,"Authenticating user %s with password length %u",
username.c_str(),pass->length());
return true;
}
bool RegistHandler::received(Message &msg)
{
if (!msg.getBoolValue(YSTRING("register_regfile"),true))
return false;
String username(msg.getValue("username"));
if (username.null() || username == s_general)
return false;
const char* driver = msg.getValue("driver");
const char* data = msg.getValue("data");
if (!data)
return false;
Lock lock(s_mutex);
int expire = msg.getIntValue("expires",0);
NamedList* sect = s_cfg.getSection(username);
if (!sect) {
if (!s_create)
return false;
Debug(&__plugin,DebugInfo,"Auto creating new user %s",username.c_str());
}
NamedList* s = s_accounts.createSection(username);
if (driver)
s->setParam("driver",driver);
s->setParam("data",data);
// Clear existing route parameters
clearListParams(*s,"route_params");
const String& route = msg["route_params"];
if (route) {
s->copyParams(msg,route);
s->setParam("route_params",route);
}
s->clearParam("connection",'_');
s->copyParams(msg,"connection",'_');
if (expire)
s->setParam("expires",String(msg.msgTime().sec() + expire));
#ifdef DEBUG
String tmp;
s->dump(tmp," ");
Debug(&__plugin,DebugAll,"Registered user %s",tmp.c_str());
#else
Debug(&__plugin,DebugAll,"Registered user %s via %s",username.c_str(),data);
#endif
return true;
}
bool UnRegistHandler::received(Message &msg)
{
if (!msg.getBoolValue(YSTRING("register_regfile"),true))
return false;
const String& username = msg["username"];
if (username) {
if (username == s_general)
return false;
Lock lock(s_mutex);
NamedList* nl = s_accounts.getSection(username);
if (!nl)
return false;
Debug(&__plugin,DebugAll,"Removing user %s, reason unregistered",username.c_str());
s_accounts.clearSection(username);
return true;
}
const String& conn = msg["connection_id"];
if (!conn)
return false;
Lock lock(s_mutex);
ObjList remove;
unsigned int n = s_accounts.sections();
for (unsigned int i = 0; i < n; i++) {
NamedList* nl = s_accounts.getSection(i);
if (nl && (*nl)["connection_id"] == conn)
remove.append(new String(*nl));
}
for (ObjList* o = remove.skipNull(); o; o = o->skipNext()) {
String* s = static_cast<String*>(o->get());
Debug(&__plugin,DebugAll,"Removing user %s, reason connection down",s->c_str());
s_accounts.clearSection(*s);
}
return false;
}
RouteHandler::RouteHandler(const char *name, unsigned prio)
: MessageHandler(name,prio,__plugin.name())
{
m_skip = String("alternatives,password").split(',');
}
RouteHandler::~RouteHandler()
{
TelEngine::destruct(m_skip);
}
bool RouteHandler::received(Message &msg)
{
if (!msg.getBoolValue(YSTRING("route_regfile"),true))
return false;
String user = msg.getValue("caller");
Lock lock(s_mutex);
NamedList* params = 0;
if (user) {
params = s_cfg.getSection(user);
if (params) {
unsigned int n = params->length();
for (unsigned int i = 0; i < n; i++) {
const NamedString* s = params->getParam(i);
if (s && !m_skip->find(s->name())) {
String value = *s;
msg.replaceParams(value);
msg.setParam(s->name(),value);
}
}
}
}
String username(msg.getValue("called"));
if (username.null() || username == s_general)
return false;
NamedList* ac = s_accounts.getSection(username);
while (true) {
String data;
String extra;
if (!ac) {
if (s_cfg.getSection(username)) {
msg.setParam("error","offline");
break;
}
ObjList* o = s_expand.find(username);
if (!o)
break;
ExpandedUser* eu = static_cast<ExpandedUser*>(o->get());
if (!eu)
break;
ObjList targets;
int count = 0;
for (ObjList* ob = eu->skipNull(); ob;ob = ob->skipNext()) {
String* s = static_cast<String*>(ob->get());
if (!s)
continue;
NamedList* n = s_accounts.getSection(*s);
if (!n)
continue;
targets.append(n)->setDelete(false);
count ++;
}
if (count == 0) {
msg.setParam("error","offline");
break;
}
if (count == 1) {
NamedList* n = static_cast<NamedList*>(targets.skipNull()->get());
data = (*n)["data"];
regfileCopyParams(msg,*n,(*n)["route_params"],"driver");
}
else {
int callto = 1;
data = "fork";
for (ObjList* o = targets.skipNull(); o; o = o->skipNext()) {
NamedList* n = static_cast<NamedList*>(o->get());
String prefix;
prefix << "callto." << callto++;
const String& d = (*n)["data"];
NamedList* target = new NamedList(d);
msg.addParam(new NamedPointer(prefix,target,d));
extra << " " << d;
regfileCopyParams(*target,*n,(*n)["route_params"],"driver");
}
}
} else {
data = ac->getValue("data");
regfileCopyParams(msg,*ac,(*ac)["route_params"],"driver");
}
msg.retValue() = data;
Debug(&__plugin,DebugInfo,"Routed '%s' via '%s%s'",username.c_str(),
data.c_str(),extra.c_str());
return true;
}
return false;
}
bool ExpireHandler::received(Message &msg)
{
if ((s_count = (s_count+1) % 30)) // Check for timeouts once at 30 seconds
return false;
unsigned int time = msg.msgTime().sec();
Lock lock(s_mutex);
int count = s_accounts.sections();
for (int i = 0;i < count;) {
NamedList* sec = s_accounts.getSection(i);
if (sec && *sec != s_general && expired(*sec,time)) {
Debug(&__plugin,DebugAll,"Removing user %s, Reason: Registration expired",sec->c_str());
s_accounts.clearSection(*sec);
count--;
} else
i++;
}
if (s_accounts)
s_accounts.save();
return false;
}
bool StatusHandler::received(Message &msg)
{
String dest(msg.getValue("module"));
if (dest && (dest != "regfile") && (dest != "misc"))
return false;
Lock lock(s_mutex);
unsigned int n = s_cfg.sections();
if (s_cfg.getSection("general") || !s_cfg.getSection(0))
n--;
msg.retValue() << "name=regfile,type=misc;create=" << s_create;
msg.retValue() << ",defined=" << n;
unsigned int usrCount = 0;
String tmp;
bool details = msg.getBoolValue("details",true);
unsigned int count = s_accounts.sections();
for (unsigned int i = 0; i < count; i ++) {
NamedList* ac = s_accounts.getSection(i);
if (!ac)
continue;
String data = ac->getValue("data");
if (data.null())
continue;
usrCount++;
if (!details)
continue;
if (tmp.null())
tmp << ";";
else
tmp << ",";
for (char* s = const_cast<char*>(data.c_str()); *s; s++) {
if (*s < ' ' || *s == ',')
*s = '?';
}
tmp << *ac << "=" << data;
}
msg.retValue() << ",users=" << usrCount;
msg.retValue() << tmp << "\r\n";
return false;
}
bool CommandHandler::received(Message &msg)
{
if (msg.getValue("line"))
return false;
if (msg["partline"] == "status") {
const String& partWord = msg["partword"];
if (partWord.null() || __plugin.name().startsWith(partWord))
msg.retValue().append(__plugin.name(),"\t");
}
return false;
}
RegfilePlugin::RegfilePlugin()
: Plugin("regfile"),
m_init(false)
{
Output("Loaded module Registration from file");
}
RegfilePlugin::~RegfilePlugin()
{
Output("Unload module Registration from file");
if (s_accounts)
s_accounts.save();
}
void RegfilePlugin::initialize()
{
Output("Initializing module Register from file");
Lock lock(s_mutex);
s_cfg.load();
bool first = !m_init;
if (!m_init) {
m_init = true;
s_create = s_cfg.getBoolValue("general","autocreate",false);
String conf = s_cfg.getValue("general","file");
Engine::self()->runParams().replaceParams(conf);
if (conf) {
s_accounts = conf;
s_accounts.load();
}
Engine::install(new AuthHandler("user.auth",s_cfg.getIntValue("general","auth",100)));
Engine::install(new RegistHandler("user.register",s_cfg.getIntValue("general","register",100)));
Engine::install(new UnRegistHandler("user.unregister",s_cfg.getIntValue("general","register",100)));
Engine::install(new RouteHandler("call.route",s_cfg.getIntValue("general","route",100)));
Engine::install(new StatusHandler("engine.status"));
Engine::install(new CommandHandler("engine.command"));
Engine::install(new ExpireHandler());
}
populate(first);
}
void RegfilePlugin::populate(bool first)
{
s_expand.clear();
int count = s_cfg.sections();
for (int i = 0;i < count; i++) {
NamedList* nl = s_cfg.getSection(i);
if (!nl || *nl == s_general)
continue;
DDebug(this,DebugAll,"Loaded account '%s'",nl->c_str());
String* ids = nl->getParam("alternatives");
if (!ids)
continue;
ObjList* ob = ids->split(',');
for (ObjList* o = ob->skipNull(); o;o = o->skipNext()) {
String* sec = static_cast<String*>(o->get());
if (!sec)
continue;
ObjList* ret = s_expand.find(*sec);
ExpandedUser* eu = 0;
if (!ret) {
eu = new ExpandedUser(*sec);
s_expand.append(eu);
} else
eu = static_cast<ExpandedUser*>(ret->get());
eu->append(new String(*nl));
DDebug(this,DebugAll,"Added alternative '%s' for account '%s'",sec->c_str(),nl->c_str());
}
TelEngine::destruct(ob);
}
if (s_create)
return;
count = s_accounts.sections();
for (int i = 0;i < count;i++) {
NamedList* nl = s_accounts.getSection(i);
if (!nl)
continue;
// Delete saved accounts logged in on reliable connections on first load
bool exist = s_cfg.getSection(*nl) != 0;
if (exist && !(first && nl->getBoolValue("connection_reliable"))) {
DDebug(this,DebugAll,"Loaded saved account '%s'",nl->c_str());
continue;
}
DDebug(this,DebugAll,"Not loading saved account '%s': %s",
nl->c_str(),exist ? "logged in on reliable connection" : "account deleted");
s_accounts.clearSection(*nl);
count--;
i--;
}
}
}; // anonymous namespace
/* vi: set ts=8 sw=4 sts=4 noet: */