diff --git a/daemon/src/sip/sipaccount.cpp b/daemon/src/sip/sipaccount.cpp index fdd463449f0e624cae0ad0f99c1eca0473013fa3..efde26c18c528e5b3c463427eb14ffdaf1bcdacf 100644 --- a/daemon/src/sip/sipaccount.cpp +++ b/daemon/src/sip/sipaccount.cpp @@ -38,6 +38,7 @@ #include <sstream> #include <cassert> + SIPAccount::SIPAccount(const std::string& accountID) : Account(accountID, "SIP") , transport_(NULL) @@ -82,6 +83,7 @@ SIPAccount::SIPAccount(const std::string& accountID) , zrtpHelloHash_(true) , zrtpNotSuppWarning_(true) , registrationStateDetailed_() + , keepAliveTimer_() { link_ = SIPVoIPLink::instance(); } @@ -234,9 +236,9 @@ void SIPAccount::serialize(Conf::YamlEmitter *emitter) delete node->getValue(REALM); delete node; } -} - + +} void SIPAccount::unserialize(Conf::MappingNode *map) { @@ -567,6 +569,31 @@ void SIPAccount::unregisterVoIPLink() } } +void SIPAccount::startKeepAliveTimer() { + pj_time_val keepAliveDelay_; + + if (isTlsEnabled()) + return; + + keepAliveTimer_.cb = &SIPAccount::keepAliveRegistrationCb; + keepAliveTimer_.user_data = (void *)this; + + // expiration may no be determined when during the first registration request + if(registrationExpire_ == 0) { + keepAliveDelay_.sec = 60; + } + else { + keepAliveDelay_.sec = registrationExpire_; + } + keepAliveDelay_.msec = 0; + + reinterpret_cast<SIPVoIPLink *>(link_)->registerKeepAliveTimer(keepAliveTimer_, keepAliveDelay_); +} + +void SIPAccount::stopKeepAliveTimer() { + reinterpret_cast<SIPVoIPLink *>(link_)->cancelKeepAliveTimer(keepAliveTimer_); +} + pjsip_ssl_method SIPAccount::sslMethodStringToPjEnum(const std::string& method) { if (method == "Default") @@ -745,6 +772,25 @@ std::string SIPAccount::getContactHeader(const std::string& address, const std:: address + ":" + port + transport + ">"; } +void SIPAccount::keepAliveRegistrationCb(UNUSED pj_timer_heap_t *th, pj_timer_entry *te) +{ + SIPAccount *sipAccount = reinterpret_cast<SIPAccount *>(te->user_data); + + if (sipAccount->isTlsEnabled()) + return; + + if(sipAccount->isRegistered()) { + + // send a new register request + sipAccount->registerVoIPLink(); + + // make sure the current timer is deactivated + sipAccount->stopKeepAliveTimer(); + + // register a new timer + sipAccount->startKeepAliveTimer(); + } +} namespace { std::string computeMd5HashFromCredential( diff --git a/daemon/src/sip/sipaccount.h b/daemon/src/sip/sipaccount.h index 076fe6076c967e381219594d24a8eea01cd103eb..32d5671ddc3b7da07528e45b3f5815361bdff475 100644 --- a/daemon/src/sip/sipaccount.h +++ b/daemon/src/sip/sipaccount.h @@ -143,6 +143,19 @@ class SIPAccount : public Account { */ void unregisterVoIPLink(); + /** + * Start the keep alive function, once started, the account will be registered periodically + * a new REGISTER request is sent bey the client application. The account must be initially + * registered for this call to be effective. + */ + void startKeepAliveTimer(); + + /** + * Stop the keep alive timer. Once canceled, no further registration will be scheduled + */ + void stopKeepAliveTimer(); + + pjsip_cred_info *getCredInfo() const { return cred_; } @@ -174,6 +187,15 @@ class SIPAccount : public Account { return registrationExpire_; } + /** + * Set the expiration for this account as found in + * the "Expire" sip header or the Contact "expire" param. + */ + void setRegistrationExpire(int expire) { + if(expire > 0) + registrationExpire_ = expire; + } + /** * Doubles the Expiration Interval of Contact Addresses. */ @@ -188,10 +210,16 @@ class SIPAccount : public Account { bool userMatch(const std::string& username) const; bool hostnameMatch(const std::string& hostname) const; - /* Registration flag */ - bool isRegister() const { + /** + * Registration flag + */ + bool isRegistered() const { return bRegister_; } + + /** + * Set registration flag + */ void setRegister(bool result) { bRegister_ = result; } @@ -410,6 +438,12 @@ class SIPAccount : public Account { return zrtpHelloHash_; } + /** + * Timer used to periodically send re-register request based + * on the "Expire" sip header (or the "expire" Contact parameter) + */ + static void keepAliveRegistrationCb(pj_timer_heap_t *th, pj_timer_entry *te); + pjsip_transport* transport_; private: NON_COPYABLE(SIPAccount); @@ -442,9 +476,14 @@ class SIPAccount : public Account { */ static std::string getLoginName(); - // The pjsip client registration information + /** + * The pjsip client registration information + */ pjsip_regc *regc_; - // To check if the account is registered + + /** + * To check if the account is registered + */ bool bRegister_; // Network settings @@ -517,6 +556,17 @@ class SIPAccount : public Account { * This is a protocol Code:Description pair. */ std::pair<int, std::string> registrationStateDetailed_; + + /** + * Timer used to regularrly send re-register request based + * on the "Expire" sip header (or the "expire" Contact parameter) + */ + pj_timer_entry keepAliveTimer_; + + /** + * Delay coresponding to a registration interval + */ + // pj_time_val keepAliveDelay_; }; #endif diff --git a/daemon/src/sip/sipvoiplink.cpp b/daemon/src/sip/sipvoiplink.cpp index cc59163995c5acc63da22704499131dffb7d7aec..e0f00a7df9a3b0fe68f610c6a623a384ef0676cd 100644 --- a/daemon/src/sip/sipvoiplink.cpp +++ b/daemon/src/sip/sipvoiplink.cpp @@ -346,6 +346,10 @@ void SIPVoIPLink::sendRegister(Account *a) pjsip_transport_dec_ref(account->transport_); account->setRegistrationInfo(regc); + + // start the periodic registration request based on Expire header + // account determines itself if a keep alive is required + account->startKeepAliveTimer(); } void SIPVoIPLink::sendUnregister(Account *a) @@ -353,11 +357,14 @@ void SIPVoIPLink::sendUnregister(Account *a) SIPAccount *account = dynamic_cast<SIPAccount *>(a); // This may occurs if account failed to register and is in state INVALID - if (!account->isRegister()) { + if (!account->isRegistered()) { account->setRegistrationState(Unregistered); return; } + // Make sure to cancel any ongoing timers before unregister + account->stopKeepAliveTimer(); + pjsip_regc *regc = account->getRegistrationInfo(); if (!regc) @@ -374,6 +381,21 @@ void SIPVoIPLink::sendUnregister(Account *a) account->setRegister(false); } +void SIPVoIPLink::registerKeepAliveTimer(pj_timer_entry& timer, pj_time_val& delay) +{ + pj_status_t status; + + status = pjsip_endpt_schedule_timer(endpt_, &timer, &delay); + if(status != PJ_SUCCESS) { + ERROR("Could not schedule new timer in pjsip endpoint"); + } +} + +void SIPVoIPLink::cancelKeepAliveTimer(pj_timer_entry& timer) +{ + pjsip_endpt_cancel_timer(endpt_, &timer); +} + Call *SIPVoIPLink::newOutgoingCall(const std::string& id, const std::string& toUrl) { SIPAccount *account = dynamic_cast<SIPAccount *>(Manager::instance().getAccount(Manager::instance().getAccountFromCall(id))); @@ -1599,6 +1621,8 @@ void registration_cb(struct pjsip_regc_cbparam *param) std::pair<int, std::string> details(param->code, state); // TODO: there id a race condition for this ressource when closing the application account->setRegistrationStateDetailed(details); + + account->setRegistrationExpire(param->expiration); } if (param->status != PJ_SUCCESS) { @@ -1642,7 +1666,7 @@ void registration_cb(struct pjsip_regc_cbparam *param) SIPVoIPLink::instance()->shutdownSipTransport(account); } else { - if (account->isRegister()) + if (account->isRegistered()) account->setRegistrationState(Registered); else { account->setRegistrationState(Unregistered); diff --git a/daemon/src/sip/sipvoiplink.h b/daemon/src/sip/sipvoiplink.h index 1c2fceb9bc8fe1f63814dd1cd071e8c1d748e965..665171e1d9e8cf1e3ebc581e11edee5043cafca4 100644 --- a/daemon/src/sip/sipvoiplink.h +++ b/daemon/src/sip/sipvoiplink.h @@ -99,6 +99,16 @@ class SIPVoIPLink : public VoIPLink { */ virtual void sendUnregister(Account *a); + /** + * Register a new keepalive registration timer to this endpoint + */ + void registerKeepAliveTimer(pj_timer_entry& timer, pj_time_val& delay); + + /** + * Abort currently registered timer + */ + void cancelKeepAliveTimer(pj_timer_entry& timer); + /** * Place a new call * @param id The call identifier