diff --git a/conf.d/regfile.conf.sample b/conf.d/regfile.conf.sample index faa48f76..88837e2c 100644 --- a/conf.d/regfile.conf.sample +++ b/conf.d/regfile.conf.sample @@ -16,10 +16,36 @@ ; ; route: int: Priority of the call.route handler ;route=100 +; +; file: string. An auxiliary conf file used to save and load from it autocreated and registered entries +; If file don't exists the registered entities will be lost on reload +;file=filename ; you have to put username as a category and password into key password ; if password is missing the module will only register but not authenticate ; if password exists and is empty will blindly authenticate anybody + + ;[username] ;password=something + +; alternatives: list of called party numbers separated by coma +; Note if multiple users share the same party number the call will go to all users. +;alternatives= num1,num2,.... +; +; Set/Modify call.route parameters when the caller matches this section +; You can put call.route replacement ${variables} on the right side +; Note: Any parameter except for "password" and "alternatives" will be used for set/modify +; +; Ex: +; Modify caller name +;callername=name +; +; Modify caller number +;caller=number +; +; Replacement example +;callername=John Doe from ${address} +; +;param=value diff --git a/modules/server/regfile.cpp b/modules/server/regfile.cpp index 97af8061..d6634c02 100644 --- a/modules/server/regfile.cpp +++ b/modules/server/regfile.cpp @@ -5,7 +5,7 @@ * Ask for a registration from this module. * * Yet Another Telephony Engine - a fully featured software PBX and IVR - * Copyright (C) 2004-2006 Null Team + * Copyright (C) 2004-2010 Null Team * * 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 @@ -27,13 +27,6 @@ using namespace TelEngine; namespace { // anonymous -Mutex lmutex(false,"RegFile"); - -static Configuration s_cfg(Engine::configFile("regfile")); - -static const String s_general = "general"; -static bool s_create = false; - class AuthHandler : public MessageHandler { @@ -62,9 +55,11 @@ public: class RouteHandler : public MessageHandler { public: - RouteHandler(const char *name, unsigned prio = 100) - : MessageHandler(name,prio) { } + RouteHandler(const char *name, unsigned prio = 100); + ~RouteHandler(); virtual bool received(Message &msg); +private: + ObjList* m_skip; }; class StatusHandler : public MessageHandler @@ -75,144 +70,308 @@ public: virtual bool received(Message &msg); }; +class ExpireHandler : public MessageHandler +{ +public: + ExpireHandler() + : MessageHandler("engine.timer",100) { } + 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; +}; + class RegfilePlugin : public Plugin { public: RegfilePlugin(); - ~RegfilePlugin(){} + ~RegfilePlugin(); virtual void initialize(); + void populate(); private: - AuthHandler *m_authhandler; + 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; + +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; +} + bool AuthHandler::received(Message &msg) { String username(msg.getValue("username")); if (username.null() || username == s_general) return false; - const NamedList* sect = s_cfg.getSection(username); - if (sect) { - const String* pass = sect->getParam("password"); - if (!pass) - return false; - msg.retValue() = *pass; - return true; - } - 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; + return true; +} bool RegistHandler::received(Message &msg) { 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"); + const char* driver = msg.getValue("driver"); + const char* data = msg.getValue("data"); if (!data) return false; - - Lock lock(lmutex); + Lock lock(s_mutex); + int expire = msg.getIntValue("expires",0); NamedList* sect = s_cfg.getSection(username); if (s_create && !sect) { - Debug("RegFile",DebugNote,"Auto creating new user '%s'",username.c_str()); - s_cfg.createSection(username); - sect = s_cfg.getSection(username); + sect = new NamedList(username); + Debug("RegFile",DebugInfo,"Auto creating new user %s",username.c_str()); } - if (sect) { - Debug("RegFile",DebugInfo,"Registered '%s' via '%s'",username.c_str(),data); - sect->setParam("driver",driver); - sect->setParam("data",data); - return true; - } - return false; -}; + if (!sect) + return false; + s_accounts.createSection(*sect); + NamedList* s = s_accounts.getSection(*sect); + if (driver) + s->setParam("driver",driver); + s->setParam("data",data); + if (expire) + s->setParam("expires",String(msg.msgTime().sec() + expire)); + Debug("RegFile",DebugAll,"Registered user %s via %s",username.c_str(),data); + return true; +} bool UnRegistHandler::received(Message &msg) { String username(msg.getValue("username")); if (username.null() || username == s_general) return false; - - Lock lock(lmutex); - if (s_cfg.getSection(username)) { - Debug("RegFile",DebugInfo,"Unregistered '%s'",username.c_str()); - s_cfg.clearKey(username,"data"); - return true; - } - return false; -}; + Lock lock(s_mutex); + NamedList* nl = s_accounts.getSection(username); + if (!nl) + return false; + Debug("RegFile",DebugAll,"Removing user %s, reson unregistered",username.c_str()); + s_accounts.clearSection(username); + return true; +} + +RouteHandler::RouteHandler(const char *name, unsigned prio) + : MessageHandler(name,prio) +{ + m_skip = String("alternatives,password").split(','); +} + +RouteHandler::~RouteHandler() +{ + TelEngine::destruct(m_skip); +} bool RouteHandler::received(Message &msg) { + 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; - - Lock lock(lmutex); - const NamedList* sect = s_cfg.getSection(username); - if (sect) { - const char* data = sect->getValue("data"); - if (data) { - Debug("RegFile",DebugInfo,"Routed '%s' via '%s'",username.c_str(),data); - msg.retValue() = data; - msg.setParam("driver",sect->getValue("driver")); - return true; + NamedList* ac = s_accounts.getSection(username); + while (true) { + String data; + if (!ac) { + if (s_cfg.getSection(username)) + break; + ObjList* o = s_expand.find(username); + if (!o) + break; + ExpandedUser* eu = static_cast(o->get()); + if (!eu) + break; + String d; + int count = 0; + for (ObjList* ob = eu->skipNull(); ob;ob = ob->skipNext()) { + String* s = static_cast(ob->get()); + if (!s) + continue; + NamedList* n = s_accounts.getSection(*s); + if (!n) + continue; + if (count > 0) + d << " "; + d << n->getValue("data"); + count ++; + } + if (count > 1) + data = "fork "; + data << d; + } else { + data = ac->getValue("data"); + msg.copyParams(*ac,"driver"); } - // signal to other modules we know about this user but it's offline - msg.setParam("error","offline"); + msg.retValue() = data; + Debug("RegFile",DebugInfo,"Routed '%s' via '%s'",username.c_str(),data.c_str()); + return true; } + msg.setParam("error","offline"); return false; -}; +} + +static int s_count = 0; + +bool ExpireHandler::received(Message &msg) +{ + if ((s_count = (s_count+1) % 30)) // Check for timeouts once at 30 seconds + return false; + u_int64_t 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("RegFile",DebugAll,"Removing user %s, Reson: 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) { + bool first = true; String dest(msg.getValue("module")); if (dest && (dest != "regfile") && (dest != "misc")) - return false; - Lock lock(lmutex); - unsigned int n = s_cfg.sections(); - if (!s_cfg.getSection(0)) - --n; - msg.retValue() << "name=regfile,type=misc;users=" << n << ",create=" << s_create; - if (msg.getBoolValue("details",true)) { - msg.retValue() << ";"; - bool first = true; - for (unsigned int i=0;igetValue("data"); + if (first) + first = false; + else + msg.retValue() << ","; + if (data) + msg.retValue() << *ac << "=" << data; } msg.retValue() << "\r\n"; return false; } RegfilePlugin::RegfilePlugin() - : m_authhandler(0) + : 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 for file"); - if (!m_authhandler) { - s_cfg.load(); - s_create = s_cfg.getBoolValue("general","autocreate"); - Engine::install(m_authhandler = new AuthHandler("user.auth",s_cfg.getIntValue("general","auth",100))); + Output("Initializing module Register from file"); + Lock lock(s_mutex); + s_cfg.load(); + if (!m_init) { + m_init = true; + s_create = s_cfg.getBoolValue("general","autocreate",false); + String conf = s_cfg.getValue("general","file"); + 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 ExpireHandler()); + } + populate(); +} + +void RegfilePlugin::populate() +{ + 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; + 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(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(ret->get()); + eu->append(new String(*nl)); + } + } + if (s_create) + return; + count = s_accounts.sections(); + for (int i = 0;i < count;i++) { + NamedList* nl = s_accounts.getSection(i); + if (!nl) + continue; + if (s_cfg.getSection(*nl)) + continue; + s_accounts.clearSection(*nl); + count--; + i--; } }