mirror of https://github.com/sipwise/sems.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
268 lines
7.1 KiB
268 lines
7.1 KiB
|
|
#include "MyCC.h"
|
|
|
|
#include "log.h"
|
|
#include "AmConfigReader.h"
|
|
#include "AmUtils.h"
|
|
#include "AmAudioFile.h"
|
|
#include "AmPlugIn.h"
|
|
#include "AmMediaProcessor.h"
|
|
|
|
#include <sys/time.h>
|
|
#include <time.h>
|
|
|
|
#define MOD_NAME "mycc"
|
|
|
|
#define TIMERID_CREDIT_TIMEOUT 1
|
|
|
|
EXPORT_SESSION_FACTORY(MyCCFactory,MOD_NAME);
|
|
|
|
string MyCCFactory::InitialAnnouncement;
|
|
string MyCCFactory::IncorrectPIN;
|
|
string MyCCFactory::OutOfCredit;
|
|
string MyCCFactory::Dialing;
|
|
string MyCCFactory::DialFailed;
|
|
string MyCCFactory::EnterNumber;
|
|
string MyCCFactory::ConnectSuffix;
|
|
|
|
MyCCFactory::MyCCFactory(const string& _app_name)
|
|
: AmSessionFactory(_app_name), user_timer_fact(NULL)
|
|
{
|
|
}
|
|
|
|
int MyCCFactory::onLoad()
|
|
{
|
|
AmConfigReader cfg;
|
|
if(cfg.loadFile(AmConfig::ModConfigPath + string(MOD_NAME ".conf")))
|
|
return -1;
|
|
|
|
InitialAnnouncement = cfg.getParameter("initial_announcement", "/tmp/hello.wav");
|
|
IncorrectPIN = cfg.getParameter("incorrect_pin", "/tmp/incorrect_pin.wav");
|
|
OutOfCredit = cfg.getParameter("out_of_credit", "/tmp/out_of_credit.wav");
|
|
Dialing = cfg.getParameter("dialing", "/tmp/dialing.wav");
|
|
DialFailed = cfg.getParameter("dial_failed", "/tmp/dial_failed.wav");
|
|
EnterNumber = cfg.getParameter("enter_number", "/tmp/enter_number.wav");
|
|
ConnectSuffix = cfg.getParameter("connect_suffix", "@127.0.0.1");
|
|
|
|
user_timer_fact = AmPlugIn::instance()->getFactory4Di("user_timer");
|
|
if(!user_timer_fact){
|
|
ERROR("could not load user_timer from session_timer plug-in\n");
|
|
return -1;
|
|
}
|
|
cc_acc_fact = AmPlugIn::instance()->getFactory4Di("cc_acc");
|
|
if(!cc_acc_fact){
|
|
ERROR("could not load cc_acc accounting, please provide a module\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
AmSession* MyCCFactory::onInvite(const AmSipRequest& req)
|
|
{
|
|
AmDynInvoke* user_timer = user_timer_fact->getInstance();
|
|
if(!user_timer){
|
|
ERROR("could not get a user timer reference\n");
|
|
throw AmSession::Exception(500,"could not get a user timer reference");
|
|
}
|
|
|
|
AmDynInvoke* cc_acc = cc_acc_fact->getInstance();
|
|
if(!user_timer){
|
|
ERROR("could not get a cc acc reference\n");
|
|
throw AmSession::Exception(500,"could not get a cc acc reference");
|
|
}
|
|
|
|
return new MyCCDialog(cc_acc, user_timer);
|
|
}
|
|
|
|
|
|
MyCCDialog::MyCCDialog(AmDynInvoke* cc_acc, AmDynInvoke* user_timer)
|
|
: playlist(this),
|
|
state(CC_Collecting_PIN),
|
|
cc_acc(cc_acc), user_timer(user_timer),
|
|
AmB2BCallerSession()
|
|
|
|
{
|
|
set_sip_relay_only(false);
|
|
memset(&acc_start, 0, sizeof(struct timeval));
|
|
}
|
|
|
|
MyCCDialog::~MyCCDialog()
|
|
{
|
|
}
|
|
|
|
|
|
void MyCCDialog::addToPlaylist(string fname) {
|
|
AmAudioFile* wav_file = new AmAudioFile();
|
|
if(wav_file->open(fname,AmAudioFile::Read)) {
|
|
ERROR("MyCCDialog::addToPlaylist: Cannot open file\n");
|
|
delete wav_file;
|
|
} else {
|
|
AmPlaylistItem* item = new AmPlaylistItem(wav_file, NULL);
|
|
playlist.addToPlaylist(item);
|
|
}
|
|
}
|
|
|
|
void MyCCDialog::onSessionStart(const AmSipRequest& req)
|
|
{
|
|
DBG("MyCCDialog::onSessionStart");
|
|
|
|
AmB2BCallerSession::onSessionStart(req);
|
|
|
|
setInOut(&playlist, &playlist);
|
|
addToPlaylist(MyCCFactory::InitialAnnouncement);
|
|
|
|
setDtmfDetectionEnabled(true);
|
|
}
|
|
|
|
void MyCCDialog::onDtmf(int event, int duration) {
|
|
DBG("MyCCDialog::onDtmf, got event %d, duration %d.\n", event, duration);
|
|
|
|
switch (state) {
|
|
case CC_Collecting_PIN: {
|
|
// flush the playlist (stop playing)
|
|
// if a key is entered
|
|
playlist.close();
|
|
|
|
if(event <10) {
|
|
pin +=int2str(event);
|
|
DBG("pin is now '%s'\n", pin.c_str());
|
|
} else {
|
|
AmArg di_args,ret;
|
|
di_args.push(pin.c_str());
|
|
cc_acc->invoke("getCredit", di_args, ret);
|
|
credit = ret.get(0).asInt();
|
|
if (credit < 0) {
|
|
addToPlaylist(MyCCFactory::IncorrectPIN);
|
|
pin = "";
|
|
} else if (credit == 0) {
|
|
addToPlaylist(MyCCFactory::OutOfCredit);
|
|
pin = "";
|
|
} else {
|
|
number = "";
|
|
state = CC_Collecting_Number;
|
|
addToPlaylist(MyCCFactory::EnterNumber);
|
|
}
|
|
}
|
|
} break;
|
|
case CC_Collecting_Number: {
|
|
// flush the playlist (stop playing)
|
|
// if a key is entered
|
|
playlist.close();
|
|
if(event <10) {
|
|
number +=int2str(event);
|
|
DBG("number is now '%s'\n", number.c_str());
|
|
} else {
|
|
if (getCalleeStatus() == None) {
|
|
state = CC_Dialing;
|
|
connectCallee(number + " <sip:" + number+ MyCCFactory::ConnectSuffix + ">",
|
|
"sip:"+number+MyCCFactory::ConnectSuffix);
|
|
addToPlaylist(MyCCFactory::Dialing);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case CC_Dialing:
|
|
case CC_Connected:
|
|
default: break;
|
|
};
|
|
}
|
|
|
|
void MyCCDialog::process(AmEvent* ev)
|
|
{
|
|
DBG("MyCCDialog::process\n");
|
|
|
|
AmAudioEvent* audio_ev = dynamic_cast<AmAudioEvent*>(ev);
|
|
if(audio_ev && (audio_ev->event_id == AmAudioEvent::noAudio)){
|
|
DBG("MyCCDialog::process: Playlist is empty!\n");
|
|
return;
|
|
}
|
|
|
|
AmPluginEvent* plugin_event = dynamic_cast<AmPluginEvent*>(ev);
|
|
if(plugin_event && plugin_event->name == "timer_timeout") {
|
|
int timer_id = plugin_event->data.get(0).asInt();
|
|
if (timer_id == TIMERID_CREDIT_TIMEOUT) {
|
|
DBG("timer timeout: no credit...\n");
|
|
stopAccounting();
|
|
terminateOtherLeg();
|
|
terminateLeg();
|
|
|
|
ev->processed = true;
|
|
return;
|
|
}
|
|
}
|
|
|
|
AmB2BCallerSession::process(ev);
|
|
}
|
|
|
|
bool MyCCDialog::onOtherReply(const AmSipReply& reply) {
|
|
DBG("OnOtherReply \n");
|
|
if (state == CC_Dialing) {
|
|
if (reply.code < 200) {
|
|
DBG("Callee is trying... code %d\n", reply.code);
|
|
} else if(reply.code < 300){
|
|
if (getCalleeStatus() == Connected) {
|
|
state = CC_Connected;
|
|
startAccounting();
|
|
// clear audio input and output
|
|
setInOut(NULL, NULL);
|
|
// detach from media processor (not in RTP path any more)
|
|
AmMediaProcessor::instance()->removeSession(this);
|
|
// set the call timer
|
|
AmArg di_args,ret;
|
|
di_args.push(TIMERID_CREDIT_TIMEOUT);
|
|
di_args.push(credit); // in seconds
|
|
di_args.push(dlg.local_tag.c_str());
|
|
user_timer->invoke("setTimer", di_args, ret);
|
|
}
|
|
} else {
|
|
DBG("Callee final error with code %d\n",reply.code);
|
|
addToPlaylist(MyCCFactory::DialFailed);
|
|
number = "";
|
|
state = CC_Collecting_Number;
|
|
}
|
|
}
|
|
// we dont call
|
|
// AmB2BCallerSession::onOtherReply(reply);
|
|
// as it tears down the call if callee could
|
|
// not be reached
|
|
return false;
|
|
}
|
|
|
|
void MyCCDialog::onOtherBye(const AmSipRequest& req) {
|
|
DBG("onOtherBye\n");
|
|
stopAccounting();
|
|
AmB2BCallerSession::onOtherBye(req); // will stop the session
|
|
}
|
|
|
|
void MyCCDialog::onBye(const AmSipRequest& req)
|
|
{
|
|
DBG("onBye: stopSession\n");
|
|
if (state == CC_Connected) {
|
|
stopAccounting();
|
|
}
|
|
terminateOtherLeg();
|
|
setStopped();
|
|
}
|
|
|
|
void MyCCDialog::startAccounting() {
|
|
gettimeofday(&acc_start,NULL);
|
|
DBG("start accounting at %ld\n", acc_start.tv_sec);
|
|
}
|
|
|
|
void MyCCDialog::stopAccounting() {
|
|
if ((acc_start.tv_sec != 0) || (acc_start.tv_usec != 0)) {
|
|
struct timeval now;
|
|
gettimeofday(&now,NULL);
|
|
DBG("stop accounting at %ld\n", now.tv_sec);
|
|
timersub(&now,&acc_start,&now);
|
|
if (now.tv_usec>500000) now.tv_sec++;
|
|
DBG("Call lasted %ld seconds\n", now.tv_sec);
|
|
|
|
AmArg di_args,ret;
|
|
di_args.push(pin.c_str());
|
|
di_args.push((int)now.tv_sec);
|
|
cc_acc->invoke("subtractCredit", di_args, ret);
|
|
}
|
|
}
|