yate/modules/server/users.cpp

475 lines
12 KiB
C++

/**
* users.cpp
* This file is part of the YATE Project http://YATE.null.ro
*
* Users module
*
* Yet Another Telephony Engine - a fully featured software PBX and IVR
* Copyright (C) 2004-2009 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
* 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 St, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <yatephone.h>
using namespace TelEngine;
namespace {
class UsersModule;
class UserUpdateHandler;
/**
* Class UserUpdateHandler
* Handles a user.update message
*/
class UserUpdateHandler : public MessageHandler
{
public:
inline UserUpdateHandler(unsigned int priority = 100)
: MessageHandler("user.update", priority)
{ }
virtual ~UserUpdateHandler()
{ }
virtual bool received(Message& msg);
};
/**
* Class UsersModule
* Module for handling users operations
*/
class UsersModule : public Module
{
public:
enum Commands {
CmdUpdate = 1,
CmdAdd,
CmdDelete,
};
UsersModule();
virtual ~UsersModule();
// Check if a message was sent by us
inline bool isModule(const Message& msg) {
String* module = msg.getParam("module");
return module && *module == name();
}
// Build a message. Fill the module parameter
inline Message* message(const char* msg) {
Message* m = new Message(msg);
m->addParam("module",name());
return m;
}
//inherited methods
virtual void initialize();
// uninstall relays and message handlers
bool unload();
// User management
bool addUser(const NamedList& params, String* error = 0);
bool deleteUser(const NamedList& params, String* error = 0);
bool updateUser(const NamedList& params, String* error = 0);
bool searchUser(const NamedList& params);
// Notify user changes add/del/update
void notifyUser(const char* user, const char* notify);
// Build and dispatch a database message. Return true on success
bool queryDb(const String& account, const String& query,
const NamedList& params, String& error, bool search = false);
// parse the command line
bool parseParams(const String& line, NamedList& parsed, String& error);
// Module commands
static const TokenDict s_cmds[];
protected:
// inherited methods
virtual bool received(Message& msg, int id);
virtual bool commandExecute(String& retVal, const String& line);
virtual bool commandComplete(Message& msg, const String& partLine, const String& partWord);
private:
//flag for first time initialization
bool m_init;
// Query strings
// SQL statement for inserting to database
String m_insertDB;
// SQL statement for updating information in the database
String m_updateDB;
String m_removeDB;
// SQL statement for interrogating the database about a contact with a resource
String m_selectDB;
// database connection
String m_accountDB;
UserUpdateHandler* m_updateHandler;
};
INIT_PLUGIN(UsersModule);
const TokenDict UsersModule::s_cmds[] = {
{"update", CmdUpdate},
{"add", CmdAdd},
{"delete", CmdDelete},
{0,0}
};
static const char* s_cmdsLine = "users {add user [parameter=value...]|delete user|update user [parameter=value...]}";
UNLOAD_PLUGIN(unloadNow)
{
if (unloadNow && !__plugin.unload())
return false;
return true;
}
// Get a space separated word from a buffer. msgUnescape() it if requested
// Return false if empty
static bool getWord(String& buf, String& word, bool unescape = false)
{
XDebug(&__plugin,DebugAll,"getWord(%s)",buf.c_str());
int pos = buf.find(" ");
if (pos >= 0) {
word = buf.substr(0,pos);
buf = buf.substr(pos + 1);
}
else {
word = buf;
buf = "";
}
if (!word)
return false;
if (unescape)
word.msgUnescape();
return true;
}
/**
* UserUpdateHandler
*/
// could change
bool UserUpdateHandler::received(Message& msg)
{
if (__plugin.isModule(msg))
return false;
// TODO
// see message parameters and what to do with them
String* operation = msg.getParam("operation");
String* user = msg.getParam("user");
if (TelEngine::null(operation) || TelEngine::null(user)) {
msg.setParam("error","Mandatory parameters missing");
return false;
}
NamedList params("");
params.addParam("user",*user);
params.copyParams(msg,"password");
String msgPrefix = msg.getValue("message-prefix");
if (!msgPrefix.null()) {
msgPrefix << ".";
for (unsigned int i = 0; i < msg.length(); i++) {
NamedString* ns = msg.getParam(i);
if (ns && ns->name().startsWith(msgPrefix))
params.addParam(ns->name().substr(msgPrefix.length()),*ns);
}
}
bool ok = false;
if (*operation == "add")
ok = __plugin.addUser(params);
else if (*operation == "delete")
ok = __plugin.deleteUser(params);
else if (*operation == "update")
ok = __plugin.updateUser(params);
else
return false;
if (ok)
__plugin.notifyUser(*user,*operation);
else
msg.setParam("error","failure");
return ok;
}
/**
* UsersModule
*/
UsersModule:: UsersModule()
: Module("users","misc"),
m_updateHandler(0)
{
Output("Loaded module Users Management");
}
UsersModule::~UsersModule()
{
Output("Unloaded module Users Management");
TelEngine::destruct(m_updateHandler);
}
void UsersModule::initialize()
{
Output("Initializing module Users Management");
Configuration cfg(Engine::configFile("users"));
cfg.load();
if (!m_init) {
m_init = true;
setup();
installRelay(Halt);
installRelay(Help);
m_updateHandler = new UserUpdateHandler();
Engine::install(m_updateHandler);
// queries init
m_insertDB = cfg.getValue("database", "add_user");
m_updateDB = cfg.getValue("database", "update_user");
m_removeDB = cfg.getValue("database", "remove_user");
m_selectDB = cfg.getValue("database", "select_user");
// database connection init
m_accountDB = cfg.getValue("database", "account");
}
}
bool UsersModule::unload()
{
DDebug(this,DebugAll,"unload()");
if (!lock(500000))
return false;
uninstallRelays();
Engine::uninstall(m_updateHandler);
unlock();
return true;
}
bool UsersModule::addUser(const NamedList& params, String* error)
{
if (TelEngine::null(m_insertDB))
return false;
String tmp;
if (!error)
error = &tmp;
if (!searchUser(params)) {
if (queryDb(m_accountDB,m_insertDB,params,*error)) {
Debug(this,DebugAll,"Added user '%s'",params.getValue("user"));
return true;
}
if (error->null())
*error = "Failure";
}
else
*error = "Already exists";
Debug(this,DebugInfo,"Failed to add user '%s' error='%s'",
params.getValue("user"),error->c_str());
return false;
}
bool UsersModule::deleteUser(const NamedList& params, String* error)
{
if (TelEngine::null(m_removeDB))
return false;
String tmp;
if (!error)
error = &tmp;
if (queryDb(m_accountDB,m_removeDB,params,*error)) {
Debug(this,DebugAll,"Deleted user '%s'",params.getValue("user"));
return true;
}
if (error->null())
*error = "User not found";
Debug(this,DebugInfo,"Failed to delete user '%s' error='%s'",
params.getValue("user"),error->c_str());
return false;
}
bool UsersModule::updateUser(const NamedList& params, String* error)
{
if (TelEngine::null(m_updateDB))
return false;
String tmp;
if (!error)
error = &tmp;
if (queryDb(m_accountDB,m_updateDB,params,*error)) {
Debug(this,DebugAll,"Updated user '%s'",params.getValue("user"));
return true;
}
if (!error)
*error = "User not found";
Debug(this,DebugInfo,"Failed to update user '%s' error='%s'",
params.getValue("user"),error->c_str());
return false;
}
bool UsersModule::searchUser(const NamedList& params)
{
if (TelEngine::null(m_selectDB))
return false;
String error;
return queryDb(m_accountDB,m_selectDB,params,error,true);
}
// Notify user changes add/del/update
void UsersModule::notifyUser(const char* user, const char* notify)
{
Message* m = __plugin.message("user.update");
m->addParam("notify",notify);
m->addParam("user",user);
Engine::enqueue(m);
}
// Build and dispatch a database message. Return true on success
bool UsersModule::queryDb(const String& account, const String& query,
const NamedList& params, String& error, bool search)
{
Message msg("database");
msg.addParam("module",name());
msg.addParam("account", account);
String tmp = query;
params.replaceParams(tmp,true);
msg.addParam("query", tmp);
msg.addParam("results", String::boolText(true));
bool ok = Engine::dispatch(msg) && !msg.getParam("error");
if (ok) {
if (query != m_insertDB) {
if (search)
ok = msg.getIntValue("rows") > 1;
else
ok = msg.getIntValue("affected") >= 1;
}
else {
Array* a = static_cast<Array*>(msg.userObject("Array"));
String* res = a ? YOBJECT(String,a->get(0,1)) : 0;
ok = res && (res->toInteger() != 0);
}
}
if (!ok)
error = msg.getValue("error");
return ok;
}
bool UsersModule::parseParams(const String& line, NamedList& parsed, String& error)
{
Debug(this,DebugAll,"parseParams(%s)",line.c_str());
bool ok = true;
ObjList* list = line.split(' ',false);
for (ObjList* o = list->skipNull(); o; o = o->skipNext()) {
String* s = static_cast<String*>(o->get());
int pos = s->find("=");
// Empty parameter name is not allowed
if (pos < 1) {
error << "Invalid parameter " << *s;
ok = false;
break;
}
String name = s->substr(0,pos);
String value = s->substr(pos + 1);
name.msgUnescape();
value.msgUnescape();
parsed.addParam(name,value);
DDebug(&__plugin,DebugAll,"parseParams() found '%s'='%s'",name.c_str(),value.c_str());
}
TelEngine::destruct(list);
return ok;
}
bool UsersModule::received(Message& msg, int id)
{
if (id == Halt)
unload();
else if (id == Help) {
String line = msg.getValue("line");
if (line.null()) {
msg.retValue() << " " << s_cmdsLine << "\r\n";
return false;
}
if (line != name())
return false;
msg.retValue() << "Commands used to control the Users Management module\r\n";
msg.retValue() << s_cmdsLine << "\r\n";
return true;
}
return Module::received(msg,id);
}
bool UsersModule::commandExecute(String& retVal, const String& line)
{
String tmp(line);
if (!tmp.startSkip(name(),false))
return false;
tmp.trimSpaces();
XDebug(this,DebugAll,"commandExecute(%s)",tmp.c_str());
// Retrieve the command
String cmdStr;
int cmd = 0;
if (getWord(tmp,cmdStr))
cmd = lookup(cmdStr,s_cmds);
if (!cmd) {
retVal << "Unknown command\r\n";
return true;
}
// Retrieve the user
String user;
if (!getWord(tmp,user,true)) {
retVal << "Empty username\r\n";
return true;
}
// Execute the command
bool ok = false;
String error;
if (cmd == CmdUpdate || cmd == CmdAdd || cmd == CmdDelete) {
NamedList p("");
p.addParam("user",user);
if (parseParams(tmp,p,error)) {
if (cmd == CmdUpdate)
ok = updateUser(p,&error);
else if (cmd == CmdAdd)
ok = addUser(p,&error);
else
ok = deleteUser(p,&error);
}
if (ok)
notifyUser(user,cmdStr);
}
else {
Debug(this,DebugStub,"Command '%s' not implemented",cmdStr.c_str());
error = "Unknown command";
}
retVal << name() << " " << cmdStr << (ok ? " succedded" : " failed");
if (!ok && error)
retVal << ". " << error;
retVal << "\r\n";
return true;
}
bool UsersModule::commandComplete(Message& msg, const String& partLine, const String& partWord)
{
if (partLine.null() && partWord.null())
return false;
XDebug(this,DebugAll,"commandComplete() partLine='%s' partWord=%s",
partLine.c_str(),partWord.c_str());
// No line or 'help': complete module name
if (partLine.null() || partLine == "help")
return Module::itemComplete(msg.retValue(),name(),partWord);
// Line is module name: complete module commands
if (partLine == name()) {
for (const TokenDict* list = s_cmds; list->token; list++)
Module::itemComplete(msg.retValue(),list->token,partWord);
return true;
}
return Module::commandComplete(msg,partLine,partWord);
}
}
/* vi: set ts=8 sw=4 sts=4 noet: */