diff --git a/apps/app_directed_pickup.c b/apps/app_directed_pickup.c index 6fcde07..50d117b 100644 --- a/apps/app_directed_pickup.c +++ b/apps/app_directed_pickup.c @@ -92,6 +92,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") context is set on matching channels by the channel driver for the device. + + PickupOld1v4 + @@ -114,10 +117,58 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") This will pickup a specified channel if ringing. + + + Directed extension call pickup like it used to work before Asterisk 1.8; + it behaves as the one in Asterisk 1.4. + + + + + Specification of the pickup target. + + + + + Additional specifications of pickup targets. + + + + + + + This application can pickup a specified ringing channel like it did + before Asterisk 1.4. The REGULAR Pickup() + matches the context with the channel + context whereas THIS ONE matches the + dialcontext; the context where the + Dial() application was called. + Take this dialplan: + [incoming] + exten => 200,1,Goto(outgoing,call,1) + exten => *200,1,Goto(pickup,both,1) + [outgoing] + exten => call,1,Dial(SIP/alice) + [pickup] + exten => both,1,Pickup(call@incoming) + exten => both,n,PickupOld1v4(call@outgoing) + Compare the two pickup styles in + [pickup]. They will both match the call to + SIP/alice, but the PickupOld1v4() + application has more intuitive semantics. + + + Pickup + + ***/ +#define PICKUP_MODE_DEFAULT 0 +#define PICKUP_MODE_OLD1V4 1 + static const char app[] = "Pickup"; static const char app2[] = "PickupChan"; +static const char app3[] = "PickupOld1v4"; struct pickup_by_name_args { const char *name; @@ -187,13 +238,18 @@ static int pickup_by_channel(struct ast_channel *chan, char *pickup) } /* Attempt to pick up specified extension with context */ -static int pickup_by_exten(struct ast_channel *chan, const char *exten, const char *context) +static int pickup_by_exten(struct ast_channel *chan, const char *exten, const char *context, int old1v4) { struct ast_channel *target = NULL;/*!< Potential pickup target */ struct ast_channel_iterator *iter; int res = -1; - if (!(iter = ast_channel_iterator_by_exten_new(exten, context))) { + if (old1v4 == PICKUP_MODE_OLD1V4) { + iter = ast_channel_iterator_by_exten_and_dialcontext(exten, context); + } else { + iter = ast_channel_iterator_by_exten_new(exten, context); + } + if (!iter) { return -1; } @@ -269,17 +325,12 @@ static int pickup_by_group(struct ast_channel *chan) return res; } -/* application entry point for Pickup() */ -static int pickup_exec(struct ast_channel *chan, const char *data) +static int pickup_exec_by_mode(struct ast_channel *chan, const char *data, int old1v4) { char *tmp; char *exten; char *context; - if (ast_strlen_zero(data)) { - return pickup_by_group(chan) ? 0 : -1; - } - /* Parse extension (and context if there) */ tmp = ast_strdupa(data); while (!ast_strlen_zero(tmp) && (exten = strsep(&tmp, "&"))) { @@ -294,7 +345,7 @@ static int pickup_exec(struct ast_channel *chan, const char *data) if (ast_strlen_zero(context)) { context = (char *) ast_channel_context(chan); } - if (!pickup_by_exten(chan, exten, context)) { + if (!pickup_by_exten(chan, exten, context, old1v4)) { /* Pickup successful. Stop the dialplan this channel is a zombie. */ return -1; } @@ -306,6 +357,27 @@ static int pickup_exec(struct ast_channel *chan, const char *data) return 0; } +/* application entry point for Pickup() */ +static int pickup_exec(struct ast_channel *chan, const char *data) +{ + if (ast_strlen_zero(data)) { + return pickup_by_group(chan) ? 0 : -1; + } + + return pickup_exec_by_mode(chan, data, PICKUP_MODE_DEFAULT); +} + +/* application entry point for PickupOld1v4() */ +static int pickupold1v4_exec(struct ast_channel *chan, const char *data) +{ + if (ast_strlen_zero(data)) { + ast_log(LOG_WARNING, "Pickup requires an argument (extension)!\n"); + return -1; + } + + return pickup_exec_by_mode(chan, data, PICKUP_MODE_OLD1V4); +} + /* Find channel for pick up specified by partial channel name */ static int find_by_part(void *obj, void *arg, void *data, int flags) { @@ -391,6 +463,7 @@ static int unload_module(void) res = ast_unregister_application(app); res |= ast_unregister_application(app2); + res |= ast_unregister_application(app3); return res; } @@ -401,6 +474,7 @@ static int load_module(void) res = ast_register_application_xml(app, pickup_exec); res |= ast_register_application_xml(app2, pickupchan_exec); + res |= ast_register_application_xml(app3, pickupold1v4_exec); return res; } diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index 02aee5e..43851ef 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -2566,6 +2566,30 @@ struct ast_channel_iterator; struct ast_channel_iterator *ast_channel_iterator_destroy(struct ast_channel_iterator *i); /*! + * \brief Create a new channel iterator based on extension and context (old style) + * + * \param exten The extension that channels must be in + * \param dialcontext The dialcontext that channels must be in (not regular context) + * + * After creating an iterator using this function, the ast_channel_iterator_next() + * function can be used to iterate through all channels that are currently + * in the specified dialcontext and extension. + * + * \note You must call ast_channel_iterator_destroy() when done. + * + * \note This function was added to support pre-1.8 Pickup() behaviour which + * was changed by commit cba19c8a671. That commit altered the pickup from + * checking the "dialcontext" to checking the "context". + * + * \retval NULL on failure + * \retval a new channel iterator based on the specified parameters + * + * \since UNRELEASED + */ +struct ast_channel_iterator *ast_channel_iterator_by_exten_and_dialcontext( + const char *exten, const char *dialcontext); + +/*! * \brief Create a new channel iterator based on extension * * \param exten The extension that channels must be in diff --git a/main/channel.c b/main/channel.c index af7845a..276f158 100644 --- a/main/channel.c +++ b/main/channel.c @@ -1483,6 +1483,29 @@ static int ast_channel_by_name_cb(void *obj, void *arg, void *data, int flags) return ret; } +static int ast_channel_by_exten_and_dialcontext_cb(void *obj, void *arg, void *data, int flags) +{ + struct ast_channel *chan = obj; + char *dialcontext = arg; + char *exten = data; + int ret = CMP_MATCH; + + if (ast_strlen_zero(exten) || ast_strlen_zero(dialcontext)) { + ast_log(LOG_ERROR, "BUG! Must have a dialcontext and extension to match!\n"); + return CMP_STOP; + } + + ast_channel_lock(chan); + if (strcasecmp(ast_channel_dialcontext(chan), dialcontext)) { + ret = 0; /* Context match failed, continue */ + } else if (strcasecmp(ast_channel_exten(chan), exten) && strcasecmp(ast_channel_macroexten(chan), exten)) { + ret = 0; /* Extension match failed, continue */ + } + ast_channel_unlock(chan); + + return ret; +} + static int ast_channel_by_exten_cb(void *obj, void *arg, void *data, int flags) { struct ast_channel *chan = obj; @@ -1545,6 +1568,30 @@ struct ast_channel_iterator *ast_channel_iterator_destroy(struct ast_channel_ite return NULL; } +/*! Like ast_channel_iterator_by_exten_new, except it picks up channels by + * exten+dialcontext instead of exten+context. This re-introduces Pickup() + * behaviour removed in 1.8 in commit cba19c8a671. */ +struct ast_channel_iterator *ast_channel_iterator_by_exten_and_dialcontext( + const char *exten, const char *dialcontext) +{ + struct ast_channel_iterator *i; + char *l_exten = (char *) exten; + char *l_dialcontext = (char *) dialcontext; + + if (!(i = ast_calloc(1, sizeof(*i)))) { + return NULL; + } + + i->active_iterator = (void *) ast_channel_callback(ast_channel_by_exten_and_dialcontext_cb, + l_dialcontext, l_exten, OBJ_MULTIPLE); + if (!i->active_iterator) { + ast_free(i); + return NULL; + } + + return i; +} + struct ast_channel_iterator *ast_channel_iterator_by_exten_new(const char *exten, const char *context) { struct ast_channel_iterator *i;