diff --git a/apps/call_timer/CallTimer.cpp b/apps/call_timer/CallTimer.cpp new file mode 100644 index 00000000..3087a5be --- /dev/null +++ b/apps/call_timer/CallTimer.cpp @@ -0,0 +1,269 @@ +/* + * $Id: $ + * + * Copyright (C) 2008 iptego GmbH + * Based on the concept of auth_b2b, Copyright (C) 2008 iptego GmbH + * Based on the concept of mycc, Copyright (C) 2007 Sipwise GmbH + * Based on the concept of sw_prepaid_sip, Copyright (C) 2002-2003 Fhg Fokus + * + * 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 "CallTimer.h" + +#include "log.h" +#include "AmUtils.h" +#include "AmAudio.h" +#include "AmPlugIn.h" +#include "AmMediaProcessor.h" +#include "AmConfigReader.h" +#include "AmSessionContainer.h" + +#define TIMERID_CALL_TIMER 1 + +#define DEFAULT_CALL_TIMER 1200 // 2 h default call timer + +unsigned int CallTimerFactory::DefaultCallTimer; +bool CallTimerFactory::UseAppParam = true; + +EXPORT_SESSION_FACTORY(CallTimerFactory,MOD_NAME); + +CallTimerFactory::CallTimerFactory(const string& _app_name) +: AmSessionFactory(_app_name), + user_timer_fact(NULL) +{ +} + +CallTimerFactory::~CallTimerFactory() { +} + + + +int CallTimerFactory::onLoad() +{ + AmConfigReader cfg; + if(cfg.loadFile(AmConfig::ModConfigPath + string(MOD_NAME ".conf"))) { + DBG("using default timer of %d seconds\n", DEFAULT_CALL_TIMER); + DefaultCallTimer = DEFAULT_CALL_TIMER; + } else { + DefaultCallTimer = cfg.getParameterInt("default_call_time", DEFAULT_CALL_TIMER); + UseAppParam = (cfg.getParameter("use_app_param") == "yes"); + } + + 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; + } + + return 0; +} + + +AmSession* CallTimerFactory::onInvite(const AmSipRequest& req) +{ + DBG(" *** creating new call timer session ***\n"); + 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"); + } + + string app_param = getHeader(req.hdrs, PARAM_HDR); + + unsigned int call_time = CallTimerFactory::DefaultCallTimer; + + if (CallTimerFactory::UseAppParam) { + if (!app_param.length()) { + INFO("call_time: no call timer parameters found. " + "Using default value of %d seconds\n", + CallTimerFactory::DefaultCallTimer); + } else { + string call_time_s = get_header_keyvalue(app_param,"t", "Timer"); + + if (str2i(call_time_s, call_time)) { + WARN("Error decoding call time value '%s'. Using default time of %d\n", + call_time_s.c_str(), call_time); + } + } + } + + DBG("using call timer %d seconds\n", call_time); + + return new CallTimerDialog(user_timer, call_time); +} + + +CallTimerDialog::CallTimerDialog(AmDynInvoke* user_timer, + unsigned int call_time) +: m_state(BB_Init), + call_time(call_time), + m_user_timer(user_timer), + AmB2BCallerSession() + +{ + set_sip_relay_only(false); +} + + +CallTimerDialog::~CallTimerDialog() +{ +} + +void CallTimerDialog::onInvite(const AmSipRequest& req) +{ + + if (dlg.getStatus() == AmSipDialog::Connected) { + DBG("not acting on re-Invite\n"); + return; + } + + setReceiving(false); + AmMediaProcessor::instance()->removeSession(this); + + m_state = BB_Dialing; + + if(dlg.reply(req, 100, "Trying") != 0) { + throw AmSession::Exception(500,"Failed to reply 100"); + } + + invite_req = req; + size_t pos1, pos2, hdr_start; + + // remove P-App-Name, P-App-Param header + if (findHeader(invite_req.hdrs,PARAM_HDR, pos1, pos2, + hdr_start)) { + while (invite_req.hdrs[pos2]=='\r' ||invite_req.hdrs[pos2]=='\n') + pos2++; + + hdr_start -= 11; //"P-App-Param" + invite_req.hdrs.erase(hdr_start, pos2-hdr_start); + } + + if (findHeader(invite_req.hdrs,"P-App-Name", pos1, pos2, + hdr_start)) { + while (invite_req.hdrs[pos2]=='\r' ||invite_req.hdrs[pos2]=='\n') + pos2++; + hdr_start -= 10; //"P-App-Name" + invite_req.hdrs.erase(hdr_start, pos2-hdr_start); + } + + dlg.updateStatus(invite_req); + recvd_req.insert(std::make_pair(invite_req.cseq,invite_req)); + + set_sip_relay_only(true); + connectCallee(invite_req.to, invite_req.r_uri, true); +} + +void CallTimerDialog::process(AmEvent* ev) +{ + AmPluginEvent* plugin_event = dynamic_cast(ev); + if(plugin_event && plugin_event->name == "timer_timeout") { + int timer_id = plugin_event->data.get(0).asInt(); + if (timer_id == TIMERID_CALL_TIMER) { + DBG("timer timeout.\n"); + terminateOtherLeg(); + dlg.bye(); + terminateLeg(); + ev->processed = true; + return; + } + } + + AmB2BCallerSession::process(ev); +} + + +bool CallTimerDialog::onOtherReply(const AmSipReply& reply) +{ + bool ret = false; + + if (m_state == BB_Dialing) { + if (reply.code < 200) { + DBG("Callee is trying... code %d\n", reply.code); + } + else if(reply.code < 300) { + if(getCalleeStatus() == Connected) { + m_state = BB_Connected; + setInOut(NULL, NULL); + // startAccounting(); + // set the call timer + AmArg di_args,ret; + di_args.push(TIMERID_CALL_TIMER); + di_args.push((int)call_time); // in seconds + di_args.push(dlg.local_tag.c_str()); + m_user_timer->invoke("setTimer", di_args, ret); + } + } + else if(reply.code == 487 && dlg.getStatus() == AmSipDialog::Pending) { + DBG("Stopping leg A on 487 from B with 487\n"); + dlg.reply(invite_req, 487, "Request terminated"); + setStopped(); + ret = true; + } + else if (reply.code >= 300 && dlg.getStatus() == AmSipDialog::Connected) { + DBG("Callee final error in connected state with code %d\n",reply.code); + terminateLeg(); + } + else if (reply.code >= 300 && m_state == BB_Dialing) { + DBG("Callee final error with code %d\n",reply.code); + AmB2BCallerSession::onOtherReply(reply); + // reset into non-b2b mode to get possible INVITE again + sip_relay_only = false; + } else { + DBG("Callee final error with code %d\n",reply.code); + AmB2BCallerSession::onOtherReply(reply); + } + } + return ret; +} + + +void CallTimerDialog::onOtherBye(const AmSipRequest& req) +{ +// stopAccounting(); + AmB2BCallerSession::onOtherBye(req); +} + + +void CallTimerDialog::onBye(const AmSipRequest& req) +{ + if (m_state == BB_Connected) { +// stopAccounting(); + } + terminateOtherLeg(); + setStopped(); +} + + +void CallTimerDialog::onCancel() +{ + if(dlg.getStatus() == AmSipDialog::Pending) { + DBG("Wait for leg B to terminate"); + } + else { + DBG("Canceling leg A on CANCEL since dialog is not pending"); + dlg.reply(invite_req, 487, "Request terminated"); + setStopped(); + } +} + diff --git a/apps/call_timer/CallTimer.h b/apps/call_timer/CallTimer.h new file mode 100644 index 00000000..358afdd8 --- /dev/null +++ b/apps/call_timer/CallTimer.h @@ -0,0 +1,85 @@ +/* + * $Id: $ + * + * Copyright (C) 2008 iptego GmbH + * Based on the concept of auth_b2b, Copyright (C) 2008 iptego GmbH + * Based on the concept of sw_prepaid_sip, Copyright (C) 2007 Sipwise GmbH + * Based on the concept of mycc, Copyright (C) 2002-2003 Fhg Fokus + * + * 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 _AUTH_B2B_H +#define _AUTH_B2B_H + +#include "AmB2BSession.h" + +using std::string; + +class CallTimerFactory: public AmSessionFactory +{ + public: + AmDynInvokeFactory* user_timer_fact; + + static unsigned int DefaultCallTimer; + static bool UseAppParam; + + CallTimerFactory(const string& _app_name); + ~CallTimerFactory(); + + int onLoad(); + AmSession* onInvite(const AmSipRequest& req); +}; + +class CallTimerDialog : public AmB2BCallerSession +{ + enum { + BB_Init = 0, + BB_Dialing, + BB_Connected, + BB_Teardown + } CallerState; + + int m_state; + + AmDynInvoke* m_user_timer; + + unsigned int call_time; + + public: + + CallTimerDialog(AmDynInvoke* user_timer, + unsigned int call_time); + ~CallTimerDialog(); + + void process(AmEvent* ev); + void onBye(const AmSipRequest& req); + void onInvite(const AmSipRequest& req); + void onCancel(); + + + protected: + + bool onOtherReply(const AmSipReply& reply); + void onOtherBye(const AmSipRequest& req); +}; +#endif diff --git a/apps/call_timer/Makefile b/apps/call_timer/Makefile new file mode 100644 index 00000000..c2380e70 --- /dev/null +++ b/apps/call_timer/Makefile @@ -0,0 +1,7 @@ +plug_in_name = call_timer + +module_ldflags = +module_cflags = -DMOD_NAME=\"$(plug_in_name)\" + +COREPATH ?= ../../core +include $(COREPATH)/plug-in/Makefile.app_module diff --git a/apps/call_timer/etc/call_timer.conf b/apps/call_timer/etc/call_timer.conf new file mode 100644 index 00000000..ff4da06b --- /dev/null +++ b/apps/call_timer/etc/call_timer.conf @@ -0,0 +1,13 @@ + +# +# use_app_param=[yes|no] +# +# sets whether App-Param header is used for call time value +# (or default_call_time below) +# +use_app_param=yes + +# +# call timer value used if not in P-App-Param. in seconds +# +default_call_time=1200 \ No newline at end of file diff --git a/doc/Readme.call_timer b/doc/Readme.call_timer new file mode 100644 index 00000000..12d0ba76 --- /dev/null +++ b/doc/Readme.call_timer @@ -0,0 +1,23 @@ +call_timer application +---------------------- + +call_timer is a simple back-to-back user agent application +that ends the call after a call timer expired. + +If use_app_param is configured to "yes", then the call timer value +is taken from App-Param "t" or "Timer" value. If it is set to "no" +or that is not present, the configured default value is used. + +The default value is configured with default_call_time config option. +If that is not present, 1200 seconds are used. + +Examples (ser script): + remove_hf("P-App-Param"); + append_hf("P-App-Param: t=120\r\n"); + t_relay_to_udp("10.0.0.3","5070"); + + remove_hf("P-App-Param"); + append_hf("P-App-Param: Timer=120\r\n"); + t_relay_to_udp("10.0.0.3","5070"); + +