911 lines
26 KiB
C++
911 lines
26 KiB
C++
/**
|
|
* presence.cpp
|
|
* This file is part of the YATE Project http://YATE.null.ro
|
|
*
|
|
* Presence 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 <yatecbase.h>
|
|
#include <yateclass.h>
|
|
|
|
|
|
using namespace TelEngine;
|
|
|
|
|
|
namespace {
|
|
|
|
#define MIN_COUNT 16 // number of lists for holding presences
|
|
#define EXPIRE_CHECK_MAX 10000 // interval in miliseconds for checking object for expiring
|
|
#define TIME_TO_KEEP 30000 // interval in miliseconds for keeping an object in memory
|
|
|
|
class PresenceList;
|
|
class Presence;
|
|
class ResNotifyHandler;
|
|
class EngineStartHandler;
|
|
class PresenceModule;
|
|
class ExpirePresence;
|
|
|
|
/*
|
|
* Class ResNotifyHandler
|
|
* Handles a resource.notify message
|
|
*/
|
|
class ResNotifyHandler : public MessageHandler
|
|
{
|
|
public:
|
|
inline ResNotifyHandler(unsigned int priority = 10)
|
|
: MessageHandler("resource.notify", priority)
|
|
{ }
|
|
virtual ~ResNotifyHandler()
|
|
{ }
|
|
virtual bool received(Message& msg);
|
|
};
|
|
|
|
/*
|
|
* Class EngineStartHandler
|
|
* Handles a engine.start message
|
|
*/
|
|
class EngineStartHandler : public MessageHandler
|
|
{
|
|
public:
|
|
inline EngineStartHandler()
|
|
: MessageHandler("engine.start",100)
|
|
{}
|
|
virtual bool received(Message& msg);
|
|
};
|
|
|
|
/*
|
|
* class Presence
|
|
* A presence object
|
|
*/
|
|
class Presence : public GenObject
|
|
{
|
|
public:
|
|
Presence(const String& id, bool online = true, const char* instance = 0,
|
|
const char* data = 0, unsigned int expiresMsecs = 0);
|
|
~Presence();
|
|
inline void update(const char* data, unsigned int expireMs) {
|
|
m_data = data;
|
|
updateExpireTime(expireMs);
|
|
}
|
|
inline const String& getInstance() const
|
|
{ return m_instance; }
|
|
inline const String& data() const
|
|
{ return m_data; }
|
|
virtual const String& toString() const
|
|
{ return m_id; }
|
|
inline bool hasExpired(u_int64_t time = Time::msecNow()) const
|
|
{ return m_expires && m_expires < time; }
|
|
inline bool isOnline() const
|
|
{ return m_online;}
|
|
inline void setOnline(bool online = true)
|
|
{ m_online = online; }
|
|
inline void updateExpireTime(unsigned int msecs)
|
|
{ m_expires = msecs ? Time::msecNow() + msecs : 0; }
|
|
inline bool isCaps(const String& capsid) const
|
|
{ return m_caps && *m_caps == capsid; }
|
|
inline void setCaps(const String& capsid, const NamedList& list) {
|
|
TelEngine::destruct(m_caps);
|
|
m_caps = new NamedList(capsid);
|
|
m_caps->copyParams(list,"caps",'.');
|
|
}
|
|
// Copy parameters to a list
|
|
void addCaps(NamedList& list, const String& prefix = String::empty());
|
|
private:
|
|
String m_id; // presence id
|
|
String m_instance; // presence instance, for jabber it represents the resource
|
|
String m_data; // presence data, format unknown
|
|
u_int64_t m_expires;// time at which this object will expire
|
|
bool m_online; // online/offline flag
|
|
NamedList* m_caps; // Capabilities
|
|
};
|
|
|
|
/*
|
|
* class PresenceList
|
|
* A list of presences
|
|
*/
|
|
class PresenceList : public ObjList, public Mutex
|
|
{
|
|
public:
|
|
// create a list
|
|
PresenceList();
|
|
~PresenceList();
|
|
// find all presences with given id, disregarding the instance
|
|
ObjList* findPresence(const String& id);
|
|
// find a presence with the given instance
|
|
inline Presence* findPresence(const String& contact, const String& instance) {
|
|
ObjList* o = find(contact,instance);
|
|
return o ? static_cast<Presence*>(o->get()) : 0;
|
|
}
|
|
// Remove an item from list. Optionally delete it
|
|
// Return the item if found and not deleted
|
|
inline Presence* removePresence(const String& contact, const String& instance,
|
|
bool delObj = true) {
|
|
ObjList* o = find(contact,instance);
|
|
return o ? static_cast<Presence*>(o->remove(delObj)) : 0;
|
|
}
|
|
// delete expired objects
|
|
void expire();
|
|
// Find an item by id and instance
|
|
ObjList* find(const String& contact, const String& instance);
|
|
};
|
|
|
|
/*
|
|
* Class PresenceModule
|
|
* Module for handling presences
|
|
*/
|
|
class PresenceModule : public Module
|
|
{
|
|
friend class ExpirePresence;
|
|
public:
|
|
PresenceModule();
|
|
virtual ~PresenceModule();
|
|
//inherited methods
|
|
virtual void initialize();
|
|
virtual bool received(Message& msg, int id);
|
|
// Get the list containing a given user
|
|
inline PresenceList* getList(const String& contact)
|
|
{ return m_list + (contact.hash() % m_listCount); }
|
|
// Update capabilities for all instances with the given caps id
|
|
void updateCaps(const String& capsid, Message& msg);
|
|
// append presence
|
|
void addPresence(Presence* pres, bool onlyLocal = false);
|
|
// remove presence
|
|
void removePresence(Presence* pres);
|
|
// remove presence by id
|
|
void removePresenceById(const String& id);
|
|
// find presence by id
|
|
ObjList* findPresenceById(const String& id);
|
|
// find presence with an instance
|
|
Presence* findPresence(const String& contact, const String& instance);
|
|
// update a presence
|
|
void updatePresence(Presence* pres, const char* data);
|
|
// uninstall relays and message handlers
|
|
bool unload();
|
|
|
|
// Build a 'database' message used to update presence
|
|
Message* buildUpdateDb(const Presence& pres, bool newPres);
|
|
// Build a 'database' message used to delete presence
|
|
Message* buildDeleteDb(const Presence& pres);
|
|
|
|
// database comunnication functions
|
|
bool insertDB(Presence* pres);
|
|
bool updateDB(Presence* pres);
|
|
bool removeDB(Presence* pres, bool allInstances = false, bool allPresences = false, String machine = "");
|
|
bool queryDB(String id, String instance);
|
|
bool getInfoDB(String id, String instance, NamedList* result);
|
|
|
|
// Build and dispatch a 'database' message. Replace query params
|
|
// Return a valid Message on success
|
|
Message* queryDb(const String& account, const String& query,
|
|
const NamedList& params);
|
|
// Dispatch a 'database' message. Return a valid Message pointer on success
|
|
// Consume the given pointer
|
|
Message* queryDb(Message* msg);
|
|
|
|
private:
|
|
// array of lists that hold presences
|
|
PresenceList* m_list;
|
|
// resource.notify handler
|
|
ResNotifyHandler* m_notifyHandler;
|
|
// engine.start handler
|
|
EngineStartHandler* m_engineStartHandler;
|
|
// number of lists in which presences will be held
|
|
unsigned int m_listCount;
|
|
// thread for removing expired objects
|
|
ExpirePresence* m_expireThread;
|
|
// Query strings
|
|
// SQL statement for inserting to database
|
|
String m_insertDB;
|
|
// SQL statement for updating information in the database
|
|
String m_updateDB;
|
|
// SQL statement for remove a presence with a resource
|
|
String m_removeResDB;
|
|
// SQL statement for removing all instances of a contact
|
|
String m_removePresDB;
|
|
// SQL statement for removing all presences of this node
|
|
String m_removeAllDB;
|
|
// SQL statement for interrogating the database about a contact with a resource
|
|
String m_selectResDB;
|
|
// SQL statement for interrogating the database about a contact without a resource
|
|
String m_selectPresDB;
|
|
// database connection
|
|
String m_accountDB;
|
|
};
|
|
|
|
/*
|
|
* class ExpirePresence
|
|
* A thread that deletes old presences
|
|
*/
|
|
class ExpirePresence : public Thread
|
|
{
|
|
public:
|
|
// create a thread whick will check for expired objects after the given "checkAfter" interval
|
|
ExpirePresence(unsigned int checkAfter = 5000);
|
|
~ExpirePresence();
|
|
void run();
|
|
static unsigned int s_expireTime;
|
|
private:
|
|
unsigned int m_checkMs;
|
|
};
|
|
|
|
static String s_msgPrefix = "presence";
|
|
static unsigned int s_presExpire; // Presence expire interval (relese memory only)
|
|
static unsigned int s_presExpireCheck; // Presence expire check interval
|
|
unsigned int ExpirePresence::s_expireTime = 0;
|
|
INIT_PLUGIN(PresenceModule);
|
|
|
|
|
|
UNLOAD_PLUGIN(unloadNow)
|
|
{
|
|
if (unloadNow && !__plugin.unload())
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* ResNotifyHandler
|
|
*/
|
|
bool ResNotifyHandler::received(Message& msg)
|
|
{
|
|
// TODO
|
|
// see message parameters and what to do with them
|
|
String* node = msg.getParam("nodename");
|
|
if (!TelEngine::null(node) && *node != Engine::nodeName())
|
|
__plugin.removeDB(0, true, true, *node);
|
|
|
|
String* operation = msg.getParam("operation");
|
|
if (TelEngine::null(operation))
|
|
return false;
|
|
|
|
if (*operation == "updatecaps") {
|
|
String* capsid = msg.getParam("caps.id");
|
|
if (TelEngine::null(capsid))
|
|
return false;
|
|
DDebug(&__plugin,DebugAll,"Processing %s oper=%s capsid=%s",
|
|
msg.c_str(),operation->c_str(),capsid->c_str());
|
|
__plugin.updateCaps(*capsid,msg);
|
|
return false;
|
|
}
|
|
|
|
String* contact = msg.getParam("contact");
|
|
if (TelEngine::null(contact))
|
|
return false;
|
|
DDebug(&__plugin,DebugAll,"Processing %s contact=%s oper=%s",
|
|
msg.c_str(),contact->c_str(),operation->c_str());
|
|
String* instance = msg.getParam("instance");
|
|
PresenceList* list = __plugin.getList(*contact);
|
|
if (*operation == "online" || *operation == "update") {
|
|
if (TelEngine::null(instance))
|
|
return false;
|
|
Lock lock(list);
|
|
Presence* pres = list->findPresence(*contact,*instance);
|
|
bool newPres = (pres == 0);
|
|
if (newPres) {
|
|
pres = new Presence(*contact,true,*instance);
|
|
list->append(pres);
|
|
}
|
|
pres->update(msg.getValue("data"),s_presExpire);
|
|
String* capsid = msg.getParam("caps.id");
|
|
if (!TelEngine::null(capsid))
|
|
pres->setCaps(*capsid,msg);
|
|
// Update database only if we expire the data from memory
|
|
if (s_presExpire) {
|
|
Message* m = __plugin.buildUpdateDb(*pres,newPres);
|
|
lock.drop();
|
|
TelEngine::destruct(__plugin.queryDb(m));
|
|
}
|
|
}
|
|
else if (*operation == "remove" || *operation == "offline") {
|
|
if (TelEngine::null(instance)) {
|
|
// TODO: all contact's instances are offline
|
|
return false;
|
|
}
|
|
list->lock();
|
|
Presence* pres = list->removePresence(*contact,*instance,false);
|
|
list->unlock();
|
|
// Remove from database only if we expire the data from memory
|
|
if (pres && s_presExpire)
|
|
TelEngine::destruct(__plugin.queryDb(__plugin.buildDeleteDb(*pres)));
|
|
TelEngine::destruct(pres);
|
|
}
|
|
else if (*operation == "query") {
|
|
Lock lock(list);
|
|
if (!TelEngine::null(instance)) {
|
|
Presence* pres = list->findPresence(*contact,*instance);
|
|
if (pres) {
|
|
msg.addParam("data",pres->data());
|
|
msg.addParam("nodename",Engine::nodeName());
|
|
pres->addCaps(msg);
|
|
return true;
|
|
}
|
|
}
|
|
else {
|
|
ObjList* l = list->findPresence(*contact);
|
|
if (!l)
|
|
return false;
|
|
msg.addParam("message-prefix",s_msgPrefix);
|
|
unsigned int n = 0;
|
|
String prefix = s_msgPrefix + ".";
|
|
for (ObjList* o = l->skipNull(); o; o = o->skipNext()) {
|
|
Presence* pres = static_cast<Presence*>(o->get());
|
|
String param;
|
|
param << prefix << ++n << ".";
|
|
msg.addParam(param + "instance",pres->getInstance());
|
|
msg.addParam(param + "data",pres->data());
|
|
msg.addParam(param + "nodename",Engine::nodeName());
|
|
pres->addCaps(msg,param);
|
|
}
|
|
msg.addParam(prefix + "count",String(n));
|
|
TelEngine::destruct(l);
|
|
return n != 0;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
* EngineStartHandler
|
|
*/
|
|
bool EngineStartHandler::received(Message& msg)
|
|
{
|
|
__plugin.removeDB(0,true,true);
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Presence
|
|
*/
|
|
Presence::Presence(const String& id, bool online, const char* instance,
|
|
const char* data, unsigned int expireMs)
|
|
: m_id(id), m_instance(instance), m_data(data), m_online(online),
|
|
m_caps(0)
|
|
{
|
|
updateExpireTime(expireMs);
|
|
DDebug(&__plugin,DebugAll,"Presence contact='%s' instance='%s' online=%s [%p]",
|
|
id.c_str(),instance,String::boolText(m_online),this);
|
|
}
|
|
|
|
Presence::~Presence()
|
|
{
|
|
TelEngine::destruct(m_caps);
|
|
DDebug(&__plugin,DebugAll,"Presence contact='%s' instance='%s' destroyed [%p]",
|
|
m_id.c_str(),m_instance.c_str(),this);
|
|
}
|
|
|
|
// Copy parameters to a list
|
|
void Presence::addCaps(NamedList& list, const String& prefix)
|
|
{
|
|
if (!m_caps)
|
|
return;
|
|
if (!prefix) {
|
|
list.copyParams(*m_caps);
|
|
return;
|
|
}
|
|
unsigned int n = m_caps->count();
|
|
for (unsigned int i = 0; i < n; i++) {
|
|
NamedString* ns = m_caps->getParam(i);
|
|
if (ns)
|
|
list.addParam(prefix + ns->name(),*ns);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* PresenceList
|
|
*/
|
|
PresenceList::PresenceList()
|
|
: Mutex("PresenceList")
|
|
{
|
|
XDebug(&__plugin,DebugAll,"PresenceList() [%p]",this);
|
|
}
|
|
|
|
PresenceList::~PresenceList()
|
|
{
|
|
XDebug(&__plugin,DebugAll, "PresenceList destroyed [%p]",this);
|
|
}
|
|
|
|
ObjList* PresenceList::findPresence(const String& id)
|
|
{
|
|
if (TelEngine::null(id))
|
|
return 0;
|
|
DDebug(&__plugin,DebugAll,"PresenceList::findPresence('%s') [%p]",id.c_str(),this);
|
|
ObjList* res = 0;
|
|
for (ObjList* o = skipNull(); o; o = o->skipNext()) {
|
|
if (id == o->get()->toString()) {
|
|
if (!res)
|
|
res = new ObjList();
|
|
res->append(o->get())->setDelete(false);
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
void PresenceList::expire()
|
|
{
|
|
u_int64_t time = Time::msecNow();
|
|
Lock lock(this);
|
|
for (ObjList* o = skipNull(); o; o = o->skipNext()) {
|
|
Presence* pres = static_cast<Presence*>(o->get());
|
|
if (pres->hasExpired(time)) {
|
|
Debug(&__plugin,DebugAll,"Presence (%p) contact=%s instance=%s expired",
|
|
pres,pres->toString().c_str(),pres->getInstance().c_str());
|
|
__plugin.removePresence(pres);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Find an item by id and instance
|
|
ObjList* PresenceList::find(const String& contact, const String& instance)
|
|
{
|
|
if (contact.null() || instance.null())
|
|
return 0;
|
|
for (ObjList* o = skipNull(); o; o = o->skipNext()) {
|
|
Presence* pres = static_cast<Presence*>(o->get());
|
|
if (contact == pres->toString() && instance == pres->getInstance())
|
|
return o;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* ExpirePresence
|
|
*/
|
|
ExpirePresence::ExpirePresence(unsigned int checkAfter)
|
|
: Thread("ExpirePresence"), m_checkMs(checkAfter)
|
|
{
|
|
__plugin.lock();
|
|
__plugin.m_expireThread = this;
|
|
__plugin.unlock();
|
|
}
|
|
|
|
ExpirePresence::~ExpirePresence()
|
|
{
|
|
Debug(&__plugin,DebugAll,"ExpirePresence thread terminated [%p]",this);
|
|
__plugin.lock();
|
|
__plugin.m_expireThread = 0;
|
|
__plugin.unlock();
|
|
}
|
|
|
|
void ExpirePresence::run()
|
|
{
|
|
Debug(&__plugin,DebugAll,"%s started [%p]",currentName(),this);
|
|
while (true) {
|
|
if (Thread::check(false) || Engine::exiting())
|
|
break;
|
|
if (s_expireTime < m_checkMs)
|
|
Thread::idle();
|
|
else {
|
|
s_expireTime = 0;
|
|
for (unsigned int i = 0; i < __plugin.m_listCount; i++)
|
|
__plugin.m_list[i].expire();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* PresenceModule
|
|
*/
|
|
PresenceModule::PresenceModule()
|
|
: Module("presence", "misc"),
|
|
m_list(0), m_notifyHandler(0), m_engineStartHandler(0),
|
|
m_listCount(0), m_expireThread(0)
|
|
{
|
|
Output("Loaded module Presence");
|
|
}
|
|
|
|
PresenceModule::~PresenceModule()
|
|
{
|
|
Output("Unloaded module Presence");
|
|
TelEngine::destruct(m_notifyHandler);
|
|
TelEngine::destruct(m_engineStartHandler);
|
|
if (m_list)
|
|
delete[] m_list;
|
|
}
|
|
|
|
void PresenceModule::initialize()
|
|
{
|
|
Output("Initializing module Presence");
|
|
|
|
Configuration cfg(Engine::configFile("presence"));
|
|
cfg.load();
|
|
|
|
if (!m_list) {
|
|
setup();
|
|
installRelay(Halt);
|
|
|
|
m_notifyHandler = new ResNotifyHandler();
|
|
Engine::install(m_notifyHandler);
|
|
m_engineStartHandler = new EngineStartHandler();
|
|
Engine::install(m_engineStartHandler);
|
|
|
|
m_listCount = cfg.getIntValue("general", "listcount", MIN_COUNT);
|
|
if (m_listCount < MIN_COUNT)
|
|
m_listCount = MIN_COUNT;
|
|
m_list = new PresenceList[m_listCount];
|
|
|
|
// expire thread?
|
|
// TODO: Make sure these values are correct
|
|
s_presExpireCheck = cfg.getIntValue("general", "expirecheck");
|
|
if (s_presExpireCheck < 0)
|
|
s_presExpireCheck = 0;
|
|
if (s_presExpireCheck) {
|
|
if (s_presExpireCheck > EXPIRE_CHECK_MAX)
|
|
s_presExpireCheck = EXPIRE_CHECK_MAX;
|
|
s_presExpire = cfg.getIntValue("general", "expiretime", TIME_TO_KEEP);
|
|
(new ExpirePresence(s_presExpireCheck))->startup();
|
|
}
|
|
|
|
// queries init
|
|
m_insertDB = cfg.getValue("database", "insert_presence", "");
|
|
m_updateDB = cfg.getValue("database", "update_presence", "");
|
|
m_removeResDB = cfg.getValue("database", "remove_instance", "");
|
|
m_removePresDB = cfg.getValue("database", "remove_presence", "");
|
|
m_removeAllDB = cfg.getValue("database", "remove_all", "");
|
|
m_selectResDB = cfg.getValue("database", "select_instance", "");
|
|
m_selectPresDB = cfg.getValue("database", "select_presence", "");
|
|
|
|
// database connection init
|
|
m_accountDB = cfg.getValue("database", "account");
|
|
}
|
|
}
|
|
|
|
bool PresenceModule::unload()
|
|
{
|
|
DDebug(this,DebugAll,"unload()");
|
|
if (!lock(500000))
|
|
return false;
|
|
uninstallRelays();
|
|
Engine::uninstall(m_notifyHandler);
|
|
Engine::uninstall(m_engineStartHandler);
|
|
if (m_expireThread)
|
|
m_expireThread->cancel();
|
|
unlock();
|
|
// Wait for expire thread termination
|
|
while (m_expireThread)
|
|
Thread::yield();
|
|
return true;
|
|
}
|
|
|
|
bool PresenceModule::received(Message& msg, int id)
|
|
{
|
|
if (id == Timer)
|
|
ExpirePresence::s_expireTime += 1000;
|
|
else if (id == Halt) {
|
|
unload();
|
|
DDebug(this,DebugAll,"Halted");
|
|
}
|
|
return Module::received(msg,id);
|
|
}
|
|
|
|
// Update capabilities for all instances with the given caps id
|
|
void PresenceModule::updateCaps(const String& capsid, Message& msg)
|
|
{
|
|
for (unsigned int i = 0; i < m_listCount; i++) {
|
|
Lock lock(m_list[i]);
|
|
for (ObjList* o = m_list[i].skipNull(); o; o = o->skipNext()) {
|
|
Presence* p = static_cast<Presence*>(o->get());
|
|
if (p->isCaps(capsid))
|
|
p->setCaps(capsid,msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
void PresenceModule::addPresence(Presence* pres, bool onlyLocal)
|
|
{
|
|
if (!pres)
|
|
return;
|
|
unsigned int index = pres->toString().hash() % m_listCount;
|
|
DDebug(this,DebugAll,"Adding presence (%p) contact='%s' instance='%s'",
|
|
pres,pres->toString().c_str(),pres->getInstance().c_str());
|
|
Lock lock(m_list[index]);
|
|
m_list[index].append(pres);
|
|
pres->updateExpireTime(s_presExpire);
|
|
if (!onlyLocal)
|
|
insertDB(pres);
|
|
}
|
|
|
|
void PresenceModule::removePresence(Presence* pres)
|
|
{
|
|
if (!pres)
|
|
return;
|
|
unsigned int index = pres->toString().hash() % m_listCount;
|
|
DDebug(this,DebugAll,"Removing presence (%p) contact=%s instance=%s",
|
|
pres,pres->toString().c_str(),pres->getInstance().c_str());
|
|
Lock lock(m_list[index]);
|
|
if (!pres->isOnline())
|
|
removeDB(pres);
|
|
m_list[index].remove(pres);
|
|
}
|
|
|
|
void PresenceModule::removePresenceById(const String& id)
|
|
{
|
|
if (TelEngine::null(id))
|
|
return;
|
|
unsigned int index = id.hash() % m_listCount;
|
|
Lock lock(m_list[index]);
|
|
for (ObjList* o = m_list[index].skipNull(); o; o = o->skipNext()) {
|
|
Presence* pres = static_cast<Presence*>(o->get());
|
|
if (pres->toString() == id) {
|
|
m_list[index].remove(pres);
|
|
removeDB(pres, true);
|
|
}
|
|
}
|
|
// list->remove(id);
|
|
}
|
|
|
|
void PresenceModule::updatePresence(Presence* pres, const char* data)
|
|
{
|
|
if (!pres)
|
|
return;
|
|
DDebug(this,DebugAll,"updatePresence() contact='%s' instance='%s' data='%s'",
|
|
pres->toString().c_str(),pres->getInstance().c_str(),data);
|
|
pres->update(data,s_presExpire);
|
|
updateDB(pres);
|
|
}
|
|
|
|
ObjList* PresenceModule::findPresenceById(const String& id)
|
|
{
|
|
if (TelEngine::null(id))
|
|
return 0;
|
|
unsigned int index = id.hash() % m_listCount;
|
|
Lock lock(m_list[index]);
|
|
ObjList* ol = m_list[index].findPresence(id);
|
|
NamedList info("");
|
|
if (!getInfoDB(id, "", &info))
|
|
return ol;
|
|
int count = info.getIntValue("count");
|
|
for (int i = 1; i <= count; i++) {
|
|
String prefix = String(i) + ".";
|
|
String* instance = info.getParam(prefix + "instance");
|
|
const char* data = info.getValue(prefix + "data");
|
|
if (instance && !findPresence(id, *instance)) {
|
|
Presence* pres = new Presence(id, true, *instance, data);
|
|
ol->append(pres)->setDelete(false);
|
|
}
|
|
}
|
|
return ol;
|
|
}
|
|
|
|
Presence* PresenceModule::findPresence(const String& contact, const String& instance)
|
|
{
|
|
if (TelEngine::null(contact))
|
|
return 0;
|
|
DDebug(this,DebugAll,"findPresence('%s','%s')",contact.c_str(),instance.c_str());
|
|
unsigned int index = contact.hash() % m_listCount;
|
|
Presence* pres = m_list[index].findPresence(contact, instance);
|
|
if (pres)
|
|
return pres;
|
|
NamedList info("");
|
|
if (!getInfoDB(contact, instance, &info))
|
|
return 0;
|
|
int count = info.getIntValue("count");
|
|
for (int i = 1; i <= count; i++) {
|
|
String data = info.getValue("data");
|
|
Presence* pres = new Presence(contact, true, instance, data);
|
|
addPresence(pres, true);
|
|
return pres;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Build a 'database' message used to update presence
|
|
Message* PresenceModule::buildUpdateDb(const Presence& pres, bool newPres)
|
|
{
|
|
String& query = newPres ? m_insertDB : m_updateDB;
|
|
if (!(m_accountDB && query))
|
|
return 0;
|
|
Message* msg = new Message("database");
|
|
msg->addParam("account",m_accountDB);
|
|
NamedList p("");
|
|
p.addParam("contact",pres.toString());
|
|
p.addParam("instance",pres.getInstance());
|
|
p.addParam("nodename",Engine::nodeName());
|
|
p.addParam("data",pres.data());
|
|
String tmp = query;
|
|
p.replaceParams(tmp,true);
|
|
msg->addParam("query",tmp);
|
|
return msg;
|
|
}
|
|
|
|
// Build a 'database' message used to delete presence
|
|
Message* PresenceModule::buildDeleteDb(const Presence& pres)
|
|
{
|
|
if (!(m_accountDB && m_removeResDB))
|
|
return 0;
|
|
Message* msg = new Message("database");
|
|
msg->addParam("account",m_accountDB);
|
|
NamedList p("");
|
|
p.addParam("contact",pres.toString());
|
|
p.addParam("instance",pres.getInstance());
|
|
String tmp = m_removeResDB;
|
|
p.replaceParams(tmp,true);
|
|
msg->addParam("query",tmp);
|
|
return msg;
|
|
}
|
|
|
|
bool PresenceModule::insertDB(Presence* pres)
|
|
{
|
|
if (!pres || TelEngine::null(m_insertDB))
|
|
return false;
|
|
NamedList p("");
|
|
p.addParam("contact", pres->toString());
|
|
p.addParam("instance", pres->getInstance());
|
|
p.addParam("nodename", Engine::nodeName());
|
|
p.addParam("data", pres->data());
|
|
Message* msg = queryDb(m_accountDB, m_insertDB, p);
|
|
if (msg) {
|
|
TelEngine::destruct(msg);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool PresenceModule::updateDB(Presence* pres)
|
|
{
|
|
if (!pres || TelEngine::null(m_updateDB))
|
|
return false;
|
|
NamedList p("");
|
|
p.addParam("contact", pres->toString());
|
|
p.addParam("instance", pres->getInstance());
|
|
p.addParam("nodename", Engine::nodeName());
|
|
p.addParam("data", pres->data());
|
|
Message* msg = queryDb(m_accountDB, m_updateDB, p);
|
|
if (msg) {
|
|
TelEngine::destruct(msg);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool PresenceModule::removeDB(Presence* pres, bool allInstances, bool allPresences, String machine)
|
|
{
|
|
NamedList queryList("");
|
|
String query;
|
|
if (TelEngine::null(machine))
|
|
queryList.addParam("nodename", Engine::nodeName());
|
|
else
|
|
queryList.addParam("nodename", machine);
|
|
if (allPresences) {
|
|
query = m_removeAllDB;
|
|
}
|
|
else {
|
|
if (!pres)
|
|
return false;
|
|
if (!allInstances) {
|
|
query = m_removeResDB;
|
|
queryList.addParam("instance", pres->getInstance());
|
|
}
|
|
else
|
|
query = m_removePresDB;
|
|
queryList.addParam("contact", pres->toString());
|
|
}
|
|
Message* msg = queryDb(m_accountDB, query, queryList);
|
|
if (msg) {
|
|
int n = msg->getIntValue("affected");
|
|
if (n > 0)
|
|
Debug(this, DebugInfo, "Removed %d items from database", n);
|
|
TelEngine::destruct(msg);
|
|
return true;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// check only if a contact with/without instance is present, return true or false
|
|
bool PresenceModule::queryDB(String id, String instance)
|
|
{
|
|
if (TelEngine::null(id))
|
|
return false;
|
|
NamedList queryList("");
|
|
String query;
|
|
queryList.addParam("contact", id);
|
|
if (TelEngine::null(instance))
|
|
query = m_selectPresDB;
|
|
else {
|
|
queryList.addParam("instance", instance);
|
|
query = m_selectResDB;
|
|
}
|
|
Message* msg = queryDb(m_accountDB, query, queryList);
|
|
if (msg) {
|
|
bool ok = msg->getIntValue("rows") > 0;
|
|
TelEngine::destruct(msg);
|
|
return ok;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Interrogate database about a contact and retrieve data about it
|
|
bool PresenceModule::getInfoDB(String id, String instance, NamedList* result)
|
|
{
|
|
if (TelEngine::null(id))
|
|
return false;
|
|
NamedList queryList("");
|
|
String query;
|
|
queryList.addParam("contact", id);
|
|
if (TelEngine::null(instance))
|
|
query = m_selectPresDB;
|
|
else {
|
|
queryList.addParam("instance", instance);
|
|
query = m_selectResDB;
|
|
}
|
|
Message* msg = queryDb(m_accountDB, query, queryList);
|
|
Array* res = msg ? static_cast<Array*>(msg->userObject("Array")) : 0;
|
|
int n = res ? msg->getIntValue("rows") : 0;
|
|
if (!msg || n < 1) {
|
|
TelEngine::destruct(msg);
|
|
return false;
|
|
}
|
|
result->setParam("count", String(res->getRows() - 1));
|
|
for (int i = 0; i < res->getColumns(); i++) {
|
|
String* colName = YOBJECT(String, res->get(i, 0));
|
|
if (!(colName && *colName))
|
|
continue;
|
|
for (int j = 1; j < res->getRows(); j++) {
|
|
String* val = YOBJECT(String, res->get(i, j));
|
|
String paramName = String(j);
|
|
paramName << "." << *colName;
|
|
if (!val)
|
|
continue;
|
|
result->setParam(paramName, *val);
|
|
}
|
|
}
|
|
TelEngine::destruct(msg);
|
|
return true;
|
|
}
|
|
|
|
// Build and dispatch a 'database' message. Replace query params
|
|
Message* PresenceModule::queryDb(const String& account, const String& query,
|
|
const NamedList& params)
|
|
{
|
|
Message* msg = new Message("database");
|
|
msg->addParam("account", account);
|
|
String tmp = query;
|
|
params.replaceParams(tmp,true);
|
|
msg->addParam("query", tmp);
|
|
msg->addParam("results", String::boolText(true));
|
|
if (!Engine::dispatch(msg) || msg->getParam("error")) {
|
|
DDebug(this,DebugNote,"Database query '%s' failed error='%s'",
|
|
tmp.c_str(),msg->getValue("error"));
|
|
TelEngine::destruct(msg);
|
|
}
|
|
return msg;
|
|
}
|
|
|
|
// Dispatch a 'database' message. Return a valid Message pointer on success
|
|
// Consume the given pointer
|
|
Message* PresenceModule::queryDb(Message* msg)
|
|
{
|
|
if (!msg)
|
|
return 0;
|
|
if (!Engine::dispatch(msg) || msg->getParam("error")) {
|
|
DDebug(this,DebugNote,"Database query '%s' failed error='%s'",
|
|
msg->getValue("query"),msg->getValue("error"));
|
|
TelEngine::destruct(msg);
|
|
}
|
|
return msg;
|
|
}
|
|
|
|
}
|
|
|
|
/* vi: set ts=8 sw=4 sts=4 noet: */
|