Add the ability to execute connected line interception macros.
authorMark Michelson <mmichelson@digium.com>
Mon, 1 Jun 2009 20:57:31 +0000 (20:57 +0000)
committerMark Michelson <mmichelson@digium.com>
Mon, 1 Jun 2009 20:57:31 +0000 (20:57 +0000)
When connected line updates are received or generated in the middle
of an application call, it is now possible to execute a macro to
manipulate the connected line data. This way, phone numbers may be
manipulated to be more presentable to users, names may be changed
for...whatever reason, or whatever else needs to be done may be.

Review: https://reviewboard.asterisk.org/r/256

AST-165

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@198727 65c4cc65-6c06-0410-ace0-fbb531ad65f3

12 files changed:
apps/app_dial.c
apps/app_directed_pickup.c
apps/app_macro.c
apps/app_queue.c
channels/chan_sip.c
doc/tex/channelvariables.tex
include/asterisk/app.h
include/asterisk/channel.h
main/app.c
main/channel.c
main/dial.c
main/features.c

index 249c0dc..92000ab 100644 (file)
@@ -937,7 +937,9 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
                                        ast_verb(3, "%s answered %s\n", c->name, in->name);
                                        if (!single && !ast_test_flag64(peerflags, OPT_IGNORE_CONNECTEDLINE)) {
                                                if (o->connected.id.number) {
-                                                       ast_channel_update_connected_line(in, &o->connected);
+                                                       if (ast_channel_connected_line_macro(c, in, &o->connected, 1, 0)) {
+                                                               ast_channel_update_connected_line(in, &o->connected);
+                                                       }
                                                } else if (!ast_test_flag64(o, DIAL_NOCONNECTEDLINE)) {
                                                        ast_channel_lock(c);
                                                        ast_connected_line_copy_from_caller(&connected_caller, &c->cid);
@@ -987,7 +989,9 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
                                                ast_verb(3, "%s answered %s\n", c->name, in->name);
                                                if (!single && !ast_test_flag64(peerflags, OPT_IGNORE_CONNECTEDLINE)) {
                                                        if (o->connected.id.number) {
-                                                               ast_channel_update_connected_line(in, &o->connected);
+                                                               if (ast_channel_connected_line_macro(c, in, &o->connected, 1, 0)) {
+                                                                       ast_channel_update_connected_line(in, &o->connected);
+                                                               }
                                                        } else if (!ast_test_flag64(o, DIAL_NOCONNECTEDLINE)) {
                                                                ast_channel_lock(c);
                                                                ast_connected_line_copy_from_caller(&connected_caller, &c->cid);
@@ -1076,8 +1080,9 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
                                                ast_party_connected_line_set(&o->connected, &connected);
                                                ast_party_connected_line_free(&connected);
                                        } else {
-                                               ast_verb(3, "%s connected line has changed, passing it to %s\n", c->name, in->name);
-                                               ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen);
+                                               if (ast_channel_connected_line_macro(c, in, f, 1, 1)) {
+                                                       ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen);
+                                               }
                                        }
                                        break;
                                case AST_CONTROL_REDIRECTING:
@@ -1198,15 +1203,19 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
                                if (ast_write(outgoing->chan, f))
                                        ast_log(LOG_WARNING, "Unable to forward voice or dtmf\n");
                        }
