Index: channels/chan_sip.c =================================================================== --- channels/chan_sip.c (.../branches/1.4) (revision 143963) +++ channels/chan_sip.c (.../team/seanbright/issue5014-1.4) (revision 143963) @@ -875,6 +875,13 @@ REFER_NOAUTH /*!< We had no auth for REFER */ }; +/*! \brief struct to store off exten and contact for tech independent pickup. + */ +struct pickup_target { + char *exten; + char *context; +}; + static const struct c_referstatusstring { enum referstatus status; char *text; @@ -1355,6 +1362,7 @@ 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 *exten, const char *context); /*--- Device monitoring and Device/extension state handling */ static int cb_extensionstate(char *context, char* exten, int state, void *data); @@ -7382,11 +7390,25 @@ break; case DIALOG_INFO_XML: /* SNOM subscribes in this format */ ast_build_string(&t, &maxbytes, "\n"); - ast_build_string(&t, &maxbytes, "\n", p->dialogver++, full ? "full":"partial", mto); - if ((state & AST_EXTENSION_RINGING) && global_notifyringing) - ast_build_string(&t, &maxbytes, "\n", p->exten); - else + ast_build_string(&t, &maxbytes, "\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_build_string(&t, &maxbytes, "\n", p->exten, p->callid); + ast_build_string(&t, &maxbytes, "\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 */ + ast_build_string(&t, &maxbytes, "%s\n", mto); + ast_build_string(&t, &maxbytes, "\n", mto); + ast_build_string(&t, &maxbytes, "\n"); + ast_build_string(&t, &maxbytes, "\n"); + ast_build_string(&t, &maxbytes, "%s\n", mto); + ast_build_string(&t, &maxbytes, "\n", mto); + ast_build_string(&t, &maxbytes, "\n"); + } else { ast_build_string(&t, &maxbytes, "\n", p->exten); + } ast_build_string(&t, &maxbytes, "%s\n", statestring); if (state == AST_EXTENSION_ONHOLD) { ast_build_string(&t, &maxbytes, "\n\n" @@ -14074,7 +14096,37 @@ return sip_uri_params_cmp(params1, params2); } +static int do_magic_pickup(struct ast_channel *channel, const char *extension, const char *context) +{ + char *argument = NULL; + int length; + 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; + } + + /* Enough room for 'extension', 'context', and '@' (and \0) */ + length = strlen(extension) + strlen(context) + 2; + + if (!(argument = ast_malloc(length))) { + ast_log(LOG_ERROR, "Failed to allocate memory for pickup extension.\n"); + return -1; + } + + snprintf(argument, length, "%s@%s", extension, context); + + /* 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, argument); + + ast_free(argument); + + return 0; +} + /*! \brief Handle incoming INVITE request \note If the INVITE has a Replaces header, it is part of an * attended transfer. If so, we do not go through the dial @@ -14091,6 +14143,7 @@ unsigned int required_profile = 0; struct ast_channel *c = NULL; /* New channel */ int reinvite = 0; + struct pickup_target *pickup = NULL; /* Find out what they support */ if (!p->sipoptions) { @@ -14227,10 +14280,35 @@ ast_log(LOG_DEBUG,"Invite/replaces: Will use Replace-Call-ID : %s Fromtag: %s Totag: %s\n", replace_id, fromtag ? fromtag : "", totag ? totag : ""); - /* 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) { + /* Try to find the 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 { + if (!(pickup = ast_calloc(1, sizeof(*pickup)))) { + ast_log(LOG_ERROR, "Memory allocation failed.\n"); + error = 1; + } else { + ast_log(LOG_NOTICE, "Trying to pick up %s@%s\n", subscription->exten, subscription->context); + pickup->exten = ast_strdup(subscription->exten); + pickup->context = ast_strdup(subscription->context); + ast_mutex_unlock(&subscription->lock); + if (subscription->owner) { + ast_channel_unlock(subscription->owner); + } + } + } + } + + if (!error && !pickup && (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; @@ -14249,7 +14327,7 @@ error = 1; } - if (!error && !p->refer->refer_call->owner) { + if (!error && !pickup && !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 */ @@ -14257,7 +14335,7 @@ 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 && !pickup && 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; @@ -14447,10 +14525,27 @@ p->lastinvite = seqno; if (replace_id) { /* Attended transfer or call pickup - we're the target */ - /* Go and take over the target call */ - if (sipdebug && option_debug > 3) - ast_log(LOG_DEBUG, "Sending this call to the invite/replcaes handler %s\n", p->callid); - return handle_invite_replaces(p, req, debug, ast_test_flag(req, SIP_PKT_IGNORE), seqno, sin); + if (pickup) { + 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 */ + do_magic_pickup(c, pickup->exten, pickup->context); + + /* Do a bit of cleanup */ + ast_free(pickup->exten); + ast_free(pickup->context); + ast_free(pickup); + return 0; + } else { + /* Go and take over the target call */ + if (sipdebug && option_debug > 3) + ast_log(LOG_DEBUG, "Sending this call to the invite/replcaes handler %s\n", p->callid); + return handle_invite_replaces(p, req, debug, ast_test_flag(req, SIP_PKT_IGNORE), seqno, sin); + } } Property changes on: . ___________________________________________________________________ Added: automerge + * Added: svnmerge-integrated + /branches/1.4:1-143955 Added: automerge-email + sean.bright+$branch@gmail.com