Commit d75e0940 authored by Guillaume Roguez's avatar Guillaume Roguez

daemon: move transfer/attendedTransfer API to Call class

Refs #51555

Change-Id: I52ba765b2ff9ec896d3d5a8172495be80947c134
parent 8bad0be4
......@@ -253,6 +253,19 @@ class Call : public Recordable {
*/
virtual void refuse() = 0;
/**
* Transfer a call to specified URI
* @param to The recipient of the call
*/
virtual void transfer(const std::string &to) = 0;
/**
* Attended transfer
* @param The target call id
* @return True on success
*/
virtual bool attendedTransfer(const std::string& to) = 0;
private:
bool validTransition(CallState newState);
......
......@@ -166,3 +166,18 @@ IAXCall::refuse()
link_->removeIaxCall(getCallId());
}
void
IAXCall::transfer(const std::string& to)
{
std::lock_guard<std::mutex> lock(IAXVoIPLink::mutexIAX);
char callto[to.length() + 1];
strcpy(callto, to.c_str());
iax_transfer(session, callto);
}
bool
IAXCall::attendedTransfer(const std::string& /*targetID*/)
{
return false; // TODO
}
......@@ -86,6 +86,10 @@ class IAXCall : public Call {
void refuse();
void transfer(const std::string& to);
bool attendedTransfer(const std::string& to);
private:
NON_COPYABLE(IAXCall);
......
......@@ -371,30 +371,6 @@ IAXVoIPLink::offhold(const std::string& id)
Manager::instance().startAudioDriverStream();
}
void
IAXVoIPLink::transfer(const std::string& id, const std::string& to)
{
char callto[to.length() + 1];
strcpy(callto, to.c_str());
{
std::lock_guard<std::mutex> lock(iaxCallMapMutex_);
auto call = getIAXCall(id);
if (!call)
return;
{
std::lock_guard<std::mutex> lock(mutexIAX);
iax_transfer(call->session, callto);
}
}
}
bool
IAXVoIPLink::attendedTransfer(const std::string& /*transferID*/, const std::string& /*targetID*/)
{
return false; // TODO
}
void
IAXVoIPLink::carryingDTMFdigits(const std::string& id, char code)
{
......
......@@ -149,21 +149,6 @@ class IAXVoIPLink : public VoIPLink {
*/
virtual void offhold(const std::string& id);
/**
* Transfer a call
* @param id The ID of the call
* @param to The recipient of the transfer
*/
virtual void transfer(const std::string& id, const std::string& to);
/**
* Perform attended transfer
* @param Transfered call ID
* @param Target call ID
* @return true on success
*/
virtual bool attendedTransfer(const std::string& transferID, const std::string& targetID);
/**
* Send DTMF
* @param id The ID of the call
......
......@@ -615,10 +615,10 @@ bool ManagerImpl::transferCall(const std::string& callId, const std::string& to)
} else if (not isConference(getCurrentCallId()))
unsetCurrentCall();
std::string accountID(getAccountFromCall(callId));
if (accountID.empty())
if (auto call = getCallFromCallID(callId))
call->transfer(to);
else
return false;
getAccountLink(accountID)->transfer(callId, to);
// remove waiting call in case we make transfer without even answer
removeWaitingCall(callId);
......@@ -638,10 +638,9 @@ void ManagerImpl::transferSucceeded()
bool ManagerImpl::attendedTransfer(const std::string& transferID, const std::string& targetID)
{
std::string accountid(getAccountFromCall(transferID));
if (accountid.empty())
return false;
return getAccountLink(accountid)->attendedTransfer(transferID, targetID);
if (auto call = getCallFromCallID(transferID))
return call->attendedTransfer(targetID);
return false;
}
//THREAD=Main : Call:Incoming
......
......@@ -51,6 +51,10 @@ getSettings()
static const int INITIAL_SIZE = 16384;
static const int INCREMENT_SIZE = INITIAL_SIZE;
/** A map to retreive SFLphone internal call id
* Given a SIP call ID (usefull for transaction sucha as transfer)*/
static std::map<std::string, std::string> transferCallID;
static void
updateSDPFromSTUN(SIPCall &call, SIPAccount &account, const SipTransport &transport)
{
......@@ -349,3 +353,173 @@ SIPCall::refuse()
siplink.removeSipCall(getCallId());
}
static void
transfer_client_cb(pjsip_evsub *sub, pjsip_event *event)
{
auto mod_ua_id = SIPVoIPLink::instance().getMod()->id;
switch (pjsip_evsub_get_state(sub)) {
case PJSIP_EVSUB_STATE_ACCEPTED:
if (!event)
return;
pj_assert(event->type == PJSIP_EVENT_TSX_STATE && event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
break;
case PJSIP_EVSUB_STATE_TERMINATED:
pjsip_evsub_set_mod_data(sub, mod_ua_id, NULL);
break;
case PJSIP_EVSUB_STATE_ACTIVE: {
if (!event)
return;
pjsip_rx_data* r_data = event->body.rx_msg.rdata;
if (!r_data)
return;
std::string request(pjsip_rx_data_get_info(r_data));
pjsip_status_line status_line = { 500, *pjsip_get_status_text(500) };
if (!r_data->msg_info.msg)
return;
if (r_data->msg_info.msg->line.req.method.id == PJSIP_OTHER_METHOD and
request.find("NOTIFY") != std::string::npos) {
pjsip_msg_body *body = r_data->msg_info.msg->body;
if (!body)
return;
if (pj_stricmp2(&body->content_type.type, "message") or
pj_stricmp2(&body->content_type.subtype, "sipfrag"))
return;
if (pjsip_parse_status_line((char*) body->data, body->len, &status_line) != PJ_SUCCESS)
return;
}
if (!r_data->msg_info.cid)
return;
auto call = static_cast<SIPCall *>(pjsip_evsub_get_mod_data(sub, mod_ua_id));
if (!call)
return;
if (status_line.code / 100 == 2) {
pjsip_tx_data *tdata;
if (!call->inv)
return;
if (pjsip_inv_end_session(call->inv, PJSIP_SC_GONE, NULL, &tdata) == PJ_SUCCESS)
pjsip_inv_send_msg(call->inv, tdata);
Manager::instance().hangupCall(call->getCallId());
pjsip_evsub_set_mod_data(sub, mod_ua_id, NULL);
}
break;
}
default:
break;
}
}
bool
SIPCall::transferCommon(pj_str_t *dst)
{
if (!inv)
return false;
pjsip_evsub_user xfer_cb;
pj_bzero(&xfer_cb, sizeof(xfer_cb));
xfer_cb.on_evsub_state = &transfer_client_cb;
pjsip_evsub *sub;
if (pjsip_xfer_create_uac(inv->dlg, &xfer_cb, &sub) != PJ_SUCCESS)
return false;
auto& siplink = SIPVoIPLink::instance();
/* Associate this voiplink of call with the client subscription
* We can not just associate call with the client subscription
* because after this function, we can no find the cooresponding
* voiplink from the call any more. But the voiplink is useful!
*/
pjsip_evsub_set_mod_data(sub, siplink.getMod()->id, this);
/*
* Create REFER request.
*/
pjsip_tx_data *tdata;
if (pjsip_xfer_initiate(sub, dst, &tdata) != PJ_SUCCESS)
return false;
// Put SIP call id in map in order to retrieve call during transfer callback
std::string callidtransfer(inv->dlg->call_id->id.ptr, inv->dlg->call_id->id.slen);
transferCallID[callidtransfer] = getCallId();
/* Send. */
if (pjsip_xfer_send_request(sub, tdata) != PJ_SUCCESS)
return false;
return true;
}
void
SIPCall::transfer(const std::string& to)
{
stopRecording();
std::string account_id(getAccountId());
SIPAccount *account = Manager::instance().getSipAccount(account_id);
if (account == NULL)
throw VoipLinkException("Could not find account");
std::string toUri;
pj_str_t dst = { 0, 0 };
toUri = account->getToUri(to);
pj_cstr(&dst, toUri.c_str());
DEBUG("Transferring to %.*s", dst.slen, dst.ptr);
if (!transferCommon(&dst))
throw VoipLinkException("Couldn't transfer");
}
bool
SIPCall::attendedTransfer(const std::string& /*to*/)
{
if (!inv or !inv->dlg)
throw VoipLinkException("Couldn't get invite dialog");
pjsip_dialog *target_dlg = inv->dlg;
pjsip_uri *uri = (pjsip_uri*) pjsip_uri_get_uri(target_dlg->remote.info->uri);
char str_dest_buf[PJSIP_MAX_URL_SIZE * 2] = { '<' };
pj_str_t dst = { str_dest_buf, 1 };
dst.slen += pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri, str_dest_buf + 1, sizeof(str_dest_buf) - 1);
dst.slen += pj_ansi_snprintf(str_dest_buf + dst.slen,
sizeof(str_dest_buf) - dst.slen,
"?"
"Replaces=%.*s"
"%%3Bto-tag%%3D%.*s"
"%%3Bfrom-tag%%3D%.*s>",
(int)target_dlg->call_id->id.slen,
target_dlg->call_id->id.ptr,
(int)target_dlg->remote.info->tag.slen,
target_dlg->remote.info->tag.ptr,
(int)target_dlg->local.info->tag.slen,
target_dlg->local.info->tag.ptr);
return transferCommon(&dst);
}
......@@ -121,6 +121,10 @@ class SIPCall : public Call {
void refuse();
void transfer(const std::string& to);
bool attendedTransfer(const std::string& to);
private:
// override of Call::createHistoryEntry
......@@ -129,6 +133,11 @@ class SIPCall : public Call {
NON_COPYABLE(SIPCall);
/**
* Transfer method used for both type of transfer
*/
bool transferCommon(pj_str_t *dst);
/**
* Audio Rtp Session factory
*/
......
......@@ -91,10 +91,6 @@ SIPVoIPLink *SIPVoIPLink::instance_ = nullptr;
/** Environment variable used to set pjsip's logging level */
#define SIPLOGLEVEL "SIPLOGLEVEL"
/** A map to retreive SFLphone internal call id
* Given a SIP call ID (usefull for transaction sucha as transfer)*/
static std::map<std::string, std::string> transferCallID;
/**************** EXTERN VARIABLES AND FUNCTIONS (callbacks) **************************/
/**
......@@ -117,8 +113,6 @@ static void outgoing_request_forked_cb(pjsip_inv_session *inv, pjsip_event *e);
static void transaction_state_changed_cb(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e);
static void registration_cb(pjsip_regc_cbparam *param);
static void transfer_client_cb(pjsip_evsub *sub, pjsip_event *event);
/**
* Helper function to process refer function on call transfer
*/
......@@ -1210,109 +1204,6 @@ SIPVoIPLink::tryGetSIPCall(const std::string& id)
return call;
}
bool
SIPVoIPLink::transferCommon(SIPCall *call, pj_str_t *dst)
{
if (!call or !call->inv)
return false;
pjsip_evsub_user xfer_cb;
pj_bzero(&xfer_cb, sizeof(xfer_cb));
xfer_cb.on_evsub_state = &transfer_client_cb;
pjsip_evsub *sub;
if (pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub) != PJ_SUCCESS)
return false;
/* Associate this voiplink of call with the client subscription
* We can not just associate call with the client subscription
* because after this function, we can no find the cooresponding
* voiplink from the call any more. But the voiplink is useful!
*/
pjsip_evsub_set_mod_data(sub, mod_ua_.id, this);
/*
* Create REFER request.
*/
pjsip_tx_data *tdata;
if (pjsip_xfer_initiate(sub, dst, &tdata) != PJ_SUCCESS)
return false;
// Put SIP call id in map in order to retrieve call during transfer callback
std::string callidtransfer(call->inv->dlg->call_id->id.ptr, call->inv->dlg->call_id->id.slen);
transferCallID[callidtransfer] = call->getCallId();
/* Send. */
if (pjsip_xfer_send_request(sub, tdata) != PJ_SUCCESS)
return false;
return true;
}
void
SIPVoIPLink::transfer(const std::string& id, const std::string& to)
{
auto call = getSipCall(id);
if (!call)
return;
call->stopRecording();
std::string account_id(call->getAccountId());
SIPAccount *account = Manager::instance().getSipAccount(account_id);
if (account == NULL)
throw VoipLinkException("Could not find account");
std::string toUri;
pj_str_t dst = { 0, 0 };
toUri = account->getToUri(to);
pj_cstr(&dst, toUri.c_str());
DEBUG("Transferring to %.*s", dst.slen, dst.ptr);
if (!transferCommon(call.get(), &dst))
throw VoipLinkException("Couldn't transfer");
}
bool SIPVoIPLink::attendedTransfer(const std::string& id, const std::string& /*to*/)
{
auto toCall = getSipCall(id);
if (!toCall)
return false;
if (!toCall->inv or !toCall->inv->dlg)
throw VoipLinkException("Couldn't get invite dialog");
pjsip_dialog *target_dlg = toCall->inv->dlg;
pjsip_uri *uri = (pjsip_uri*) pjsip_uri_get_uri(target_dlg->remote.info->uri);
char str_dest_buf[PJSIP_MAX_URL_SIZE * 2] = { '<' };
pj_str_t dst = { str_dest_buf, 1 };
dst.slen += pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri, str_dest_buf + 1, sizeof(str_dest_buf) - 1);
dst.slen += pj_ansi_snprintf(str_dest_buf + dst.slen,
sizeof(str_dest_buf) - dst.slen,
"?"
"Replaces=%.*s"
"%%3Bto-tag%%3D%.*s"
"%%3Bfrom-tag%%3D%.*s>",
(int)target_dlg->call_id->id.slen,
target_dlg->call_id->id.ptr,
(int)target_dlg->remote.info->tag.slen,
target_dlg->remote.info->tag.ptr,
(int)target_dlg->local.info->tag.slen,
target_dlg->local.info->tag.ptr);
auto call = getSipCall(id);
if (!call)
return false;
return transferCommon(call.get(), &dst);
}
static void
sendSIPInfo(const SIPCall &call, const char *const body, const char *const subtype)
{
......@@ -2173,83 +2064,6 @@ onCallTransfered(pjsip_inv_session *inv, pjsip_rx_data *rdata)
}
}
static void
transfer_client_cb(pjsip_evsub *sub, pjsip_event *event)
{
switch (pjsip_evsub_get_state(sub)) {
case PJSIP_EVSUB_STATE_ACCEPTED:
if (!event)
return;
pj_assert(event->type == PJSIP_EVENT_TSX_STATE && event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
break;
case PJSIP_EVSUB_STATE_TERMINATED:
pjsip_evsub_set_mod_data(sub, mod_ua_.id, NULL);
break;
case PJSIP_EVSUB_STATE_ACTIVE: {
SIPVoIPLink *link = static_cast<SIPVoIPLink *>(pjsip_evsub_get_mod_data(sub, mod_ua_.id));
if (!link or !event)
return;
pjsip_rx_data* r_data = event->body.rx_msg.rdata;
if (!r_data)
return;
std::string request(pjsip_rx_data_get_info(r_data));
pjsip_status_line status_line = { 500, *pjsip_get_status_text(500) };
if (!r_data->msg_info.msg)
return;
if (r_data->msg_info.msg->line.req.method.id == PJSIP_OTHER_METHOD and
request.find("NOTIFY") != std::string::npos) {
pjsip_msg_body *body = r_data->msg_info.msg->body;
if (!body)
return;
if (pj_stricmp2(&body->content_type.type, "message") or
pj_stricmp2(&body->content_type.subtype, "sipfrag"))
return;
if (pjsip_parse_status_line((char*) body->data, body->len, &status_line) != PJ_SUCCESS)
return;
}
if (!r_data->msg_info.cid)
return;
std::string transferID(r_data->msg_info.cid->id.ptr, r_data->msg_info.cid->id.slen);
auto call = SIPVoIPLink::instance().getSipCall(transferCallID[transferID]);
if (!call)
return;
if (status_line.code / 100 == 2) {
pjsip_tx_data *tdata;
if (!call->inv)
return;
if (pjsip_inv_end_session(call->inv, PJSIP_SC_GONE, NULL, &tdata) == PJ_SUCCESS)
pjsip_inv_send_msg(call->inv, tdata);
Manager::instance().hangupCall(call->getCallId());
pjsip_evsub_set_mod_data(sub, mod_ua_.id, NULL);
}
break;
}
default:
break;
}
}
static void
setCallMediaLocal(SIPCall* call, const pj_sockaddr& localIP)
{
......
......@@ -179,26 +179,6 @@ class SIPVoIPLink : public VoIPLink {
*/
virtual void offhold(const std::string& id);
/**
* Transfer method used for both type of transfer
*/
bool transferCommon(SIPCall *call, pj_str_t *dst);
/**
* Transfer the call
* @param id The call identifier
* @param to The recipient of the transfer
*/
virtual void transfer(const std::string& id, const std::string& to);
/**
* Attended transfer
* @param The transfered call id
* @param The target call id
* @return True on success
*/
virtual bool attendedTransfer(const std::string&, const std::string&);
/**
* Send DTMF refering to account configuration
* @param id The call identifier
......
......@@ -113,21 +113,6 @@ class VoIPLink {
*/
virtual void offhold(const std::string &id) = 0;
/**
* Transfer a call to specified URI
* @param id The call identifier
* @param to The recipient of the call
*/
virtual void transfer(const std::string &id, const std::string &to) = 0;
/**
* Attended transfer
* @param The transfered call id
* @param The target call id
* @return True on success
*/
virtual bool attendedTransfer(const std::string&, const std::string&) = 0;
/**
* Send DTMF
* @param id The call identifier
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment