mirror of https://github.com/sipwise/sems.git
parent
4dc5f23b7b
commit
d8b3cbf475
@ -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
|
||||
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
|
||||
@ -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
|
||||
@ -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(®);
|
||||
}
|
||||
}
|
||||
|
||||
int TsRegex::regcomp(const char *regex, int cflags) {
|
||||
m.lock();
|
||||
if (i) {
|
||||
regfree(®);
|
||||
}
|
||||
int res = ::regcomp(®, 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(®, _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
|
||||
@ -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;
|
||||
@ -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')
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue