summaryrefslogtreecommitdiff
path: root/channels
diff options
context:
space:
mode:
authorRussell Bryant <russell@russellbryant.com>2008-09-30 21:32:53 +0000
committerRussell Bryant <russell@russellbryant.com>2008-09-30 21:32:53 +0000
commitf1dd1fe1c7a2fcdbbe6c64919dad9345f20aa379 (patch)
treeb7bfa36f7449073e0046d1ae9c7671a4cb91c986 /channels
parente3311c98754b4e846e9c788241e74af04538c89f (diff)
Add support for call pickup on Snom phones. Asterisk now includes a magic
call-id in the dialog-info event package used with extension state subscriptions on Snom phones. Then, when the phone sends an INVITE with Replaces for the special callid, Asterisk will perform a pickup on the extension that was subscribed to. The original code on this issue was submitted by xylome. However, contributions have been made by (at least) mgernoth and pkempgen. The final patch was written by seanbright, and includes the necessary logic to allow this work in a technology independent way. (closes issue #5014) Reported by: xylome Patches: issue5014-trunk.diff uploaded by seanbright (license 71) git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@145226 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'channels')
-rw-r--r--channels/chan_sip.c119
1 files changed, 102 insertions, 17 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index e5d911ec9..3bba054ea 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -1957,6 +1957,7 @@ static void peer_mailboxes_to_str(struct ast_str **mailbox_str, struct sip_peer
static int sip_refer_allocate(struct sip_pvt *p);
static void ast_quiet_chan(struct ast_channel *chan);
static int attempt_transfer(struct sip_dual *transferer, struct sip_dual *target);
+static int do_magic_pickup(struct ast_channel *channel, const char *extension, const char *context);
/*!
* \brief generic function for determining if a correct transport is being
* used to contact a peer
@@ -9367,11 +9368,28 @@ static int transmit_state_notify(struct sip_pvt *p, int state, int full, int tim
break;
case DIALOG_INFO_XML: /* SNOM subscribes in this format */
ast_str_append(&tmp, 0, "<?xml version=\"1.0\"?>\n");
- ast_str_append(&tmp, 0, "<dialog-info xmlns=\"urn:ietf:params:xml:ns:dialog-info\" version=\"%d\" state=\"%s\" entity=\"%s\">\n", p->dialogver++, full ? "full":"partial", mto);
- if ((state & AST_EXTENSION_RINGING) && global_notifyringing)
- ast_str_append(&tmp, 0, "<dialog id=\"%s\" direction=\"recipient\">\n", p->exten);
- else
+ ast_str_append(&tmp, 0, "<dialog-info xmlns=\"urn:ietf:params:xml:ns:dialog-info\" version=\"%d\" state=\"%s\" entity=\"%s\">\n", p->dialogver++, full ? "full" : "partial", mto);
+ if ((state & AST_EXTENSION_RINGING) && global_notifyringing) {
+ /* We create a fake call-id which the phone will send back in an INVITE
+ Replaces header which we can grab and do some magic with. */
+ ast_str_append(&tmp, 0,
+ "<dialog id=\"%s\" call-id=\"pickup-%s\" direction=\"recipient\">\n"
+ "<remote>\n"
+ /* Note that the identity and target elements for the local participant are currently
+ (and may forever be) incorrect since we have no reliable way to get at that information
+ at the moment. Luckily the phone seems to still live happily without it being correct */
+ "<identity>%s</identity>\n"
+ "<target uri=\"%s\"/>\n"
+ "</remote>\n"
+ "<local>\n"
+ "<identity>%s</identity>\n"
+ "<target uri=\"%s\"/>\n"
+ "</local>\n",
+ p->exten, p->callid,
+ mto, mto, mto, mto);
+ } else {
ast_str_append(&tmp, 0, "<dialog id=\"%s\">\n", p->exten);
+ }
ast_str_append(&tmp, 0, "<state>%s</state>\n", statestring);
if (state == AST_EXTENSION_ONHOLD) {
ast_str_append(&tmp, 0, "<local>\n<target uri=\"%s\">\n"
@@ -17116,6 +17134,27 @@ static int sip_uri_cmp(const char *input1, const char *input2)
return sip_uri_params_cmp(params1, params2);
}
+static int do_magic_pickup(struct ast_channel *channel, const char *extension, const char *context)
+{
+ struct ast_str *str = ast_str_alloca(AST_MAX_EXTENSION + AST_MAX_CONTEXT + 2);
+ struct ast_app *pickup = pbx_findapp("Pickup");
+
+ if (!pickup) {
+ ast_log(LOG_ERROR, "Unable to perform pickup: Application 'Pickup' not loaded (app_directed_pickup.so).\n");
+ return -1;
+ }
+
+ ast_str_set(&str, 0, "%s@%s", extension, context);
+
+ ast_debug(2, "About to call Pickup(%s)\n", str->str);
+
+ /* There is no point in capturing the return value since pickup_exec
+ doesn't return anything meaningful unless the passed data is an empty
+ string (which in our case it will not be) */
+ pbx_exec(channel, pickup, str->str);
+
+ return 0;
+}
/*! \brief Handle incoming INVITE request
\note If the INVITE has a Replaces header, it is part of an
@@ -17143,6 +17182,12 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int
int st_interval = 0; /* Session-Timer negotiated refresh interval */
enum st_refresher st_ref; /* Session-Timer session refresher */
int dlg_min_se = -1;
+ struct {
+ char exten[AST_MAX_EXTENSION];
+ char context[AST_MAX_CONTEXT];
+ } pickup = {
+ .exten = "",
+ };
st_ref = SESSION_TIMER_REFRESHER_AUTO;
/* Find out what they support */
@@ -17279,14 +17324,36 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int
}
}
- if (sipdebug)
- ast_debug(4, "Invite/replaces: Will use Replace-Call-ID : %s Fromtag: %s Totag: %s\n", replace_id, fromtag ? fromtag : "<no from tag>", totag ? totag : "<no to tag>");
-
+ if (sipdebug)
+ ast_debug(4, "Invite/replaces: Will use Replace-Call-ID : %s Fromtag: %s Totag: %s\n",
+ replace_id,
+ fromtag ? fromtag : "<no from tag>",
+ totag ? totag : "<no to tag>");
+
+ /* Try to find call that we are replacing.
+ If we have a Replaces header, we need to cancel that call if we succeed with this call.
+ First we cheat a little and look for a magic call-id from phones that support
+ dialog-info+xml so we can do technology independent pickup... */
+ if (strncmp(replace_id, "pickup-", 7) == 0) {
+ struct sip_pvt *subscription = NULL;
+ replace_id += 7; /* Worst case we are looking at \0 */
+
+ if ((subscription = get_sip_pvt_byid_locked(replace_id, NULL, NULL)) == NULL) {
+ ast_log(LOG_NOTICE, "Unable to find subscription with call-id: %s\n", replace_id);
+ transmit_response_reliable(p, "481 Call Leg Does Not Exist (Replaces)", req);
+ error = 1;
+ } else {
+ ast_log(LOG_NOTICE, "Trying to pick up %s@%s\n", subscription->exten, subscription->context);
+ ast_copy_string(pickup.exten, subscription->exten, sizeof(pickup.exten));
+ ast_copy_string(pickup.context, subscription->context, sizeof(pickup.context));
+ sip_pvt_unlock(subscription);
+ if (subscription->owner) {
+ ast_channel_unlock(subscription->owner);
+ }
+ }
+ }
- /* Try to find call that we are replacing
- If we have a Replaces header, we need to cancel that call if we succeed with this call
- */
- if ((p->refer->refer_call = get_sip_pvt_byid_locked(replace_id, totag, fromtag)) == NULL) {
+ if (!error && ast_strlen_zero(pickup.exten) && (p->refer->refer_call = get_sip_pvt_byid_locked(replace_id, totag, fromtag)) == NULL) {
ast_log(LOG_NOTICE, "Supervised transfer attempted to replace non-existent call id (%s)!\n", replace_id);
transmit_response_reliable(p, "481 Call Leg Does Not Exist (Replaces)", req);
error = 1;
@@ -17305,7 +17372,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int
error = 1;
}
- if (!error && !p->refer->refer_call->owner) {
+ if (!error && ast_strlen_zero(pickup.exten) && !p->refer->refer_call->owner) {
/* Oops, someting wrong anyway, no owner, no call */
ast_log(LOG_NOTICE, "Supervised transfer attempted to replace non-existing call id (%s)!\n", replace_id);
/* Check for better return code */
@@ -17313,7 +17380,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int
error = 1;
}
- if (!error && p->refer->refer_call->owner->_state != AST_STATE_RINGING && p->refer->refer_call->owner->_state != AST_STATE_RING && p->refer->refer_call->owner->_state != AST_STATE_UP ) {
+ if (!error && ast_strlen_zero(pickup.exten) && p->refer->refer_call->owner->_state != AST_STATE_RINGING && p->refer->refer_call->owner->_state != AST_STATE_RING && p->refer->refer_call->owner->_state != AST_STATE_UP) {
ast_log(LOG_NOTICE, "Supervised transfer attempted to replace non-ringing or active call id (%s)!\n", replace_id);
transmit_response_reliable(p, "603 Declined (Replaces)", req);
error = 1;
@@ -17626,10 +17693,28 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int
p->lastinvite = seqno;
if (replace_id) { /* Attended transfer or call pickup - we're the target */
- /* Go and take over the target call */
- if (sipdebug)
- ast_debug(4, "Sending this call to the invite/replcaes handler %s\n", p->callid);
- return handle_invite_replaces(p, req, debug, seqno, sin);
+ if (!ast_strlen_zero(pickup.exten)) {
+ append_history(p, "Xfer", "INVITE/Replace received");
+
+ /* Let the caller know we're giving it a shot */
+ transmit_response(p, "100 Trying", req);
+ ast_setstate(c, AST_STATE_RING);
+
+ /* Do the pickup itself */
+ ast_channel_unlock(c);
+ *nounlock = 1;
+ do_magic_pickup(c, pickup.exten, pickup.context);
+
+ /* Now we're either masqueraded or we failed to pickup, in either case we... */
+ ast_hangup(c);
+
+ return 0;
+ } else {
+ /* Go and take over the target call */
+ if (sipdebug)
+ ast_debug(4, "Sending this call to the invite/replcaes handler %s\n", p->callid);
+ return handle_invite_replaces(p, req, debug, seqno, sin);
+ }
}