Index: channels/chan_sip.c =================================================================== --- channels/chan_sip.c (.../branches/1.4) (revision 153828) +++ channels/chan_sip.c (.../team/seanbright/issue13827-1.4) (revision 153828) @@ -510,6 +510,7 @@ #define DEFAULT_ALLOW_EXT_DOM TRUE #define DEFAULT_REALM "asterisk" #define DEFAULT_NOTIFYRINGING TRUE +#define DEFAULT_NOTIFYCID FALSE #define DEFAULT_PEDANTIC FALSE #define DEFAULT_AUTOCREATEPEER FALSE #define DEFAULT_QUALIFY FALSE @@ -542,6 +543,7 @@ static int global_rtautoclear; static int global_notifyringing; /*!< Send notifications on ringing */ static int global_notifyhold; /*!< Send notifications on hold */ +static int global_notifycid; /*!< Send CID with ringing notifications */ static int global_alwaysauthreject; /*!< Send 401 Unauthorized for all failing requests */ static int srvlookup; /*!< SRV Lookup on or off. Default is on */ static int pedanticsipchecking; /*!< Extra checking ? Default off */ @@ -1357,6 +1359,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); @@ -7424,11 +7427,47 @@ 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) { + char *local_display = (char *) p->exten, *local_target = mto; + + /* There are some limitations to how this works. The primary one is that the + callee must be dialing the same extension that is being monitored. Simply dialing + the hint'd device is not sufficient. */ + if (global_notifycid) { + struct ast_channel *caller = NULL; + + while ((caller = ast_channel_walk_locked(caller))) { + if (caller->pbx && + (!strcasecmp(caller->macroexten, p->exten) || !strcasecmp(caller->exten, p->exten)) && + !strcasecmp(caller->context, p->context)) { + local_display = ast_strdupa(caller->cid.cid_name); + local_target = ast_strdupa(caller->cid.cid_num); + ast_channel_unlock(caller); + break; + } + ast_channel_unlock(caller); + } + } + + /* 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" + "\n" + /* See the limitations of this above. Luckily the phone seems to still be + happy when these values are not correct. */ + "%s\n" + "\n" + "\n" + "\n" + "%s\n" + "\n" + "\n", + p->exten, p->callid, local_display, local_target, local_target, mto, mto); + } 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" @@ -10898,6 +10937,9 @@ ast_cli(fd, " Outbound reg. timeout: %d secs\n", global_reg_timeout); ast_cli(fd, " Outbound reg. attempts: %d\n", global_regattempts_max); ast_cli(fd, " Notify ringing state: %s\n", global_notifyringing ? "Yes" : "No"); + if (global_notifyringing) { + ast_cli(fd, " Include CID: %s\n", global_notifycid ? "Yes" : "No"); + } ast_cli(fd, " Notify hold state: %s\n", global_notifyhold ? "Yes" : "No"); ast_cli(fd, " SIP Transfer mode: %s\n", transfermode2str(global_allowtransfer)); ast_cli(fd, " Max Call Bitrate: %d kbps\r\n", default_maxcallbitrate); @@ -14120,7 +14162,27 @@ return sip_uri_params_cmp(params1, params2); } +static int do_magic_pickup(struct ast_channel *channel, const char *extension, const char *context) +{ + int length = AST_MAX_EXTENSION + AST_MAX_CONTEXT + 2; + char *argument = alloca(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; + } + + 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); + + 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 @@ -14137,6 +14199,12 @@ unsigned int required_profile = 0; struct ast_channel *c = NULL; /* New channel */ int reinvite = 0; + struct { + char exten[AST_MAX_EXTENSION]; + char context[AST_MAX_CONTEXT]; + } pickup = { + .exten = "", + }; /* Find out what they support */ if (!p->sipoptions) { @@ -14273,10 +14341,30 @@ 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 { + 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)); + ast_mutex_unlock(&subscription->lock); + if (subscription->owner) { + ast_channel_unlock(subscription->owner); + } + } + } + + 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; @@ -14295,7 +14383,7 @@ 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 */ @@ -14303,7 +14391,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 && 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; @@ -14493,10 +14581,28 @@ 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 (!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 && 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); + } } @@ -17470,6 +17576,7 @@ global_regcontext[0] = '\0'; expiry = DEFAULT_EXPIRY; global_notifyringing = DEFAULT_NOTIFYRINGING; + global_notifycid = DEFAULT_NOTIFYCID; global_limitonpeers = FALSE; global_directrtpsetup = FALSE; /* Experimental feature, disabled by default */ global_notifyhold = FALSE; @@ -17612,6 +17719,8 @@ global_notifyringing = ast_true(v->value); } else if (!strcasecmp(v->name, "notifyhold")) { global_notifyhold = ast_true(v->value); + } else if (!strcasecmp(v->name, "notifycid")) { + global_notifycid = ast_true(v->value); } else if (!strcasecmp(v->name, "alwaysauthreject")) { global_alwaysauthreject = ast_true(v->value); } else if (!strcasecmp(v->name, "mohinterpret") Index: configs/sip.conf.sample =================================================================== --- configs/sip.conf.sample (.../branches/1.4) (revision 153828) +++ configs/sip.conf.sample (.../team/seanbright/issue13827-1.4) (revision 153828) @@ -208,6 +208,16 @@ ;notifyhold = yes ; Notify subscriptions on HOLD state (default: no) ; Turning on notifyringing and notifyhold will add a lot ; more database transactions if you are using realtime. +;notifycid = yes ; Control whether caller ID information is sent along with + ; dialog-info+xml notifications (supported by snom phones). + ; Note that this feature will only work properly when the + ; incoming call is using the same extension and context that + ; is being used as the hint for the called extension. This means + ; that it won't work when using subscribecontext for your sip + ; user or peer (if subscribecontext is different than context). + ; This is also limited to a single caller, meaning that if an + ; extension is ringing because multiple calls are incoming, + ; only one will be used as the source of caller ID. ;limitonpeers = yes ; Apply call limits on peers only. This will improve ; status notification when you are using type=friend ; Inbound calls, that really apply to the user part