Imported Upstream version 1.4.3

upstream upstream/1.4.3
Victor Seva 13 years ago
parent 4dc5f23b7b
commit d8b3cbf475

21
.gitignore vendored

@ -1,21 +0,0 @@
*.a
*.d
*.o
*.pyc
*.pyo
*.so
*~
apps/py_sems/sip/*.cpp
apps/py_sems/sip/*.h
apps/py_sems/sip/Makefile.gen
core/etc/*.conf
core/etc/*.template
getarch
getos
sems
sems-stats
sems-logfile-callextract
cscope.out
cscope.files
semantic.cache

@ -23,11 +23,11 @@ clean:
.PHONY: modules
modules:
@for r in $(subdirs) "" ; do \
-@for r in $(subdirs) "" ; do \
if [ -n "$$r" ]; then \
echo "" ; \
echo "making $$r" ; \
$(MAKE) -C $$r all || exit 1; \
$(MAKE) -C $$r all; \
fi ; \
done

@ -3,25 +3,25 @@ else
makefile_defs=1
export makefile_defs
GIT = $(shell which git)
SVNVERSION = $(shell which svnversion)
ifneq ($(GIT),)
SCM_REV = $(shell if git --help describe |grep dirty 2>&1 >/dev/null ; \
then git describe --always --dirty 2>/dev/null; \
else git describe --always 2>/dev/null; fi)
endif
ifeq ($(SCM_REV),)
ifneq ($(SVNVERSION),)
SCM_REV = r$(shell svnversion -n .)
endif
endif
#GIT = $(shell which git)
#SVNVERSION = $(shell which svnversion)
#
#ifneq ($(GIT),)
# SCM_REV = $(shell if git --help describe |grep dirty 2>&1 >/dev/null ; \
# then git describe --always --dirty 2>/dev/null; \
# else git describe --always 2>/dev/null; fi)
#endif
#
#ifeq ($(SCM_REV),)
#ifneq ($(SVNVERSION),)
# SCM_REV = r$(shell svnversion -n .)
#endif
#endif
#version number
VERSION = 1
PATCHLEVEL = 4
SUBLEVEL = 3
SUBLEVEL = 3
ifneq ($(SCM_REV),)
RELEASE = $(SCM_REV)
@ -50,7 +50,7 @@ CPPFLAGS += -D_DEBUG \
# if compiled without thread pool support, every
# session will have its own thread.
#
USE_THREADPOOL = yes
#USE_THREADPOOL = yes
# compile with spandsp DTMF detection? see soft-switch.org
# this needs a fairly new version of spandsp - tested with 0.0.4pre11

@ -23,7 +23,7 @@ else
ifneq ($(local_exclude_app_modules),)
exclude_app_modules = $(local_exclude_app_modules)
else
exclude_app_modules += mp3 examples py_sems xmlrpc2di gateway jsonrpc twit mobile_push
exclude_app_modules += mp3 examples py_sems xmlrpc2di gateway jsonrpc twit
ifneq ($(USE_MONITORING), yes)
exclude_app_modules += monitoring
@ -58,16 +58,16 @@ clean:
.PHONY: modules
modules:
@for r in $(app_modules) "" ; do \
if [ -n "$$r" -a -f "$$r"/Makefile ]; then \
-@for r in $(app_modules) "" ; do \
if [ -n "$$r" ]; then \
echo "" ; \
echo "" ; \
COREPATH=../$(COREPATH) $(MAKE) -C $$r all || exit 1; \
COREPATH=../$(COREPATH) $(MAKE) -C $$r all; \
fi ; \
done
.PHONY: install
install: install-bin
install: install-bin install-cfg
.PHONY: install-bin
install-bin:

File diff suppressed because it is too large Load Diff

@ -1,269 +0,0 @@
/*
* Copyright (C) 2011 Stefan Sayer
*
* This file is part of SEMS, a free SIP media server.
*
* SEMS 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.
*
* For a license to use the sems software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* SEMS 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
*/
#ifndef _DB_REGAgent_h_
#define _DB_REGAgent_h_
#include <sys/time.h>
#include <mysql++/mysql++.h>
#include <map>
using std::map;
#include <queue>
using std::queue;
#include "AmApi.h"
#include "AmSipRegistration.h"
#include "RegistrationTimer.h"
#define REG_STATUS_INACTIVE 0
#define REG_STATUS_PENDING 1
#define REG_STATUS_ACTIVE 2
#define REG_STATUS_FAILED 3
#define REG_STATUS_REMOVED 4
#define REG_STATUS_TO_BE_REMOVED 5
#define REG_STATUS_INACTIVE_S "0"
#define REG_STATUS_PENDING_S "1"
#define REG_STATUS_ACTIVE_S "2"
#define REG_STATUS_FAILED_S "3"
#define REG_STATUS_REMOVED_S "4"
#define REG_STATUS_TO_BE_REMOVED_S "5"
#define COLNAME_SUBSCRIBER_ID "subscriber_id"
#define COLNAME_USER "user"
#define COLNAME_PASS "pass"
#define COLNAME_REALM "realm"
#define COLNAME_CONTACT "contact"
#define COLNAME_STATUS "registration_status"
#define COLNAME_EXPIRY "expiry"
#define COLNAME_REGISTRATION_TS "last_registration"
#define COLNAME_LAST_CODE "last_code"
#define COLNAME_LAST_REASON "last_reason"
#define RegistrationActionEventID 117
#define ERR_REASON_UNABLE_TO_SEND_REQUEST "unable to send request"
struct RegistrationActionEvent : public AmEvent {
enum RegAction { Register=0, Deregister };
RegistrationActionEvent(RegAction action, int subscriber_id)
: AmEvent(RegistrationActionEventID),
action(action), subscriber_id(subscriber_id) { }
RegAction action;
int subscriber_id;
};
class DBRegAgent;
// separate thread for REGISTER sending, which can block for rate limiting
class DBRegAgentProcessorThread
: public AmThread,
public AmEventQueue,
public AmEventHandler
{
DBRegAgent* reg_agent;
bool stopped;
void rateLimitWait();
double allowance;
struct timeval last_check;
protected:
void process(AmEvent* ev);
public:
DBRegAgentProcessorThread();
~DBRegAgentProcessorThread();
void run();
void on_stop();
};
class DBRegAgent
: public AmDynInvokeFactory,
public AmDynInvoke,
public AmThread,
public AmEventQueue,
public AmEventHandler
{
static string joined_query;
static string registrations_table;
static double reregister_interval;
static double minimum_reregister_interval;
static bool enable_ratelimiting;
static unsigned int ratelimit_rate;
static unsigned int ratelimit_per;
static bool ratelimit_slowstart;
static bool delete_removed_registrations;
static bool delete_failed_deregistrations;
static bool save_contacts;
static bool db_read_contact;
static string contact_hostport;
static string outbound_proxy;
static bool save_auth_replies;
static unsigned int error_retry_interval;
map<long, AmSIPRegistration*> registrations;
map<string, long> registration_ltags;
map<long, RegTimer*> registration_timers;
AmMutex registrations_mut;
// connection used in main DBRegAgent thread
static mysqlpp::Connection MainDBConnection;
// connection used in other thread (processor thread)
static mysqlpp::Connection ProcessorDBConnection;
int onLoad();
void onUnload();
RegistrationTimer registration_scheduler;
DBRegAgentProcessorThread registration_processor;
bool loadRegistrations();
void createDBRegistration(long subscriber_id, mysqlpp::Connection& conn);
void deleteDBRegistration(long subscriber_id, mysqlpp::Connection& conn);
void updateDBRegistration(mysqlpp::Connection& db_connection,
long subscriber_id, int last_code,
const string& last_reason,
bool update_status = false, int status = 0,
bool update_ts=false, unsigned int expiry = 0,
bool update_contacts=false, const string& contacts = "");
/** create registration in our list */
void createRegistration(long subscriber_id,
const string& user,
const string& pass,
const string& realm,
const string& contact);
/** update registration in our list */
void updateRegistration(long subscriber_id,
const string& user,
const string& pass,
const string& realm,
const string& contact);
/** remove registration */
void removeRegistration(long subscriber_id);
/** schedule this subscriber to REGISTER imminently */
void scheduleRegistration(long subscriber_id);
/** schedule this subscriber to de-REGISTER imminently*/
void scheduleDeregistration(long subscriber_id);
/** create a timer for the registration - fixed expiry + action */
void setRegistrationTimer(long subscriber_id, unsigned int timeout,
RegistrationActionEvent::RegAction reg_action);
/** create a registration refresh timer for that registration
@param subscriber_id - ID of subscription
@param expiry - SIP registration expiry time
@param reg_start_ts - start TS of the SIP registration
@param now_time - current time
*/
void setRegistrationTimer(long subscriber_id,
time_t expiry, time_t reg_start_ts, time_t now_time);
/** clear re-registration timer and remove timer object */
void clearRegistrationTimer(long subscriber_id);
/** remove timer object */
void removeRegistrationTimer(long subscriber_id);
// void run_tests();
// amThread
void run();
void on_stop();
// AmEventHandler
void process(AmEvent* ev);
void onSipReplyEvent(AmSipReplyEvent* ev);
void onRegistrationActionEvent(RegistrationActionEvent* reg_action_ev);
unsigned int expires;
/** processing thread running? */
bool running;
/** processing thread shutdown finished? */
bool shutdown_finished;
AmDynInvoke* uac_auth_i;
void DIcreateRegistration(int subscriber_id, const string& user,
const string& pass, const string& realm,
const string& contact, AmArg& ret);
void DIupdateRegistration(int subscriber_id, const string& user,
const string& pass, const string& realm,
const string& contact, AmArg& ret);
void DIremoveRegistration(int subscriber_id, AmArg& ret);
void DIrefreshRegistration(int subscriber_id, AmArg& ret);
public:
DBRegAgent(const string& _app_name);
~DBRegAgent();
DECLARE_MODULE_INSTANCE(DBRegAgent);
// DI
// DI factory
AmDynInvoke* getInstance() { return instance(); }
// DI API
void invoke(const string& method,
const AmArg& args, AmArg& ret);
/** re-registration timer callback */
void timer_cb(RegTimer* timer, long subscriber_id, int data2);
friend class DBRegAgentProcessorThread;
};
#endif

@ -1,7 +0,0 @@
plug_in_name = db_reg_agent
module_ldflags = -lmysqlpp
module_cflags = -DMOD_NAME=\"$(plug_in_name)\" -I/usr/include/mysql++ -I/usr/include/mysql
COREPATH ?=../../core
include $(COREPATH)/plug-in/Makefile.app_module

@ -1,301 +0,0 @@
/*
* Copyright (C) 2011 Stefan Sayer
*
* This file is part of SEMS, a free SIP media server.
*
* SEMS 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.
*
* For a license to use the sems software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* SEMS 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 "RegistrationTimer.h"
#include <stdlib.h>
RegistrationTimer::RegistrationTimer()
: current_bucket(0)
{
struct timeval now;
gettimeofday(&now, 0);
current_bucket_start = now.tv_sec;
}
// unsafe!
int RegistrationTimer::get_bucket_index(time_t tv) {
time_t buckets_start_time = current_bucket_start;
if (tv < buckets_start_time)
return -1;
// offset
int bucket_index = (tv - buckets_start_time);
bucket_index /= TIMER_BUCKET_LENGTH;
if (bucket_index > TIMER_BUCKETS) { // too far in the future
ERROR("requested timer too far in the future (index %d vs %d TIMER_BUCKETS)\n",
bucket_index, TIMER_BUCKETS);
return -2;
}
bucket_index += current_bucket;
bucket_index %= TIMER_BUCKETS; // circular array
return bucket_index;
}
void RegistrationTimer::place_timer(RegTimer* timer, int bucket_index) {
if (bucket_index < 0) {
ERROR("trying to place_timer with negative index (%i)\n", bucket_index);
return;
}
if (bucket_index > TIMER_BUCKETS) {
ERROR("trying to place_timer with too high index (%i vs %i)\n",
bucket_index, TIMER_BUCKETS);
return;
}
std::list<RegTimer*>::iterator it = buckets[bucket_index].timers.begin();
while (it != buckets[bucket_index].timers.end() &&
(timer->expires > (*it)->expires))
it++;
buckets[bucket_index].timers.insert(it, timer);
size_t b_size = buckets[bucket_index].timers.size();
DBG("inserted timer [%p] in bucket %i (now sized %zd)\n",
timer, bucket_index, b_size);
}
void RegistrationTimer::fire_timer(RegTimer* timer) {
if (timer && timer->cb) {
DBG("firing timer [%p]\n", timer);
timer->cb(timer, timer->data1, timer->data2);
}
}
bool RegistrationTimer::insert_timer(RegTimer* timer) {
if (!timer)
return false;
buckets_mut.lock();
int bucket_index = get_bucket_index(timer->expires);
if (bucket_index == -1) {
// already expired, fire timer
buckets_mut.unlock();
DBG("inserting already expired timer [%p], firing\n", timer);
fire_timer(timer);
return false;
}
if (bucket_index == -2) {
ERROR("trying to place timer too far in the future\n");
buckets_mut.unlock();
return false;
}
place_timer(timer, bucket_index);
buckets_mut.unlock();
return true;
}
bool RegistrationTimer::remove_timer(RegTimer* timer) {
if (!timer)
return false;
bool res = false;
buckets_mut.lock();
int bucket_index = get_bucket_index(timer->expires);
if (bucket_index < 0) {
buckets_mut.unlock();
return false;
}
std::list<RegTimer*>& timerlist = buckets[bucket_index].timers;
for (std::list<RegTimer*>::iterator it = timerlist.begin();
it != timerlist.end(); it++) {
if (*it == timer) {
timerlist.erase(it);
res = true;
break;
}
}
buckets_mut.unlock();
if (res) {
DBG("successfully removed timer [%p]\n", timer);
} else {
DBG("timer [%p] not found for removing\n", timer);
}
return res;
}
void RegistrationTimer::run_timers() {
std::list<RegTimer*> timers_tbf;
struct timeval now;
gettimeofday(&now, 0);
buckets_mut.lock();
// bucket over?
if (now.tv_sec > current_bucket_start + TIMER_BUCKET_LENGTH) {
timers_tbf.insert(timers_tbf.begin(),
buckets[current_bucket].timers.begin(),
buckets[current_bucket].timers.end());
buckets[current_bucket].timers.clear();
current_bucket++;
current_bucket %= TIMER_BUCKETS;
current_bucket_start += TIMER_BUCKET_LENGTH;
// DBG("turned bucket to %i\n", current_bucket);
}
// move timers from current_bucket
RegTimerBucket& bucket = buckets[current_bucket];
std::list<RegTimer*>::iterator it = bucket.timers.begin();
while (it != bucket.timers.end() &&
now.tv_sec > (*it)->expires) {
std::list<RegTimer*>::iterator c_it = it;
it++;
timers_tbf.push_back(*c_it);
bucket.timers.erase(c_it);
}
buckets_mut.unlock();
if (!timers_tbf.empty()) {
DBG("firing %zd timers\n", timers_tbf.size());
for (std::list<RegTimer*>::iterator it=timers_tbf.begin();
it != timers_tbf.end(); it++) {
fire_timer(*it);
}
}
}
void RegistrationTimer::run()
{
struct timeval now,next_tick,diff,tick;
_shutdown_finished = false;
tick.tv_sec = 0;
tick.tv_usec = TIMER_RESOLUTION;
gettimeofday(&now, NULL);
timeradd(&tick,&now,&next_tick);
_timer_thread_running = true;
while(_timer_thread_running){
gettimeofday(&now,NULL);
if(timercmp(&now,&next_tick,<)){
struct timespec sdiff,rem;
timersub(&next_tick, &now,&diff);
sdiff.tv_sec = diff.tv_sec;
sdiff.tv_nsec = diff.tv_usec * 1000;
if(sdiff.tv_nsec > 2000000) // 2 ms
nanosleep(&sdiff,&rem);
}
//else {
//printf("missed one tick\n");
//}
run_timers();
timeradd(&tick,&next_tick,&next_tick);
}
DBG("RegistrationTimer thread finishing.\n");
_shutdown_finished = true;
}
void RegistrationTimer::on_stop() {
}
bool RegistrationTimer::insert_timer_leastloaded(RegTimer* timer,
time_t from_time,
time_t to_time) {
buckets_mut.lock();
int from_index = get_bucket_index(from_time);
int to_index = get_bucket_index(to_time);
if (from_index < 0 && to_index < 0) {
ERROR("could not find timer bucket indices - "
"from_index = %d, to_index = %d, from_time = %ld, to_time %ld, "
"current_bucket_start = %ld\n",
from_index, to_index, from_time, to_time, current_bucket_start);
buckets_mut.unlock();
return false;
}
if (from_index < 0) {
// use now .. to_index
DBG("from_time (%ld) in the past - searching load loaded from now()\n", from_time);
from_index = current_bucket;
}
// find least loaded bucket
int res_index = from_index;
size_t least_load = buckets[from_index].timers.size();
int i = from_index;
while (i != to_index) {
if (buckets[i].timers.size() <= least_load) {
least_load = buckets[i].timers.size();
res_index = i;
}
i++;
i %= TIMER_BUCKETS;
}
DBG("found bucket %i with least load %zd (between %i and %i)\n",
res_index, least_load, from_index, to_index);
// update expires to some random value inside the selected bucket
int diff = (unsigned)res_index - current_bucket;
if ((unsigned)res_index < current_bucket) {
diff+=TIMER_BUCKETS;
}
timer->expires = current_bucket_start +
diff * TIMER_BUCKET_LENGTH + // bucket start
rand() % TIMER_BUCKET_LENGTH;
DBG("setting expires to %ld (between %ld and %ld)\n",
timer->expires, from_time, to_time);
place_timer(timer, res_index);
buckets_mut.unlock();
return false;
}

@ -1,115 +0,0 @@
/*
* Copyright (C) 2011 Stefan Sayer
*
* This file is part of SEMS, a free SIP media server.
*
* SEMS 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.
*
* For a license to use the sems software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* SEMS 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
*/
#ifndef _RegistrationTimer_h_
#define _RegistrationTimer_h_
#include <list>
#include <vector>
#include <sys/time.h>
#include "log.h"
#include "AmThread.h"
#define TIMER_BUCKET_LENGTH 10 // 10 sec
#define TIMER_BUCKETS 40000 // 40000 buckets (400000 sec, 111 hrs)
// 100 ms == 100000 us
#define TIMER_RESOLUTION 100000
class RegTimer;
typedef void (*timer_cb)(RegTimer*, long /*data1*/,int /*data2*/);
class RegTimerBucket;
class RegTimer {
public:
time_t expires;
timer_cb cb;
long data1;
int data2;
RegTimer()
: expires(0), cb(0), data1(0), data2(0) { }
};
class RegTimerBucket {
public:
std::list<RegTimer*> timers;
RegTimerBucket() { }
};
/**
Additionally to normal timer operation (setting and removing timer,
fire the timer when it is expired), this RegistrationTimer timers
class needs to support insert_timer_leastloaded() which should insert
the timer in some least loaded interval between from_time and to_time
in order to flatten out re-register spikes (due to restart etc).
Timer granularity is seconds.
Timers are saved in buckets of TIMER_BUCKET_LENGTH seconds. the buckets
array is a circular one, the current bucket starts from the time
current_bucket_start (in seconds as in time(2)).
The timer object is owned by the caller, and MUST be valid until it is
fired or removed.
*/
class RegistrationTimer
: public AmThread
{
time_t current_bucket_start;
// every bucket contains TIMER_BUCKET_LENGTH seconds of timers
RegTimerBucket buckets[TIMER_BUCKETS];
unsigned int current_bucket;
AmMutex buckets_mut;
int get_bucket_index(time_t tv);
void place_timer(RegTimer* timer, int bucket_index);
void fire_timer(RegTimer* timer);
void run_timers();
protected:
void run();
void on_stop();
public:
bool insert_timer(RegTimer* timer);
bool remove_timer(RegTimer* timer);
bool insert_timer_leastloaded(RegTimer* timer,
time_t from_time,
time_t to_time);
RegistrationTimer();
bool _timer_thread_running;
bool _shutdown_finished;
};
#endif

@ -1,99 +0,0 @@
# Database connection
mysql_user=root
mysql_passwd=mypass123
# mysql_server, default: localhost
# mysql_server=localhost
#mysql_db, default: sems
# mysql_db=sems
# table for registration status
# default: registrations
# registrations_table="registrations"
# query joining subscriber info with registration table
joined_query="select subscribers.subscriber_id as subscriber_id, subscribers.user as user, subscribers.pass as pass, subscribers.realm as realm, registrations.registration_status as registration_status, registrations.expiry as expiry, registrations.last_registration as last_registration from subscribers left join registrations on subscribers.subscriber_id=registrations.subscriber_id;"
#example with contact:
# joined_query="select subscribers.subscriber_id as subscriber_id, subscribers.user as user, subscribers.pass as pass, subscribers.realm as realm, subscribers.contact as contact, registrations.registration_status as registration_status, registrations.expiry as expiry, registrations.last_registration as last_registration from subscribers left join registrations on subscribers.subscriber_id=registrations.subscriber_id;"
# outbound_proxy=<sip_uri>
# set the outbound proxy to send registrations through
#
#outbound_proxy=sip:10.0.0.2:5070
# contact_hostport=<host:port> - overriding contact host:port
# sets the contact host:port portion that is registered
# default: empty
#contact_hostport=10.0.0.50:5080
# db_read_contact=[yes, no] : read individual contact to register from DB?
# note: joined_query must select the contact, too
# default: no
#db_read_contact=yes
#save_contacts=[yes, no] : save contacts?
# for monitoring and troubleshooting, all contacts returned with a positive reply
# may be saved to DB in the registrations.contacts colums
# default: yes
#save_contacts=yes
#save_auth_replies=[yes, no] : update status on 401 replies (auth)?
# usually, 401 replies which are resent with auth are not saved. if save_auth_replies=yes,
# last_code and last_reason in DB is updated with the 401
#save_auth_replies=yes
# expires: desired expires, i.e. expires value that is requested
# default: 7200
# expires=300
# error_retry_interval
#
# default: 300
# reregister_interval: fraction of actual expires after which register is refreshed
# default: reregister_interval=0.5
#reregister_interval=0.5
# minimum_reregister_interval: if set, re-register is scheduled in least loaded time
# in minimum_reregister_interval .. reregister_interval i order to smooth load spikes
# must be smaller than reregister_interval
# default: off
#
# example:
# reregister_interval=0.5
# minimum_reregister_interval=0.4
# on a registration expiring in 3600s, the least loaded spot between 1440s and 1800s
# is chosen
#
#minimum_reregister_interval=0.4
# enable_ratelimiting=yes : Enable ratelimiting?
# default: no
# if enabled, the amount of initial REGISTER requests is limited (not counting re-trans-
# missions and requests re-sent for authentication)
#enable_ratelimiting=yes
# ratelimit_rate=300 : rate of initial REGISTER requests to send as maximum
#ratelimit_rate=2
# ratelimit_per=1 : per time unit (in seconds, e.g. 300 REGISTER in 1 second)
#ratelimit_per=1
#ratelimit_slowstart=[yes|no] : on startup send initially ratelimit_rate or 0 requests
#default: no
#ratelimit_slowstart=yes
# delete_removed_registrations=yes : delete removed registrations from registrations
# table in DB? (otherwise they will stay with STATUS_REMOVED)
# default: yes
#
#delete_removed_registrations=no
# delete_failed_deregistrations=yes : delete failed de-registrations from registrations
# table in DB? (otherwise they will stay with STATUS_TO_BE_REMOVED)
# only applicable if delete_removed_registrations=yes
# default: no
#
#delete_failed_deregistrations=no

