mirror of https://github.com/sipwise/sems.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
822 lines
19 KiB
822 lines
19 KiB
/*
|
|
* $Id$
|
|
* Copyright (C) 2002-2003 Fhg Fokus
|
|
*
|
|
* This file is part of sems, a free SIP media server.
|
|
*
|
|
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "IvrDialogBase.h"
|
|
#include "IvrSipDialog.h"
|
|
#include "IvrSipRequest.h"
|
|
#include "IvrSipReply.h"
|
|
#include "IvrAudio.h"
|
|
#include "IvrAudioMixIn.h"
|
|
#include "IvrUAC.h"
|
|
#include "Ivr.h"
|
|
|
|
#include "AmSessionContainer.h"
|
|
|
|
#include "AmConfigReader.h"
|
|
#include "AmConfig.h"
|
|
#include "log.h"
|
|
#include "AmApi.h"
|
|
#include "AmUtils.h"
|
|
#include "AmPlugIn.h"
|
|
|
|
#include <unistd.h>
|
|
#include <pthread.h>
|
|
#include <regex.h>
|
|
#include <dirent.h>
|
|
|
|
#include <set>
|
|
using std::set;
|
|
|
|
|
|
#define PYFILE_REGEX "(.+)\\.(py|pyc|pyo)$"
|
|
|
|
|
|
EXPORT_SESSION_FACTORY(IvrFactory,MOD_NAME);
|
|
|
|
|
|
struct PythonGIL
|
|
{
|
|
PyGILState_STATE gst;
|
|
|
|
PythonGIL() { gst = PyGILState_Ensure(); }
|
|
~PythonGIL(){ PyGILState_Release(gst); }
|
|
};
|
|
|
|
|
|
// This must be the first declaration of every
|
|
// function using Python C-API.
|
|
// But this is not necessary in function which
|
|
// will get called from Python
|
|
#define PYLOCK PythonGIL _py_gil
|
|
|
|
extern "C" {
|
|
|
|
static PyObject* ivr_log(PyObject*, PyObject* args)
|
|
{
|
|
int level;
|
|
char *msg;
|
|
|
|
if(!PyArg_ParseTuple(args,"is",&level,&msg))
|
|
return NULL;
|
|
|
|
if((level)<=log_level) {
|
|
if(log_stderr)
|
|
log_print( level, msg );
|
|
else {
|
|
switch(level){
|
|
case L_ERR:
|
|
syslog(LOG_ERR, "Error: %s", msg);
|
|
break;
|
|
case L_WARN:
|
|
syslog(LOG_WARNING, "Warning: %s", msg);
|
|
break;
|
|
case L_INFO:
|
|
syslog(LOG_INFO, "Info: %s", msg);
|
|
break;
|
|
case L_DBG:
|
|
syslog(LOG_DEBUG, "Debug: %s", msg);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
Py_INCREF(Py_None);
|
|
return Py_None;
|
|
}
|
|
|
|
static PyObject* ivr_getHeader(PyObject*, PyObject* args)
|
|
{
|
|
char* headers;
|
|
char* header_name;
|
|
if(!PyArg_ParseTuple(args,"ss",&headers,&header_name))
|
|
return NULL;
|
|
|
|
string res = getHeader(headers,header_name);
|
|
return PyString_FromString(res.c_str());
|
|
}
|
|
|
|
|
|
static PyObject* ivr_ignoreSigchld(PyObject*, PyObject* args)
|
|
{
|
|
int* ignore;
|
|
if(!PyArg_ParseTuple(args,"i",&ignore))
|
|
return NULL;
|
|
|
|
AmConfig::IgnoreSIGCHLD = ignore;
|
|
DBG("%sgnoring SIGCHLD.\n", ignore?"I":"Not i");
|
|
|
|
return Py_None;
|
|
}
|
|
|
|
static PyObject* ivr_getSessionParam(PyObject*, PyObject* args)
|
|
{
|
|
char* headers;
|
|
char* header_name;
|
|
if(!PyArg_ParseTuple(args,"ss",&headers,&header_name))
|
|
return NULL;
|
|
|
|
string res;
|
|
string iptel_app_param = getHeader(headers, PARAM_HDR);
|
|
if (iptel_app_param.length()) {
|
|
res = get_header_keyvalue(iptel_app_param,header_name);
|
|
} else {
|
|
INFO("Use of P-%s is deprecated. \n", header_name);
|
|
INFO("Use '%s: %s=<addr>' instead.\n", PARAM_HDR, header_name);
|
|
|
|
res = getHeader(headers,string("P-") + header_name);
|
|
}
|
|
|
|
|
|
return PyString_FromString(res.c_str());
|
|
}
|
|
|
|
static PyObject* ivr_createThread(PyObject*, PyObject* args)
|
|
{
|
|
PyObject* py_thread_object = NULL;
|
|
|
|
if(!PyArg_ParseTuple(args,"O",&py_thread_object))
|
|
return NULL;
|
|
|
|
IvrFactory* pIvrFactory = NULL;
|
|
PyObject *module = PyImport_ImportModule(MOD_NAME);
|
|
if (module != NULL) {
|
|
PyObject *ivrFactory = PyObject_GetAttrString(module, "__c_ivrFactory");
|
|
if (ivrFactory != NULL){
|
|
if (PyCObject_Check(ivrFactory))
|
|
pIvrFactory = (IvrFactory*)PyCObject_AsVoidPtr(ivrFactory);
|
|
Py_DECREF(ivrFactory);
|
|
}
|
|
}
|
|
if (pIvrFactory)
|
|
pIvrFactory->addDeferredThread(py_thread_object);
|
|
else
|
|
ERROR("Could not find __c_ivrFactory in Python state.\n");
|
|
|
|
return Py_None;
|
|
}
|
|
|
|
static PyMethodDef ivr_methods[] = {
|
|
{"log", (PyCFunction)ivr_log, METH_VARARGS,"Log a message using Sems' logging system"},
|
|
{"getHeader", (PyCFunction)ivr_getHeader, METH_VARARGS,"Python getHeader wrapper"},
|
|
{"getSessionParam", (PyCFunction)ivr_getSessionParam, METH_VARARGS,"Python getSessionParam wrapper"},
|
|
{"createThread", (PyCFunction)ivr_createThread, METH_VARARGS, "Create another interpreter thread"},
|
|
{"setIgnoreSigchld", (PyCFunction)ivr_ignoreSigchld, METH_VARARGS, "ignore SIGCHLD signal"},
|
|
{NULL} /* Sentinel */
|
|
};
|
|
}
|
|
|
|
void PythonScriptThread::run() {
|
|
PYLOCK;
|
|
DBG("PythonScriptThread - calling python function.\n");
|
|
PyObject_CallObject(py_thread_object, NULL);
|
|
DBG("PythonScriptThread - thread finished..\n");
|
|
}
|
|
|
|
void PythonScriptThread::on_stop() {
|
|
DBG("PythonScriptThread::on_stop.\n");
|
|
}
|
|
|
|
IvrFactory::IvrFactory(const string& _app_name)
|
|
: AmSessionFactory(_app_name),
|
|
user_timer_fact(NULL)
|
|
{
|
|
}
|
|
|
|
// void IvrFactory::setScriptPath(const string& path)
|
|
// {
|
|
// string python_path = script_path = path;
|
|
|
|
|
|
// if(python_path.length()){
|
|
|
|
// python_path = AmConfig::PlugInPath + ":" + python_path;
|
|
// }
|
|
// else
|
|
// python_path = AmConfig::PlugInPath;
|
|
|
|
// char* old_path=0;
|
|
// if((old_path = getenv("PYTHONPATH")) != 0)
|
|
// if(strlen(old_path))
|
|
// python_path += ":" + string(old_path);
|
|
|
|
// DBG("setting PYTHONPATH to: '%s'\n",python_path.c_str());
|
|
// setenv("PYTHONPATH",python_path.c_str(),1);
|
|
|
|
// }
|
|
|
|
void IvrFactory::import_object(PyObject* m, char* name, PyTypeObject* type)
|
|
{
|
|
if (PyType_Ready(type) < 0){
|
|
ERROR("PyType_Ready failed !\n");
|
|
return;
|
|
}
|
|
Py_INCREF(type);
|
|
PyModule_AddObject(m, name, (PyObject *)type);
|
|
}
|
|
|
|
void IvrFactory::import_ivr_builtins()
|
|
{
|
|
// ivr module - start
|
|
PyImport_AddModule("ivr");
|
|
ivr_module = Py_InitModule("ivr",ivr_methods);
|
|
|
|
PyObject* pIvrFactory = PyCObject_FromVoidPtr((void*)this,NULL);
|
|
if (pIvrFactory != NULL)
|
|
PyModule_AddObject(ivr_module, "__c_ivrFactory", pIvrFactory);
|
|
|
|
// IvrSipDialog (= AmSipDialog)
|
|
import_object(ivr_module, "IvrSipDialog", &IvrSipDialogType);
|
|
|
|
// IvrDialogBase
|
|
import_object(ivr_module,"IvrDialogBase",&IvrDialogBaseType);
|
|
|
|
// IvrSipRequest
|
|
import_object(ivr_module,"IvrSipRequest",&IvrSipRequestType);
|
|
|
|
// IvrSipReply
|
|
import_object(ivr_module,"IvrSipReply",&IvrSipReplyType);
|
|
|
|
// IvrAudioFile
|
|
import_object(ivr_module,"IvrAudioFile",&IvrAudioFileType);
|
|
|
|
// IvrAudioMixIn
|
|
import_object(ivr_module,"IvrAudioMixIn",&IvrAudioMixInType);
|
|
|
|
// IvrUAC
|
|
import_object(ivr_module,"IvrUAC",&IvrUACType);
|
|
|
|
PyModule_AddIntConstant(ivr_module, "AUDIO_READ",AUDIO_READ);
|
|
PyModule_AddIntConstant(ivr_module, "AUDIO_WRITE",AUDIO_WRITE);
|
|
|
|
// add log level for the log module
|
|
PyModule_AddIntConstant(ivr_module, "SEMS_LOG_LEVEL",log_level);
|
|
|
|
PyObject* log_mod_name = PyString_FromString("log");
|
|
PyObject* log_mod = PyImport_Import(log_mod_name);
|
|
Py_DECREF(log_mod_name);
|
|
|
|
if(!log_mod){
|
|
PyErr_Print();
|
|
ERROR("IvrFactory: could not find the log python module.\n");
|
|
ERROR("IvrFactory: please check your installation.\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
void IvrFactory::init_python_interpreter(const string& script_path)
|
|
{
|
|
if(!Py_IsInitialized()){
|
|
|
|
add_env_path("PYTHONPATH",AmConfig::PlugInPath);
|
|
Py_Initialize();
|
|
}
|
|
|
|
PyEval_InitThreads();
|
|
set_sys_path(script_path);
|
|
import_ivr_builtins();
|
|
PyEval_ReleaseLock();
|
|
}
|
|
|
|
void IvrFactory::set_sys_path(const string& script_path)
|
|
{
|
|
|
|
PyObject* py_mod_name = PyString_FromString("sys");
|
|
PyObject* py_mod = PyImport_Import(py_mod_name);
|
|
Py_DECREF(py_mod_name);
|
|
|
|
if(!py_mod){
|
|
PyErr_Print();
|
|
ERROR("IvrFactory: could not import 'sys' module.\n");
|
|
ERROR("IvrFactory: please check your installation.\n");
|
|
return;
|
|
}
|
|
|
|
PyObject* sys_path_str = PyString_FromString("path");
|
|
PyObject* sys_path = PyObject_GetAttr(py_mod,sys_path_str);
|
|
Py_DECREF(sys_path_str);
|
|
|
|
if(!sys_path){
|
|
PyErr_Print();
|
|
Py_DECREF(py_mod);
|
|
return;
|
|
}
|
|
|
|
if(!PyList_Insert(sys_path,0,PyString_FromString(script_path.c_str()))){
|
|
PyErr_Print();
|
|
}
|
|
}
|
|
|
|
IvrDialog* IvrFactory::newDlg(const string& name)
|
|
{
|
|
PYLOCK;
|
|
|
|
map<string,IvrScriptDesc>::iterator mod_it = mod_reg.find(name);
|
|
if(mod_it == mod_reg.end()){
|
|
ERROR("Unknown script name '%s'\n", name.c_str());
|
|
throw AmSession::Exception(500,"Unknown Application");
|
|
}
|
|
|
|
IvrScriptDesc& mod_desc = mod_it->second;
|
|
|
|
AmDynInvoke* user_timer = user_timer_fact->getInstance();
|
|
if(!user_timer){
|
|
ERROR("could not get a user timer reference\n");
|
|
throw AmSession::Exception(500,"could not get a user timer reference");
|
|
}
|
|
|
|
|
|
IvrDialog* dlg = new IvrDialog(user_timer);
|
|
|
|
PyObject* c_dlg = PyCObject_FromVoidPtr(dlg,NULL);
|
|
PyObject* dlg_inst = PyObject_CallMethod(mod_desc.dlg_class,"__new__","OO",
|
|
mod_desc.dlg_class,c_dlg);
|
|
Py_DECREF(c_dlg);
|
|
|
|
if(!dlg_inst){
|
|
|
|
delete dlg;
|
|
|
|
PyErr_Print();
|
|
ERROR("IvrFactory: while loading \"%s\": could not create instance\n",
|
|
name.c_str());
|
|
throw AmSession::Exception(500,"Internal error in IVR plug-in.\n");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
dlg->setPyPtrs(mod_desc.mod,dlg_inst);
|
|
Py_DECREF(dlg_inst);
|
|
|
|
return dlg;
|
|
}
|
|
|
|
bool IvrFactory::loadScript(const string& path)
|
|
{
|
|
PYLOCK;
|
|
|
|
PyObject *modName=NULL,*mod=NULL,*dict=NULL,*dlg_class=NULL,*config=NULL;
|
|
|
|
// load module configuration
|
|
AmConfigReader cfg;
|
|
string cfg_file = add2path(AmConfig::ModConfigPath,1,(path + ".conf").c_str());
|
|
config = PyDict_New();
|
|
if(!config){
|
|
ERROR("could not allocate new dict for config\n");
|
|
goto error2;
|
|
}
|
|
|
|
if(cfg.loadFile(cfg_file)){
|
|
WARN("could not load config file at %s\n",cfg_file.c_str());
|
|
} else {
|
|
for(map<string,string>::const_iterator it = cfg.begin();
|
|
it != cfg.end(); it++){
|
|
PyDict_SetItem(config,
|
|
PyString_FromString(it->first.c_str()),
|
|
PyString_FromString(it->second.c_str()));
|
|
}
|
|
}
|
|
|
|
// set config ivr ivr_module while loading
|
|
Py_INCREF(config);
|
|
PyObject_SetAttrString(ivr_module,"config",config);
|
|
|
|
// load module
|
|
modName = PyString_FromString(path.c_str());
|
|
|
|
mod = PyImport_Import(modName);
|
|
Py_DECREF(modName);
|
|
if (NULL != config) {
|
|
// remove config ivr ivr_module while loading
|
|
PyObject_DelAttrString(ivr_module, "config");
|
|
Py_DECREF(config);
|
|
}
|
|
|
|
if(!mod){
|
|
PyErr_Print();
|
|
WARN("IvrFactory: Failed to load \"%s\"\n", path.c_str());
|
|
|
|
dict = PyImport_GetModuleDict();
|
|
Py_INCREF(dict);
|
|
PyDict_DelItemString(dict,path.c_str());
|
|
Py_DECREF(dict);
|
|
|
|
return false;
|
|
}
|
|
|
|
dict = PyModule_GetDict(mod);
|
|
dlg_class = PyDict_GetItemString(dict, "IvrDialog");
|
|
|
|
if(!dlg_class){
|
|
|
|
PyErr_Print();
|
|
WARN("IvrFactory: class IvrDialog not found in \"%s\"\n", path.c_str());
|
|
goto error1;
|
|
}
|
|
|
|
Py_INCREF(dlg_class);
|
|
|
|
if(!PyObject_IsSubclass(dlg_class,(PyObject*)&IvrDialogBaseType)){
|
|
|
|
WARN("IvrFactory: in \"%s\": IvrDialog is not a subtype of IvrDialogBase\n",
|
|
path.c_str());
|
|
goto error2;
|
|
}
|
|
|
|
PyObject_SetAttrString(mod,"config",config);
|
|
|
|
mod_reg.insert(std::make_pair(path,
|
|
IvrScriptDesc(mod,dlg_class)));
|
|
|
|
return true;
|
|
|
|
error2:
|
|
Py_DECREF(dlg_class);
|
|
error1:
|
|
Py_DECREF(mod);
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Loads python script path and default script file from configuration file
|
|
*/
|
|
int IvrFactory::onLoad()
|
|
{
|
|
user_timer_fact = AmPlugIn::instance()->getFactory4Di("user_timer");
|
|
if(!user_timer_fact){
|
|
|
|
ERROR("could not load user_timer from session_timer plug-in\n");
|
|
return -1;
|
|
}
|
|
|
|
|
|
AmConfigReader cfg;
|
|
|
|
if(cfg.loadFile(add2path(AmConfig::ModConfigPath,1,MOD_NAME ".conf")))
|
|
return -1;
|
|
|
|
// get application specific global parameters
|
|
configureModule(cfg);
|
|
|
|
//setScriptPath(cfg.getParameter("script_path"));
|
|
string script_path = cfg.getParameter("script_path");
|
|
init_python_interpreter(script_path);
|
|
|
|
DBG("** IVR compile time configuration:\n");
|
|
DBG("** built with PYTHON support.\n");
|
|
|
|
#ifdef IVR_WITH_TTS
|
|
DBG("** Text-To-Speech enabled\n");
|
|
#else
|
|
DBG("** Text-To-Speech disabled\n");
|
|
#endif
|
|
|
|
DBG("** IVR run time configuration:\n");
|
|
DBG("** script path: \'%s\'\n", script_path.c_str());
|
|
|
|
regex_t reg;
|
|
if(regcomp(®,PYFILE_REGEX,REG_EXTENDED)){
|
|
ERROR("while compiling regular expression\n");
|
|
return -1;
|
|
}
|
|
|
|
DIR* dir = opendir(script_path.c_str());
|
|
if(!dir){
|
|
regfree(®);
|
|
ERROR("Ivr: script pre-loader (%s): %s\n",
|
|
script_path.c_str(),strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
DBG("directory '%s' opened\n",script_path.c_str());
|
|
|
|
set<string> unique_entries;
|
|
regmatch_t pmatch[2];
|
|
|
|
struct dirent* entry=0;
|
|
while((entry = readdir(dir)) != NULL){
|
|
|
|
if(!regexec(®,entry->d_name,2,pmatch,0)){
|
|
|
|
string name(entry->d_name + pmatch[1].rm_so,
|
|
pmatch[1].rm_eo - pmatch[1].rm_so);
|
|
|
|
unique_entries.insert(name);
|
|
}
|
|
}
|
|
closedir(dir);
|
|
regfree(®);
|
|
|
|
AmPlugIn* plugin = AmPlugIn::instance();
|
|
for(set<string>::iterator it = unique_entries.begin();
|
|
it != unique_entries.end(); it++) {
|
|
|
|
if(loadScript(*it)){
|
|
bool res = plugin->registerFactory4App(*it,this);
|
|
if(res)
|
|
INFO("Application script registered: %s.\n",
|
|
it->c_str());
|
|
}
|
|
}
|
|
|
|
start_deferred_threads();
|
|
|
|
return 0; // don't stop sems from starting up
|
|
}
|
|
|
|
void IvrFactory::addDeferredThread(PyObject* pyCallable) {
|
|
deferred_threads.push(pyCallable);
|
|
}
|
|
|
|
void IvrFactory::start_deferred_threads() {
|
|
if (!deferred_threads.empty()) {
|
|
while (!deferred_threads.empty()) {
|
|
PythonScriptThread* t = new PythonScriptThread(deferred_threads.front());
|
|
deferred_threads.pop();
|
|
t->start();
|
|
AmThreadWatcher::instance()->add(t);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int IvrDialog::transfer(const string& target)
|
|
{
|
|
return dlg.transfer(target);
|
|
}
|
|
|
|
int IvrDialog::drop()
|
|
{
|
|
int res = dlg.drop();
|
|
if (res)
|
|
setStopped();
|
|
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Load a script using user name from URI.
|
|
* Note: there is no default script.
|
|
*/
|
|
AmSession* IvrFactory::onInvite(const AmSipRequest& req)
|
|
{
|
|
if(req.cmd != MOD_NAME)
|
|
return newDlg(req.cmd);
|
|
else
|
|
return newDlg(req.user);
|
|
}
|
|
|
|
IvrDialog::IvrDialog(AmDynInvoke* user_timer)
|
|
: py_mod(NULL),
|
|
py_dlg(NULL),
|
|
playlist(this),
|
|
user_timer(user_timer)
|
|
{
|
|
set_sip_relay_only(false);
|
|
}
|
|
|
|
IvrDialog::~IvrDialog()
|
|
{
|
|
DBG("----------- IvrDialog::~IvrDialog() ------------- \n");
|
|
|
|
playlist.close(false);
|
|
|
|
PYLOCK;
|
|
Py_XDECREF(py_mod);
|
|
Py_XDECREF(py_dlg);
|
|
}
|
|
|
|
void IvrDialog::setPyPtrs(PyObject *mod, PyObject *dlg)
|
|
{
|
|
assert(py_mod = mod);
|
|
assert(py_dlg = dlg);
|
|
Py_INCREF(py_mod);
|
|
Py_INCREF(py_dlg);
|
|
}
|
|
|
|
static PyObject *
|
|
type_error(const char *msg)
|
|
{
|
|
PyErr_SetString(PyExc_TypeError, msg);
|
|
return NULL;
|
|
}
|
|
|
|
static PyObject *
|
|
null_error(void)
|
|
{
|
|
if (!PyErr_Occurred())
|
|
PyErr_SetString(PyExc_SystemError,
|
|
"null argument to internal routine");
|
|
return NULL;
|
|
}
|
|
|
|
PyObject *
|
|
PyObject_VaCallMethod(PyObject *o, char *name, char *format, va_list va)
|
|
{
|
|
PyObject *args, *func = 0, *retval;
|
|
|
|
if (o == NULL || name == NULL)
|
|
return null_error();
|
|
|
|
func = PyObject_GetAttrString(o, name);
|
|
if (func == NULL) {
|
|
PyErr_SetString(PyExc_AttributeError, name);
|
|
return 0;
|
|
}
|
|
|
|
if (!PyCallable_Check(func))
|
|
return type_error("call of non-callable attribute");
|
|
|
|
if (format && *format) {
|
|
args = Py_VaBuildValue(format, va);
|
|
}
|
|
else
|
|
args = PyTuple_New(0);
|
|
|
|
if (!args)
|
|
return NULL;
|
|
|
|
if (!PyTuple_Check(args)) {
|
|
PyObject *a;
|
|
|
|
a = PyTuple_New(1);
|
|
if (a == NULL)
|
|
return NULL;
|
|
if (PyTuple_SetItem(a, 0, args) < 0)
|
|
return NULL;
|
|
args = a;
|
|
}
|
|
|
|
retval = PyObject_Call(func, args, NULL);
|
|
|
|
Py_DECREF(args);
|
|
Py_DECREF(func);
|
|
|
|
return retval;
|
|
}
|
|
|
|
bool IvrDialog::callPyEventHandler(char* name, char* fmt, ...)
|
|
{
|
|
bool ret=false;
|
|
va_list va;
|
|
|
|
PYLOCK;
|
|
|
|
va_start(va, fmt);
|
|
PyObject* o = PyObject_VaCallMethod(py_dlg,name,fmt,va);
|
|
va_end(va);
|
|
|
|
if(!o) {
|
|
|
|
if(PyErr_ExceptionMatches(PyExc_AttributeError)){
|
|
|
|
DBG("method %s is not implemented, trying default one\n",name);
|
|
return true;
|
|
}
|
|
|
|
PyErr_Print();
|
|
}
|
|
else {
|
|
if(o && PyBool_Check(o) && (o == Py_True)) {
|
|
|
|
ret = true;
|
|
}
|
|
|
|
Py_DECREF(o);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void IvrDialog::onSessionStart(const AmSipRequest& req)
|
|
{
|
|
callPyEventHandler("onSessionStart","s",req.hdrs.c_str());
|
|
setInOut(&playlist,&playlist);
|
|
AmB2BCallerSession::onSessionStart(req);
|
|
}
|
|
|
|
void IvrDialog::onBye(const AmSipRequest& req)
|
|
{
|
|
if(callPyEventHandler("onBye",NULL))
|
|
AmB2BSession::onBye(req);
|
|
}
|
|
|
|
void IvrDialog::onDtmf(int event, int duration_msec)
|
|
{
|
|
if(callPyEventHandler("onDtmf","ii",event,duration_msec))
|
|
AmB2BSession::onDtmf(event,duration_msec);
|
|
}
|
|
|
|
void IvrDialog::onOtherBye(const AmSipRequest& req)
|
|
{
|
|
if(callPyEventHandler("onOtherBye",NULL))
|
|
AmB2BSession::onOtherBye(req);
|
|
}
|
|
|
|
bool IvrDialog::onOtherReply(const AmSipReply& r)
|
|
{
|
|
if(callPyEventHandler("onOtherReply","is",
|
|
r.code,r.reason.c_str()))
|
|
AmB2BSession::onOtherReply(r);
|
|
return false;
|
|
}
|
|
|
|
void IvrDialog::onSipReply(const AmSipReply& r) {
|
|
AmSipReply* rep_cpy = new AmSipReply(r);
|
|
callPyEventHandler("onSipReply","O",IvrSipReply_FromPtr(rep_cpy));
|
|
AmB2BSession::onSipReply(r);
|
|
}
|
|
|
|
void IvrDialog::onSipRequest(const AmSipRequest& r){
|
|
AmSipRequest* req_cpy = new AmSipRequest(r);
|
|
callPyEventHandler("onSipRequest","O", IvrSipRequest_FromPtr(req_cpy));
|
|
AmB2BSession::onSipRequest(r);
|
|
}
|
|
|
|
void IvrDialog::onRtpTimeout() {
|
|
callPyEventHandler("onRtpTimeout",NULL);
|
|
}
|
|
|
|
void IvrDialog::process(AmEvent* event)
|
|
{
|
|
DBG("IvrDialog::process\n");
|
|
|
|
AmAudioEvent* audio_event = dynamic_cast<AmAudioEvent*>(event);
|
|
if(audio_event && audio_event->event_id == AmAudioEvent::noAudio){
|
|
|
|
callPyEventHandler("onEmptyQueue", NULL);
|
|
event->processed = true;
|
|
}
|
|
|
|
AmPluginEvent* plugin_event = dynamic_cast<AmPluginEvent*>(event);
|
|
if(plugin_event && plugin_event->name == "timer_timeout") {
|
|
|
|
callPyEventHandler("onTimer", "i", plugin_event->data.get(0).asInt());
|
|
event->processed = true;
|
|
}
|
|
|
|
if (!event->processed)
|
|
AmB2BCallerSession::process(event);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
void IvrDialog::connectCallee(const string& remote_party, const string& remote_uri,
|
|
const string& from_party, const string& from_uri) {
|
|
b2b_callee_from_party = from_party;
|
|
b2b_callee_from_uri = from_uri;
|
|
AmB2BCallerSession::connectCallee(remote_party, remote_uri);
|
|
}
|
|
|
|
void IvrDialog::createCalleeSession()
|
|
{
|
|
AmB2BCalleeSession* callee_session = new AmB2BCalleeSession(this);
|
|
AmSipDialog& callee_dlg = callee_session->dlg;
|
|
|
|
other_id = AmSession::getNewId();
|
|
|
|
callee_dlg.local_tag = other_id;
|
|
callee_dlg.callid = AmSession::getNewId() + "@" + AmConfig::LocalIP;
|
|
|
|
// this will be overwritten by ConnectLeg event
|
|
callee_dlg.remote_party = dlg.local_party;
|
|
callee_dlg.remote_uri = dlg.local_uri;
|
|
|
|
if (b2b_callee_from_party.empty() && b2b_callee_from_uri.empty()) {
|
|
// default: use the original To as From in the callee leg
|
|
callee_dlg.local_party = dlg.remote_party;
|
|
callee_dlg.local_uri = dlg.remote_uri;
|
|
} else {
|
|
// if given as parameters, use these
|
|
callee_dlg.local_party = b2b_callee_from_party;
|
|
callee_dlg.local_uri = b2b_callee_from_uri;
|
|
}
|
|
|
|
DBG("Created B2BUA callee leg, From: %s\n",
|
|
callee_dlg.local_party.c_str());
|
|
|
|
callee_session->start();
|
|
|
|
AmSessionContainer* sess_cont = AmSessionContainer::instance();
|
|
sess_cont->addSession(other_id,callee_session);
|
|
}
|