-                       if (single && (f->frametype == AST_FRAME_CONTROL) &&
-                               ((f->subclass == AST_CONTROL_HOLD) ||
-                               (f->subclass == AST_CONTROL_UNHOLD) ||
-                               (f->subclass == AST_CONTROL_VIDUPDATE) ||
-                               (f->subclass == AST_CONTROL_SRCUPDATE) ||
-                               (f->subclass == AST_CONTROL_CONNECTED_LINE) ||
-                               (f->subclass == AST_CONTROL_REDIRECTING))) {
-                               ast_verb(3, "%s requested special control %d, passing it to %s\n", in->name, f->subclass, outgoing->chan->name);
-                               ast_indicate_data(outgoing->chan, f->subclass, f->data.ptr, f->datalen);
+                       if (single && (f->frametype == AST_FRAME_CONTROL)) { 
+                               if ((f->subclass == AST_CONTROL_HOLD) ||
+                                   (f->subclass == AST_CONTROL_UNHOLD) ||
+                                   (f->subclass == AST_CONTROL_VIDUPDATE) ||
+                                   (f->subclass == AST_CONTROL_SRCUPDATE) ||
+                                   (f->subclass == AST_CONTROL_REDIRECTING)) {
+                                       ast_verb(3, "%s requested special control %d, passing it to %s\n", in->name, f->subclass, outgoing->chan->name);
+                                       ast_indicate_data(outgoing->chan, f->subclass, f->data.ptr, f->datalen);
+                               } else if (f->subclass == AST_CONTROL_CONNECTED_LINE) {
+                                       if (ast_channel_connected_line_macro(in, outgoing->chan, f, 0, 1)) {
+                                               ast_indicate_data(outgoing->chan, f->subclass, f->data.ptr, f->datalen);
+                                       }
+                               }
                        }
                        ast_frfree(f);
                }
index 2696b44..2e196a7 100644 (file)
@@ -98,7 +98,9 @@ static int pickup_do(struct ast_channel *chan, struct ast_channel *target)
 
        connected_caller = target->connected;
        connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
-       ast_channel_update_connected_line(chan, &connected_caller);
+       if (ast_channel_connected_line_macro(NULL, chan, &connected_caller, 0, 0)) {
+               ast_channel_update_connected_line(chan, &connected_caller);
+       }
 
        ast_channel_lock(chan);
        ast_connected_line_copy_from_caller(&connected_caller, &chan->cid);
index 1db3b27..f3a6b54 100644 (file)
@@ -36,6 +36,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/config.h"
 #include "asterisk/utils.h"
 #include "asterisk/lock.h"
+#include "asterisk/app.h"
 
 /*** DOCUMENTATION
        <application name="Macro" language="en_US">
index 3467947..3d367f6 100644 (file)
@@ -3092,7 +3092,9 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
                                        ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
                                        if (update_connectedline) {
                                                if (o->connected.id.number) {
-                                                       ast_channel_update_connected_line(in, &o->connected);
+                                                       if (ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) {
+                                                               ast_channel_update_connected_line(in, &o->connected);
+                                                       }
                                                } else if (o->update_connectedline) {
                                                        ast_channel_lock(o->chan);
                                                        ast_connected_line_copy_from_caller(&connected_caller, &o->chan->cid);
@@ -3192,7 +3194,9 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
                                                                ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
                                                                if (update_connectedline) {
                                                                        if (o->connected.id.number) {
-                                                                               ast_channel_update_connected_line(in, &o->connected);
+                                                                               if (ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) {
+                                                                                       ast_channel_update_connected_line(in, &o->connected);
+                                                                               }
                                                                        } else if (o->update_connectedline) {
                                                                                ast_channel_lock(o->chan);
                                                                                ast_connected_line_copy_from_caller(&connected_caller, &o->chan->cid);
@@ -3252,8 +3256,9 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
                                                                ast_party_connected_line_set(&o->connected, &connected);
                                                                ast_party_connected_line_free(&connected);
                                                        } else {
-                                                               ast_verb(3, "%s connected line has changed, passing it to %s\n", ochan_name, inchan_name);
-                                                               ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen);
+                                                               if (ast_channel_connected_line_macro(o->chan, in, f, 1, 1)) {
+                                                                       ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen);
+                                                               }
                                                        }
                                                        break;
                                                case AST_CONTROL_REDIRECTING:
index 9de5626..0913438 100644 (file)
@@ -20407,6 +20407,8 @@ static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual *
                                        /* Chan 2: Call from Asterisk to target */
        int res = 0;
        struct sip_pvt *targetcall_pvt;