@ -29,9 +29,6 @@
#include "AmUtils.h"
#include "AmMediaProcessor.h"
#include "DSM.h"
#include "AmConferenceStatus.h"
#include "AmAdvancedAudio.h"
#include "AmSipSubscription.h"
#include "../apps/jsonrpc/JsonRPCEvents.h" // todo!
@ -96,7 +93,6 @@ void DSMCall::onInvite(const AmSipRequest& req) {
bool run_session_invite = engine.onInvite(req, this);
if (run_invite_event) {
avar[DSM_AVAR_REQUEST] = AmArg(&req);
if (!engine.init(this, this, startDiagName, DSMCondition::Invite))
run_session_invite =false;
@ -104,7 +100,7 @@ void DSMCall::onInvite(const AmSipRequest& req) {
DBG("session choose to not connect media\n");
run_session_invite = false; // don't accept audio
}
avar.erase(DSM_AVAR_REQUEST);
}
if (run_session_invite)
@ -387,14 +383,6 @@ void DSMCall::process(AmEvent* event)
engine.runEvent(this, this, DSMCondition::PlaylistSeparator, &params);
}
ConferenceEvent * conf_ev = dynamic_cast<ConferenceEvent*>(event);
if (conf_ev) {
map<string, string> params;
params["type"] = "conference_event";
params["id"] = int2str(conf_ev->event_id);
engine.runEvent(this, this, DSMCondition::DSMEvent, &params);
}
// todo: give modules the possibility to define/process events
JsonRpcEvent* jsonrpc_ev = dynamic_cast<JsonRpcEvent*>(event);
if (jsonrpc_ev) {
@ -451,33 +439,6 @@ void DSMCall::process(AmEvent* event)
}
if (event->event_id == E_SIP_SUBSCRIPTION) {
SIPSubscriptionEvent* sub_ev = dynamic_cast<SIPSubscriptionEvent*>(event);
if (sub_ev) {
DBG("DSM Call received SIP Subscription Event\n");
map<string, string> params;
params["status"] = sub_ev->getStatusText();
params["code"] = int2str(sub_ev->code);
params["reason"] = sub_ev->reason;
params["expires"] = int2str(sub_ev->expires);
params["has_body"] = (!sub_ev->notify_body.empty())?"true":"false";
if (!sub_ev->notify_body.empty()) {
avar[DSM_AVAR_SIPSUBSCRIPTION_BODY] = AmArg(sub_ev->notify_body);
}
engine.runEvent(this, this, DSMCondition::SIPSubscription, &params);
avar.erase(DSM_AVAR_SIPSUBSCRIPTION_BODY);
}
}
AmRtpTimeoutEvent* timeout_ev = dynamic_cast<AmRtpTimeoutEvent*>(event);
if (timeout_ev) {
map<string, string> params;
params["type"] = "rtp_timeout";
params["timeout_value"] = int2str(AmConfig::DeadRtpTime);
engine.runEvent(this, this, DSMCondition::RTPTimeout, &params);
return;
}
AmB2BCallerSession::process(event);
}
@ -485,13 +446,13 @@ inline UACAuthCred* DSMCall::getCredentials() {
return cred.get();
}
void DSMCall::playPrompt(const string& name, bool loop, bool front) {
void DSMCall::playPrompt(const string& name, bool loop) {
DBG("playing prompt '%s'\n", name.c_str());
if (prompts->addToPlaylist(name, (long)this, playlist,
front, loop)) {
/*front =*/ false, loop)) {
if ((var["prompts.default_fallback"] != "yes") ||
default_prompts->addToPlaylist(name, (long)this, playlist,
front, loop)) {
/*front =*/ false, loop)) {
DBG("checked [%p]\n", default_prompts);
throw DSMException("prompt", "name", name);
} else {
@ -508,17 +469,9 @@ void DSMCall::closePlaylist(bool notify) {
playlist.close(notify);
}
void DSMCall::flushPlaylist() {
DBG("flush playlist\n");
playlist.close(false);
}
void DSMCall::addToPlaylist(AmPlaylistItem* item, bool front) {
void DSMCall::addToPlaylist(AmPlaylistItem* item) {
DBG("add item to playlist\n");
if (front)
playlist.addToPlayListFront(item);
else
playlist.addToPlaylist(item);
playlist.addToPlaylist(item);
}
void DSMCall::playFile(const string& name, bool loop, bool front) {
@ -544,18 +497,6 @@ void DSMCall::playFile(const string& name, bool loop, bool front) {
CLR_ERRNO;
}
void DSMCall::playSilence(unsigned int length, bool front) {
AmNullAudio* af = new AmNullAudio();
af->setReadLength(length);
if (front)
playlist.addToPlayListFront(new AmPlaylistItem(af, NULL));
else
playlist.addToPlaylist(new AmPlaylistItem(af, NULL));
audiofiles.push_back(af);
CLR_ERRNO;
}
void DSMCall::recordFile(const string& name) {
if (rec_file)
stopRecord();
@ -678,14 +619,10 @@ void DSMCall::addSeparator(const string& name, bool front) {
}
void DSMCall::transferOwnership(DSMDisposable* d) {
if (d == NULL)
return;
gc_trash.insert(d);
}
void DSMCall::releaseOwnership(DSMDisposable* d) {
if (d == NULL)
return;
gc_trash.erase(d);
}

@ -97,12 +97,10 @@ public:
void setPromptSets(map<string, AmPromptCollection*>& new_prompt_sets);
// DSMSession interface
void playPrompt(const string& name, bool loop = false, bool front = false);
void playPrompt(const string& name, bool loop = false);
void closePlaylist(bool notify);
void flushPlaylist();
void addToPlaylist(AmPlaylistItem* item, bool front = false);
void addToPlaylist(AmPlaylistItem* item);
void playFile(const string& name, bool loop, bool front=false);
void playSilence(unsigned int length, bool front=false);
void recordFile(const string& name);
unsigned int getRecordLength();
unsigned int getRecordDataSize();

@ -55,12 +55,9 @@ DSMAction* DSMCoreModule::getAction(const string& from_str) {
DEF_CMD("stop", SCStopAction);
DEF_CMD("playPrompt", SCPlayPromptAction);
DEF_CMD("playPromptFront", SCPlayPromptFrontAction);
DEF_CMD("playPromptLooped", SCPlayPromptLoopedAction);
DEF_CMD("playFile", SCPlayFileAction);
DEF_CMD("playFileFront", SCPlayFileFrontAction);
DEF_CMD("playSilence", SCPlaySilenceAction);
DEF_CMD("playSilenceFront", SCPlaySilenceFrontAction);
DEF_CMD("recordFile", SCRecordFileAction);
DEF_CMD("stopRecord", SCStopRecordAction);
DEF_CMD("getRecordLength", SCGetRecordLengthAction);
@ -70,11 +67,6 @@ DSMAction* DSMCoreModule::getAction(const string& from_str) {
DEF_CMD("addSeparator", SCAddSeparatorAction);
DEF_CMD("connectMedia", SCConnectMediaAction);
DEF_CMD("disconnectMedia", SCDisconnectMediaAction);
DEF_CMD("enableReceiving", SCEnableReceivingAction);
DEF_CMD("disableReceiving", SCDisableReceivingAction);
DEF_CMD("enableForceDTMFReceiving", SCEnableForceDTMFReceiving);
DEF_CMD("disableForceDTMFReceiving", SCDisableForceDTMFReceiving);
DEF_CMD("monitorRTPTimeout", SCMonitorRTPTimeoutAction);
DEF_CMD("mute", SCMuteAction);
DEF_CMD("unmute", SCUnmuteAction);
DEF_CMD("enableDTMFDetection", SCEnableDTMFDetection);
@ -131,10 +123,6 @@ DSMAction* DSMCoreModule::getAction(const string& from_str) {
DEF_CMD("B2B.clearHeaders", SCB2BClearHeadersAction);
DEF_CMD("B2B.setHeaders", SCB2BSetHeadersAction);
DEF_CMD("trackObject", SCTrackObjectAction);
DEF_CMD("releaseObject", SCReleaseObjectAction);
DEF_CMD("freeObject", SCFreeObjectAction);
return NULL;
}
@ -205,9 +193,6 @@ DSMCondition* DSMCoreModule::getCondition(const string& from_str) {
if (cmd == "jsonRpcResponse")
return new TestDSMCondition(params, DSMCondition::JsonRpcResponse);
if (cmd == "subscription")
return new TestDSMCondition(params, DSMCondition::SIPSubscription);
if (cmd == "startup")
return new TestDSMCondition(params, DSMCondition::Startup);
@ -217,9 +202,6 @@ DSMCondition* DSMCoreModule::getCondition(const string& from_str) {
if (cmd == "system")
return new TestDSMCondition(params, DSMCondition::System);
if (cmd == "rtpTimeout")
return new TestDSMCondition(params, DSMCondition::RTPTimeout);
return NULL;
}
@ -227,10 +209,6 @@ EXEC_ACTION_START(SCPlayPromptAction) {
sc_sess->playPrompt(resolveVars(arg, sess, sc_sess, event_params));
} EXEC_ACTION_END;
EXEC_ACTION_START(SCPlayPromptFrontAction) {
sc_sess->playPrompt(resolveVars(arg, sess, sc_sess, event_params), false, true);
} EXEC_ACTION_END;
EXEC_ACTION_START(SCSetPromptsAction) {
sc_sess->setPromptSet(resolveVars(arg, sess, sc_sess, event_params));
} EXEC_ACTION_END;
@ -306,24 +284,6 @@ EXEC_ACTION_START(SCPlayFileFrontAction) {
loop, true);
} EXEC_ACTION_END;
EXEC_ACTION_START(SCPlaySilenceAction) {
int length;
string length_str = resolveVars(arg, sess, sc_sess, event_params);
if (!str2int(length_str, length)) {
throw DSMException("core", "cause", "cannot parse number");
}
sc_sess->playSilence(length);
} EXEC_ACTION_END;
EXEC_ACTION_START(SCPlaySilenceFrontAction) {
int length;
string length_str = resolveVars(arg, sess, sc_sess, event_params);
if (!str2int(length_str, length)) {
throw DSMException("core", "cause", "cannot parse number");
}
sc_sess->playSilence(length, true);
} EXEC_ACTION_END;
EXEC_ACTION_START(SCRecordFileAction) {
sc_sess->recordFile(resolveVars(arg, sess, sc_sess, event_params));
} EXEC_ACTION_END;
@ -365,32 +325,6 @@ EXEC_ACTION_START(SCDisconnectMediaAction) {
sc_sess->disconnectMedia();
} EXEC_ACTION_END;
EXEC_ACTION_START(SCEnableReceivingAction) {
DBG("enabling RTP receving in session\nb");
sess->setReceiving(true);
} EXEC_ACTION_END;
EXEC_ACTION_START(SCDisableReceivingAction) {
DBG("disabling RTP receving in session\nb");
sess->setReceiving(false);
} EXEC_ACTION_END;
EXEC_ACTION_START(SCEnableForceDTMFReceiving) {
DBG("enabling forced DTMF RTP receving in session\nb");
sess->setForceDtmfReceiving(true);
} EXEC_ACTION_END;
EXEC_ACTION_START(SCDisableForceDTMFReceiving) {
DBG("disabling forced DTMF RTP receving in session\nb");
sess->setForceDtmfReceiving(false);
} EXEC_ACTION_END;
EXEC_ACTION_START(SCMonitorRTPTimeoutAction) {
string e = resolveVars(arg, sess, sc_sess, event_params);
DBG("setting RTP stream to %smonitor RTP timeout\n", e=="true"?"":"not");
sess->RTPStream()->setMonitorRTPTimeout(e=="true");
} EXEC_ACTION_END;
EXEC_ACTION_START(SCMuteAction) {
sc_sess->mute();
} EXEC_ACTION_END;
@ -1399,49 +1333,3 @@ EXEC_ACTION_START(SCCreateSystemDSMAction) {
} EXEC_ACTION_END;
DSMDisposable* getObjectFromVariable(DSMSession* sc_sess, const string& var_name) {
AVarMapT::iterator it = sc_sess->avar.find(var_name);
if (it == sc_sess->avar.end()) {
DBG("object '%s' not found\n", var_name.c_str());
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
sc_sess->SET_STRERROR("object '"+var_name+"' not found\n");
return NULL;
}
DSMDisposable* disp = dynamic_cast<DSMDisposable*>(it->second.asObject());
if (NULL == disp) {
DBG("object '%s' is not a DSMDisposable\n", var_name.c_str());
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
sc_sess->SET_STRERROR("object '"+var_name+"' is not a DSMDisposable\n");
return NULL;
}
return disp;
}
EXEC_ACTION_START(SCTrackObjectAction) {
string var_name = resolveVars(arg, sess, sc_sess, event_params);
DSMDisposable* disp = getObjectFromVariable(sc_sess, var_name);
if (NULL == disp) {
EXEC_ACTION_STOP;
}
sc_sess->transferOwnership(disp);
} EXEC_ACTION_END;
EXEC_ACTION_START(SCReleaseObjectAction) {
string var_name = resolveVars(arg, sess, sc_sess, event_params);
DSMDisposable* disp = getObjectFromVariable(sc_sess, var_name);
if (NULL == disp) {
EXEC_ACTION_STOP;
}
sc_sess->releaseOwnership(disp);
} EXEC_ACTION_END;
EXEC_ACTION_START(SCFreeObjectAction) {
string var_name = resolveVars(arg, sess, sc_sess, event_params);
DSMDisposable* disp = getObjectFromVariable(sc_sess, var_name);
if (NULL == disp) {
EXEC_ACTION_STOP;
}
delete disp;
sc_sess->avar.erase(var_name);
} EXEC_ACTION_END;

@ -48,7 +48,6 @@ class DSMCoreModule
};
DEF_ACTION_1P(SCPlayPromptAction);
DEF_ACTION_1P(SCPlayPromptFrontAction);
DEF_ACTION_1P(SCPlayPromptLoopedAction);
DEF_ACTION_1P(SCRecordFileAction);
DEF_ACTION_1P(SCStopRecordAction);
@ -59,11 +58,6 @@ DEF_ACTION_1P(SCSetInOutPlaylistAction);
DEF_ACTION_1P(SCStopAction);
DEF_ACTION_1P(SCConnectMediaAction);
DEF_ACTION_1P(SCDisconnectMediaAction);
DEF_ACTION_1P(SCEnableReceivingAction);
DEF_ACTION_1P(SCDisableReceivingAction);
DEF_ACTION_1P(SCEnableForceDTMFReceiving);
DEF_ACTION_1P(SCDisableForceDTMFReceiving);
DEF_ACTION_1P(SCMonitorRTPTimeoutAction);
DEF_ACTION_1P(SCMuteAction);
DEF_ACTION_1P(SCUnmuteAction);
DEF_ACTION_1P(SCEnableDTMFDetection);
@ -71,7 +65,6 @@ DEF_ACTION_1P(SCDisableDTMFDetection);
DEF_ACTION_2P(SCSendDTMFAction);
DEF_ACTION_2P(SCSendDTMFSequenceAction);
DEF_ACTION_1P(SCSetPromptsAction);
DEF_ACTION_2P(SCAddSeparatorAction);
@ -105,8 +98,6 @@ DEF_ACTION_2P(SCGetParamAction);
DEF_ACTION_2P(SCSetVarAction);
DEF_ACTION_2P(SCPlayFileAction);
DEF_ACTION_2P(SCPlayFileFrontAction);
DEF_ACTION_1P(SCPlaySilenceAction);
DEF_ACTION_1P(SCPlaySilenceFrontAction);
DEF_ACTION_2P(SCPostEventAction);
DEF_ACTION_2P(SCB2BConnectCalleeAction);
@ -122,10 +113,6 @@ DEF_ACTION_1P(SCUnregisterEventQueueAction);
DEF_ACTION_2P(SCCreateSystemDSMAction);
DEF_ACTION_1P(SCTrackObjectAction);
DEF_ACTION_1P(SCReleaseObjectAction);
DEF_ACTION_1P(SCFreeObjectAction);
class SCDIAction
: public DSMAction {
vector<string> params;

@ -52,8 +52,6 @@ class DSMModule {
virtual int preload() { return 0; }
virtual bool onInvite(const AmSipRequest& req, DSMSession* sess) { return true; }
virtual void onBeforeDestroy(DSMSession* sc_sess, AmSession* sess) { }
virtual void processSdpOffer(AmSdp& offer) { }
virtual void processSdpAnswer(const AmSdp& offer, AmSdp& answer) { }
};
typedef map<string,string> EventParamT;
@ -132,7 +130,8 @@ class SCStrArgAction
/* bool xsplit(const string& arg, char sep, bool optional, string& par1, string& par2); */
#define SPLIT_ARGS(sep, optional) \
#define CONST_ACTION_2P(CL_name, sep, optional) \
CL_name::CL_name(const string& arg) { \
size_t p = 0; \
char last_c = ' '; \
bool quot=false; \
@ -195,14 +194,9 @@ class SCStrArgAction
ERROR("expected two parameters separated with '%c' in expression '%s' for %s\n", \
sep,arg.c_str(),typeid(this).name()); \
return; \
}
#define CONST_ACTION_2P(CL_name, _sep, _optional) \
CL_name::CL_name(const string& arg) { \
SPLIT_ARGS(_sep, _optional); \
}
} \
} \
#define EXEC_ACTION_START(act_name) \
bool act_name::execute(AmSession* sess, DSMSession* sc_sess, \
@ -247,24 +241,7 @@ void splitCmd(const string& from_str,
bool match(AmSession* sess, DSMSession* sc_sess, DSMCondition::EventType event, \
map<string,string>* event_params); \
};
#define DEF_CONDITION_2P(cond_name) \
class cond_name \
: public DSMCondition { \
string par1; \
string par2; \
bool inv; \
public: \
cond_name(const string& arg, bool inv); \
bool match(AmSession* sess, DSMSession* sc_sess, DSMCondition::EventType event, \
map<string,string>* event_params); \
};
#define CONST_CONDITION_2P(cond_name, _sep, _optional) \
cond_name::cond_name(const string& arg, bool inv) \
: inv(inv) { \
SPLIT_ARGS(_sep, _optional); \
}
#define MATCH_CONDITION_START(cond_clsname) \
bool cond_clsname::match(AmSession* sess, DSMSession* sc_sess, \

@ -66,8 +66,6 @@ using std::map;
#define DSM_AVAR_JSONRPCRESPONSEDATA "JsonRpcResponseParameters"
#define DSM_AVAR_JSONRPCRESPONSEUDATA "JsonRpcResponseUData"
#define DSM_AVAR_SIPSUBSCRIPTION_BODY "SipSubscriptionBody"
#define DSM_ERRNO_FILE "file"
#define DSM_ERRNO_UNKNOWN_ARG "arg"
#define DSM_ERRNO_SCRIPT "script"
@ -102,9 +100,8 @@ class DSMSession {
DSMSession();
virtual ~DSMSession();
virtual void playPrompt(const string& name, bool loop = false, bool front = false) = 0;
virtual void playPrompt(const string& name, bool loop = false) = 0;
virtual void playFile(const string& name, bool loop, bool front = false) = 0;
virtual void playSilence(unsigned int length, bool front = false) = 0;
virtual void recordFile(const string& name) = 0;
virtual unsigned int getRecordLength() = 0;
virtual unsigned int getRecordDataSize() = 0;
@ -113,9 +110,8 @@ class DSMSession {
virtual void setInputPlaylist() = 0;
virtual void setOutputPlaylist() = 0;
virtual void addToPlaylist(AmPlaylistItem* item, bool front = false) = 0;
virtual void addToPlaylist(AmPlaylistItem* item) = 0;
virtual void closePlaylist(bool notify) = 0;
virtual void flushPlaylist() = 0;
virtual void setPromptSet(const string& name) = 0;
virtual void addSeparator(const string& name, bool front = false) = 0;
virtual void connectMedia() = 0;

@ -224,18 +224,6 @@ void DSMStateEngine::onBeforeDestroy(DSMSession* sc_sess, AmSession* sess) {
(*it)->onBeforeDestroy(sc_sess, sess);
}
void DSMStateEngine::processSdpOffer(AmSdp& offer) {
for (vector<DSMModule*>::iterator it =
mods.begin(); it != mods.end(); it++)
(*it)->processSdpOffer(offer);
}
void DSMStateEngine::processSdpAnswer(const AmSdp& offer, AmSdp& answer) {
for (vector<DSMModule*>::iterator it =
mods.begin(); it != mods.end(); it++)
(*it)->processSdpAnswer(offer, answer);
}
bool DSMStateEngine::runactions(vector<DSMElement*>::iterator from,
vector<DSMElement*>::iterator to,
AmSession* sess, DSMSession* sc_sess, DSMCondition::EventType event,

@ -30,7 +30,6 @@
#include "DSMElemContainer.h"
#include "AmSipMsg.h"
#include "AmArg.h"
#include "AmSdp.h"
class AmSession;
class DSMSession;
@ -91,10 +90,7 @@ class DSMCondition
Startup,
Reload,
System,
RTPTimeout,
SIPSubscription
System
};
bool invert;
@ -288,9 +284,6 @@ class DSMStateEngine {
/** @return whether call should be accepted */
bool onInvite(const AmSipRequest& req, DSMSession* sess);
void onBeforeDestroy(DSMSession* sc_sess, AmSession* sess);
void processSdpOffer(AmSdp& offer);
void processSdpAnswer(const AmSdp& offer, AmSdp& answer);
};
extern void varPrintArg(const AmArg& a, map<string, string>& dst, const string& name);

@ -6,7 +6,6 @@
#include "DSMStateDiagramCollection.h"
#include "../apps/jsonrpc/JsonRPCEvents.h" // todo!
#include "AmSipSubscription.h"
SystemDSM::SystemDSM(const DSMScriptConfig& config,
const string& startDiagName,
@ -128,28 +127,10 @@ void SystemDSM::process(AmEvent* event) {
}
if (event->event_id == E_SIP_SUBSCRIPTION) {
SIPSubscriptionEvent* sub_ev = dynamic_cast<SIPSubscriptionEvent*>(event);
if (sub_ev) {
DBG("SystemDSM received SIP Subscription Event\n");
map<string, string> params;
params["status"] = sub_ev->getStatusText();
params["code"] = int2str(sub_ev->code);
params["reason"] = sub_ev->reason;
params["expires"] = int2str(sub_ev->expires);
params["has_body"] = (!sub_ev->notify_body.empty())?"true":"false";
if (!sub_ev->notify_body.empty()) {
avar[DSM_AVAR_SIPSUBSCRIPTION_BODY] = AmArg(sub_ev->notify_body);
}
engine.runEvent(&dummy_session, this, DSMCondition::SIPSubscription, &params);
avar.erase(DSM_AVAR_SIPSUBSCRIPTION_BODY);
}
}
if (event->event_id == E_SYSTEM) {
AmSystemEvent* sys_ev = dynamic_cast<AmSystemEvent*>(event);
if(sys_ev){
DBG("SystemDSM received system Event\n");
DBG("SystemDSM received system Event\n");
map<string, string> params;
params["type"] = AmSystemEvent::getDescription(sys_ev->sys_event);
engine.runEvent(&dummy_session, this, DSMCondition::System, &params);
@ -170,9 +151,8 @@ void SystemDSM::_func { \
throw DSMException("core", "cause", "not implemented"); \
}
NOT_IMPLEMENTED(playPrompt(const string& name, bool loop, bool front));
NOT_IMPLEMENTED(playPrompt(const string& name, bool loop));
NOT_IMPLEMENTED(playFile(const string& name, bool loop, bool front));
NOT_IMPLEMENTED(playSilence(unsigned int length, bool front));
NOT_IMPLEMENTED(recordFile(const string& name));
NOT_IMPLEMENTED_UINT(getRecordLength());
NOT_IMPLEMENTED_UINT(getRecordDataSize());
@ -181,8 +161,7 @@ NOT_IMPLEMENTED(setInOutPlaylist());
NOT_IMPLEMENTED(setInputPlaylist());
NOT_IMPLEMENTED(setOutputPlaylist());
NOT_IMPLEMENTED(addToPlaylist(AmPlaylistItem* item, bool front));
NOT_IMPLEMENTED(flushPlaylist());
NOT_IMPLEMENTED(addToPlaylist(AmPlaylistItem* item));
NOT_IMPLEMENTED(closePlaylist(bool notify));
NOT_IMPLEMENTED(setPromptSet(const string& name));
NOT_IMPLEMENTED(addSeparator(const string& name, bool front));

@ -52,9 +52,8 @@ class SystemDSM
void process(AmEvent* event);
// DSMSession interface
void playPrompt(const string& name, bool loop = false, bool front = false);
void playPrompt(const string& name, bool loop = false);
void playFile(const string& name, bool loop, bool front = false);
void playSilence(unsigned int length, bool front = false);
void recordFile(const string& name);
unsigned int getRecordLength();
unsigned int getRecordDataSize();
@ -63,9 +62,8 @@ class SystemDSM
void setInputPlaylist();
void setOutputPlaylist();
void addToPlaylist(AmPlaylistItem* item, bool front = false);
void addToPlaylist(AmPlaylistItem* item);
void closePlaylist(bool notify);
void flushPlaylist();
void setPromptSet(const string& name);
void addSeparator(const string& name, bool front = false);
void connectMedia();

@ -4,7 +4,7 @@ COREPATH = $(DSMPATH)/../../core
include $(COREPATH)/../Makefile.defs
exclude_dsm_modules ?= mod_mysql mod_aws mod_curl mod_xml
exclude_dsm_modules ?= mod_mysql mod_aws
ifneq ($(USE_MONITORING), yes)
exclude_dsm_modules += mod_monitoring
@ -41,7 +41,7 @@ modules:
done
.PHONY: install
install: install-bin
install: install-bin install-cfg
.PHONY: install-bin
install-bin:

@ -1,14 +0,0 @@
plug_in_name = mod_curl
DSMPATH ?= ../..
module_ldflags = `curl-config --libs`
module_cflags = -DMOD_NAME=\"$(plug_in_name)\" -I$(DSMPATH) `curl-config --cflags`
# for local libcurl:
#module_cflags = -DMOD_NAME=\"$(plug_in_name)\" -I$(DSMPATH) -Icurl-7.15.5/include
#module_ldflags = curl-7.15.5/lib/.libs/libcurl.a
COREPATH ?=$(DSMPATH)/../../core
lib_full_name = $(DSMPATH)/mods/lib/$(lib_name)
include $(DSMPATH)/mods/Makefile.dsm_module

@ -1,415 +0,0 @@
/*
* Copyright (C) 2009 TelTech Systems Inc.
* Copyright (C) 2011 Stefan Sayer
*
* This file is part of SEMS, a free SIP media server.
*
* sems 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
*
* For a license to use the SEMS software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* SEMS 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 "ModCurl.h"
#include "log.h"
#include "AmUtils.h"
#include "DSMSession.h"
#include "AmSession.h"
#include <curl/curl.h>
#include <sstream>
#include "AmConfigReader.h"
bool CurlModule::curl_initialized = false;
SC_EXPORT(CurlModule);
CurlModule::CurlModule() {
if (!curl_initialized) {
curl_initialized = true;
if (curl_global_init(CURL_GLOBAL_ALL)) {
ERROR("Initializing libcurl\n");
throw string("Initializing libcurl");
}
curl_version_info_data *data = curl_version_info(CURLVERSION_NOW);
if (data && data->version >=0) {
DBG("using libcurl version '%s'\n",
data->version);
if (data->features & CURL_VERSION_SSL) {
DBG("libcurl with SSL version '%s'\n", data->ssl_version);
} else {
DBG("libcurl without SSL support\n");
}
}
}
}
CurlModule::~CurlModule() {
}
DSMAction* CurlModule::getAction(const string& from_str) {
string cmd;
string params;
splitCmd(from_str, cmd, params);
DEF_CMD("curl.get", SCJCurlGetAction);
DEF_CMD("curl.getDiscardResult", SCJCurlGetNoresultAction);
DEF_CMD("curl.getFile", SCJCurlGetFileAction);
DEF_CMD("curl.getForm", SCJCurlGetFormAction);
DEF_CMD("curl.post", SCJCurlPOSTGetResultAction);
DEF_CMD("curl.postDiscardResult", SCJCurlPOSTAction);
return NULL;
}
DSMCondition* CurlModule::getCondition(const string& from_str) {
return NULL;
}
size_t debug_output_func(void *ptr, size_t size, size_t
nmemb, void *stream) {
string data((char*)ptr, size*nmemb);
DBG("server out: <<%s>>\n", data.c_str());
return size*nmemb;
}
/** append output to $curl.out */
size_t var_output_func(void *ptr, size_t size, size_t
nmemb, void *stream) {
if (NULL == stream)
return size*nmemb;
string data((char*)ptr, size*nmemb);
DBG("server out: <<%s>>\n", data.c_str());
DSMSession* sc_sess = reinterpret_cast<DSMSession*>(stream);
if (sc_sess) {
sc_sess->var["curl.out"]+=data;
}
return size*nmemb;
}
bool curl_run_get(DSMSession* sc_sess, const string& url,
bool get_result) {
CURL* m_curl_handle = curl_easy_init();
if (!m_curl_handle) {
ERROR("getting curl handle\n");
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
return false;
}
char* enc_url = curl_easy_escape(m_curl_handle, url.c_str(), url.length());
if (NULL == enc_url) {
ERROR("URL-encoding url '%s'\n", url.c_str());
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
curl_easy_cleanup(m_curl_handle);
return false;
}
if (curl_easy_setopt(m_curl_handle, CURLOPT_URL, url.c_str())
!= CURLE_OK) {
ERROR("setting URL '%s'\n", url.c_str());
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
curl_easy_cleanup(m_curl_handle);
free(enc_url);
return false;
}
if (!sc_sess->var["curl.timeout"].empty()) {
unsigned int curl_timeout = 0;
if (str2i(sc_sess->var["curl.timeout"], curl_timeout)) {
WARN("curl.timeout '%s' not understood\n", sc_sess->var["curl.timeout"].c_str());
} else {
if ((curl_easy_setopt(m_curl_handle, CURLOPT_TIMEOUT, curl_timeout) != CURLE_OK) ||
(curl_easy_setopt(m_curl_handle, CURLOPT_NOSIGNAL, 1L) != CURLE_OK)) {
ERROR("setting timeout '%u'\n", curl_timeout);
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
curl_easy_cleanup(m_curl_handle);
free(enc_url);
return false;
}
}
}
if (!get_result) {
if (curl_easy_setopt(m_curl_handle, CURLOPT_WRITEFUNCTION, debug_output_func)
!= CURLE_OK) {
ERROR("setting curl write function\n");
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
curl_easy_cleanup(m_curl_handle);
free(enc_url);
return false;
}
} else {
if ((curl_easy_setopt(m_curl_handle, CURLOPT_WRITEFUNCTION, var_output_func)
!= CURLE_OK)||
(curl_easy_setopt(m_curl_handle, CURLOPT_WRITEDATA, sc_sess)
!= CURLE_OK)) {
ERROR("setting curl write function\n");
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
curl_easy_cleanup(m_curl_handle);
free(enc_url);
return false;
}
}
char curl_err[CURL_ERROR_SIZE];
curl_err[0]='\0';
if (curl_easy_setopt(m_curl_handle, CURLOPT_ERRORBUFFER, curl_err)
!= CURLE_OK) {
ERROR("setting curl error buffer\n");
sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL);
curl_easy_cleanup(m_curl_handle);
free(enc_url);
return false;
}
CURLcode rescode = curl_easy_perform(m_curl_handle);
if (rescode) {
DBG("Error while trying to retrieve '%s': '%s'\n",
url.c_str(), curl_err);
sc_sess->var["curl.err"] = string(curl_err);
sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL);
} else {
sc_sess->SET_ERRNO(DSM_ERRNO_OK);
}
curl_easy_cleanup(m_curl_handle);
free(enc_url);
return false;
}
EXEC_ACTION_START(SCJCurlGetAction) {
sc_sess->var.erase("curl.out");
return curl_run_get(sc_sess, resolveVars(arg, sess, sc_sess, event_params), true);
} EXEC_ACTION_END;
EXEC_ACTION_START(SCJCurlGetNoresultAction) {
return curl_run_get(sc_sess, resolveVars(arg, sess, sc_sess, event_params), false);
} EXEC_ACTION_END;
CONST_ACTION_2P(SCJCurlGetFormAction, ',', true);
EXEC_ACTION_START(SCJCurlGetFormAction) {
sc_sess->var.erase("curl.out");
string form_url = resolveVars(par1, sess, sc_sess, event_params);;
bool url_has_qmark = form_url.find('?')!=string::npos;
vector<string> p_vars=explode(par2, ";");
for (vector<string>::iterator it=
p_vars.begin();it != p_vars.end();it++) {
string varname = (it->size() && ((*it)[0]=='$')) ? (it->substr(1)) : (*it);
DBG("adding '%s' = '%s'\n", varname.c_str(), sc_sess->var[varname].c_str());
if (!url_has_qmark && it == p_vars.begin())
form_url+= "?";
else
form_url+= "&";
form_url += varname + "=" + sc_sess->var[varname];
}
return curl_run_get(sc_sess, form_url, true);
} EXEC_ACTION_END;
void curl_run_getfile(DSMSession* sc_sess, const string& url, const string& outfile) {
CURL* m_curl_handle = curl_easy_init();
if (!m_curl_handle) {
ERROR("getting curl handle\n");
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
return;
}
if (curl_easy_setopt(m_curl_handle, CURLOPT_URL, url.c_str())
!= CURLE_OK) {
ERROR("setting URL '%s'\n", url.c_str());
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
curl_easy_cleanup(m_curl_handle);
return;
}
if (!sc_sess->var["curl.timeout"].empty()) {
unsigned int curl_timeout = 0;
if (str2i(sc_sess->var["curl.timeout"], curl_timeout)) {
WARN("curl.timeout '%s' not understood\n", sc_sess->var["curl.timeout"].c_str());
} else {
if ((curl_easy_setopt(m_curl_handle, CURLOPT_TIMEOUT, curl_timeout) != CURLE_OK) ||
(curl_easy_setopt(m_curl_handle, CURLOPT_NOSIGNAL, 1L) != CURLE_OK)) {
ERROR("setting timeout '%u'\n", curl_timeout);
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
curl_easy_cleanup(m_curl_handle);
return;
}
}
}
FILE* f = fopen(outfile.c_str(), "wb");
if (NULL == f) {
DBG("Error opening file '%s' for writing\n", outfile.c_str());
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
return;
}
if (curl_easy_setopt(m_curl_handle, CURLOPT_WRITEDATA, f)
!= CURLE_OK) {
ERROR("setting curl data file\n");
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
fclose(f);
return;
}
char curl_err[CURL_ERROR_SIZE];
curl_err[0]='\0';
if (curl_easy_setopt(m_curl_handle, CURLOPT_ERRORBUFFER, curl_err)
!= CURLE_OK) {
ERROR("setting URL '%s'\n", url.c_str());
sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL);
fclose(f);
return;
}
CURLcode rescode = curl_easy_perform(m_curl_handle);
if (rescode) {
DBG("Error while trying to retrieve '%s' to '%s': '%s'\n",
url.c_str(), outfile.c_str(), curl_err);
sc_sess->var["curl.err"] = string(curl_err);
sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL);
}else {
sc_sess->SET_ERRNO(DSM_ERRNO_OK);
}
fclose(f);
curl_easy_cleanup(m_curl_handle);
}
CONST_ACTION_2P(SCJCurlGetFileAction, ',', true);
EXEC_ACTION_START(SCJCurlGetFileAction) {
curl_run_getfile(sc_sess,
resolveVars(par1, sess, sc_sess, event_params),
resolveVars(par2, sess, sc_sess, event_params));
} EXEC_ACTION_END;
bool curl_run_post(DSMSession* sc_sess, const string& par1, const string& par2,
bool get_result) {
CURL* m_curl_handle = curl_easy_init();
if (!m_curl_handle) {
ERROR("getting curl handle\n");
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
return false;
}
if (curl_easy_setopt(m_curl_handle, CURLOPT_URL, par1.c_str())
!= CURLE_OK) {
ERROR("setting URL '%s'\n", par1.c_str());
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
curl_easy_cleanup(m_curl_handle);
return false;
}
if (!sc_sess->var["curl.timeout"].empty()) {
unsigned int curl_timeout = 0;
if (str2i(sc_sess->var["curl.timeout"], curl_timeout)) {
WARN("curl.timeout '%s' not understood\n", sc_sess->var["curl.timeout"].c_str());
} else {
if ((curl_easy_setopt(m_curl_handle, CURLOPT_TIMEOUT, curl_timeout) != CURLE_OK) ||
(curl_easy_setopt(m_curl_handle, CURLOPT_NOSIGNAL, 1L) != CURLE_OK)) {
ERROR("setting timeout '%u'\n", curl_timeout);
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
curl_easy_cleanup(m_curl_handle);
return false;
}
}
}
if (!get_result) {
if (curl_easy_setopt(m_curl_handle, CURLOPT_WRITEFUNCTION, debug_output_func)
!= CURLE_OK) {
ERROR("setting curl write function\n");
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
curl_easy_cleanup(m_curl_handle);
return false;
}
} else {
if ((curl_easy_setopt(m_curl_handle, CURLOPT_WRITEFUNCTION, var_output_func)
!= CURLE_OK)||
(curl_easy_setopt(m_curl_handle, CURLOPT_WRITEDATA, sc_sess)
!= CURLE_OK)) {
ERROR("setting curl write function\n");
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
curl_easy_cleanup(m_curl_handle);
return false;
}
}
char curl_err[CURL_ERROR_SIZE];
curl_err[0]='\0';
if (curl_easy_setopt(m_curl_handle, CURLOPT_ERRORBUFFER, curl_err)
!= CURLE_OK) {
ERROR("setting curl error buffer\n");
sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL);
curl_easy_cleanup(m_curl_handle);
return false;
}
struct curl_httppost *post=NULL;
struct curl_httppost *last=NULL;
string post_vars;
vector<string> p_vars=explode(par2, ";");
for (vector<string>::iterator it=
p_vars.begin();it != p_vars.end();it++) {
string varname = (it->size() && ((*it)[0]=='$')) ? (it->substr(1)) : (*it);
DBG("adding '%s' = '%s'\n", varname.c_str(), sc_sess->var[varname].c_str());
curl_formadd(&post, &last,
CURLFORM_COPYNAME, varname.c_str(),
CURLFORM_COPYCONTENTS, sc_sess->var[varname].c_str(), CURLFORM_END);
}
CURLcode rescode = curl_easy_perform(m_curl_handle);
bool res = false;
if (rescode) {
DBG("Error while trying to POST to '%s': '%s'\n",
par1.c_str(), curl_err);
sc_sess->var["curl.err"] = string(curl_err);
sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL);
} else {
sc_sess->SET_ERRNO(DSM_ERRNO_OK);
res = true;
}
curl_formfree(post);
curl_easy_cleanup(m_curl_handle);
return false;
}
CONST_ACTION_2P(SCJCurlPOSTAction, ',', true);
EXEC_ACTION_START(SCJCurlPOSTAction) {
curl_run_post(sc_sess, resolveVars(par1, sess, sc_sess, event_params),
par2, false);
return false;
} EXEC_ACTION_END;
CONST_ACTION_2P(SCJCurlPOSTGetResultAction, ',', true);
EXEC_ACTION_START(SCJCurlPOSTGetResultAction) {
sc_sess->var.erase("curl.out");
curl_run_post(sc_sess, resolveVars(par1, sess, sc_sess, event_params),
par2, true);
return false;
} EXEC_ACTION_END;

@ -1,52 +0,0 @@
/*
*
* Copyright (C) 2009 TelTech Systems
* Copyright (C) 2011 Stefan Sayer
*
* This file is part of SEMS, a free SIP media server.
*
* sems 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
*
* For a license to use the SEMS software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* SEMS 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
*/
#ifndef _MOD_CURL_H
#define _MOD_CURL_H
#include "DSMModule.h"
#include "DSMSession.h"
class CurlModule
: public DSMModule {
public:
CurlModule();
~CurlModule();
DSMAction* getAction(const string& from_str);
DSMCondition* getCondition(const string& from_str);
static bool curl_initialized;
};
DEF_ACTION_1P(SCJCurlGetAction);
DEF_ACTION_1P(SCJCurlGetNoresultAction);
DEF_ACTION_2P(SCJCurlGetFileAction);
DEF_ACTION_2P(SCJCurlGetFormAction);
DEF_ACTION_2P(SCJCurlPOSTAction);
DEF_ACTION_2P(SCJCurlPOSTGetResultAction);
#endif

@ -60,23 +60,6 @@ bool DLGModule::onInvite(const AmSipRequest& req, DSMSession* sess) {
return true;
}
string replaceLineEnds(string input)
{
string result;
size_t last = 0;
size_t pos;
while ((pos = input.find("\\r\\n", last)) != string::npos) {
result += input.substr(last, pos-last);
result += "\r\n";
last = pos + 4;
}
if (!input.substr(last).empty()) {
result += input.substr(last);
result += "\r\n";
}
return result;
}
// todo: convert errors to exceptions
void replyRequest(DSMSession* sc_sess, AmSession* sess,
EventParamT* event_params,
@ -84,7 +67,6 @@ void replyRequest(DSMSession* sc_sess, AmSession* sess,
const AmSipRequest& req) {
string code = resolveVars(par1, sess, sc_sess, event_params);
string reason = resolveVars(par2, sess, sc_sess, event_params);
string hdrs = replaceLineEnds(resolveVars("$dlg.reply.hdrs", sess, sc_sess, event_params));
unsigned int code_i;
if (str2i(code, code_i)) {
ERROR("decoding reply code '%s'\n", code.c_str());
@ -92,9 +74,14 @@ void replyRequest(DSMSession* sc_sess, AmSession* sess,
return;
}
DBG("replying with %i %s, hdrs='%s'\n", code_i, reason.c_str(), hdrs.c_str());
if (!sc_sess->last_req.get()) {
ERROR("no last request to reply\n");
sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL);
sc_sess->SET_STRERROR("no last request to reply");
return;
}
if (sess->dlg.reply(req, code_i, reason, "","", hdrs)) {
if (sess->dlg.reply(req, code_i, reason)) {
sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL);
sc_sess->SET_STRERROR("error sending reply");
} else
@ -103,13 +90,6 @@ void replyRequest(DSMSession* sc_sess, AmSession* sess,
CONST_ACTION_2P(DLGReplyAction, ',', true);
EXEC_ACTION_START(DLGReplyAction) {
if (!sc_sess->last_req.get()) {
ERROR("no last request to reply\n");
sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL);
sc_sess->SET_STRERROR("no last request to reply");
EXEC_ACTION_STOP;
}
replyRequest(sc_sess, sess, event_params, par1, par2, *sc_sess->last_req.get());
} EXEC_ACTION_END;
@ -134,7 +114,7 @@ EXEC_ACTION_START(DLGAcceptInviteAction) {
unsigned int code_i=200;
string reason = "OK";
string code = resolveVars(par1, sess, sc_sess, event_params);
string hdrs = replaceLineEnds(resolveVars("$dlg.reply.hdrs", sess, sc_sess, event_params));
DBG("replying with code %s\n", code.c_str());
if (code.length()) {
reason = resolveVars(par2, sess, sc_sess, event_params);
@ -147,8 +127,6 @@ EXEC_ACTION_START(DLGAcceptInviteAction) {
}
}
DBG("replying with %i %s, hdrs='%s'\n", code_i, reason.c_str(), hdrs.c_str());
if (!sc_sess->last_req.get()) {
ERROR("no last request to reply\n");
sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL);
@ -162,17 +140,14 @@ EXEC_ACTION_START(DLGAcceptInviteAction) {
sess->acceptAudio(sc_sess->last_req.get()->body,
sc_sess->last_req.get()->hdrs,&sdp_reply);
if(sess->dlg.reply(*sc_sess->last_req.get(),code_i, reason,
"application/sdp",sdp_reply, hdrs) != 0)
"application/sdp",sdp_reply) != 0)
throw AmSession::Exception(500,"could not send response");
}catch(const AmSession::Exception& e){
ERROR("%i %s\n",e.code,e.reason.c_str());
sess->setStopped();
sess->dlg.reply(*sc_sess->last_req.get(),e.code,e.reason);
sc_sess->SET_ERRNO(DSM_ERRNO_DLG);
sc_sess->SET_STRERROR("Error accepting call: "+ int2str(e.code) + " "+ e.reason);
}
} EXEC_ACTION_END;

@ -30,8 +30,6 @@
#define MOD_CLS_NAME DLGModule
#define DSM_ERRNO_DLG "dlg"
DECLARE_MODULE_BEGIN(MOD_CLS_NAME);
bool onInvite(const AmSipRequest& req, DSMSession* sess);
DECLARE_MODULE_END;

@ -61,8 +61,6 @@ DSMAction* SCMysqlModule::getAction(const string& from_str) {
DEF_CMD("mysql.saveResult", SCMySaveResultAction);
DEF_CMD("mysql.useResult", SCMyUseResultAction);
DEF_CMD("mysql.playDBAudio", SCMyPlayDBAudioAction);
DEF_CMD("mysql.playDBAudioFront", SCMyPlayDBAudioFrontAction);
DEF_CMD("mysql.playDBAudioLooped", SCMyPlayDBAudioLoopedAction);
DEF_CMD("mysql.getFileFromDB", SCMyGetFileFromDBAction);
DEF_CMD("mysql.putFileToDB", SCMyPutFileToDBAction);
@ -452,14 +450,12 @@ EXEC_ACTION_START(SCMyUseResultAction) {
} EXEC_ACTION_END;
bool playDBAudio(AmSession* sess, DSMSession* sc_sess, DSMCondition::EventType event,
map<string,string>* event_params, const string& par1, const string& par2,
bool looped, bool front) {
CONST_ACTION_2P(SCMyPlayDBAudioAction, ',', true);
EXEC_ACTION_START(SCMyPlayDBAudioAction) {
mysqlpp::Connection* conn =
getMyDSMSessionConnection(sc_sess);
if (NULL == conn)
EXEC_ACTION_STOP;
if (NULL == conn)
return false;
string qstr = replaceQueryParams(par1, sc_sess, event_params);
try {
@ -470,14 +466,14 @@ bool playDBAudio(AmSession* sess, DSMSession* sc_sess, DSMCondition::EventType e
mysqlpp::Row row = res.fetch_row();
if (!row) {
sc_sess->SET_ERRNO(DSM_ERRNO_MY_NOROW);
sc_sess->SET_STRERROR("result does not have row");
EXEC_ACTION_STOP;
sc_sess->SET_STRERROR("result does not have row");
return false;
}
FILE *t_file = tmpfile();
if (NULL == t_file) {
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
sc_sess->SET_STRERROR("tmpfile() failed: "+string(strerror(errno)));
EXEC_ACTION_STOP;
return false;
}
fwrite(row.at(0).data(), 1, row.at(0).size(), t_file);
@ -487,12 +483,10 @@ bool playDBAudio(AmSession* sess, DSMSession* sc_sess, DSMCondition::EventType e
if (a_file->fpopen(par2, AmAudioFile::Read, t_file)) {
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
sc_sess->SET_STRERROR("fpopen failed!");
EXEC_ACTION_STOP;
return false;
}
a_file->loop.set(looped);
sc_sess->addToPlaylist(new AmPlaylistItem(a_file, NULL), front);
sc_sess->addToPlaylist(new AmPlaylistItem(a_file, NULL));
sc_sess->transferOwnership(a_file);
sc_sess->CLR_ERRNO;
@ -507,25 +501,6 @@ bool playDBAudio(AmSession* sess, DSMSession* sc_sess, DSMCondition::EventType e
sc_sess->SET_STRERROR(e.what());
sc_sess->var["db.ereason"] = e.what();
}
return false;
}
CONST_ACTION_2P(SCMyPlayDBAudioAction, ',', true);
EXEC_ACTION_START(SCMyPlayDBAudioAction) {
playDBAudio(sess, sc_sess, event, event_params, par1, par2,
/*looped = */ false, /*front = */ false);
} EXEC_ACTION_END;
CONST_ACTION_2P(SCMyPlayDBAudioFrontAction, ',', true);
EXEC_ACTION_START(SCMyPlayDBAudioFrontAction) {
playDBAudio(sess, sc_sess, event, event_params, par1, par2,
/*looped = */ false, /*front = */ true);
} EXEC_ACTION_END;
CONST_ACTION_2P(SCMyPlayDBAudioLoopedAction, ',', true);
EXEC_ACTION_START(SCMyPlayDBAudioLoopedAction) {
playDBAudio(sess, sc_sess, event, event_params, par1, par2,
/*looped = */ true, /*front = */ false);
} EXEC_ACTION_END;
CONST_ACTION_2P(SCMyGetFileFromDBAction, ',', true);

@ -88,8 +88,6 @@ DEF_SCCondition(MyConnectedCondition);
DEF_ACTION_1P(SCMySaveResultAction);
DEF_ACTION_1P(SCMyUseResultAction);
DEF_ACTION_2P(SCMyPlayDBAudioAction);
DEF_ACTION_2P(SCMyPlayDBAudioFrontAction);
DEF_ACTION_2P(SCMyPlayDBAudioLoopedAction);
DEF_ACTION_2P(SCMyGetFileFromDBAction);
DEF_ACTION_2P(SCMyPutFileToDBAction);

@ -1,7 +0,0 @@
set (mod_regex_SRCS
ModRegex.cpp
)
SET(sems_dsm_module_name mod_regex)
INCLUDE(${CMAKE_SOURCE_DIR}/cmake/dsm.lib.rules.txt)

@ -1,10 +0,0 @@
plug_in_name = mod_regex
DSMPATH ?= ../..
module_ldflags =
module_cflags = -DMOD_NAME=\"$(plug_in_name)\" -I$(DSMPATH)
COREPATH ?=$(DSMPATH)/../../core
lib_full_name = $(DSMPATH)/mods/lib/$(lib_name)
include $(DSMPATH)/mods/Makefile.dsm_module

@ -1,163 +0,0 @@
/*
* Copyright (C) 2012 Stefan Sayer
*
* This file is part of SEMS, a free SIP media server.
*
* SEMS 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 released under
* the GPL with the additional exemption that compiling, linking,
* and/or using OpenSSL is allowed.
*
* For a license to use the SEMS software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* SEMS 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 "ModRegex.h"
#include "log.h"
#include "AmUtils.h"
#include "AmConfigReader.h"
SC_EXPORT(MOD_CLS_NAME);
map<string, TsRegex> MOD_CLS_NAME::regexes;
MOD_ACTIONEXPORT_BEGIN(MOD_CLS_NAME) {
DEF_CMD("regex.compile", SCCompileRegexAction);
DEF_CMD("regex.match", SCExecRegexAction);
DEF_CMD("regex.clear", SCClearRegexAction);
} MOD_ACTIONEXPORT_END;
MOD_CONDITIONEXPORT_BEGIN(MOD_CLS_NAME) {
if (cmd == "regex.match") {
return new SCExecRegexCondition(params, false);
}
} MOD_CONDITIONEXPORT_END;
int MOD_CLS_NAME::preload() {
AmConfigReader cfg;
if(cfg.loadFile(AmConfig::ModConfigPath + string(MOD_NAME ".conf"))) {
INFO("no module configuration for '%s' found, not preloading regular expressions\n",
MOD_NAME);
return 0;
}
bool failed = false;
for (std::map<string,string>::const_iterator it =
cfg.begin(); it != cfg.end(); it++) {
if (add_regex(it->first, it->second)) {
ERROR("compiling regex '%s' for '%s'\n",
it->second.c_str(), it->first.c_str());
failed = true;
} else {
DBG("compiled regex '%s' as '%s'\n", it->second.c_str(), it->first.c_str());
}
}
return failed? -1 : 0;
}
int MOD_CLS_NAME::add_regex(const string& r_name, const string& r_reg) {
if (regexes[r_name].regcomp(r_reg.c_str(), REG_NOSUB | REG_EXTENDED)) {
ERROR("compiling '%s' for regex '%s'\n", r_reg.c_str(), r_name.c_str());
regexes.erase(r_name);
return -1;
}
return 0;
}
CONST_CONDITION_2P(SCExecRegexCondition, ',', false);
MATCH_CONDITION_START(SCExecRegexCondition) {
DBG("checking condition '%s' '%s'\n", par1.c_str(), par2.c_str());
return true;
} MATCH_CONDITION_END;
CONST_ACTION_2P(SCCompileRegexAction, ',', false);
EXEC_ACTION_START(SCCompileRegexAction) {
string rname = resolveVars(par1, sess, sc_sess, event_params);
string rval = par2; //resolveVars(par2, sess, sc_sess, event_params);
DBG("compiling '%s' for regex '%s'\n", rval.c_str(), rname.c_str());
if (MOD_CLS_NAME::add_regex(rname, rval)) {
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
ERROR("compiling '%s' for regex '%s'\n", rval.c_str(), rname.c_str());
}
} EXEC_ACTION_END;
CONST_ACTION_2P(SCExecRegexAction, ',', false);
EXEC_ACTION_START(SCExecRegexAction) {
string rname = resolveVars(par1, sess, sc_sess, event_params);
string val = resolveVars(par2, sess, sc_sess, event_params);
DBG("matching '%s' on regex '%s'\n", val.c_str(), rname.c_str());
map<string, TsRegex>::iterator it=MOD_CLS_NAME::regexes.find(rname);
if (it == MOD_CLS_NAME::regexes.end()) {
ERROR("regex '%s' not found for matching '%s'\n", rname.c_str(), val.c_str());
EXEC_ACTION_STOP;
}
int res = it->second.regexec(val.c_str(), 1, NULL, 0);
if (!res) {
// yeah side effects
sc_sess->var["regex.match"] = "1";
} else {
sc_sess->var["regex.match"] = "0";
}
} EXEC_ACTION_END;
EXEC_ACTION_START(SCClearRegexAction) {
string r_name = resolveVars(arg, sess, sc_sess, event_params);
DBG("clearing regex '%s'\n", r_name.c_str());
MOD_CLS_NAME::regexes.erase(r_name);
} EXEC_ACTION_END;
TsRegex::TsRegex()
: i(false) { }
TsRegex::~TsRegex()
{
if (i) {
regfree(&reg);
}
}
int TsRegex::regcomp(const char *regex, int cflags) {
m.lock();
if (i) {
regfree(&reg);
}
int res = ::regcomp(&reg, regex, cflags);
if (!res)
i=true;
m.unlock();
return res;
}
int TsRegex::regexec(const char *_string, size_t nmatch,
regmatch_t pmatch[], int eflags) {
if (!i) {
ERROR("uninitialized regex when matching '%s'\n", _string);
return -1;
}
m.lock();
int res = ::regexec(&reg, _string, nmatch, pmatch, eflags);
m.unlock();
return res;
}

@ -1,65 +0,0 @@
/*
* Copyright (C) 2012 Stefan Sayer
*
* This file is part of SEMS, a free SIP media server.
*
* SEMS 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 released under
* the GPL with the additional exemption that compiling, linking,
* and/or using OpenSSL is allowed.
*
* For a license to use the SEMS software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* SEMS 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
*/
#ifndef _MOD_REGEX_H
#define _MOD_REGEX_H
#include "DSMModule.h"
#include "DSMSession.h"
#define MOD_CLS_NAME SCRegexModule
#include <sys/types.h>
#include <regex.h>
#include <map>
// in some earlier versions of libc apparently regex were inot thread-safe
class TsRegex {
regex_t reg;
AmMutex m;
bool i;
public:
TsRegex();
~TsRegex();
int regcomp(const char *regex, int cflags);
int regexec(const char *_string, size_t nmatch, regmatch_t pmatch[], int eflags);
};
DECLARE_MODULE_BEGIN(MOD_CLS_NAME);
int preload();
static map<string, TsRegex> regexes;
static int add_regex(const string& r_name, const string& r_reg);
DECLARE_MODULE_END;
/* }; */
DEF_ACTION_2P(SCCompileRegexAction);
DEF_ACTION_2P(SCExecRegexAction);
DEF_ACTION_1P(SCClearRegexAction);
DEF_CONDITION_2P(SCExecRegexCondition);
#endif

@ -1,6 +0,0 @@
# list of regex's to compile on start
# use preload_mods=mod_regex in dsm.conf if you want to use those
# ex:
#my_r1=^sip:
#my_r2=mydomain\.net

@ -1,10 +0,0 @@
plug_in_name = mod_subscription
DSMPATH ?= ../..
module_ldflags =
module_cflags = -DMOD_NAME=\"$(plug_in_name)\" -I$(DSMPATH)
COREPATH ?=$(DSMPATH)/../../core
lib_full_name = $(DSMPATH)/mods/lib/$(lib_name)
include $(DSMPATH)/mods/Makefile.dsm_module

@ -1,102 +0,0 @@
/*
* Copyright (C) 2012 FRAFOS GmbH
*
* Development sponsored by Sipwise GmbH.
*
* This file is part of SEMS, a free SIP media server.
*
* SEMS 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 released under
* the GPL with the additional exemption that compiling, linking,
* and/or using OpenSSL is allowed.
*
* For a license to use the SEMS software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* SEMS 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 "ModSubscription.h"
#include "log.h"
#include "AmUtils.h"
#include "AmSipSubscriptionContainer.h"
SC_EXPORT(MOD_CLS_NAME);
MOD_ACTIONEXPORT_BEGIN(MOD_CLS_NAME) {
DEF_CMD("subscription.create", SIPSUBCreateAction);
DEF_CMD("subscription.refresh", SIPSUBRefreshAction);
DEF_CMD("subscription.remove", SIPSUBRemoveAction);
} MOD_ACTIONEXPORT_END;
MOD_CONDITIONEXPORT_NONE(MOD_CLS_NAME);
CONST_ACTION_2P(SIPSUBCreateAction, ',', true);
EXEC_ACTION_START(SIPSUBCreateAction) {
string params_s = resolveVars(par1, sess, sc_sess, event_params);
// string basedir = resolveVars(par2, sess, sc_sess, event_params);
AmSipSubscriptionInfo info(sc_sess->var[params_s+".domain"],
sc_sess->var[params_s+".user"],
sc_sess->var[params_s+".from_user"],
sc_sess->var[params_s+".pwd"],
sc_sess->var[params_s+".proxy"],
sc_sess->var[params_s+".event"]);
info.accept = sc_sess->var[params_s+".accept"];
info.id = sc_sess->var[params_s+".id"];
unsigned int expires = 0;
if (sc_sess->var.find(params_s+".expires") != sc_sess->var.end()) {
str2i(sc_sess->var[params_s+".expires"], expires);
}
string handle = AmSipSubscriptionContainer::instance()->
createSubscription(info, sess->getLocalTag(), expires);
DBG("got handle '%s'\n", handle.c_str());
sc_sess->var[params_s+".handle"] = handle;
if (handle.empty()) {
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
sc_sess->SET_STRERROR("could not create subscription\n");
} else {
sc_sess->CLR_ERRNO;
}
} EXEC_ACTION_END;
CONST_ACTION_2P(SIPSUBRefreshAction, ',', true);
EXEC_ACTION_START(SIPSUBRefreshAction) {
string handle = resolveVars(par1, sess, sc_sess, event_params);
string expires = resolveVars(par2, sess, sc_sess, event_params);
unsigned int expires_i = 0;
if (!expires.empty())
str2i(expires, expires_i);
DBG("refreshing subscription with handle '%s'\n", handle.c_str());
if (!AmSipSubscriptionContainer::instance()->refreshSubscription(handle, expires_i)) {
sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL);
sc_sess->SET_STRERROR("could not refresh subscription\n");
} else {
sc_sess->CLR_ERRNO;
}
} EXEC_ACTION_END;
EXEC_ACTION_START(SIPSUBRemoveAction) {
string handle = resolveVars(arg, sess, sc_sess, event_params);
DBG("removing subscription with handle '%s'\n", handle.c_str());
AmSipSubscriptionContainer::instance()->removeSubscription(handle);
} EXEC_ACTION_END;

@ -1,41 +0,0 @@
/*
* Copyright (C) 2012 FRAFOS GmbH
*
* Development sponsored by Sipwise GmbH.
*
* This file is part of SEMS, a free SIP media server.
*
* SEMS 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 released under
* the GPL with the additional exemption that compiling, linking,
* and/or using OpenSSL is allowed.
*
* For a license to use the SEMS software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* SEMS 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
*/
#ifndef _MOD_SUBSCRIPTION_H
#define _MOD_SUBSCRIPTION_H
#include "DSMModule.h"
#include "DSMSession.h"
#define MOD_CLS_NAME SCSubscriptionModule
DECLARE_MODULE(MOD_CLS_NAME);
DEF_ACTION_2P(SIPSUBCreateAction);
DEF_ACTION_2P(SIPSUBRefreshAction);
DEF_ACTION_1P(SIPSUBRemoveAction);
#endif

@ -1,11 +0,0 @@
plug_in_name = mod_xml
DSMPATH ?= ../..
module_ldflags = `xml2-config --libs`
module_cflags = -DMOD_NAME=\"$(plug_in_name)\" -I$(DSMPATH) \
`xml2-config --cflags`
COREPATH ?=$(DSMPATH)/../../core
lib_full_name = $(DSMPATH)/mods/lib/$(lib_name)
include $(DSMPATH)/mods/Makefile.dsm_module

@ -1,281 +0,0 @@
/*
* Copyright (C) 2012 FRAFOS GmbH
*
* Development sponsored by Sipwise GmbH.
*
* This file is part of SEMS, a free SIP media server.
*
* SEMS 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 released under
* the GPL with the additional exemption that compiling, linking,
* and/or using OpenSSL is allowed.
*
* For a license to use the SEMS software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* SEMS 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 "ModXml.h"
#include "log.h"
#include "AmUtils.h"
#include <string.h>
SC_EXPORT(MOD_CLS_NAME);
void xml_err_func(void *ctx, const char *msg, ...);
xmlGenericErrorFunc handler = (xmlGenericErrorFunc)xml_err_func;
int xml_log_level = L_ERR;
int MOD_CLS_NAME::preload() {
DBG("initializing libxml2...\n");
xmlInitParser();
initGenericErrorDefaultFunc(&handler);
handler = (xmlGenericErrorFunc)xml_err_func;
xmlSetGenericErrorFunc(NULL, &xml_err_func);
return 0;
}
MOD_ACTIONEXPORT_BEGIN(MOD_CLS_NAME) {
DEF_CMD("xml.parse", MODXMLParseAction);
DEF_CMD("xml.parseSIPMsgBody", MODXMLParseSIPMsgBodyAction);
DEF_CMD("xml.evalXPath", MODXMLEvalXPathAction);
DEF_CMD("xml.XPathResultCount", MODXMLXPathResultNodeCount);
DEF_CMD("xml.setLoglevel", MODXMLSetLogLevelAction);
} MOD_ACTIONEXPORT_END;
MOD_CONDITIONEXPORT_NONE(MOD_CLS_NAME);
ModXmlDoc::~ModXmlDoc() {
if (NULL != doc) {
DBG("freeing XML document [%p]\n", doc);
xmlFreeDoc(doc);
}
}
ModXmlXPathObj::~ModXmlXPathObj() {
if (NULL != xpathObj) {
DBG("freeing XML xpath obj [%p]\n", xpathObj);
xmlXPathFreeObject(xpathObj);
}
if (NULL != xpathCtx) {
DBG("freeing XML xpath ctx [%p]\n", xpathCtx);
xmlXPathFreeContext(xpathCtx);
}
}
#define TMP_BUF_SIZE 256
void xml_err_func(void *ctx, const char *msg, ...) {
char _string[TMP_BUF_SIZE];
va_list arg_ptr;
va_start(arg_ptr, msg);
vsnprintf(_string, TMP_BUF_SIZE, msg, arg_ptr);
va_end(arg_ptr);
_LOG(xml_log_level, "%s", _string);
}
CONST_ACTION_2P(MODXMLParseSIPMsgBodyAction, ',', false);
EXEC_ACTION_START(MODXMLParseSIPMsgBodyAction) {
string msgbody_var = resolveVars(par1, sess, sc_sess, event_params);
string dstname = resolveVars(par2, sess, sc_sess, event_params);
AVarMapT::iterator it = sc_sess->avar.find(msgbody_var);
if (it==sc_sess->avar.end()) {
DBG("no message body in avar '%s'\n", msgbody_var.c_str());
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
sc_sess->SET_STRERROR("no message body in avar " + msgbody_var);
EXEC_ACTION_STOP;
}
if (!isArgCStr(it->second)) {
DBG("no SIP MSG body in avar '%s'\n", msgbody_var.c_str());
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
sc_sess->SET_STRERROR("no SIP MSG body in avar " + msgbody_var);
EXEC_ACTION_STOP;
}
const char* b = it->second.asCStr();
if (b==NULL) {
DBG("empty SIP MSG body in avar '%s'\n", msgbody_var.c_str());
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
sc_sess->SET_STRERROR("empty SIP MSG body in avar " + msgbody_var);
EXEC_ACTION_STOP;
}
xmlSetGenericErrorFunc(NULL, &xml_err_func);
xmlDocPtr doc =
xmlReadMemory((const char*)b, strlen(b), "noname.xml", NULL, 0);
if (doc == NULL) {
DBG("failed parsing XML document from '%s'\n", msgbody_var.c_str());
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
sc_sess->SET_STRERROR("failed parsing XML document from " + msgbody_var);
EXEC_ACTION_STOP;
}
xmlSetGenericErrorFunc(doc, &xml_err_func);
ModXmlDoc* xml_doc = new ModXmlDoc(doc);
sc_sess->avar[dstname] = xml_doc;
DBG("parsed XML body document to '%s'\n", dstname.c_str());
// string basedir = resolveVars(par2, sess, sc_sess, event_params);
} EXEC_ACTION_END;
CONST_ACTION_2P(MODXMLParseAction, ',', false);
EXEC_ACTION_START(MODXMLParseAction) {
string xml_doc = resolveVars(par1, sess, sc_sess, event_params);
string dstname = resolveVars(par2, sess, sc_sess, event_params);
xmlSetGenericErrorFunc(NULL, &xml_err_func);
xmlDocPtr doc =
xmlReadMemory(xml_doc.c_str(), xml_doc.length(), "noname.xml", NULL, 0);
if (doc == NULL) {
DBG("failed parsing XML document from '%s'\n", xml_doc.c_str());
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
sc_sess->SET_STRERROR("failed parsing XML document from " + xml_doc);
EXEC_ACTION_STOP;
}
xmlSetGenericErrorFunc(doc, &xml_err_func);
ModXmlDoc* xml_doc_var = new ModXmlDoc(doc);
sc_sess->avar[dstname] = xml_doc_var;
DBG("parsed XML body document to '%s'\n", dstname.c_str());
} EXEC_ACTION_END;
template<class T>
T* getXMLElemFromVariable(DSMSession* sc_sess, const string& var_name) {
AVarMapT::iterator it = sc_sess->avar.find(var_name);
if (it == sc_sess->avar.end()) {
DBG("object '%s' not found\n", var_name.c_str());
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
sc_sess->SET_STRERROR("object '"+var_name+"' not found\n");
return NULL;
}
T* doc = dynamic_cast<T*>(it->second.asObject());
if (NULL == doc) {
DBG("object '%s' is not the right type\n", var_name.c_str());
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
sc_sess->SET_STRERROR("object '"+var_name+"' is not the right type\n");
return NULL;
}
return doc;
}
CONST_ACTION_2P(MODXMLEvalXPathAction, ',', false);
EXEC_ACTION_START(MODXMLEvalXPathAction) {
string xpath_expr = resolveVars(par1, sess, sc_sess, event_params);
string xml_doc_var = resolveVars(par2, sess, sc_sess, event_params);
xmlSetGenericErrorFunc(NULL, &xml_err_func);
ModXmlDoc* xml_doc = getXMLElemFromVariable<ModXmlDoc>(sc_sess, xml_doc_var);
if (NULL == xml_doc)
EXEC_ACTION_STOP;
xmlDocPtr doc = xml_doc->doc;
xmlXPathContextPtr xpathCtx = xmlXPathNewContext(doc);
if(xpathCtx == NULL) {
DBG("unable to create new XPath context\n");
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
sc_sess->SET_STRERROR("unable to create new XPath context");
EXEC_ACTION_STOP;
}
xmlSetGenericErrorFunc(xpathCtx, &xml_err_func);
string xml_doc_ns = sc_sess->var[xml_doc_var+".ns"];
vector<string> ns_entries = explode(xml_doc_ns, " ");
for (vector<string>::iterator it=ns_entries.begin(); it != ns_entries.end(); it++) {
vector<string> ns = explode(*it, "=");
if (ns.size() != 2) {
DBG("script writer error: namespace entry must be prefix=href (got '%s')\n",
it->c_str());
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
sc_sess->SET_STRERROR("script writer error: namespace entry must be prefix=href\n");
xmlXPathFreeContext(xpathCtx);
EXEC_ACTION_STOP;
}
if(xmlXPathRegisterNs(xpathCtx, (const xmlChar*)ns[0].c_str(),
(const xmlChar*)ns[1].c_str()) != 0) {
DBG("unable to register namespace %s=%s\n", ns[0].c_str(), ns[1].c_str());
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
sc_sess->SET_STRERROR("unable to register namespace\n");
xmlXPathFreeContext(xpathCtx);
EXEC_ACTION_STOP;
}
DBG("registered namespace %s=%s\n", ns[0].c_str(), ns[1].c_str());
}
xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression((const xmlChar*)xpath_expr.c_str(),
xpathCtx);
if(xpathObj == NULL) {
DBG("unable to evaluate xpath expression \"%s\"\n", xpath_expr.c_str());
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
sc_sess->SET_STRERROR("unable to evaluate xpath expression");
xmlXPathFreeContext(xpathCtx);
EXEC_ACTION_STOP;
}
ModXmlXPathObj* xpath_obj = new ModXmlXPathObj(xpathObj, xpathCtx);
sc_sess->avar[xml_doc_var+".xpath"] = xpath_obj;
DBG("evaluated XPath expression on '%s' to '%s'\n",
xml_doc_var.c_str(), (xml_doc_var+".xpath").c_str());
} EXEC_ACTION_END;
CONST_ACTION_2P(MODXMLXPathResultNodeCount, '=', false);
EXEC_ACTION_START(MODXMLXPathResultNodeCount) {
string cnt_var = par1;
string xpath_res_var = resolveVars(par2, sess, sc_sess, event_params);
if (cnt_var.size() && cnt_var[0]=='$') {
cnt_var.erase(0,1);
}
ModXmlXPathObj* xpath_obj =
getXMLElemFromVariable<ModXmlXPathObj>(sc_sess, xpath_res_var);
if (NULL == xpath_obj){
DBG("no xpath result found in '%s'\n", xpath_res_var.c_str());
sc_sess->var[cnt_var] = "0";
EXEC_ACTION_STOP;
}
unsigned int res = (xpath_obj->xpathObj->nodesetval) ?
xpath_obj->xpathObj->nodesetval->nodeNr : 0;
sc_sess->var[cnt_var] = int2str(res);
DBG("set count $%s=%u\n", cnt_var.c_str(), res);
} EXEC_ACTION_END;
EXEC_ACTION_START(MODXMLSetLogLevelAction) {
string xml_log_level_s = resolveVars(arg, sess, sc_sess, event_params);
if (xml_log_level_s == "error")
xml_log_level = L_ERR;
else if (xml_log_level_s == "warn")
xml_log_level = L_WARN;
else if (xml_log_level_s == "info")
xml_log_level = L_INFO;
else if (xml_log_level_s == "debug")
xml_log_level = L_DBG;
else {
ERROR("script writer error: '%s' is no valid log level (error, warn, info, debug)\n",
xml_log_level_s.c_str());
}
} EXEC_ACTION_END;

@ -1,75 +0,0 @@
/*
* Copyright (C) 2012 FRAFOS GmbH
*
* Development sponsored by Sipwise GmbH.
*
* This file is part of SEMS, a free SIP media server.
*
* SEMS 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 released under
* the GPL with the additional exemption that compiling, linking,
* and/or using OpenSSL is allowed.
*
* For a license to use the SEMS software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* SEMS 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
*/
#ifndef _MOD_XML_H
#define _MOD_XML_H
#include "DSMModule.h"
#include "DSMSession.h"
#include <libxml/tree.h>
#include <libxml/parser.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
#define MOD_CLS_NAME SCXMLModule
DECLARE_MODULE_BEGIN(MOD_CLS_NAME);
int preload();
DECLARE_MODULE_END;
DEF_ACTION_2P(MODXMLParseSIPMsgBodyAction);
DEF_ACTION_2P(MODXMLParseAction);
DEF_ACTION_2P(MODXMLEvalXPathAction);
DEF_ACTION_2P(MODXMLXPathResultNodeCount);
DEF_ACTION_1P(MODXMLSetLogLevelAction);
class ModXmlDoc
: public DSMDisposable,
public ArgObject {
public:
ModXmlDoc() : doc(NULL) {}
ModXmlDoc(xmlDocPtr doc) : doc(doc) {}
~ModXmlDoc();
xmlDocPtr doc;
};
class ModXmlXPathObj
: public DSMDisposable,
public ArgObject {
public:
ModXmlXPathObj() : xpathObj(NULL), xpathCtx(NULL) {}
ModXmlXPathObj(xmlXPathObjectPtr xpathObj, xmlXPathContextPtr xpathCtx)
: xpathObj(xpathObj), xpathCtx(xpathCtx) { }
~ModXmlXPathObj();
xmlXPathObjectPtr xpathObj;
xmlXPathContextPtr xpathCtx;
};
#endif

@ -1,70 +0,0 @@
--
-- This DSM app
-- o plays early media from a file in the DB
-- o replies with final reply code/reason from P-App-Param
--
-- Parameters (P-App-Param):
-- audio_id - indicating audio file in DB; must be wav 16 bit pcm format (with wav header)
-- fr_code - final reply code
-- fr_reason - final reply reason
-- play_looped - "yes" to play the prompt in loop
--
-- Example: P-App-Param: audio_id=rbt;play_looped=yes;fr_code=404;fr_reason="Not found"
import(mod_utils);
import(mod_dlg);
import(mod_mysql);
initial state START;
transition "DB exception" START - exception / {
log(1, "Error in initializing :");
logAll(1);
-- reply with fr_code/reason immediately
dlg.reply($fr_code, $fr_reason);
-- alternatively, reply with 500:
-- dlg.reply(500, "Server Internal Error");
stop(false);
} -> END;
transition "invite" START - invite / {
if test($audio_id != "") {
mysql.connect();
throwOnError();
-- play waiting file from DB
if test($play_looped=="yes") {
mysql.playDBAudioLooped(SELECT data FROM provisioning.audio_files WHERE id="$audio_id", rbt.wav);
} else {
mysql.playDBAudio(SELECT data FROM provisioning.audio_files WHERE id="$audio_id", rbt.wav);
}
throwOnError();
mysql.disconnect();
}
-- send 183 with early media
dlg.acceptInvite(183, "Progress");
-- no default 200 OK
set($connect_session=0);
-- start RTP processing
connectMedia();
} -> WAITING;
state WAITING;
transition "CANCEL received" WAITING - hangup / {
dlg.reply(487, "Request Terminated");
removeTimers();
stop(false);
} -> END;
transition "end of prompt" WAITING - noAudio / {
dlg.reply($fr_code, $fr_reason);
removeTimers();
stop(false);
} -> END;
state END;

@ -1,20 +0,0 @@
# configuration for early_dbprompt app
# DSM configuration
diag_path=/usr/lib/sems/early_dbprompt
mod_path=/usr/lib/sems/plug-in/
load_diags=early_dbprompt
register_apps=early_dbprompt
run_invite_event=yes
set_param_variables=yes
# application configuration:
# database connection:
db_url="mysql://root:frafos@localhost/provisioning"
# local testing:
#diag_path=../apps/early_dbprompt
#mod_path=../apps/dsm/mods/lib

@ -33,7 +33,7 @@ modules:
done
.PHONY: install
install: install-bin
install: install-bin install-cfg
.PHONY: install-bin
install-bin:

@ -33,7 +33,7 @@ modules:
done
.PHONY: install
install: install-bin
install: install-bin install-cfg
.PHONY: install-bin
install-bin:

@ -281,46 +281,6 @@ static PyObject* IvrDialogBase_unmute(IvrDialogBase* self, PyObject* args)
return Py_None;
}
static PyObject* IvrDialogBase_enableReceiving(IvrDialogBase* self, PyObject* args)
{
assert(self->p_dlg);
self->p_dlg->setReceiving(true);
Py_INCREF(Py_None);
return Py_None;
}
static PyObject* IvrDialogBase_disableReceiving(IvrDialogBase* self, PyObject* args)
{
assert(self->p_dlg);
self->p_dlg->setReceiving(false);
Py_INCREF(Py_None);
return Py_None;
}
static PyObject* IvrDialogBase_enableDTMFReceiving(IvrDialogBase* self, PyObject* args)
{
assert(self->p_dlg);
self->p_dlg->setForceDtmfReceiving(true);
Py_INCREF(Py_None);
return Py_None;
}
static PyObject* IvrDialogBase_disableDTMFReceiving(IvrDialogBase* self, PyObject* args)
{
assert(self->p_dlg);
self->p_dlg->setForceDtmfReceiving(false);
Py_INCREF(Py_None);
return Py_None;
}
static PyObject* IvrDialogBase_remove_mediaprocessor(IvrDialogBase* self,
PyObject* args)
{
@ -597,18 +557,6 @@ static PyMethodDef IvrDialogBase_methods[] = {
{"unmute", (PyCFunction)IvrDialogBase_unmute, METH_NOARGS,
"unmute the RTP stream (send packets)"
},
{"enableReceiving", (PyCFunction)IvrDialogBase_enableReceiving, METH_NOARGS,
"enable receiving of RTP packets"
},
{"disableReceiving", (PyCFunction)IvrDialogBase_disableReceiving, METH_NOARGS,
"disable receiving of RTP packets"
},
{"enableDTMFReceiving", (PyCFunction)IvrDialogBase_enableDTMFReceiving, METH_NOARGS,
"enable receiving of RFC-2833 DTMF packets even if RTP receiving is disabled"
},
{"disableDTMFReceiving", (PyCFunction)IvrDialogBase_disableDTMFReceiving, METH_NOARGS,
"disable receiving of RFC-2833 DTMF packets when RTP receiving is disabled"
},
{"connectMedia", (PyCFunction)IvrDialogBase_add_mediaprocessor, METH_NOARGS,
"enable the processing of audio and RTP"
},

@ -1,45 +0,0 @@
# configuration for mobile_push app
# DSM configuration
diag_path=/usr/lib/sems/mobile_push
load_diags=mobile_push
register_apps=mobile_push
mod_path=/usr/lib/sems/plug-in/
preload_mods=mod_xml
run_invite_event=yes
set_param_variables=yes
#run_system_dsms=
# application configuration:
# enable_rbt=[yes|no] - enable playing of ring-back tone from DB?
enable_rbt=yes
# database connection:
db_url="mysql://root:frafos@localhost/provisioning"
# play_looped=[yes|no] - loop file?
play_looped=yes
# mobile push web app server URL:
push_server_url=http://127.0.0.1:8000/mobilepush/
# use_post=[yes|no] - use HTTP POST? (or GET)
use_post=no
# subscription user/pwd (From in subscription)
subscription_user=sub_user
subscription_pwd=sub_pwd
# proxy for subscription (outbound-proxy setting)
#proxy=sip:127.0.0.1:5060
# extra_3xx_uri_append - append extra string to Contact in 300 (e.g. port)
# extra_3xx_uri_append=":5090"
# use_subscription_id=[yes|no] - use an ID for SUBSCRIBE? (problems with kamailio :( )
use_subscription_id=no

@ -1,5 +0,0 @@
#!/usr/bin/env python
from xmlrpclib import *
s = ServerProxy('http://127.0.0.1:8092')
print s.setTarget(100, 1, 0, '4', '192.168.5.110', 2, 0, 30, 40)

@ -1,36 +0,0 @@
import time
import BaseHTTPServer
HOST_NAME = '127.0.0.1' # !!!REMEMBER TO CHANGE THIS!!!
PORT_NUMBER = 8000 # Maybe set this to 9000.
class MyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_HEAD(s):
s.send_response(200)
s.send_header("Content-type", "text/html")
s.end_headers()
def do_GET(s):
"""Respond to a GET request."""
s.send_response(200)
s.send_header("Content-type", "text/html")
s.end_headers()
s.wfile.write("<html><head><title>Title goes here.</title></head>")
s.wfile.write("<body><p>This is a test.</p>")
# If someone went to "http://something.somewhere.net/foo/bar/",
# then s.path equals "/foo/bar/".
s.wfile.write("<p>You accessed path: %s</p>" % s.path)
s.wfile.write("</body></html>")
if __name__ == '__main__':
server_class = BaseHTTPServer.HTTPServer
httpd = server_class((HOST_NAME, PORT_NUMBER), MyHandler)
print time.asctime(), "Server Starts - %s:%s" % (HOST_NAME, PORT_NUMBER)
try:
httpd.serve_forever()
except KeyboardInterrupt:
pass
httpd.server_close()
print time.asctime(), "Server Stops - %s:%s" % (HOST_NAME, PORT_NUMBER)

@ -1,105 +0,0 @@
this is a small set of scripts and config files to do load tests on the
mobile-push application. It uses another SEMS instance to register/deregister
users using db_reg_agent module, and place calls using call_gen application.
both sems instances are expected to run on 192.168.5.110 - if that IP is
different, it needs to be changed in some (many) places.
1. create users 400..499 (pwd 400..499) in kamailio with
for ((i=400;i<500;i++)); do kamctl add $i $i; done
1.b for Kamailio from packages (default config),
- enable:
#!define WITH_MYSQL
#!define WITH_PRESENCE
undefine WITH_AUTH
- add to presence loading section:
#!ifdef WITH_PRESENCE
loadmodule "presence.so"
loadmodule "presence_xml.so"
# reginfo related
loadmodule "presence_reginfo.so"
loadmodule "pua.so"
loadmodule "pua_reginfo.so"
#!endif
- add to presence config section:
#!ifdef WITH_PRESENCE
# ----- presence params -----
modparam("presence", "db_url", DBURL)
# ----- presence_xml params -----
modparam("presence_xml", "db_url", DBURL)
modparam("presence_xml", "force_active", 1)
# reginfo related
modparam("pua_reginfo", "default_domain", "192.168.5.110")
modparam("pua_reginfo", "publish_reginfo", 1)
modparam("pua_reginfo", "server_address", "sip:reginfo@192.168.5.110")
- remove auth check in request_route (easier)
# authentication
# if (!is_method("SUBSCRIBE|NOTIFY")) {
# route(AUTH);
# }
- add SERVICES route to route calls to sems:
# dispatch requests to foreign domains
route(SIPOUT);
route(SERVICES);
...
route[SERVICES] {
if (method=="INVITE") {
if ((uri=~"sip:38") || (uri=~"sip:4"))
{
# $ru = "sip:" + $rU + "@" + "192.168.5.110:5080";
append_hf("P-App-Param: audio_id=connecting;caller=+492222222222;callee=+491111111111;domain=sip.sipwise.com\r\n");
append_hf("P-hint: services\r\n");
t_relay_to("192.168.5.110:5080");
exit();
}
}
}
2. create database tables for sems with:
CREATE TABLE `registrations` (
`subscriber_id` int(11) NOT NULL,
`registration_status` tinyint(1) NOT NULL DEFAULT '0',
`last_registration` datetime NOT NULL,
`expiry` datetime NOT NULL,
`last_code` smallint(2) NOT NULL,
`last_reason` varchar(256) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
`contacts` varchar(512) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
PRIMARY KEY (`subscriber_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
CREATE TABLE `subscribers` (
`subscriber_id` int(10) NOT NULL AUTO_INCREMENT,
`user` varchar(256) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
`pass` varchar(256) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
`realm` varchar(256) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
`contact` varchar(256) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
PRIMARY KEY (`subscriber_id`)
) ENGINE=MyISAM AUTO_INCREMENT=20005 DEFAULT CHARSET=latin1;
3. set mysql pwd in db_reg_agent.conf
4. start load gen sems instance with the configuration given here.
(git clone git://git.sip-router.org/sems ; cp -R sems_cfg/* sems ; cd sems ; make -C apps/xmlrpc2di ;
make -C apps/examples/call_gen; make -C apps/db_reg_agent; make -C core ; cd core ;
./sems -f etc/sems.conf -D 3 -E )
5. run reg_client.py, which will very quickly register and deregister subscribers
by calling removeRegistration/createRegistration on the load gen sems instance
6. run call_gen.py to set a target of 100 calls (or run sth like
s.createCalls(1, 1, 0, '4', '192.168.5.110', 2, 0, 30, 40) to create one call for testing first

@ -1,20 +0,0 @@
#!/usr/bin/env python
from xmlrpclib import *
from random import *
import time
s = ServerProxy('http://127.0.0.1:8092')
r = set()
while True:
n = randint(400, 499)
if n in r:
print s.removeRegistration(n)
r.remove(n)
else:
print s.createRegistration(n, str(n), str(n), '192.168.5.110')
r.add(n)
time.sleep(.1)

@ -1,3 +0,0 @@
play_file=wav/default_en.wav
digits_dir=../apps/examples/call_gen/wav/
from_host=callgen.example.net

@ -1,99 +0,0 @@
# Database connection
mysql_user=root
mysql_passwd=mysqlrootpwd
# mysql_server, default: localhost
# mysql_server=localhost
#mysql_db, default: sems
# mysql_db=sems
# table for registration status
# default: registrations
# registrations_table="registrations"
# query joining subscriber info with registration table
joined_query="select subscribers.subscriber_id as subscriber_id, subscribers.user as user, subscribers.pass as pass, subscribers.realm as realm, registrations.registration_status as registration_status, registrations.expiry as expiry, registrations.last_registration as last_registration from subscribers left join registrations on subscribers.subscriber_id=registrations.subscriber_id;"
#example with contact:
# joined_query="select subscribers.subscriber_id as subscriber_id, subscribers.user as user, subscribers.pass as pass, subscribers.realm as realm, subscribers.contact as contact, registrations.registration_status as registration_status, registrations.expiry as expiry, registrations.last_registration as last_registration from subscribers left join registrations on subscribers.subscriber_id=registrations.subscriber_id;"
# outbound_proxy=<sip_uri>
# set the outbound proxy to send registrations through
#
#outbound_proxy=sip:10.0.0.2:5070
# contact_hostport=<host:port> - overriding contact host:port
# sets the contact host:port portion that is registered
# default: empty
#contact_hostport=10.0.0.50:5080
# db_read_contact=[yes, no] : read individual contact to register from DB?
# note: joined_query must select the contact, too
# default: no
#db_read_contact=yes
#save_contacts=[yes, no] : save contacts?
# for monitoring and troubleshooting, all contacts returned with a positive reply
# may be saved to DB in the registrations.contacts colums
# default: yes
#save_contacts=yes
#save_auth_replies=[yes, no] : update status on 401 replies (auth)?
# usually, 401 replies which are resent with auth are not saved. if save_auth_replies=yes,
# last_code and last_reason in DB is updated with the 401
#save_auth_replies=yes
# expires: desired expires, i.e. expires value that is requested
# default: 7200
# expires=300
# error_retry_interval
#
# default: 300
# reregister_interval: fraction of actual expires after which register is refreshed
# default: reregister_interval=0.5
#reregister_interval=0.5
# minimum_reregister_interval: if set, re-register is scheduled in least loaded time
# in minimum_reregister_interval .. reregister_interval i order to smooth load spikes
# must be smaller than reregister_interval
# default: off
#
# example:
# reregister_interval=0.5
# minimum_reregister_interval=0.4
# on a registration expiring in 3600s, the least loaded spot between 1440s and 1800s
# is chosen
#
#minimum_reregister_interval=0.4
# enable_ratelimiting=yes : Enable ratelimiting?
# default: no
# if enabled, the amount of initial REGISTER requests is limited (not counting re-trans-
# missions and requests re-sent for authentication)
#enable_ratelimiting=yes
# ratelimit_rate=300 : rate of initial REGISTER requests to send as maximum
#ratelimit_rate=2
# ratelimit_per=1 : per time unit (in seconds, e.g. 300 REGISTER in 1 second)
#ratelimit_per=1
#ratelimit_slowstart=[yes|no] : on startup send initially ratelimit_rate or 0 requests
#default: no
#ratelimit_slowstart=yes
# delete_removed_registrations=yes : delete removed registrations from registrations
# table in DB? (otherwise they will stay with STATUS_REMOVED)
# default: yes
#
#delete_removed_registrations=no
# delete_failed_deregistrations=yes : delete failed de-registrations from registrations
# table in DB? (otherwise they will stay with STATUS_TO_BE_REMOVED)
# only applicable if delete_removed_registrations=yes
# default: no
#
#delete_failed_deregistrations=no

@ -1,544 +0,0 @@
# $Id$
#
# sems.conf.sample
#
# Sip Express Media Server (sems)
#
# sample configuration file
#
#
# whitespaces (spaces and tabs) are ignored
# comments start with a "#" and may be used inline
#
# example: option=value # i like this option
#
# @filename includes mod_config_path/filename
# @/absolute/path/to/file includes file
############################################################
# Network configuration
# optional parameter: media_ip=<ip_address>|<device>
#
# - this informs SEMS about the IP address or interface that
# SEMS uses to send and receive media.
# - If neither 'media_ip' nor 'sip_ip' are set, defaults
# to first non-loopback interface. If 'sip_ip' is set,
# 'media_ip' defaults to 'sip_ip.
#
# Examples:
# media_ip=10.0.0.34
# media_ip=eth0
# optional parameter: sip_ip=<ip_address>|<device>
#
# - this informs SEMS about the SIP IP where its SIP stack is
# bound to or should be bound to. This also sets
# the value used for contact header in outgoing calls and
# registrations.
# - If neither 'media_ip' nor 'sip_ip' are set, defaults
# to first non-loopback interface. If 'media_ip' is set,
# 'sip_ip' defaults to 'media_ip.
#
# Example:
# sip_ip=10.0.0.34
# sip_ip=en0
#
# optional parameter: public_ip=<ip_address>
#
# - when running SEMS behind certain simple NAT configurations,
# you can use this parameter to inform SEMS of its public IP
# address. If this parameter is set, SEMS will write this value
# into SDP bodies.
# If this parameter is not set, the local IP address is used.
# N.B., there is no support for port translation; the local
# RTP port is advertised in SDP in either case.
#
# Example:
# public_ip=75.101.219.48
#
# optional parameter: sip_port=<port_number>
#
# - this informs SEMS about the port where its SIP stack is
# bound to or should be bound to. SEMS needs this information
# to correctly set the contact header in outgoing calls
# and registrations. Should be set to equal the 'port'
# configuration option in ser_sems.cfg.
#
# default: 5060
#
sip_port=5080
# optional parameter: outbound_proxy=uri
#
# - this sets an outbound proxy for calls and registrations initiated
# by SEMS. This does not apply to requests in a dialog that
# is initiated by someone else and incoming to SEMS.
# If this is not set (default setting), then for dialogs
# initiated by SEMS the r-uri is resolved and the request
# is sent there directly.
# This is resolved by the SIP stack with DNS if a name is given.
# Warning: If the value set here can not be resolved, no
# requests will be sent out at all!
#
# default: empty
#
# Example:
# outbound_proxy=sip:proxy.mydomain.net
# optional parameter: force_outbound_proxy={yes|no}
#
# - forces SEMS to send any request to the outbound proxy in any
# situation, by adding an extra first Route to the outbound_proxy.
# This option will only have an effect if the outbound_proxy
# option has been set, and it will break 3261 compatibility
# in some cases; better use next_hop_ip/next_hop_port.
#
# default: no
#
# Example:
# force_outbound_proxy=yes
# optional parameter: next_hop_ip
# - if this is set, all outgoing requests will be sent to
# this IP, regardless of R-URI etc.
#
#next_hop_ip=192.168.5.106
# optional parameter: next_hop_port
# defaults to 5060
#next_hop_port=5060
# optional parameter:next_hop_for_replies
# - use next_hop for replies, too?
#
#next_hop_for_replies=yes
# optional parameter: rtp_low_port=<port>
#
# - sets lowest for RTP used port
rtp_low_port=10000
# optional parameter: rtp_high_port=<port>
#
# - sets highest for RTP used port
rtp_high_port=60000
# Additional IFs (optional):
# additional_interfaces = <list of interfaces>
#
# additional_interfaces must be set if more than one
# interface is to be used for the same purpose (e.g.
# more than one interface for SIP). Configure additional
# interfaces if networks should be bridged or separate
# networks should be served.
#
# For each additional interface, a set of parameters
# suffixed with the interface name should be listed
# with the 'additional_interfaces' parameter.
#
# Please note that for each additional interface,
# 'sip_ip_[if_name]' is mandatory (but can be the interface
# name, then the first assigned IP is used). The other
# parameters are optional. 'media_ip_[if_name]'
# is derived from 'sip_ip_[if_name]' if not set.
# 'public_ip_[ip_name]' is also based on 'sip_ip_[if_name]'
# if not set explicitly.
#
# Example:
# additional_interfaces=intern,extern
#
# sip_ip_intern=192.168.0.5
# sip_port_intern=5060
# media_ip_intern=192.168.10.5
# rtp_low_port_intern=2000
# rtp_high_port_intern=5000
#
# sip_ip_extern=213.192.59.73
# sip_port_extern=5060
# media_ip_extern=213.192.59.73
# rtp_low_port_extern=2000
# rtp_high_port_extern=5000
# public_ip_extern=213.192.35.73
#
############################################################
# modules and application configuration
#
# Configuration of plugin (module) loading:
# - if load_plugins is set, only those are loaded.
# - if load_plugins is not set, all modules in the plugin_path
# directory are loaded, except those which are listed
# in exclude_plugins.
#
# optional parameter: plugin_path=<path>
#
# - sets the path to the plug-ins' binaries
# - may be absolute or relative to CWD
plugin_path=/usr/local/lib/sems/plug-in/
# optional parameter: load_plugins=<modules list>
#
# semicolon-separated list of modules to load.
# If empty, all modules in plugin_path are loaded.
#
# example for announcement with only g711 and ilbc codecs
# load_plugins=wav;ilbc;announcement
# optional parameter: exclude_plugins=<modules list>
#
# semicolon-separated list of modules to exclude from loading
# ('blacklist'). If empty, all modules in plugin_path are loaded.
# This has only effect it load_plugins is not set.
#
# o precoded_announce: no precoded sample files present
# o py_sems: conflicts with ivr (in some cases)
# o db_reg_agent: needs DB tables
exclude_plugins=precoded_announce;py_sems;db_reg_agent
# optional: load_plugins_rtld_global=<modules list>
#
# load these plugins with RTLD_GLOBAL (by default py_sems,
# dsm, ivr, sbc, diameter_client, registrar_client, uac_auth)
# optional parameter: application
#
# This controls which application is to be executed if there
# is no explicit application requested from the SIP stack
# (i.e. unixsockctrl and second parameter of t_write_unix).
#
# This can be one of
# $(ruri.user) - user part of ruri is taken as application,
# e.g. sip:announcement@host
# $(ruri.param) - uri parameter "app", e.g.
# sip:joe@host.net;app=announcement
# $(apphdr) - the value of the P-App-Name header is used
#
# $(mapping) - regex=>application mapping is read from
# app_mapping.conf (see app_mapping.conf)
# <application name> - application name configured here, e.g.
# application=announcement
#
# examples:
# application = conference
# application = $(mapping)
# application = $(ruri.user)
# application = $(ruri.param)
application = $(apphdr)
# parameter: plugin_config_path=<path>
#
# - in this path configuration files of the applications
# (e.g. announcement.conf) are searched
plugin_config_path=/usr/local/etc/sems/etc/
# optional parameter: exclude_payloads=<payload list>
#
# semicolon-separated list of payloads to exclude from loading
# ('blacklist').
#
# For example, to only use low bandwidth codecs:
# exclude_payloads=PCMU;PCMA;G726-40;G726-32;G721;L16
# or, to use only codecs which are not CPU-intensive:
# exclude_payloads=iLBC;speex;
# only use G711 (exclude everything else):
# exclude_payloads=iLBC;speex;G726-40;G726-32;G721;G726-24;G726-16;GSM;L16
############################################################
# logging and running
# optional parameter: fork={yes|no}
#
# - specifies if sems should run in daemon mode (background)
# (fork=no is the same as -E)
fork=yes
# optional parameter: stderr={yes|no}
#
# - debug mode: do not fork and log to stderr
# (stderr=yes is the same as -E)
stderr=no
# optional parameter: loglevel={0|1|2|3}
#
# - sets log level (error=0, warning=1, info=2, debug=3)
# (same as -D)
loglevel=2
# optional parameter: syslog_facility={DAEMON|USER|LOCAL[0-7]}
#
# - sets the log facility that is used for syslog. Using this,
# the log can for example be filtered into a special file
# by the syslog daemon.
#
# Default: DAEMON
#
# Example:
# syslog_facility=LOCAL0
# optional parameter: log_sessions=[yes|no]
#
# Default: no
#
# If log_sessions=yes is set, INFO level log messages are generated
# for each session when it is started and stopped.
#
# log_sessions=yes
# optional parameter: log_events=[yes|no]
#
# Default: no
#
# If log_eventy=yes is set, generic DBG level log messages are
# generated for each event that is posted into an event queue.
#
# log_events=yes
# optional parameter: max_shutdown_time=<time in seconds>
#
# Limit on server shutdown time (time to send/resend BYE
# to active calls). 0 to disable (infinite).
#
# Default: 10
#
#max_shutdown_time = 10
# optional parameter: shutdown_mode_reply="<code> <reason>"
#
# Error reply that is used as reply to INVITE and OPTION
# when SEMS is shutting down.
#
# Default: shutdown_mode_reply="503 Server shutting down"
############################################################
# tuning
# optional parameter: session_processor_threads=<num_value>
#
# - controls how many threads should be created that
# process the application logic and in-dialog signaling.
# This is only available if compiled with threadpool support!
# (set USE_THREADPOOL in Makefile.defs)
# Defaults to 10
#
# session_processor_threads=50
# optional parameter: media_processor_threads=<num_value>
#
# - controls how many threads should be created that
# process media - on single-processor systems set this
# parameter to 1 (default), on MP systems to a higher
# value
#
# media_processor_threads=1
# optional parameter: session_limit=<limit>;<err code>;<err reason>
#
# - this sets a maximum active session limit. If that limit is
# reached, no further calls are accepted, but the error reply
# with err code/err reason is sent out.
#
# Default: 0 (None)
#
# Example:
# session_limit="1000;503;Server overload"
# optional parameter: options_session_limit=<limit>;<err code>;<err reason>
#
# - this sets a custom response to OPTIONS, if the session count reaches
# a certain limit. This way health monitor could raise an alarm to syste
# administrator.
#
# Default: 0 (None)
#
# Example:
# options_session_limit="900;503;Warning, server soon overloaded"
# optional parameter: dead_rtp_time=<unsigned int>
#
# - if != 0, after this time (in seconds) of no RTP
# a session is considered dead and stopped. If set
# to 0 no check is done for rtp timeout.
#
# default=300 (5 minutes)
#
# Examples:
# # disable RTP timeout
# dead_rtp_time=0
# # RTP timeout after 10 seconds
# dead_rtp_time=10
# optional parameter: use_default_signature={yes|no}
#
# - use a Server/User-Agent header with the SEMS server
# signature and version.
# Set server_signature=0 in ser_sems.cfg if you use SER
# as SIP stack.
#
# default=no
#
use_default_signature=yes
# optional parameter: signature=<signature string>
#
# - use a Server/User-Agent header with a custom user agent
# signature.
# Overridden by default signature if
# use_default_signature is set.
# Set server_signature=0 in ser_sems.cfg if you use it.
#
#
# signature="SEMS media server 1.0"
# optional parameter: single_codec_in_ok={yes|no}
#
# - use single codec in 200 OK response
#
# default=no
#
# single_codec_in_ok=no
# optional parameter: codec_order=codec_name_1,codec_name2,...
#
# - Codec order used when sending INVITE requests. Codecs in codec_order
# will be on the top of the list followed by other supported codecs
# (if any).
#
# default=empty
#
# codec_order=iLBC,GSM
# optional parameter: ignore_rtpxheaders={yes|no}
#
# - if this is set to yes, RTP extension headers (e.g. when using ZRTP)
# are ignored. If set to no, the whole RTP packets with extension
# headers will be ignored and a debug message is printed on every
# received packet.
#
# default=no
#
# ignore_rtpxheaders=yes
# optional parameter: dtmf_detector={spandsp|internal}
#
# sets inband DTMF detector to use. spandsp support must be compiled in
# for this to have effect if dtmf_detector=spandsp.
#
# default: internal
#
# dtmf_detector=spandsp
# optional parameter: unhandled_reply_loglevel={error|warn|info|debug|no}
#
# the default application logic implemented in the applications is to stop
# the session right after sending BYE, without waiting for a reply. this
# leads to many log entries of the form
# ERROR: [b6fa6bb0] handleSipMsg (AmSipDispatcher.cpp:48): unhandled
# reply: [code:200;phrase:[OK];... ]
#
# This parameter sets the log lovel of unhandled positive (200 class) replies.
#
# default: error
#
# unhandled_reply_loglevel=info
############################################################
# SIP stack settings
# default settings (i.e. leave out) for these should be OK
# for most applications
# skip DNS SRV lookup? [yes, no]
#
# according to RFC, if no port is specified, destination IP address
# should be resolved with a DNS SRV lookup. If SEMS should not do that
# (only an A record lookup), set disable_dns_srv=yes.
#
# Default: no
#
#disable_dns_srv=yes
# support 100rel (PRACK) extension (RFC3262)? [disabled|supported|require]
#
# disabled - disable support for 100rel
# supported - support it if remote end does, default
# require - required
#
# Default: supported
#
#100rel=require
#
# accept forked dialogs on UAS side? [yes|no]
#
# no - INVITE with existing callid+remote_tag is replied with 482.
# yes - INVITE with existing callid+remote_tag+via_branch is replied with 482.
# Forked INVITEs (!= via-branch) are accepted.
#
# Default: yes
#
#accept_forked_dialogs=no
# Make SIP authenticated requests sticky to the proxy? [yes | no]
#
# If enabled, host of request-URI of out-of-dialog requests that are
# authenticated with SIP auth is changed to the previously resolved
# next-hop IP:port.
#
# default: no
#
# proxy_sticky_auth=yes
# Ignore too low CSeq for NOTIFYs? [yes | no]
#
# May be necessary to interwork with simplistic/old SIP event notification
# implementations.
#
#ignore_notify_lower_cseq=yes
#
# Accept final replies without To-tag? [yes|no]
#
accept_fr_without_totag=yes
#
# Log raw messages? [no|debug|info|warn|error]
#
# Default: debug
#
#log_raw_messages=no
#
# Log parsed received messages? [yes|no]
#
# Default: yes
#
#log_parsed_messages=no
# SIP UDP socket receive buffer size (in bytes)
#
# if not set, system default is used (which usually
# is modest). set sytem wide upper limit with
# e.g. sysctl -w net.core.rmem_max=8388608
#
# udp_rcvbuf = <value>
# Number of SIP UDP receiver threads
#
# Default: 4
#
# sip_server_threads=8
sip_ip=192.168.5.110
sip_port=5090
load_plugins=uac_auth;db_reg_agent;xmlrpc2di;callgen;wav
plugin_config_path=etc/
plugin_path=lib/
application=callgen
#codec_order=PCMU,PCMA
max_shutdown_time = 2

@ -1,42 +0,0 @@
# server_ip : IP to bind XMLRPC server to
# leave empty for ANY interface
#server_ip=127.0.0.1
# port to bind XMLRPC server to
xmlrpc_port=8092
# run multi-threaded server?
# Default: yes
#
# multithreaded = yes
# threads to run - this many requests can be processed in parallel
# Default: 5
#
# threads=5
# export all DI functions with the function call 'di'?
# defaults to: yes
# export_di=yes
#
# these DI interfaces are searched for functions to
# export under their proper function names
# defaults to: none
# direct_export=di_dial;registrar_client
direct_export=db_reg_agent;di_dial;callgen
# run the XMLRPC server at all (default: yes)
#
# run_server=yes
# timeout for client requests, in milliseconds (0 to disable)
#
# server_timeout=500
# print parameters of XMLRPC server calls into debug log [yes|no]
# debug_server_params=yes
#
# print result of XMLRPC server calls into debug log [yes|no]
# debug_server_result=yes
#

@ -1,203 +0,0 @@
--
-- This DSM app
-- o plays early media from a file in the DB
-- o sends a HTTP request to a web server
-- o SUBSCRIBEs to registration state
-- o if registration becomes active (at least one active contact), it sends back "300 Multiple Choices"
-- o otherwise (timeout) it sends back "480 Not found"
-- Parameters (P-App-Param):
-- audio_id - indicating audio file in DB
-- expiration - subscription expiration/waiting time (default: 60)
-- caller - caller passed to notification web app
-- callee - callee passed to notification web app
-- domain - domain passed to notification web app
--
-- Example: P-App-Param: audio_id=rbt;expiration=30;caller=+43111111111;callee=+432222222222;domain=sip.sipwise.com
--
-- Example DB:
-- CREATE TABLE `audio_files` (
-- `id` varchar(20) DEFAULT NULL,
-- `data` mediumblob
-- ) ENGINE=MyISAM DEFAULT CHARSET=latin1;
import(mod_utils);
import(mod_dlg);
import(mod_subscription);
import(mod_xml);
import(mod_mysql);
import(mod_curl);
initial state START;
transition "SIP dialog error" START - exception; test(#type=="dlg") / {
log(1, "Error in accepting call:");
logParams(1);
set($connect_session=0);
stop(false);
} -> END;
transition "DB exception" START - exception / {
log(1, "Error in initializing mobile push:");
logAll(1);
dlg.reply(500, "Server Internal Error");
stop(false);
} -> END;
transition "invite" START - invite / {
if test($config.enable_rbt=="yes") {
if test($audio_id != "") {
mysql.connect();
throwOnError();
-- play waiting file from DB
if test($config.play_looped=="yes") {
mysql.playDBAudioLooped(SELECT data FROM provisioning.audio_files WHERE id="$audio_id", rbt.wav);
} else {
mysql.playDBAudio(SELECT data FROM provisioning.audio_files WHERE id="$audio_id", rbt.wav);
}
throwOnError();
mysql.disconnect();
}
}
-- send 183 with early media
dlg.acceptInvite(183, "Progress");
throwOnError();
-- no default 200 OK
set($connect_session=0);
-- start RTP processing
connectMedia();
-- no need to process incoming RTP (also no RTP timeout needed)
disableReceiving();
-- create subscription to reg event
set($r.domain=@domain);
set($r.user=@user);
set($r.from_user=$config.subscription_user);
set($r.pwd=$config.subscription_pwd);
set($r.event="reg");
if test($config.proxy != "") {
set($r.proxy=$config.proxy);
}
if test($config.use_subscription_id == "yes") {
utils.getNewId(r.id);
}
if test($expiration != "") {
set($r.expires=$expiration);
} else {
set($r.expires=60);
}
subscription.create(r);
if test($r.handle == "") {
log(1, "Subscription creation failed!");
logVars(1);
dlg.reply(500, "Server Internal Error");
stop(false);
throw(subscription);
}
-- send HTTP request
set($curl.timeout=15);
if test($config.use_post=="yes") {
curl.postDiscardResult($config.push_server_url, $caller;$callee;$domain)
} else {
curl.getForm($config.push_server_url, $caller;$callee;$domain)
}
-- set Timer on our side, too (server subscription timing could be broken...)
setTimer(1, $r.expires);
} -> WAITING;
state WAITING;
transition "RTP timeout - ignore" WAITING - rtpTimeout / set(#processed=true) -> WAITING;
transition "subscription failed" WAITING - subscription(#status=="failed") / {
log(1, "Subscription failed:");
logParams(1);
logVars(1);
dlg.reply(500, "Server Internal Error");
stop(false);
} -> END;
function evaluateRegNotifyBody() {
xml.parseSIPMsgBody("SipSubscriptionBody", "substatus");
-- namespaces to be used:
set($substatus.ns="a=urn:ietf:params:xml:ns:reginfo")
-- look for an active contact
xml.evalXPath("/a:reginfo/a:registration/a:contact[@state='active']", "substatus");
xml.XPathResultCount($active_contacts="substatus.xpath");
};
function releaseRegEvalObjects() {
freeObject("substatus");
freeObject("substatus.xpath");
};
function replyWith300() {
if test($has_replied!="yes") {
set($dlg.reply.hdrs="Contact: ");
append($dlg.reply.hdrs, @local_uri);
append($dlg.reply.hdrs, $config.extra_3xx_uri_append);
dlg.reply(300, "Multiple Choices");
set($has_replied="yes");
}
stop(false);
};
function replyWith480() {
if test($has_replied!="yes") {
dlg.reply(480, "Not found");
set($has_replied="yes");
}
stop(false);
};
transition "NOTIFY received, with body" WAITING - subscription(#status!="terminated"); test(#has_body=="true") / {
evaluateRegNotifyBody();
if test($active_contacts != 0) {
replyWith300();
subscription.remove($r.handle);
}
releaseRegEvalObjects();
} -> WAITING;
transition "NOTIFY received, no body" WAITING - subscription(#status!="terminated") / {
log(3, "subscription active");
logParams(3);
} -> WAITING;
transition "NOTIFY received, terminated (but with body)" WAITING - subscription(#status=="terminated"); test(#has_body=="true") / {
evaluateRegNotifyBody();
if test($active_contacts != 0) {
replyWith300();
} else {
replyWith480();
}
releaseRegEvalObjects();
} -> END;
transition "NOTIFY received, subscription terminated" WAITING - subscription(#status=="terminated") / {
replyWith480();
} -> END;
transition "timer hit" WAITING - timer(#id==1) / {
subscription.remove($r.handle);
replyWith480();
} -> END;
transition "CANCEL received" WAITING - hangup / {
subscription.remove($r.handle);
dlg.reply(487, "Request Terminated");
stop(false);
} -> END;
state END;

@ -2,13 +2,5 @@ set (monitoring_SRCS
Monitoring.cpp
)
INSTALL(PROGRAMS
tools/sems-list-calls
tools/sems-list-active-calls
tools/sems-list-finished-calls
tools/sems-get-callproperties
DESTINATION ${SEMS_EXEC_PREFIX}/sbin
)
SET(sems_module_name monitoring)
INCLUDE(${CMAKE_SOURCE_DIR}/cmake/module.rules.txt)

@ -3,10 +3,5 @@ plug_in_name = monitoring
module_ldflags =
module_cflags = -DMOD_NAME=\"$(plug_in_name)\"
extra_install = install_tools
COREPATH ?=../../core
include $(COREPATH)/plug-in/Makefile.app_module
install_tools:
-@$(MAKE) -C tools/ install

@ -1,15 +0,0 @@
COREPATH_TOOLS ?= ../../../core
include $(COREPATH_TOOLS)/../Makefile.defs
monitoring_scripts = $(wildcard sems-*)
all: install_tools
install: install_tools
install_tools: $(DESTDIR)$(bin-prefix)/$(bin-dir)
-@for r in $(monitoring_scripts) ; do \
$(INSTALL-TOUCH) $(DESTDIR)$(bin-prefix)/$(bin-dir)/$$r ; \
$(INSTALL-BIN) $$r $(DESTDIR)$(bin-prefix)/$(bin-dir) ; \
done

@ -1,12 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
from xmlrpclib import *
import sys
if len(sys.argv) != 2:
print "usage: %s <ltag/ID of call to list>" % sys.argv[0]
sys.exit(1)
s = ServerProxy('http://localhost:8090')
print "Active calls: %d" % s.calls()
print s.di('monitoring','get', sys.argv[1])

@ -1,7 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
from xmlrpclib import *
s = ServerProxy('http://localhost:8090')
print "Active calls: %d" % s.calls()
print s.di('monitoring','listActive')

@ -1,7 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
from xmlrpclib import *
s = ServerProxy('http://localhost:8090')
print "Active calls: %d" % s.calls()
print s.di('monitoring','list')

@ -1,7 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
from xmlrpclib import *
s = ServerProxy('http://localhost:8090')
print "Active calls: %d" % s.calls()
print s.di('monitoring','listFinished')

@ -42,7 +42,6 @@
#define CFG_PARAM_AUTH "auth_user"
#define CFG_PARAM_PASS "pwd"
#define CFG_PARAM_PROXY "proxy"
#define CFG_PARAM_CONTACT "contact"
#define MAX_ACCOUNTS 100
@ -73,7 +72,6 @@ int RegistrationAgentFactory::onLoad()
ri.auth_user = cfg.getParameter(CFG_PARAM_AUTH+idx_str,"");
ri.passwd = cfg.getParameter(CFG_PARAM_PASS+idx_str,"");
ri.proxy = cfg.getParameter(CFG_PARAM_PROXY+idx_str,"");
ri.contact = cfg.getParameter(CFG_PARAM_CONTACT+idx_str,"");
if (!ri.domain.length() || !ri.user.length()) {
// not including the passwd: might be IP based registration
@ -87,9 +85,9 @@ int RegistrationAgentFactory::onLoad()
ri.auth_user = ri.user;
dialer.add_reg(ri);
DBG("Adding registration account #%d (%s %s %s %s %s %s)\n", i,
DBG("Adding registration account #%d (%s %s %s %s %s)\n", i,
ri.domain.c_str(), ri.user.c_str(), ri.display_name.c_str(),
ri.auth_user.c_str(), ri.proxy.c_str(), ri.contact.c_str());
ri.auth_user.c_str(), ri.proxy.c_str());
i ++;
idx_str = int2str(i);
@ -132,11 +130,10 @@ void RegThread::create_registration(RegInfo& ri) {
di_args.push(ri.domain.c_str());
di_args.push(ri.user.c_str());
di_args.push(ri.display_name.c_str()); // display name
di_args.push(ri.auth_user.c_str()); // auth_user
di_args.push(ri.passwd.c_str()); // pwd
di_args.push("reg_agent"); //sess_link
di_args.push(ri.proxy.c_str());
di_args.push(ri.contact.c_str());
di_args.push(ri.auth_user.c_str()); // auth_user
di_args.push(ri.passwd.c_str()); // pwd
di_args.push("reg_agent"); //sess_link
di_args.push(ri.proxy.c_str());
uac_auth_i->invoke("createRegistration", di_args, reg_handle);
if (reg_handle.size())

@ -41,7 +41,6 @@ struct RegInfo {
string auth_user;
string passwd;
string proxy;
string contact;
string handle;
};

@ -15,8 +15,6 @@
#auth_user=myuser
# optional (defaults to resolved by domain):
#proxy=sip.mydomain.net:5060
# optional (default to <user>@<publicip/localip>):
#contact=sip:myuser@10.0.0.2
#
# For multiple registrations add more entries
@ -28,7 +26,6 @@
#display_name1=xyz
#auth_user1=xyz
#proxy1=sip.iptel.org:5060
#contact1=sip:xyz@10.0.0.3
#domain2=iptel.org
#user2=xyz

@ -33,6 +33,8 @@
#define MOD_NAME "registrar_client"
#define REGISTER_SEND_TIMEOUT 60
#include <unistd.h>
//EXPORT_SIP_EVENT_HANDLER_FACTORY(SIPRegistrarClient, MOD_NAME);
@ -46,6 +48,142 @@ extern "C" void* plugin_class_create()
return (AmPluginFactory*)reg_c;
}
SIPRegistration::SIPRegistration(const string& handle,
const SIPRegistrationInfo& info,
const string& sess_link)
: info(info),
dlg(this),
cred(info.domain, info.auth_user, info.pwd),
active(false),
reg_begin(0),
reg_expires(0),
remove(false),
sess_link(sess_link),
reg_send_begin(0),
waiting_result(false),
seh(NULL)
{
req.cmd = "sems";
req.user = info.user;
req.method = "REGISTER";
req.r_uri = "sip:"+info.domain;
req.from = info.name+" <sip:"+info.user+"@"+info.domain+">";
req.from_uri = "sip:"+info.user+"@"+info.domain;
req.from_tag = handle;
req.to = req.from;
req.to_tag = "";
req.callid = AmSession::getNewId();
//
// clear dlg.callid? ->reregister?
dlg.updateStatusFromLocalRequest(req);
dlg.cseq = 50;
}
SIPRegistration::~SIPRegistration() {
setSessionEventHandler(NULL);
}
void SIPRegistration::setSessionEventHandler(AmSessionEventHandler* new_seh) {
if (seh)
delete seh;
seh = new_seh;
}
void SIPRegistration::doRegistration()
{
waiting_result = true;
req.to_tag = "";
dlg.remote_tag = "";
req.r_uri = "sip:"+info.domain;
dlg.remote_uri = req.r_uri;
// set outbound proxy as next hop
if (!info.proxy.empty()) {
dlg.outbound_proxy = info.proxy;
} else if (!AmConfig::OutboundProxy.empty())
dlg.outbound_proxy = AmConfig::OutboundProxy;
//else
// dlg.outbound_proxy = "";
if (dlg.sendRequest(req.method, "", "", "Expires: 1000\n") < 0)
ERROR("failed to send registration.\n");
// save TS
struct timeval now;
gettimeofday(&now, NULL);
reg_send_begin = now.tv_sec;
}
void SIPRegistration::doUnregister()
{
waiting_result = true;
req.to_tag = "";
dlg.remote_tag = "";
req.r_uri = "sip:"+info.domain;
dlg.remote_uri = req.r_uri;
// set outbound proxy as next hop
if (!info.proxy.empty()) {
dlg.outbound_proxy = info.proxy;
} else if (!AmConfig::OutboundProxy.empty())
dlg.outbound_proxy = AmConfig::OutboundProxy;
//else
// dlg.outbound_proxy = "";
if (dlg.sendRequest(req.method, "", "", "Expires: 0\n") < 0)
ERROR("failed to send deregistration.\n");
// save TS
struct timeval now;
gettimeofday(&now, NULL);
reg_send_begin = now.tv_sec;
}
void SIPRegistration::onSendRequest(const string& method,
const string& content_type,
const string& body,
string& hdrs,
int flags,
unsigned int cseq) {
if (seh)
seh->onSendRequest(method, content_type, body,
hdrs,flags,cseq);
}
void SIPRegistration::onSendReply(const AmSipRequest& req,
unsigned int code,
const string& reason,
const string& content_type,
const string& body,
string& hdrs,
int flags) {
if (seh)
seh->onSendReply(req,code,reason,
content_type,body,hdrs,flags);
}
SIPRegistration::RegistrationState SIPRegistration::getState() {
if (active)
return RegisterActive;
if (waiting_result)
return RegisterPending;
return RegisterExpired;
}
unsigned int SIPRegistration::getExpiresLeft() {
struct timeval now;
gettimeofday(&now, NULL);
int diff = reg_begin + reg_expires - now.tv_sec;
if (diff < 0)
return 0;
else
return diff;
}
//-----------------------------------------------------------
SIPRegistrarClient* SIPRegistrarClient::_instance=0;
@ -91,6 +229,31 @@ void SIPRegistrarClient::run() {
}
}
}
void SIPRegistration::onRegisterExpired() {
if (sess_link.length()) {
AmSessionContainer::instance()->postEvent(sess_link,
new SIPRegistrationEvent(SIPRegistrationEvent::RegisterTimeout,
req.from_tag));
}
DBG("Registration '%s' expired.\n", (info.user+"@"+info.domain).c_str());
active = false;
remove = true;
}
void SIPRegistration::onRegisterSendTimeout() {
if (sess_link.length()) {
AmSessionContainer::instance()->postEvent(sess_link,
new SIPRegistrationEvent(SIPRegistrationEvent::RegisterSendTimeout,
req.from_tag));
}
DBG("Registration '%s' REGISTER request timeout.\n",
(info.user+"@"+info.domain).c_str());
active = false;
remove = true;
}
void SIPRegistrarClient::checkTimeouts() {
// DBG("checking timeouts...\n");
@ -99,11 +262,11 @@ void SIPRegistrarClient::checkTimeouts() {
reg_mut.lock();
vector<string> remove_regs;
for (map<string, AmSIPRegistration*>::iterator it = registrations.begin();
for (map<string, SIPRegistration*>::iterator it = registrations.begin();
it != registrations.end(); it++) {
if (it->second->active) {
if (it->second->registerExpired(now.tv_sec)) {
AmSIPRegistration* reg = it->second;
SIPRegistration* reg = it->second;
reg->onRegisterExpired();
} else if (!it->second->waiting_result &&
it->second->timeToReregister(now.tv_sec)) {
@ -113,14 +276,14 @@ void SIPRegistrarClient::checkTimeouts() {
remove_regs.push_back(it->first);
} else if (it->second->waiting_result &&
it->second->registerSendTimeout(now.tv_sec)) {
AmSIPRegistration* reg = it->second;
SIPRegistration* reg = it->second;
reg->onRegisterSendTimeout();
}
}
for (vector<string>::iterator it = remove_regs.begin();
it != remove_regs.end(); it++) {
DBG("removing registration\n");
AmSIPRegistration* reg = registrations[*it];
SIPRegistration* reg = registrations[*it];
registrations.erase(*it);
if (reg)
delete reg;
@ -137,7 +300,7 @@ int SIPRegistrarClient::onLoad() {
void SIPRegistrarClient::onServerShutdown() {
// TODO: properly wait until unregistered, with timeout
DBG("shutdown SIP registrar client: deregistering\n");
for (std::map<std::string, AmSIPRegistration*>::iterator it=
for (std::map<std::string, SIPRegistration*>::iterator it=
registrations.begin(); it != registrations.end(); it++) {
it->second->doUnregister();
AmEventDispatcher::instance()->delEventQueue(it->first);
@ -184,16 +347,116 @@ void SIPRegistrarClient::process(AmEvent* ev)
}
void SIPRegistrarClient::onSipReplyEvent(AmSipReplyEvent* ev) {
AmSIPRegistration* reg = get_reg(ev->reply.local_tag);
SIPRegistration* reg = get_reg(ev->reply.local_tag);
if (reg != NULL) {
reg->getDlg()->updateStatus(ev->reply);//onSipReply(ev->reply);
}
}
bool SIPRegistration::registerSendTimeout(time_t now_sec) {
return now_sec > reg_send_begin + REGISTER_SEND_TIMEOUT;
}
bool SIPRegistration::timeToReregister(time_t now_sec) {
// if (active)
// DBG("compare %lu with %lu\n",(reg_begin+reg_expires), (unsigned long)now_sec);
return (((unsigned long)reg_begin+ reg_expires/2) < (unsigned long)now_sec);
}
bool SIPRegistration::registerExpired(time_t now_sec) {
return ((reg_begin+reg_expires) < (unsigned int)now_sec);
}
void SIPRegistration::onSipReply(const AmSipReply& reply, int old_dlg_status, const string& trans_method)
{
if ((seh!=NULL) && seh->onSipReply(reply, old_dlg_status, trans_method))
return;
waiting_result = false;
if ((reply.code>=200)&&(reply.code<300)) {
DBG("positive reply to REGISTER!\n");
size_t end = 0;
string local_contact_hdr = dlg.getContactHdr();
local_contact.parse_contact(local_contact_hdr, (size_t)0, end);
local_contact.dump();
string contacts = reply.contact;
if (contacts.empty())
contacts = getHeader(reply.hdrs, "Contact", "m", true);
bool found = false;
if (!contacts.length()) {
DBG("received positive reply to de-Register \n");
active = false;
remove = true;
} else {
end = 0;
while (!found) {
if (contacts.length() == end)
break;
if (!server_contact.parse_contact(contacts, end, end)) {
ERROR("while parsing contact\n");
break;
}
server_contact.dump();
if (server_contact.isEqual(local_contact)) {
DBG("contact found\n");
found = active = true;
//bool str2i(const string& str, unsigned int& result);
if (str2i(server_contact.params["expires"], reg_expires)) {
ERROR("could not extract expires value.\n");
reg_expires = 500;
}
DBG("got an expires of %d\n", reg_expires);
// save TS
struct timeval now;
gettimeofday(&now, NULL);
reg_begin = now.tv_sec;
if (sess_link.length()) {
DBG("posting SIPRegistrationEvent to '%s'\n", sess_link.c_str());
AmSessionContainer::instance()->postEvent(sess_link,
new SIPRegistrationEvent(SIPRegistrationEvent::RegisterSuccess,
req.from_tag,
reply.code, reply.reason));
}
break;
}
}
}
if (!found) {
if (sess_link.length()) {
AmSessionContainer::instance()->postEvent(sess_link,
new SIPRegistrationEvent(SIPRegistrationEvent::RegisterNoContact,
req.from_tag,
reply.code, reply.reason));
}
DBG("no matching Contact - deregistered.\n");
active = false;
remove = true;
}
} else if (reply.code >= 300) {
DBG("Registration failed.\n");
if (sess_link.length()) {
AmSessionContainer::instance()->postEvent(sess_link,
new SIPRegistrationEvent(SIPRegistrationEvent::RegisterFailed,
req.from_tag,
reply.code, reply.reason));
}
active = false;
remove = true;
}
}
void SIPRegistrarClient::onNewRegistration(SIPNewRegistrationEvent* new_reg) {
AmSIPRegistration* reg = new AmSIPRegistration(new_reg->handle, new_reg->info,
new_reg->sess_link);
SIPRegistration* reg = new SIPRegistration(new_reg->handle, new_reg->info,
new_reg->sess_link);
if (uac_auth_i != NULL) {
DBG("enabling UAC Auth for new registration.\n");
@ -224,7 +487,7 @@ void SIPRegistrarClient::onNewRegistration(SIPNewRegistrationEvent* new_reg) {
}
void SIPRegistrarClient::onRemoveRegistration(SIPRemoveRegistrationEvent* new_reg) {
AmSIPRegistration* reg = get_reg(new_reg->handle);
SIPRegistration* reg = get_reg(new_reg->handle);
if (reg)
reg->doUnregister();
}
@ -247,13 +510,13 @@ bool SIPRegistrarClient::hasRegistration(const string& handle) {
return get_reg(handle) != NULL;
}
AmSIPRegistration* SIPRegistrarClient::
SIPRegistration* SIPRegistrarClient::
get_reg(const string& reg_id)
{
DBG("get registration '%s'\n", reg_id.c_str());
AmSIPRegistration* res = NULL;
SIPRegistration* res = NULL;
reg_mut.lock();
map<string, AmSIPRegistration*>::iterator it =
map<string, SIPRegistration*>::iterator it =
registrations.find(reg_id);
if (it!=registrations.end())
res = it->second;
@ -262,12 +525,12 @@ get_reg(const string& reg_id)
return res;
}
AmSIPRegistration* SIPRegistrarClient::
SIPRegistration* SIPRegistrarClient::
get_reg_unsafe(const string& reg_id)
{
// DBG("get registration_unsafe '%s'\n", reg_id.c_str());
AmSIPRegistration* res = NULL;
map<string, AmSIPRegistration*>::iterator it =
SIPRegistration* res = NULL;
map<string, SIPRegistration*>::iterator it =
registrations.find(reg_id);
if (it!=registrations.end())
res = it->second;
@ -275,19 +538,19 @@ get_reg_unsafe(const string& reg_id)
return res;
}
AmSIPRegistration* SIPRegistrarClient::
SIPRegistration* SIPRegistrarClient::
remove_reg(const string& reg_id) {
reg_mut.lock();
AmSIPRegistration* reg = remove_reg_unsafe(reg_id);
SIPRegistration* reg = remove_reg_unsafe(reg_id);
reg_mut.unlock();
return reg;
}
AmSIPRegistration* SIPRegistrarClient::
SIPRegistration* SIPRegistrarClient::
remove_reg_unsafe(const string& reg_id) {
DBG("removing registration '%s'\n", reg_id.c_str());
AmSIPRegistration* reg = NULL;
map<string, AmSIPRegistration*>::iterator it =
SIPRegistration* reg = NULL;
map<string, SIPRegistration*>::iterator it =
registrations.find(reg_id);
if (it!=registrations.end()) {
reg = it->second;
@ -300,12 +563,12 @@ remove_reg_unsafe(const string& reg_id) {
}
void SIPRegistrarClient::
add_reg(const string& reg_id, AmSIPRegistration* new_reg)
add_reg(const string& reg_id, SIPRegistration* new_reg)
{
DBG("adding registration '%s' (this = %ld)\n", reg_id.c_str(), (long)this);
AmSIPRegistration* reg = NULL;
SIPRegistration* reg = NULL;
reg_mut.lock();
map<string, AmSIPRegistration*>::iterator it =
map<string, SIPRegistration*>::iterator it =
registrations.find(reg_id);
if (it!=registrations.end()) {
reg = it->second;
@ -329,14 +592,13 @@ string SIPRegistrarClient::createRegistration(const string& domain,
const string& auth_user,
const string& pwd,
const string& sess_link,
const string& proxy,
const string& contact) {
const string& proxy) {
string handle = AmSession::getNewId();
instance()->
postEvent(new SIPNewRegistrationEvent(SIPRegistrationInfo(domain, user,
name, auth_user, pwd,
proxy, contact),
proxy),
handle, sess_link));
return handle;
}
@ -353,7 +615,7 @@ bool SIPRegistrarClient::getRegistrationState(const string& handle,
bool res = false;
reg_mut.lock();
AmSIPRegistration* reg = get_reg_unsafe(handle);
SIPRegistration* reg = get_reg_unsafe(handle);
if (reg) {
res = true;
state = reg->getState();
@ -367,7 +629,7 @@ bool SIPRegistrarClient::getRegistrationState(const string& handle,
void SIPRegistrarClient::listRegistrations(AmArg& res) {
reg_mut.lock();
for (map<string, AmSIPRegistration*>::iterator it =
for (map<string, SIPRegistration*>::iterator it =
registrations.begin(); it != registrations.end(); it++) {
AmArg r;
r["handle"] = it->first;
@ -377,7 +639,6 @@ void SIPRegistrarClient::listRegistrations(AmArg& res) {
r["auth_user"] = it->second->getInfo().auth_user;
r["proxy"] = it->second->getInfo().proxy;
r["event_sink"] = it->second->getEventSink();
r["contact"] = it->second->getInfo().contact;
res.push(r);
}
@ -389,11 +650,9 @@ void SIPRegistrarClient::invoke(const string& method, const AmArg& args,
AmArg& ret)
{
if(method == "createRegistration"){
string proxy, contact;
string proxy;
if (args.size() > 6)
proxy = args.get(6).asCStr();
if (args.size() > 7)
contact = args.get(7).asCStr();
ret.push(createRegistration(args.get(0).asCStr(),
args.get(1).asCStr(),
@ -401,7 +660,7 @@ void SIPRegistrarClient::invoke(const string& method, const AmArg& args,
args.get(3).asCStr(),
args.get(4).asCStr(),
args.get(5).asCStr(),
proxy, contact
proxy
).c_str());
}
else if(method == "removeRegistration"){

@ -28,8 +28,12 @@
#ifndef RegisterClient_h
#define RegisterClient_h
#include "AmSipRegistration.h"
#include "AmApi.h"
#include "AmSession.h"
#include "ContactInfo.h"
#include "ampi/SIPRegistrarClientAPI.h"
#include "ampi/UACAuthAPI.h"
#include <sys/time.h>
@ -38,6 +42,119 @@
using std::map;
using std::string;
struct SIPRegistrationInfo {
string domain;
string user;
string name;
string auth_user;
string pwd;
string proxy;
SIPRegistrationInfo(const string& domain,
const string& user,
const string& name,
const string& auth_user,
const string& pwd,
const string& proxy)
: domain(domain),user(user),name(name),
auth_user(auth_user),pwd(pwd),proxy(proxy)
{ }
};
class SIPRegistration : public AmSipDialogEventHandler,
public DialogControl,
public CredentialHolder
{
AmSipDialog dlg;
UACAuthCred cred;
SIPRegistrationInfo info;
// session to post events to
string sess_link;
AmSessionEventHandler* seh;
AmSipRequest req;
ContactInfo server_contact;
ContactInfo local_contact;
time_t reg_begin;
unsigned int reg_expires;
time_t reg_send_begin;
public:
SIPRegistration(const string& handle,
const SIPRegistrationInfo& info,
const string& sess_link);
~SIPRegistration();
void setSessionEventHandler(AmSessionEventHandler* new_seh);
void doRegistration();
void doUnregister();
inline bool timeToReregister(time_t now_sec);
inline bool registerExpired(time_t now_sec);
void onRegisterExpired();
void onRegisterSendTimeout();
inline bool registerSendTimeout(time_t now_sec);
void onSendRequest(const string& method,
const string& content_type,
const string& body,
string& hdrs,
int flags,
unsigned int cseq);
void onSendReply(const AmSipRequest& req,
unsigned int code,
const string& reason,
const string& content_type,
const string& body,
string& hdrs,
int flags);
// DialogControl if
AmSipDialog* getDlg() { return &dlg; }
// CredentialHolder
UACAuthCred* getCredentials() { return &cred; }
void onSipReply(const AmSipReply& reply, int old_dlg_status, const string& trans_method);
void onSipRequest(const AmSipRequest& req) {}
void onInvite2xx(const AmSipReply&) {}
void onNoAck(unsigned int) {}
void onNoPrack(const AmSipRequest &, const AmSipReply &) {}
void onInvite1xxRel(const AmSipReply &){}
void onPrack2xx(const AmSipReply &){}
void onFailure(AmSipDialogEventHandler::FailureCause cause,
const AmSipRequest*, const AmSipReply*){}
/** is this registration registered? */
bool active;
/** should this registration be removed from container? */
bool remove;
/** are we waiting for the response to a register? */
bool waiting_result;
enum RegistrationState {
RegisterPending = 0,
RegisterActive,
RegisterExpired
};
/** return the state of the registration */
RegistrationState getState();
/** return the expires left for the registration */
unsigned int getExpiresLeft();
SIPRegistrationInfo& getInfo() { return info; }
const string& getEventSink() { return sess_link; }
};
struct SIPNewRegistrationEvent;
class SIPRemoveRegistrationEvent;
@ -49,14 +166,14 @@ class SIPRegistrarClient : public AmThread,
{
// registrations container
AmMutex reg_mut;
std::map<std::string, AmSIPRegistration*> registrations;
std::map<std::string, SIPRegistration*> registrations;
void add_reg(const string& reg_id,
AmSIPRegistration* new_reg);
AmSIPRegistration* remove_reg(const string& reg_id);
AmSIPRegistration* remove_reg_unsafe(const string& reg_id);
AmSIPRegistration* get_reg(const string& reg_id);
AmSIPRegistration* get_reg_unsafe(const string& reg_id);
SIPRegistration* new_reg);
SIPRegistration* remove_reg(const string& reg_id);
SIPRegistration* remove_reg_unsafe(const string& reg_id);
SIPRegistration* get_reg(const string& reg_id);
SIPRegistration* get_reg_unsafe(const string& reg_id);
void onSipReplyEvent(AmSipReplyEvent* ev);
void onNewRegistration(SIPNewRegistrationEvent* new_reg);
@ -79,8 +196,7 @@ class SIPRegistrarClient : public AmThread,
void invoke(const string& method,
const AmArg& args, AmArg& ret);
bool onSipReply(const AmSipReply& rep, int old_dlg_status,
const string& trans_method);
bool onSipReply(const AmSipReply& rep, int old_dlg_status, const string& trans_method);
int onLoad();
void run();
@ -95,8 +211,7 @@ class SIPRegistrarClient : public AmThread,
const string& auth_user,
const string& pwd,
const string& sess_link,
const string& proxy,
const string& contact);
const string& proxy);
void removeRegistration(const string& handle);
bool hasRegistration(const string& handle);

@ -1,171 +0,0 @@
/*
* Copyright (C) 2013 Stefan Sayer
*
* This file is part of SEMS, a free SIP media server.
*
* SEMS 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.
*
* For a license to use the SEMS software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* SEMS 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 "ReplacesMapper.h"
#include "AmUtils.h"
#include "AmUriParser.h"
#include "AmSipHeaders.h"
bool findTag(const string replaces, const string& tag, size_t& p1, size_t& len);
void fixReplaces(AmSipRequest& req, bool is_invite) {
string replaces;
string refer_to;
AmUriParser refer_target;
vector<string> hdrs; // headers from Refer-To URI
vector<string>::iterator replaces_hdr_it; // Replaces header from Refer-To URI
DBG("Replaces handler: fixing %s request\n", is_invite?"INVITE":"REFER");
if (is_invite) {
replaces = getHeader(req.hdrs, SIP_HDR_REPLACES, true);
if (replaces.empty()) {
DBG("Replaces handler: no Replaces in INVITE, ignoring\n");
return;
}
} else {
refer_to = getHeader(req.hdrs, SIP_HDR_REFER_TO, SIP_HDR_REFER_TO_COMPACT, true);
if (refer_to.empty()) {
DBG("Replaces handler: empty Refer-To header, ignoring\n");
return;
}
size_t pos=0; size_t end=0;
if (!refer_target.parse_contact(refer_to, pos, end)) {
DBG("Replaces handler: unable to parse Refer-To name-addr, ignoring\n");
return;
}
if (refer_target.uri_headers.empty()) {
DBG("Replaces handler: no headers in Refer-To target, ignoring\n");
return;
}
hdrs = explode(refer_target.uri_headers, ";");
for (replaces_hdr_it=hdrs.begin(); replaces_hdr_it != hdrs.end(); replaces_hdr_it++) {
string s = URL_decode(*replaces_hdr_it);
const char* Replaces_str = "Replaces";
if ((s.length() >= 8) &&
!strncmp(Replaces_str, s.c_str(), 8)) {
size_t pos = 8;
while (s.length()>pos && (s[pos] == ' ' || s[pos] == '\t')) pos++;
if (s[pos] != '=')
continue;
pos++;
while (s.length()>pos && (s[pos] == ' ' || s[pos] == '\t')) pos++;
replaces = s.substr(pos);
break;
}
}
if (replaces_hdr_it == hdrs.end()) {
DBG("Replaces handler: no Replaces headers in Refer-To target, ignoring\n");
return;
}
}
DBG("Replaces found: '%s'\n", replaces.c_str());
size_t ftag_begin; size_t ftag_len;
size_t ttag_begin; size_t ttag_len;
size_t cid_len=0;
// todo: parse full replaces header and reconstruct including unknown params
if (!findTag(replaces, "from-tag=", ftag_begin, ftag_len)) {
WARN("Replaces missing 'from-tag', ignoring\n");
return;
}
if (!findTag(replaces, "to-tag=", ttag_begin, ttag_len)) {
WARN("Replaces missing 'to-tag', ignoring\n");
return;
}
while (cid_len < replaces.size() && replaces[cid_len] != ';')
cid_len++;
string ftag = replaces.substr(ftag_begin, ftag_len);
string ttag = replaces.substr(ttag_begin, ttag_len);
string callid = replaces.substr(0, cid_len);
bool early_only = replaces.find("early-only") != string::npos;
DBG("Replaces handler: found callid='%s', ftag='%s', ttag='%s'\n",
callid.c_str(), ftag.c_str(), ttag.c_str());
SBCCallRegistryEntry other_dlg;
if (SBCCallRegistry::lookupCall(ttag, other_dlg)) {
replaces = other_dlg.callid+
";from-tag="+other_dlg.ltag+";to-tag="+other_dlg.rtag;
if (early_only)
replaces += ";early_only";
DBG("Replaces handler: mapped Replaces to: '%s'\n", replaces.c_str());
if (is_invite) {
removeHeader(req.hdrs, SIP_HDR_REPLACES);
req.hdrs+=SIP_HDR_COLSP(SIP_HDR_REPLACES)+replaces+CRLF;
} else {
string replaces_enc = SIP_HDR_REPLACES "="+URL_encode(replaces);
string new_hdrs;
for (vector<string>::iterator it = hdrs.begin(); it != hdrs.end(); it++) {
if (it != hdrs.begin())
new_hdrs+=";";
if (it != replaces_hdr_it) {
// different hdr, just add it
new_hdrs+=*it;
} else {
//reconstructed replaces hdr
new_hdrs+=replaces_enc;
}
}
refer_target.uri_headers=new_hdrs;
removeHeader(req.hdrs, SIP_HDR_REFER_TO);
removeHeader(req.hdrs, SIP_HDR_REFER_TO_COMPACT);
req.hdrs+=SIP_HDR_COLSP(SIP_HDR_REFER_TO)+refer_target.nameaddr_str()+CRLF;
}
} else {
DBG("Replaces handler: call with tag '%s' not found\n", ttag.c_str());
}
}
bool findTag(const string replaces, const string& tag, size_t& p1, size_t& len)
{
size_t i = replaces.find(tag);
if (i == string::npos) return false;
p1 = i+tag.length();
size_t j = replaces.find(';', p1);
if (j != string::npos) {
len = j - p1;
} else {
len = replaces.size() - i;
}
return true;
}

@ -1,34 +0,0 @@
/*
* Copyright (C) 2013 Stefan Sayer
*
* This file is part of SEMS, a free SIP media server.
*
* SEMS 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.
*
* For a license to use the SEMS software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* SEMS 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
*/
#ifndef _ReplacesMapper_H
#define _ReplacesMapper_H
#include "SBCCallRegistry.h"
#include "AmSipMsg.h"
void fixReplaces(AmSipRequest& req, bool is_invite);
#endif

@ -33,7 +33,6 @@ SBC - feature-wishlist
- fallback profile
*/
#include "SBC.h"
#include "ampi/SBCCallControlAPI.h"
#include "log.h"
#include "AmUtils.h"
@ -47,8 +46,6 @@ SBC - feature-wishlist
#include "HeaderFilter.h"
#include "ParamReplacer.h"
#include "SDPFilter.h"
#include "SBCCallRegistry.h"
#include "ReplacesMapper.h"
using std::map;
@ -290,9 +287,6 @@ void SBCFactory::invoke(const string& method, const AmArg& args,
} else if (method == "setRegexMap"){
args.assertArrayFmt("u");
setRegexMap(args,ret);
} else if (method == "postControlCmd"){
args.assertArrayFmt("ss"); // at least call-ltag, cmd
postControlCmd(args,ret);
} else if(method == "_list"){
ret.push(AmArg("listProfiles"));
ret.push(AmArg("reloadProfiles"));
@ -302,7 +296,6 @@ void SBCFactory::invoke(const string& method, const AmArg& args,
ret.push(AmArg("setActiveProfile"));
ret.push(AmArg("getRegexMapNames"));
ret.push(AmArg("setRegexMap"));
ret.push(AmArg("postControlCmd"));
} else
throw AmDynInvoke::NotImplemented(method);
}
@ -486,22 +479,6 @@ void SBCFactory::setRegexMap(const AmArg& args, AmArg& ret) {
ret.push("OK");
}
void SBCFactory::postControlCmd(const AmArg& args, AmArg& ret) {
SBCControlEvent* evt;
if (args.size()<3) {
evt = new SBCControlEvent(args[1].asCStr());
} else {
evt = new SBCControlEvent(args[1].asCStr(), args[2]);
}
if (!AmSessionContainer::instance()->postEvent(args[0].asCStr(), evt)) {
ret.push(404);
ret.push("Not found");
} else {
ret.push(202);
ret.push("Accepted");
}
}
SBCDialog::SBCDialog(const SBCCallProfile& call_profile)
: m_state(BB_Init),
prepaid_acc(NULL),
@ -515,7 +492,6 @@ SBCDialog::SBCDialog(const SBCCallProfile& call_profile)
SBCDialog::~SBCDialog()
{
SBCCallRegistry::removeCall(dlg.local_tag);
}
void SBCDialog::onInvite(const AmSipRequest& req)
@ -642,15 +618,6 @@ void SBCDialog::onInvite(const AmSipRequest& req)
replaceParameters(call_profile.auth_credentials.pwd, "auth_pwd", REPLACE_VALS);
}
if (call_profile.uas_auth_bleg_enabled) {
call_profile.uas_auth_bleg_credentials.realm =
replaceParameters(call_profile.uas_auth_bleg_credentials.realm, "uas_auth_bleg_realm", REPLACE_VALS);
call_profile.uas_auth_bleg_credentials.user =
replaceParameters(call_profile.uas_auth_bleg_credentials.user, "uas_auth_bleg_user", REPLACE_VALS);
call_profile.uas_auth_bleg_credentials.pwd =
replaceParameters(call_profile.uas_auth_bleg_credentials.pwd, "uas_auth_bleg_pwd", REPLACE_VALS);
}
if (!call_profile.outbound_interface.empty()) {
call_profile.outbound_interface =
replaceParameters(call_profile.outbound_interface, "outbound_interface",
@ -730,15 +697,6 @@ void SBCDialog::onInvite(const AmSipRequest& req)
}
}
call_profile.fix_replaces_inv =
replaceParameters(call_profile.fix_replaces_inv, "fix_replaces_inv", REPLACE_VALS);
call_profile.fix_replaces_ref =
replaceParameters(call_profile.fix_replaces_ref, "fix_replaces_ref", REPLACE_VALS);
if (call_profile.fix_replaces_inv == "yes") {
fixReplaces(invite_req, true);
}
#undef REPLACE_VALS
DBG("SBC: connecting to '%s'\n",ruri.c_str());
@ -787,41 +745,17 @@ void SBCDialog::process(AmEvent* ev)
}
}
SBCControlEvent* ctl_event;
if (ev->event_id == SBCControlEvent_ID &&
(ctl_event = dynamic_cast<SBCControlEvent*>(ev)) != NULL) {
onControlCmd(ctl_event->cmd, ctl_event->params);
return;
}
AmB2BCallerSession::process(ev);
}
void SBCDialog::onControlCmd(string& cmd, AmArg& params) {
if (cmd == "teardown") {
DBG("teardown requested from control cmd\n");
stopCall();
return;
}
DBG("ignoring unknown control cmd : '%s'\n", cmd.c_str());
}
int SBCDialog::relayEvent(AmEvent* ev) {
if (ev->event_id == B2BSipRequest) {
if ((call_profile.headerfilter != Transparent) &&
(ev->event_id == B2BSipRequest)) {
// header filter
B2BSipRequestEvent* req_ev = dynamic_cast<B2BSipRequestEvent*>(ev);
assert(req_ev);
if (call_profile.headerfilter != Transparent) {
inplaceHeaderFilter(req_ev->req.hdrs,
call_profile.headerfilter_list, call_profile.headerfilter);
}
if (req_ev->req.method == SIP_METH_REFER &&
call_profile.fix_replaces_ref == "yes") {
fixReplaces(req_ev->req, false);
}
assert(req_ev);
inplaceHeaderFilter(req_ev->req.hdrs,
call_profile.headerfilter_list, call_profile.headerfilter);
} else {
if (ev->event_id == B2BSipReply) {
if ((call_profile.headerfilter != Transparent) ||
@ -887,11 +821,6 @@ void SBCDialog::onSipRequest(const AmSipRequest& req) {
}
}
if (fwd && req.method == SIP_METH_INVITE) {
DBG("replying 100 Trying of INVITE msg to be fwd'ed\n");
dlg.reply(req, 100, SIP_REPLY_TRYING);
}
AmB2BCallerSession::onSipRequest(req);
}
@ -1078,18 +1007,6 @@ void SBCDialog::createCalleeSession()
}
}
if (call_profile.uas_auth_bleg_enabled) {
AmDynInvokeFactory* fact = AmPlugIn::instance()->getFactory4Di("uac_auth");
if (NULL != fact) {
AmDynInvoke* di_inst = fact->getInstance();
if(NULL != di_inst) {
callee_session->setAuthDI(di_inst);
}
} else {
ERROR("B-leg UAS auth enabled (uas_auth_bleg_enabled), but uac_auth module not loaded!\n");
}
}
if (call_profile.sst_enabled) {
AmSessionEventHandler* h = SBCFactory::session_timer_fact->getHandler(callee_session);
if(!h) {
@ -1166,11 +1083,6 @@ void SBCDialog::createCalleeSession()
throw;
}
// A->B
SBCCallRegistry::addCall(dlg.local_tag, SBCCallRegistryEntry(callee_dlg.callid, callee_dlg.local_tag, ""));
// B->A
SBCCallRegistry::addCall(callee_dlg.local_tag, SBCCallRegistryEntry(dlg.callid, dlg.local_tag, dlg.remote_tag));
callee_session->start();
AmSessionContainer* sess_cont = AmSessionContainer::instance();
@ -1179,7 +1091,7 @@ void SBCDialog::createCalleeSession()
SBCCalleeSession::SBCCalleeSession(const AmB2BCallerSession* caller,
const SBCCallProfile& call_profile)
: auth(NULL),auth_di(NULL),
: auth(NULL),
call_profile(call_profile),
AmB2BCalleeSession(caller)
{
@ -1194,8 +1106,6 @@ SBCCalleeSession::SBCCalleeSession(const AmB2BCallerSession* caller,
SBCCalleeSession::~SBCCalleeSession() {
if (auth)
delete auth;
SBCCallRegistry::removeCall(dlg.local_tag);
}
inline UACAuthCred* SBCCalleeSession::getCredentials() {
@ -1203,21 +1113,13 @@ inline UACAuthCred* SBCCalleeSession::getCredentials() {
}
int SBCCalleeSession::relayEvent(AmEvent* ev) {
if (ev->event_id == B2BSipRequest) {
if ((call_profile.headerfilter != Transparent) &&
(ev->event_id == B2BSipRequest)) {
// header filter
B2BSipRequestEvent* req_ev = dynamic_cast<B2BSipRequestEvent*>(ev);
assert(req_ev);
if (call_profile.headerfilter != Transparent) {
inplaceHeaderFilter(req_ev->req.hdrs,
call_profile.headerfilter_list, call_profile.headerfilter);
}
if (req_ev->req.method == SIP_METH_REFER &&
call_profile.fix_replaces_ref == "yes") {
fixReplaces(req_ev->req, false);
}
assert(req_ev);
inplaceHeaderFilter(req_ev->req.hdrs,
call_profile.headerfilter_list, call_profile.headerfilter);
} else {
if (ev->event_id == B2BSipReply) {
if ((call_profile.headerfilter != Transparent) ||
@ -1248,17 +1150,6 @@ int SBCCalleeSession::relayEvent(AmEvent* ev) {
return AmB2BCalleeSession::relayEvent(ev);
}
void SBCCalleeSession::process(AmEvent* ev) {
SBCControlEvent* ctl_event;
if (ev->event_id == SBCControlEvent_ID &&
(ctl_event = dynamic_cast<SBCControlEvent*>(ev)) != NULL) {
onControlCmd(ctl_event->cmd, ctl_event->params);
return;
}
AmB2BCalleeSession::process(ev);
}
void SBCCalleeSession::onSipRequest(const AmSipRequest& req) {
// AmB2BSession does not call AmSession::onSipRequest for
// forwarded requests - so lets call event handlers here
@ -1282,45 +1173,6 @@ void SBCCalleeSession::onSipRequest(const AmSipRequest& req) {
}
}
if (call_profile.uas_auth_bleg_enabled && NULL != auth_di) {
AmArg di_args, di_ret;
try {
DBG("Auth: checking authentication\n");
di_args.push((ArgObject*)&req);
di_args.push(call_profile.uas_auth_bleg_credentials.realm);
di_args.push(call_profile.uas_auth_bleg_credentials.user);
di_args.push(call_profile.uas_auth_bleg_credentials.pwd);
auth_di->invoke("checkAuth", di_args, di_ret);
if (di_ret.size() >= 3) {
if (di_ret[0].asInt() != 200) {
DBG("Auth: replying %u %s - hdrs: '%s'\n",
di_ret[0].asInt(), di_ret[1].asCStr(), di_ret[2].asCStr());
dlg.reply(req, di_ret[0].asInt(), di_ret[1].asCStr(), "", "", di_ret[2].asCStr());
return;
} else {
DBG("Successfully authenticated request.\n");
}
} else {
ERROR("internal: no proper result from checkAuth: '%s'\n", AmArg::print(di_ret).c_str());
}
} catch (const AmDynInvoke::NotImplemented& ni) {
ERROR("not implemented DI function 'checkAuth'\n");
} catch (const AmArg::OutOfBoundsException& oob) {
ERROR("out of bounds in DI call 'checkAuth'\n");
} catch (const AmArg::TypeMismatchException& oob) {
ERROR("type mismatch in DI call checkAuth\n");
} catch (...) {
ERROR("unexpected Exception in DI call checkAuth\n");
}
}
if (fwd) {
DBG("replying 100 Trying of %s msg to be fwd'ed\n", req.method.c_str());
dlg.reply(req, 100, SIP_REPLY_TRYING);
}
AmB2BCalleeSession::onSipRequest(req);
}
@ -1332,25 +1184,18 @@ void SBCCalleeSession::onSipReply(const AmSipReply& reply, int old_dlg_status,
bool fwd = t != relayed_req.end();
DBG("onSipReply: %i %s (fwd=%i)\n",reply.code,reply.reason.c_str(),fwd);
DBG("onSipReply: content-type = %s\n",reply.content_type.c_str());
if(fwd) {
CALL_EVENT_H(onSipReply,reply, old_dlg_status, trans_method);
}
// update call registry (unfortunately has to be done always -
// not possible to determine if learned in this reply)
if (!dlg.remote_tag.empty() && reply.code >= 200 && trans_method == SIP_METH_INVITE) {
SBCCallRegistry::updateCall(other_id, dlg.remote_tag);
}
if (NULL == auth) {
if (NULL == auth) {
AmB2BCalleeSession::onSipReply(reply,old_dlg_status, trans_method);
return;
}
unsigned int cseq_before = dlg.cseq;
if (!auth->onSipReply(reply, old_dlg_status, trans_method)) {
AmB2BCalleeSession::onSipReply(reply, old_dlg_status, trans_method);
AmB2BCalleeSession::onSipReply(reply, old_dlg_status, trans_method);
} else {
if (cseq_before != dlg.cseq) {
DBG("uac_auth consumed reply with cseq %d and resent with cseq %d; "
@ -1378,15 +1223,6 @@ void SBCCalleeSession::onSendRequest(const string& method, const string& content
body, hdrs, flags, cseq);
}
void SBCCalleeSession::onControlCmd(string& cmd, AmArg& params) {
if (cmd == "teardown") {
DBG("relaying teardown control cmd to A leg\n");
relayEvent(new SBCControlEvent(cmd, params));
return;
}
DBG("ignoring unknown control cmd : '%s'\n", cmd.c_str());
}
int SBCCalleeSession::filterBody(AmSdp& sdp, bool is_a2b) {
if (call_profile.sdpfilter_enabled) {
// normalize SDP

@ -60,7 +60,6 @@ class SBCFactory: public AmSessionFactory,
void setActiveProfile(const AmArg& args, AmArg& ret);
void getRegexMapNames(const AmArg& args, AmArg& ret);
void setRegexMap(const AmArg& args, AmArg& ret);
void postControlCmd(const AmArg& args, AmArg& ret);
string getActiveProfileMatch(string& profile_rule, const AmSipRequest& req,
const string& app_param, AmUriParser& ruri_parser,
@ -145,8 +144,6 @@ class SBCDialog : public AmB2BCallerSession
bool onOtherReply(const AmSipReply& reply);
void onOtherBye(const AmSipRequest& req);
void onControlCmd(string& cmd, AmArg& params);
int filterBody(AmSdp& sdp, bool is_a2b);
void createCalleeSession();
@ -156,7 +153,6 @@ class SBCCalleeSession
: public AmB2BCalleeSession, public CredentialHolder
{
AmSessionEventHandler* auth;
AmDynInvoke* auth_di;
SBCCallProfile call_profile;
protected:
@ -170,8 +166,6 @@ class SBCCalleeSession
/* bool onOtherReply(const AmSipReply& reply); */
void onControlCmd(string& cmd, AmArg& params);
int filterBody(AmSdp& sdp, bool is_a2b);
public:
@ -179,12 +173,9 @@ class SBCCalleeSession
const SBCCallProfile& call_profile);
~SBCCalleeSession();
void process(AmEvent* ev);
inline UACAuthCred* getCredentials();
void setAuthHandler(AmSessionEventHandler* h) { auth = h; }
void setAuthDI(AmDynInvoke* di_inst) { auth_di = di_inst; }
};
extern void assertEndCRLF(string& s);

@ -108,9 +108,6 @@ bool SBCCallProfile::readFromConfiguration(const string& name,
}
}
fix_replaces_inv = cfg.getParameter("fix_replaces_inv");
fix_replaces_ref = cfg.getParameter("fix_replaces_ref");;
sst_enabled = cfg.getParameter("enable_session_timer", "no") == "yes";
use_global_sst_config = !cfg.hasParameter("session_expires");
@ -121,11 +118,6 @@ bool SBCCallProfile::readFromConfiguration(const string& name,
call_timer_enabled = cfg.getParameter("enable_call_timer", "no") == "yes";
call_timer = cfg.getParameter("call_timer");
uas_auth_bleg_enabled = cfg.getParameter("enable_bleg_uas_auth", "no") == "yes";
uas_auth_bleg_credentials.realm = cfg.getParameter("uas_auth_bleg_realm");
uas_auth_bleg_credentials.user = cfg.getParameter("uas_auth_bleg_user");
uas_auth_bleg_credentials.pwd = cfg.getParameter("uas_auth_bleg_pwd");
prepaid_enabled = cfg.getParameter("enable_prepaid", "no") == "yes";
prepaid_accmodule = cfg.getParameter("prepaid_accmodule");
prepaid_uuid = cfg.getParameter("prepaid_uuid");
@ -223,9 +215,6 @@ bool SBCCallProfile::readFromConfiguration(const string& name,
sdpfilter_enabled?"en":"dis", FilterType2String(sdpfilter),
sdpfilter_list.size());
INFO("SBC: fixing Replaces in INVITE: '%s'\n", fix_replaces_inv.c_str());
INFO("SBC: fixing Replaces in REFER: '%s'\n", fix_replaces_ref.c_str());
INFO("SBC: RTP relay %sabled\n", rtprelay_enabled?"en":"dis");
if (rtprelay_enabled) {
if (!force_symmetric_rtp.empty()) {
@ -239,7 +228,6 @@ bool SBCCallProfile::readFromConfiguration(const string& name,
INFO("SBC: SST %sabled\n", sst_enabled?"en":"dis");
INFO("SBC: SIP auth %sabled\n", auth_enabled?"en":"dis");
INFO("SBC: SIP UAS auth for B leg %sabled\n", uas_auth_bleg_enabled?"en":"dis");
INFO("SBC: call timer %sabled\n", call_timer_enabled?"en":"dis");
if (call_timer_enabled) {
INFO("SBC: %s seconds\n", call_timer.c_str());

@ -68,18 +68,12 @@ struct SBCCallProfile {
FilterType sdpfilter;
set<string> sdpfilter_list;
string fix_replaces_inv;
string fix_replaces_ref;
bool sst_enabled;
bool use_global_sst_config;
bool auth_enabled;
UACAuthCred auth_credentials;
bool uas_auth_bleg_enabled;
UACAuthCred uas_auth_bleg_credentials;
bool call_timer_enabled;
string call_timer;

@ -1,79 +0,0 @@
/*
* Copyright (C) 2013 Stefan Sayer
*
* This file is part of SEMS, a free SIP media server.
*
* SEMS 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.
*
* For a license to use the SEMS software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* SEMS 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 "SBCCallRegistry.h"
#include "log.h"
AmMutex SBCCallRegistry::registry_mutex;
std::map<string, SBCCallRegistryEntry> SBCCallRegistry::registry;
void SBCCallRegistry::addCall(const string& ltag, const SBCCallRegistryEntry& other_dlg) {
registry_mutex.lock();
registry[ltag] = other_dlg;
registry_mutex.unlock();
DBG("SBCCallRegistry: Added call '%s' - mapped to: '%s'/'%s'/'%s'\n", ltag.c_str(), other_dlg.ltag.c_str(), other_dlg.rtag.c_str(), other_dlg.callid.c_str());
}
void SBCCallRegistry::updateCall(const string& ltag, const string& other_rtag) {
registry_mutex.lock();
std::map<string, SBCCallRegistryEntry>::iterator it = registry.find(ltag);
if (it != registry.end()) {
it->second.rtag = other_rtag;
}
registry_mutex.unlock();
DBG("SBCCallRegistry: Updated call '%s' - rtag to: '%s'\n", ltag.c_str(), other_rtag.c_str());
}
bool SBCCallRegistry::lookupCall(const string& ltag, SBCCallRegistryEntry& other_dlg) {
bool res = false;
registry_mutex.lock();
std::map<string, SBCCallRegistryEntry>::iterator it = registry.find(ltag);
if (it != registry.end()) {
res = true;
other_dlg = it->second;
}
registry_mutex.unlock();
if (res) {
DBG("SBCCallRegistry: found call mapping '%s' -> '%s'/'%s'/'%s'\n",
ltag.c_str(), other_dlg.ltag.c_str(), other_dlg.rtag.c_str(), other_dlg.callid.c_str());
} else {
DBG("SBCCallRegistry: no call mapping found for '%s'\n", ltag.c_str());
}
return res;
}
void SBCCallRegistry::removeCall(const string& ltag) {
registry_mutex.lock();
registry.erase(ltag);
registry_mutex.unlock();
DBG("SBCCallRegistry: removed entry for call '%s'\n", ltag.c_str());
}

@ -1,61 +0,0 @@
/*
* Copyright (C) 2013 Stefan Sayer
*
* This file is part of SEMS, a free SIP media server.
*
* SEMS 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.
*
* For a license to use the SEMS software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* SEMS 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
*/
#ifndef _SBCCallRegistry_H
#define _SBCCallRegistry_H
#include "AmThread.h"
#include <string>
using std::string;
#include <map>
struct SBCCallRegistryEntry
{
string ltag;
string rtag;
string callid;
SBCCallRegistryEntry() { }
SBCCallRegistryEntry(const string& callid, const string& ltag, const string& rtag)
: ltag(ltag), rtag(rtag), callid(callid) { }
};
class SBCCallRegistry
{
static AmMutex registry_mutex;
static std::map<string, SBCCallRegistryEntry> registry;
public:
SBCCallRegistry() { }
~SBCCallRegistry() { }
static void addCall(const string& ltag, const SBCCallRegistryEntry& other_dlg);
static void updateCall(const string& ltag, const string& other_rtag);
static bool lookupCall(const string& ltag, SBCCallRegistryEntry& other_dlg);
static void removeCall(const string& ltag);
};
#endif

@ -46,21 +46,11 @@
# translate some 6xx class replies to 4xx class:
#reply_translations="603=>488 Not acceptable here|600=>406 Not Acceptable"
## fix replaces for call transfers
# fix_replaces_inv=yes
# fix_replaces_ref=yes
## authentication:
#enable_auth=yes
#auth_user=$P(u)
#auth_pwd=$P(p)
## UAS auth for B leg
#uas_auth_bleg_enabled=yes
#uas_auth_bleg_realm=$P(sr)
#uas_auth_bleg_user=$P(su)
#uas_auth_bleg_pwd=$P(sp)
## call timer
#enable_call_timer=yes
#call_timer=60

@ -1,19 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
from xmlrpclib import *
if len(sys.argv) != 2:
print "usage: %s <ltag/ID of call to tear down>" % sys.argv[0]
sys.exit(1)
s = ServerProxy('http://localhost:8090')
print "Active calls: %d" % s.calls()
res = s.di('sbc', 'postControlCmd', sys.argv[1], "teardown")
if res[0] >= 200 and res[0] < 300:
print "OK"
sys.exit(0)
else:
print "Error: %s" % str(res)
sys.exit(2)

@ -1,10 +0,0 @@
plug_in_name = sw_vsc
module_ldflags = -lmysqlclient `pcre-config --libs` -lpcrecpp
module_cflags = -I/usr/include/mysql `pcre-config --cflags`
module_extra_objs =
extra_install = $(plug_in_name)_audio
COREPATH ?=../../core
include $(COREPATH)/plug-in/Makefile.app_module

File diff suppressed because it is too large Load Diff

@ -1,137 +0,0 @@
#ifndef _SW_VSC_H_
#define _SW_VSC_H_
#include "AmSession.h"
#include "AmConfigReader.h"
#include "AmAudioFile.h"
#include "ampi/UACAuthAPI.h"
#include <string>
using std::string;
#include <memory>
#include <regex.h>
//#include <my_global.h>
//#include <m_string.h>
#include <mysql.h>
typedef struct {
string mysqlHost;
int mysqlPort;
string mysqlUser;
string mysqlPass;
string failAnnouncement;
string unknownAnnouncement;
string voicemailNumber;
regex_t cfuOnPattern;
string cfuOnAnnouncement;
regex_t cfuOffPattern;
string cfuOffAnnouncement;
regex_t cfbOnPattern;
string cfbOnAnnouncement;
regex_t cfbOffPattern;
string cfbOffAnnouncement;
regex_t cftOnPattern;
string cftOnAnnouncement;
regex_t cftOffPattern;
string cftOffAnnouncement;
regex_t cfnaOnPattern;
string cfnaOnAnnouncement;
regex_t cfnaOffPattern;
string cfnaOffAnnouncement;
regex_t speedDialPattern;
string speedDialAnnouncement;
regex_t reminderOnPattern;
string reminderOnAnnouncement;
regex_t reminderOffPattern;
string reminderOffAnnouncement;
} sw_vsc_patterns_t;
class SW_VscFactory: public AmSessionFactory
{
inline string getAnnounceFile(const AmSipRequest& req);
sw_vsc_patterns_t m_patterns;
public:
SW_VscFactory(const string& _app_name);
virtual ~SW_VscFactory();
int onLoad();
AmSession* onInvite(const AmSipRequest& req);
AmSession* onInvite(const AmSipRequest& req,
AmArg& session_params);
};
class SW_VscDialog : public AmSession,
public CredentialHolder
{
AmAudioFile m_wav_file;
sw_vsc_patterns_t *m_patterns;
std::auto_ptr<UACAuthCred> cred;
u_int64_t getAttributeId(MYSQL *my_handler, const char* attribute);
u_int64_t getSubscriberId(MYSQL *my_handler, const char* uuid,
string *domain, u_int64_t &domain_id);
u_int64_t getPreference(MYSQL *my_handler, u_int64_t subscriberId, u_int64_t attributeId,
int *foundPref, string *value);
int deletePreferenceId(MYSQL *my_handler, u_int64_t preferenceId);
int insertPreference(MYSQL *my_handler, u_int64_t subscriberId,
u_int64_t attributeId, string &uri);
int updatePreferenceId(MYSQL *my_handler, u_int64_t preferenceId, string &uri);
int insertSpeedDialSlot(MYSQL *my_handler, u_int64_t subscriberId, string &slot, string &uri);
int insertReminder(MYSQL *my_handler, u_int64_t subscriberId, string &repeat, string &tim);
int deleteReminder(MYSQL *my_handler, u_int64_t subscriberId);
int number2uri(const AmSipRequest& req, MYSQL *my_handler, string &uuid, u_int64_t subId,
string &domain, u_int64_t domId, int offset, string &uri);
u_int64_t createCFMap(MYSQL *my_handler, u_int64_t subscriberId, string &uri,
const char* mapName, const char* type);
u_int64_t deleteCFMap(MYSQL *my_handler, u_int64_t subscriberId,
const char* mapName, const char* type);
public:
SW_VscDialog(sw_vsc_patterns_t *patterns,
UACAuthCred* credentials = NULL);
~SW_VscDialog();
void onSessionStart(const AmSipRequest& req);
void onSessionStart(const AmSipReply& rep);
void startSession(const AmSipRequest& req);
void startSession();
void onBye(const AmSipRequest& req);
void onDtmf(int event, int duration_msec) {}
void process(AmEvent* event);
UACAuthCred* getCredentials();
};
#endif

@ -1,40 +0,0 @@
mysql_host = 127.0.0.1
mysql_port = 3306
mysql_user = soap
mysql_pass = s:wMP4Si
announce_path = /usr/lib/sems/audio/
error_announcement = sw_vsc_error.wav
unknown_announcement = sw_vsc_unavailable.wav
voicemail_number = 2000
cfu_on_pattern = ^\*72\*[0-9]+$
cfu_on_announcement = sw_vsc_activated.wav
cfu_off_pattern = ^(\%23)72(\%23)?.*$
cfu_off_announcement = sw_vsc_deactivated.wav
cfb_on_pattern = ^\*90\*[0-9]+$
cfb_on_announcement = sw_vsc_activated.wav
cfb_off_pattern = ^(\%23)90(\%23)?.*$
cfb_off_announcement = sw_vsc_deactivated.wav
cft_on_pattern = ^\*92\*[0-9]+\*[0-9]+$
cft_on_announcement = sw_vsc_activated.wav
cft_off_pattern = ^(\%23)92(\%23)?.*$
cft_off_announcement = sw_vsc_deactivated.wav
cfna_on_pattern = ^\*93\*[0-9]+$
cfna_on_announcement = sw_vsc_activated.wav
cfna_off_pattern = ^(\%23)93(\%23)?.*$
cfna_off_announcement = sw_vsc_deactivated.wav
speed_dial_pattern = ^\*50\*[0-9]+$
speed_dial_announcement = sw_vsc_activated.wav
reminder_on_pattern = ^\*55\*[0-9]+$
reminder_on_announcement = sw_vsc_activated.wav
reminder_off_pattern = ^(\%23)55(\%23)?.*$
reminder_off_announcement = sw_vsc_deactivated.wav

@ -47,8 +47,6 @@ unsigned int XMLRPC2DI::ServerRetryAfter = 10;
bool XMLRPC2DI::DebugServerParams = false;
bool XMLRPC2DI::DebugServerResult = false;
unsigned int XMLRPC2DI::debug_log_level = 3;
double XMLRPC2DI::ServerTimeout = -1;
XMLRPC2DI* XMLRPC2DI::instance()
@ -83,8 +81,6 @@ int XMLRPC2DI::load() {
DebugServerResult = cfg.getParameter("debug_server_result", "no") == "yes";
DebugServerParams = cfg.getParameter("debug_server_params", "no") == "yes";
debug_log_level = cfg.getParameterInt("debug_log_level", 3);
XmlRpcServer* s;
bool multi_threaded = false;
unsigned int threads = 0;
@ -542,7 +538,7 @@ void XMLRPC2DIServerDIMethod::execute(XmlRpcValue& params, XmlRpcValue& result)
string fact_name = params[0];
string fct_name = params[1];
_LOG(XMLRPC2DI::debug_log_level, "XMLRPC2DI: factory '%s' function '%s'\n",
DBG("XMLRPC2DI: factory '%s' function '%s'\n",
fact_name.c_str(), fct_name.c_str());
// get args
@ -550,7 +546,7 @@ void XMLRPC2DIServerDIMethod::execute(XmlRpcValue& params, XmlRpcValue& result)
XMLRPC2DIServer::xmlrpcval2amargarray(params, args, 2);
if (XMLRPC2DI::DebugServerParams) {
_LOG(XMLRPC2DI::debug_log_level, " params: <%s>\n", AmArg::print(args).c_str());
DBG(" params: <%s>\n", AmArg::print(args).c_str());
}
AmDynInvokeFactory* di_f = AmPlugIn::instance()->getFactory4Di(fact_name);
@ -566,7 +562,7 @@ void XMLRPC2DIServerDIMethod::execute(XmlRpcValue& params, XmlRpcValue& result)
if (XMLRPC2DI::DebugServerResult) {
_LOG(XMLRPC2DI::debug_log_level, " result: <%s>\n", AmArg::print(ret).c_str());
DBG(" result: <%s>\n", AmArg::print(ret).c_str());
}
XMLRPC2DIServer::amarg2xmlrpcval(ret, result);
@ -709,19 +705,19 @@ void DIMethodProxy::execute(XmlRpcValue& params,
AmArg args, ret;
_LOG(XMLRPC2DI::debug_log_level, "XMLRPC2DI '%s': function '%s'\n",
server_method_name.c_str(),
di_method_name.c_str());
DBG("XMLRPC2DI '%s': function '%s'\n",
server_method_name.c_str(),
di_method_name.c_str());
XMLRPC2DIServer::xmlrpcval2amarg(params, args);
if (XMLRPC2DI::DebugServerParams) {
_LOG(XMLRPC2DI::debug_log_level, " params: <%s>\n", AmArg::print(args).c_str());
DBG(" params: <%s>\n", AmArg::print(args).c_str());
}
di->invoke(di_method_name, args, ret);
if (XMLRPC2DI::DebugServerResult) {
_LOG(XMLRPC2DI::debug_log_level, " result: <%s>\n", AmArg::print(ret).c_str());
DBG(" result: <%s>\n", AmArg::print(ret).c_str());
}
XMLRPC2DIServer::amarg2xmlrpcval(ret, result);

@ -192,8 +192,6 @@ class XMLRPC2DI
static bool DebugServerParams;
static bool DebugServerResult;
static unsigned int debug_log_level;
};
#endif

@ -38,7 +38,4 @@ xmlrpc_port=8090
#
# print result of XMLRPC server calls into debug log [yes|no]
# debug_server_result=yes
#
# log level to use, default: 3 (debug)
#
#debug_log_level=2
#

@ -39,7 +39,6 @@
#include "AmConfigReader.h"
#include "AmUtils.h"
#include "AmSession.h"
#include "sip/sip_timers.h"
#include <cctype>
#include <algorithm>
@ -77,7 +76,6 @@ string AmConfig::NextHopIP = "";
unsigned int AmConfig::NextHopPort = 0;
bool AmConfig::NextHopForReplies = false;
bool AmConfig::ProxyStickyAuth = false;
bool AmConfig::IgnoreNotifyLowerCSeq = false;
bool AmConfig::DisableDNSSRV = false;
string AmConfig::Signature = "";
unsigned int AmConfig::MaxForwards = MAX_FORWARDS;
@ -99,8 +97,6 @@ unsigned int AmConfig::OptionsSessionLimit = 0;
unsigned int AmConfig::OptionsSessionLimitErrCode = 503;
string AmConfig::OptionsSessionLimitErrReason = "Server overload";
bool AmConfig::AcceptForkedDialogs = true;
bool AmConfig::ShutdownMode = false;
unsigned int AmConfig::ShutdownModeErrCode = 503;
string AmConfig::ShutdownModeErrReason = "Server shutting down";
@ -310,27 +306,10 @@ int AmConfig::readConfiguration()
ProxyStickyAuth = (cfg.getParameter("proxy_sticky_auth") == "yes");
}
if(cfg.hasParameter("ignore_notify_lower_cseq")) {
IgnoreNotifyLowerCSeq = (cfg.getParameter("ignore_notify_lower_cseq") == "yes");
}
if(cfg.hasParameter("disable_dns_srv")) {
DisableDNSSRV = (cfg.getParameter("disable_dns_srv") == "yes");
}
for (char c = 'a'; c <= 'm'; c++) {
if(cfg.hasParameter(string("sip_timer_")+c)) {
sip_timers[c-'a']=cfg.getParameterInt(string("sip_timer_")+c, sip_timers[c-'a']);
DBG("Set SIP Timer '%c' to %u ms\n", 'A'+c-'a', sip_timers[c-'a']);
}
}
if (cfg.hasParameter("sip_timer_t2")) {
sip_timer_t2 = cfg.getParameterInt("sip_timer_t2", DEFAULT_T2_TIMER);
DBG("Set SIP Timer T2 to %u ms\n", sip_timer_t2);
}
// plugin_path
if (cfg.hasParameter("plugin_path"))
PlugInPath = cfg.getParameter("plugin_path");
@ -533,9 +512,6 @@ int AmConfig::readConfiguration()
}
}
if(cfg.hasParameter("accept_forked_dialogs"))
AcceptForkedDialogs = !(cfg.getParameter("accept_forked_dialogs") == "no");
if(cfg.hasParameter("shutdown_mode_reply")){
string c_reply = cfg.getParameter("shutdown_mode_reply");
size_t spos = c_reply.find(" ");

@ -134,8 +134,6 @@ struct AmConfig
static bool NextHopForReplies;
/** update ruri-host to previously resolved IP:port on SIP auth */
static bool ProxyStickyAuth;
/** Ignore Low CSeq on NOTIFY - for RFC 3265 instead of 5057 */
static bool IgnoreNotifyLowerCSeq;
/** skip DNS SRV lookup for resolving destination address*/
static bool DisableDNSSRV;
/** Server/User-Agent header (optional) */
@ -172,8 +170,6 @@ struct AmConfig
static unsigned int OptionsSessionLimitErrCode;
static string OptionsSessionLimitErrReason;
static bool AcceptForkedDialogs;
static bool ShutdownMode;
static unsigned int ShutdownModeErrCode;
static string ShutdownModeErrReason;

@ -33,9 +33,8 @@
#include <string>
using std::string;
#define E_PLUGIN 100
#define E_SYSTEM 101
#define E_SIP_SUBSCRIPTION 102
#define E_PLUGIN 100
#define E_SYSTEM 101
/** \brief base event class */
struct AmEvent

@ -27,7 +27,6 @@
#include "AmEventDispatcher.h"
#include "AmSipEvent.h"
#include "AmConfig.h"
#include "sip/hash.h"
unsigned int AmEventDispatcher::hash(const string& s1)
@ -53,38 +52,12 @@ AmEventDispatcher* AmEventDispatcher::instance()
return _instance ? _instance : ((_instance = new AmEventDispatcher()));
}
bool AmEventDispatcher::addEventQueue(const string& local_tag,
AmEventQueueInterface* q)
{
unsigned int queue_bucket = hash(local_tag);
queues_mut[queue_bucket].lock();
if (queues[queue_bucket].find(local_tag) != queues[queue_bucket].end()) {
queues_mut[queue_bucket].unlock();
return false;
}
queues[queue_bucket][local_tag] = q;
queues_mut[queue_bucket].unlock();
return true;
}
/** @return false on error */
bool AmEventDispatcher::addEventQueue(const string& local_tag,
AmEventQueueInterface* q,
const string& callid,
const string& remote_tag,
const string& via_branch)
const string& remote_tag)
{
if(local_tag.empty () ||callid.empty() || remote_tag.empty() | via_branch.empty()) {
ERROR("local_tag, callid, remote_tag or via_branch is empty");
return false;
}
unsigned int queue_bucket = hash(local_tag);
queues_mut[queue_bucket].lock();
@ -94,54 +67,36 @@ bool AmEventDispatcher::addEventQueue(const string& local_tag,
return false;
}
// try to find via id_lookup
unsigned int id_bucket = hash(callid, remote_tag);
string id = callid+remote_tag;
if(AmConfig::AcceptForkedDialogs){
id += via_branch;
}
unsigned int id_bucket = 0;
id_lookup_mut[id_bucket].lock();
if (id_lookup[id_bucket].find(id) !=
id_lookup[id_bucket].end()) {
id_lookup_mut[id_bucket].unlock();
queues_mut[queue_bucket].unlock();
return false;
if(!callid.empty() && !remote_tag.empty()) {
// try to find via id_lookup
id_bucket = hash(callid, remote_tag);
id_lookup_mut[id_bucket].lock();
if (id_lookup[id_bucket].find(callid+remote_tag) !=
id_lookup[id_bucket].end()) {
id_lookup_mut[id_bucket].unlock();
queues_mut[queue_bucket].unlock();
return false;
}
}
queues[queue_bucket][local_tag] = q;
id_lookup[id_bucket][id] = local_tag;
id_lookup_mut[id_bucket].unlock();
queues_mut[queue_bucket].unlock();
return true;
}
AmEventQueueInterface* AmEventDispatcher::delEventQueue(const string& local_tag)
{
AmEventQueueInterface* q = NULL;
unsigned int queue_bucket = hash(local_tag);
queues_mut[queue_bucket].lock();
EvQueueMapIter qi = queues[queue_bucket].find(local_tag);
if(qi != queues[queue_bucket].end()) {
q = qi->second;
queues[queue_bucket].erase(qi);
if(!callid.empty() && !remote_tag.empty()) {
id_lookup[id_bucket][callid+remote_tag] = local_tag;
id_lookup_mut[id_bucket].unlock();
}
queues_mut[queue_bucket].unlock();
return q;
return true;
}
AmEventQueueInterface* AmEventDispatcher::delEventQueue(const string& local_tag,
const string& callid,
const string& remote_tag,
const string& via_branch)
const string& remote_tag)
{
AmEventQueueInterface* q = NULL;
@ -155,16 +110,11 @@ AmEventQueueInterface* AmEventDispatcher::delEventQueue(const string& local_tag,
q = qi->second;
queues[queue_bucket].erase(qi);
if(!callid.empty() && !remote_tag.empty() && !via_branch.empty()) {
if(!callid.empty() && !remote_tag.empty()) {
unsigned int id_bucket = hash(callid, remote_tag);
string id = callid+remote_tag;
if(AmConfig::AcceptForkedDialogs){
id += via_branch;
}
id_lookup_mut[id_bucket].lock();
DictIter di = id_lookup[id_bucket].find(id);
DictIter di = id_lookup[id_bucket].find(callid+remote_tag);
if(di != id_lookup[id_bucket].end()) {
id_lookup[id_bucket].erase(di);
}
@ -197,20 +147,11 @@ bool AmEventDispatcher::post(const string& local_tag, AmEvent* ev)
}
bool AmEventDispatcher::post(const string& callid,
const string& remote_tag,
const string& via_branch,
AmEvent* ev)
bool AmEventDispatcher::post(const string& callid, const string& remote_tag, AmEvent* ev)
{
unsigned int id_bucket = hash(callid, remote_tag);
string id = callid+remote_tag;
if(AmConfig::AcceptForkedDialogs){
id += via_branch;
}
id_lookup_mut[id_bucket].lock();
DictIter di = id_lookup[id_bucket].find(id);
DictIter di = id_lookup[id_bucket].find(callid+remote_tag);
if (di == id_lookup[id_bucket].end()) {
id_lookup_mut[id_bucket].unlock();
return false;
@ -267,24 +208,16 @@ void AmEventDispatcher::dispose()
_instance = NULL;
}
}
/** this function optimizes posting of SIP Requests
- if the session does not exist, no event need to be created (req copied) */
bool AmEventDispatcher::postSipRequest(const AmSipRequest& req)
bool AmEventDispatcher::postSipRequest(const string& callid, const string& remote_tag,
const AmSipRequest& req)
{
// get local tag
bool posted = false;
string callid = req.callid;
string remote_tag = req.from_tag;
unsigned int id_bucket = hash(callid, remote_tag);
string id = callid+remote_tag;
if(AmConfig::AcceptForkedDialogs){
id += req.via_branch;
}
id_lookup_mut[id_bucket].lock();
DictIter di = id_lookup[id_bucket].find(id);
DictIter di = id_lookup[id_bucket].find(callid+remote_tag);
if (di == id_lookup[id_bucket].end()) {
id_lookup_mut[id_bucket].unlock();
return false;

@ -59,8 +59,8 @@ private:
AmMutex queues_mut[EVENT_DISPATCHER_BUCKETS];
/**
* Call ID + remote tag + via_branch -> local tag
* (needed for CANCELs)
* Call ID + remote tag -> local tag
* (needed for CANCELs and some provisionnal answers)
* (UAS sessions only)
*/
Dictionnary id_lookup[EVENT_DISPATCHER_BUCKETS];
@ -74,32 +74,23 @@ public:
static AmEventDispatcher* instance();
static void dispose();
bool postSipRequest(const AmSipRequest& req);
bool postSipRequest(const string& callid, const string& remote_tag,
const AmSipRequest& req);
bool post(const string& local_tag, AmEvent* ev);
bool post(const string& callid,
const string& remote_tag,
const string& via_branch,
AmEvent* ev);
bool post(const string& callid, const string& remote_tag, AmEvent* ev);
/* send event to all event queues. Note: event instances will be cloned */
bool broadcast(AmEvent* ev);
bool addEventQueue(const string& local_tag,
AmEventQueueInterface* q);
bool addEventQueue(const string& local_tag,
AmEventQueueInterface* q,
const string& callid,
const string& remote_tag,
const string& via_branch);
AmEventQueueInterface* delEventQueue(const string& local_tag);
const string& callid="",
const string& remote_tag="");
AmEventQueueInterface* delEventQueue(const string& local_tag,
const string& callid,
const string& remote_tag,
const string& via_branch);
const string& callid="",
const string& remote_tag="");
bool empty();
};

@ -1,88 +0,0 @@
/*
* Copyright (C) 2011 Stefan Sayer
*
* This file is part of SEMS, a free SIP media server.
*
* SEMS 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.
*
* For a license to use the SEMS software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* SEMS 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 "AmEventProcessingThread.h"
#include "log.h"
AmEventProcessingThread::AmEventProcessingThread()
: AmEventQueue(this),
processing_events(true)
{
}
AmEventProcessingThread::~AmEventProcessingThread() {
}
bool AmEventProcessingThread::police_event(AmEvent* ev) {
// default: accept all events
return true;
}
void AmEventProcessingThread::postEvent(AmEvent* ev) {
if (police_event(ev)) {
AmEventQueue::postEvent(ev);
} else {
DBG("dropping event [%p] due to policing\n", ev);
delete ev;
}
}
void AmEventProcessingThread::on_stop() {
DBG("AmEventProcessingThread::on_stop\n");
}
void AmEventProcessingThread::run() {
DBG("AmEventProcessingThread running...\n");
while (processing_events) {
waitForEvent();
processEvents();
}
DBG("AmEventProcessingThread stopping.\n");
}
void AmEventProcessingThread::stop_processing() {
DBG("stop of event processing requested.\n");
processing_events = false;
}
void AmEventProcessingThread::process(AmEvent* ev) {
// check for shutdown
if (ev->event_id == E_SYSTEM) {
AmSystemEvent* sys_ev = dynamic_cast<AmSystemEvent*>(ev);
if(sys_ev){
DBG("received system Event\n");
if (sys_ev->sys_event == AmSystemEvent::ServerShutdown) {
DBG("received system Event: ServerShutdown. Stopping event processing.\n");
processing_events = false;
}
}
}
onEvent(ev);
}

@ -1,75 +0,0 @@
/*
* Copyright (C) 2011 Stefan Sayer
*
* This file is part of SEMS, a free SIP media server.
*
* SEMS 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.
*
* For a license to use the SEMS software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* SEMS 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
*/
#ifndef _AmEventProcessingThread_h_
#define _AmEventProcessingThread_h_
#include "AmThread.h"
#include "AmEventQueue.h"
#include "AmEvent.h"
/**
AmEventProcessingThread processes events posted
in the event queue until either server shutdown
is signaled or processing is requested to stop
with the stop_processing function.
Override the onEvent(AmEvent* ev) method, create
an object, start() it and post events to it.
If you need queue policing (e.g. overflow protection),
override police_event() function.
*/
class AmEventProcessingThread
: public AmThread,
public AmEventQueue,
public AmEventHandler
{
bool processing_events;
void process(AmEvent* ev);
protected:
void run();
void on_stop();
virtual void onEvent(AmEvent* ev) { }
virtual bool police_event(AmEvent* ev);
public:
AmEventProcessingThread();
~AmEventProcessingThread();
void postEvent(AmEvent* ev);
void stop_processing();
};
#endif

@ -71,7 +71,7 @@ public:
AmEventQueue(AmEventHandler* handler);
virtual ~AmEventQueue();
virtual void postEvent(AmEvent*);
void postEvent(AmEvent*);
void processEvents();
void waitForEvent();
void wakeup();

@ -592,15 +592,6 @@ void AmRtpStream::bufferPacket(AmRtpPacket* p)
memcpy(&last_recv_time, &p->recv_time, sizeof(struct timeval));
if (!receiving && !passive) {
if (force_receive_dtmf &&
telephone_event_pt.get() && p->payload == telephone_event_pt->payload_type)
{
dtmf_payload_t* dpl = (dtmf_payload_t*)p->getData();
DBG("DTMF: event=%i; e=%i; r=%i; volume=%i; duration=%i; ts=%u\n",
dpl->event,dpl->e,dpl->r,dpl->volume,ntohs(dpl->duration),p->timestamp);
session->postDtmfEvent(new AmRtpDtmfEvent(dpl, getTelephoneEventRate(), p->timestamp));
}
mem.freePacket(p);
return;
}

@ -188,9 +188,6 @@ public:
/** should we receive packets? if not -> drop */
bool receiving;
/** should we receive RFC-2833-style DTMF even when receiving is disabled? */
bool force_receive_dtmf;
int send( unsigned int ts,
unsigned char* buffer,
unsigned int size );

@ -207,18 +207,19 @@ void AmSdp::print(string& body) const
for(std::vector<SdpMedia>::const_iterator media_it = media.begin();
media_it != media.end(); media_it++) {
out_buf += "m=" + media_t_2_str(media_it->type) + " " + int2str(media_it->port) + " " + transport_p_2_str(media_it->transport);
string options;
if (media_it->transport == TP_RTPAVP || media_it->transport == TP_RTPSAVP) {
string options;
for(std::vector<SdpPayload>::const_iterator pl_it = media_it->payloads.begin();
pl_it != media_it->payloads.end(); pl_it++) {
out_buf += " " + int2str(pl_it->payload_type);
if (!media_it->conn.address.empty())
options += "c=IN IP4 "+media_it->conn.address+"\r\n";
if (pl_it->encoding_name.empty()) // don't add rtpmap if no encoding name given
continue;
@ -240,16 +241,12 @@ void AmSdp::print(string& body) const
}
out_buf += "\r\n" + options;
} else {
// for other transports (UDP/UDPTL) just print out fmt
out_buf += " " + media_it->fmt;
// ... and continue with c=, attributes, ...
out_buf += " " + media_it->fmt + "\r\n";
}
if (!media_it->conn.address.empty())
out_buf += "\r\nc=IN IP4 "+media_it->conn.address;
out_buf += "\r\n" + options;
// add attributes (media level)
for (std::vector<SdpAttribute>::const_iterator a_it=

@ -211,11 +211,6 @@ const string& AmSession::getLocalTag() const
return dlg.local_tag;
}
const string& AmSession::getFirstBranch() const
{
return dlg.first_branch;
}
void AmSession::setUri(const string& uri)
{
DBG("AmSession::setUri(%s)\n",uri.c_str());
@ -914,11 +909,11 @@ void AmSession::onSipReply(const AmSipReply& reply,
DBG("negotiate_onreply = %s\n", negotiate_onreply?"true":"false");
if (negotiate_onreply) {
if(old_dlg_status < AmSipDialog::Connected){
DBG("old is < conn\n");
switch(dlg.getStatus()){
case AmSipDialog::Connected:
DBG("case conn\n");
try {
RTPStream()->setMonitorRTPTimeout(true);
@ -947,7 +942,7 @@ void AmSession::onSipReply(const AmSipReply& reply,
break;
case AmSipDialog::Pending:
DBG("case pending\n");
switch(reply.code){
// todo: 180 with body (remote rbt)
case 180: {

@ -328,10 +328,6 @@ public:
/** setter for rtp_str->receiving */
void setReceiving(bool receive) { RTPStream()->receiving = receive; }
/** setter for rtp_str->force_receive_dtmf*/
void setForceDtmfReceiving(bool receive) { RTPStream()->force_receive_dtmf = receive; }
/* ---- SIP dialog attributes ---- */
/** Gets the Session's call ID */
const string& getCallID() const;
@ -341,9 +337,6 @@ public:
/** Gets the Session's local tag */
const string& getLocalTag() const;
/** Gets the branch param of the first via in the original INVITE*/
const string& getFirstBranch() const;
/** Sets the Session's local tag if not set already */
void setLocalTag();

@ -195,10 +195,9 @@ void AmSessionContainer::destroySession(AmSession* s)
AmEventQueueInterface* q = AmEventDispatcher::instance()->
delEventQueue(s->getLocalTag(),
s->getCallID(),
s->getRemoteTag(),
s->getFirstBranch());
s->getRemoteTag());
if(q) {
if(q) {
stopAndQueue(s);
}
else {
@ -301,7 +300,7 @@ void AmSessionContainer::startSessionUAS(AmSipRequest& req)
}
switch(addSession(req.callid,req.from_tag,local_tag,
req.via_branch,session.get())) {
session.get())) {
case AmSessionContainer::Inserted:
// successful case
@ -331,8 +330,7 @@ void AmSessionContainer::startSessionUAS(AmSipRequest& req)
session->start();
} catch (...) {
AmEventDispatcher::instance()->
delEventQueue(req.callid,req.from_tag,local_tag,
req.via_branch);
delEventQueue(req.callid,req.from_tag,local_tag);
throw;
}
@ -357,12 +355,11 @@ void AmSessionContainer::startSessionUAS(AmSipRequest& req)
bool AmSessionContainer::postEvent(const string& callid,
const string& remote_tag,
const string& via_branch,
AmEvent* event)
{
bool posted =
AmEventDispatcher::instance()->
post(callid,remote_tag,via_branch,event);
AmEventDispatcher::instance()->
post(callid,remote_tag,event);
if(!posted)
delete event;
@ -449,7 +446,6 @@ AmSessionContainer::AddSessionStatus
AmSessionContainer::addSession(const string& callid,
const string& remote_tag,
const string& local_tag,
const string& via_branch,
AmSession* session)
{
if(_container_closed.get())
@ -457,7 +453,7 @@ AmSessionContainer::addSession(const string& callid,
if(AmEventDispatcher::instance()->
addEventQueue(local_tag,(AmEventQueue*)session,
callid,remote_tag,via_branch)) {
callid,remote_tag)) {
return Inserted;
}

@ -108,7 +108,6 @@ class AmSessionContainer : public AmThread
AddSessionStatus addSession(const string& callid,
const string& remote_tag,
const string& local_tag,
const string& via_branch,
AmSession* session);
/**
@ -140,9 +139,7 @@ class AmSessionContainer : public AmThread
* post an event into the event queue of the identified dialog.
* @return false if session doesn't exist
*/
bool postEvent(const string& callid,
const string& remote_tag,
const string& via_branch,
bool postEvent(const string& callid, const string& remote_tag,
AmEvent* event);
/**

@ -110,25 +110,19 @@ void AmSipDialog::updateStatus(const AmSipRequest& req)
// Sanity checks
if (r_cseq_i && req.cseq <= r_cseq){
string hdrs; bool i = false;
if (req.method == SIP_METH_NOTIFY) {
if (AmConfig::IgnoreNotifyLowerCSeq)
i = true;
else
// clever trick to not break subscription dialog usage
// for implementations which follow 3265 instead of 5057
hdrs = SIP_HDR_COLSP(SIP_HDR_RETRY_AFTER) "0" CRLF;
INFO("remote cseq lower than previous ones - refusing request\n");
// see 12.2.2
string hdrs;
if (req.method == "NOTIFY") {
// clever trick to not break subscription dialog usage
// for implementations which follow 3265 instead of 5057
hdrs = SIP_HDR_COLSP(SIP_HDR_RETRY_AFTER) "0" CRLF;
}
if (!i) {
INFO("remote cseq lower than previous ones - refusing request\n");
// see 12.2.2
reply_error(req, 500, SIP_REPLY_SERVER_INTERNAL_ERROR, hdrs,
next_hop_for_replies ? next_hop_ip : "",
next_hop_for_replies ? next_hop_port : 0);
return;
}
reply_error(req, 500, SIP_REPLY_SERVER_INTERNAL_ERROR, hdrs,
next_hop_for_replies ? next_hop_ip : "",
next_hop_for_replies ? next_hop_port : 0);
return;
}
if (req.method == "INVITE") {
@ -168,7 +162,6 @@ void AmSipDialog::updateStatus(const AmSipRequest& req)
remote_party = req.from;
local_party = req.to;
route = req.route;
first_branch = req.via_branch;
}
int cont = rel100OnRequestIn(req);
@ -1015,6 +1008,12 @@ int AmSipDialog::sendRequest(const string& method,
int flags)
{
string msg,ser_cmd;
string m_hdrs = hdrs;
if(hdl)
hdl->onSendRequest(method,content_type,body,m_hdrs,flags,cseq);
rel100OnRequestOut(method, m_hdrs);
AmSipRequest req;
@ -1034,9 +1033,10 @@ int AmSipDialog::sendRequest(const string& method,
if((method!="BYE")&&(method!="CANCEL"))
req.contact = getContactHdr();
req.hdrs = hdrs;
if(!m_hdrs.empty())
req.hdrs = m_hdrs;
if (!(flags&SIP_FLAGS_VERBATIM)) {
// add Signature
if (AmConfig::Signature.length())
@ -1053,11 +1053,6 @@ int AmSipDialog::sendRequest(const string& method,
req.body = body;
}
if(hdl)
hdl->onSendRequest(method,content_type,body,req.hdrs,flags,cseq);
rel100OnRequestOut(method, req.hdrs);
if (SipCtrlInterface::send(req, next_hop_ip, next_hop_port,outbound_interface))
return -1;

@ -173,8 +173,6 @@ class AmSipDialog
string remote_tag;
string local_tag;
string first_branch;
string remote_party; // To/From
string local_party; // To/From

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save