diff --git a/daemon/src/client/callmanager.h b/daemon/src/client/callmanager.h index e0733b53b427268d2f7a0fbee220917359d844a7..7bd367899dafd394db72c075d27f8fba547c1088 100644 --- a/daemon/src/client/callmanager.h +++ b/daemon/src/client/callmanager.h @@ -182,8 +182,8 @@ class CallManager /* Presence subscription/Notification. */ void enablePresence(const std::string& accountID, const bool& flag); - void sendPresence(const std::string& accountID, const std::string& status, const std::string& note); - void approvePresSubServer(const bool& flag, const std::string& uri); + void sendPresence(const std::string& accountID, const bool& status, const std::string& note); + void approvePresSubServer(const std::string& uri, const bool& flag); void subscribePresSubClient(const std::string& accountID, const std::string& uri, const bool& flag); private: diff --git a/daemon/src/client/dbus/callmanager-introspec.xml b/daemon/src/client/dbus/callmanager-introspec.xml index efee97a78b9bf9d3f853e6854d8271f85f711bf8..c1777487114beace2d65e78eb1d9e38dea9b8c0e 100644 --- a/daemon/src/client/dbus/callmanager-introspec.xml +++ b/daemon/src/client/dbus/callmanager-introspec.xml @@ -836,14 +836,14 @@ <method name="sendPresence" tp:name-for-bindings="sendPresence"> <tp:added version="0.9.7"/> <arg type="s" name="accountID" direction="in"/> - <arg type="s" name="status" direction="in"/> + <arg type="b" name="status" direction="in"/> <arg type="s" name="note" direction="in"/> </method> <method name="approvePresSubServer" tp:name-for-bindings="approvePresSubServer"> <tp:added version="0.9.7"/> - <arg type="b" name="flag" direction="in"/> <arg type="s" name="uri" direction="in"/> + <arg type="b" name="flag" direction="in"/> </method> <signal name="newPresSubClientNotification" tp:name-for-bindings="newPresSubClientNotification"> diff --git a/daemon/src/client/dbus/callmanager.cpp b/daemon/src/client/dbus/callmanager.cpp index d7822f41daf6413efeb85b6dc221861ce5c16f84..9f5d9b44ca043e6bc25d7f8a9cf542372be238a0 100644 --- a/daemon/src/client/dbus/callmanager.cpp +++ b/daemon/src/client/dbus/callmanager.cpp @@ -461,14 +461,14 @@ CallManager::enablePresence(const std::string& accountID, const bool& flag){ * Notify for IP2IP account and publish for PBX account */ void -CallManager::sendPresence(const std::string& accountID, const std::string& status, const std::string& note) +CallManager::sendPresence(const std::string& accountID, const bool& status, const std::string& note) { SIPAccount *sipaccount = Manager::instance().getSipAccount(accountID); if (!sipaccount) - ERROR("Could not find account %s",accountID.c_str()); + ERROR("Could not find account %s.",accountID.c_str()); else{ - DEBUG("Send Presence (acc:%s)",accountID.c_str()); - sipaccount->getPresence()->sendPresence(status,note); + DEBUG("Send Presence (acc:%s, status %s).",accountID.c_str(),status? "online":"offline"); + sipaccount->getPresence()->sendPresence(status, note); } } @@ -476,13 +476,13 @@ CallManager::sendPresence(const std::string& accountID, const std::string& statu * Accept or not a PresSubServer request for IP2IP account */ void -CallManager::approvePresSubServer(const bool& flag, const std::string& buddySipUri) +CallManager::approvePresSubServer(const std::string& uri, const bool& flag) { SIPAccount *sipaccount = Manager::instance().getIP2IPAccount(); if (!sipaccount) ERROR("Could not find account IP2IP"); else{ - DEBUG("Approve presence (acc:IP2IP, buddy:%s)", buddySipUri.c_str()); - sipaccount->getPresence()->approvePresSubServer(flag, buddySipUri); + DEBUG("Approve presence (acc:IP2IP, serv:%s, flag:%s)", uri.c_str(), flag? "true":"false"); + sipaccount->getPresence()->approvePresSubServer(uri, flag); } } diff --git a/daemon/src/sip/pres_sub_client.cpp b/daemon/src/sip/pres_sub_client.cpp index ee41e339c0506e258f197cc38753e4dc10e71c4b..f0f89743ad25e89e3c60d697990ebc2ce9b78878 100644 --- a/daemon/src/sip/pres_sub_client.cpp +++ b/daemon/src/sip/pres_sub_client.cpp @@ -47,26 +47,25 @@ #include "sipaccount.h" #include "sippresence.h" #include "sipvoiplink.h" - +#include "sip_utils.h" #include "manager.h" #include "logger.h" -#define BUDDY_SUB_TERM_REASON_LEN 32 -#define PRES_TIMER 300 +#define PRES_TIMER 300 // 5min -int modId; +int modId; // used to extract data structure from event_subscription void pres_client_timer_cb(pj_timer_heap_t *th, pj_timer_entry *entry) { - (void) th; - PresSubClient *b = (PresSubClient *) entry->user_data; - b->reportPresence(); + //(void) th; + /* TODO : clean*/ + PresSubClient *c = (PresSubClient *) entry->user_data; + DEBUG("timout for %s",c->getURI().c_str()); + //c->reportPresence(); } /* Callback called when *client* subscription state has changed. */ void pres_client_evsub_on_state(pjsip_evsub *sub, pjsip_event *event) { - PresSubClient *pres_client; - PJ_UNUSED_ARG(event); /* Note: #937: no need to acuire PJSUA_LOCK here. Since the pres_client has @@ -74,36 +73,26 @@ void pres_client_evsub_on_state(pjsip_evsub *sub, pjsip_event *event) { * lock, which we are currently holding! */ - pres_client = (PresSubClient *) pjsip_evsub_get_mod_data(sub, modId); + PresSubClient *pres_client = (PresSubClient *) pjsip_evsub_get_mod_data(sub, modId); if (pres_client) { pres_client->incLock(); - DEBUG("Presence subscription to '%s' is '%s'", pres_client->getURI().c_str(), + DEBUG("pres_client '%s' is '%s'", pres_client->getURI().c_str(), pjsip_evsub_get_state_name(sub) ? pjsip_evsub_get_state_name(sub) : "null"); pjsip_evsub_state state = pjsip_evsub_get_state(sub); if(state == PJSIP_EVSUB_STATE_ACCEPTED){ - DEBUG("PresSubClient accepted."); + DEBUG("pres_client accepted."); pres_client->accept(); } else if (state == PJSIP_EVSUB_STATE_TERMINATED) { int resub_delay = -1; - -// const pj_str_t *pjTermReason = pjsip_evsub_get_termination_reason(sub); -// std::string termReason(pjTermReason->ptr, -// pjTermReason->slen > BUDDY_SUB_TERM_REASON_LEN? -// BUDDY_SUB_TERM_REASON_LEN: -// pjTermReason->slen -// ); pj_strdup_with_null(pres_client->pool, &pres_client->term_reason, pjsip_evsub_get_termination_reason(sub)); -// buddy->setTermReason(termReason); -// pres_client->setTermCode(200); pres_client->term_code = 200; /* Determine whether to resubscribe automatically */ if (event && event->type == PJSIP_EVENT_TSX_STATE) { const pjsip_transaction *tsx = event->body.tsx_state.tsx; if (pjsip_method_cmp(&tsx->method, &pjsip_subscribe_method) == 0) { -// pres_client->setTermCode(tsx->status_code); pres_client->term_code = tsx->status_code; switch (tsx->status_code) { case PJSIP_SC_CALL_TSX_DOES_NOT_EXIST: @@ -168,27 +157,24 @@ void pres_client_evsub_on_state(pjsip_evsub *sub, pjsip_event *event) { * some random value, to avoid sending SUBSCRIBEs all at once) */ if (resub_delay == -1) { -// pj_assert(PRES_TIMER >= 3); - resub_delay = PRES_TIMER * 1000;// - 2500 + (pj_rand() % 5000); - } + resub_delay = PRES_TIMER * 1000; } pres_client->sub = sub; pres_client->rescheduleTimer(PJ_TRUE, resub_delay); - }/* else { - This will clear the last termination code/reason + } else { //state==ACTIVE ...... + //This will clear the last termination code/reason pres_client->term_code = 0; pres_client->term_reason.ptr = NULL; - }*/ + } /* Clear subscription */ - /* if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) { + if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) { pjsip_evsub_terminate(pres_client->sub, PJ_FALSE); // = NULL; pres_client->status.info_cnt = 0; pres_client->dlg = NULL; pres_client->rescheduleTimer(PJ_FALSE, 0); -// pjsip_evsub_set_mod_data(sub, modId, NULL); - }*/ + pjsip_evsub_set_mod_data(sub, modId, NULL); + } -// pj_log_pop_indent(); pres_client->decLock(); } } @@ -243,17 +229,15 @@ void pres_client_evsub_on_tsx_state(pjsip_evsub *sub, pjsip_transaction *tsx, pj } /* Callback called when we receive NOTIFY */ -static void pres_client_evsub_on_rx_notify(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, - pjsip_hdr *res_hdr, pjsip_msg_body **p_body) { - PresSubClient *pres_client; +static void pres_client_evsub_on_rx_notify(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text,pjsip_hdr *res_hdr, pjsip_msg_body **p_body) { /* Note: #937: no need to acuire PJSUA_LOCK here. Since the pres_client has * a dialog attached to it, lock_pres_client() will use the dialog * lock, which we are currently holding! */ - pres_client = (PresSubClient *) pjsip_evsub_get_mod_data(sub, modId); + PresSubClient *pres_client = (PresSubClient *) pjsip_evsub_get_mod_data(sub, modId); if (!pres_client){ - ERROR("Couldn't create new pres_client"); + WARN ("Couldn't extract pres_client from ev_sub."); return; } @@ -272,10 +256,10 @@ static void pres_client_evsub_on_rx_notify(pjsip_evsub *sub, pjsip_rx_data *rdat pres_client->decLock(); } -PresSubClient::PresSubClient(const std::string& uri_, SIPAccount *acc_) : - acc(acc_), +PresSubClient::PresSubClient(const std::string& uri_, SIPPresence *pres_) : + pres(pres_), uri(pj_str(strdup(uri_.c_str()))), - contact(pj_str(strdup(acc->getFromUri().c_str()))), + contact(pj_str(strdup(pres->getAccount()->getFromUri().c_str()))), display(), dlg(NULL), monitor(false), @@ -298,7 +282,7 @@ PresSubClient::~PresSubClient() { while(lock_count >0) { usleep(200); } - DEBUG("Destroying PresSubClient object with uri %s", uri.ptr); + DEBUG("Destroying pres_client object with uri %s", uri.ptr); rescheduleTimer(PJ_FALSE, 0); unsubscribe(); @@ -320,8 +304,8 @@ bool PresSubClient::isTermReason(std::string reason) { } void PresSubClient::rescheduleTimer(bool reschedule, unsigned msec) { + SIPAccount * acc = pres->getAccount(); if (timer.id) { - // pjsua_cancel_timer(&timer); pjsip_endpt_cancel_timer(((SIPVoIPLink*) acc->getVoIPLink())->getEndpoint(), &timer); timer.id = PJ_FALSE; } @@ -329,7 +313,7 @@ void PresSubClient::rescheduleTimer(bool reschedule, unsigned msec) { if (reschedule) { pj_time_val delay; - WARN("Resubscribing pres_client %.*s in %u ms (reason: %.*s)", + WARN("pres_client %.*s will resubscribe in %u ms (reason: %.*s)", uri.slen, uri.ptr, msec, (int) term_reason.slen, term_reason.ptr); monitor = PJ_TRUE; pj_timer_entry_init(&timer, 0, this, &pres_client_timer_cb); @@ -337,24 +321,19 @@ void PresSubClient::rescheduleTimer(bool reschedule, unsigned msec) { delay.msec = msec; pj_time_val_normalize(&delay); - // if (pjsua_schedule_timer(&timer, &delay)==PJ_SUCCESS) - if (pjsip_endpt_schedule_timer(((SIPVoIPLink*) acc->getVoIPLink())->getEndpoint(), &timer, &delay) == PJ_SUCCESS) { -// timer.id = PJ_TRUE; + timer.id = PJ_TRUE; } } } void PresSubClient::accept() { - acc->getPresence()->addPresSubClient(this); + pres->addPresSubClient(this); } void PresSubClient::reportPresence() { - - //incLock(); /* callback*/ - acc->getPresence()->reportPresSubClientNotification(getURI(),&status); - //decLock(); + pres->reportPresSubClientNotification(getURI(),&status); } @@ -371,23 +350,16 @@ pj_status_t PresSubClient::updateSubscription() { } if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) { - WARN("PresSubClient already unsubscribed sub=TERMINATED."); + WARN("pres_client already unsubscribed sub=TERMINATED."); //pjsip_evsub_terminate(sub, PJ_FALSE); // sub = NULL; return PJ_SUCCESS; } - WARN("PresSubClient %s: unsubscribing..", uri.ptr); + WARN("pres_client %s: unsubscribing..", uri.ptr); retStatus = pjsip_pres_initiate(sub, 0, &tdata); if (retStatus == PJ_SUCCESS) { - acc->getPresence()->fillDoc(tdata, NULL); - /*if (tdata->msg->type == PJSIP_REQUEST_MSG) { - const pj_str_t STR_USER_AGENT = {"User-Agent", 10}; - pj_str_t ua = pj_str(strdup(acc->getUserAgentName().c_str())); - pjsip_hdr *h; - h = (pjsip_hdr*) pjsip_generic_string_hdr_create(tdata->pool, &STR_USER_AGENT, &ua); - pjsip_msg_add_hdr(tdata->msg, h); - }*/ + pres->fillDoc(tdata, NULL); retStatus = pjsip_pres_send_request(sub, tdata); } @@ -402,18 +374,13 @@ pj_status_t PresSubClient::updateSubscription() { return PJ_SUCCESS; } -//#if 0 if (sub && dlg) { //do not bother if already subscribed -// return PJ_SUCCESS; pjsip_evsub_terminate(sub, PJ_FALSE); DEBUG("Terminate existing sub."); } -//#endif //subscribe pjsip_evsub_user pres_callback; -// pj_pool_t *tmp_pool = NULL; // related to "contact field. TODO: check if this is necessary" - pjsip_tx_data *tdata; pj_status_t status; @@ -423,29 +390,11 @@ pj_status_t PresSubClient::updateSubscription() { pres_callback.on_tsx_state = &pres_client_evsub_on_tsx_state; pres_callback.on_rx_notify = &pres_client_evsub_on_rx_notify; - DEBUG("PresSubClient %s: subscribing presence,using account %s..", + SIPAccount * acc = pres->getAccount(); + DEBUG("PresSubClient %s: subscribing presence,using %s..", uri.ptr, acc->getAccountID().c_str()); - /* Generate suitable Contact header unless one is already set in - * the account - */ -#if 0 - if (acc->contact.slen) { - contact = acc->contact; - } else { - tmp_pool = pjsua_pool_create("tmppres_client", 512, 256); - - status = pjsua_acc_create_uac_contact(tmp_pool, &contact, - acc_id, &pres_client->uri); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to generate Contact header", - status); - pj_pool_release(tmp_pool); - pj_log_pop_indent(); - return; - } - } -#endif + /* Create UAC dialog */ pj_str_t from = pj_str(strdup(acc->getFromUri().c_str())); status = pjsip_dlg_create_uac(pjsip_ua_instance(), &from, &contact, &uri, NULL, &dlg); @@ -453,7 +402,7 @@ pj_status_t PresSubClient::updateSubscription() { ERROR("Unable to create dialog \n"); return PJ_FALSE; } - // Add credential for auth. + /* Add credential for auth. */ if (acc->hasCredentials() and pjsip_auth_clt_set_credentials(&dlg->auth_sess, acc->getCredentialCount(), acc->getCredInfo()) != PJ_SUCCESS) { ERROR("Could not initialize credentials for subscribe session authentication"); } @@ -473,35 +422,25 @@ pj_status_t PresSubClient::updateSubscription() { if (dlg) { pjsip_dlg_dec_lock(dlg); } -// if (tmp_pool) pj_pool_release(tmp_pool); -// pj_log_pop_indent(); return PJ_SUCCESS; } -#if 0 - /* If account is locked to specific transport, then lock dialog - * to this transport too. - */ - if (acc->cfg.transport_id != PJSUA_INVALID_ID) { - pjsip_tpselector tp_sel; - pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel); - pjsip_dlg_set_transport(pres_client->dlg, &tp_sel); + + /* Add credential for authentication */ + if (acc->hasCredentials() and pjsip_auth_clt_set_credentials(&dlg->auth_sess, acc->getCredentialCount(), acc->getCredInfo()) != PJ_SUCCESS) { + ERROR("Could not initialize credentials for invite session authentication"); + return status; } /* Set route-set */ - if (!pj_list_empty(&acc->route_set)) { - pjsip_dlg_set_route_set(pres_client->dlg, &acc->route_set); - } + if (acc->hasServiceRoute()) + pjsip_regc_set_route_set( + acc->getRegistrationInfo(), + sip_utils::createRouteSet(acc->getServiceRoute(), + pres->getPool())); - /* Set credentials */ - if (acc->cred_cnt) { - pjsip_auth_clt_set_credentials(&pres_client->dlg->auth_sess - acc->cred_cnt, acc->cred); - } - /* Set authentication preference */ - pjsip_auth_clt_set_prefs(&pres_client->dlg->auth_sess, &acc->cfg.auth_pref); -#endif - modId = ((SIPVoIPLink*) acc->getVoIPLink())->getModId(); + /* FIXME : not sure this is acceptable */ + modId = pres->getModId(); pjsip_evsub_set_mod_data(sub, modId, this); status = pjsip_pres_initiate(sub, -1, &tdata); @@ -513,7 +452,6 @@ pj_status_t PresSubClient::updateSubscription() { } pjsip_evsub_terminate(sub, PJ_FALSE); // = NULL; WARN("Unable to create initial SUBSCRIBE", status); -// if (tmp_pool) pj_pool_release(tmp_pool); return PJ_SUCCESS; } @@ -529,12 +467,10 @@ pj_status_t PresSubClient::updateSubscription() { } WARN("Unable to send initial SUBSCRIBE", status); -// if (tmp_pool) pj_pool_release(tmp_pool); return PJ_SUCCESS; } pjsip_dlg_dec_lock(dlg); -// if (tmp_pool) pj_pool_release(tmp_pool); return PJ_SUCCESS; } diff --git a/daemon/src/sip/pres_sub_client.h b/daemon/src/sip/pres_sub_client.h index c0754eacee48d8348100d36bafde70a88ac3b282..7fa31f1da5626edfc39fe5de7ee6afa8ef765a63 100644 --- a/daemon/src/sip/pres_sub_client.h +++ b/daemon/src/sip/pres_sub_client.h @@ -42,7 +42,7 @@ #include <pjsip/sip_transport.h> #include "noncopyable.h" -class SIPAccount; +class SIPPresence; /** * Transaction functions of event subscription client side. @@ -57,7 +57,8 @@ static void pres_client_evsub_on_rx_notify(pjsip_evsub *sub, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body); -static void pres_client_timer_cb(pj_timer_heap_t *th, pj_timer_entry *entry); +static void pres_client_timer_cb(pj_timer_heap_t *th, + pj_timer_entry *entry); class PresSubClient { @@ -67,7 +68,7 @@ class PresSubClient { * Constructor * @param uri SIP uri of remote user that we want to subscribe, */ - PresSubClient(const std::string &uri, SIPAccount *acc); + PresSubClient(const std::string &uri, SIPPresence *pres_); /** * Destructor. * Process the the unsubscription before the destruction. @@ -150,7 +151,7 @@ class PresSubClient { */ unsigned getTermCode(); - SIPAccount *acc; /**< Associated SIP account pointer */ + SIPPresence *pres; /**< Associated SIPPresence pointer */ pj_str_t uri; /**< pres_client URI. */ pj_str_t contact; /**< Contact learned from subscrp. */ pj_str_t display; /**< pres_client display name. */ diff --git a/daemon/src/sip/sippresence.cpp b/daemon/src/sip/sippresence.cpp index 4d8b09e0052a780b40790d7f65cbcba1c2f46494..6472f9bea5406da98dc189bd6fe4b78b30255787 100644 --- a/daemon/src/sip/sippresence.cpp +++ b/daemon/src/sip/sippresence.cpp @@ -46,9 +46,7 @@ SIPPresence::SIPPresence(SIPAccount *acc) : pres_status_data() - , online_status() , publish_sess() - , publish_state() , enabled(true) , acc_(acc) , pres_sub_server_list_ () //IP2IP context @@ -56,11 +54,11 @@ SIPPresence::SIPPresence(SIPAccount *acc) , mutex_() , mutex_nesting_level_() , mutex_owner_() - , pool_() , cp_() + , pool_() { /* init default status */ - updateStatus("open","Available"); + updateStatus(true,"Available"); /* init pool */ pj_caching_pool_init(&cp_, &pj_pool_factory_default_policy, 0); @@ -100,10 +98,11 @@ void SIPPresence::enable(const bool& flag){ enabled = flag; } -void SIPPresence::updateStatus(const std::string &status, const std::string ¬e){ +void SIPPresence::updateStatus(const bool& status, const std::string ¬e){ //char* pj_note = (char*) pj_pool_alloc(pool_, "50"); - pjrpid_element rpid = {PJRPID_ELEMENT_TYPE_PERSON, + pjrpid_element rpid = { + PJRPID_ELEMENT_TYPE_PERSON, pj_str("20"), PJRPID_ACTIVITY_UNKNOWN, pj_str((char *) note.c_str())}; @@ -118,13 +117,13 @@ void SIPPresence::updateStatus(const std::string &status, const std::string ¬ pj_bzero(&pres_status_data, sizeof(pres_status_data)); pres_status_data.info_cnt = 1; - pres_status_data.info[0].basic_open = (status == "open")? true: false; + pres_status_data.info[0].basic_open = status; pres_status_data.info[0].id = pj_str("0"); /* todo: tuplie_id*/ pj_memcpy(&pres_status_data.info[0].rpid, &rpid,sizeof(pjrpid_element)); /* "contact" field is optionnal */ } -void SIPPresence::sendPresence(const std::string &status, const std::string ¬e){ +void SIPPresence::sendPresence(const bool& status, const std::string ¬e){ updateStatus(status,note); if(enabled){ if (acc_->isIP2IP()) @@ -139,7 +138,7 @@ void SIPPresence::reportPresSubClientNotification(const std::string& uri, pjsip_ /* Update our info. See pjsua_buddy_get_info() for additionnal ideas*/ const std::string basic(status->info[0].basic_open ? "open" : "closed"); const std::string note(status->info[0].rpid.note.ptr,status->info[0].rpid.note.slen); - DEBUG(" Received presenceStateChange for %s status=%s note=%s",uri.c_str(),basic.c_str(),note.c_str()); + DEBUG(" Received status of PresSubClient %s: status=%s note=%s",uri.c_str(),basic.c_str(),note.c_str()); /* report status to client signal */ Manager::instance().getClient()->getCallManager()->newPresSubClientNotification(uri, basic, note); } @@ -160,7 +159,7 @@ void SIPPresence::subscribePresSubClient(const std::string& uri, const bool& fla } if(flag){ - PresSubClient *c = new PresSubClient(uri, acc_); + PresSubClient *c = new PresSubClient(uri,this); if(!(c->subscribe())){ WARN("Failed send subscribe."); delete c; @@ -172,16 +171,16 @@ void SIPPresence::subscribePresSubClient(const std::string& uri, const bool& fla void SIPPresence::addPresSubClient(PresSubClient *c){ if(pres_sub_client_list_.size() < MAX_N_PRES_SUB_CLIENT){ pres_sub_client_list_.push_back(c); - DEBUG("-New Presence_subscription_client client added in the list[l=%i].",pres_sub_client_list_.size()); + DEBUG("New Presence_subscription_client client added in the list[l=%i].",pres_sub_client_list_.size()); } else{ - WARN("-Max Presence_subscription_client is reach."); + WARN("Max Presence_subscription_client is reach."); // let the client alive //delete c; } } void SIPPresence::removePresSubClient(PresSubClient *c){ - DEBUG("-Presence_subscription_client removed from the buddy list."); + DEBUG("Presence_subscription_client removed from the buddy list."); pres_sub_client_list_.remove(c); } @@ -190,10 +189,10 @@ void SIPPresence::reportNewPresSubServerRequest(PresSubServer *s){ Manager::instance().getClient()->getCallManager()->newPresSubServerRequest(s->remote); } -void SIPPresence::approvePresSubServer(const bool& flag, const std::string& uri){ +void SIPPresence::approvePresSubServer(const std::string& uri, const bool& flag){ for (auto s : pres_sub_server_list_) if(s->matches((char *) uri.c_str())){ - DEBUG("-Approve Presence_subscription_server for %s.",s->remote); + DEBUG("Approve Presence_subscription_server for %s: %s.",s->remote,flag? "true":"false"); s->approve(flag); // return; // 'return' would prevent multiple-time subscribers from spam } @@ -202,22 +201,22 @@ void SIPPresence::approvePresSubServer(const bool& flag, const std::string& uri) void SIPPresence::addPresSubServer(PresSubServer *s) { if(pres_sub_server_list_.size() < MAX_N_PRES_SUB_SERVER){ - DEBUG("-Presence_subscription_server added: %s.",s->remote); + DEBUG("Presence_subscription_server added: %s.",s->remote); pres_sub_server_list_.push_back(s); } else{ - WARN("-Max Presence_subscription_server is reach."); + WARN("Max Presence_subscription_server is reach."); // let de server alive // delete s; } } void SIPPresence::removePresSubServer(PresSubServer *s) { pres_sub_server_list_.remove(s); - DEBUG("-Presence_subscription_server removed"); + DEBUG("Presence_subscription_server removed"); } void SIPPresence::notifyPresSubServer() { - DEBUG("-Iterating through Presence_subscription_server:"); + DEBUG("Iterating through Presence_subscription_server:"); for (auto s : pres_sub_server_list_) s->notify(); } diff --git a/daemon/src/sip/sippresence.h b/daemon/src/sip/sippresence.h index 1dae8372fb73ccd0519e7301f1daa41179d87796..9e7a7b0ae11d1703663fd00bde70c47f59e054af 100644 --- a/daemon/src/sip/sippresence.h +++ b/daemon/src/sip/sippresence.h @@ -143,12 +143,12 @@ public: * Modify the presence data * @param status is basically "open" or "close" */ - void updateStatus(const std::string &status, const std::string ¬e); + void updateStatus(const bool& status, const std::string ¬e); /** * Send the presence data in a PUBLISH to the PBX or in a NOTIFY * to a remote subscriber (IP2IP) */ - void sendPresence(const std::string &status, const std::string ¬e); + void sendPresence(const bool& status, const std::string ¬e); /** * Send a signal to the client on DBus. The signal contain the status * of a remote user. @@ -182,7 +182,7 @@ public: * @param flag client decision. * @param uri uri of the remote subscriber */ - void approvePresSubServer(const bool& flag, const std::string& uri); + void approvePresSubServer(const std::string& uri, const bool& flag); /** * IP2IP context. * Add a server associated to a subscriber in the list. @@ -210,9 +210,7 @@ public: bool isLocked(); pjsip_pres_status pres_status_data; /**< Presence Data.*/ - pj_bool_t online_status; /**< Our online status. */ pjsip_publishc *publish_sess; /**< Client publication session.*/ - pj_bool_t publish_state; /**< Last published online status.*/ pj_bool_t enabled; /**< Allow for status publish,*/ private: diff --git a/daemon/src/sip/sippublish.cpp b/daemon/src/sip/sippublish.cpp index 7ba211575972fac3abc48acd0b3a371154c62658..e1da4475f3046a296c8f156f620df50e12f517f7 100644 --- a/daemon/src/sip/sippublish.cpp +++ b/daemon/src/sip/sippublish.cpp @@ -125,7 +125,7 @@ pj_status_t pres_send_publish(SIPPresence * pres, pj_bool_t active) } /* Create and add PIDF message body */ - status = pjsip_pres_create_pidf(tdata->pool, acc->getPresence()->getStatus(), + status = pjsip_pres_create_pidf(tdata->pool, pres->getStatus(), &entity, &tdata->msg->body); if (status != PJ_SUCCESS) { ERROR("Error creating PIDF for PUBLISH request"); @@ -134,12 +134,7 @@ pj_status_t pres_send_publish(SIPPresence * pres, pj_bool_t active) } } else { - //status = pjsip_publishc_unpublish(pres->publish_sess, &tdata); - //if (status != PJ_SUCCESS) { - // pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status); - // goto on_error; - //} - WARN("SHOULD UNPUBLISH"); + WARN("Unpublish is not implemented."); } @@ -151,25 +146,6 @@ pj_status_t pres_send_publish(SIPPresence * pres, pj_bool_t active) pres->fillDoc(tdata, &msg_data); - - /* Set Via sent-by */ - /*if (acc->cfg.allow_via_rewrite && acc->via_addr.host.slen > 0) { - pjsip_publishc_set_via_sent_by(acc->publish_sess, &acc->via_addr, - acc->via_tp); - } else if (!pjsua_sip_acc_is_using_stun(acc_id)) { - // Choose local interface to use in Via if acc is not using STUN. See https://trac.pjsip.org/repos/ticket/1412 - pjsip_host_port via_addr; - const void *via_tp; - - if (pjsua_acc_get_uac_addr(acc_id, acc->pool, &acc_cfg->id, - &via_addr, NULL, NULL, - &via_tp) == PJ_SUCCESS) - { - pjsip_publishc_set_via_sent_by(acc->publish_sess, &via_addr, - (pjsip_transport*)via_tp); - } - }*/ - /* Send the PUBLISH request */ status = pjsip_publishc_send(pres->publish_sess, tdata); if (status == PJ_EPENDING) { @@ -179,7 +155,6 @@ pj_status_t pres_send_publish(SIPPresence * pres, pj_bool_t active) goto on_error; } - pres->publish_state = pres->online_status; return PJ_SUCCESS; on_error: diff --git a/daemon/test/scripts/presence_test.py b/daemon/test/scripts/presence_test.py new file mode 100644 index 0000000000000000000000000000000000000000..7565a2ec38724c95fce551fb618653fc9c6fc0d1 --- /dev/null +++ b/daemon/test/scripts/presence_test.py @@ -0,0 +1,175 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +print "\ +# --SFLPhone-- #\n\ +# #\n\ +# copyright: Savoir-Faire Linux (2013) #\n\ +# author: Patrick keroulas <patrick.keroulas@savoirfairelinux.com> #\n\ +# description: This script sends a sequence of methods to the daemon #\n\ +# through the dbus to test the presence feature of SFLPhone. #\n\ +# SET THE PARAMS IS THE 'data' SECTION OF THIS SCRIPT BEFORE #\n\ +# YOU EXECUTE IT. 'The normal mode process the tasks #\n\ +# in order while the random mode generates a random sequence #\n\ +# A Freeswitch server must be setup since it supports PUBLISH #\n\ +# requests and Asterisk doesn't. #\n\ +# The user must have a valid account. This script will #\n\ +# use the first account in the list and the IP2IP account. #\n\ +# This is a self subscribe test, set another buddy IP if needed#\n\ +#\n" + + + +import time, sys, gobject +from random import randint +import dbus, dbus.mainloop.glib +import logging, commands + + +#----------------- logger to file and stdout ------------------------------ +f = '%(asctime)s - %(name)s - %(levelname)s - %(message)s' +logfile = 'sflphone_doombot.log' +logging.basicConfig(filename=logfile,level=logging.INFO,format=f) # log to file +logger = logging.getLogger() +ch = logging.StreamHandler(sys.stdout) #log to console +ch.setLevel(logging.INFO) +formatter = logging.Formatter(f) +ch.setFormatter(formatter) +logger.addHandler(ch) + +#------------------ Initialise DBUS ------------------------------ +dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) +bus = dbus.SessionBus() +callManagerBus = bus.get_object('org.sflphone.SFLphone', '/org/sflphone/SFLphone/CallManager') +callManager = dbus.Interface(callManagerBus, dbus_interface='org.sflphone.SFLphone.CallManager') +configurationManagerBus = bus.get_object('org.sflphone.SFLphone', '/org/sflphone/SFLphone/ConfigurationManager') +configurationManager = dbus.Interface(configurationManagerBus, dbus_interface='org.sflphone.SFLphone.ConfigurationManager') + + +#------------------------- General purpose functions ---------------------------- + +#Get the first non-IP2IP account +def get_first_account(): + accounts = configurationManager.getAccountList() + for i, v in enumerate(accounts): + if v != "IP2IP": + details = configurationManager.getAccountDetails(v) + if details["Account.type"] == True or details["Account.type"] == "SIP": + return v + return "IP2IP" + +def registerSend(arg): + configurationManager.sendRegister(arg['acc'],arg['enable']) + logging.info('Send register : '+ str(arg)) + +#------------------------- Presence functions ---------------------------- + + + +def presSubscribe(arg): + callManager.subscribePresSubClient(arg['acc'],arg['buddy'],arg['flag']) + logging.info('Subscribe to ' + str(arg)) + +def presSend(arg): + callManager.sendPresence(arg['acc'],arg['status'],arg['note']) + logging.info('Send to ' + str(arg)) + +def presSubApprove(arg): + callManager.approvePresSubServer(arg['uri'],arg['flag']) + logging.info('Approve subscription from' + str(arg)) + +def newPresSubCientNotificationHandler(uri, status, activity): + global subscribe_flag + if subscribe_flag: + logging.info("Received DBus signal : < from:"+str(uri)+" (status:" + str(status)+ ", "+ str(activity)+ ").") + else: + logging.error("Not supposed to receive DBus singal when unsubscribe.") + +def newPresSubServerRequestHandler(uri): + logging.info("Received a PresenceSubscription request from " +str(uri)) + subscriber_uri = uri + +def randbool(): + return bool(randint(0,1)) + +#--------------------------- Data ------------------------------- + +TEST_DURATION = 10 # in sec +first_account = get_first_account() +IP2IP = 'IP2IP' +server_ip = '192.168.50.124' + +host_user = '1001' +host_ip = '192.168.50.196' +host_uri = '<sip:'+host_user+'@'+server_ip+'>' + +# self subscribing +buddy_user = host_user +buddy_ip = host_ip +buddy_uri = '<sip:'+buddy_user+'@'+server_ip+'>' +buddy_ip_uri = '<sip:'+buddy_ip+'>' # IP2IP +subscriber_uri = '' + +#---------------------------- Sequence ---------------------------- + +start_time = 0 +task_count = 0 +task_N = 0 + +SEQ_MODE_NORMAL = 0 +SEQ_MODE_RANDOM = 1 +sequence_mode = SEQ_MODE_NORMAL + +task_list = [ + + # regular account + (presSubscribe, {'acc':first_account,'buddy':buddy_uri,'flag':True}), + (presSend, {'acc':first_account,'status':randbool,'note':'Oh yeah!'}), + (presSubscribe, {'acc':first_account,'buddy':buddy_uri,'flag':False}), + (presSend, {'acc':first_account,'status':randbool,'note':'This notify should not be recieved'}) + + # IP2IP + (presSubscribe, {'acc': IP2IP,'buddy':buddy_ip_uri,'flag':True}), + (presSend, {'acc': IP2IP,'status':randbool(),'note':'This notify should not be recieved'}) + (presSubApprove, {'uri':subscriber_uri,'flag':randbool}), + (presSend, {'acc': IP2IP,'status':randbool(),'note':'Oh yeah!'}), + (presSubscribe, {'acc': IP2IP,'buddy':buddy_ip_uri,'flag':False}), +] + + +def run(): + + if sequence_mode == SEQ_MODE_NORMAL: + global task_count + task_index = task_count%task_N + task_count += 1 + elif sequence_mode == SEQ_MODE_RANDOM: + task_index = randint(0,task_N-1) + + task_list[task_index][0](task_list[task_index][1]) + + if(int(time.time()-start_time) < TEST_DURATION): + gobject.timeout_add(randint(50,2000), run) # random time step in ms + else: + logging.info("Test sequence finished") + # TODO clear dbus sessio. Unfortunately stackoverflow.com is down today + + +if __name__ == '__main__': + + try: + # dbus signal monitor + callManagerBus.connect_to_signal("newPresSubCientNotification", newPresSubCientNotificationHandler, dbus_interface='org.sflphone.SFLphone.CallManager') + callManagerBus.connect_to_signal("newPresSubServerRequest", newPresSubServerRequestHandler, dbus_interface='org.sflphone.SFLphone.CallManager') + + registerSend({'acc':first_account, 'enable':True}) + start_time = time.time() + task_N = len(task_list) + #sequence_mode = SEQ_MODE_RANDOM + + run() + + except Exception as e: + print e + + loop = gobject.MainLoop() + loop.run() diff --git a/daemon/test/scripts/stress_test.py b/daemon/test/scripts/stress_test.py new file mode 100644 index 0000000000000000000000000000000000000000..ab90345b95197876de674d69a84b52848cc6cae7 --- /dev/null +++ b/daemon/test/scripts/stress_test.py @@ -0,0 +1,329 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +print "\ +# --SFLPhone-- #\n\ +# /¯¯¯¯\ /¯¯¯¯\ /¯¯¯¯\ /¯¯¯\_/¯¯¯\ /¯¯¯¯¯\ /¯¯¯¯\ |¯¯¯¯¯¯¯| #\n\ +# / /¯\ | / /\ \ /\ \| /¯\ /¯\ | | |¯| | | /\ | ¯¯| |¯¯ #\n\ +# / / / |/ | | | | | || | | | | | | ¯ < | | | | | | #\n\ +# / /__/ / | ¯ | ¯ || | | | | | | |¯| | | |_| | | | #\n\ +# |______/ \_____/ \____/ |_| |_| |_| \_____/ \____/ |_| #\n\ +# #\n\ +# copyright: Savoir-Faire Linux (2012) #\n\ +# author: Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> #\n\ +# description: This script perform stress tests to trigger rare race #\n\ +# conditions or ASSERT caused by excessive load. This script #\n\ +# should, in theory, never crash or end the sflphone daemon #\n" + +import dbus +import time +import sys +from random import randint + +#Initialise DBUS +bus = dbus.SessionBus() +callManagerBus = bus.get_object('org.sflphone.SFLphone', '/org/sflphone/SFLphone/CallManager') +callManager = dbus.Interface(callManagerBus, dbus_interface='org.sflphone.SFLphone.CallManager') +configurationManagerBus = bus.get_object('org.sflphone.SFLphone', '/org/sflphone/SFLphone/ConfigurationManager') +configurationManager = dbus.Interface(configurationManagerBus, dbus_interface='org.sflphone.SFLphone.ConfigurationManager') + +#---------------------------------------------------------------------# +# # +# Tools # +# # +#---------------------------------------------------------------------# + +#Get the first non-IP2IP account +def get_first_account(): + accounts = configurationManager->getAccountList() + for i, v in enumerate(accounts): + if v != "IP2IP": + details = configurationManager->getAccountDetails(v) + if details["Account.type"] == True or details["Account.type"] == "SIP": + return v + return "IP2IP" + +#Get the first IAX account +def get_first_iax_account(): + accounts = configurationManager->getAccountList() + for i, v in enumerate(accounts): + if v != "IP2IP": + details = configurationManager->getAccountDetails(v) + if details["Account.type"] != True and details["Account.type"] != "SIP": + return v + return "IP2IP" + +def get_account_number(account): + details = configurationManager->getAccountDetails(account) + return details["Account.username"] + +def answer_all_calls(): + calls = callManager.getCallList() + for i, v in enumerate(calls): + details = callManager.getCallDetails(v) + if details["CALL_STATE"] == "INCOMING": + callManager.accept(v) + +#Return true is the account is registered +def check_account_state(account): + details = configurationManager->getAccountDetails(account) + #details = {'test':1,'test2':2,'registrationStatus':3} + return details['Account.registrationStatus'] == "REGISTERED" + +#Meta test, common for all tests +def meta_test(test_func): + try: + for y in range(0,15): + for x in range(0,10): + ret = test_func() + if ret['code'] > 0: + sys.stdout.write(' \033[91m(Failure)\033[0m\n') + print " \033[0;33m"+ret['error']+"\033[0m" + return 1 + sys.stdout.write('#') + sys.stdout.flush() + sys.stdout.write(' \033[92m(Success)\033[0m\n') + except dbus.exceptions.DBusException: + sys.stdout.write(' \033[91m(Failure)\033[0m\n') + print " \033[0;33mUnit test \"stress_answer_hangup_server\" failed: Timeout, the daemon is unreachable, it may be a crash, a lock or an assert\033[0m" + return 1 + #except Exception: + #sys.stdout.write(' \033[91m(Failure)\033[0m\n') + #print " \033[0;33mUnit test \"stress_answer_hangup_server\" failed: Unknown error, disable 'except Exception' for details\033[0m" + #return 1 + return 0 + +#Add a new test +suits = {} +def add_to_suit(test_suite_name,test_name,test_func): + if not test_suite_name in suits: + suits[test_suite_name] = [] + suits[test_suite_name].append({'test_name':test_name,'test_func':test_func}) + +# Run tests +def run(): + counter = 1 + results = {} + for k in suits.keys(): + print "\n\033[1mExecuting \""+str(k)+"\" tests suit:\033[0m ("+str(counter)+"/"+str(len(suits))+")" + for i, v in enumerate(suits[k]): + sys.stdout.write(" ["+str(i+1)+"/"+str(len(suits[k]))+"] Testing \""+v['test_name']+"\": ") + sys.stdout.flush() + retval = meta_test(v['test_func']) + if not k in results: + results[k] = 0 + if retval > 0: + results[k]= results[k] + 1 + counter = counter + 1 + + print "\n\n\033[1mSummary:\033[0m" + totaltests = 0 + totalsuccess = 0 + for k in suits.keys(): + print " Suit \""+k+"\": "+str(len(suits[k])-results[k])+"/"+str(len(suits[k])) + totaltests = totaltests + len(suits[k]) + totalsuccess = totalsuccess + len(suits[k])-results[k] + + print "\nTotal: "+str(totalsuccess)+"/"+str(totaltests)+", "+str(totaltests-totalsuccess)+" failures" + + +#---------------------------------------------------------------------# +# # +# Variables # +# # +#---------------------------------------------------------------------# +first_account = get_first_account() +first_iax_account = get_first_iax_account() +first_account_number = get_account_number(first_account) + + +#---------------------------------------------------------------------# +# # +# Unit Tests # +# # +#---------------------------------------------------------------------# + +# This unit case test the basic senario of calling asterisk/freeswitch and then hanging up +# It call itself to make the test simpler, this also test answering up as a side bonus +def stress_answer_hangup_server(): + callManager.placeCall(first_account,str(randint(100000000,100000000000)),first_account_number) + time.sleep(0.05) + calls = callManager.getCallList() + + # Check if the call worked + if len(calls) < 2: + if not check_account_state(first_account): + #TODO Try to register again instead of failing + return {'code':2,'error':"Unit test \"stress_answer_hangup_server\" failed: Account went unregistered"} + else: + return {'code':1,'error':"Unit test \"stress_answer_hangup_server\" failed: Error while placing call, there is "+str(len(calls))+" calls"} + else: + + #Accept the calls + for i, v in enumerate(calls): + time.sleep(0.05) + #callManager.accept(v) + + #Hang up + callManager.hangUp(calls[0]) + return {'code':0,'error':""} +add_to_suit("Place call",'Place, answer and hangup',stress_answer_hangup_server) + + + +# This test is similar to stress_answer_hangup_server, but test using IP2IP calls +def stress_answer_hangup_IP2IP(): + callManager.placeCall(first_account,str(randint(100000000,100000000000)),"sip:127.0.0.1") + time.sleep(0.05) + calls = callManager.getCallList() + + # Check if the call worked + if len(calls) < 2: + if not check_account_state(first_account): + #TODO Try to register again instead of failing + return {'code':2,'error':"\nUnit test \"stress_answer_hangup_server\" failed: Account went unregistered"} + else: + return {'code':1,'error':"\nUnit test \"stress_answer_hangup_server\" failed: Error while placing call, there is "+str(len(calls))+" calls"} + else: + #Accept the calls + for i, v in enumerate(calls): + time.sleep(0.05) + #callManager.accept(v) + #Hang up + callManager.hangUp(calls[0]) + return {'code':0,'error':""} +add_to_suit("Place call",'Place, answer and hangup (IP2IP)',stress_answer_hangup_IP2IP) + +# Test various type of transfers between various type of calls +# Use both localhost and +def stress_transfers(): + for i in range(0,50): #repeat the tests + for j in range(0,3): # alternate between IP2IP, SIP and IAX + for k in range(0,2): #alternate between classic transfer and attended one + acc1 = "" + if j == 0: + acc1 = first_account + elif j == 1: + acc1 = "IP2IP" + else: + acc1 = first_iax_account + acc2 = "" + if i%3 == 0: #Use the first loop to shuffle second account type + acc2 = first_account + elif i%3 == 1: + acc2 = "IP2IP" + else: + acc2 = first_iax_account + print "ACC1"+acc1+" ACC2 "+acc2+ " FIRST IAX "+ first_iax_account +" FISRT "+first_account + destination_number = "" + if acc2 == "IP2IP": + destination_number = "sip:127.0.0.1" + else: + destination_number = configurationManager->getAccountDetails(acc2)["Account.username"] + callManager.placeCall(acc1,str(randint(100000000,100000000000)),destination_number) + second_call = None + if k == 1: + callManager.placeCall(acc1,str(randint(100000000,100000000000)),"188") + answer_all_calls() + + if k == 1: + first_call = None + calls = callManager.getCallList() + for i, v in enumerate(calls): + if first_call == None: + first_call = v + else: + callManager.attendedTransfer(v,first_call) + else: + calls = callManager.getCallList() + for i, v in enumerate(calls): + callManager.transfer(v,destination_number) + + +# This test make as tons or calls, then hangup them all as fast as it can +def stress_concurent_calls(): + for i in range(0,50): #repeat the tests + for j in range(0,3): # alternate between IP2IP, SIP and IAX + for k in range(0,2): #alternate between classic transfer and attended one + acc1 = "" + if j == 0: + acc1 = first_account + elif j == 1: + acc1 = "IP2IP" + else: + acc1 = first_iax_account + acc2 = "" + if i%3 == 0: #Use the first loop to shuffle second account type + acc2 = first_account + elif i%3 == 1: + acc2 = "IP2IP" + else: + acc2 = first_iax_account + print "ACC1"+acc1+" ACC2 "+acc2+ " FIRST IAX "+ first_iax_account +" FISRT "+first_account + destination_number = "" + if acc2 == "IP2IP": + destination_number = "sip:127.0.0.1" + else: + destination_number = configurationManager->getAccountDetails(acc2)["Account.username"] + callManager.placeCall(acc1,str(randint(100000000,100000000000)),destination_number) + calls = callManager.getCallList() + for i, v in enumerate(calls): + callManager.hangUp(v) +add_to_suit("Place call",'Many simultanious calls (IP2IP)',stress_concurent_calls) + +# Test if SFLPhone can handle more than 50 simultanious IP2IP call over localhost +# Using localhost to save bandwidth, this is about concurent calls, not network load +#def stress_concurent_calls(): + + ## Create 50 calls + #for i in range(0,50): + #callManager.placeCall(first_account,str(randint(100000000,100000000000)),"sip:127.0.0.1") + + ##TODO check if the could is right + + ## Accept all calls that worked + #calls = callManager.getCallList() + #for i, v in enumerate(calls): + #callManager.accept(v) + + ## Hang up all calls + #for i, v in enumerate(calls): + #callManager.hangUp(v) + #return {'code':0,'error':""} +#add_to_suit("Place call",'Many simultanious calls (IP2IP)',stress_concurent_calls) + + +# Test if a call can be put and removed from hold multiple time +def stress_hold_unhold_server(): + # Hang up everything left + calls = callManager.getCallList() + for i, v in enumerate(calls): + callManager.hangUp(v) + + #Place a call + callManager.placeCall(first_account,str(randint(100000000,100000000000)),first_account_number) + calls = callManager.getCallList() + if len(calls) < 1: + return {'code':5,'error':"\nUnit test \"stress_hold_unhold\" failed: The call is gone"} + call = calls[0] + + #Hold and unhold it + for i in range(0,10): + callManager.hold(call) + details = callManager.getCallDetails(call) + if not 'CALL_STATE' in details: + return {'code':1,'error':"\nUnit test \"stress_hold_unhold\" failed: The call is gone (hold)"} + if not details['CALL_STATE'] == "HOLD": + return {'code':2,'error':"\nUnit test \"stress_hold_unhold\" failed: The call should be on hold, but is "+details['CALL_STATE']} + callManager.unhold(call) + details = callManager.getCallDetails(call) + if not 'CALL_STATE' in details: + return {'code':3,'error':"\nUnit test \"stress_hold_unhold\" failed: The call is gone (unhold)"} + if not details['CALL_STATE'] == "CURRENT": + return {'code':4,'error':"\nUnit test \"stress_hold_unhold\" failed: The call should be current, but is "+details['CALL_STATE']} + return {'code':0,'error':""} +#add_to_suit("Hold call",'Hold and unhold',stress_hold_unhold_server) + +#Run the tests +#run() +stress_transfers() +#kate: space-indent off; tab-indents on; mixedindent off; indent-width 4;tab-width 4;