+       struct ast_party_connected_line connected_to_transferee;
+       struct ast_party_connected_line connected_to_target;
 
        /* Check if the call ID of the replaces header does exist locally */
        if (!(targetcall_pvt = get_sip_pvt_byid_locked(transferer->refer->replaces_callid, transferer->refer->replaces_callid_totag, 
@@ -20474,6 +20476,13 @@ static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual *
                transferer->callid,
                target.chan1->name,
                target.chan1->uniqueid);
+       ast_party_connected_line_init(&connected_to_transferee);
+       ast_party_connected_line_init(&connected_to_target);
+       /* No need to lock current->chan1 here since it was locked in sipsock_read */
+       ast_party_connected_line_copy(&connected_to_transferee, &current->chan1->connected);
+       /* No need to lock target.chan1 here since it was locked in get_sip_pvt_byid_locked */
+       ast_party_connected_line_copy(&connected_to_target, &target.chan1->connected);
+       connected_to_target.source = connected_to_transferee.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;
        res = attempt_transfer(current, &target);
        sip_pvt_unlock(targetcall_pvt);
        if (res) {
@@ -20484,8 +20493,6 @@ static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual *
                        ast_channel_unlock(targetcall_pvt->owner);
                ast_clear_flag(&transferer->flags[0], SIP_DEFER_BYE_ON_TRANSFER);
        } else {
-               struct ast_party_connected_line connected_caller;
-
                /* Transfer succeeded! */
                const char *xfersound = pbx_builtin_getvar_helper(target.chan1, "ATTENDED_TRANSFER_COMPLETE_SOUND");
 
@@ -20501,45 +20508,29 @@ static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual *
                        ast_channel_unlock(targetcall_pvt->owner);
                }
 
-               ast_party_connected_line_init(&connected_caller);
+               /* By forcing the masquerade, we know that target.chan1 and target.chan2 are bridged. We then
+                * can queue connected line updates where they need to go.
+                *
+                * No need to lock target.chan1 here since it was previously locked in get_sip_pvt_byid_locked
+                */
+               if (target.chan1->masq) {
+                       /* If the channel thread already did the masquerade, then we don't need to do anything */
+                       ast_do_masquerade(target.chan1);
+               }
                if (target.chan2) {
-                       if (current->chan2) {
-                               /* Tell each of the other channels to whom they are now connected */
-                               ast_channel_lock(current->chan2);
-                               ast_connected_line_copy_from_caller(&connected_caller, &current->chan2->cid);
-                               ast_channel_unlock(current->chan2);
-                               connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;
-                               ast_channel_update_connected_line(target.chan2, &connected_caller);
-                               ast_channel_lock(target.chan2);
-                               ast_connected_line_copy_from_caller(&connected_caller, &target.chan2->cid);
-                               ast_channel_unlock(target.chan2);
-                               connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;
-                               ast_channel_update_connected_line(current->chan2, &connected_caller);
-                               ast_party_connected_line_free(&connected_caller);
-                       }
+                       ast_channel_queue_connected_line_update(target.chan1, &connected_to_transferee);
+                       ast_channel_queue_connected_line_update(target.chan2, &connected_to_target);
                } else {
-                       /* Notify the first other party that they are connected to someone else assuming that target.chan1
-                          has progressed far enough through the dialplan to have its called party information set. */
-                       if (current->chan2) {
-                               ast_channel_lock(target.chan1);
-                               ast_party_connected_line_copy(&connected_caller, &target.chan1->connected);
-                               ast_channel_unlock(target.chan1);
-                               connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;
-                               ast_channel_update_connected_line(current->chan2, &connected_caller);
-                               ast_party_connected_line_free(&connected_caller);
-                       }
-
-                       /* We can't indicate to the called channel directly so we force the masquerade to complete
-                          and queue and update to be read and passed-through */
-                       ast_channel_lock(target.chan1);
-                       ast_do_masquerade(target.chan1);
-                       ast_channel_unlock(target.chan1);
-
-                       ast_party_connected_line_collect_caller(&connected_caller, &target.chan1->cid);
-                       connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;
-                       ast_channel_queue_connected_line_update(target.chan1, &connected_caller);
+                       /* Since target.chan1 isn't actually connected to another channel, there is no way for us
+                        * to queue a frame so that its connected line status will be updated. Instead, we have to
+                        * change it directly. Since we are not the channel thread, we cannot run a connected line
+                        * interception macro on target.chan1
+                        */
+                       ast_channel_update_connected_line(target.chan1, &connected_to_target);
                }
        }
+       ast_party_connected_line_free(&connected_to_target);
+       ast_party_connected_line_free(&connected_to_transferee);
        if (targetcall_pvt)
                ao2_t_ref(targetcall_pvt, -1, "drop targetcall_pvt");
        return 1;
index 0a7e810..6b549ef 100644 (file)
@@ -1005,3 +1005,10 @@ ${OSPOUTTOKEN}          OSP token to use for out_bound call
 ${OSPOUTTIMELIMIT}      Duration limit for out_bound call
 ${OSPRESULTS}           Number of remained destinations
 \end{verbatim}
+
+\subsection{Connected line digit manipulation}
+\begin{verbatim}
+${CONNECTED_LINE_SEND_CALLEE_MACRO}        Macro to call before sending a connected line update to the callee
+${CONNECTED_LINE_SEND_CALLEE_MACRO_ARGS}   Arguments to pass to ${CONNECTED_LINE_SEND_CALLEE_MACRO}
+${CONNECTED_LINE_SEND_CALLER_MACRO}        Macro to call before sending a connected line update to the caller
+${CONNECTED_LINE_SEND_CALLER_MACRO_ARGS}   Arguments to pass to ${CONNECTED_LINE_SEND_CALLER_MACRO}
index d442e52..da947b5 100644 (file)
@@ -109,6 +109,26 @@ int ast_app_getdata(struct ast_channel *c, const char *prompt, char *s, int maxl
 int ast_app_getdata_full(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout, int audiofd, int ctrlfd);
 
 /*!
+ * \since 1.6.3
+ * \brief Run a macro on a channel, placing a second channel into autoservice.
+ *
+ * This is a shorthand method that makes it very easy to run a macro on any given 
+ * channel. It is perfectly reasonable to supply a NULL autoservice_chan here in case
+ * there is no channel to place into autoservice. It is very important that the 
+ * autoservice_chan parameter is not locked prior to calling ast_app_run_macro. A 
+ * deadlock could result, otherwise.
+ *
+ * \param autoservice_chan A channel to place into autoservice while the macro is run
+ * \param macro_chan The channel to run the macro on
+ * \param macro_name The name of the macro to run
+ * \param macro_args The arguments to pass to the macro
+ * \retval 0 success
+ * \retval -1 failure
+ */
+int ast_app_run_macro(struct ast_channel *autoservice_chan, struct ast_channel 
+               *macro_chan, const char * const macro_name, const char * const macro_args);
+
+/*!
  * \brief Set voicemail function callbacks
  * \param[in] inboxcount2_func set function pointer
  * \param[in] sayname_func set function pointer
index 193d0ff..6bfb7b6 100644 (file)
@@ -2550,6 +2550,34 @@ void ast_channel_update_redirecting(struct ast_channel *chan, const struct ast_p
  */
 void ast_channel_queue_redirecting_update(struct ast_channel *chan, const struct ast_party_redirecting *redirecting);
 
+/*!
+ * \since 1.6.3
+ * \brief Run a connected line interception macro and update a channel's connected line
+ * information
+ *
+ * Whenever we want to update a channel's connected line information, we may need to run
+ * a macro so that an administrator can manipulate the information before sending it 
+ * out. This function both runs the macro and sends the update to the channel.
+ *
+ * \param autoservice_chan Channel to place into autoservice while the macro is running. 
+ *     It is perfectly safe for this to be NULL 
+ * \param macro_chan The channel to run the macro on. Also the channel from which we 
+ *     determine which macro we need to run.
+ * \param connected_info Either an ast_party_connected_line or ast_frame pointer of type 
+ *     AST_CONTROL_CONNECTED_LINE
+ * \param caller If true, then run CONNECTED_LINE_CALLER_SEND_MACRO, otherwise run 
+ *     CONNECTED_LINE_CALLEE_SEND_MACRO
+ * \param frame If true, then connected_info is an ast_frame pointer, otherwise it is an 
+ *     ast_party_connected_line pointer.
+ * \retval 0 Success
+ * \retval -1 Either the macro does not exist, or there was an error while attempting to 
+ *     run the macro
+ *
+ * \todo Have multiple return codes based on the MACRO_RESULT
+ * \todo Make constants so that caller and frame can be more expressive than just '1' and
+ *     '0'
+ */
+int ast_channel_connected_line_macro(struct ast_channel *autoservice_chan, struct ast_channel *macro_chan, const void *connected_info, int caller, int frame);
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif
index 72af3e0..eacefab 100644 (file)
@@ -203,6 +203,28 @@ int ast_app_getdata_full(struct ast_channel *c, const char *prompt, char *s, int
        return res;
 }
 
+int ast_app_run_macro(struct ast_channel *autoservice_chan, struct ast_channel *macro_chan, const char * const macro_name, const char * const macro_args)
+{
+       struct ast_app *macro_app;
+       int res;
+       char buf[1024];
+
+       macro_app = pbx_findapp("Macro");
+       if (!macro_app) {
+               ast_log(LOG_WARNING, "Cannot run macro '%s' because the 'Macro' application in not available\n", macro_name);
+               return -1;
+       }
+       snprintf(buf, sizeof(buf), "%s%s%s", macro_name, ast_strlen_zero(macro_args) ? "" : ",", S_OR(macro_args, ""));
+       if (autoservice_chan) {
+               ast_autoservice_start(autoservice_chan);
+       }
+       res = pbx_exec(macro_chan, macro_app, buf);
+       if (autoservice_chan) {
+               ast_autoservice_stop(autoservice_chan);
+       }
+       return res;
+}
+
 static int (*ast_has_voicemail_func)(const char *mailbox, const char *folder) = NULL;
 static int (*ast_inboxcount_func)(const char *mailbox, int *newmsgs, int *oldmsgs) = NULL;
 static int (*ast_inboxcount2_func)(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs) = NULL;
index bd997de..d9ce2ef 100644 (file)
@@ -5008,13 +5008,19 @@ static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct
                        int bridge_exit = 0;
 
                        switch (f->subclass) {
+                       case AST_CONTROL_REDIRECTING:
+                               ast_indicate_data(other, f->subclass, f->data.ptr, f->datalen);
+                               break;
+                       case AST_CONTROL_CONNECTED_LINE:
+                               if (ast_channel_connected_line_macro(who, other, f, other == c0, 1)) {
+                                       ast_indicate_data(other, f->subclass, f->data.ptr, f->datalen);
+                               }
+                               break;
                        case AST_CONTROL_HOLD:
                        case AST_CONTROL_UNHOLD:
                        case AST_CONTROL_VIDUPDATE:
                        case AST_CONTROL_T38:
                        case AST_CONTROL_SRCUPDATE:
-                       case AST_CONTROL_CONNECTED_LINE:
-                       case AST_CONTROL_REDIRECTING:
                                ast_indicate_data(other, f->subclass, f->data.ptr, f->datalen);
                                if (jb_in_use) {
                                        ast_jb_empty_and_reset(c0, c1);
@@ -6503,6 +6509,44 @@ void ast_channel_queue_redirecting_update(struct ast_channel *chan, const struct
        ast_queue_control_data(chan, AST_CONTROL_REDIRECTING, data, datalen);
 }
 
+int ast_channel_connected_line_macro(struct ast_channel *autoservice_chan, struct ast_channel *macro_chan, const void *connected_info, int caller, int frame)
+{
+       const char *macro;
+       const char *macro_args;
+       union {
+               const struct ast_frame *frame;
+               const struct ast_party_connected_line *connected;
+       } pointer;
+       int retval;
+
+       if (frame) {
+               pointer.frame = connected_info;
+       } else {
+               pointer.connected = connected_info;
+       }
+
+       ast_channel_lock(macro_chan);
+       macro = ast_strdupa(S_OR(pbx_builtin_getvar_helper(macro_chan, caller ? "CONNECTED_LINE_CALLER_SEND_MACRO" : "CONNECTED_LINE_CALLEE_SEND_MACRO"), ""));
+       macro_args = ast_strdupa(S_OR(pbx_builtin_getvar_helper(macro_chan, caller ? "CONNECTED_LINE_CALLER_SEND_MACRO_ARGS" : "CONNECTED_LINE_CALLEE_SEND_MACRO_ARGS"), ""));
+       ast_channel_unlock(macro_chan);
+
+       if (ast_strlen_zero(macro)) {
+               return -1;
+       }
+
+       if (frame) {
+               ast_connected_line_parse_data(pointer.frame->data.ptr, pointer.frame->datalen, &macro_chan->connected);
+       } else {
+               ast_party_connected_line_copy(&macro_chan->connected, pointer.connected);
+       }
+
+       if (!(retval = ast_app_run_macro(autoservice_chan, macro_chan, macro, macro_args))) {
+               ast_channel_update_connected_line(macro_chan, &macro_chan->connected);
+       }
+
+       return retval;
+}
+
 /* DO NOT PUT ADDITIONAL FUNCTIONS BELOW THIS BOUNDARY
  *
  * ONLY FUNCTIONS FOR PROVIDING BACKWARDS ABI COMPATIBILITY BELONG HERE
index d38b482..1f65f50 100644 (file)
@@ -37,6 +37,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/dial.h"
 #include "asterisk/pbx.h"
 #include "asterisk/musiconhold.h"
+#include "asterisk/app.h"
 
 /*! \brief Main dialing structure. Contains global options, channels being dialed, and more! */
 struct ast_dial {
@@ -430,7 +431,9 @@ static void handle_frame(struct ast_dial *dial, struct ast_dial_channel *channel
                        break;
                case AST_CONTROL_CONNECTED_LINE:
                        ast_verb(3, "%s connected line has changed, passing it to %s\n", channel->owner->name, chan->name);
-                       ast_indicate_data(chan, AST_CONTROL_CONNECTED_LINE, fr->data.ptr, fr->datalen);
+                       if (ast_channel_connected_line_macro(channel->owner, chan, fr, 1, 1)) {
+                               ast_indicate_data(chan, AST_CONTROL_CONNECTED_LINE, fr->data.ptr, fr->datalen);
+                       }
                        break;
                case AST_CONTROL_REDIRECTING:
                        ast_verb(3, "%s redirecting info has changed, passing it to %s\n", channel->owner->name, chan->name);
index 6e3570e..6c15f25 100644 (file)
@@ -1377,6 +1377,9 @@ static int builtin_blindtransfer(struct ast_channel *chan, struct ast_channel *p
                        /* Set the channel's new extension, since it exists, using transferer context */
                        ast_set_flag(transferee, AST_FLAG_BRIDGE_HANGUP_DONT); /* don't let the after-bridge code run the h-exten */
                        ast_log(LOG_DEBUG,"ABOUT TO AST_ASYNC_GOTO, have a pbx... set HANGUP_DONT on chan=%s\n", transferee->name);
+                       if (ast_channel_connected_line_macro(transferee, transferer, &transferer->connected, 1, 0)) {
+                               ast_channel_update_connected_line(transferee, &transferer->connected);
+                       }
                        set_c_e_p(transferee, transferer_real_context, xferto, 0);
                }
                check_goto_on_transfer(transferer);
@@ -1635,13 +1638,43 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
                /* Due to a limitation regarding when callerID is set on a Local channel,
                 * we use the transferer's connected line information here.
                 */
+
+               /* xferchan is transferee, and newchan is the transfer target
+                * So...in a transfer, who is the caller and who is the callee?
+                *
+                * When the call is originally made, it is clear who is caller and callee.
+                * When a transfer occurs, it is my humble opinion that the transferee becomes
+                * the caller, and the transfer target is the callee.
+                *
+                * The problem is that these macros were set with the intention of the original
+                * caller and callee taking those roles. A transfer can totally mess things up,
+                * to be technical. What sucks even more is that you can't effectively change
+                * the macros in the dialplan during the call from the transferer to the transfer
+                * target because the transferee is stuck with whatever role he originally had.
+                *
+                * I think the answer here is just to make sure that it is well documented that
+                * during a transfer, the transferee is the "caller" and the transfer target
+                * is the "callee."
+                *
+                * This means that if party A calls party B, and party A transfers party B to
+                * party C, then B has switched roles for the call. Now party B will have the
+                * caller macro called on his channel instead of the callee macro.
+                *
+                * Luckily, the method by which the bridge is launched here ensures that the 
+                * transferee is the "chan" on the bridge and the transfer target is the "peer,"
+                * so my idea for the roles post-transfer does not require extensive code changes.
+                */
                connected_line.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;
-               ast_channel_update_connected_line(xferchan, &connected_line);
+               if (ast_channel_connected_line_macro(newchan, xferchan, &connected_line, 1, 0)) {
+                       ast_channel_update_connected_line(xferchan, &connected_line);
+               }
                ast_channel_lock(xferchan);
                ast_connected_line_copy_from_caller(&connected_line, &xferchan->cid);
                ast_channel_unlock(xferchan);
                connected_line.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;
-               ast_channel_update_connected_line(newchan, &connected_line);
+               if (ast_channel_connected_line_macro(xferchan, newchan, &connected_line, 0, 0)) {
+                       ast_channel_update_connected_line(newchan, &connected_line);
+               }
                ast_party_connected_line_free(&connected_line);
 
                if (ast_stream_and_wait(newchan, xfersound, ""))
@@ -1749,12 +1782,16 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
                ast_connected_line_copy_from_caller(&connected_line, &newchan->cid);
                ast_channel_unlock(newchan);
                connected_line.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;
-               ast_channel_update_connected_line(xferchan, &connected_line);
+               if (ast_channel_connected_line_macro(newchan, xferchan, &connected_line, 1, 0)) {
+                       ast_channel_update_connected_line(xferchan, &connected_line);
+               }
                ast_channel_lock(xferchan);
                ast_connected_line_copy_from_caller(&connected_line, &xferchan->cid);
                ast_channel_unlock(xferchan);
                connected_line.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;
-               ast_channel_update_connected_line(newchan, &connected_line);
+               if (ast_channel_connected_line_macro(xferchan, newchan, &connected_line, 0, 0)) {
+                       ast_channel_update_connected_line(newchan, &connected_line);
+               }
 
                ast_party_connected_line_free(&connected_line);
                
@@ -2370,7 +2407,9 @@ static struct ast_channel *feature_request_and_dial(struct ast_channel *caller,
                                        ready=1;
                                        break;
                                } else if (f->subclass == AST_CONTROL_CONNECTED_LINE) {
-                                       ast_indicate_data(caller, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen);
+                                       if (ast_channel_connected_line_macro(chan, caller, f, 1, 1)) {
+                                               ast_indicate_data(caller, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen);
+                                       }
                                } else if (f->subclass != -1) {
                                        ast_log(LOG_NOTICE, "Don't know what to do about control frame: %d\n", f->subclass);
                                }
@@ -2766,9 +2805,13 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast
                        case -1:
                                ast_indicate(other, f->subclass);
                                break;
+                       case AST_CONTROL_CONNECTED_LINE:
+                               if (!ast_channel_connected_line_macro(who, other, f, who != chan, 1)) {
+                                       break;
+                               }
+                               /* The implied "else" falls through purposely */
                        case AST_CONTROL_HOLD:
                        case AST_CONTROL_UNHOLD:
-                       case AST_CONTROL_CONNECTED_LINE:
                                ast_indicate_data(other, f->subclass, f->data.ptr, f->datalen);
                                break;
                        case AST_CONTROL_OPTION:
@@ -4546,7 +4589,9 @@ int ast_pickup_call(struct ast_channel *chan)
 
        connected_caller = cur->connected;
        connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
-       ast_channel_update_connected_line(chan, &connected_caller);
+       if (ast_channel_connected_line_macro(NULL, chan, &connected_caller, 0, 0)) {
+               ast_channel_update_connected_line(chan, &connected_caller);
+       }
 
        ast_party_connected_line_collect_caller(&connected_caller, &chan->cid);
        connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;