/* * $Id$ * * 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 ser 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 "SessionTimer.h" #include "AmUtils.h" #include "UserTimer.h" #include "AmSipHeaders.h" EXPORT_SESSION_EVENT_HANDLER_FACTORY(SessionTimerFactory, MOD_NAME); int SessionTimerFactory::onLoad() { return 0; } bool SessionTimerFactory::onInvite(const AmSipRequest& req) { if(!checkSessionExpires(req)) return true; return false; } AmSessionEventHandler* SessionTimerFactory::getHandler(AmSession* s) { return new SessionTimer(s); } SessionTimer::SessionTimer(AmSession* s) :AmSessionEventHandler(), s(s), session_interval(0), session_refresher(refresh_remote) {} bool SessionTimer::process(AmEvent* ev) { AmTimeoutEvent* timeout_ev = dynamic_cast(ev); if (timeout_ev) { DBG("received timeout Event with ID %d\n", timeout_ev->data.get(0).asInt()); onTimeoutEvent(timeout_ev); return true; } return false; } bool SessionTimer::onSipEvent(AmSipEvent* ev) { return false; } bool SessionTimer::onSipRequest(const AmSipRequest& req) { updateTimer(s,req); return false; } bool SessionTimer::onSipReply(const AmSipReply& reply) { updateTimer(s,reply); return false; } bool SessionTimer::onSendRequest(const string& method, const string& content_type, const string& body, string& hdrs, int flags, unsigned int cseq) { string m_hdrs = SIP_HDR_COLSP(SIP_HDR_SUPPORTED) "timer" CRLF; if ((method != "INVITE") && (method != "UPDATE")) goto end; m_hdrs += "Session-Expires: "+ int2str(session_timer_conf.getSessionExpires()) +CRLF + "Min-SE: " + int2str(session_timer_conf.getMinimumTimer()) + CRLF; end: hdrs += m_hdrs; return false; } bool SessionTimer::onSendReply(const AmSipRequest& req, unsigned int code,const string& reason, const string& content_type,const string& body, string& hdrs, int flags) { //if (!session_timer_conf.getEnableSessionTimer()) // return ""; string m_hdrs = SIP_HDR_COLSP(SIP_HDR_SUPPORTED) "timer" CRLF; if ((req.method != "INVITE") && (req.method != "UPDATE")) return false; // only in 2xx responses to INV/UPD m_hdrs += "Session-Expires: " + int2str(session_interval) + ";refresher="+ (session_refresher_role==UAC ? "uac":"uas")+CRLF; if (((session_refresher_role==UAC) && (session_refresher==refresh_remote)) || ((session_refresher_role==UAS) && remote_timer_aware)) m_hdrs += SIP_HDR_COLSP(SIP_HDR_REQUIRED) "timer" CRLF; hdrs += m_hdrs; return false; } /* Session Timer: -ssa */ void SessionTimer::configureSessionTimer(const AmSessionTimerConfig& conf) { session_timer_conf = conf; DBG("Configured session with EnableSessionTimer = %s, SessionExpires = %u, MinimumTimer = %u\n", session_timer_conf.getEnableSessionTimer() ? "yes":"no", session_timer_conf.getSessionExpires(), session_timer_conf.getMinimumTimer() ); } /** * check if UAC requests too low Session-Expires * ( min) min = i_minse; if ((session_timer_conf.getSessionExpires() < min)|| (session_interval= 300)) return; // determine session interval string sess_expires_hdr = getHeader(reply.hdrs, "Session-Expires"); if (sess_expires_hdr.empty()) sess_expires_hdr = getHeader(reply.hdrs, "x"); // compact form session_interval = session_timer_conf.getSessionExpires(); session_refresher = refresh_local; session_refresher_role = UAC; if (!sess_expires_hdr.empty()) { //session_interval = get_session_interval_from(req); unsigned int sess_i_tmp = 0; if (str2i(strip_header_params(sess_expires_hdr), sess_i_tmp)) { WARN("error while parsing Session-Expires header value '%s'\n", strip_header_params(sess_expires_hdr).c_str()); // exception? } else { // this is forbidden by rfc, but to be sure against 'rogue' proxy/uas if (sess_i_tmp < session_timer_conf.getMinimumTimer()) { session_interval = session_timer_conf.getMinimumTimer(); } else { session_interval = sess_i_tmp; } } if (get_header_param(sess_expires_hdr, "refresher") == "uas") { session_refresher = refresh_remote; session_refresher_role = UAS; } } removeTimers(s); setTimers(s); } void SessionTimer::setTimers(AmSession* s) { // set session timer DBG("Setting session interval timer: %ds, tag '%s'\n", session_interval, s->getLocalTag().c_str()); UserTimer::instance()-> setTimer(ID_SESSION_INTERVAL_TIMER, session_interval, s->getLocalTag()); // set session refresh action timer, after half the expiration if (session_refresher == refresh_local) { DBG("Setting session refresh timer: %ds, tag '%s'\n", session_interval/2, s->getLocalTag().c_str()); UserTimer::instance()-> setTimer(ID_SESSION_REFRESH_TIMER, session_interval/2, s->getLocalTag()); } } void SessionTimer::removeTimers(AmSession* s) { UserTimer::instance()-> removeTimer(ID_SESSION_REFRESH_TIMER, s->getLocalTag()); UserTimer::instance()-> removeTimer(ID_SESSION_INTERVAL_TIMER, s->getLocalTag()); } void SessionTimer::onTimeoutEvent(AmTimeoutEvent* timeout_ev) { int timer_id = timeout_ev->data.get(0).asInt(); if (timer_id == ID_SESSION_REFRESH_TIMER) { if (session_refresher == refresh_local) // send reinvite with SDP s->sendReinvite(true); else WARN("need session refresh but remote session is refresher\n"); } else if (timer_id == ID_SESSION_INTERVAL_TIMER) { // // let the session know it got timeout // onTimeout(); s->dlg.bye(); s->setStopped(); } else { DBG("unknown timeout event received.\n"); } return; }