yate/yatengine.h

1905 lines
58 KiB
C
Raw Normal View History

/**
* yatengine.h
* This file is part of the YATE Project http://YATE.null.ro
*
* Engine, plugins and messages related classes
*
* Yet Another Telephony Engine - a fully featured software PBX and IVR
2023-05-23 14:01:06 +00:00
* Copyright (C) 2004-2023 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.
*/
#ifndef __YATENGINE_H
#define __YATENGINE_H
#ifndef __cplusplus
#error C++ is required
#endif
#include <yateclass.h>
/**
* Holds all Telephony Engine related classes.
*/
namespace TelEngine {
/**
* A class for parsing and quickly accessing INI style configuration files
* @short Configuration file handling
*/
class YATE_API Configuration : public String
{
friend class Engine;
YNOCOPY(Configuration); // no automatic copies please
public:
/**
* Create an empty configuration
*/
Configuration();
/**
* Create a configuration from a file
* @param filename Name of file to initialize from
* @param warn True to warn if the configuration could not be loaded
*/
explicit Configuration(const char* filename, bool warn = true);
/**
* Assignment from string operator
*/
inline Configuration& operator=(const String& value)
{ String::operator=(value); return *this; }
/**
* Get the number of sections
* @return Count of sections
*/
inline unsigned int sections() const
{ return m_sections.length(); }
/**
* Get the number of non null sections
* @return Count of sections
*/
inline unsigned int count() const
{ return m_sections.count(); }
/**
* Retrieve an entire section
* @param index Index of the section
* @return The section's content or NULL if no such section
*/
NamedList* getSection(unsigned int index) const;
/**
* Retrieve an entire section
* @param sect Name of the section
* @return The section's content or NULL if no such section
*/
NamedList* getSection(const String& sect) const;
/**
* Locate a key/value pair in the section.
* @param sect Name of the section
* @param key Name of the key in section
* @return A pointer to the key/value pair or NULL.
*/
NamedString* getKey(const String& sect, const String& key) const;
/**
* Retrieve the value of a key in a section.
* @param sect Name of the section
* @param key Name of the key in section
* @param defvalue Default value to return if not found
* @return The string contained in the key or the default
*/
const char* getValue(const String& sect, const String& key, const char* defvalue = 0) const;
/**
* Retrieve the numeric value of a key in a section.
* @param sect Name of the section
* @param key Name of the key in section
* @param defvalue Default value to return if not found
* @param minvalue Minimum value allowed for the parameter
* @param maxvalue Maximum value allowed for the parameter
* @param clamp Control the out of bound values: true to adjust to the nearest
* bound, false to return the default value
* @return The number contained in the key or the default
*/
int getIntValue(const String& sect, const String& key, int defvalue = 0,
int minvalue = INT_MIN, int maxvalue = INT_MAX, bool clamp = true) const;
/**
* Retrieve the numeric value of a key in a section trying first a table lookup.
* @param sect Name of the section
* @param key Name of the key in section
* @param tokens A pointer to an array of tokens to try to lookup
* @param defvalue Default value to return if not found
* @return The number contained in the key or the default
*/
int getIntValue(const String& sect, const String& key, const TokenDict* tokens, int defvalue = 0) const;
/**
* Retrieve the 64-bit numeric value of a key in a section.
* @param sect Name of the section
* @param key Name of the key in section
* @param defvalue Default value to return if not found
* @param minvalue Minimum value allowed for the parameter
* @param maxvalue Maximum value allowed for the parameter
* @param clamp Control the out of bound values: true to adjust to the nearest
* bound, false to return the default value
* @return The number contained in the key or the default
*/
int64_t getInt64Value(const String& sect, const String& key, int64_t defvalue = 0,
int64_t minvalue = LLONG_MIN, int64_t maxvalue = LLONG_MAX, bool clamp = true) const;
/**
* Retrieve the floating point value of a key in a section.
* @param sect Name of the section
* @param key Name of the key in section
* @param defvalue Default value to return if not found
* @return The numeric value contained in the key or the default
*/
double getDoubleValue(const String& sect, const String& key, double defvalue = 0.0) const;
/**
* Retrieve the boolean value of a key in a section.
* @param sect Name of the section
* @param key Name of the key in section
* @param defvalue Default value to return if not found
* @return The boolean value contained in the key or the default
*/
bool getBoolValue(const String& sect, const String& key, bool defvalue = false) const;
/**
* Deletes an entire section
* @param sect Name of section to delete, NULL to delete all
*/
void clearSection(const char* sect = 0);
/**
* Makes sure a section with a given name exists, creates if required
* @param sect Name of section to check or create
* @return The section's content or NULL if no such section
*/
NamedList* createSection(const String& sect);
/**
* Deletes a key/value pair
* @param sect Name of section
* @param key Name of the key to delete
*/
void clearKey(const String& sect, const String& key);
/**
* Add the value of a key in a section.
* @param sect Name of the section, will be created if missing
* @param key Name of the key to add in the section
* @param value Value to set in the key
*/
void addValue(const String& sect, const char* key, const char* value = 0);
/**
* Set the value of a key in a section.
* @param sect Name of the section, will be created if missing
* @param key Name of the key in section, will be created if missing
* @param value Value to set in the key
*/
void setValue(const String& sect, const char* key, const char* value = 0);
/**
* Set the numeric value of a key in a section.
* @param sect Name of the section, will be created if missing
* @param key Name of the key in section, will be created if missing
* @param value Value to set in the key
*/
void setValue(const String& sect, const char* key, int value);
/**
* Set the boolean value of a key in a section.
* @param sect Name of the section, will be created if missing
* @param key Name of the key in section, will be created if missing
* @param value Value to set in the key
*/
void setValue(const String& sect, const char* key, bool value);
/**
* Load the configuration from file
* @param warn True to also warn if the configuration could not be loaded
* @return True if successfull, false for failure
*/
bool load(bool warn = true);
/**
* Save the configuration to file
* @return True if successfull, false for failure
*/
bool save() const;
private:
/**
* Load the main configuration file
* @param warn True to also warn if the configuration could not be loaded
* @return True if successfull, false for failure
*/
inline bool loadMain(bool warn = true) {
m_main = true;
return load(warn);
}
ObjList *getSectHolder(const String& sect) const;
ObjList *makeSectHolder(const String& sect);
bool loadFile(const char* file, String sect, unsigned int depth, bool warn, void* priv);
ObjList m_sections;
bool m_main;
};
/**
* Class that implements atomic / locked access and operations to its shared variables
* @short Atomic access and operations to shared variables
*/
class YATE_API SharedVars : public Mutex, public RefObject
{
public:
/**
* Constructor
* @param name Optional name
*/
inline SharedVars(const char* name = 0)
: Mutex(false,"SharedVars"), m_vars(name)
{ }
/**
* Get the string value of a variable
* @param name Name of the variable
* @param rval String to return the value into
*/
void get(const String& name, String& rval);
/**
* Set the string value of a variable
* @param name Name of the variable to set
* @param val New value to assign to a variable
*/
void set(const String& name, const char* val);
/**
* Create and set a variable only if the variable is not already set
* @param name Name of the variable to set
* @param val New value to assign to a variable
* @return True if a new variable was created
*/
bool create(const String& name, const char* val = 0);
/**
* Clear a variable
* @param name Name of the variable to clear
*/
void clear(const String& name);
/**
* Clear all variables. Does nothing for Engine (global shared list)
*/
void clearAll();
/**
* Check if a variable exists
* @param name Name of the variable
* @return True if the variable exists
*/
bool exists(const String& name);
/**
* Atomically increment a variable as unsigned integer
* @param name Name of the variable
* @param wrap Value to wrap around at, zero disables
* @return Value of the variable before increment, zero if it was not defined or not numeric
*/
uint64_t inc(const String& name, uint64_t wrap = 0);
/**
* Atomically decrement a variable as unsigned integer
* @param name Name of the variable
* @param wrap Value to wrap around at, zero disables (stucks at zero)
* @return Value of the variable after decrement, zero if it was not defined or not numeric
*/
uint64_t dec(const String& name, uint64_t wrap = 0);
/**
* Atomically add a value to a variable as unsigned integer
* @param name Name of the variable
* @param val Value to add
* @param wrap Value to wrap around at, zero disables
* @return Value of the variable before addition, zero if it was not defined or not numeric
*/
uint64_t add(const String& name, uint64_t val, uint64_t wrap = 0);
/**
* Atomically substract a value from a variable as unsigned integer
* @param name Name of the variable
* @param val Value to substract
* @param wrap Value to wrap around at, zero disables
* @return Value of the variable after substraction, zero if it was not defined or not numeric
*/
uint64_t sub(const String& name, uint64_t val, uint64_t wrap = 0);
/**
* Atomically copy parameters to destination
* @param dest Destination list
* @param prefix Optional prefix to match in parameter names
* @param skipPrefix Skip over the prefix when building new parameter name
* @param replace Set to true to replace list parameter instead of adding a new one
*/
inline void copy(NamedList& dest, const String& prefix = String::empty(),
bool skipPrefix = true, bool replace = false) {
Lock lck(this);
if (prefix)
dest.copySubParams(m_vars,prefix,skipPrefix,replace);
else
dest.copyParams(m_vars);
}
/**
* Retrieve list name
* @return List name
*/
virtual const String& toString() const
{ return m_vars; }
/**
* Retrieve a named list of SharedVars. Create it if not found
* @param dest Destination to be filled with requested list
* @param name Name of the list
* @return True id destination is iset. The function will fail if name is empty
*/
static bool getList(RefPointer<SharedVars>& dest, const String& name);
private:
NamedList m_vars;
};
class MessageDispatcher;
class MessageRelay;
class Engine;
/**
* This class holds the messages that are moved around in the engine.
* @short A message container class
*/
class YATE_API Message : public NamedList
{
friend class MessageDispatcher;
public:
/**
* Creates a new message.
*
* @param name Name of the message - must not be NULL or empty
* @param retval Default return value
* @param broadcast Broadcast flag, true if handling the mesage must not stop it
*/
explicit Message(const char* name, const char* retval = 0, bool broadcast = false);
/**
* Copy constructor.
* Note that user data and notification are not copied
* @param original Message we are copying from
*/
Message(const Message& original);
/**
* Copy constructor that can alter the broadcast flag.
* Note that user data and notification are not copied
* @param original Message we are copying from
* @param broadcast Broadcast flag, true if handling the mesage must not stop it
*/
Message(const Message& original, bool broadcast);
/**
* Destruct the message and dereferences any user data
*/
~Message();
/**
* Get a pointer to a derived class given that class name
* @param name Name of the class we are asking for
* @return Pointer to the requested class or NULL if this object doesn't implement it
*/
virtual void* getObject(const String& name) const;
/**
* Retrieve a reference to the value returned by the message.
* @return A reference to the value the message will return
*/
inline String& retValue()
{ return m_return; }
/**
* Retrieve a const reference to the value returned by the message.
* @return A reference to the value the message will return
*/
inline const String& retValue() const
{ return m_return; }
/**
* Retrieve the object associated with the message
* @return Pointer to arbitrary user RefObject
*/
inline RefObject* userData() const
{ return m_data; }
/**
* Set obscure data associated with the message.
* The user data is reference counted to avoid stray pointers.
* Note that setting new user data will disable any notification.
* @param data Pointer to arbitrary user data
*/
void userData(RefObject* data);
/**
* Get a pointer to a derived class of user data given that class name
* @param name Name of the class we are asking for
* @return Pointer to the requested class or NULL if user object id NULL or doesn't implement it
*/
inline void* userObject(const String& name) const
{ return m_data ? m_data->getObject(name) : 0; }
/**
* Enable or disable notification of any @ref MessageNotifier that was set
* as user data. This method must be called after userData()
* @param notify True to have the message call the notifier
*/
inline void setNotify(bool notify = true)
{ m_notify = notify; }
/**
* Retrieve the broadcast flag
* @return True if the message is a broadcast (handling does not stop it)
*/
inline bool broadcast() const
{ return m_broadcast; }
/**
* Reset message. This method should be used when message is going to be re-dispatched.
* Reset message time, track param, return value.
* @param tm Time to set, defaults to current time
*/
void resetMsg(Time tm = Time::now());
/**
* Retrieve a reference to the creation time of the message.
* @return A reference to the @ref Time when the message was created
*/
inline Time& msgTime()
{ return m_time; }
/**
* Retrieve a const reference to the creation time of the message.
* @return A reference to the @ref Time when the message was created
*/
inline const Time& msgTime() const
{ return m_time; }
/**
* Retrieve a reference to the time when message was put in dispatcher.
* @return A reference to the @ref Time when the message was put in dispatcher queue.
* May be 0 if the message was not put in queue or time tracking is not enabled in dispatcher
*/
inline Time& msgTimeEnqueue()
{ return m_timeEnqueue; }
/**
* Retrieve a const reference to the time when message was put in dispatcher.
* @return A reference to the @ref Time when the message was put in dispatcher queue.
* May be 0 if the message was not put in queue or time tracking is not enabled in dispatcher
*/
inline const Time& msgTimeEnqueue() const
{ return m_timeEnqueue; }
/**
* Retrieve a reference to the time when message was dispatched.
* @return A reference to the @ref Time when the message was dispatched
* May be 0 if the message was not dispatched yet or time tracking is not enabled in dispatcher
*/
inline Time& msgTimeDispatch()
{ return m_timeDispatch; }
/**
* Retrieve a const reference to the time when message was dispatched.
* @return A reference to the @ref Time when the message was dispatched
* May be 0 if the message was not dispatched yet or time tracking is not enabled in dispatcher
*/
inline const Time& msgTimeDispatch() const
{ return m_timeDispatch; }
/**
* Name assignment operator
*/
inline Message& operator=(const char* value)
{ String::operator=(value); return *this; }
/**
* Encode the message into a string adequate for sending for processing
* to an external communication interface
* @param id Unique identifier to add to the string
*/
String encode(const char* id) const;
/**
* Encode the message into a string adequate for sending as answer
* to an external communication interface
* @param received True if message was processed locally
* @param id Unique identifier to add to the string
*/
String encode(bool received, const char* id) const;
/**
* Decode a string from an external communication interface for processing
* in the engine. The message is modified accordingly.
* @param str String to decode
* @param id A String object in which the identifier is stored
* @return -2 for success, -1 if the string was not a text form of a
* message, index of first erroneous character if failed
*/
int decode(const char* str, String& id);
/**
* Decode a string from an external communication interface that is an
* answer to a specific external processing request.
* @param str String to decode
* @param received Pointer to variable to store the dispatch return value
* @param id The identifier expected
* @return -2 for success, -1 if the string was not the expected answer,
* index of first erroneous character if failed
*/
int decode(const char* str, bool& received, const char* id);
protected:
/**
* Notify the message it has been dispatched.
* The default behaviour is to call the dispatched() method of the user
* data if it implements @ref MessageNotifier
* @param accepted True if one handler accepted the message
*/
virtual void dispatched(bool accepted);
private:
Message(); // no default constructor please
Message& operator=(const Message& value); // no assignment please
String m_return;
Time m_time;
Time m_timeEnqueue;
Time m_timeDispatch;
RefObject* m_data;
bool m_notify;
bool m_broadcast;
void commonEncode(String& str) const;
int commonDecode(const char* str, int offs);
};
/**
* The purpose of this class is to hold a message received method that is
* called for matching messages. It holds as well the matching criteria
* and priority among other handlers.
* @short A message handler
*/
class YATE_API MessageHandler : public String
{
friend class MessageDispatcher;
YNOCOPY(MessageHandler); // no automatic copies please
public:
/**
* Creates a new message handler.
* @param name Name of the handled message - may be NULL
* @param priority Priority of the handler, 0 = top
* @param trackName Name to be used in handler tracking
* @param addPriority True to append :priority to trackName
*/
explicit MessageHandler(const char* name, unsigned priority = 100,
const char* trackName = 0, bool addPriority = true);
/**
* Handler destructor.
*/
virtual ~MessageHandler();
/**
* Destroys the object, performs cleanup first
*/
virtual void destruct();
/**
* This method is called whenever the registered name matches the message.
* @param msg The received message
* @return True to stop processing, false to try other handlers
*/
virtual bool received(Message& msg) = 0;
/**
* Find out the priority of the handler
* @return Stored priority of the handler, 0 = top
*/
inline unsigned priority() const
{ return m_priority; }
/**
* Retrieve the tracking name of this handler
* @return Name that is to be used in tracking operation
*/
inline const String& trackName() const
{ return m_trackName; }
/**
* Retrieve the tracking name of this handler without added priority
* @return Name that is to be used in tracking operation
*/
inline const String& trackNameOnly() const
{ return m_trackNameOnly; }
/**
* Set a new tracking name for this handler.
* Works only if the handler was not yet inserted into a dispatcher
* @param name Name that is to be used in tracking operation
*/
inline void trackName(const char* name) {
if (m_dispatcher)
return;
m_trackName = name;
String tmp;
tmp << ':' << priority();
if (m_trackName.endsWith(tmp))
m_trackNameOnly = m_trackName.substr(0,m_trackName.length() - tmp.length());
else
m_trackNameOnly = m_trackName;
}
/**
* Retrive the objects counter associated to this handler
* @return Pointer to handler's objects counter or NULL
*/
inline NamedCounter* objectsCounter() const
{ return m_counter; }
/**
* Retrieve the filter (if installed) associated to this handler
* @return MatchingItemBase pointer, NULL if not set
*/
inline const MatchingItemBase* filter() const
{ return m_filter; }
/**
* Set a filters list associated to this handler
* @param filter Pointer to the filters list to install, will be owned and
* destroyed by the handler
*/
inline void setFilter(MatchingItemBase* filter) {
if (filter == m_filter)
return;
clearFilter();
m_filter = filter;
}
/**
* Set a filter for this handler
* @param filter Pointer to the filter to install, will be owned and
* destroyed by the handler. The filter may be a NamedPointer carrying a Regexp
*/
void setFilter(NamedString* filter);
/**
* Set a filter for this handler
* @param name Name of the parameter to filter
* @param value Value of the parameter to filter
*/
inline void setFilter(const char* name, const char* value)
{ setFilter(new MatchingItemString(name,value)); }
/**
* Remove and destroy any filter associated to this handler
*/
void clearFilter();
protected:
/**
* Remove the handler from its dispatcher, remove any installed filter.
* This method is called internally from destruct and the destructor
*/
void cleanup();
/**
* Internal use received function, do not call or override!
* @param msg The received message
* @return True if message was processed
*/
virtual bool receivedInternal(Message& msg);
/**
* Internal use handler unlock, do not call!
*/
void safeNowInternal();
private:
String m_trackName;
String m_trackNameOnly;
unsigned m_priority;
AtomicInt m_unsafe;
MessageDispatcher* m_dispatcher;
MatchingItemBase* m_filter;
NamedCounter* m_counter;
};
/**
* A multiple message receiver to be invoked by a message relay
* @short A multiple message receiver
*/
class YATE_API MessageReceiver : public GenObject
{
public:
/**
* This method is called from the message relay.
* @param msg The received message
* @param id The identifier with which the relay was created
* @return True to stop processing, false to try other handlers
*/
virtual bool received(Message& msg, int id) = 0;
};
/**
* A message handler that allows to relay several messages to a single receiver
* @short A message handler relay
*/
class YATE_API MessageRelay : public MessageHandler
{
YNOCOPY(MessageRelay); // no automatic copies please
public:
/**
* Creates a new message relay.
* @param name Name of the handled message - may be NULL
* @param receiver Receiver of th relayed messages
* @param id Numeric identifier to pass to receiver
* @param priority Priority of the handler, 0 = top
* @param trackName Name to be used in handler tracking
* @param addPriority True to append :priority to trackName
*/
inline MessageRelay(const char* name, MessageReceiver* receiver, int id,
int priority = 100, const char* trackName = 0, bool addPriority = true)
: MessageHandler(name,priority,trackName,addPriority),
m_receiver(receiver), m_id(id)
{ }
/**