diff options
Diffstat (limited to 'channels')
-rw-r--r-- | channels/chan_sip.c | 119 |
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); + } } |