diff options
30 files changed, 11611 insertions, 232 deletions
@@ -387,6 +387,13 @@ Calendaring for Asterisk iCalendar, CalDAV, and Exchange Server calendars are supported (Exchange support only tested on Exchange Server 2003 with no support for forms-based authentication). +Call Completion Supplementary Services for Asterisk +--------------------------------------------------- + * Call completion support has been added for SIP, DAHDI/ISDN, and DAHDI/analog. + DAHDI/ISDN supports call completion for the following switch types: + EuroIsdn(ETSI) for PTP and PTMP modes, and Qsig. + See doc/CCSS_architecture.pdf and doc/tex/ccss.tex(asterisk.pdf) for details. + Multicast RTP Support --------------------- * A new RTP engine and channel driver have been added which supports Multicast RTP. diff --git a/apps/app_dial.c b/apps/app_dial.c index 8a58932a8..b1de21d5f 100644 --- a/apps/app_dial.c +++ b/apps/app_dial.c @@ -62,6 +62,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/global_datastores.h" #include "asterisk/dsp.h" #include "asterisk/cel.h" +#include "asterisk/ccss.h" #include "asterisk/indications.h" /*** DOCUMENTATION @@ -810,6 +811,12 @@ static void do_forward(struct chanlist *o, ast_channel_make_compatible(o->chan, in); ast_channel_inherit_variables(in, o->chan); ast_channel_datastore_inherit(in, o->chan); + /* When a call is forwarded, we don't want to track new interfaces + * dialed for CC purposes. Setting the done flag will ensure that + * any Dial operations that happen later won't record CC interfaces. + */ + ast_ignore_cc(o->chan); + ast_log(LOG_NOTICE, "Not accepting call completion offers from call-forward recipient %s\n", o->chan->name); } else ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s' (cause = %d)\n", tech, stuff, cause); } @@ -904,7 +911,8 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, struct chanlist *outgoing, int *to, struct ast_flags64 *peerflags, char *opt_args[], struct privacy_args *pa, - const struct cause_args *num_in, int *result, char *dtmf_progress) + const struct cause_args *num_in, int *result, char *dtmf_progress, + const int ignore_cc) { struct cause_args num = *num_in; int prestart = num.busy + num.congestion + num.nochan; @@ -917,6 +925,10 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, #endif struct ast_party_connected_line connected_caller; struct ast_str *featurecode = ast_str_alloca(FEATURE_MAX_LEN + 1); + int cc_recall_core_id; + int is_cc_recall; + int cc_frame_received = 0; + int num_ringing = 0; ast_party_connected_line_init(&connected_caller); if (single) { @@ -938,6 +950,8 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, } } + is_cc_recall = ast_cc_is_recall(in, &cc_recall_core_id, NULL); + #ifdef HAVE_EPOLL for (epollo = outgoing; epollo; epollo = epollo->next) ast_poll_channel_add(in, epollo->chan); @@ -970,6 +984,9 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, ast_verb(3, "No one is available to answer at this time (%d:%d/%d/%d)\n", numlines, num.busy, num.congestion, num.nochan); } *to = 0; + if (is_cc_recall) { + ast_cc_failed(cc_recall_core_id, "Everyone is busy/congested for the recall. How sad"); + } return NULL; } winner = ast_waitfor_n(watchers, pos, to); @@ -1014,6 +1031,15 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, /* here, o->chan == c == winner */ if (!ast_strlen_zero(c->call_forward)) { pa->sentringing = 0; + if (!ignore_cc && (f = ast_read(c))) { + if (f->frametype == AST_FRAME_CONTROL && f->subclass.integer == AST_CONTROL_CC) { + /* This channel is forwarding the call, and is capable of CC, so + * be sure to add the new device interface to the list + */ + ast_handle_cc_control_frame(in, c, f->data.ptr); + } + ast_frfree(f); + } do_forward(o, &num, peerflags, single, to); continue; } @@ -1088,13 +1114,41 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, handle_cause(AST_CAUSE_CONGESTION, &num); break; case AST_CONTROL_RINGING: - ast_verb(3, "%s is ringing\n", c->name); - /* Setup early media if appropriate */ - if (single && CAN_EARLY_BRIDGE(peerflags, in, c)) - ast_channel_early_bridge(in, c); - if (!(pa->sentringing) && !ast_test_flag64(outgoing, OPT_MUSICBACK) && ast_strlen_zero(opt_args[OPT_ARG_RINGBACK])) { - ast_indicate(in, AST_CONTROL_RINGING); - pa->sentringing++; + /* This is a tricky area to get right when using a native + * CC agent. The reason is that we do the best we can to send only a + * single ringing notification to the caller. + * + * Call completion complicates the logic used here. CCNR is typically + * offered during a ringing message. Let's say that party A calls + * parties B, C, and D. B and C do not support CC requests, but D + * does. If we were to receive a ringing notification from B before + * the others, then we would end up sending a ringing message to + * A with no CCNR offer present. + * + * The approach that we have taken is that if we receive a ringing + * response from a party and no CCNR offer is present, we need to + * wait. Specifically, we need to wait until either a) a called party + * offers CCNR in its ringing response or b) all called parties have + * responded in some way to our call and none offers CCNR. + * + * The drawback to this is that if one of the parties has a delayed + * response or, god forbid, one just plain doesn't respond to our + * outgoing call, then this will result in a significant delay between + * when the caller places the call and hears ringback. + * + * Note also that if CC is disabled for this call, then it is perfectly + * fine for ringing frames to get sent through. + */ + ++num_ringing; + if (ignore_cc || cc_frame_received || num_ringing == numlines) { + ast_verb(3, "%s is ringing\n", c->name); + /* Setup early media if appropriate */ + if (single && CAN_EARLY_BRIDGE(peerflags, in, c)) + ast_channel_early_bridge(in, c); + if (!(pa->sentringing) && !ast_test_flag64(outgoing, OPT_MUSICBACK) && ast_strlen_zero(opt_args[OPT_ARG_RINGBACK])) { + ast_indicate(in, AST_CONTROL_RINGING); + pa->sentringing++; + } } break; case AST_CONTROL_PROGRESS: @@ -1163,6 +1217,12 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, case AST_CONTROL_FLASH: /* Ignore going off hook and flash */ break; + case AST_CONTROL_CC: + if (!ignore_cc) { + ast_handle_cc_control_frame(in, c, f->data.ptr); + cc_frame_received = 1; + } + break; case -1: if (!ast_test_flag64(outgoing, OPT_RINGBACK | OPT_MUSICBACK)) { ast_verb(3, "%s stopped sounds\n", c->name); @@ -1212,6 +1272,9 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, } ast_frfree(f); } + if (is_cc_recall) { + ast_cc_completed(in, "CC completed, although the caller hung up (cancelled)"); + } return NULL; } @@ -1229,6 +1292,9 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, strcpy(pa->status, "CANCEL"); ast_frfree(f); ast_channel_unlock(in); + if (is_cc_recall) { + ast_cc_completed(in, "CC completed, but the caller used DTMF to exit"); + } return NULL; } ast_channel_unlock(in); @@ -1241,6 +1307,9 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, strcpy(pa->status, "CANCEL"); ast_cdr_noanswer(in->cdr); ast_frfree(f); + if (is_cc_recall) { + ast_cc_completed(in, "CC completed, but the caller hung up with DTMF"); + } return NULL; } } @@ -1283,6 +1352,9 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, } #endif + if (is_cc_recall) { + ast_cc_completed(in, "Recall completed!"); + } return peer; } @@ -1656,6 +1728,8 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast char *opt_args[OPT_ARG_ARRAY_SIZE]; struct ast_datastore *datastore = NULL; int fulldial = 0, num_dialed = 0; + int ignore_cc = 0; + char device_name[AST_CHANNEL_NAME]; /* Reset all DIAL variables back to blank, to prevent confusion (in case we don't reset all of them). */ pbx_builtin_setvar_helper(chan, "DIALSTATUS", ""); @@ -1686,6 +1760,10 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast goto done; } + if (ast_cc_call_init(chan, &ignore_cc)) { + goto done; + } + if (ast_test_flag64(&opts, OPT_SCREEN_NOINTRO) && !ast_strlen_zero(opt_args[OPT_ARG_SCREEN_NOINTRO])) { delprivintro = atoi(opt_args[OPT_ARG_SCREEN_NOINTRO]); @@ -1871,8 +1949,17 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast if (!rest) /* we are on the last destination */ chan->hangupcause = cause; chanlist_free(tmp); + if (!ignore_cc && (cause == AST_CAUSE_BUSY || cause == AST_CAUSE_CONGESTION)) { + if (!ast_cc_callback(chan, tech, numsubst, ast_cc_busy_interface)) { + ast_cc_extension_monitor_add_dialstring(chan, interface, ""); + } + } continue; } + ast_channel_get_device_name(tc, device_name, sizeof(device_name)); + if (!ignore_cc) { + ast_cc_extension_monitor_add_dialstring(chan, interface, device_name); + } pbx_builtin_setvar_helper(tc, "DIALEDPEERNUMBER", numsubst); ast_channel_lock(tc); @@ -1965,6 +2052,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast chan->hangupcause = tc->hangupcause; } ast_channel_unlock(chan); + ast_cc_call_failed(chan, tc, interface); ast_hangup(tc); tc = NULL; chanlist_free(tmp); @@ -2038,7 +2126,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast } } - peer = wait_for_answer(chan, outgoing, &to, peerflags, opt_args, &pa, &num, &result, dtmf_progress); + peer = wait_for_answer(chan, outgoing, &to, peerflags, opt_args, &pa, &num, &result, dtmf_progress, ignore_cc); /* The ast_channel_datastore_remove() function could fail here if the * datastore was moved to another channel during a masquerade. If this is @@ -2513,6 +2601,7 @@ done: if (config.start_sound) { ast_free((char *)config.start_sound); } + ast_ignore_cc(chan); return res; } diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c index 356106e4a..d9ae8a6f5 100644 --- a/channels/chan_dahdi.c +++ b/channels/chan_dahdi.c @@ -116,6 +116,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/event.h" #include "asterisk/devicestate.h" #include "asterisk/paths.h" +#include "asterisk/ccss.h" /*** DOCUMENTATION <application name="DAHDISendKeypadFacility" language="en_US"> @@ -608,6 +609,11 @@ struct dahdi_pri { static struct dahdi_pri pris[NUM_SPANS]; +#if defined(HAVE_PRI_CCSS) +/*! DAHDI PRI CCSS agent and monitor type name. */ +static const char dahdi_pri_cc_type[] = "DAHDI/PRI"; +#endif /* defined(HAVE_PRI_CCSS) */ + #else /*! Shut up the compiler */ struct dahdi_pri; @@ -1252,6 +1258,14 @@ struct dahdi_pvt { /*! \brief TRUE if confrence is muted. */ int muting; void *sig_pvt; + struct ast_cc_config_params *cc_params; + /* DAHDI channel names may differ greatly from the + * string that was provided to an app such as Dial. We + * need to save the original string passed to dahdi_request + * for call completion purposes. This way, we can replicate + * the original dialed string later. + */ + char dialstring[AST_CHANNEL_NAME]; }; static struct dahdi_pvt *iflist = NULL; /*!< Main interface list start */ @@ -1315,6 +1329,12 @@ static struct dahdi_chan_conf dahdi_chan_conf_default(void) .nodetype = PRI_CPE, .qsigchannelmapping = DAHDI_CHAN_MAPPING_PHYSICAL, +#if defined(HAVE_PRI_CCSS) + .cc_ptmp_recall_mode = 1,/* specificRecall */ + .cc_qsig_signaling_link_req = 1,/* retain */ + .cc_qsig_signaling_link_rsp = 1,/* retain */ +#endif /* defined(HAVE_PRI_CCSS) */ + .minunused = 2, .idleext = "", .idledial = "", @@ -1398,6 +1418,7 @@ static struct dahdi_chan_conf dahdi_chan_conf_default(void) .buf_policy = DAHDI_POLICY_IMMEDIATE, .buf_no = numbufs, .usefaxbuffers = 0, + .cc_params = ast_cc_config_params_init(), }, .timing = { .prewinktime = -1, @@ -1433,6 +1454,8 @@ static int dahdi_setoption(struct ast_channel *chan, int option, void *data, int static int dahdi_queryoption(struct ast_channel *chan, int option, void *data, int *datalen); static int dahdi_func_read(struct ast_channel *chan, const char *function, char *data, char *buf, size_t len); static int dahdi_func_write(struct ast_channel *chan, const char *function, char *data, const char *value); +static int dahdi_devicestate(void *data); +static int dahdi_cc_callback(struct ast_channel *inbound, const char *dest, ast_cc_callback_fn callback); static const struct ast_channel_tech dahdi_tech = { .type = "DAHDI", @@ -1455,6 +1478,8 @@ static const struct ast_channel_tech dahdi_tech = { .queryoption = dahdi_queryoption, .func_channel_read = dahdi_func_read, .func_channel_write = dahdi_func_write, + .devicestate = dahdi_devicestate, + .cc_callback = dahdi_cc_callback, }; #define GET_CHANNEL(p) ((p)->channel) @@ -2152,6 +2177,13 @@ static void my_set_pulsedial(void *pvt, int flag) p->pulsedial = flag; } +static const char *my_get_orig_dialstring(void *pvt) +{ + struct dahdi_pvt *p = pvt; + + return p->dialstring; +} + static void my_increase_ss_count(void) { ast_mutex_lock(&ss_thread_lock); @@ -2785,6 +2817,160 @@ static void my_pri_set_rdnis(void *pvt, const char *rdnis) ast_copy_string(p->rdnis, rdnis, sizeof(p->rdnis)); } +/*! + * \internal + * \brief Make a dialstring for native ISDN CC to recall properly. + * \since 1.8 + * + * \param priv Channel private control structure. + * \param buf Where to put the modified dialstring. + * \param buf_size Size of modified dialstring buffer. + * + * \details + * original dialstring: + * DAHDI/[i<span>-]<channel#>[c|r<cadance#>|d][/extension[/options]] + * DAHDI/[i<span>-](g|G|r|R)<group#(0-63)>[c|r<cadance#>|d][/extension[/options]] + * + * The modified dialstring will have prefixed the channel-group section + * with the ISDN channel restriction. + * + * buf: + * DAHDI/i<span>-<channel#>[c|r<cadance#>|d][/extension[/options]] + * DAHDI/i<span>-(g|G|r|R)<group#(0-63)>[c|r<cadance#>|d][/extension[/options]] + * + * The routine will check to see if the ISDN channel restriction is already + * in the original dialstring. + * + * \return Nothing + */ +static void my_pri_make_cc_dialstring(void *priv, char *buf, size_t buf_size) +{ + char *dial; + struct dahdi_pvt *pvt; + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(tech); /* channel technology token */ + AST_APP_ARG(group); /* channel/group token */ + //AST_APP_ARG(ext); /* extension token */ + //AST_APP_ARG(opts); /* options token */ + //AST_APP_ARG(other); /* Any remining unused arguments */ + ); + + pvt = priv; + dial = ast_strdupa(pvt->dialstring); + AST_NONSTANDARD_APP_ARGS(args, dial, '/'); + if (!args.tech) { + ast_copy_string(buf, pvt->dialstring, buf_size); + return; + } + if (!args.group) { + /* Append the ISDN span channel restriction to the dialstring. */ + snprintf(buf, buf_size, "%s/i%d-", args.tech, pvt->pri->span); + return; + } + if (args.group[0] == 'i') { + /* The ISDN span channel restriction is already in the dialstring. */ + ast_copy_string(buf, pvt->dialstring, buf_size); + return; + } + /* Insert the ISDN span channel restriction into the dialstring. */ + snprintf(buf, buf_size, "%s/i%d-%s", args.tech, pvt->pri->span, args.group); +} + +/*! + * \internal + * \brief Reevaluate the PRI span device state. + * \since 1.8 + * + * \param pri Asterisk D channel control structure. + * + * \return Nothing + * + * \note Assumes the pri->lock is already obtained. + */ +static void dahdi_pri_update_span_devstate(struct sig_pri_pri *pri) +{ + unsigned idx; + unsigned num_b_chans; /* Number of B channels provisioned on the span. */ + unsigned in_use; /* Number of B channels in use on the span. */ + unsigned in_alarm; /* TRUE if the span is in alarm condition. */ + enum ast_device_state new_state; + + /* Count the number of B channels and the number of B channels in use. */ + num_b_chans = 0; + in_use = 0; + in_alarm = 1; + for (idx = pri->numchans; idx--;) { + if (pri->pvts[idx] && !pri->pvts[idx]->no_b_channel) { + /* This is a B channel interface. */ + ++num_b_chans; + if (pri->pvts[idx]->owner +#if defined(HAVE_PRI_SERVICE_MESSAGES) + /* Out-of-service B channels are "in-use". */ + && pri->pvts[idx]->service_status +#endif /* defined(HAVE_PRI_SERVICE_MESSAGES) */ + ) { + ++in_use; + } + if (!pri->pvts[idx]->inalarm) { + /* There is a channel that is not in alarm. */ + in_alarm = 0; + } + } + } + + /* Update the span congestion device state and report any change. */ + if (in_alarm) { + new_state = AST_DEVICE_UNAVAILABLE; + } else { + new_state = num_b_chans == in_use ? AST_DEVICE_BUSY : AST_DEVICE_NOT_INUSE; + } + if (pri->congestion_devstate != new_state) { + pri->congestion_devstate = new_state; + ast_devstate_changed(AST_DEVICE_UNKNOWN, "DAHDI/I%d/congestion", pri->span); + } +#if defined(THRESHOLD_DEVSTATE_PLACEHOLDER) + /* Update the span threshold device state and report any change. */ + if (in_alarm) { + new_state = AST_DEVICE_UNAVAILABLE; + } else if (!in_use) { + new_state = AST_DEVICE_NOT_INUSE; + } else if (!pri->user_busy_threshold) { + new_state = in_use < num_b_chans ? AST_DEVICE_INUSE : AST_DEVICE_BUSY; + } else { + new_state = in_use < pri->user_busy_threshold ? AST_DEVICE_INUSE + : AST_DEVICE_BUSY; + } + if (pri->threshold_devstate != new_state) { + pri->threshold_devstate = new_state; + ast_devstate_changed(AST_DEVICE_UNKNOWN, "DAHDI/I%d/threshold", pri->span); + } +#endif /* defined(THRESHOLD_DEVSTATE_PLACEHOLDER) */ +} + +/*! + * \internal + * \brief Reference this module. + * \since 1.8 + * + * \return Nothing + */ +static void my_module_ref(void) +{ + ast_module_ref(ast_module_info->self); +} + +/*! + * \internal + * \brief Unreference this module. + * \since 1.8 + * + * \return Nothing + */ +static void my_module_unref(void) +{ + ast_module_unref(ast_module_info->self); +} + static int dahdi_new_pri_nobch_channel(struct sig_pri_pri *pri); static struct sig_pri_callback dahdi_pri_callbacks = @@ -2803,6 +2989,11 @@ static struct sig_pri_callback dahdi_pri_callbacks = .set_dnid = my_pri_set_dnid, .set_rdnis = my_pri_set_rdnis, .new_nobch_intf = dahdi_new_pri_nobch_channel, + .get_orig_dialstring = my_get_orig_dialstring, + .make_cc_dialstring = my_pri_make_cc_dialstring, + .update_span_devstate = dahdi_pri_update_span_devstate, + .module_ref = my_module_ref, + .module_unref = my_module_unref, }; #endif /* defined(HAVE_PRI) */ @@ -2932,6 +3123,7 @@ static struct analog_callback dahdi_analog_callbacks = .cancel_cidspill = my_cancel_cidspill, .confmute = my_confmute, .set_pulsedial = my_set_pulsedial, + .get_orig_dialstring = my_get_orig_dialstring, }; static struct dahdi_pvt *round_robin[32]; @@ -5122,6 +5314,9 @@ static void destroy_dahdi_pvt(struct dahdi_pvt *pvt) if (p->vars) { ast_variables_destroy(p->vars); } + if (p->cc_params) { + ast_cc_config_params_destroy(p->cc_params); + } ast_mutex_destroy(&p->lock); dahdi_close_sub(p, SUB_REAL); if (p->owner) @@ -5957,6 +6152,18 @@ static int dahdi_queryoption(struct ast_channel *chan, int option, void *data, i *cp = (p->callprogress & CALLPROGRESS_FAX) ? 0 : 1; ast_debug(1, "Reporting fax tone detection %sabled on %s\n", *cp ? "en" : "dis", chan->name); break; + case AST_OPTION_CC_AGENT_TYPE: +#if defined(HAVE_PRI) +#if defined(HAVE_PRI_CCSS) + if (dahdi_sig_pri_lib_handles(p->sig)) { + ast_copy_string((char *) data, dahdi_pri_cc_type, *datalen); + break; + } +#endif /* defined(HAVE_PRI_CCSS) */ +#endif /* defined(HAVE_PRI) */ + return -1; + default: + return -1; } errno = 0; @@ -8582,37 +8789,28 @@ static int dahdi_indicate(struct ast_channel *chan, int condition, const void *d return res; } -static struct ast_channel *dahdi_new(struct dahdi_pvt *i, int state, int startpbx, int idx, int law, int transfercapability, const char *linkedid) +#if defined(HAVE_PRI) +static struct ast_str *create_channel_name(struct dahdi_pvt *i, int is_outgoing, char *address) +#else +static struct ast_str *create_channel_name(struct dahdi_pvt *i) +#endif /* defined(HAVE_PRI) */ { - struct ast_channel *tmp; - format_t deflaw; - int res; - int x,y; - int features; struct ast_str *chan_name; - struct ast_variable *v; - struct dahdi_params ps; + int x, y; - if (i->subs[idx].owner) { - ast_log(LOG_WARNING, "Channel %d already has a %s call\n", i->channel,subnames[idx]); + /* Create the new channel name tail. */ + if (!(chan_name = ast_str_create(32))) { return NULL; } - - /* Create the new channel name tail. */ - chan_name = ast_str_alloca(32); if (i->channel == CHAN_PSEUDO) { ast_str_set(&chan_name, 0, "pseudo-%ld", ast_random()); #if defined(HAVE_PRI) } else if (i->pri) { ast_mutex_lock(&i->pri->lock); y = ++i->pri->new_chan_seq; - if (i->outgoing) { - /* - * The dnid has been stuffed with the called-number[:subaddress] - * by dahdi_request(). - */ - ast_str_set(&chan_name, 0, "i%d/%s-%x", i->pri->span, i->dnid, y); - i->dnid[0] = '\0'; + if (is_outgoing) { + ast_str_set(&chan_name, 0, "i%d/%s-%x", i->pri->span, address, y); + address[0] = '\0'; } else if (ast_strlen_zero(i->cid_subaddr)) { /* Put in caller-id number only since there is no subaddress. */ ast_str_set(&chan_name, 0, "i%d/%s-%x", i->pri->span, i->cid_num, y); @@ -8636,11 +8834,49 @@ static struct ast_channel *dahdi_new(struct dahdi_pvt *i, int state, int startpb ++y; } while (x < 3); } + return chan_name; +} + +static struct ast_channel *dahdi_new(struct dahdi_pvt *i, int state, int startpbx, int idx, int law, int transfercapability, const char *linkedid) +{ + struct ast_channel *tmp; + format_t deflaw; + int res; + int x; + int features; + struct ast_str *chan_name; + struct ast_variable *v; + struct dahdi_params ps; + + if (i->subs[idx].owner) { + ast_log(LOG_WARNING, "Channel %d already has a %s call\n", i->channel,subnames[idx]); + return NULL; + } + +#if defined(HAVE_PRI) + /* + * The dnid has been stuffed with the called-number[:subaddress] + * by dahdi_request() for outgoing calls. + */ + chan_name = create_channel_name(i, i->outgoing, i->dnid); +#else + chan_name = create_channel_name(i); +#endif /* defined(HAVE_PRI) */ + if (!chan_name) { + return NULL; + } tmp = ast_channel_alloc(0, state, i->cid_num, i->cid_name, i->accountcode, i->exten, i->context, linkedid, i->amaflags, "DAHDI/%s", ast_str_buffer(chan_name)); + ast_free(chan_name); if (!tmp) return NULL; tmp->tech = &dahdi_tech; +#if defined(HAVE_PRI) + if (i->pri) { + ast_cc_copy_config_params(i->cc_params, i->pri->cc_params); + } +#endif /* defined(HAVE_PRI) */ + ast_channel_cc_params_init(tmp, i->cc_params); memset(&ps, 0, sizeof(ps)); res = ioctl(i->subs[SUB_REAL].dfd, DAHDI_GET_PARAMS, &ps); if (res) { @@ -11169,6 +11405,11 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf, if (!tmp) { return NULL; } + tmp->cc_params = ast_cc_config_params_init(); + if (!tmp->cc_params) { + ast_free(tmp); + return NULL; + } ast_mutex_init(&tmp->lock); ifcount++; for (x = 0; x < 3; x++) @@ -11412,6 +11653,16 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf, tmp->sig_pvt = pchan; tmp->pri = &pris[span].pri; + if (!tmp->pri->cc_params) { + tmp->pri->cc_params = ast_cc_config_params_init(); + if (!tmp->pri->cc_params) { + destroy_dahdi_pvt(tmp); + return NULL; + } + } + ast_cc_copy_config_params(tmp->pri->cc_params, + conf->chan.cc_params); + pris[span].pri.sig = chan_sig; pris[span].pri.nodetype = conf->pri.pri.nodetype; pris[span].pri.switchtype = myswitchtype; @@ -11434,6 +11685,14 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf, pris[span].pri.hold_disconnect_transfer = conf->pri.pri.hold_disconnect_transfer; #endif /* defined(HAVE_PRI_CALL_HOLD) */ +#if defined(HAVE_PRI_CCSS) + pris[span].pri.cc_ptmp_recall_mode = + conf->pri.pri.cc_ptmp_recall_mode; + pris[span].pri.cc_qsig_signaling_link_req = + conf->pri.pri.cc_qsig_signaling_link_req; + pris[span].pri.cc_qsig_signaling_link_rsp = + conf->pri.pri.cc_qsig_signaling_link_rsp; +#endif /* defined(HAVE_PRI_CCSS) */ pris[span].pri.facilityenable = conf->pri.pri.facilityenable; ast_copy_string(pris[span].pri.msn_list, conf->pri.pri.msn_list, sizeof(pris[span].pri.msn_list)); ast_copy_string(pris[span].pri.idledial, conf->pri.pri.idledial, sizeof(pris[span].pri.idledial)); @@ -11742,6 +12001,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf, tmp->answeronpolarityswitch = conf->chan.answeronpolarityswitch; tmp->hanguponpolarityswitch = conf->chan.hanguponpolarityswitch; tmp->sendcalleridafter = conf->chan.sendcalleridafter; + ast_cc_copy_config_params(tmp->cc_params, conf->chan.cc_params); if (!here) { tmp->locallyblocked = tmp->remotelyblocked = 0; @@ -11881,21 +12141,36 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf, return tmp; } -static inline int available(struct dahdi_pvt *p, int channelmatch, ast_group_t groupmatch, int *channelmatched, int *groupmatched) +static int is_group_or_channel_match(struct dahdi_pvt *p, int span, ast_group_t groupmatch, int *groupmatched, int channelmatch, int *channelmatched) { - /* First, check group matching */ +#if defined(HAVE_PRI) + if (0 < span) { + /* The channel must be on the specified PRI span. */ + if (!p->pri || p->pri->span != span) { + return 0; + } + } +#endif /* defined(HAVE_PRI) */ + /* check group matching */ if (groupmatch) { if ((p->group & groupmatch) != groupmatch) + /* Doesn't match the specified group, try the next one */ return 0; *groupmatched = 1; } /* Check to see if we have a channel match */ if (channelmatch != -1) { if (p->channel != channelmatch) + /* Doesn't match the specified channel, try the next one */ return 0; *channelmatched = 1; } + return 1; +} + +static int available(struct dahdi_pvt *p) +{ if (p->inalarm) return 0; @@ -11988,6 +12263,11 @@ static int dahdi_new_pri_nobch_channel(struct sig_pri_pri *pri) if (!pvt) { return -1; } + pvt->cc_params = ast_cc_config_params_init(); + if (!pvt->cc_params) { + ast_free(pvt); + return -1; + } ast_mutex_init(&pvt->lock); for (idx = 0; idx < ARRAY_LEN(pvt->subs); ++idx) { pvt->subs[idx].dfd = -1; @@ -12089,24 +12369,31 @@ static struct dahdi_pvt *duplicate_pseudo(struct dahdi_pvt *src) return p; } -static struct ast_channel *dahdi_request(const char *type, format_t format, const struct ast_channel *requestor, void *data, int *cause) +struct dahdi_starting_point { + /*! Group matching mask. Zero if not specified. */ + ast_group_t groupmatch; + /*! DAHDI channel to match with. -1 if not specified. */ + int channelmatch; + /*! Round robin saved search location index. (Valid if roundrobin TRUE) */ + int rr_starting_point; + /*! ISDN span where channels can be picked (Zero if not specified) */ + int span; + /*! Analog channel distinctive ring cadance index. */ + int cadance; + /*! Dialing option. c/r/d if present and valid. */ + char opt; + /*! TRUE if to search the channel list backwards. */ + char backwards; + /*! TRUE if search is done with round robin sequence. */ + char roundrobin; +}; +static struct dahdi_pvt *determine_starting_point(const char *data, struct dahdi_starting_point *param) { - ast_group_t groupmatch = 0; - int channelmatch = -1; - int roundrobin = 0; - int callwait = 0; - struct dahdi_pvt *p; - struct ast_channel *tmp = NULL; char *dest; - int x; char *s; - char opt=0; - int res=0, y=0; - int backwards = 0; - struct dahdi_pvt *exitpvt; - int channelmatched = 0; - int groupmatched = 0; - int transcapdigital = 0; + int x; + int res = 0; + struct dahdi_pvt *p; AST_DECLARE_APP_ARGS(args, AST_APP_ARG(group); /* channel/group token */ //AST_APP_ARG(ext); /* extension token */ @@ -12117,8 +12404,11 @@ static struct ast_channel *dahdi_request(const char *type, format_t format, cons /* * data is ---v * Dial(DAHDI/pseudo[/extension[/options]]) - * Dial(DAHDI/<channel#>[c|r<cadance#>|d][/extension[/options]]) - * Dial(DAHDI/(g|G|r|R)<group#(0-63)>[c|r<cadance#>|d][/extension[/options]]) + * Dial(DAHDI/[i<span>-]<channel#>[c|r<cadance#>|d][/extension[/options]]) + * Dial(DAHDI/[i<span>-](g|G|r|R)<group#(0-63)>[c|r<cadance#>|d][/extension[/options]]) + * + * i - ISDN span channel restriction. + * Used by CC to ensure that the CC recall goes out the same span. * * g - channel group allocation search forward * G - channel group allocation search backward @@ -12131,7 +12421,7 @@ static struct ast_channel *dahdi_request(const char *type, format_t format, cons */ if (data) { - dest = ast_strdupa((char *)data); + dest = ast_strdupa(data); } else { ast_log(LOG_WARNING, "Channel requested with no data\n"); return NULL; @@ -12142,27 +12432,47 @@ static struct ast_channel *dahdi_request(const char *type, format_t format, cons return NULL; } + /* Initialize the output parameters */ + memset(param, 0, sizeof(*param)); + param->channelmatch = -1; + + if (args.group[0] == 'i') { + /* Extract the ISDN span channel restriction specifier. */ + res = sscanf(args.group + 1, "%30d", &x); + if (res < 1) { + ast_log(LOG_WARNING, "Unable to determine ISDN span for data %s\n", data); + return NULL; + } + param->span = x; + + /* Remove the ISDN span channel restriction specifier. */ + s = strchr(args.group, '-'); + if (!s) { + ast_log(LOG_WARNING, "Bad ISDN span format for data %s\n", data); + return NULL; + } + args.group = s + 1; + res = 0; + } if (toupper(args.group[0]) == 'G' || toupper(args.group[0])=='R') { /* Retrieve the group number */ s = args.group + 1; - if ((res = sscanf(s, "%30d%1c%30d", &x, &opt, &y)) < 1) { - ast_log(LOG_WARNING, "Unable to determine group for data %s\n", (char *)data); + res = sscanf(s, "%30d%1c%30d", &x, ¶m->opt, ¶m->cadance); + if (res < 1) { + ast_log(LOG_WARNING, "Unable to determine group for data %s\n", data); return NULL; } - groupmatch = ((ast_group_t) 1 << x); - - /* Lock the interface list */ - ast_mutex_lock(&iflock); + param->groupmatch = ((ast_group_t) 1 << x); if (toupper(args.group[0]) == 'G') { if (args.group[0] == 'G') { - backwards = 1; + param->backwards = 1; p = ifend; } else p = iflist; } else { if (args.group[0] == 'R') { - backwards = 1; + param->backwards = 1; p = round_robin[x]?round_robin[x]->prev:ifend; if (!p) p = ifend; @@ -12171,36 +12481,62 @@ static struct ast_channel *dahdi_request(const char *type, format_t format, cons if (!p) p = iflist; } - roundrobin = 1; + param->roundrobin = 1; + param->rr_starting_point = x; } } else { s = args.group; if (!strcasecmp(s, "pseudo")) { /* Special case for pseudo */ x = CHAN_PSEUDO; - channelmatch = x; - } else if ((res = sscanf(s, "%30d%1c%30d", &x, &opt, &y)) < 1) { - ast_log(LOG_WARNING, "Unable to determine channel for data %s\n", (char *)data); - return NULL; + param->channelmatch = x; } else { - channelmatch = x; + res = sscanf(s, "%30d%1c%30d", &x, ¶m->opt, ¶m->cadance); + if (res < 1) { + ast_log(LOG_WARNING, "Unable to determine channel for data %s\n", data); + return NULL; + } else { + param->channelmatch = x; + } } - /* Lock the interface list */ - ast_mutex_lock(&iflock); - p = iflist; } + + if (param->opt == 'r' && res < 3) { + ast_log(LOG_WARNING, "Distinctive ring missing identifier in '%s'\n", data); + param->opt = '\0'; + } + + return p; +} + +static struct ast_channel *dahdi_request(const char *type, format_t format, const struct ast_channel *requestor, void *data, int *cause) +{ + int callwait = 0; + struct dahdi_pvt *p; + struct ast_channel *tmp = NULL; + struct dahdi_pvt *exitpvt; + int channelmatched = 0; + int groupmatched = 0; + int transcapdigital = 0; + struct dahdi_starting_point start; + + p = determine_starting_point(data, &start); + if (!p) { + /* We couldn't determine a starting point, which likely means badly-formatted channel name. Abort! */ + return NULL; + } + /* Search for an unowned channel */ exitpvt = p; + ast_mutex_lock(&iflock); while (p && !tmp) { - if (roundrobin) - round_robin[x] = p; -#if 0 - ast_verbose("name = %s, %d, %d, %llu\n",p->owner ? p->owner->name : "<none>", p->channel, channelmatch, groupmatch); -#endif + if (start.roundrobin) + round_robin[start.rr_starting_point] = p; - if (p && available(p, channelmatch, groupmatch, &channelmatched, &groupmatched)) { + if (is_group_or_channel_match(p, start.span, start.groupmatch, &groupmatched, start.channelmatch, &channelmatched) + && available(p)) { ast_debug(1, "Using channel %d\n", p->channel); callwait = (p->owner != NULL); @@ -12224,22 +12560,25 @@ static struct ast_channel *dahdi_request(const char *type, format_t format, cons } /* Make special notes */ - if (res > 1) { - if (opt == 'c') { - /* Confirm answer */ - p->confirmanswer = 1; - } else if (opt == 'r') { - /* Distinctive ring */ - if (res < 3) - ast_log(LOG_WARNING, "Distinctive ring missing identifier in '%s'\n", (char *)data); - else - p->distinctivering = y; - } else if (opt == 'd') { - /* If this is an ISDN call, make it digital */ - transcapdigital = AST_TRANS_CAP_DIGITAL; - } else { - ast_log(LOG_WARNING, "Unknown option '%c' in '%s'\n", opt, (char *)data); - } + switch (start.opt) { + case '\0': + /* No option present. */ + break; + case 'c': + /* Confirm answer */ + p->confirmanswer = 1; + break; + case 'r': + /* Distinctive ring */ + p->distinctivering = start.cadance; + break; + case 'd': + /* If this is an ISDN call, make it digital */ + transcapdigital = AST_TRANS_CAP_DIGITAL; + break; + default: + ast_log(LOG_WARNING, "Unknown option '%c' in '%s'\n", start.opt, (char *)data); + break; } p->outgoing = 1; @@ -12256,13 +12595,15 @@ static struct ast_channel *dahdi_request(const char *type, format_t format, cons } if (!tmp) { p->outgoing = 0; + } else { + snprintf(p->dialstring, sizeof(p->dialstring), "DAHDI/%s", (char *) data); } break; } #ifdef HAVE_OPENR2 next: #endif - if (backwards) { + if (start.backwards) { p = p->prev; if (!p) p = ifend; @@ -12293,6 +12634,167 @@ next: return tmp; } +/*! + * \internal + * \brief Determine the device state for a given DAHDI device if we can. + * \since 1.8 + * + * \param data DAHDI device name after "DAHDI/". + * + * \retval device_state enum ast_device_state value. + * \retval AST_DEVICE_UNKNOWN if we could not determine the device's state. + */ +static int dahdi_devicestate(void *data) +{ +#if defined(HAVE_PRI) + char *device; + unsigned span; + int res; + + device = data; + + if (*device != 'I') { + /* The request is not for an ISDN span device. */ + return AST_DEVICE_UNKNOWN; + } + res = sscanf(device, "I%30u", &span); + if (res != 1 || !span || NUM_SPANS < span) { + /* Bad format for ISDN span device name. */ + return AST_DEVICE_UNKNOWN; + } + device = strchr(device, '/'); + if (!device) { + /* Bad format for ISDN span device name. */ + return AST_DEVICE_UNKNOWN; + } + + /* + * Since there are currently no other span devstate's defined, + * it must be congestion. + */ +#if defined(THRESHOLD_DEVSTATE_PLACEHOLDER) + ++device; + if (!strcmp(device, "congestion")) +#endif /* defined(THRESHOLD_DEVSTATE_PLACEHOLDER) */ + { + return pris[span - 1].pri.congestion_devstate; + } +#if defined(THRESHOLD_DEVSTATE_PLACEHOLDER) + else if (!strcmp(device, "threshold")) { + return pris[span - 1].pri.threshold_devstate; + } + return AST_DEVICE_UNKNOWN; +#endif /* defined(THRESHOLD_DEVSTATE_PLACEHOLDER) */ +#else + return AST_DEVICE_UNKNOWN; +#endif /* defined(HAVE_PRI) */ +} + +/*! + * \brief Callback made when dial failed to get a channel out of dahdi_request(). + * \since 1.8 + * + * \param inbound Incoming asterisk channel. + * \param dest Same dial string passed to dahdi_request(). + * \param callback Callback into CC core to announce a busy channel available for CC. + * + * \details + * This callback acts like a forked dial with all prongs of the fork busy. + * Essentially, for each channel that could have taken the call, indicate that + * it is busy. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int dahdi_cc_callback(struct ast_channel *inbound, const char *dest, ast_cc_callback_fn callback) +{ + struct dahdi_pvt *p; + struct dahdi_pvt *exitpvt; + struct dahdi_starting_point start; + int groupmatched = 0; + int channelmatched = 0; + + p = determine_starting_point(dest, &start); + if (!p) { + return -1; + } + ast_mutex_lock(&iflock); + exitpvt = p; + for (;;) { + if (is_group_or_channel_match(p, start.span, start.groupmatch, &groupmatched, start.channelmatch, &channelmatched)) { + /* We found a potential match. call the callback */ + struct ast_str *device_name; + char *dash; + const char *monitor_type; + char dialstring[AST_CHANNEL_NAME]; + char full_device_name[AST_CHANNEL_NAME]; + + switch (ast_get_cc_monitor_policy(p->cc_params)) { + case AST_CC_MONITOR_NEVER: + break; + case AST_CC_MONITOR_NATIVE: + case AST_CC_MONITOR_ALWAYS: + case AST_CC_MONITOR_GENERIC: +#if defined(HAVE_PRI) + if (dahdi_sig_pri_lib_handles(p->sig)) { + /* + * ISDN is in a trunk busy condition so we need to monitor + * the span congestion device state. + */ + snprintf(full_device_name, sizeof(full_device_name), + "DAHDI/I%d/congestion", p->pri->span); + } else +#endif /* defined(HAVE_PRI) */ + { +#if defined(HAVE_PRI) + device_name = create_channel_name(p, 1, ""); +#else + device_name = create_channel_name(p); +#endif /* defined(HAVE_PRI) */ + snprintf(full_device_name, sizeof(full_device_name), "DAHDI/%s", + device_name ? ast_str_buffer(device_name) : ""); + ast_free(device_name); + /* + * The portion after the '-' in the channel name is either a random + * number, a sequence number, or a subchannel number. None are + * necessary so strip them off. + */ + dash = strrchr(full_device_name, '-'); + if (dash) { + *dash = '\0'; + } + } + snprintf(dialstring, sizeof(dialstring), "DAHDI/%s", dest); + + /* + * Analog can only do generic monitoring. + * ISDN is in a trunk busy condition and any "device" is going + * to be busy until a B channel becomes available. The generic + * monitor can do this task. + */ + monitor_type = AST_CC_GENERIC_MONITOR_TYPE; + callback(inbound, +#if defined(HAVE_PRI) + p->pri ? p->pri->cc_params : p->cc_params, +#else + p->cc_params, +#endif /* defined(HAVE_PRI) */ + monitor_type, full_device_name, dialstring, NULL); + break; + } + } + p = start.backwards ? p->prev : p->next; + if (!p) { + p = start.backwards ? ifend : iflist; + } + if (p == exitpvt) { + break; + } + } + ast_mutex_unlock(&iflock); + return 0; +} + #if defined(HAVE_SS7) static int ss7_find_cic(struct dahdi_ss7 *linkset, int cic, unsigned int dpc) { @@ -13480,9 +13982,7 @@ static char *handle_pri_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_a for (x = 0; x < NUM_DCHANS; x++) { if (pris[span-1].pri.dchans[x]) { if (level == 1) { - pri_set_debug(pris[span-1].pri.dchans[x], PRI_DEBUG_APDU | - PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q931_STATE | - PRI_DEBUG_Q921_STATE); + pri_set_debug(pris[span-1].pri.dchans[x], SIG_PRI_DEBUG_NORMAL); ast_cli(a->fd, "Enabled debugging on span %d\n", span); } else if (level == 0) { pri_set_debug(pris[span-1].pri.dchans[x], 0); @@ -13493,9 +13993,7 @@ static char *handle_pri_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_a ast_cli(a->fd, "PRI debug output to file disabled\n"); ast_mutex_unlock(&pridebugfdlock); } else { - pri_set_debug(pris[span-1].pri.dchans[x], PRI_DEBUG_APDU | - PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q931_STATE | - PRI_DEBUG_Q921_RAW | PRI_DEBUG_Q921_DUMP | PRI_DEBUG_Q921_STATE); + pri_set_debug(pris[span-1].pri.dchans[x], SIG_PRI_DEBUG_INTENSE); ast_cli(a->fd, "Enabled debugging on span %d\n", span); } } @@ -13583,6 +14081,8 @@ static char *handle_pri_service_generic(struct ast_cli_entry *e, int cmd, struct if (*why) { snprintf(db_answer, sizeof(db_answer), "%s:%u", SRVST_TYPE_OOS, *why); ast_db_put(db_chan_name, SRVST_DBKEY, db_answer); + } else { + dahdi_pri_update_span_devstate(tmp->pri); } break; /* case 1: -- loop */ @@ -13592,6 +14092,7 @@ static char *handle_pri_service_generic(struct ast_cli_entry *e, int cmd, struct *why |= SRVST_NEAREND; snprintf(db_answer, sizeof(db_answer), "%s:%u", SRVST_TYPE_OOS, *why); ast_db_put(db_chan_name, SRVST_DBKEY, db_answer); + dahdi_pri_update_span_devstate(tmp->pri); break; /* case 3: -- continuity */ /* case 4: -- shutdown */ @@ -15612,6 +16113,110 @@ static struct ast_cli_entry dahdi_ss7_cli[] = { }; #endif /* defined(HAVE_SS7) */ +#if defined(HAVE_PRI) +#if defined(HAVE_PRI_CCSS) +/*! + * \internal + * \brief CC agent initialization. + * \since 1.8 + * + * \param agent CC core agent control. + * \param chan Original channel the agent will attempt to recall. + * + * \details + * This callback is called when the CC core is initialized. Agents should allocate + * any private data necessary for the call and assign it to the private_data + * on the agent. Additionally, if any ast_cc_agent_flags are pertinent to the + * specific agent type, they should be set in this function as well. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int dahdi_pri_cc_agent_init(struct ast_cc_agent *agent, struct ast_channel *chan) +{ + struct dahdi_pvt *pvt; + struct sig_pri_chan *pvt_chan; + int res; + + ast_assert(!strcmp(chan->tech->type, "DAHDI")); + + pvt = chan->tech_pvt; + if (dahdi_sig_pri_lib_handles(pvt->sig)) { + pvt_chan = pvt->sig_pvt; + } else { + pvt_chan = NULL; + } + if (!pvt_chan) { + return -1; + } + + ast_module_ref(ast_module_info->self); + + res = sig_pri_cc_agent_init(agent, pvt_chan); + if (res) { + ast_module_unref(ast_module_info->self); + } + return res; +} +#endif /* defined(HAVE_PRI_CCSS) */ +#endif /* defined(HAVE_PRI) */ + +#if defined(HAVE_PRI) +#if defined(HAVE_PRI_CCSS) +/*! + * \internal + * \brief Destroy private data on the agent. + * \since 1.8 + * + * \param agent CC core agent control. + * + * \details + * The core will call this function upon completion + * or failure of CC. + * + * \return Nothing + */ +static void dahdi_pri_cc_agent_destructor(struct ast_cc_agent *agent) +{ + sig_pri_cc_agent_destructor(agent); + + ast_module_unref(ast_module_info->self); +} +#endif /* defined(HAVE_PRI_CCSS) */ +#endif /* defined(HAVE_PRI) */ + +#if defined(HAVE_PRI) +#if defined(HAVE_PRI_CCSS) +static struct ast_cc_agent_callbacks dahdi_pri_cc_agent_callbacks = { + .type = dahdi_pri_cc_type, + .init = dahdi_pri_cc_agent_init, + .start_offer_timer = sig_pri_cc_agent_start_offer_timer, + .stop_offer_timer = sig_pri_cc_agent_stop_offer_timer, + .ack = sig_pri_cc_agent_req_ack, + .status_request = sig_pri_cc_agent_status_req, + .stop_ringing = sig_pri_cc_agent_stop_ringing, + .party_b_free = sig_pri_cc_agent_party_b_free, + .start_monitoring = sig_pri_cc_agent_start_monitoring, + .callee_available = sig_pri_cc_agent_callee_available, + .destructor = dahdi_pri_cc_agent_destructor, +}; +#endif /* defined(HAVE_PRI_CCSS) */ +#endif /* defined(HAVE_PRI) */ + +#if defined(HAVE_PRI) +#if defined(HAVE_PRI_CCSS) +static struct ast_cc_monitor_callbacks dahdi_pri_cc_monitor_callbacks = { + .type = dahdi_pri_cc_type, + .request_cc = sig_pri_cc_monitor_req_cc, + .suspend = sig_pri_cc_monitor_suspend, + .unsuspend = sig_pri_cc_monitor_unsuspend, + .status_response = sig_pri_cc_monitor_status_rsp, + .cancel_available_timer = sig_pri_cc_monitor_cancel_available_timer, + .destructor = sig_pri_cc_monitor_destructor, +}; +#endif /* defined(HAVE_PRI_CCSS) */ +#endif /* defined(HAVE_PRI) */ + static int __unload_module(void) { struct dahdi_pvt *p; @@ -15680,6 +16285,11 @@ static int __unload_module(void) dahdi_close_pri_fd(&(pris[i]), j); } } +#if defined(HAVE_PRI_CCSS) + ast_cc_agent_unregister(&dahdi_pri_cc_agent_callbacks); + ast_cc_monitor_unregister(&dahdi_pri_cc_monitor_callbacks); +#endif /* defined(HAVE_PRI_CCSS) */ + sig_pri_unload(); #endif #if defined(HAVE_SS7) @@ -16100,6 +16710,8 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct confp->chan.sendcalleridafter = atoi(v->value); } else if (!strcasecmp(v->name, "mwimonitornotify")) { ast_copy_string(mwimonitornotify, v->value, sizeof(mwimonitornotify)); + } else if (ast_cc_is_config_param(v->name)) { + ast_cc_set_param(confp->chan.cc_params, v->name, v->value); } else if (!strcasecmp(v->name, "mwisendtype")) { #ifndef HAVE_DAHDI_LINEREVERSE_VMWI /* backward compatibility for older dahdi VMWI implementation */ if (!strcasecmp(v->value, "rpas")) { /* Ring Pulse Alert Signal */ @@ -16478,6 +17090,34 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct } else if (!strcasecmp(v->name, "hold_disconnect_transfer")) { confp->pri.pri.hold_disconnect_transfer = ast_true(v->value); #endif /* defined(HAVE_PRI_CALL_HOLD) */ +#if defined(HAVE_PRI_CCSS) + } else if (!strcasecmp(v->name, "cc_ptmp_recall_mode")) { + if (!strcasecmp(v->value, "global")) { + confp->pri.pri.cc_ptmp_recall_mode = 0;/* globalRecall */ + } else if (!strcasecmp(v->value, "specific")) { + confp->pri.pri.cc_ptmp_recall_mode = 1;/* specificRecall */ + } else { + confp->pri.pri.cc_ptmp_recall_mode = 1;/* specificRecall */ + } + } else if (!strcasecmp(v->name, "cc_qsig_signaling_link_req")) { + if (!strcasecmp(v->value, "release")) { + confp->pri.pri.cc_qsig_signaling_link_req = 0;/* release */ + } else if (!strcasecmp(v->value, "retain")) { + confp->pri.pri.cc_qsig_signaling_link_req = 1;/* retain */ + } else if (!strcasecmp(v->value, "do_not_care")) { + confp->pri.pri.cc_qsig_signaling_link_req = 2;/* do-not-care */ + } else { + confp->pri.pri.cc_qsig_signaling_link_req = 1;/* retain */ + } + } else if (!strcasecmp(v->name, "cc_qsig_signaling_link_rsp")) { + if (!strcasecmp(v->value, "release")) { + confp->pri.pri.cc_qsig_signaling_link_rsp = 0;/* release */ + } else if (!strcasecmp(v->value, "retain")) { + confp->pri.pri.cc_qsig_signaling_link_rsp = 1;/* retain */ + } else { + confp->pri.pri.cc_qsig_signaling_link_rsp = 1;/* retain */ + } +#endif /* defined(HAVE_PRI_CCSS) */ #endif /* HAVE_PRI */ #ifdef HAVE_SS7 } else if (!strcasecmp(v->name, "ss7type")) { @@ -16800,23 +17440,57 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct */ struct dahdi_chan_conf conf = dahdi_chan_conf_default(); - tmp = mkintf(CHAN_PSEUDO, &conf, reload); - + if (conf.chan.cc_params) { + tmp = mkintf(CHAN_PSEUDO, &conf, reload); + } else { + tmp = NULL; + } if (tmp) { ast_verb(3, "Automatically generated pseudo channel\n"); } else { ast_log(LOG_WARNING, "Unable to register pseudo channel!\n"); } + ast_cc_config_params_destroy(conf.chan.cc_params); } return 0; } -static int setup_dahdi(int reload) +/*! + * \internal + * \brief Deep copy struct dahdi_chan_conf. + * \since 1.8 + * + * \param dest Destination. + * \param src Source. + * + * \return Nothing + */ +static void deep_copy_dahdi_chan_conf(struct dahdi_chan_conf *dest, const struct dahdi_chan_conf *src) +{ + struct ast_cc_config_params *cc_params; + + cc_params = dest->chan.cc_params; + memcpy(dest, src, sizeof(dest)); + dest->chan.cc_params = cc_params; + ast_cc_copy_config_params(dest->chan.cc_params, src->chan.cc_params); +} + +/*! + * \internal + * \brief Setup DAHDI channel driver. + * + * \param reload enum: load_module(0), reload(1), restart(2). + * \param base_conf Default config parameters. So cc_params can be properly destroyed. + * \param conf Local config parameters. So cc_params can be properly destroyed. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int setup_dahdi_int(int reload, struct dahdi_chan_conf *base_conf, struct dahdi_chan_conf *conf) { - struct ast_config *cfg, *ucfg; + struct ast_config *cfg; + struct ast_config *ucfg; struct ast_variable *v; - struct dahdi_chan_conf base_conf = dahdi_chan_conf_default(); - struct dahdi_chan_conf conf; struct ast_flags config_flags = { reload == 1 ? CONFIG_FLAG_FILEUNCHANGED : 0 }; const char *cat; int res; @@ -16931,7 +17605,7 @@ static int setup_dahdi(int reload) mwimonitornotify[0] = '\0'; v = ast_variable_browse(cfg, "channels"); - if ((res = process_dahdi(&base_conf, "", v, reload, 0))) { + if ((res = process_dahdi(base_conf, "", v, reload, 0))) { ast_mutex_unlock(&iflock); ast_config_destroy(cfg); if (ucfg) { @@ -16952,9 +17626,10 @@ static int setup_dahdi(int reload) continue; } - memcpy(&conf, &base_conf, sizeof(conf)); + /* Copy base_conf to conf. */ + deep_copy_dahdi_chan_conf(conf, base_conf); - if ((res = process_dahdi(&conf, cat, ast_variable_browse(cfg, cat), reload, PROC_DAHDI_OPT_NOCHAN))) { + if ((res = process_dahdi(conf, cat, ast_variable_browse(cfg, cat), reload, PROC_DAHDI_OPT_NOCHAN))) { ast_mutex_unlock(&iflock); ast_config_destroy(cfg); if (ucfg) { @@ -16969,7 +17644,7 @@ static int setup_dahdi(int reload) if (ucfg) { const char *chans; - process_dahdi(&base_conf, "", ast_variable_browse(ucfg, "general"), 1, 0); + process_dahdi(base_conf, "", ast_variable_browse(ucfg, "general"), 1, 0); for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) { if (!strcasecmp(cat, "general")) { @@ -16982,9 +17657,10 @@ static int setup_dahdi(int reload) continue; } - memcpy(&conf, &base_conf, sizeof(conf)); + /* Copy base_conf to conf. */ + deep_copy_dahdi_chan_conf(conf, base_conf); - if ((res = process_dahdi(&conf, cat, ast_variable_browse(ucfg, cat), reload, PROC_DAHDI_OPT_NOCHAN | PROC_DAHDI_OPT_NOWARN))) { + if ((res = process_dahdi(conf, cat, ast_variable_browse(ucfg, cat), reload, PROC_DAHDI_OPT_NOCHAN | PROC_DAHDI_OPT_NOWARN))) { ast_config_destroy(ucfg); ast_mutex_unlock(&iflock); return res; @@ -17041,6 +17717,32 @@ static int setup_dahdi(int reload) return 0; } +/*! + * \internal + * \brief Setup DAHDI channel driver. + * + * \param reload enum: load_module(0), reload(1), restart(2). + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int setup_dahdi(int reload) +{ + int res; + struct dahdi_chan_conf base_conf = dahdi_chan_conf_default(); + struct dahdi_chan_conf conf = dahdi_chan_conf_default(); + + if (base_conf.chan.cc_params && conf.chan.cc_params) { + res = setup_dahdi_int(reload, &base_conf, &conf); + } else { + res = -1; + } + ast_cc_config_params_destroy(base_conf.chan.cc_params); + ast_cc_config_params_destroy(conf.chan.cc_params); + + return res; +} + static int load_module(void) { int res; @@ -17061,6 +17763,23 @@ static int load_module(void) #ifdef HAVE_PRI_PROG_W_CAUSE ast_register_application_xml(dahdi_send_callrerouting_facility_app, dahdi_send_callrerouting_facility_exec); #endif +#if defined(HAVE_PRI_CCSS) + if (ast_cc_agent_register(&dahdi_pri_cc_agent_callbacks) + || ast_cc_monitor_register(&dahdi_pri_cc_monitor_callbacks)) { + __unload_module(); + return AST_MODULE_LOAD_FAILURE; + } +#endif /* defined(HAVE_PRI_CCSS) */ + if (sig_pri_load( +#if defined(HAVE_PRI_CCSS) + dahdi_pri_cc_type +#else + NULL +#endif /* defined(HAVE_PRI_CCSS) */ + )) { + __unload_module(); + return AST_MODULE_LOAD_FAILURE; + } #endif #ifdef HAVE_SS7 memset(linksets, 0, sizeof(linksets)); diff --git a/channels/chan_local.c b/channels/chan_local.c index 5e522e797..b8052b0fd 100644 --- a/channels/chan_local.c +++ b/channels/chan_local.c @@ -545,6 +545,8 @@ static int local_call(struct ast_channel *ast, char *dest, int timeout) int res; struct ast_var_t *varptr = NULL, *new; size_t len, namelen; + char *reduced_dest = ast_strdupa(dest); + char *slash; if (!p) return -1; @@ -594,6 +596,8 @@ start_over: ast_string_field_set(p->chan, musicclass, p->owner->musicclass); ast_cdr_update(p->chan); + ast_channel_cc_params_init(p->chan, ast_channel_get_cc_config_params(p->owner)); + if (!ast_exists_extension(NULL, p->chan->context, p->chan->exten, 1, p->owner->cid.cid_num)) { ast_log(LOG_NOTICE, "No such extension/context %s@%s while calling Local channel\n", p->chan->exten, p->chan->context); ast_mutex_unlock(&p->lock); @@ -618,6 +622,14 @@ start_over: } } ast_channel_datastore_inherit(p->owner, p->chan); + /* If the local channel has /n or /b on the end of it, + * we need to lop that off for our argument to setting + * up the CC_INTERFACES variable + */ + if ((slash = strrchr(reduced_dest, '/'))) { + *slash = '\0'; + } + ast_set_cc_interfaces_chanvar(p->chan, reduced_dest); /* Start switch on sub channel */ if (!(res = ast_pbx_start(p->chan))) @@ -857,6 +869,10 @@ static struct ast_channel *local_request(const char *type, format_t format, cons AST_LIST_UNLOCK(&locals); p = local_pvt_destroy(p); } + if (ast_channel_cc_params_init(chan, ast_channel_get_cc_config_params((struct ast_channel *)requestor))) { + chan = ast_channel_release(chan); + p = local_pvt_destroy(p); + } } return chan; diff --git a/channels/chan_sip.c b/channels/chan_sip.c index bd6cb1889..91773b05c 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -266,6 +266,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "sip/include/config_parser.h" #include "sip/include/reqresp_parser.h" #include "sip/include/sip_utils.h" +#include "asterisk/ccss.h" +#include "asterisk/xml.h" #include "sip/include/dialog.h" #include "sip/include/dialplan_functions.h" @@ -625,7 +627,7 @@ static const struct cfsip_methods { { SIP_UPDATE, NO_RTP, "UPDATE", CAN_NOT_CREATE_DIALOG }, { SIP_INFO, NO_RTP, "INFO", CAN_NOT_CREATE_DIALOG }, { SIP_CANCEL, NO_RTP, "CANCEL", CAN_NOT_CREATE_DIALOG }, - { SIP_PUBLISH, NO_RTP, "PUBLISH", CAN_CREATE_DIALOG_UNSUPPORTED_METHOD }, + { SIP_PUBLISH, NO_RTP, "PUBLISH", CAN_CREATE_DIALOG }, { SIP_PING, NO_RTP, "PING", CAN_CREATE_DIALOG_UNSUPPORTED_METHOD } }; @@ -784,6 +786,14 @@ static int global_max_se; /*!< Highest threshold for session static int global_dynamic_exclude_static = 0; /*!< Exclude static peers from contact registrations */ /*@}*/ +/*! + * We use libxml2 in order to parse XML that may appear in the body of a SIP message. Currently, + * the only usage is for parsing PIDF bodies of incoming PUBLISH requests in the call-completion + * event package. This variable is set at module load time and may be checked at runtime to determine + * if XML parsing support was found. + */ +static int can_parse_xml; + /*! \name Object counters @{ * \bug These counters are not handled in a thread-safe way ast_atomic_fetchadd_int() * should be used to modify these values. */ @@ -851,6 +861,251 @@ static const int HASH_PEER_SIZE = 563; /*!< Size of peer hash table, prime numbe static const int HASH_DIALOG_SIZE = 563; #endif +static const struct { + enum ast_cc_service_type service; + const char *service_string; +} sip_cc_service_map [] = { + [AST_CC_NONE] = { AST_CC_NONE, "" }, + [AST_CC_CCBS] = { AST_CC_CCBS, "BS" }, + [AST_CC_CCNR] = { AST_CC_CCNR, "NR" }, + [AST_CC_CCNL] = { AST_CC_CCNL, "NL" }, +}; + +static enum ast_cc_service_type service_string_to_service_type(const char * const service_string) +{ + enum ast_cc_service_type service; + for (service = AST_CC_CCBS; service <= AST_CC_CCNL; ++service) { + if (!strcasecmp(service_string, sip_cc_service_map[service].service_string)) { + return service; + } + } + return AST_CC_NONE; +} + +static const struct { + enum sip_cc_notify_state state; + const char *state_string; +} sip_cc_notify_state_map [] = { + [CC_QUEUED] = {CC_QUEUED, "cc-state: queued"}, + [CC_READY] = {CC_READY, "cc-state: ready"}, +}; + +AST_LIST_HEAD_STATIC(epa_static_data_list, epa_backend); + +static int sip_epa_register(const struct epa_static_data *static_data) +{ + struct epa_backend *backend = ast_calloc(1, sizeof(*backend)); + + if (!backend) { + return -1; + } + + backend->static_data = static_data; + + AST_LIST_LOCK(&epa_static_data_list); + AST_LIST_INSERT_TAIL(&epa_static_data_list, backend, next); + AST_LIST_UNLOCK(&epa_static_data_list); + return 0; +} + +static void cc_handle_publish_error(struct sip_pvt *pvt, const int resp, struct sip_request *req, struct sip_epa_entry *epa_entry); + +static void cc_epa_destructor(void *data) +{ + struct sip_epa_entry *epa_entry = data; + struct cc_epa_entry *cc_entry = epa_entry->instance_data; + ast_free(cc_entry); +} + +static const struct epa_static_data cc_epa_static_data = { + .event = CALL_COMPLETION, + .name = "call-completion", + .handle_error = cc_handle_publish_error, + .destructor = cc_epa_destructor, +}; + +static const struct epa_static_data *find_static_data(const char * const event_package) +{ + const struct epa_backend *backend = NULL; + + AST_LIST_LOCK(&epa_static_data_list); + AST_LIST_TRAVERSE(&epa_static_data_list, backend, next) { + if (!strcmp(backend->static_data->name, event_package)) { + break; + } + } + AST_LIST_UNLOCK(&epa_static_data_list); + return backend ? backend->static_data : NULL; +} + +static struct sip_epa_entry *create_epa_entry (const char * const event_package, const char * const destination) +{ + struct sip_epa_entry *epa_entry; + const struct epa_static_data *static_data; + + if (!(static_data = find_static_data(event_package))) { + return NULL; + } + + if (!(epa_entry = ao2_t_alloc(sizeof(*epa_entry), static_data->destructor, "Allocate new EPA entry"))) { + return NULL; + } + + epa_entry->static_data = static_data; + ast_copy_string(epa_entry->destination, destination, sizeof(epa_entry->destination)); + return epa_entry; +} + +/*! + * Used to create new entity IDs by ESCs. + */ +static int esc_etag_counter; +static const int DEFAULT_PUBLISH_EXPIRES = 3600; + +#ifdef HAVE_LIBXML2 +static int cc_esc_publish_handler(struct sip_pvt *pvt, struct sip_request *req, struct event_state_compositor *esc, struct sip_esc_entry *esc_entry); + +static const struct sip_esc_publish_callbacks cc_esc_publish_callbacks = { + .initial_handler = cc_esc_publish_handler, + .modify_handler = cc_esc_publish_handler, +}; +#endif + +/*! + * \brief The Event State Compositors + * + * An Event State Compositor is an entity which + * accepts PUBLISH requests and acts appropriately + * based on these requests. + * + * The actual event_state_compositor structure is simply + * an ao2_container of sip_esc_entrys. When an incoming + * PUBLISH is received, we can match the appropriate sip_esc_entry + * using the entity ID of the incoming PUBLISH. + */ +static struct event_state_compositor { + enum subscriptiontype event; + const char * name; + const struct sip_esc_publish_callbacks *callbacks; + struct ao2_container *compositor; +} event_state_compositors [] = { +#ifdef HAVE_LIBXML2 + {CALL_COMPLETION, "call-completion", &cc_esc_publish_callbacks}, +#endif +}; + +static const int ESC_MAX_BUCKETS = 37; + +static void esc_entry_destructor(void *obj) +{ + struct sip_esc_entry *esc_entry = obj; + if (esc_entry->sched_id > -1) { + AST_SCHED_DEL(sched, esc_entry->sched_id); + } +} + +static int esc_hash_fn(const void *obj, const int flags) +{ + const struct sip_esc_entry *entry = obj; + return ast_str_hash(entry->entity_tag); +} + +static int esc_cmp_fn(void *obj, void *arg, int flags) +{ + struct sip_esc_entry *entry1 = obj; + struct sip_esc_entry *entry2 = arg; + + return (!strcmp(entry1->entity_tag, entry2->entity_tag)) ? (CMP_MATCH | CMP_STOP) : 0; +} + +static struct event_state_compositor *get_esc(const char * const event_package) { + int i; + for (i = 0; i < ARRAY_LEN(event_state_compositors); i++) { + if (!strcasecmp(event_package, event_state_compositors[i].name)) { + return &event_state_compositors[i]; + } + } + return NULL; +} + +static struct sip_esc_entry *get_esc_entry(const char * entity_tag, struct event_state_compositor *esc) { + struct sip_esc_entry *entry; + struct sip_esc_entry finder; + + ast_copy_string(finder.entity_tag, entity_tag, sizeof(finder.entity_tag)); + + entry = ao2_find(esc->compositor, &finder, OBJ_POINTER); + + return entry; +} + +static int publish_expire(const void *data) +{ + struct sip_esc_entry *esc_entry = (struct sip_esc_entry *) data; + struct event_state_compositor *esc = get_esc(esc_entry->event); + + ast_assert(esc != NULL); + + ao2_unlink(esc->compositor, esc_entry); + ao2_ref(esc_entry, -1); + return 0; +} + +static void create_new_sip_etag(struct sip_esc_entry *esc_entry, int is_linked) +{ + int new_etag = ast_atomic_fetchadd_int(&esc_etag_counter, +1); + struct event_state_compositor *esc = get_esc(esc_entry->event); + + ast_assert(esc != NULL); + if (is_linked) { + ao2_unlink(esc->compositor, esc_entry); + } + snprintf(esc_entry->entity_tag, sizeof(esc_entry->entity_tag), "%d", new_etag); + ao2_link(esc->compositor, esc_entry); +} + +static struct sip_esc_entry *create_esc_entry(struct event_state_compositor *esc, struct sip_request *req, const int expires) +{ + struct sip_esc_entry *esc_entry; + int expires_ms; + + if (!(esc_entry = ao2_alloc(sizeof(*esc_entry), esc_entry_destructor))) { + return NULL; + } + + esc_entry->event = esc->name; + + expires_ms = expires * 1000; + /* Bump refcount for scheduler */ + ao2_ref(esc_entry, +1); + esc_entry->sched_id = ast_sched_add(sched, expires_ms, publish_expire, esc_entry); + + /* Note: This links the esc_entry into the ESC properly */ + create_new_sip_etag(esc_entry, 0); + + return esc_entry; +} + +static int initialize_escs(void) +{ + int i, res = 0; + for (i = 0; i < ARRAY_LEN(event_state_compositors); i++) { + if (!((event_state_compositors[i].compositor) = + ao2_container_alloc(ESC_MAX_BUCKETS, esc_hash_fn, esc_cmp_fn))) { + res = -1; + } + } + return res; +} + +static void destroy_escs(void) +{ + int i; + for (i = 0; i < ARRAY_LEN(event_state_compositors); i++) { + ao2_ref(event_state_compositors[i].compositor, -1); + } +} + /*! \brief * Here we implement the container for dialogs (sip_pvt), defining * generic wrapper functions to ease the transition from the current @@ -1001,6 +1256,7 @@ static int sip_prepare_socket(struct sip_pvt *p); static int sipsock_read(int *id, int fd, short events, void *ignore); static int __sip_xmit(struct sip_pvt *p, struct ast_str *data, int len); static int __sip_reliable_xmit(struct sip_pvt *p, int seqno, int resp, struct ast_str *data, int len, int fatal, int sipmethod); +static void add_cc_call_info_to_response(struct sip_pvt *p, struct sip_request *resp); static int __transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable); static int retrans_pkt(const void *data); static int transmit_response_using_temp(ast_string_field callid, struct sockaddr_in *sin, int useglobal_nat, const int intended_method, const struct sip_request *req, const char *msg); @@ -1015,7 +1271,8 @@ static int transmit_response_with_allow(struct sip_pvt *p, const char *msg, cons static void transmit_fake_auth_response(struct sip_pvt *p, int sipmethod, struct sip_request *req, enum xmittype reliable); static int transmit_request(struct sip_pvt *p, int sipmethod, int inc, enum xmittype reliable, int newbranch); static int transmit_request_with_auth(struct sip_pvt *p, int sipmethod, int seqno, enum xmittype reliable, int newbranch); -static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init); +static int transmit_publish(struct sip_epa_entry *epa_entry, enum sip_publish_type publish_type, const char * const explicit_uri); +static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init, const char * const explicit_uri); static int transmit_reinvite_with_sdp(struct sip_pvt *p, int t38version, int oldsdp); static int transmit_info_with_digit(struct sip_pvt *p, const char digit, unsigned int duration); static int transmit_info_with_vidupdate(struct sip_pvt *p); @@ -1023,6 +1280,7 @@ static int transmit_message_with_text(struct sip_pvt *p, const char *text); static int transmit_refer(struct sip_pvt *p, const char *dest); static int transmit_notify_with_mwi(struct sip_pvt *p, int newmsgs, int oldmsgs, const char *vmexten); static int transmit_notify_with_sipfrag(struct sip_pvt *p, int cseq, char *message, int terminate); +static int transmit_cc_notify(struct ast_cc_agent *agent, struct sip_pvt *subscription, enum sip_cc_notify_state state); static int transmit_register(struct sip_registry *r, int sipmethod, const char *auth, const char *authheader); static int send_response(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, int seqno); static int send_request(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, int seqno); @@ -1236,7 +1494,7 @@ static int set_address_from_contact(struct sip_pvt *pvt); static void check_via(struct sip_pvt *p, struct sip_request *req); static int get_rpid(struct sip_pvt *p, struct sip_request *oreq); static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, char **number, int *reason); -static int get_destination(struct sip_pvt *p, struct sip_request *oreq); +static int get_destination(struct sip_pvt *p, struct sip_request *oreq, int *cc_recall_core_id); static int get_msg_text(char *buf, int len, struct sip_request *req, int addnewline); static int transmit_state_notify(struct sip_pvt *p, int state, int full, int timeout); static void update_connectedline(struct sip_pvt *p, const void *data, size_t datalen); @@ -1253,7 +1511,7 @@ static void *sip_tcp_worker_fn(void *); static void initialize_initreq(struct sip_pvt *p, struct sip_request *req); static int init_req(struct sip_request *req, int sipmethod, const char *recip); static int reqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod, int seqno, int newbranch); -static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod); +static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod, const char * const explicit_uri); static int init_resp(struct sip_request *resp, const char *msg); static inline int resp_needs_contact(const char *msg, enum sipmethod method); static int respprep(struct sip_request *resp, struct sip_pvt *p, const char *msg, const struct sip_request *req); @@ -1297,6 +1555,7 @@ static int handle_request_notify(struct sip_pvt *p, struct sip_request *req, str static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual *current, struct sip_request *req, int seqno, int *nounlock); /*------Response handling functions */ +static void handle_response_publish(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno); static void handle_response_invite(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno); static void handle_response_notify(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno); static void handle_response_refer(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno); @@ -1372,6 +1631,556 @@ const struct ast_channel_tech sip_tech = { */ struct ast_channel_tech sip_tech_info; +static int sip_cc_agent_init(struct ast_cc_agent *agent, struct ast_channel *chan); +static int sip_cc_agent_start_offer_timer(struct ast_cc_agent *agent); +static int sip_cc_agent_stop_offer_timer(struct ast_cc_agent *agent); +static void sip_cc_agent_ack(struct ast_cc_agent *agent); +static int sip_cc_agent_status_request(struct ast_cc_agent *agent); +static int sip_cc_agent_start_monitoring(struct ast_cc_agent *agent); +static int sip_cc_agent_recall(struct ast_cc_agent *agent); +static void sip_cc_agent_destructor(struct ast_cc_agent *agent); + +static struct ast_cc_agent_callbacks sip_cc_agent_callbacks = { + .type = "SIP", + .init = sip_cc_agent_init, + .start_offer_timer = sip_cc_agent_start_offer_timer, + .stop_offer_timer = sip_cc_agent_stop_offer_timer, + .ack = sip_cc_agent_ack, + .status_request = sip_cc_agent_status_request, + .start_monitoring = sip_cc_agent_start_monitoring, + .callee_available = sip_cc_agent_recall, + .destructor = sip_cc_agent_destructor, +}; + +static int find_by_notify_uri_helper(void *obj, void *arg, int flags) +{ + struct ast_cc_agent *agent = obj; + struct sip_cc_agent_pvt *agent_pvt = agent->private_data; + const char *uri = arg; + + return !strcmp(agent_pvt->notify_uri, uri) ? CMP_MATCH | CMP_STOP : 0; +} + +static struct ast_cc_agent *find_sip_cc_agent_by_notify_uri(const char * const uri) +{ + struct ast_cc_agent *agent = ast_cc_agent_callback(0, find_by_notify_uri_helper, (char *)uri, "SIP"); + return agent; +} + +static int find_by_subscribe_uri_helper(void *obj, void *arg, int flags) +{ + struct ast_cc_agent *agent = obj; + struct sip_cc_agent_pvt *agent_pvt = agent->private_data; + const char *uri = arg; + + return !strcmp(agent_pvt->subscribe_uri, uri) ? CMP_MATCH | CMP_STOP : 0; +} + +static struct ast_cc_agent *find_sip_cc_agent_by_subscribe_uri(const char * const uri) +{ + struct ast_cc_agent *agent = ast_cc_agent_callback(0, find_by_subscribe_uri_helper, (char *)uri, "SIP"); + return agent; +} + +static int find_by_callid_helper(void *obj, void *arg, int flags) +{ + struct ast_cc_agent *agent = obj; + struct sip_cc_agent_pvt *agent_pvt = agent->private_data; + struct sip_pvt *call_pvt = arg; + + return !strcmp(agent_pvt->original_callid, call_pvt->callid) ? CMP_MATCH | CMP_STOP : 0; +} + +static struct ast_cc_agent *find_sip_cc_agent_by_original_callid(struct sip_pvt *pvt) +{ + struct ast_cc_agent *agent = ast_cc_agent_callback(0, find_by_callid_helper, pvt, "SIP"); + return agent; +} + +static int sip_cc_agent_init(struct ast_cc_agent *agent, struct ast_channel *chan) +{ + struct sip_cc_agent_pvt *agent_pvt = ast_calloc(1, sizeof(*agent_pvt)); + struct sip_pvt *call_pvt = chan->tech_pvt; + + if (!agent_pvt) { + return -1; + } + + ast_assert(!strcmp(chan->tech->type, "SIP")); + + ast_copy_string(agent_pvt->original_callid, call_pvt->callid, sizeof(agent_pvt->original_callid)); + ast_copy_string(agent_pvt->original_exten, call_pvt->exten, sizeof(agent_pvt->original_exten)); + agent_pvt->offer_timer_id = -1; + agent->private_data = agent_pvt; + sip_pvt_lock(call_pvt); + ast_set_flag(&call_pvt->flags[0], SIP_OFFER_CC); + sip_pvt_unlock(call_pvt); + return 0; +} + +static int sip_offer_timer_expire(const void *data) +{ + struct ast_cc_agent *agent = (struct ast_cc_agent *) data; + struct sip_cc_agent_pvt *agent_pvt = agent->private_data; + + agent_pvt->offer_timer_id = -1; + + return ast_cc_failed(agent->core_id, "SIP agent %s's offer timer expired", agent->device_name); +} + +static int sip_cc_agent_start_offer_timer(struct ast_cc_agent *agent) +{ + struct sip_cc_agent_pvt *agent_pvt = agent->private_data; + int when; + + when = ast_get_cc_offer_timer(agent->cc_params) * 1000; + agent_pvt->offer_timer_id = ast_sched_add(sched, when, sip_offer_timer_expire, agent); + return 0; +} + +static int sip_cc_agent_stop_offer_timer(struct ast_cc_agent *agent) +{ + struct sip_cc_agent_pvt *agent_pvt = agent->private_data; + + AST_SCHED_DEL(sched, agent_pvt->offer_timer_id); + return 0; +} + +static void sip_cc_agent_ack(struct ast_cc_agent *agent) +{ + struct sip_cc_agent_pvt *agent_pvt = agent->private_data; + + sip_pvt_lock(agent_pvt->subscribe_pvt); + ast_set_flag(&agent_pvt->subscribe_pvt->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED); + transmit_response(agent_pvt->subscribe_pvt, "200 OK", &agent_pvt->subscribe_pvt->initreq); + transmit_cc_notify(agent, agent_pvt->subscribe_pvt, CC_QUEUED); + sip_pvt_unlock(agent_pvt->subscribe_pvt); + agent_pvt->is_available = TRUE; +} + +static int sip_cc_agent_status_request(struct ast_cc_agent *agent) +{ + struct sip_cc_agent_pvt *agent_pvt = agent->private_data; + enum ast_device_state state = agent_pvt->is_available ? AST_DEVICE_NOT_INUSE : AST_DEVICE_INUSE; + return ast_cc_agent_status_response(agent->core_id, state); +} + +static int sip_cc_agent_start_monitoring(struct ast_cc_agent *agent) +{ + /* To start monitoring just means to wait for an incoming PUBLISH + * to tell us that the caller has become available again. No special + * action is needed + */ + return 0; +} + +static int sip_cc_agent_recall(struct ast_cc_agent *agent) +{ + struct sip_cc_agent_pvt *agent_pvt = agent->private_data; + /* If we have received a PUBLISH beforehand stating that the caller in question + * is not available, we can save ourself a bit of effort here and just report + * the caller as busy + */ + if (!agent_pvt->is_available) { + return ast_cc_agent_caller_busy(agent->core_id, "Caller %s is busy, reporting to the core", + agent->device_name); + } + /* Otherwise, we transmit a NOTIFY to the caller and await either + * a PUBLISH or an INVITE + */ + sip_pvt_lock(agent_pvt->subscribe_pvt); + transmit_cc_notify(agent, agent_pvt->subscribe_pvt, CC_READY); + sip_pvt_unlock(agent_pvt->subscribe_pvt); + return 0; +} + +static void sip_cc_agent_destructor(struct ast_cc_agent *agent) +{ + struct sip_cc_agent_pvt *agent_pvt = agent->private_data; + + if (!agent_pvt) { + /* The agent constructor probably failed. */ + return; + } + + sip_cc_agent_stop_offer_timer(agent); + if (agent_pvt->subscribe_pvt) { + sip_pvt_lock(agent_pvt->subscribe_pvt); + if (!ast_test_flag(&agent_pvt->subscribe_pvt->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED)) { + /* If we haven't sent a 200 OK for the SUBSCRIBE dialog yet, then we need to send a response letting + * the subscriber know something went wrong + */ + transmit_response(agent_pvt->subscribe_pvt, "500 Internal Server Error", &agent_pvt->subscribe_pvt->initreq); + } + sip_pvt_unlock(agent_pvt->subscribe_pvt); + agent_pvt->subscribe_pvt = dialog_unref(agent_pvt->subscribe_pvt, "SIP CC agent destructor: Remove ref to subscription"); + } + ast_free(agent_pvt); +} + +struct ao2_container *sip_monitor_instances; + +static int sip_monitor_instance_hash_fn(const void *obj, const int flags) +{ + const struct sip_monitor_instance *monitor_instance = obj; + return monitor_instance->core_id; +} + +static int sip_monitor_instance_cmp_fn(void *obj, void *arg, int flags) +{ + struct sip_monitor_instance *monitor_instance1 = obj; + struct sip_monitor_instance *monitor_instance2 = arg; + + return monitor_instance1->core_id == monitor_instance2->core_id ? CMP_MATCH | CMP_STOP : 0; +} + +static void sip_monitor_instance_destructor(void *data) +{ + struct sip_monitor_instance *monitor_instance = data; + if (monitor_instance->subscription_pvt) { + sip_pvt_lock(monitor_instance->subscription_pvt); + monitor_instance->subscription_pvt->expiry = 0; + transmit_invite(monitor_instance->subscription_pvt, SIP_SUBSCRIBE, FALSE, 0, monitor_instance->subscribe_uri); + sip_pvt_unlock(monitor_instance->subscription_pvt); + dialog_unref(monitor_instance->subscription_pvt, "Unref monitor instance ref of subscription pvt"); + } + if (monitor_instance->suspension_entry) { + monitor_instance->suspension_entry->body[0] = '\0'; + transmit_publish(monitor_instance->suspension_entry, SIP_PUBLISH_REMOVE ,monitor_instance->notify_uri); + ao2_t_ref(monitor_instance->suspension_entry, -1, "Decrementing suspension entry refcount in sip_monitor_instance_destructor"); + } + ast_string_field_free_memory(monitor_instance); +} + +static struct sip_monitor_instance *sip_monitor_instance_init(int core_id, const char * const subscribe_uri, const char * const peername, const char * const device_name) +{ + struct sip_monitor_instance *monitor_instance = ao2_alloc(sizeof(*monitor_instance), sip_monitor_instance_destructor); + + if (!monitor_instance) { + return NULL; + } + + if (ast_string_field_init(monitor_instance, 256)) { + ao2_ref(monitor_instance, -1); + return NULL; + } + + ast_string_field_set(monitor_instance, subscribe_uri, subscribe_uri); + ast_string_field_set(monitor_instance, peername, peername); + ast_string_field_set(monitor_instance, device_name, device_name); + monitor_instance->core_id = core_id; + ao2_link(sip_monitor_instances, monitor_instance); + return monitor_instance; +} + +static int find_sip_monitor_instance_by_subscription_pvt(void *obj, void *arg, int flags) +{ + struct sip_monitor_instance *monitor_instance = obj; + return monitor_instance->subscription_pvt == arg ? CMP_MATCH | CMP_STOP : 0; +} + +static int find_sip_monitor_instance_by_suspension_entry(void *obj, void *arg, int flags) +{ + struct sip_monitor_instance *monitor_instance = obj; + return monitor_instance->suspension_entry == arg ? CMP_MATCH | CMP_STOP : 0; +} + +static int sip_cc_monitor_request_cc(struct ast_cc_monitor *monitor, int *available_timer_id); +static int sip_cc_monitor_suspend(struct ast_cc_monitor *monitor); +static int sip_cc_monitor_status_response(struct ast_cc_monitor *monitor, enum ast_device_state devstate); +static int sip_cc_monitor_unsuspend(struct ast_cc_monitor *monitor); +static int sip_cc_monitor_cancel_available_timer(struct ast_cc_monitor *monitor, int *sched_id); +static void sip_cc_monitor_destructor(void *private_data); + +static struct ast_cc_monitor_callbacks sip_cc_monitor_callbacks = { + .type = "SIP", + .request_cc = sip_cc_monitor_request_cc, + .suspend = sip_cc_monitor_suspend, + .status_response = sip_cc_monitor_status_response, + .unsuspend = sip_cc_monitor_unsuspend, + .cancel_available_timer = sip_cc_monitor_cancel_available_timer, + .destructor = sip_cc_monitor_destructor, +}; + +static int sip_cc_monitor_request_cc(struct ast_cc_monitor *monitor, int *available_timer_id) +{ + struct sip_monitor_instance *monitor_instance = monitor->private_data; + enum ast_cc_service_type service = monitor->service_offered; + int when; + + if (!monitor_instance) { + return -1; + } + + if (!(monitor_instance->subscription_pvt = sip_alloc(NULL, NULL, 0, SIP_SUBSCRIBE, NULL))) { + return -1; + } + + when = service == AST_CC_CCBS ? ast_get_ccbs_available_timer(monitor->interface->config_params) : + ast_get_ccnr_available_timer(monitor->interface->config_params); + + sip_pvt_lock(monitor_instance->subscription_pvt); + create_addr(monitor_instance->subscription_pvt, monitor_instance->peername, 0, 1); + ast_sip_ouraddrfor(&monitor_instance->subscription_pvt->sa.sin_addr, &monitor_instance->subscription_pvt->ourip, monitor_instance->subscription_pvt); + monitor_instance->subscription_pvt->subscribed = CALL_COMPLETION; + monitor_instance->subscription_pvt->expiry = when; + + transmit_invite(monitor_instance->subscription_pvt, SIP_SUBSCRIBE, FALSE, 2, monitor_instance->subscribe_uri); + sip_pvt_unlock(monitor_instance->subscription_pvt); + + ao2_t_ref(monitor, +1, "Adding a ref to the monitor for the scheduler"); + *available_timer_id = ast_sched_add(sched, when * 1000, ast_cc_available_timer_expire, monitor); + return 0; +} + +static int construct_pidf_body(enum sip_cc_publish_state state, char *pidf_body, size_t size, const char *presentity) +{ + struct ast_str *body = ast_str_alloca(size); + char tuple_id[32]; + + generate_random_string(tuple_id, sizeof(tuple_id)); + + /* We'll make this a bare-bones pidf body. In state_notify_build_xml, the PIDF + * body gets a lot more extra junk that isn't necessary, so we'll leave it out here. + */ + ast_str_append(&body, 0, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); + /* XXX The entity attribute is currently set to the peer name associated with the + * dialog. This is because we currently only call this function for call-completion + * PUBLISH bodies. In such cases, the entity is completely disregarded. For other + * event packages, it may be crucial to have a proper URI as the presentity so this + * should be revisited as support is expanded. + */ + ast_str_append(&body, 0, "<presence xmlns=\"urn:ietf:params:xml:ns:pidf\" entity=\"%s\">\n", presentity); + ast_str_append(&body, 0, "<tuple id=\"%s\">\n", tuple_id); + ast_str_append(&body, 0, "<status><basic>%s</basic></status>\n", state == CC_OPEN ? "open" : "closed"); + ast_str_append(&body, 0, "</tuple>\n"); + ast_str_append(&body, 0, "</presence>\n"); + ast_copy_string(pidf_body, ast_str_buffer(body), size); + return 0; +} + +static int sip_cc_monitor_suspend(struct ast_cc_monitor *monitor) +{ + struct sip_monitor_instance *monitor_instance = monitor->private_data; + enum sip_publish_type publish_type; + struct cc_epa_entry *cc_entry; + + if (!monitor_instance) { + return -1; + } + + if (!monitor_instance->suspension_entry) { + /* We haven't yet allocated the suspension entry, so let's give it a shot */ + if (!(monitor_instance->suspension_entry = create_epa_entry("call-completion", monitor_instance->peername))) { + ast_log(LOG_WARNING, "Unable to allocate sip EPA entry for call-completion\n"); + ao2_ref(monitor_instance, -1); + return -1; + } + if (!(cc_entry = ast_calloc(1, sizeof(*cc_entry)))) { + ast_log(LOG_WARNING, "Unable to allocate space for instance data of EPA entry for call-completion\n"); + ao2_ref(monitor_instance, -1); + return -1; + } + cc_entry->core_id = monitor->core_id; + monitor_instance->suspension_entry->instance_data = cc_entry; + publish_type = SIP_PUBLISH_INITIAL; + } else { + publish_type = SIP_PUBLISH_MODIFY; + cc_entry = monitor_instance->suspension_entry->instance_data; + } + + cc_entry->current_state = CC_CLOSED; + + if (ast_strlen_zero(monitor_instance->notify_uri)) { + /* If we have no set notify_uri, then what this means is that we have + * not received a NOTIFY from this destination stating that he is + * currently available. + * + * This situation can arise when the core calls the suspend callbacks + * of multiple destinations. If one of the other destinations aside + * from this one notified Asterisk that he is available, then there + * is no reason to take any suspension action on this device. Rather, + * we should return now and if we receive a NOTIFY while monitoring + * is still "suspended" then we can immediately respond with the + * proper PUBLISH to let this endpoint know what is going on. + */ + return 0; + } + construct_pidf_body(CC_CLOSED, monitor_instance->suspension_entry->body, sizeof(monitor_instance->suspension_entry->body), monitor_instance->peername); + return transmit_publish(monitor_instance->suspension_entry, publish_type, monitor_instance->notify_uri); +} + +static int sip_cc_monitor_status_response(struct ast_cc_monitor *monitor, enum ast_device_state devstate) +{ + /* This will never be called because the SIP monitor will never make a status request to + * begin with + */ + ast_log(LOG_WARNING, "sip_cc_monitor_status_response called. Something dreadfully wrong must have happened.\n"); + return 0; +} + +static int sip_cc_monitor_unsuspend(struct ast_cc_monitor *monitor) +{ + struct sip_monitor_instance *monitor_instance = monitor->private_data; + struct cc_epa_entry *cc_entry; + + if (!monitor_instance) { + return -1; + } + + ast_assert(monitor_instance->suspension_entry != NULL); + + cc_entry = monitor_instance->suspension_entry->instance_data; + cc_entry->current_state = CC_OPEN; + if (ast_strlen_zero(monitor_instance->notify_uri)) { + /* This means we are being asked to unsuspend a call leg we never + * sent a PUBLISH on. As such, there is no reason to send another + * PUBLISH at this point either. We can just return instead. + */ + return 0; + } + construct_pidf_body(CC_OPEN, monitor_instance->suspension_entry->body, sizeof(monitor_instance->suspension_entry->body), monitor_instance->peername); + return transmit_publish(monitor_instance->suspension_entry, SIP_PUBLISH_MODIFY, monitor_instance->notify_uri); +} + +static int sip_cc_monitor_cancel_available_timer(struct ast_cc_monitor *monitor, int *sched_id) +{ + if (*sched_id != -1) { + AST_SCHED_DEL(sched, *sched_id); + ao2_t_ref(monitor, -1, "Removing scheduler's reference to the monitor"); + } + return 0; +} + +static void sip_cc_monitor_destructor(void *private_data) +{ + struct sip_monitor_instance *monitor_instance = private_data; + ao2_unlink(sip_monitor_instances, monitor_instance); + ast_module_unref(ast_module_info->self); +} + +static int sip_get_cc_information(struct sip_request *req, char *subscribe_uri, size_t size, enum ast_cc_service_type *service) +{ + char *call_info = ast_strdupa(get_header(req, "Call-Info")); + char *uri; + char *purpose; + char *service_str; + static const char cc_purpose[] = "purpose=call-completion"; + static const int cc_purpose_len = sizeof(cc_purpose) - 1; + + if (ast_strlen_zero(call_info)) { + /* No Call-Info present. Definitely no CC offer */ + return -1; + } + + uri = strsep(&call_info, ";"); + + while ((purpose = strsep(&call_info, ";"))) { + if (!strncmp(purpose, cc_purpose, cc_purpose_len)) { + break; + } + } + if (!purpose) { + /* We didn't find the appropriate purpose= parameter. Oh well */ + return -1; + } + + /* Okay, call-completion has been offered. Let's figure out what type of service this is */ + while ((service_str = strsep(&call_info, ";"))) { + if (!strncmp(service_str, "m=", 2)) { + break; + } + } + if (!service_str) { + /* So they didn't offer a particular service, We'll just go with CCBS since it really + * doesn't matter anyway + */ + service_str = "BS"; + } else { + /* We already determined that there is an "m=" so no need to check + * the result of this strsep + */ + strsep(&service_str, "="); + } + + if ((*service = service_string_to_service_type(service_str)) == AST_CC_NONE) { + /* Invalid service offered */ + return -1; + } + + ast_copy_string(subscribe_uri, get_in_brackets(uri), size); + + return 0; +} + +/* + * \brief Determine what, if any, CC has been offered and queue a CC frame if possible + * + * After taking care of some formalities to be sure that this call is eligible for CC, + * we first try to see if we can make use of native CC. We grab the information from + * the passed-in sip_request (which is always a response to an INVITE). If we can + * use native CC monitoring for the call, then so be it. + * + * If native cc monitoring is not possible or not supported, then we will instead attempt + * to use generic monitoring. Falling back to generic from a failed attempt at using native + * monitoring will only work if the monitor policy of the endpoint is "always" + * + * \param pvt The current dialog. Contains CC parameters for the endpoint + * \param req The response to the INVITE we want to inspect + * \param service The service to use if generic monitoring is to be used. For native + * monitoring, we get the service from the SIP response itself + */ +static void sip_handle_cc(struct sip_pvt *pvt, struct sip_request *req, enum ast_cc_service_type service) +{ + enum ast_cc_monitor_policies monitor_policy = ast_get_cc_monitor_policy(pvt->cc_params); + int core_id; + char interface_name[AST_CHANNEL_NAME]; + + if (monitor_policy == AST_CC_MONITOR_NEVER) { + /* Don't bother, just return */ + return; + } + + if ((core_id = ast_cc_get_current_core_id(pvt->owner)) == -1) { + /* For some reason, CC is invalid, so don't try it! */ + return; + } + + ast_channel_get_device_name(pvt->owner, interface_name, sizeof(interface_name)); + + if (monitor_policy == AST_CC_MONITOR_ALWAYS || monitor_policy == AST_CC_MONITOR_NATIVE) { + char subscribe_uri[SIPBUFSIZE]; + char device_name[AST_CHANNEL_NAME]; + enum ast_cc_service_type offered_service; + struct sip_monitor_instance *monitor_instance; + if (sip_get_cc_information(req, subscribe_uri, sizeof(subscribe_uri), &offered_service)) { + /* If CC isn't being offered to us, or for some reason the CC offer is + * not formatted correctly, then it may still be possible to use generic + * call completion since the monitor policy may be "always" + */ + goto generic; + } + ast_channel_get_device_name(pvt->owner, device_name, sizeof(device_name)); + if (!(monitor_instance = sip_monitor_instance_init(core_id, subscribe_uri, pvt->peername, device_name))) { + /* Same deal. We can try using generic still */ + goto generic; + } + /* We bump the refcount of chan_sip because once we queue this frame, the CC core + * will have a reference to callbacks in this module. We decrement the module + * refcount once the monitor destructor is called + */ + ast_module_ref(ast_module_info->self); + ast_queue_cc_frame(pvt->owner, "SIP", pvt->dialstring, offered_service, monitor_instance); + ao2_ref(monitor_instance, -1); + return; + } + +generic: + if (monitor_policy == AST_CC_MONITOR_GENERIC || monitor_policy == AST_CC_MONITOR_ALWAYS) { + ast_queue_cc_frame(pvt->owner, AST_CC_GENERIC_MONITOR_TYPE, interface_name, service, NULL); + } +} + /*! \brief Working TLS connection configuration */ static struct ast_tls_config sip_tls_cfg; @@ -2700,7 +3509,7 @@ static int __sip_autodestruct(const void *data) struct sip_pvt *p = (struct sip_pvt *)data; /* If this is a subscription, tell the phone that we got a timeout */ - if (p->subscribed) { + if (p->subscribed && p->subscribed != MWI_NOTIFICATION && p->subscribed != CALL_COMPLETION) { transmit_state_notify(p, AST_EXTENSION_DEACTIVATED, 1, TRUE); /* Send last notification */ p->subscribed = NONE; append_history(p, "Subscribestatus", "timeout"); @@ -3159,6 +3968,16 @@ static int sip_queryoption(struct ast_channel *chan, int option, void *data, int *cp = p->dsp ? 1 : 0; ast_debug(1, "Reporting digit detection %sabled on %s\n", *cp ? "en" : "dis", chan->name); break; + case AST_OPTION_DEVICE_NAME: + if (p && p->outgoing_call) { + cp = (char *) data; + ast_copy_string(cp, p->dialstring, *datalen); + res = 0; + } + /* We purposely break with a return of -1 in the + * implied else case here + */ + break; default: break; } @@ -3401,6 +4220,8 @@ static void sip_destroy_peer(struct sip_peer *peer) peer->socket.tcptls_session = NULL; } + ast_cc_config_params_destroy(peer->cc_params); + ast_string_field_free_memory(peer); } @@ -3943,6 +4764,7 @@ static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer) dialog->peerauth = peer->auth; dialog->maxcallbitrate = peer->maxcallbitrate; dialog->disallowed_methods = peer->disallowed_methods; + ast_cc_copy_config_params(dialog->cc_params, peer->cc_params); if (ast_strlen_zero(dialog->tohost)) ast_string_field_set(dialog, tohost, ast_inet_ntoa(dialog->sa.sin_addr)); if (!ast_strlen_zero(peer->fromdomain)) { @@ -4131,12 +4953,26 @@ static int sip_call(struct ast_channel *ast, char *dest, int timeout) struct varshead *headp; struct ast_var_t *current; const char *referer = NULL; /* SIP referrer */ + int cc_core_id; + char uri[SIPBUFSIZE] = ""; if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) { ast_log(LOG_WARNING, "sip_call called on %s, neither down nor reserved\n", ast->name); return -1; } + if (ast_cc_is_recall(ast, &cc_core_id, "SIP")) { + char device_name[AST_CHANNEL_NAME]; + struct ast_cc_monitor *recall_monitor; + struct sip_monitor_instance *monitor_instance; + ast_channel_get_device_name(ast, device_name, sizeof(device_name)); + if ((recall_monitor = ast_cc_get_monitor_by_recall_core_id(cc_core_id, device_name))) { + monitor_instance = recall_monitor->private_data; + ast_copy_string(uri, monitor_instance->notify_uri, sizeof(uri)); + ao2_t_ref(recall_monitor, -1, "Got the URI we need so unreffing monitor"); + } + } + /* Check whether there is vxml_url, distinctive ring variables */ headp=&ast->varshead; AST_LIST_TRAVERSE(headp, current, entries) { @@ -4201,7 +5037,7 @@ static int sip_call(struct ast_channel *ast, char *dest, int timeout) int xmitres; sip_pvt_lock(p); - xmitres = transmit_invite(p, SIP_INVITE, 1, 2); + xmitres = transmit_invite(p, SIP_INVITE, 1, 2, uri); sip_pvt_unlock(p); if (xmitres == XMIT_ERROR) return -1; @@ -4371,6 +5207,13 @@ void __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist) ast_string_field_free_memory(p); + ast_cc_config_params_destroy(p->cc_params); + + if (p->epa_entry) { + ao2_ref(p->epa_entry, -1); + p->epa_entry = NULL; + } + if (p->socket.tcptls_session) { ao2_ref(p->socket.tcptls_session, -1); p->socket.tcptls_session = NULL; @@ -5446,7 +6289,10 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *tit sip_pvt_lock(i); return NULL; } + ast_channel_lock(tmp); sip_pvt_lock(i); + ast_channel_cc_params_init(tmp, i->cc_params); + ast_channel_unlock(tmp); tmp->tech = ( ast_test_flag(&i->flags[0], SIP_DTMF) == SIP_DTMF_INFO || ast_test_flag(&i->flags[0], SIP_DTMF) == SIP_DTMF_SHORTINFO) ? &sip_tech_info : &sip_tech; @@ -5911,6 +6757,23 @@ static char *generate_random_string(char *buf, size_t size) return buf; } +static char *generate_uri(struct sip_pvt *pvt, char *buf, size_t size) +{ + struct ast_str *uri = ast_str_alloca(size); + int ourport = ntohs(pvt->ourip.sin_port); + ast_str_set(&uri, 0, "%s", pvt->socket.type == SIP_TRANSPORT_TLS ? "sips:" : "sip:"); + /* Here would be a great place to generate a UUID, but for now we'll + * use the handy random string generation function we already have + */ + ast_str_append(&uri, 0, "%s", generate_random_string(buf, size)); + ast_str_append(&uri, 0, "@%s", ast_inet_ntoa(pvt->ourip.sin_addr)); + if (!sip_standard_port(pvt->socket.type, ourport)) { + ast_str_append(&uri, 0, ":%d", ourport); + } + ast_copy_string(buf, ast_str_buffer(uri), size); + return buf; +} + /*! \brief Build SIP Call-ID value for a non-REGISTER transaction */ static void build_callid_pvt(struct sip_pvt *pvt) { @@ -5975,6 +6838,11 @@ struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *sin, return NULL; } + if (!(p->cc_params = ast_cc_config_params_init())) { + ao2_t_ref(p, -1, "Yuck, couldn't allocate cc_params struct. Get rid o' p"); + return NULL; + } + if (req) { set_socket_transport(&p->socket, req->socket.type); /* Later in ast_sip_ouraddrfor we need this to choose the right ip and port for the specific transport */ } else { @@ -8234,6 +9102,9 @@ static int __transmit_response(struct sip_pvt *p, const char *msg, const struct ast_clear_flag(&p->flags[1], SIP_PAGE2_CONNECTLINEUPDATE_PEND); add_rpid(&resp, p); } + if (ast_test_flag(&p->flags[0], SIP_OFFER_CC)) { + add_cc_call_info_to_response(p, &resp); + } add_header_contentLength(&resp, 0); /* If we are cancelling an incoming invite for some reason, add information @@ -8269,6 +9140,19 @@ static int __transmit_response(struct sip_pvt *p, const char *msg, const struct return send_response(p, &resp, reliable, seqno); } +static int transmit_response_with_sip_etag(struct sip_pvt *p, const char *msg, const struct sip_request *req, struct sip_esc_entry *esc_entry, int need_new_etag) +{ + struct sip_request resp; + + if (need_new_etag) { + create_new_sip_etag(esc_entry, 1); + } + respprep(&resp, p, msg, req); + add_header(&resp, "SIP-ETag", esc_entry->entity_tag); + + return send_response(p, &resp, 0, 0); +} + static int temp_pvt_init(void *data) { struct sip_pvt *p = data; @@ -9305,6 +10189,38 @@ static void copy_request(struct sip_request *dst, const struct sip_request *src) dst->data->used = src->data->used; } +static void add_cc_call_info_to_response(struct sip_pvt *p, struct sip_request *resp) +{ + char uri[SIPBUFSIZE]; + struct ast_str *header = ast_str_alloca(SIPBUFSIZE); + struct ast_cc_agent *agent = find_sip_cc_agent_by_original_callid(p); + struct sip_cc_agent_pvt *agent_pvt; + + if (!agent) { + /* Um, what? How could the SIP_OFFER_CC flag be set but there not be an + * agent? Oh well, we'll just warn and return without adding the header. + */ + ast_log(LOG_WARNING, "Can't find SIP CC agent for call '%s' even though OFFER_CC flag was set?\n", p->callid); + return; + } + + agent_pvt = agent->private_data; + + if (!ast_strlen_zero(agent_pvt->subscribe_uri)) { + ast_copy_string(uri, agent_pvt->subscribe_uri, sizeof(uri)); + } else { + generate_uri(p, uri, sizeof(uri)); + ast_copy_string(agent_pvt->subscribe_uri, uri, sizeof(agent_pvt->subscribe_uri)); + } + /* XXX Hardcode "NR" as the m reason for now. This should perhaps be changed + * to be more accurate. This parameter has no bearing on the actual operation + * of the feature; it's just there for informational purposes. + */ + ast_str_set(&header, 0, "<%s>;purpose=call-completion;m=%s", uri, "NR"); + add_header(resp, "Call-Info", ast_str_buffer(header)); + ao2_ref(agent, -1); +} + /*! \brief Used for 200 OK and 183 early media \return Will return XMIT_ERROR for network errors. */ @@ -9320,6 +10236,9 @@ static int transmit_response_with_sdp(struct sip_pvt *p, const char *msg, const if (rpid == TRUE) { add_rpid(&resp, p); } + if (ast_test_flag(&p->flags[0], SIP_OFFER_CC)) { + add_cc_call_info_to_response(p, &resp); + } if (p->rtp) { if (!p->autoframing && !ast_test_flag(&p->flags[0], SIP_OUTGOING)) { ast_debug(1, "Setting framing from config on incoming call\n"); @@ -9479,7 +10398,7 @@ static void build_contact(struct sip_pvt *p) } /*! \brief Initiate new SIP request to peer/user */ -static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod) +static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod, const char * const explicit_uri) { struct ast_str *invite = ast_str_alloca(256); char from[256]; @@ -9564,25 +10483,30 @@ static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmetho else snprintf(from, sizeof(from), "\"%s\" <sip:%s@%s>;tag=%s", n, l, d, p->tag); - /* If we're calling a registered SIP peer, use the fullcontact to dial to the peer */ - if (!ast_strlen_zero(p->fullcontact)) { - /* If we have full contact, trust it */ - ast_str_append(&invite, 0, "%s", p->fullcontact); + + if (!ast_strlen_zero(explicit_uri)) { + ast_str_set(&invite, 0, "%s", explicit_uri); } else { - /* Otherwise, use the username while waiting for registration */ - ast_str_append(&invite, 0, "sip:"); - if (!ast_strlen_zero(p->username)) { - n = p->username; - if (sip_cfg.pedanticsipchecking) { - ast_uri_encode(n, tmp_n, sizeof(tmp_n), 0); - n = tmp_n; + /* If we're calling a registered SIP peer, use the fullcontact to dial to the peer */ + if (!ast_strlen_zero(p->fullcontact)) { + /* If we have full contact, trust it */ + ast_str_append(&invite, 0, "%s", p->fullcontact); + } else { + /* Otherwise, use the username while waiting for registration */ + ast_str_append(&invite, 0, "sip:"); + if (!ast_strlen_zero(p->username)) { + n = p->username; + if (sip_cfg.pedanticsipchecking) { + ast_uri_encode(n, tmp_n, sizeof(tmp_n), 0); + n = tmp_n; + } + ast_str_append(&invite, 0, "%s@", n); } - ast_str_append(&invite, 0, "%s@", n); + ast_str_append(&invite, 0, "%s", p->tohost); + if (p->portinuri) + ast_str_append(&invite, 0, ":%d", ntohs(p->sa.sin_port)); + ast_str_append(&invite, 0, "%s", urioptions); } - ast_str_append(&invite, 0, "%s", p->tohost); - if (p->portinuri) - ast_str_append(&invite, 0, ":%d", ntohs(p->sa.sin_port)); - ast_str_append(&invite, 0, "%s", urioptions); } /* If custom URI options have been provided, append them */ @@ -9674,6 +10598,39 @@ static void add_diversion_header(struct sip_request *req, struct sip_pvt *pvt) add_header(req, "Diversion", header_text); } +static int transmit_publish(struct sip_epa_entry *epa_entry, enum sip_publish_type publish_type, const char * const explicit_uri) +{ + struct sip_pvt *pvt; + int expires; + + epa_entry->publish_type = publish_type; + + if (!(pvt = sip_alloc(NULL, NULL, 0, SIP_PUBLISH, NULL))) { + return -1; + } + + sip_pvt_lock(pvt); + + if (create_addr(pvt, epa_entry->destination, NULL, TRUE)) { + dialog_unlink_all(pvt, TRUE, TRUE); + dialog_unref(pvt, "create_addr failed in transmit_publish. Unref dialog"); + } + ast_sip_ouraddrfor(&pvt->sa.sin_addr, &pvt->ourip, pvt); + ast_set_flag(&pvt->flags[0], SIP_OUTGOING); + expires = (publish_type == SIP_PUBLISH_REMOVE) ? 0 : DEFAULT_PUBLISH_EXPIRES; + pvt->expiry = expires; + + /* Bump refcount for sip_pvt's reference */ + ao2_ref(epa_entry, +1); + pvt->epa_entry = epa_entry; + + transmit_invite(pvt, SIP_PUBLISH, FALSE, 2, explicit_uri); + sip_pvt_unlock(pvt); + sip_scheddestroy(pvt, DEFAULT_TRANS_TIMEOUT); + dialog_unref(pvt, "Done with the sip_pvt allocated for transmitting PUBLISH"); + return 0; +} + /*! \brief Build REFER/INVITE/OPTIONS/SUBSCRIBE message and transmit it \param init 0 = Prepare request within dialog, 1= prepare request, new branch, 2= prepare new request and new dialog. do_proxy_auth calls this with init!=2 \param p sip_pvt structure @@ -9681,7 +10638,7 @@ static void add_diversion_header(struct sip_request *req, struct sip_pvt *pvt) \param sipmethod unknown */ -static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init) +static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init, const char * const explicit_uri) { struct sip_request req; struct ast_variable *var; @@ -9693,7 +10650,7 @@ static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init) build_via(p); } if (init > 1) - initreqprep(&req, p, sipmethod); + initreqprep(&req, p, sipmethod, explicit_uri); else /* If init=1, we should not generate a new branch. If it's 0, we need a new branch. */ reqprep(&req, p, sipmethod, 0, init ? 0 : 1); @@ -9711,12 +10668,16 @@ static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init) add_header(&req, "Referred-By", buf); } } - } else if (sipmethod == SIP_SUBSCRIBE) { /* We only support sending MWI subscriptions right now */ + } else if (sipmethod == SIP_SUBSCRIBE) { char buf[SIPBUFSIZE]; - - add_header(&req, "Event", "message-summary"); - add_header(&req, "Accept", "application/simple-message-summary"); - snprintf(buf, sizeof(buf), "%d", mwi_expiry); + if (p->subscribed == MWI_NOTIFICATION) { + add_header(&req, "Event", "message-summary"); + add_header(&req, "Accept", "application/simple-message-summary"); + } else if (p->subscribed == CALL_COMPLETION) { + add_header(&req, "Event", "call-completion"); + add_header(&req, "Accept", "application/call-completion"); + } + snprintf(buf, sizeof(buf), "%d", p->expiry); add_header(&req, "Expires", buf); } @@ -9805,13 +10766,33 @@ static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init) add_header_contentLength(&req, ast_str_strlen(p->notify->content)); if (ast_str_strlen(p->notify->content)) add_line(&req, ast_str_buffer(p->notify->content)); + } else if (sipmethod == SIP_PUBLISH) { + char expires[SIPBUFSIZE]; + switch (p->epa_entry->static_data->event) { + case CALL_COMPLETION: + snprintf(expires, sizeof(expires), "%d", p->expiry); + add_header(&req, "Event", "call-completion"); + add_header(&req, "Expires", expires); + if (p->epa_entry->publish_type != SIP_PUBLISH_INITIAL) { + add_header(&req, "SIP-If-Match", p->epa_entry->entity_tag); + } + if (!ast_strlen_zero(p->epa_entry->body)) { + add_header(&req, "Content-Type", "application/pidf+xml"); + add_header_contentLength(&req, strlen(p->epa_entry->body)); + add_line(&req, p->epa_entry->body); + } else { + add_header_contentLength(&req, 0); + } + default: + break; + } } else { add_header_contentLength(&req, 0); } if (!p->initreq.headers || init > 2) initialize_initreq(p, &req); - if (sipmethod == SIP_INVITE) { + if (sipmethod == SIP_INVITE || sipmethod == SIP_SUBSCRIBE) { p->lastinvite = p->ocseq; } return send_request(p, &req, init ? XMIT_CRITICAL : XMIT_RELIABLE, p->ocseq); @@ -9845,7 +10826,7 @@ static int __sip_subscribe_mwi_do(struct sip_subscription_mwi *mwi) /* If we already have a subscription up simply send a resubscription */ if (mwi->call) { - transmit_invite(mwi->call, SIP_SUBSCRIBE, 0, 0); + transmit_invite(mwi->call, SIP_SUBSCRIBE, 0, 0, NULL); return 0; } @@ -9866,6 +10847,8 @@ static int __sip_subscribe_mwi_do(struct sip_subscription_mwi *mwi) mwi->call = dialog_unref(mwi->call, "unref dialog after unlink_all"); return 0; } + + mwi->call->expiry = mwi_expiry; if (!mwi->dnsmgr && mwi->portno) { mwi->call->sa.sin_port = htons(mwi->portno); @@ -9900,7 +10883,7 @@ static int __sip_subscribe_mwi_do(struct sip_subscription_mwi *mwi) mwi->call->mwi = ASTOBJ_REF(mwi); /* Actually send the packet */ - transmit_invite(mwi->call, SIP_SUBSCRIBE, 0, 2); + transmit_invite(mwi->call, SIP_SUBSCRIBE, 0, 2, NULL); return 0; } @@ -10084,6 +11067,34 @@ static void state_notify_build_xml(int state, int full, const char *exten, const } } +static int transmit_cc_notify(struct ast_cc_agent *agent, struct sip_pvt *subscription, enum sip_cc_notify_state state) +{ + struct sip_request req; + struct sip_cc_agent_pvt *agent_pvt = agent->private_data; + char uri[SIPBUFSIZE]; + char state_str[64]; + + if (state < CC_QUEUED || state > CC_READY) { + ast_log(LOG_WARNING, "Invalid state provided for transmit_cc_notify (%d)\n", state); + return -1; + } + + reqprep(&req, subscription, SIP_NOTIFY, 0, TRUE); + snprintf(state_str, sizeof(state_str), "%s\r\n", sip_cc_notify_state_map[state].state_string); + add_header(&req, "Event", "call-completion"); + add_header(&req, "Content-Type", "application/call-completion"); + if (state == CC_READY) { + generate_uri(subscription, agent_pvt->notify_uri, sizeof(agent_pvt->notify_uri)); + snprintf(uri, sizeof(uri) - 1, "cc-URI: %s\r\n", agent_pvt->notify_uri); + } + add_header_contentLength(&req, strlen(state_str) + + (state == CC_READY ? strlen(uri) : 0)); + add_line(&req, state_str); + if (state == CC_READY) { + add_line(&req, uri); + } + return send_request(subscription, &req, XMIT_RELIABLE, subscription->ocseq); +} /*! \brief Used in the SUBSCRIBE notification subsystem (RFC3265) */ static int transmit_state_notify(struct sip_pvt *p, int state, int full, int timeout) @@ -10183,7 +11194,7 @@ static int transmit_notify_with_mwi(struct sip_pvt *p, int newmsgs, int oldmsgs, int ourport = ntohs(p->ourip.sin_port); const char *exten = S_OR(vmexten, default_vmexten); - initreqprep(&req, p, SIP_NOTIFY); + initreqprep(&req, p, SIP_NOTIFY, NULL); add_header(&req, "Event", "message-summary"); add_header(&req, "Content-Type", default_notifymime); ast_str_append(&out, 0, "Messages-Waiting: %s\r\n", newmsgs ? "yes" : "no"); @@ -10297,7 +11308,7 @@ static int manager_sipnotify(struct mansession *s, const struct message *m) dialog_ref(p, "bump the count of p, which transmit_sip_request will decrement."); sip_scheddestroy(p, SIP_TRANS_TIMEOUT); - transmit_invite(p, SIP_NOTIFY, 0, 2); + transmit_invite(p, SIP_NOTIFY, 0, 2, NULL); astman_send_ack(s, m, "Notify Sent"); ast_variables_destroy(vars); @@ -12438,13 +13449,13 @@ static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, c the dialplan, so that the outbound call also is a sips: call or encrypted IAX2 call. If that's not available, the call should FAIL. */ -static int get_destination(struct sip_pvt *p, struct sip_request *oreq) +static int get_destination(struct sip_pvt *p, struct sip_request *oreq, int *cc_recall_core_id) { char tmp[256] = "", *uri, *domain, *dummy = NULL; char tmpf[256] = "", *from = NULL; struct sip_request *req; char *decoded_uri; - + req = oreq; if (!req) req = &p->initreq; @@ -12453,7 +13464,7 @@ static int get_destination(struct sip_pvt *p, struct sip_request *oreq) if (req->rlPart2) ast_copy_string(tmp, REQ_OFFSET_TO_STR(req, rlPart2), sizeof(tmp)); - uri = get_in_brackets(tmp); + uri = ast_strdupa(get_in_brackets(tmp)); if (parse_uri(uri, "sip:,sips:", &uri, &dummy, &domain, &dummy, NULL)) { ast_log(LOG_WARNING, "Not a SIP header (%s)?\n", uri); @@ -12510,6 +13521,7 @@ static int get_destination(struct sip_pvt *p, struct sip_request *oreq) char hint[AST_MAX_EXTENSION]; return (ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, p->context, p->exten) ? 0 : -1); } else { + struct ast_cc_agent *agent; decoded_uri = ast_strdupa(uri); ast_uri_decode(decoded_uri); /* Check the dialplan for the username part of the request URI, @@ -12522,6 +13534,22 @@ static int get_destination(struct sip_pvt *p, struct sip_request *oreq) if (!oreq) ast_string_field_set(p, exten, decoded_uri); return 0; + } else if ((agent = find_sip_cc_agent_by_notify_uri(tmp))) { + struct sip_cc_agent_pvt *agent_pvt = agent->private_data; + /* This is a CC recall. We can set p's extension to the exten from + * the original INVITE + */ + ast_string_field_set(p, exten, agent_pvt->original_exten); + /* And we need to let the CC core know that the caller is attempting + * his recall + */ + ast_cc_agent_recalling(agent->core_id, "SIP caller %s is attempting recall", + agent->device_name); + if (cc_recall_core_id) { + *cc_recall_core_id = agent->core_id; + } + ao2_ref(agent, -1); + return 0; } } @@ -13033,6 +14061,7 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of, ast_string_field_set(p, engine, peer->engine); p->disallowed_methods = peer->disallowed_methods; set_pvt_allowed_methods(p, req); + ast_cc_copy_config_params(p->cc_params, peer->cc_params); if (peer->callingpres) /* Peer calling pres setting will override RPID */ p->callingpres = peer->callingpres; if (peer->maxms && peer->lastms) @@ -15981,7 +17010,7 @@ static char *sip_cli_notify(struct ast_cli_entry *e, int cmd, struct ast_cli_arg ast_cli(a->fd, "Sending NOTIFY of type '%s' to '%s'\n", a->argv[2], a->argv[i]); dialog_ref(p, "bump the count of p, which transmit_sip_request will decrement."); sip_scheddestroy(p, SIP_TRANS_TIMEOUT); - transmit_invite(p, SIP_NOTIFY, 0, 2); + transmit_invite(p, SIP_NOTIFY, 0, 2, NULL); } return CLI_SUCCESS; @@ -16061,7 +17090,7 @@ static int do_proxy_auth(struct sip_pvt *p, struct sip_request *req, enum sip_au /* Now we have a reply digest */ p->options->auth = digest; p->options->authheader = respheader; - return transmit_invite(p, sipmethod, sipmethod == SIP_INVITE, init); + return transmit_invite(p, sipmethod, sipmethod == SIP_INVITE, init, NULL); } /*! \brief reply to authentication for outbound registrations @@ -16698,6 +17727,106 @@ static void handle_response_update(struct sip_pvt *p, int resp, const char *rest } } +static void cc_handle_publish_error(struct sip_pvt *pvt, const int resp, struct sip_request *req, struct sip_epa_entry *epa_entry) +{ + struct cc_epa_entry *cc_entry = epa_entry->instance_data; + struct sip_monitor_instance *monitor_instance = ao2_callback(sip_monitor_instances, 0, + find_sip_monitor_instance_by_suspension_entry, epa_entry); + const char *min_expires; + + if (!monitor_instance) { + ast_log(LOG_WARNING, "Can't find monitor_instance corresponding to epa_entry %p.\n", epa_entry); + return; + } + + if (resp != 423) { + ast_cc_monitor_failed(cc_entry->core_id, monitor_instance->device_name, + "Received error response to our PUBLISH"); + ao2_ref(monitor_instance, -1); + return; + } + + /* Allrighty, the other end doesn't like our Expires value. They think it's + * too small, so let's see if they've provided a more sensible value. If they + * haven't, then we'll just double our Expires value and see if they like that + * instead. + * + * XXX Ideally this logic could be placed into its own function so that SUBSCRIBE, + * PUBLISH, and REGISTER could all benefit from the same shared code. + */ + min_expires = get_header(req, "Min-Expires"); + if (ast_strlen_zero(min_expires)) { + pvt->expiry *= 2; + if (pvt->expiry < 0) { + /* You dork! You overflowed! */ + ast_cc_monitor_failed(cc_entry->core_id, monitor_instance->device_name, + "PUBLISH expiry overflowed"); + ao2_ref(monitor_instance, -1); + return; + } + } else if (sscanf(min_expires, "%d", &pvt->expiry) != 1) { + ast_cc_monitor_failed(cc_entry->core_id, monitor_instance->device_name, + "Min-Expires has non-numeric value"); + ao2_ref(monitor_instance, -1); + return; + } + /* At this point, we have most certainly changed pvt->expiry, so try transmitting + * again + */ + transmit_invite(pvt, SIP_PUBLISH, FALSE, 0, NULL); + ao2_ref(monitor_instance, -1); +} + +static void handle_response_publish(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno) +{ + struct sip_epa_entry *epa_entry = p->epa_entry; + const char *etag = get_header(req, "Sip-ETag"); + + ast_assert(epa_entry != NULL); + + if (resp == 401 || resp == 407) { + ast_string_field_set(p, theirtag, NULL); + if (p->options) { + p->options->auth_type = (resp == 401 ? WWW_AUTH : PROXY_AUTH); + } + if ((p->authtries == MAX_AUTHTRIES) || do_proxy_auth(p, req, resp, SIP_PUBLISH, 0)) { + ast_log(LOG_NOTICE, "Failed to authenticate on PUBLISH to '%s'\n", get_header(&p->initreq, "From")); + pvt_set_needdestroy(p, "Failed to authenticate on PUBLISH"); + sip_alreadygone(p); + } + return; + } + + if (resp == 501 || resp == 405) { + mark_method_unallowed(&p->allowed_methods, SIP_PUBLISH); + } + + if (resp == 200) { + p->authtries = 0; + /* If I've read section 6, item 6 of RFC 3903 correctly, + * an ESC will only generate a new etag when it sends a 200 OK + */ + if (!ast_strlen_zero(etag)) { + ast_copy_string(epa_entry->entity_tag, etag, sizeof(epa_entry->entity_tag)); + } + /* The nominal case. Everything went well. Everybody is happy. + * Each EPA will have a specific action to take as a result of this + * development, so ... callbacks! + */ + if (epa_entry->static_data->handle_ok) { + epa_entry->static_data->handle_ok(p, req, epa_entry); + } + } else { + /* Rather than try to make individual callbacks for each error + * type, there is just a single error callback. The callback + * can distinguish between error messages and do what it needs to + */ + if (epa_entry->static_data->handle_error) { + epa_entry->static_data->handle_error(p, resp, req, epa_entry); + } + } +} + /*! \brief Handle SIP response to INVITE dialogue */ static void handle_response_invite(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno) { @@ -16769,6 +17898,7 @@ static void handle_response_invite(struct sip_pvt *p, int resp, const char *rest connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; ast_channel_queue_connected_line_update(p->owner, &connected); } + sip_handle_cc(p, req, AST_CC_CCNR); ast_queue_control(p->owner, AST_CONTROL_RINGING); if (p->owner->_state != AST_STATE_UP) { ast_setstate(p->owner, AST_STATE_RINGING); @@ -16794,6 +17924,7 @@ static void handle_response_invite(struct sip_pvt *p, int resp, const char *rest struct ast_party_redirecting redirecting = {{0,},}; change_redirecting_information(p, req, &redirecting, FALSE); ast_channel_queue_redirecting_update(p->owner, &redirecting); + sip_handle_cc(p, req, AST_CC_CCNR); } check_pendings(p); break; @@ -16811,6 +17942,7 @@ static void handle_response_invite(struct sip_pvt *p, int resp, const char *rest connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; ast_channel_queue_connected_line_update(p->owner, &connected); } + sip_handle_cc(p, req, AST_CC_CCNR); } if (find_sdp(req)) { if (p->invitestate != INV_CANCELLED) @@ -17569,6 +18701,11 @@ static void handle_response(struct sip_pvt *p, int resp, const char *rest, struc need to hang around for something more "definitive" */ if (resp != 100) handle_response_peerpoke(p, resp, req); + } else if (sipmethod == SIP_PUBLISH) { + /* SIP PUBLISH transcends this morass of doodoo and instead + * we just always call the response handler. Good gravy! + */ + handle_response_publish(p, resp, rest, req, seqno); } else if (ast_test_flag(&p->flags[0], SIP_OUTGOING)) { switch(resp) { case 100: /* 100 Trying */ @@ -17763,8 +18900,10 @@ static void handle_response(struct sip_pvt *p, int resp, const char *rest, struc case 486: /* Busy here */ case 600: /* Busy everywhere */ case 603: /* Decline */ - if (p->owner) + if (p->owner) { + sip_handle_cc(p, req, AST_CC_CCBS); ast_queue_control(p->owner, AST_CONTROL_BUSY); + } break; case 482: /*! \note SIP is incapable of performing a hairpin call, which @@ -18214,6 +19353,66 @@ static const char *gettag(const struct sip_request *req, const char *header, cha return NULL; } +static int handle_cc_notify(struct sip_pvt *pvt, struct sip_request *req) +{ + struct sip_monitor_instance *monitor_instance = ao2_callback(sip_monitor_instances, 0, + find_sip_monitor_instance_by_subscription_pvt, pvt); + const char *status = get_body(req, "cc-state", ':'); + struct cc_epa_entry *cc_entry; + char *uri; + + if (!monitor_instance) { + transmit_response(pvt, "400 Bad Request", req); + return -1; + } + + if (ast_strlen_zero(status)) { + ao2_ref(monitor_instance, -1); + transmit_response(pvt, "400 Bad Request", req); + return -1; + } + + if (!strcmp(status, "queued")) { + /* We've been told that we're queued. This is the endpoint's way of telling + * us that it has accepted our CC request. We need to alert the core of this + * development + */ + ast_cc_monitor_request_acked(monitor_instance->core_id, "SIP endpoint %s accepted request", monitor_instance->device_name); + transmit_response(pvt, "200 OK", req); + ao2_ref(monitor_instance, -1); + return 0; + } + + /* It's open! Yay! */ + uri = get_body(req, "cc-URI", ':'); + if (ast_strlen_zero(uri)) { + uri = get_in_brackets((char *)get_header(req, "From")); + } + + ast_string_field_set(monitor_instance, notify_uri, uri); + if (monitor_instance->suspension_entry) { + cc_entry = monitor_instance->suspension_entry->instance_data; + if (cc_entry->current_state == CC_CLOSED) { + /* If we've created a suspension entry and the current state is closed, then that means + * we got a notice from the CC core earlier to suspend monitoring, but because this particular + * call leg had not yet notified us that it was ready for recall, it meant that we + * could not yet send a PUBLISH. Now, however, we can. + */ + construct_pidf_body(CC_CLOSED, monitor_instance->suspension_entry->body, + sizeof(monitor_instance->suspension_entry->body), monitor_instance->peername); + transmit_publish(monitor_instance->suspension_entry, SIP_PUBLISH_INITIAL, monitor_instance->notify_uri); + } else { + ast_cc_monitor_callee_available(monitor_instance->core_id, "SIP monitored callee has become available"); + } + } else { + ast_cc_monitor_callee_available(monitor_instance->core_id, "SIP monitored callee has become available"); + } + ao2_ref(monitor_instance, -1); + transmit_response(pvt, "200 OK", req); + + return 0; +} + /*! \brief Handle incoming notifications */ static int handle_request_notify(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int seqno, const char *e) { @@ -18376,6 +19575,8 @@ static int handle_request_notify(struct sip_pvt *p, struct sip_request *req, str /* Used by Sipura/Linksys for NAT pinhole, * just confirm that we recieved the packet. */ transmit_response(p, "200 OK", req); + } else if (!strcmp(event, "call-completion")) { + res = handle_cc_notify(p, req); } else { /* We don't understand this event. */ transmit_response(p, "489 Bad event", req); @@ -18411,7 +19612,7 @@ static int handle_request_options(struct sip_pvt *p, struct sip_request *req) return 0; } - res = get_destination(p, req); + res = get_destination(p, req, NULL); build_contact(p); if (ast_strlen_zero(p->context)) @@ -19271,6 +20472,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int /* This is a new invite */ /* Handle authentication if this is our first invite */ struct ast_party_redirecting redirecting = {{0,},}; + int cc_recall_core_id = -1; set_pvt_allowed_methods(p, req); res = check_user(p, req, SIP_INVITE, e, XMIT_RELIABLE, sin); if (res == AUTH_CHALLENGE_SENT) { @@ -19338,7 +20540,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int res = 0; goto request_invite_cleanup; } - gotdest = get_destination(p, NULL); /* Get destination right away */ + gotdest = get_destination(p, NULL, &cc_recall_core_id); /* Get destination right away */ change_redirecting_information(p, req, &redirecting, FALSE); /*Will return immediately if no Diversion header is present */ extract_uri(p, req); /* Get the Contact URI */ build_contact(p); /* Build our contact header */ @@ -19376,6 +20578,10 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int make_our_tag(p->tag, sizeof(p->tag)); /* First invitation - create the channel */ c = sip_new(p, AST_STATE_DOWN, S_OR(p->peername, NULL), NULL); + if (cc_recall_core_id != -1) { + ast_setup_cc_recall_datastore(c, cc_recall_core_id); + ast_cc_agent_set_interfaces_chanvar(c); + } *recount = 1; /* Save Record-Route for any later requests we make on this dialogue */ @@ -20454,6 +21660,496 @@ static int handle_request_message(struct sip_pvt *p, struct sip_request *req) return 1; } +static enum sip_publish_type determine_sip_publish_type(struct sip_request *req, const char * const event, const char * const etag, const char * const expires, int *expires_int) +{ + int etag_present = !ast_strlen_zero(etag); + int body_present = req->lines > 0; + + ast_assert(expires_int != NULL); + + if (ast_strlen_zero(expires)) { + /* Section 6, item 4, second bullet point of RFC 3903 says to + * use a locally-configured default expiration if none is provided + * in the request + */ + *expires_int = DEFAULT_PUBLISH_EXPIRES; + } else if (sscanf(expires, "%30d", expires_int) != 1) { + return SIP_PUBLISH_UNKNOWN; + } + + if (*expires_int == 0) { + return SIP_PUBLISH_REMOVE; + } else if (!etag_present && body_present) { + return SIP_PUBLISH_INITIAL; + } else if (etag_present && !body_present) { + return SIP_PUBLISH_REFRESH; + } else if (etag_present && body_present) { + return SIP_PUBLISH_MODIFY; + } + + return SIP_PUBLISH_UNKNOWN; +} + +#ifdef HAVE_LIBXML2 +static void get_pidf_body(struct sip_request *req, char *pidf_body, size_t size) +{ + int i; + struct ast_str *str = ast_str_alloca(size); + for (i = 0; i < req->lines; ++i) { + ast_str_append(&str, 0, "%s", REQ_OFFSET_TO_STR(req, line[i])); + } + ast_copy_string(pidf_body, ast_str_buffer(str), size); +} + +static int pidf_validate_tuple(struct ast_xml_node *tuple_node) +{ + const char *id; + int status_found = FALSE; + struct ast_xml_node *tuple_children; + struct ast_xml_node *tuple_children_iterator; + /* Tuples have to have an id attribute or they're invalid */ + if (!(id = ast_xml_get_attribute(tuple_node, "id"))) { + ast_log(LOG_WARNING, "Tuple XML element has no attribute 'id'\n"); + return FALSE; + } + /* We don't care what it actually is, just that it's there */ + ast_xml_free_attr(id); + /* This is a tuple. It must have a status element */ + if (!(tuple_children = ast_xml_node_get_children(tuple_node))) { + /* The tuple has no children. It sucks */ + ast_log(LOG_WARNING, "Tuple XML element has no child elements\n"); + return FALSE; + } + for (tuple_children_iterator = tuple_children; tuple_children_iterator; + tuple_children_iterator = ast_xml_node_get_next(tuple_children_iterator)) { + /* Similar to the wording used regarding tuples, the status element should appear + * first. However, we will once again relax things and accept the status at any + * position. We will enforce that only a single status element can be present. + */ + if (strcmp(ast_xml_node_get_name(tuple_children_iterator), "status")) { + /* Not the status, we don't care */ + continue; + } + if (status_found == TRUE) { + /* THERE CAN BE ONLY ONE!!! */ + ast_log(LOG_WARNING, "Multiple status elements found in tuple. Only one allowed\n"); + return FALSE; + } + status_found = TRUE; + } + return status_found; +} + + +static int pidf_validate_presence(struct ast_xml_doc *doc) +{ + struct ast_xml_node *presence_node = ast_xml_get_root(doc); + struct ast_xml_node *child_nodes; + struct ast_xml_node *node_iterator; + struct ast_xml_ns *ns; + const char *entity; + const char *namespace; + const char presence_namespace[] = "urn:ietf:params:xml:ns:pidf"; + + if (!presence_node) { + ast_log(LOG_WARNING, "Unable to retrieve root node of the XML document\n"); + return FALSE; + } + /* Okay, we managed to open the document! YAY! Now, let's start making sure it's all PIDF-ified + * correctly. + */ + if (strcmp(ast_xml_node_get_name(presence_node), "presence")) { + ast_log(LOG_WARNING, "Root node of PIDF document is not 'presence'. Invalid\n"); + return FALSE; + } + + /* The presence element must have an entity attribute and an xmlns attribute. Furthermore + * the xmlns attribute must be "urn:ietf:params:xml:ns:pidf" + */ + if (!(entity = ast_xml_get_attribute(presence_node, "entity"))) { + ast_log(LOG_WARNING, "Presence element of PIDF document has no 'entity' attribute\n"); + return FALSE; + } + /* We're not interested in what the entity is, just that it exists */ + ast_xml_free_attr(entity); + + if (!(ns = ast_xml_find_namespace(doc, presence_node, NULL))) { + ast_log(LOG_WARNING, "Couldn't find default namespace...\n"); + return FALSE; + } + + namespace = ast_xml_get_ns_href(ns); + if (ast_strlen_zero(namespace) || strcmp(namespace, presence_namespace)) { + ast_log(LOG_WARNING, "PIDF document has invalid namespace value %s\n", namespace); + return FALSE; + } + + if (!(child_nodes = ast_xml_node_get_children(presence_node))) { + ast_log(LOG_WARNING, "PIDF document has no elements as children of 'presence'. Invalid\n"); + return FALSE; + } + + /* Check for tuple elements. RFC 3863 says that PIDF documents can have any number of + * tuples, including 0. The big thing here is that if there are tuple elements present, + * they have to have a single status element within. + * + * The RFC is worded such that tuples should appear as the first elements as children of + * the presence element. However, we'll be accepting of documents which may place other elements + * before the tuple(s). + */ + for (node_iterator = child_nodes; node_iterator; + node_iterator = ast_xml_node_get_next(node_iterator)) { + if (strcmp(ast_xml_node_get_name(node_iterator), "tuple")) { + /* Not a tuple. We don't give a rat's hind quarters */ + continue; + } + if (pidf_validate_tuple(node_iterator) == FALSE) { + ast_log(LOG_WARNING, "Unable to validate tuple\n"); + return FALSE; + } + } + + return TRUE; +} + +/*! + * \brief Makes sure that body is properly formatted PIDF + * + * Specifically, we check that the document has a "presence" element + * at the root and that within that, there is at least one "tuple" element + * that contains a "status" element. + * + * XXX This function currently assumes a default namespace is used. Of course + * if you're not using a default namespace, you're probably a stupid jerk anyway. + * + * \param req The SIP request to check + * \param[out] pidf_doc The validated PIDF doc. + * \retval FALSE The XML was malformed or the basic PIDF structure was marred + * \retval TRUE The PIDF document is of a valid format + */ +static int sip_pidf_validate(struct sip_request *req, struct ast_xml_doc **pidf_doc) +{ + struct ast_xml_doc *doc; + int content_length; + const char *content_length_str = get_header(req, "Content-Length"); + const char *content_type = get_header(req, "Content-Type"); + char pidf_body[SIPBUFSIZE]; + int res; + + if (ast_strlen_zero(content_type) || strcmp(content_type, "application/pidf+xml")) { + ast_log(LOG_WARNING, "Content type is not PIDF\n"); + return FALSE; + } + + if (ast_strlen_zero(content_length_str)) { + ast_log(LOG_WARNING, "No content length. Can't determine bounds of PIDF document\n"); + return FALSE; + } + + if (sscanf(content_length_str, "%30d", &content_length) != 1) { + ast_log(LOG_WARNING, "Invalid content length provided\n"); + return FALSE; + } + + if (content_length > sizeof(pidf_body)) { + ast_log(LOG_WARNING, "Content length of PIDF document truncated to %d bytes\n", (int) sizeof(pidf_body)); + content_length = sizeof(pidf_body); + } + + get_pidf_body(req, pidf_body, content_length); + + if (!(doc = ast_xml_read_memory(pidf_body, content_length))) { + ast_log(LOG_WARNING, "Unable to open XML PIDF document. Is it malformed?\n"); + return FALSE; + } + + res = pidf_validate_presence(doc); + if (res == TRUE) { + *pidf_doc = doc; + } else { + ast_xml_close(doc); + } + return res; +} + +static int cc_esc_publish_handler(struct sip_pvt *pvt, struct sip_request *req, struct event_state_compositor *esc, struct sip_esc_entry *esc_entry) +{ + const char *uri = REQ_OFFSET_TO_STR(req, rlPart2); + struct ast_cc_agent *agent = find_sip_cc_agent_by_notify_uri(uri); + struct sip_cc_agent_pvt *agent_pvt; + struct ast_xml_doc *pidf_doc = NULL; + const char *basic_status = NULL; + struct ast_xml_node *presence_node; + struct ast_xml_node *presence_children; + struct ast_xml_node *tuple_node; + struct ast_xml_node *tuple_children; + struct ast_xml_node *status_node; + struct ast_xml_node *status_children; + struct ast_xml_node *basic_node; + int res = 0; + + if (!agent) { + ast_log(LOG_WARNING, "Could not find agent using uri '%s'\n", uri); + transmit_response(pvt, "412 Conditional Request Failed", req); + return -1; + } + + agent_pvt = agent->private_data; + + if (sip_pidf_validate(req, &pidf_doc) == FALSE) { + res = -1; + goto cc_publish_cleanup; + } + + /* It's important to note that the PIDF validation routine has no knowledge + * of what we specifically want in this instance. A valid PIDF document could + * have no tuples, or it could have tuples whose status element has no basic + * element contained within. While not violating the PIDF spec, these are + * insufficient for our needs in this situation + */ + presence_node = ast_xml_get_root(pidf_doc); + if (!(presence_children = ast_xml_node_get_children(presence_node))) { + ast_log(LOG_WARNING, "No tuples within presence element.\n"); + res = -1; + goto cc_publish_cleanup; + } + + if (!(tuple_node = ast_xml_find_element(presence_children, "tuple", NULL, NULL))) { + ast_log(LOG_NOTICE, "Couldn't find tuple node?\n"); + res = -1; + goto cc_publish_cleanup; + } + + /* We already made sure that the tuple has a status node when we validated the PIDF + * document earlier. So there's no need to enclose this operation in an if statement. + */ + tuple_children = ast_xml_node_get_children(tuple_node); + status_node = ast_xml_find_element(tuple_children, "status", NULL, NULL); + + if (!(status_children = ast_xml_node_get_children(status_node))) { + ast_log(LOG_WARNING, "No basic elements within status element.\n"); + res = -1; + goto cc_publish_cleanup; + } + + if (!(basic_node = ast_xml_find_element(status_children, "basic", NULL, NULL))) { + ast_log(LOG_WARNING, "Couldn't find basic node?\n"); + res = -1; + goto cc_publish_cleanup; + } + + basic_status = ast_xml_get_text(basic_node); + + if (ast_strlen_zero(basic_status)) { + ast_log(LOG_NOTICE, "NOthing in basic node?\n"); + res = -1; + goto cc_publish_cleanup; + } + + if (!strcmp(basic_status, "open")) { + agent_pvt->is_available = TRUE; + ast_cc_agent_caller_available(agent->core_id, "Received PUBLISH stating SIP caller %s is available", + agent->device_name); + } else if (!strcmp(basic_status, "closed")) { + agent_pvt->is_available = FALSE; + ast_cc_agent_caller_busy(agent->core_id, "Received PUBLISH stating SIP caller %s is busy", + agent->device_name); + } else { + ast_log(LOG_NOTICE, "Invalid content in basic element: %s\n", basic_status); + } + +cc_publish_cleanup: + if (basic_status) { + ast_xml_free_text(basic_status); + } + if (pidf_doc) { + ast_xml_close(pidf_doc); + } + ao2_ref(agent, -1); + if (res) { + transmit_response(pvt, "400 Bad Request", req); + } + return res; +} + +#endif /* HAVE_LIBXML2 */ + +static int handle_sip_publish_initial(struct sip_pvt *p, struct sip_request *req, struct event_state_compositor *esc, const int expires) +{ + struct sip_esc_entry *esc_entry = create_esc_entry(esc, req, expires); + int res = 0; + + if (!esc_entry) { + transmit_response(p, "503 Internal Server Failure", req); + return -1; + } + + if (esc->callbacks->initial_handler) { + res = esc->callbacks->initial_handler(p, req, esc, esc_entry); + } + + if (!res) { + transmit_response_with_sip_etag(p, "200 OK", req, esc_entry, 0); + } + + ao2_ref(esc_entry, -1); + return res; +} + +static int handle_sip_publish_refresh(struct sip_pvt *p, struct sip_request *req, struct event_state_compositor *esc, const char * const etag, const int expires) +{ + struct sip_esc_entry *esc_entry = get_esc_entry(etag, esc); + int expires_ms = expires * 1000; + int res = 0; + + if (!esc_entry) { + transmit_response(p, "412 Conditional Request Failed", req); + return -1; + } + + AST_SCHED_REPLACE_UNREF(esc_entry->sched_id, sched, expires_ms, publish_expire, esc_entry, + ao2_ref(_data, -1), + ao2_ref(esc_entry, -1), + ao2_ref(esc_entry, +1)); + + if (esc->callbacks->refresh_handler) { + res = esc->callbacks->refresh_handler(p, req, esc, esc_entry); + } + + if (!res) { + transmit_response_with_sip_etag(p, "200 OK", req, esc_entry, 1); + } + + ao2_ref(esc_entry, -1); + return res; +} + +static int handle_sip_publish_modify(struct sip_pvt *p, struct sip_request *req, struct event_state_compositor *esc, const char * const etag, const int expires) +{ + struct sip_esc_entry *esc_entry = get_esc_entry(etag, esc); + int expires_ms = expires * 1000; + int res = 0; + + if (!esc_entry) { + transmit_response(p, "412 Conditional Request Failed", req); + return -1; + } + + AST_SCHED_REPLACE_UNREF(esc_entry->sched_id, sched, expires_ms, publish_expire, esc_entry, + ao2_ref(_data, -1), + ao2_ref(esc_entry, -1), + ao2_ref(esc_entry, +1)); + + if (esc->callbacks->modify_handler) { + res = esc->callbacks->modify_handler(p, req, esc, esc_entry); + } + + if (!res) { + transmit_response_with_sip_etag(p, "200 OK", req, esc_entry, 1); + } + + ao2_ref(esc_entry, -1); + return res; +} + +static int handle_sip_publish_remove(struct sip_pvt *p, struct sip_request *req, struct event_state_compositor *esc, const char * const etag) +{ + struct sip_esc_entry *esc_entry = get_esc_entry(etag, esc); + int res = 0; + + if (!esc_entry) { + transmit_response(p, "412 Conditional Request Failed", req); + return -1; + } + + AST_SCHED_DEL(sched, esc_entry->sched_id); + /* Scheduler's ref of the esc_entry */ + ao2_ref(esc_entry, -1); + + if (esc->callbacks->remove_handler) { + res = esc->callbacks->remove_handler(p, req, esc, esc_entry); + } + + if (!res) { + transmit_response_with_sip_etag(p, "200 OK", req, esc_entry, 1); + } + + /* Ref from finding the esc_entry earlier in function */ + ao2_unlink(esc->compositor, esc_entry); + ao2_ref(esc_entry, -1); + return res; +} + +static int handle_request_publish(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, const int seqno, const char *uri) +{ + const char *etag = get_header(req, "SIP-If-Match"); + const char *event = get_header(req, "Event"); + struct event_state_compositor *esc; + enum sip_publish_type publish_type; + const char *expires_str = get_header(req, "Expires"); + int expires_int; + int auth_result; + int handler_result = -1; + + if (ast_strlen_zero(event)) { + transmit_response(p, "489 Bad Event", req); + return -1; + } + + if (!(esc = get_esc(event))) { + transmit_response(p, "489 Bad Event", req); + return -1; + } + + auth_result = check_user(p, req, SIP_PUBLISH, uri, XMIT_RELIABLE, sin); + if (auth_result == AUTH_CHALLENGE_SENT) { + p->lastinvite = seqno; + return 0; + } else if (auth_result < 0) { + if (auth_result == AUTH_FAKE_AUTH) { + ast_log(LOG_NOTICE, "Sending fake auth rejection for device %s\n", get_header(req, "From")); + transmit_fake_auth_response(p, SIP_INVITE, req, XMIT_RELIABLE); + } else { + ast_log(LOG_NOTICE, "Failed to authenticate device %s\n", get_header(req, "From")); + transmit_response_reliable(p, "403 Forbidden", req); + } + sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); + ast_string_field_set(p, theirtag, NULL); + return 0; + } else if (auth_result == AUTH_SUCCESSFUL && p->lastinvite) { + /* We need to stop retransmitting the 401 */ + __sip_ack(p, p->lastinvite, 1, 0); + } + + publish_type = determine_sip_publish_type(req, event, etag, expires_str, &expires_int); + + /* It is the responsibility of these handlers to formulate any response + * sent for a PUBLISH + */ + switch (publish_type) { + case SIP_PUBLISH_UNKNOWN: + transmit_response(p, "400 Bad Request", req); + break; + case SIP_PUBLISH_INITIAL: + handler_result = handle_sip_publish_initial(p, req, esc, expires_int); + break; + case SIP_PUBLISH_REFRESH: + handler_result = handle_sip_publish_refresh(p, req, esc, etag, expires_int); + break; + case SIP_PUBLISH_MODIFY: + handler_result = handle_sip_publish_modify(p, req, esc, etag, expires_int); + break; + case SIP_PUBLISH_REMOVE: + handler_result = handle_sip_publish_remove(p, req, esc, etag); + break; + default: + transmit_response(p, "400 Impossible Condition", req); + break; + } + + return handler_result; +} + static void add_peer_mwi_subs(struct sip_peer *peer) { struct sip_mailbox *mailbox; @@ -20466,6 +22162,63 @@ static void add_peer_mwi_subs(struct sip_peer *peer) } } +static int handle_cc_subscribe(struct sip_pvt *p, struct sip_request *req) +{ + const char *uri = REQ_OFFSET_TO_STR(req, rlPart2); + char *param_separator; + struct ast_cc_agent *agent; + struct sip_cc_agent_pvt *agent_pvt; + const char *expires_str = get_header(req, "Expires"); + int expires = -1; /* Just need it to be non-zero */ + + if (!ast_strlen_zero(expires_str)) { + sscanf(expires_str, "%d", &expires); + } + + if ((param_separator = strchr(uri, ';'))) { + *param_separator = '\0'; + } + + if (!(agent = find_sip_cc_agent_by_subscribe_uri(uri))) { + if (!expires) { + /* Typically, if a 0 Expires reaches us and we can't find + * the corresponding agent, it means that the CC transaction + * has completed and so the calling side is just trying to + * clean up its subscription. We'll just respond with a + * 200 OK and be done with it + */ + transmit_response(p, "200 OK", req); + return 0; + } + ast_log(LOG_WARNING, "Invalid URI '%s' in CC subscribe\n", uri); + transmit_response(p, "404 Not Found", req); + return -1; + } + + agent_pvt = agent->private_data; + + if (!expires) { + /* We got sent a SUBSCRIBE and found an agent. This means that CC + * is being canceled. + */ + ast_cc_failed(agent->core_id, "CC is being canceled by %s", agent->device_name); + transmit_response(p, "200 OK", req); + ao2_ref(agent, -1); + return 0; + } + + agent_pvt->subscribe_pvt = dialog_ref(p, "SIP CC agent gains reference to subscription dialog"); + ast_cc_agent_accept_request(agent->core_id, "SIP caller %s has requested CC via SUBSCRIBE", + agent->device_name); + p->subscribed = CALL_COMPLETION; + + /* We don't send a response here. That is done in the agent's ack callback or in the + * agent destructor, should a failure occur before we have responded + */ + ao2_ref(agent, -1); + return 0; +} + /*! \brief Handle incoming SUBSCRIBE request */ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int seqno, const char *e) { @@ -20578,9 +22331,9 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, return 0; } - if (strcmp(event, "message-summary")) { + if (strcmp(event, "message-summary") && strcmp(event, "call-completion")) { /* Get destination right away */ - gotdest = get_destination(p, NULL); + gotdest = get_destination(p, NULL, NULL); } /* Get full contact header - this needs to be used as a request URI in NOTIFY's */ @@ -20685,6 +22438,8 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, unref_peer(p->relatedpeer, "Unref previously stored relatedpeer ptr"); p->relatedpeer = ref_peer(authpeer, "setting dialog's relatedpeer pointer"); /* already refcounted...Link from pvt to peer UH- should this be dialog_ref()? */ /* Do not release authpeer here */ + } else if (!strcmp(event, "call-completion")) { + handle_cc_subscribe(p, req); } else { /* At this point, Asterisk does not understand the specified event */ transmit_response(p, "489 Bad Event", req); ast_debug(2, "Received SIP subscribe for unknown event package: %s\n", event); @@ -20695,7 +22450,7 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, } /* Add subscription for extension state from the PBX core */ - if (p->subscribed != MWI_NOTIFICATION && !resubscribe) { + if (p->subscribed != MWI_NOTIFICATION && p->subscribed != CALL_COMPLETION && !resubscribe) { if (p->stateid > -1) { ast_extension_state_del(p->stateid, cb_extensionstate); /* we need to dec the refcount, now that the extensionstate is removed */ @@ -20716,10 +22471,13 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, p->expiry = min_expiry; if (sipdebug) { - if (p->subscribed == MWI_NOTIFICATION && p->relatedpeer) + if (p->subscribed == MWI_NOTIFICATION && p->relatedpeer) { ast_debug(2, "Adding subscription for mailbox notification - peer %s\n", p->relatedpeer->name); - else + } else if (p->subscribed == CALL_COMPLETION) { + ast_debug(2, "Adding CC subscription for peer %s\n", p->username); + } else { ast_debug(2, "Adding subscription for extension %s context %s for peer %s\n", p->exten, p->context, p->username); + } } if (p->autokillid > -1 && sip_cancel_destroy(p)) /* Remove subscription expiry for renewals */ ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n"); @@ -20734,7 +22492,7 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, sip_send_mwi_to_peer(p->relatedpeer, NULL, 0); ao2_unlock(p->relatedpeer); } - } else { + } else if (p->subscribed != CALL_COMPLETION) { struct sip_pvt *p_old; if ((firststate = ast_extension_state(NULL, p->context, p->exten)) < 0) { @@ -21005,7 +22763,7 @@ static int handle_incoming(struct sip_pvt *p, struct sip_request *req, struct so } } - if (!e && (p->method == SIP_INVITE || p->method == SIP_SUBSCRIBE || p->method == SIP_REGISTER || p->method == SIP_NOTIFY)) { + if (!e && (p->method == SIP_INVITE || p->method == SIP_SUBSCRIBE || p->method == SIP_REGISTER || p->method == SIP_NOTIFY || p->method == SIP_PUBLISH)) { transmit_response(p, "400 Bad request", req); sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); return -1; @@ -21031,6 +22789,9 @@ static int handle_incoming(struct sip_pvt *p, struct sip_request *req, struct so case SIP_MESSAGE: res = handle_request_message(p, req); break; + case SIP_PUBLISH: + res = handle_request_publish(p, req, sin, seqno, e); + break; case SIP_SUBSCRIBE: res = handle_request_subscribe(p, req, sin, seqno, e); break; @@ -22001,7 +23762,7 @@ static void proc_422_rsp(struct sip_pvt *p, struct sip_request *rsp) return; } p->stimer->st_interval = minse; - transmit_invite(p, SIP_INVITE, 1, 2); + transmit_invite(p, SIP_INVITE, 1, 2, NULL); } @@ -22195,9 +23956,9 @@ static int sip_poke_peer(struct sip_peer *peer, int force) ast_set_flag(&p->flags[0], SIP_OUTGOING); #ifdef VOCAL_DATA_HACK ast_copy_string(p->username, "__VOCAL_DATA_SHOULD_READ_THE_SIP_SPEC__", sizeof(p->username)); - xmitres = transmit_invite(p, SIP_INVITE, 0, 2); /* sinks the p refcount */ + xmitres = transmit_invite(p, SIP_INVITE, 0, 2, NULL); /* sinks the p refcount */ #else - xmitres = transmit_invite(p, SIP_OPTIONS, 0, 2); /* sinks the p refcount */ + xmitres = transmit_invite(p, SIP_OPTIONS, 0, 2, NULL); /* sinks the p refcount */ #endif peer->ps = ast_tvnow(); if (xmitres == XMIT_ERROR) { @@ -22336,6 +24097,7 @@ static struct ast_channel *sip_request_call(const char *type, format_t format, c char *md5secret = NULL; char *authname = NULL; char *trans = NULL; + char dialstring[256]; char *remote_address; enum sip_transport transport = 0; struct sockaddr_in remote_address_sin = { .sin_family = AF_INET }; @@ -22369,6 +24131,9 @@ static struct ast_channel *sip_request_call(const char *type, format_t format, c p->outgoing_call = TRUE; + snprintf(dialstring, sizeof(dialstring), "%s/%s", type, dest); + ast_string_field_set(p, dialstring, dialstring); + if (!(p->options = ast_calloc(1, sizeof(*p->options)))) { dialog_unlink_all(p, TRUE, TRUE); dialog_unref(p, "unref dialog p from mem fail"); @@ -23059,6 +24824,11 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str return NULL; } + if (!(peer->cc_params = ast_cc_config_params_init())) { + ao2_t_ref(peer, -1, "failed to allocate cc_params for peer"); + return NULL; + } + if (realtime && !ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS)) { ast_atomic_fetchadd_int(&rpeerobjs, 1); ast_debug(3, "-REALTIME- peer built. Name: %s. Peer objects: %d\n", name, rpeerobjs); @@ -23442,9 +25212,16 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str if (peer->busy_level < 0) { peer->busy_level = 0; } + } else if (ast_cc_is_config_param(v->name)) { + ast_cc_set_param(peer->cc_params, v->name, v->value); } } + if (!can_parse_xml && (ast_get_cc_agent_policy(peer->cc_params) == AST_CC_AGENT_NATIVE)) { + ast_log(LOG_WARNING, "Peer %s has a cc_agent_policy of 'native' but required libxml2 dependency is not installed. Changing policy to 'never'\n", peer->name); + ast_set_cc_agent_policy(peer->cc_params, AST_CC_AGENT_NEVER); + } + /* Note that Timer B is dependent upon T1 and MUST NOT be lower * than T1 * 64, according to RFC 3261, Section 17.1.1.2 */ if (peer->timer_b < peer->timer_t1 * 64) { @@ -24961,6 +26738,15 @@ static int sip_sipredirect(struct sip_pvt *p, const char *dest) return 0; } +static int sip_is_xml_parsable(void) +{ +#ifdef HAVE_LIBXML2 + return TRUE; +#else + return FALSE; +#endif +} + /*! \brief Send a poke to all known peers */ static void sip_poke_all_peers(void) { @@ -25276,6 +27062,7 @@ static int load_module(void) sip_reloadreason = CHANNEL_MODULE_LOAD; + can_parse_xml = sip_is_xml_parsable(); if(reload_config(sip_reloadreason)) /* Load the configuration from sip.conf */ return AST_MODULE_LOAD_DECLINE; @@ -25323,6 +27110,24 @@ static int load_module(void) sip_poke_all_peers(); sip_send_all_registers(); sip_send_all_mwi_subscriptions(); + initialize_escs(); + if (sip_epa_register(&cc_epa_static_data)) { + return AST_MODULE_LOAD_DECLINE; + } + if (can_parse_xml) { + /* SIP CC agents require the ability to parse XML PIDF bodies + * in incoming PUBLISH requests + */ + if (ast_cc_agent_register(&sip_cc_agent_callbacks)) { + return AST_MODULE_LOAD_DECLINE; + } + } + if (ast_cc_monitor_register(&sip_cc_monitor_callbacks)) { + return AST_MODULE_LOAD_DECLINE; + } + if (!(sip_monitor_instances = ao2_container_alloc(37, sip_monitor_instance_hash_fn, sip_monitor_instance_cmp_fn))) { + return AST_MODULE_LOAD_DECLINE; + } /* And start the monitor for the first time */ restart_monitor(); @@ -25433,6 +27238,7 @@ static int unload_module(void) clear_realm_authentication(authl); + destroy_escs(); if (default_tls_cfg.certfile) ast_free(default_tls_cfg.certfile); @@ -25464,6 +27270,8 @@ static int unload_module(void) ast_context_destroy(con, "SIP"); ast_unload_realtime("sipregs"); ast_unload_realtime("sippeers"); + ast_cc_monitor_unregister(&sip_cc_monitor_callbacks); + ast_cc_agent_unregister(&sip_cc_agent_callbacks); sip_unregister_tests(); diff --git a/channels/sig_analog.c b/channels/sig_analog.c index 8e4aa3391..f8b1ede45 100644 --- a/channels/sig_analog.c +++ b/channels/sig_analog.c @@ -169,6 +169,14 @@ static int analog_get_callerid(struct analog_pvt *p, char *name, char *number, e return -1; } +static const char *analog_get_orig_dialstring(struct analog_pvt *p) +{ + if (p->calls->get_orig_dialstring) { + return p->calls->get_orig_dialstring(p->chan_pvt); + } + return ""; +} + static int analog_get_event(struct analog_pvt *p) { if (p->calls->get_event) { @@ -934,6 +942,24 @@ int analog_call(struct analog_pvt *p, struct ast_channel *ast, char *rdest, int ast_setstate(ast, AST_STATE_RINGING); index = analog_get_index(ast, p, 0); if (index > -1) { + struct ast_cc_config_params *cc_params; + + /* This is where the initial ringing frame is queued for an analog call. + * As such, this is a great time to offer CCNR to the caller if it's available. + */ + cc_params = ast_channel_get_cc_config_params(p->subs[index].owner); + if (cc_params) { + switch (ast_get_cc_monitor_policy(cc_params)) { + case AST_CC_MONITOR_NEVER: + break; + case AST_CC_MONITOR_NATIVE: + case AST_CC_MONITOR_ALWAYS: + case AST_CC_MONITOR_GENERIC: + ast_queue_cc_frame(p->subs[index].owner, AST_CC_GENERIC_MONITOR_TYPE, + analog_get_orig_dialstring(p), AST_CC_CCNR, NULL); + break; + } + } ast_queue_control(p->subs[index].owner, AST_CONTROL_RINGING); } break; diff --git a/channels/sig_analog.h b/channels/sig_analog.h index 33b642289..57fc5c1f2 100644 --- a/channels/sig_analog.h +++ b/channels/sig_analog.h @@ -213,6 +213,8 @@ struct analog_callback { void (* const cancel_cidspill)(void *pvt); int (* const confmute)(void *pvt, int mute); void (* const set_pulsedial)(void *pvt, int flag); + + const char *(* const get_orig_dialstring)(void *pvt); }; diff --git a/channels/sig_pri.c b/channels/sig_pri.c index fd3b4111e..5749e89b3 100644 --- a/channels/sig_pri.c +++ b/channels/sig_pri.c @@ -55,11 +55,32 @@ /* define this to send PRI user-user information elements */ #undef SUPPORT_USERUSER -#if 0 -#define DEFAULT_PRI_DEBUG (PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q921_DUMP | PRI_DEBUG_Q921_RAW | PRI_DEBUG_Q921_STATE) -#else -#define DEFAULT_PRI_DEBUG 0 -#endif +#if defined(HAVE_PRI_CCSS) +struct sig_pri_cc_agent_prv { + /*! Asterisk span D channel control structure. */ + struct sig_pri_pri *pri; + /*! CC id value to use with libpri. -1 if invalid. */ + long cc_id; + /*! TRUE if CC has been requested and we are waiting for the response. */ + unsigned char cc_request_response_pending; +}; + +struct sig_pri_cc_monitor_instance { + /*! \brief Asterisk span D channel control structure. */ + struct sig_pri_pri *pri; + /*! CC id value to use with libpri. (-1 if already canceled). */ + long cc_id; + /*! CC core id value. */ + int core_id; + /*! Device name(Channel name less sequence number) */ + char name[1]; +}; + +/*! Upper level agent/monitor type name. */ +static const char *sig_pri_cc_type_name; +/*! Container of sig_pri monitor instances. */ +static struct ao2_container *sig_pri_cc_monitors; +#endif /* defined(HAVE_PRI_CCSS) */ static int pri_matchdigittimeout = 3000; @@ -120,6 +141,45 @@ static void sig_pri_set_digital(struct sig_pri_chan *p, int flag) p->calls->set_digital(p->chan_pvt, flag); } +static const char *sig_pri_get_orig_dialstring(struct sig_pri_chan *p) +{ + if (p->calls->get_orig_dialstring) { + return p->calls->get_orig_dialstring(p->chan_pvt); + } + ast_log(LOG_ERROR, "get_orig_dialstring callback not defined\n"); + return ""; +} + +#if defined(HAVE_PRI_CCSS) +static void sig_pri_make_cc_dialstring(struct sig_pri_chan *p, char *buf, size_t buf_size) +{ + if (p->calls->make_cc_dialstring) { + p->calls->make_cc_dialstring(p->chan_pvt, buf, buf_size); + } else { + ast_log(LOG_ERROR, "make_cc_dialstring callback not defined\n"); + buf[0] = '\0'; + } +} +#endif /* defined(HAVE_PRI_CCSS) */ + +/*! + * \internal + * \brief Reevaluate the PRI span device state. + * \since 1.8 + * + * \param pri Asterisk D channel control structure. + * + * \return Nothing + * + * \note Assumes the pri->lock is already obtained. + */ +static void sig_pri_span_devstate_changed(struct sig_pri_pri *pri) +{ + if (pri->calls->update_span_devstate) { + pri->calls->update_span_devstate(pri); + } +} + /*! * \internal * \brief Set the caller id information in the parent module. @@ -733,6 +793,12 @@ static struct ast_channel *sig_pri_new_ast_channel(struct sig_pri_chan *p, int s pbx_builtin_setvar_helper(c, "TRANSFERCAPABILITY", ast_transfercapability2str(transfercapability)); sig_pri_set_digital(p, 1); } + if (p->pri && !pri_grab(p, p->pri)) { + sig_pri_span_devstate_changed(p->pri); + pri_rel(p->pri); + } else { + ast_log(LOG_WARNING, "Failed to grab PRI!\n"); + } return c; } @@ -1476,6 +1542,615 @@ static void sig_pri_lock_owner(struct sig_pri_pri *pri, int chanpos) } } +#if defined(HAVE_PRI_CCSS) +/*! + * \internal + * \brief Compare the CC agent private data by libpri cc_id. + * \since 1.8 + * + * \param obj pointer to the (user-defined part) of an object. + * \param arg callback argument from ao2_callback() + * \param flags flags from ao2_callback() + * + * \return values are a combination of enum _cb_results. + */ +static int sig_pri_cc_agent_cmp_cc_id(void *obj, void *arg, int flags) +{ + struct ast_cc_agent *agent_1 = obj; + struct sig_pri_cc_agent_prv *agent_prv_1 = agent_1->private_data; + struct sig_pri_cc_agent_prv *agent_prv_2 = arg; + + return (agent_prv_1 && agent_prv_1->pri == agent_prv_2->pri + && agent_prv_1->cc_id == agent_prv_2->cc_id) ? CMP_MATCH | CMP_STOP : 0; +} +#endif /* defined(HAVE_PRI_CCSS) */ + +#if defined(HAVE_PRI_CCSS) +/*! + * \internal + * \brief Find the CC agent by libpri cc_id. + * \since 1.8 + * + * \param pri sig_pri PRI control structure. + * \param cc_id CC record ID to find. + * + * \note + * Since agents are refcounted, and this function returns + * a reference to the agent, it is imperative that you decrement + * the refcount of the agent once you have finished using it. + * + * \retval agent on success. + * \retval NULL not found. + */ +static struct ast_cc_agent *sig_pri_find_cc_agent_by_cc_id(struct sig_pri_pri *pri, long cc_id) +{ + struct sig_pri_cc_agent_prv finder = { + .pri = pri, + .cc_id = cc_id, + }; + + return ast_cc_agent_callback(0, sig_pri_cc_agent_cmp_cc_id, &finder, + sig_pri_cc_type_name); +} +#endif /* defined(HAVE_PRI_CCSS) */ + +#if defined(HAVE_PRI_CCSS) +/*! + * \internal + * \brief Compare the CC monitor instance by libpri cc_id. + * \since 1.8 + * + * \param obj pointer to the (user-defined part) of an object. + * \param arg callback argument from ao2_callback() + * \param flags flags from ao2_callback() + * + * \return values are a combination of enum _cb_results. + */ +static int sig_pri_cc_monitor_cmp_cc_id(void *obj, void *arg, int flags) +{ + struct sig_pri_cc_monitor_instance *monitor_1 = obj; + struct sig_pri_cc_monitor_instance *monitor_2 = arg; + + return (monitor_1->pri == monitor_2->pri + && monitor_1->cc_id == monitor_2->cc_id) ? CMP_MATCH | CMP_STOP : 0; +} +#endif /* defined(HAVE_PRI_CCSS) */ + +#if defined(HAVE_PRI_CCSS) +/*! + * \internal + * \brief Find the CC monitor instance by libpri cc_id. + * \since 1.8 + * + * \param pri sig_pri PRI control structure. + * \param cc_id CC record ID to find. + * + * \note + * Since monitor_instances are refcounted, and this function returns + * a reference to the instance, it is imperative that you decrement + * the refcount of the instance once you have finished using it. + * + * \retval monitor_instance on success. + * \retval NULL not found. + */ +static struct sig_pri_cc_monitor_instance *sig_pri_find_cc_monitor_by_cc_id(struct sig_pri_pri *pri, long cc_id) +{ + struct sig_pri_cc_monitor_instance finder = { + .pri = pri, + .cc_id = cc_id, + }; + + return ao2_callback(sig_pri_cc_monitors, 0, sig_pri_cc_monitor_cmp_cc_id, &finder); +} +#endif /* defined(HAVE_PRI_CCSS) */ + +#if defined(HAVE_PRI_CCSS) +/*! + * \internal + * \brief Destroy the given monitor instance. + * \since 1.8 + * + * \param data Monitor instance to destroy. + * + * \return Nothing + */ +static void sig_pri_cc_monitor_instance_destroy(void *data) +{ + struct sig_pri_cc_monitor_instance *monitor_instance = data; + + if (monitor_instance->cc_id != -1) { + ast_mutex_lock(&monitor_instance->pri->lock); + pri_cc_cancel(monitor_instance->pri->pri, monitor_instance->cc_id); + ast_mutex_unlock(&monitor_instance->pri->lock); + } + monitor_instance->pri->calls->module_unref(); +} +#endif /* defined(HAVE_PRI_CCSS) */ + +#if defined(HAVE_PRI_CCSS) +/*! + * \internal + * \brief Construct a new monitor instance. + * \since 1.8 + * + * \param core_id CC core ID. + * \param pri sig_pri PRI control structure. + * \param cc_id CC record ID. + * \param device_name Name of device (Asterisk channel name less sequence number). + * + * \note + * Since monitor_instances are refcounted, and this function returns + * a reference to the instance, it is imperative that you decrement + * the refcount of the instance once you have finished using it. + * + * \retval monitor_instance on success. + * \retval NULL on error. + */ +static struct sig_pri_cc_monitor_instance *sig_pri_cc_monitor_instance_init(int core_id, struct sig_pri_pri *pri, long cc_id, const char *device_name) +{ + struct sig_pri_cc_monitor_instance *monitor_instance; + + if (!pri->calls->module_ref || !pri->calls->module_unref) { + return NULL; + } + + monitor_instance = ao2_alloc(sizeof(*monitor_instance) + strlen(device_name), + sig_pri_cc_monitor_instance_destroy); + if (!monitor_instance) { + return NULL; + } + + monitor_instance->cc_id = cc_id; + monitor_instance->pri = pri; + monitor_instance->core_id = core_id; + strcpy(monitor_instance->name, device_name); + + pri->calls->module_ref(); + + ao2_link(sig_pri_cc_monitors, monitor_instance); + return monitor_instance; +} +#endif /* defined(HAVE_PRI_CCSS) */ + +#if defined(HAVE_PRI_CCSS) +/*! + * \internal + * \brief Announce to the CC core that protocol CC monitor is available for this call. + * \since 1.8 + * + * \param pri sig_pri PRI control structure. + * \param chanpos Channel position in the span. + * \param cc_id CC record ID. + * \param service CCBS/CCNR indication. + * + * \note Assumes the pri->lock is already obtained. + * \note Assumes the sig_pri_lock_private(pri->pvts[chanpos]) is already obtained. + * \note Assumes the sig_pri_lock_owner(pri, chanpos) is already obtained. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int sig_pri_cc_available(struct sig_pri_pri *pri, int chanpos, long cc_id, enum ast_cc_service_type service) +{ + struct sig_pri_chan *pvt; + struct ast_cc_config_params *cc_params; + struct sig_pri_cc_monitor_instance *monitor; + enum ast_cc_monitor_policies monitor_policy; + int core_id; + int res; + char device_name[AST_CHANNEL_NAME]; + char dialstring[AST_CHANNEL_NAME]; + + pvt = pri->pvts[chanpos]; + + core_id = ast_cc_get_current_core_id(pvt->owner); + if (core_id == -1) { + return -1; + } + + cc_params = ast_channel_get_cc_config_params(pvt->owner); + if (!cc_params) { + return -1; + } + + res = -1; + monitor_policy = ast_get_cc_monitor_policy(cc_params); + switch (monitor_policy) { + case AST_CC_MONITOR_NEVER: + /* CCSS is not enabled. */ + break; + case AST_CC_MONITOR_NATIVE: + case AST_CC_MONITOR_ALWAYS: + /* + * If it is AST_CC_MONITOR_ALWAYS and native fails we will attempt the fallback + * later in the call to sig_pri_cc_generic_check(). + */ + ast_channel_get_device_name(pvt->owner, device_name, sizeof(device_name)); + sig_pri_make_cc_dialstring(pvt, dialstring, sizeof(dialstring)); + monitor = sig_pri_cc_monitor_instance_init(core_id, pri, cc_id, device_name); + if (!monitor) { + break; + } + res = ast_queue_cc_frame(pvt->owner, sig_pri_cc_type_name, dialstring, service, + monitor); + if (res) { + monitor->cc_id = -1; + ao2_unlink(sig_pri_cc_monitors, monitor); + ao2_ref(monitor, -1); + } + break; + case AST_CC_MONITOR_GENERIC: + ast_queue_cc_frame(pvt->owner, AST_CC_GENERIC_MONITOR_TYPE, + sig_pri_get_orig_dialstring(pvt), service, NULL); + /* Say it failed to force caller to cancel native CC. */ + break; + } + return res; +} +#endif /* defined(HAVE_PRI_CCSS) */ + +/*! + * \internal + * \brief Check if generic CC monitor is needed and request it. + * \since 1.8 + * + * \param pri sig_pri PRI control structure. + * \param chanpos Channel position in the span. + * \param service CCBS/CCNR indication. + * + * \note Assumes the pri->lock is already obtained. + * \note Assumes the sig_pri_lock_private(pri->pvts[chanpos]) is already obtained. + * + * \return Nothing + */ +static void sig_pri_cc_generic_check(struct sig_pri_pri *pri, int chanpos, enum ast_cc_service_type service) +{ + struct ast_channel *owner; + struct ast_cc_config_params *cc_params; +#if defined(HAVE_PRI_CCSS) + struct ast_cc_monitor *monitor; + char device_name[AST_CHANNEL_NAME]; +#endif /* defined(HAVE_PRI_CCSS) */ + enum ast_cc_monitor_policies monitor_policy; + int core_id; + + if (!pri->pvts[chanpos]->outgoing) { + /* This is not an outgoing call so it cannot be CC monitor. */ + return; + } + + sig_pri_lock_owner(pri, chanpos); + owner = pri->pvts[chanpos]->owner; + if (!owner) { + return; + } + core_id = ast_cc_get_current_core_id(owner); + if (core_id == -1) { + /* No CC core setup */ + goto done; + } + + cc_params = ast_channel_get_cc_config_params(owner); + if (!cc_params) { + /* Could not get CC config parameters. */ + goto done; + } + +#if defined(HAVE_PRI_CCSS) + ast_channel_get_device_name(owner, device_name, sizeof(device_name)); + monitor = ast_cc_get_monitor_by_recall_core_id(core_id, device_name); + if (monitor) { + /* CC monitor is already present so no need for generic CC. */ + ao2_ref(monitor, -1); + goto done; + } +#endif /* defined(HAVE_PRI_CCSS) */ + + monitor_policy = ast_get_cc_monitor_policy(cc_params); + switch (monitor_policy) { + case AST_CC_MONITOR_NEVER: + /* CCSS is not enabled. */ + break; + case AST_CC_MONITOR_NATIVE: + if (pri->sig == SIG_BRI_PTMP && pri->nodetype == PRI_NETWORK) { + /* Request generic CC monitor. */ + ast_queue_cc_frame(owner, AST_CC_GENERIC_MONITOR_TYPE, + sig_pri_get_orig_dialstring(pri->pvts[chanpos]), service, NULL); + } + break; + case AST_CC_MONITOR_ALWAYS: + if (pri->sig == SIG_BRI_PTMP && pri->nodetype != PRI_NETWORK) { + /* + * Cannot monitor PTMP TE side since this is not defined. + * We are playing the roll of a phone in this case and + * a phone cannot monitor a party over the network without + * protocol help. + */ + break; + } + /* + * We are either falling back or this is a PTMP NT span. + * Request generic CC monitor. + */ + ast_queue_cc_frame(owner, AST_CC_GENERIC_MONITOR_TYPE, + sig_pri_get_orig_dialstring(pri->pvts[chanpos]), service, NULL); + break; + case AST_CC_MONITOR_GENERIC: + if (pri->sig == SIG_BRI_PTMP && pri->nodetype == PRI_NETWORK) { + /* Request generic CC monitor. */ + ast_queue_cc_frame(owner, AST_CC_GENERIC_MONITOR_TYPE, + sig_pri_get_orig_dialstring(pri->pvts[chanpos]), service, NULL); + } + break; + } + +done: + ast_channel_unlock(owner); +} + +#if defined(HAVE_PRI_CCSS) +/*! + * \internal + * \brief The CC link canceled the CC instance. + * \since 1.8 + * + * \param pri sig_pri PRI control structure. + * \param cc_id CC record ID. + * \param is_agent TRUE if the cc_id is for an agent. + * + * \return Nothing + */ +static void sig_pri_cc_link_canceled(struct sig_pri_pri *pri, long cc_id, int is_agent) +{ + if (is_agent) { + struct ast_cc_agent *agent; + + agent = sig_pri_find_cc_agent_by_cc_id(pri, cc_id); + if (!agent) { + return; + } + ast_cc_failed(agent->core_id, "%s agent got canceled by link", + sig_pri_cc_type_name); + ao2_ref(agent, -1); + } else { + struct sig_pri_cc_monitor_instance *monitor; + + monitor = sig_pri_find_cc_monitor_by_cc_id(pri, cc_id); + if (!monitor) { + return; + } + monitor->cc_id = -1; + ast_cc_monitor_failed(monitor->core_id, monitor->name, + "%s monitor got canceled by link", sig_pri_cc_type_name); + ao2_ref(monitor, -1); + } +} +#endif /* defined(HAVE_PRI_CCSS) */ + +/*! + * \internal + * \brief TRUE if PRI event came in on a CIS call. + * \since 1.8 + * + * \param channel PRI encoded span/channel + * + * \retval non-zero if CIS call. + */ +static int sig_pri_is_cis_call(int channel) +{ + return channel != -1 && (channel & PRI_CIS_CALL); +} + +/*! + * \internal + * \brief Handle the CIS associated PRI subcommand events. + * \since 1.8 + * + * \param pri sig_pri PRI control structure. + * \param event_id PRI event id + * \param subcmds Subcommands to process if any. (Could be NULL). + * \param call_rsp libpri opaque call structure to send any responses toward. + * Could be NULL either because it is not available or the call is for the + * dummy call reference. However, this should not be NULL in the cases that + * need to use the pointer to send a response message back. + * + * \note Assumes the pri->lock is already obtained. + * + * \return Nothing + */ +static void sig_pri_handle_cis_subcmds(struct sig_pri_pri *pri, int event_id, + const struct pri_subcommands *subcmds, q931_call *call_rsp) +{ + int index; +#if defined(HAVE_PRI_CCSS) + struct ast_cc_agent *agent; + struct sig_pri_cc_agent_prv *agent_prv; + struct sig_pri_cc_monitor_instance *monitor; +#endif /* defined(HAVE_PRI_CCSS) */ + + if (!subcmds) { + return; + } + for (index = 0; index < subcmds->counter_subcmd; ++index) { + const struct pri_subcommand *subcmd = &subcmds->subcmd[index]; + + switch (subcmd->cmd) { +#if defined(STATUS_REQUEST_PLACE_HOLDER) + case PRI_SUBCMD_STATUS_REQ: + case PRI_SUBCMD_STATUS_REQ_RSP: + /* Ignore for now. */ + break; +#endif /* defined(STATUS_REQUEST_PLACE_HOLDER) */ +#if defined(HAVE_PRI_CCSS) + case PRI_SUBCMD_CC_REQ: + agent = sig_pri_find_cc_agent_by_cc_id(pri, subcmd->u.cc_request.cc_id); + if (!agent) { + pri_cc_cancel(pri->pri, subcmd->u.cc_request.cc_id); + break; + } + if (!ast_cc_request_is_within_limits()) { + if (pri_cc_req_rsp(pri->pri, subcmd->u.cc_request.cc_id, + 5/* queue_full */)) { + pri_cc_cancel(pri->pri, subcmd->u.cc_request.cc_id); + } + ast_cc_failed(agent->core_id, "%s agent system CC queue full", + sig_pri_cc_type_name); + ao2_ref(agent, -1); + break; + } + agent_prv = agent->private_data; + agent_prv->cc_request_response_pending = 1; + if (ast_cc_agent_accept_request(agent->core_id, + "%s caller accepted CC offer.", sig_pri_cc_type_name)) { + agent_prv->cc_request_response_pending = 0; + if (pri_cc_req_rsp(pri->pri, subcmd->u.cc_request.cc_id, + 2/* short_term_denial */)) { + pri_cc_cancel(pri->pri, subcmd->u.cc_request.cc_id); + } + ast_cc_failed(agent->core_id, "%s agent CC core request accept failed", + sig_pri_cc_type_name); + } + ao2_ref(agent, -1); + break; +#endif /* defined(HAVE_PRI_CCSS) */ +#if defined(HAVE_PRI_CCSS) + case PRI_SUBCMD_CC_REQ_RSP: + monitor = sig_pri_find_cc_monitor_by_cc_id(pri, + subcmd->u.cc_request_rsp.cc_id); + if (!monitor) { + pri_cc_cancel(pri->pri, subcmd->u.cc_request_rsp.cc_id); + break; + } + switch (subcmd->u.cc_request_rsp.status) { + case 0:/* success */ + ast_cc_monitor_request_acked(monitor->core_id, + "%s far end accepted CC request", sig_pri_cc_type_name); + break; + case 1:/* timeout */ + ast_verb(2, "core_id:%d %s CC request timeout\n", monitor->core_id, + sig_pri_cc_type_name); + ast_cc_monitor_failed(monitor->core_id, monitor->name, + "%s CC request timeout", sig_pri_cc_type_name); + break; + case 2:/* error */ + ast_verb(2, "core_id:%d %s CC request error: %s\n", monitor->core_id, + sig_pri_cc_type_name, + pri_facility_error2str(subcmd->u.cc_request_rsp.fail_code)); + ast_cc_monitor_failed(monitor->core_id, monitor->name, + "%s CC request error", sig_pri_cc_type_name); + break; + case 3:/* reject */ + ast_verb(2, "core_id:%d %s CC request reject: %s\n", monitor->core_id, + sig_pri_cc_type_name, + pri_facility_reject2str(subcmd->u.cc_request_rsp.fail_code)); + ast_cc_monitor_failed(monitor->core_id, monitor->name, + "%s CC request reject", sig_pri_cc_type_name); + break; + default: + ast_verb(2, "core_id:%d %s CC request unknown status %d\n", + monitor->core_id, sig_pri_cc_type_name, + subcmd->u.cc_request_rsp.status); + ast_cc_monitor_failed(monitor->core_id, monitor->name, + "%s CC request unknown status", sig_pri_cc_type_name); + break; + } + ao2_ref(monitor, -1); + break; +#endif /* defined(HAVE_PRI_CCSS) */ +#if defined(HAVE_PRI_CCSS) + case PRI_SUBCMD_CC_REMOTE_USER_FREE: + monitor = sig_pri_find_cc_monitor_by_cc_id(pri, + subcmd->u.cc_remote_user_free.cc_id); + if (!monitor) { + pri_cc_cancel(pri->pri, subcmd->u.cc_remote_user_free.cc_id); + break; + } + ast_cc_monitor_callee_available(monitor->core_id, + "%s callee has become available", sig_pri_cc_type_name); + ao2_ref(monitor, -1); + break; +#endif /* defined(HAVE_PRI_CCSS) */ +#if defined(HAVE_PRI_CCSS) + case PRI_SUBCMD_CC_B_FREE: + monitor = sig_pri_find_cc_monitor_by_cc_id(pri, + subcmd->u.cc_b_free.cc_id); + if (!monitor) { + pri_cc_cancel(pri->pri, subcmd->u.cc_b_free.cc_id); + break; + } + ast_cc_monitor_party_b_free(monitor->core_id); + ao2_ref(monitor, -1); + break; +#endif /* defined(HAVE_PRI_CCSS) */ +#if defined(HAVE_PRI_CCSS) + case PRI_SUBCMD_CC_STATUS_REQ: + monitor = sig_pri_find_cc_monitor_by_cc_id(pri, + subcmd->u.cc_status_req.cc_id); + if (!monitor) { + pri_cc_cancel(pri->pri, subcmd->u.cc_status_req.cc_id); + break; + } + ast_cc_monitor_status_request(monitor->core_id); + ao2_ref(monitor, -1); + break; +#endif /* defined(HAVE_PRI_CCSS) */ +#if defined(HAVE_PRI_CCSS) + case PRI_SUBCMD_CC_STATUS_REQ_RSP: + agent = sig_pri_find_cc_agent_by_cc_id(pri, subcmd->u.cc_status_req_rsp.cc_id); + if (!agent) { + pri_cc_cancel(pri->pri, subcmd->u.cc_status_req_rsp.cc_id); + break; + } + ast_cc_agent_status_response(agent->core_id, + subcmd->u.cc_status_req_rsp.status ? AST_DEVICE_INUSE + : AST_DEVICE_NOT_INUSE); + ao2_ref(agent, -1); + break; +#endif /* defined(HAVE_PRI_CCSS) */ +#if defined(HAVE_PRI_CCSS) + case PRI_SUBCMD_CC_STATUS: + agent = sig_pri_find_cc_agent_by_cc_id(pri, subcmd->u.cc_status.cc_id); + if (!agent) { + pri_cc_cancel(pri->pri, subcmd->u.cc_status.cc_id); + break; + } + if (subcmd->u.cc_status.status) { + ast_cc_agent_caller_busy(agent->core_id, "%s agent caller is busy", + sig_pri_cc_type_name); + } else { + ast_cc_agent_caller_available(agent->core_id, + "%s agent caller is available", sig_pri_cc_type_name); + } + ao2_ref(agent, -1); + break; +#endif /* defined(HAVE_PRI_CCSS) */ +#if defined(HAVE_PRI_CCSS) + case PRI_SUBCMD_CC_CANCEL: + sig_pri_cc_link_canceled(pri, subcmd->u.cc_cancel.cc_id, + subcmd->u.cc_cancel.is_agent); + break; +#endif /* defined(HAVE_PRI_CCSS) */ +#if defined(HAVE_PRI_CCSS) + case PRI_SUBCMD_CC_STOP_ALERTING: + monitor = sig_pri_find_cc_monitor_by_cc_id(pri, + subcmd->u.cc_stop_alerting.cc_id); + if (!monitor) { + pri_cc_cancel(pri->pri, subcmd->u.cc_stop_alerting.cc_id); + break; + } + ast_cc_monitor_stop_ringing(monitor->core_id); + ao2_ref(monitor, -1); + break; +#endif /* defined(HAVE_PRI_CCSS) */ + default: + ast_debug(2, + "Unknown CIS subcommand(%d) in %s event on span %d.\n", + subcmd->cmd, pri_event2str(event_id), pri->span); + break; + } + } +} + /*! * \internal * \brief Handle the call associated PRI subcommand events. @@ -1647,6 +2322,63 @@ static void sig_pri_handle_subcmds(struct sig_pri_pri *pri, int chanpos, int eve } break; #endif /* defined(HAVE_PRI_CALL_REROUTING) */ +#if defined(HAVE_PRI_CCSS) + case PRI_SUBCMD_CC_AVAILABLE: + sig_pri_lock_owner(pri, chanpos); + owner = pri->pvts[chanpos]->owner; + if (owner) { + enum ast_cc_service_type service; + + switch (event_id) { + case PRI_EVENT_RINGING: + service = AST_CC_CCNR; + break; + case PRI_EVENT_HANGUP_REQ: + /* We will assume that the cause was busy/congestion. */ + service = AST_CC_CCBS; + break; + default: + service = AST_CC_NONE; + break; + } + if (service == AST_CC_NONE + || sig_pri_cc_available(pri, chanpos, subcmd->u.cc_available.cc_id, + service)) { + pri_cc_cancel(pri->pri, subcmd->u.cc_available.cc_id); + } + ast_channel_unlock(owner); + } else { + /* No asterisk channel. */ + pri_cc_cancel(pri->pri, subcmd->u.cc_available.cc_id); + } + break; +#endif /* defined(HAVE_PRI_CCSS) */ +#if defined(HAVE_PRI_CCSS) + case PRI_SUBCMD_CC_CALL: + sig_pri_lock_owner(pri, chanpos); + owner = pri->pvts[chanpos]->owner; + if (owner) { + struct ast_cc_agent *agent; + + agent = sig_pri_find_cc_agent_by_cc_id(pri, subcmd->u.cc_call.cc_id); + if (agent) { + ast_setup_cc_recall_datastore(owner, agent->core_id); + ast_cc_agent_set_interfaces_chanvar(owner); + ast_cc_agent_recalling(agent->core_id, + "%s caller is attempting recall", sig_pri_cc_type_name); + ao2_ref(agent, -1); + } + + ast_channel_unlock(owner); + } + break; +#endif /* defined(HAVE_PRI_CCSS) */ +#if defined(HAVE_PRI_CCSS) + case PRI_SUBCMD_CC_CANCEL: + sig_pri_cc_link_canceled(pri, subcmd->u.cc_cancel.cc_id, + subcmd->u.cc_cancel.is_agent); + break; +#endif /* defined(HAVE_PRI_CCSS) */ default: ast_debug(2, "Unknown call subcommand(%d) in %s event on channel %d/%d on span %d.\n", @@ -1793,6 +2525,7 @@ static int sig_pri_handle_hold(struct sig_pri_pri *pri, pri_event *ev) f.subclass.integer = AST_CONTROL_HOLD; ast_queue_frame(owner, &f); + sig_pri_span_devstate_changed(pri); retval = 0; } @@ -1866,6 +2599,7 @@ static void sig_pri_handle_retrieve(struct sig_pri_pri *pri, pri_event *ev) pri_queue_frame(pri->pvts[chanpos], &f, pri); } sig_pri_unlock_private(pri->pvts[chanpos]); + sig_pri_span_devstate_changed(pri); pri_retrieve_ack(pri->pri, ev->retrieve.call, PVT_TO_CHANNEL(pri->pvts[chanpos])); } @@ -2094,10 +2828,12 @@ static void *pri_dchannel(void *vpri) } pri->resetting = 0; /* Take the channels from inalarm condition */ - for (i = 0; i < pri->numchans; i++) + for (i = 0; i < pri->numchans; i++) { if (pri->pvts[i]) { pri->pvts[i]->inalarm = 0; } + } + sig_pri_span_devstate_changed(pri); break; case PRI_EVENT_DCHAN_DOWN: pri_find_dchan(pri); @@ -2128,6 +2864,7 @@ static void *pri_dchannel(void *vpri) p->inalarm = 1; } } + sig_pri_span_devstate_changed(pri); } break; case PRI_EVENT_RESTART: @@ -2180,6 +2917,11 @@ static void *pri_dchannel(void *vpri) } break; case PRI_EVENT_KEYPAD_DIGIT: + if (sig_pri_is_cis_call(e->digit.channel)) { + sig_pri_handle_cis_subcmds(pri, e->e, e->digit.subcmds, + e->digit.call); + break; + } chanpos = pri_find_principle(pri, e->digit.channel, e->digit.call); if (chanpos < 0) { ast_log(LOG_WARNING, "KEYPAD_DIGITs received on unconfigured channel %d/%d span %d\n", @@ -2210,6 +2952,11 @@ static void *pri_dchannel(void *vpri) break; case PRI_EVENT_INFO_RECEIVED: + if (sig_pri_is_cis_call(e->ring.channel)) { + sig_pri_handle_cis_subcmds(pri, e->e, e->ring.subcmds, + e->ring.call); + break; + } chanpos = pri_find_principle(pri, e->ring.channel, e->ring.call); if (chanpos < 0) { ast_log(LOG_WARNING, "INFO received on unconfigured channel %d/%d span %d\n", @@ -2262,6 +3009,8 @@ static void *pri_dchannel(void *vpri) snprintf(db_answer, sizeof(db_answer), "%s:%u", SRVST_TYPE_OOS, *why); ast_db_put(db_chan_name, SRVST_DBKEY, db_answer); + } else { + sig_pri_span_devstate_changed(pri); } break; case 2: /* out-of-service */ @@ -2271,6 +3020,7 @@ static void *pri_dchannel(void *vpri) snprintf(db_answer, sizeof(db_answer), "%s:%u", SRVST_TYPE_OOS, *why); ast_db_put(db_chan_name, SRVST_DBKEY, db_answer); + sig_pri_span_devstate_changed(pri); break; default: ast_log(LOG_ERROR, "Huh? changestatus is: %d\n", e->service.changestatus); @@ -2301,7 +3051,12 @@ static void *pri_dchannel(void *vpri) pri_destroycall(pri->pri, e->ring.call); break; } - if (e->ring.channel == -1) + if (sig_pri_is_cis_call(e->ring.channel)) { + sig_pri_handle_cis_subcmds(pri, e->e, e->ring.subcmds, + e->ring.call); + break; + } + if (e->ring.channel == -1 || PRI_CHANNEL(e->ring.channel) == 0xFF) chanpos = pri_find_empty_chan(pri, 1); else chanpos = pri_find_principle(pri, e->ring.channel, e->ring.call); @@ -2644,6 +3399,11 @@ static void *pri_dchannel(void *vpri) } break; case PRI_EVENT_RINGING: + if (sig_pri_is_cis_call(e->ringing.channel)) { + sig_pri_handle_cis_subcmds(pri, e->e, e->ringing.subcmds, + e->ringing.call); + break; + } chanpos = pri_find_principle(pri, e->ringing.channel, e->ringing.call); if (chanpos < 0) { ast_log(LOG_WARNING, "Ringing requested on unconfigured channel %d/%d span %d\n", @@ -2658,6 +3418,7 @@ static void *pri_dchannel(void *vpri) sig_pri_handle_subcmds(pri, chanpos, e->e, e->ringing.channel, e->ringing.subcmds, e->ringing.call); + sig_pri_cc_generic_check(pri, chanpos, AST_CC_CCNR); sig_pri_set_echocanceller(pri->pvts[chanpos], 1); pri_queue_control(pri->pvts[chanpos], AST_CONTROL_RINGING, pri); pri->pvts[chanpos]->alerting = 1; @@ -2681,7 +3442,11 @@ static void *pri_dchannel(void *vpri) } break; case PRI_EVENT_PROGRESS: - /* Get chan value if e->e is not PRI_EVNT_RINGING */ + if (sig_pri_is_cis_call(e->proceeding.channel)) { + sig_pri_handle_cis_subcmds(pri, e->e, e->proceeding.subcmds, + e->proceeding.call); + break; + } chanpos = pri_find_principle(pri, e->proceeding.channel, e->proceeding.call); if (chanpos > -1) { sig_pri_lock_private(pri->pvts[chanpos]); @@ -2731,6 +3496,11 @@ static void *pri_dchannel(void *vpri) } break; case PRI_EVENT_PROCEEDING: + if (sig_pri_is_cis_call(e->proceeding.channel)) { + sig_pri_handle_cis_subcmds(pri, e->e, e->proceeding.subcmds, + e->proceeding.call); + break; + } chanpos = pri_find_principle(pri, e->proceeding.channel, e->proceeding.call); if (chanpos > -1) { sig_pri_lock_private(pri->pvts[chanpos]); @@ -2760,6 +3530,17 @@ static void *pri_dchannel(void *vpri) } break; case PRI_EVENT_FACILITY: + if (!e->facility.call || sig_pri_is_cis_call(e->facility.channel)) { + /* Event came in on the dummy channel or a CIS call. */ +#if defined(HAVE_PRI_CALL_REROUTING) + sig_pri_handle_cis_subcmds(pri, e->e, e->facility.subcmds, + e->facility.subcall); +#else + sig_pri_handle_cis_subcmds(pri, e->e, e->facility.subcmds, + e->facility.call); +#endif /* !defined(HAVE_PRI_CALL_REROUTING) */ + break; + } chanpos = pri_find_principle(pri, e->facility.channel, e->facility.call); if (chanpos < 0) { ast_log(LOG_WARNING, "Facility requested on unconfigured channel %d/%d span %d\n", @@ -2783,6 +3564,11 @@ static void *pri_dchannel(void *vpri) } break; case PRI_EVENT_ANSWER: + if (sig_pri_is_cis_call(e->answer.channel)) { + sig_pri_handle_cis_subcmds(pri, e->e, e->answer.subcmds, + e->answer.call); + break; + } chanpos = pri_find_principle(pri, e->answer.channel, e->answer.call); if (chanpos < 0) { ast_log(LOG_WARNING, "Answer on unconfigured channel %d/%d span %d\n", @@ -2821,6 +3607,12 @@ static void *pri_dchannel(void *vpri) } break; case PRI_EVENT_HANGUP: + if (sig_pri_is_cis_call(e->hangup.channel)) { + sig_pri_handle_cis_subcmds(pri, e->e, e->hangup.subcmds, + e->hangup.call); + pri_hangup(pri->pri, e->hangup.call, e->hangup.cause); + break; + } chanpos = pri_find_principle(pri, e->hangup.channel, e->hangup.call); if (chanpos < 0) { ast_log(LOG_WARNING, "Hangup requested on unconfigured channel %d/%d span %d\n", @@ -2834,6 +3626,14 @@ static void *pri_dchannel(void *vpri) if (!pri->pvts[chanpos]->alreadyhungup) { /* we're calling here dahdi_hangup so once we get there we need to clear p->call after calling pri_hangup */ pri->pvts[chanpos]->alreadyhungup = 1; + switch (e->hangup.cause) { + case PRI_CAUSE_USER_BUSY: + case PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION: + sig_pri_cc_generic_check(pri, chanpos, AST_CC_CCBS); + break; + default: + break; + } if (pri->pvts[chanpos]->owner) { /* Queue a BUSY instead of a hangup if our cause is appropriate */ pri->pvts[chanpos]->owner->hangupcause = e->hangup.cause; @@ -2900,6 +3700,12 @@ static void *pri_dchannel(void *vpri) } break; case PRI_EVENT_HANGUP_REQ: + if (sig_pri_is_cis_call(e->hangup.channel)) { + sig_pri_handle_cis_subcmds(pri, e->e, e->hangup.subcmds, + e->hangup.call); + pri_hangup(pri->pri, e->hangup.call, e->hangup.cause); + break; + } chanpos = pri_find_principle(pri, e->hangup.channel, e->hangup.call); if (chanpos < 0) { ast_log(LOG_WARNING, "Hangup REQ requested on unconfigured channel %d/%d span %d\n", @@ -2922,6 +3728,14 @@ static void *pri_dchannel(void *vpri) sig_pri_lock_private(pri->pvts[chanpos]); } #endif /* defined(HAVE_PRI_CALL_HOLD) */ + switch (e->hangup.cause) { + case PRI_CAUSE_USER_BUSY: + case PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION: + sig_pri_cc_generic_check(pri, chanpos, AST_CC_CCBS); + break; + default: + break; + } if (pri->pvts[chanpos]->owner) { pri->pvts[chanpos]->owner->hangupcause = e->hangup.cause; switch (pri->pvts[chanpos]->owner->_state) { @@ -2984,6 +3798,11 @@ static void *pri_dchannel(void *vpri) } break; case PRI_EVENT_HANGUP_ACK: + if (sig_pri_is_cis_call(e->hangup.channel)) { + sig_pri_handle_cis_subcmds(pri, e->e, e->hangup.subcmds, + e->hangup.call); + break; + } chanpos = pri_find_principle(pri, e->hangup.channel, e->hangup.call); if (chanpos < 0) { ast_log(LOG_WARNING, "Hangup ACK requested on unconfigured channel number %d/%d span %d\n", @@ -3067,6 +3886,11 @@ static void *pri_dchannel(void *vpri) } break; case PRI_EVENT_SETUP_ACK: + if (sig_pri_is_cis_call(e->setup_ack.channel)) { + sig_pri_handle_cis_subcmds(pri, e->e, e->setup_ack.subcmds, + e->setup_ack.call); + break; + } chanpos = pri_find_principle(pri, e->setup_ack.channel, e->setup_ack.call); if (chanpos < 0) { ast_log(LOG_WARNING, "Received SETUP_ACKNOWLEDGE on unconfigured channel %d/%d span %d\n", @@ -3090,6 +3914,15 @@ static void *pri_dchannel(void *vpri) } break; case PRI_EVENT_NOTIFY: + if (sig_pri_is_cis_call(e->notify.channel)) { +#if defined(HAVE_PRI_CALL_HOLD) + sig_pri_handle_cis_subcmds(pri, e->e, e->notify.subcmds, + e->notify.call); +#else + sig_pri_handle_cis_subcmds(pri, e->e, e->notify.subcmds, NULL); +#endif /* !defined(HAVE_PRI_CALL_HOLD) */ + break; + } #if defined(HAVE_PRI_CALL_HOLD) chanpos = pri_find_principle(pri, e->notify.channel, e->notify.call); #else @@ -3130,6 +3963,7 @@ static void *pri_dchannel(void *vpri) break; #if defined(HAVE_PRI_CALL_HOLD) case PRI_EVENT_HOLD: + /* We should not be getting any CIS calls with this message type. */ if (sig_pri_handle_hold(pri, e)) { pri_hold_rej(pri->pri, e->hold.call, PRI_CAUSE_RESOURCE_UNAVAIL_UNSPECIFIED); @@ -3150,6 +3984,7 @@ static void *pri_dchannel(void *vpri) #endif /* defined(HAVE_PRI_CALL_HOLD) */ #if defined(HAVE_PRI_CALL_HOLD) case PRI_EVENT_RETRIEVE: + /* We should not be getting any CIS calls with this message type. */ sig_pri_handle_retrieve(pri, e); break; #endif /* defined(HAVE_PRI_CALL_HOLD) */ @@ -3189,7 +4024,7 @@ void sig_pri_init_pri(struct sig_pri_pri *pri) int sig_pri_hangup(struct sig_pri_chan *p, struct ast_channel *ast) { - int res = 0; + int res; #ifdef SUPPORT_USERUSER const char *useruser = pbx_builtin_getvar_helper(ast, "USERUSERINFO"); #endif @@ -3213,47 +4048,43 @@ int sig_pri_hangup(struct sig_pri_chan *p, struct ast_channel *ast) p->exten[0] = '\0'; sig_pri_set_dialing(p, 0); - if (!p->call) { - res = 0; - goto exit; - } - /* Make sure we have a call (or REALLY have a call in the case of a PRI) */ if (!pri_grab(p, p->pri)) { - if (p->alreadyhungup) { - ast_log(LOG_DEBUG, "Already hungup... Calling hangup once, and clearing call\n"); + if (p->call) { + if (p->alreadyhungup) { + ast_log(LOG_DEBUG, "Already hungup... Calling hangup once, and clearing call\n"); #ifdef SUPPORT_USERUSER - pri_call_set_useruser(p->call, useruser); + pri_call_set_useruser(p->call, useruser); #endif - pri_hangup(p->pri->pri, p->call, -1); - p->call = NULL; - } else { - const char *cause = pbx_builtin_getvar_helper(ast,"PRI_CAUSE"); - int icause = ast->hangupcause ? ast->hangupcause : -1; - ast_log(LOG_DEBUG, "Not yet hungup... Calling hangup once with icause, and clearing call\n"); + pri_hangup(p->pri->pri, p->call, -1); + p->call = NULL; + } else { + const char *cause = pbx_builtin_getvar_helper(ast,"PRI_CAUSE"); + int icause = ast->hangupcause ? ast->hangupcause : -1; + ast_log(LOG_DEBUG, "Not yet hungup... Calling hangup once with icause, and clearing call\n"); #ifdef SUPPORT_USERUSER - pri_call_set_useruser(p->call, useruser); + pri_call_set_useruser(p->call, useruser); #endif - p->alreadyhungup = 1; - if (cause) { - if (atoi(cause)) - icause = atoi(cause); + p->alreadyhungup = 1; + if (cause) { + if (atoi(cause)) + icause = atoi(cause); + } + pri_hangup(p->pri->pri, p->call, icause); } - pri_hangup(p->pri->pri, p->call, icause); } - if (res < 0) - ast_log(LOG_WARNING, "pri_disconnect failed\n"); + sig_pri_span_devstate_changed(p->pri); pri_rel(p->pri); + res = 0; } else { ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->pri->span); res = -1; } -exit: ast->tech_pvt = NULL; return res; } @@ -3356,6 +4187,7 @@ int sig_pri_call(struct sig_pri_chan *p, struct ast_channel *ast, char *rdest, i #ifdef SUPPORT_USERUSER const char *useruser; #endif + int core_id; int pridialplan; int dp_strip; int prilocaldialplan; @@ -3672,7 +4504,41 @@ int sig_pri_call(struct sig_pri_chan *p, struct ast_channel *ast, char *rdest, i pri_sr_set_useruser(sr, useruser); #endif - if (pri_setup(p->pri->pri, p->call, sr)) { +#if defined(HAVE_PRI_CCSS) + if (ast_cc_is_recall(ast, &core_id, sig_pri_cc_type_name)) { + struct ast_cc_monitor *monitor; + char device_name[AST_CHANNEL_NAME]; + + /* This is a CC recall call. */ + ast_channel_get_device_name(ast, device_name, sizeof(device_name)); + monitor = ast_cc_get_monitor_by_recall_core_id(core_id, device_name); + if (monitor) { + struct sig_pri_cc_monitor_instance *instance; + + instance = monitor->private_data; + + /* If this fails then we have monitor instance ambiguity. */ + ast_assert(p->pri == instance->pri); + + if (pri_cc_call(p->pri->pri, instance->cc_id, p->call, sr)) { + /* The CC recall call failed for some reason. */ + ast_log(LOG_WARNING, "Unable to setup CC recall call to device %s\n", + device_name); + ao2_ref(monitor, -1); + pri_rel(p->pri); + pri_sr_free(sr); + return -1; + } + ao2_ref(monitor, -1); + } else { + core_id = -1; + } + } else +#endif /* defined(HAVE_PRI_CCSS) */ + { + core_id = -1; + } + if (core_id == -1 && pri_setup(p->pri->pri, p->call, sr)) { ast_log(LOG_WARNING, "Unable to setup call to %s (using %s)\n", c + p->stripmsd + dp_strip, dialplan2str(p->pri->dialplan)); pri_rel(p->pri); @@ -3935,12 +4801,6 @@ int sig_pri_start_pri(struct sig_pri_pri *pri) #ifdef HAVE_PRI_INBANDDISCONNECT pri_set_inbanddisconnect(pri->dchans[i], pri->inbanddisconnect); #endif -#if defined(HAVE_PRI_CALL_HOLD) - pri_hold_enable(pri->dchans[i], 1); -#endif /* defined(HAVE_PRI_CALL_HOLD) */ -#if defined(HAVE_PRI_CALL_REROUTING) - pri_reroute_enable(pri->dchans[i], 1); -#endif /* defined(HAVE_PRI_CALL_REROUTING) */ /* Enslave to master if appropriate */ if (i) pri_enslave(pri->dchans[0], pri->dchans[i]); @@ -3951,7 +4811,7 @@ int sig_pri_start_pri(struct sig_pri_pri *pri) ast_log(LOG_ERROR, "Unable to create PRI structure\n"); return -1; } - pri_set_debug(pri->dchans[i], DEFAULT_PRI_DEBUG); + pri_set_debug(pri->dchans[i], SIG_PRI_DEBUG_DEFAULT); pri_set_nsf(pri->dchans[i], pri->nsf); #ifdef PRI_GETSET_TIMERS for (x = 0; x < PRI_MAX_TIMERS; x++) { @@ -3960,8 +4820,23 @@ int sig_pri_start_pri(struct sig_pri_pri *pri) } #endif } + /* Assume primary is the one we use */ pri->pri = pri->dchans[0]; + +#if defined(HAVE_PRI_CALL_HOLD) + pri_hold_enable(pri->pri, 1); +#endif /* defined(HAVE_PRI_CALL_HOLD) */ +#if defined(HAVE_PRI_CALL_REROUTING) + pri_reroute_enable(pri->pri, 1); +#endif /* defined(HAVE_PRI_CALL_REROUTING) */ +#if defined(HAVE_PRI_CCSS) + pri_cc_enable(pri->pri, 1); + pri_cc_recall_mode(pri->pri, pri->cc_ptmp_recall_mode); + pri_cc_retain_signaling_req(pri->pri, pri->cc_qsig_signaling_link_req); + pri_cc_retain_signaling_rsp(pri->pri, pri->cc_qsig_signaling_link_rsp); +#endif /* defined(HAVE_PRI_CCSS) */ + pri->resetpos = -1; if (ast_pthread_create_background(&pri->master, NULL, pri_dchannel, pri)) { for (i = 0; i < NUM_DCHANS; i++) { @@ -4160,4 +5035,601 @@ void sig_pri_fixup(struct ast_channel *oldchan, struct ast_channel *newchan, str } } +#if defined(HAVE_PRI_CCSS) +/*! + * \brief PRI CC agent initialization. + * \since 1.8 + * + * \param agent CC core agent control. + * \param pvt_chan Original channel the agent will attempt to recall. + * + * \details + * This callback is called when the CC core is initialized. Agents should allocate + * any private data necessary for the call and assign it to the private_data + * on the agent. Additionally, if any ast_cc_agent_flags are pertinent to the + * specific agent type, they should be set in this function as well. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int sig_pri_cc_agent_init(struct ast_cc_agent *agent, struct sig_pri_chan *pvt_chan) +{ + struct sig_pri_cc_agent_prv *cc_pvt; + + cc_pvt = ast_calloc(1, sizeof(*cc_pvt)); + if (!cc_pvt) { + return -1; + } + + ast_mutex_lock(&pvt_chan->pri->lock); + cc_pvt->pri = pvt_chan->pri; + cc_pvt->cc_id = pri_cc_available(pvt_chan->pri->pri, pvt_chan->call); + ast_mutex_unlock(&pvt_chan->pri->lock); + if (cc_pvt->cc_id == -1) { + ast_free(cc_pvt); + return -1; + } + agent->private_data = cc_pvt; + return 0; +} +#endif /* defined(HAVE_PRI_CCSS) */ + +#if defined(HAVE_PRI_CCSS) +/*! + * \brief Start the offer timer. + * \since 1.8 + * + * \param agent CC core agent control. + * + * \details + * This is called by the core when the caller hangs up after + * a call for which CC may be requested. The agent should + * begin the timer as configured. + * + * The primary reason why this functionality is left to + * the specific agent implementations is due to the differing + * use of schedulers throughout the code. Some channel drivers + * may already have a scheduler context they wish to use, and + * amongst those, some may use the ast_sched API while others + * may use the ast_sched_thread API, which are incompatible. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int sig_pri_cc_agent_start_offer_timer(struct ast_cc_agent *agent) +{ + /* libpri maintains it's own offer timer in the form of T_RETENTION. */ + return 0; +} +#endif /* defined(HAVE_PRI_CCSS) */ + +#if defined(HAVE_PRI_CCSS) +/*! + * \brief Stop the offer timer. + * \since 1.8 + * + * \param agent CC core agent control. + * + * \details + * This callback is called by the CC core when the caller + * has requested CC. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int sig_pri_cc_agent_stop_offer_timer(struct ast_cc_agent *agent) +{ + /* libpri maintains it's own offer timer in the form of T_RETENTION. */ + return 0; +} +#endif /* defined(HAVE_PRI_CCSS) */ + +#if defined(HAVE_PRI_CCSS) +/*! + * \brief Acknowledge CC request. + * \since 1.8 + * + * \param agent CC core agent control. + * + * \details + * When the core receives knowledge that a called + * party has accepted a CC request, it will call + * this callback. + * + * The duty of this is to accept a CC request from + * the caller by acknowledging receipt of that request. + * + * \return Nothing + */ +void sig_pri_cc_agent_req_ack(struct ast_cc_agent *agent) +{ + struct sig_pri_cc_agent_prv *cc_pvt; + int res; + + cc_pvt = agent->private_data; + ast_mutex_lock(&cc_pvt->pri->lock); + if (cc_pvt->cc_request_response_pending) { + cc_pvt->cc_request_response_pending = 0; + res = pri_cc_req_rsp(cc_pvt->pri->pri, cc_pvt->cc_id, 0/* success */); + } else { + res = 0; + } + ast_mutex_unlock(&cc_pvt->pri->lock); + if (res) { + ast_cc_failed(agent->core_id, "%s agent failed to send the CC request ack.", + sig_pri_cc_type_name); + } +} +#endif /* defined(HAVE_PRI_CCSS) */ + +#if defined(HAVE_PRI_CCSS) +/*! + * \brief Request the status of the agent's device. + * \since 1.8 + * + * \param agent CC core agent control. + * + * \details + * Asynchronous request for the status of any caller + * which may be a valid caller for the CC transaction. + * Status responses should be made using the + * ast_cc_status_response function. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int sig_pri_cc_agent_status_req(struct ast_cc_agent *agent) +{ + struct sig_pri_cc_agent_prv *cc_pvt; + + cc_pvt = agent->private_data; + ast_mutex_lock(&cc_pvt->pri->lock); + pri_cc_status_req(cc_pvt->pri->pri, cc_pvt->cc_id); + ast_mutex_unlock(&cc_pvt->pri->lock); + return 0; +} +#endif /* defined(HAVE_PRI_CCSS) */ + +#if defined(HAVE_PRI_CCSS) +/*! + * \brief Request for an agent's phone to stop ringing. + * \since 1.8 + * + * \param agent CC core agent control. + * + * \details + * The usefulness of this is quite limited. The only specific + * known case for this is if Asterisk requests CC over an ISDN + * PTMP link as the TE side. If other phones are in the same + * recall group as the Asterisk server, and one of those phones + * picks up the recall notice, then Asterisk will receive a + * "stop ringing" notification from the NT side of the PTMP + * link. This indication needs to be passed to the phone + * on the other side of the Asterisk server which originally + * placed the call so that it will stop ringing. Since the + * phone may be of any type, it is necessary to have a callback + * that the core can know about. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int sig_pri_cc_agent_stop_ringing(struct ast_cc_agent *agent) +{ + struct sig_pri_cc_agent_prv *cc_pvt; + + cc_pvt = agent->private_data; + ast_mutex_lock(&cc_pvt->pri->lock); + pri_cc_stop_alerting(cc_pvt->pri->pri, cc_pvt->cc_id); + ast_mutex_unlock(&cc_pvt->pri->lock); + return 0; +} +#endif /* defined(HAVE_PRI_CCSS) */ + +#if defined(HAVE_PRI_CCSS) +/*! + * \brief Let the caller know that the callee has become free + * but that the caller cannot attempt to call back because + * he is either busy or there is congestion on his line. + * \since 1.8 + * + * \param agent CC core agent control. + * + * \details + * This is something that really only affects a scenario where + * a phone places a call over ISDN PTMP to Asterisk, who then + * connects over PTMP again to the ISDN network. For most agent + * types, there is no need to implement this callback at all + * because they don't really need to actually do anything in + * this situation. If you're having trouble understanding what + * the purpose of this callback is, then you can be safe simply + * not implementing it. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int sig_pri_cc_agent_party_b_free(struct ast_cc_agent *agent) +{ + struct sig_pri_cc_agent_prv *cc_pvt; + + cc_pvt = agent->private_data; + ast_mutex_lock(&cc_pvt->pri->lock); + pri_cc_b_free(cc_pvt->pri->pri, cc_pvt->cc_id); + ast_mutex_unlock(&cc_pvt->pri->lock); + return 0; +} +#endif /* defined(HAVE_PRI_CCSS) */ + +#if defined(HAVE_PRI_CCSS) +/*! + * \brief Begin monitoring a busy device. + * \since 1.8 + * + * \param agent CC core agent control. + * + * \details + * The core will call this callback if the callee becomes + * available but the caller has reported that he is busy. + * The agent should begin monitoring the caller's device. + * When the caller becomes available again, the agent should + * call ast_cc_agent_caller_available. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int sig_pri_cc_agent_start_monitoring(struct ast_cc_agent *agent) +{ + /* libpri already knows when and how it needs to monitor Party A. */ + return 0; +} +#endif /* defined(HAVE_PRI_CCSS) */ + +#if defined(HAVE_PRI_CCSS) +/*! + * \brief Alert the caller that it is time to try recalling. + * \since 1.8 + * + * \param agent CC core agent control. + * + * \details + * The core will call this function when it receives notice + * that a monitored party has become available. + * + * The agent's job is to send a message to the caller to + * notify it of such a change. If the agent is able to + * discern that the caller is currently unavailable, then + * the agent should react by calling the ast_cc_caller_unavailable + * function. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int sig_pri_cc_agent_callee_available(struct ast_cc_agent *agent) +{ + struct sig_pri_cc_agent_prv *cc_pvt; + + cc_pvt = agent->private_data; + ast_mutex_lock(&cc_pvt->pri->lock); + pri_cc_remote_user_free(cc_pvt->pri->pri, cc_pvt->cc_id); + ast_mutex_unlock(&cc_pvt->pri->lock); + return 0; +} +#endif /* defined(HAVE_PRI_CCSS) */ + +#if defined(HAVE_PRI_CCSS) +/*! + * \brief Destroy private data on the agent. + * \since 1.8 + * + * \param agent CC core agent control. + * + * \details + * The core will call this function upon completion + * or failure of CC. + * + * \note + * The agent private_data pointer may be NULL if the agent + * constructor failed. + * + * \return Nothing + */ +void sig_pri_cc_agent_destructor(struct ast_cc_agent *agent) +{ + struct sig_pri_cc_agent_prv *cc_pvt; + int res; + + cc_pvt = agent->private_data; + if (!cc_pvt) { + /* The agent constructor probably failed. */ + return; + } + ast_mutex_lock(&cc_pvt->pri->lock); + res = -1; + if (cc_pvt->cc_request_response_pending) { + res = pri_cc_req_rsp(cc_pvt->pri->pri, cc_pvt->cc_id, 2/* short_term_denial */); + } + if (res) { + pri_cc_cancel(cc_pvt->pri->pri, cc_pvt->cc_id); + } + ast_mutex_unlock(&cc_pvt->pri->lock); + ast_free(cc_pvt); +} +#endif /* defined(HAVE_PRI_CCSS) */ + +#if defined(HAVE_PRI_CCSS) +/*! + * \internal + * \brief Return the hash value of the given CC monitor instance object. + * \since 1.8 + * + * \param obj pointer to the (user-defined part) of an object. + * \param flags flags from ao2_callback(). Ignored at the moment. + * + * \retval core_id + */ +static int sig_pri_cc_monitor_instance_hash_fn(const void *obj, const int flags) +{ + const struct sig_pri_cc_monitor_instance *monitor_instance = obj; + + return monitor_instance->core_id; +} +#endif /* defined(HAVE_PRI_CCSS) */ + +#if defined(HAVE_PRI_CCSS) +/*! + * \internal + * \brief Compere the monitor instance core_id key value. + * \since 1.8 + * + * \param obj pointer to the (user-defined part) of an object. + * \param arg callback argument from ao2_callback() + * \param flags flags from ao2_callback() + * + * \return values are a combination of enum _cb_results. + */ +static int sig_pri_cc_monitor_instance_cmp_fn(void *obj, void *arg, int flags) +{ + struct sig_pri_cc_monitor_instance *monitor_1 = obj; + struct sig_pri_cc_monitor_instance *monitor_2 = arg; + + return monitor_1->core_id == monitor_2->core_id ? CMP_MATCH | CMP_STOP : 0; +} +#endif /* defined(HAVE_PRI_CCSS) */ + +#if defined(HAVE_PRI_CCSS) +/*! + * \brief Request CCSS. + * \since 1.8 + * + * \param monitor CC core monitor control. + * \param available_timer_id Where to put the available timer scheduler id. + * Will never be NULL for a device monitor. + * + * \details + * Perform whatever steps are necessary in order to request CC. + * In addition, the monitor implementation is responsible for + * starting the available timer in this callback. The scheduler + * ID for the callback must be stored in the parent_link's child_avail_id + * field. + * + * \retval 0 on success + * \retval -1 on failure. + */ +int sig_pri_cc_monitor_req_cc(struct ast_cc_monitor *monitor, int *available_timer_id) +{ + struct sig_pri_cc_monitor_instance *instance; + int cc_mode; + int res; + + switch (monitor->service_offered) { + case AST_CC_CCBS: + cc_mode = 0;/* CCBS */ + break; + case AST_CC_CCNR: + cc_mode = 1;/* CCNR */ + break; + default: + /* CC service not supported by ISDN. */ + return -1; + } + + instance = monitor->private_data; + + /* libpri handles it's own available timer. */ + ast_mutex_lock(&instance->pri->lock); + res = pri_cc_req(instance->pri->pri, instance->cc_id, cc_mode); + ast_mutex_unlock(&instance->pri->lock); + + return res; +} +#endif /* defined(HAVE_PRI_CCSS) */ + +#if defined(HAVE_PRI_CCSS) +/*! + * \brief Suspend monitoring. + * \since 1.8 + * + * \param monitor CC core monitor control. + * + * \details + * Implementers must perform the necessary steps to suspend + * monitoring. + * + * \retval 0 on success + * \retval -1 on failure. + */ +int sig_pri_cc_monitor_suspend(struct ast_cc_monitor *monitor) +{ + struct sig_pri_cc_monitor_instance *instance; + + instance = monitor->private_data; + ast_mutex_lock(&instance->pri->lock); + pri_cc_status(instance->pri->pri, instance->cc_id, 1/* busy */); + ast_mutex_unlock(&instance->pri->lock); + + return 0; +} +#endif /* defined(HAVE_PRI_CCSS) */ + +#if defined(HAVE_PRI_CCSS) +/*! + * \brief Unsuspend monitoring. + * \since 1.8 + * + * \param monitor CC core monitor control. + * + * \details + * Perform the necessary steps to unsuspend monitoring. + * + * \retval 0 on success + * \retval -1 on failure. + */ +int sig_pri_cc_monitor_unsuspend(struct ast_cc_monitor *monitor) +{ + struct sig_pri_cc_monitor_instance *instance; + + instance = monitor->private_data; + ast_mutex_lock(&instance->pri->lock); + pri_cc_status(instance->pri->pri, instance->cc_id, 0/* free */); + ast_mutex_unlock(&instance->pri->lock); + + return 0; +} +#endif /* defined(HAVE_PRI_CCSS) */ + +#if defined(HAVE_PRI_CCSS) +/*! + * \brief Status response to an ast_cc_monitor_status_request(). + * \since 1.8 + * + * \param monitor CC core monitor control. + * \param devstate Current status of a Party A device. + * + * \details + * Alert a monitor as to the status of the agent for which + * the monitor had previously requested a status request. + * + * \note Zero or more responses may come as a result. + * + * \retval 0 on success + * \retval -1 on failure. + */ +int sig_pri_cc_monitor_status_rsp(struct ast_cc_monitor *monitor, enum ast_device_state devstate) +{ + struct sig_pri_cc_monitor_instance *instance; + int cc_status; + + switch (devstate) { + case AST_DEVICE_UNKNOWN: + case AST_DEVICE_NOT_INUSE: + cc_status = 0;/* free */ + break; + case AST_DEVICE_BUSY: + case AST_DEVICE_INUSE: + cc_status = 1;/* busy */ + break; + default: + /* Don't know how to interpret this device state into free/busy status. */ + return 0; + } + instance = monitor->private_data; + ast_mutex_lock(&instance->pri->lock); + pri_cc_status_req_rsp(instance->pri->pri, instance->cc_id, cc_status); + ast_mutex_unlock(&instance->pri->lock); + + return 0; +} +#endif /* defined(HAVE_PRI_CCSS) */ + +#if defined(HAVE_PRI_CCSS) +/*! + * \brief Cancel the running available timer. + * \since 1.8 + * + * \param monitor CC core monitor control. + * \param sched_id Available timer scheduler id to cancel. + * Will never be NULL for a device monitor. + * + * \details + * In most cases, this function will likely consist of just a + * call to AST_SCHED_DEL. It might have been possible to do this + * within the core, but unfortunately the mixture of sched_thread + * and sched usage in Asterisk prevents such usage. + * + * \retval 0 on success + * \retval -1 on failure. + */ +int sig_pri_cc_monitor_cancel_available_timer(struct ast_cc_monitor *monitor, int *sched_id) +{ + /* + * libpri maintains it's own available timer as one of: + * T_CCBS2/T_CCBS5/T_CCBS6/QSIG_CCBS_T2 + * T_CCNR2/T_CCNR5/T_CCNR6/QSIG_CCNR_T2 + */ + return 0; +} +#endif /* defined(HAVE_PRI_CCSS) */ + +#if defined(HAVE_PRI_CCSS) +/*! + * \brief Destroy PRI private data on the monitor. + * \since 1.8 + * + * \param monitor_pvt CC device monitor private data pointer. + * + * \details + * Implementers of this callback are responsible for destroying + * all heap-allocated data in the monitor's private_data pointer, including + * the private_data itself. + */ +void sig_pri_cc_monitor_destructor(void *monitor_pvt) +{ + struct sig_pri_cc_monitor_instance *instance; + + instance = monitor_pvt; + if (!instance) { + return; + } + ao2_unlink(sig_pri_cc_monitors, instance); + ao2_ref(instance, -1); +} +#endif /* defined(HAVE_PRI_CCSS) */ + +/*! + * \brief Load the sig_pri submodule. + * \since 1.8 + * + * \param cc_type_name CC type name to use when looking up agent/monitor. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int sig_pri_load(const char *cc_type_name) +{ +#if defined(HAVE_PRI_CCSS) + sig_pri_cc_type_name = cc_type_name; + sig_pri_cc_monitors = ao2_container_alloc(37, sig_pri_cc_monitor_instance_hash_fn, + sig_pri_cc_monitor_instance_cmp_fn); + if (!sig_pri_cc_monitors) { + return -1; + } +#endif /* defined(HAVE_PRI_CCSS) */ + return 0; +} + +/*! + * \brief Unload the sig_pri submodule. + * \since 1.8 + * + * \return Nothing + */ +void sig_pri_unload(void) +{ +#if defined(HAVE_PRI_CCSS) + if (sig_pri_cc_monitors) { + ao2_ref(sig_pri_cc_monitors, -1); + sig_pri_cc_monitors = NULL; + } +#endif /* defined(HAVE_PRI_CCSS) */ +} + #endif /* HAVE_PRI */ diff --git a/channels/sig_pri.h b/channels/sig_pri.h index 0bccd6ab0..7ea92d752 100644 --- a/channels/sig_pri.h +++ b/channels/sig_pri.h @@ -27,8 +27,44 @@ #include "asterisk/channel.h" #include "asterisk/frame.h" +#include "asterisk/ccss.h" #include <libpri.h> #include <dahdi/user.h> +#if defined(PRI_SUBCMD_CC_AVAILABLE) +/* BUGBUG the HAVE_PRI_CCSS line is to be removed when the CCSS branch is merged to trunk and the configure script is updated. */ +#define HAVE_PRI_CCSS 1 +#endif /* defined(PRI_SUBCMD_CC_AVAILABLE) */ + +#if defined(HAVE_PRI_CCSS) +/*! PRI debug message flags when normal PRI debugging is turned on at the command line. */ +#define SIG_PRI_DEBUG_NORMAL \ + (PRI_DEBUG_APDU | PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q931_STATE | PRI_DEBUG_Q921_STATE \ + | PRI_DEBUG_CC) + +/*! PRI debug message flags when intense PRI debugging is turned on at the command line. */ +#define SIG_PRI_DEBUG_INTENSE \ + (PRI_DEBUG_APDU | PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q931_STATE | PRI_DEBUG_Q921_STATE \ + | PRI_DEBUG_CC | PRI_DEBUG_Q921_RAW | PRI_DEBUG_Q921_DUMP) + +#else + +/*! PRI debug message flags when normal PRI debugging is turned on at the command line. */ +#define SIG_PRI_DEBUG_NORMAL \ + (PRI_DEBUG_APDU | PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q931_STATE | PRI_DEBUG_Q921_STATE) + +/*! PRI debug message flags when intense PRI debugging is turned on at the command line. */ +#define SIG_PRI_DEBUG_INTENSE \ + (PRI_DEBUG_APDU | PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q931_STATE | PRI_DEBUG_Q921_STATE \ + | PRI_DEBUG_Q921_RAW | PRI_DEBUG_Q921_DUMP) +#endif /* !defined(HAVE_PRI_CCSS) */ + +#if 0 +/*! PRI debug message flags set on initial startup. */ +#define SIG_PRI_DEBUG_DEFAULT SIG_PRI_DEBUG_NORMAL +#else +/*! PRI debug message flags set on initial startup. */ +#define SIG_PRI_DEBUG_DEFAULT 0 +#endif enum sig_pri_tone { SIG_PRI_TONE_RINGTONE = 0, @@ -78,6 +114,14 @@ struct sig_pri_callback { void (* const set_rdnis)(void *pvt, const char *rdnis); void (* const queue_control)(void *pvt, int subclass); int (* const new_nobch_intf)(struct sig_pri_pri *pri); + const char *(* const get_orig_dialstring)(void *pvt); + void (* const make_cc_dialstring)(void *pvt, char *buf, size_t buf_size); + void (* const update_span_devstate)(struct sig_pri_pri *pri); + + /*! Reference the parent module. */ + void (*module_ref)(void); + /*! Unreference the parent module. */ + void (*module_unref)(void); }; #define NUM_DCHANS 4 /*!< No more than 4 d-channels */ @@ -194,6 +238,7 @@ struct sig_pri_chan { struct sig_pri_pri { /* Should be set by user */ + struct ast_cc_config_params *cc_params; /*!< CC config parameters for each new call. */ int pritimers[PRI_MAX_TIMERS]; int overlapdial; /*!< In overlap dialing mode */ int qsigchannelmapping; /*!< QSIG channel mapping type */ @@ -229,6 +274,11 @@ struct sig_pri_pri { int switchtype; /*!< Type of switch to emulate */ int nsf; /*!< Network-Specific Facilities */ int trunkgroup; /*!< What our trunkgroup is */ +#if defined(HAVE_PRI_CCSS) + int cc_ptmp_recall_mode; /*!< CC PTMP recall mode. globalRecall(0), specificRecall(1) */ + int cc_qsig_signaling_link_req; /*!< CC Q.SIG signaling link retention (Party A) release(0), retain(1), do-not-care(2) */ + int cc_qsig_signaling_link_rsp; /*!< CC Q.SIG signaling link retention (Party B) release(0), retain(1) */ +#endif /* defined(HAVE_PRI_CCSS) */ int dchanavail[NUM_DCHANS]; /*!< Whether each channel is available */ int debug; /*!< set to true if to dump PRI event info (tested but never set) */ @@ -257,6 +307,37 @@ struct sig_pri_pri { ast_mutex_t lock; /*!< libpri access Mutex */ time_t lastreset; /*!< time when unused channels were last reset */ struct sig_pri_callback *calls; + /*! + * \brief Congestion device state of the span. + * \details + * AST_DEVICE_NOT_INUSE - Span does not have all B channels in use. + * AST_DEVICE_BUSY - All B channels are in use. + * AST_DEVICE_UNAVAILABLE - Span is in alarm. + * \note + * Device name: DAHDI/I<span>/congestion + */ + int congestion_devstate; +#if defined(THRESHOLD_DEVSTATE_PLACEHOLDER) + /*! \todo An ISDN span threshold device state could be useful in determining how often a span utilization goes over a configurable threshold. */ + /*! + * \brief User threshold device state of the span. + * \details + * AST_DEVICE_NOT_INUSE - There are no B channels in use. + * AST_DEVICE_INUSE - The number of B channels in use is less than + * the configured threshold but not zero. + * AST_DEVICE_BUSY - The number of B channels in use meets or exceeds + * the configured threshold. + * AST_DEVICE_UNAVAILABLE - Span is in alarm. + * \note + * Device name: DAHDI/I<span>/threshold + */ + int threshold_devstate; + /*! + * \brief Number of B channels in use to consider the span in a busy state. + * \note Setting the threshold to zero is interpreted as all B channels. + */ + int user_busy_threshold; +#endif /* defined(THRESHOLD_DEVSTATE_PLACEHOLDER) */ }; void sig_pri_extract_called_num_subaddr(struct sig_pri_chan *p, const char *rdest, char *called, size_t called_buff_size); @@ -304,4 +385,25 @@ int pri_maintenance_bservice(struct pri *pri, struct sig_pri_chan *p, int change void sig_pri_fixup(struct ast_channel *oldchan, struct ast_channel *newchan, struct sig_pri_chan *pchan); +int sig_pri_cc_agent_init(struct ast_cc_agent *agent, struct sig_pri_chan *pvt_chan); +int sig_pri_cc_agent_start_offer_timer(struct ast_cc_agent *agent); +int sig_pri_cc_agent_stop_offer_timer(struct ast_cc_agent *agent); +void sig_pri_cc_agent_req_ack(struct ast_cc_agent *agent); +int sig_pri_cc_agent_status_req(struct ast_cc_agent *agent); +int sig_pri_cc_agent_stop_ringing(struct ast_cc_agent *agent); +int sig_pri_cc_agent_party_b_free(struct ast_cc_agent *agent); +int sig_pri_cc_agent_start_monitoring(struct ast_cc_agent *agent); +int sig_pri_cc_agent_callee_available(struct ast_cc_agent *agent); +void sig_pri_cc_agent_destructor(struct ast_cc_agent *agent); + +int sig_pri_cc_monitor_req_cc(struct ast_cc_monitor *monitor, int *available_timer_id); +int sig_pri_cc_monitor_suspend(struct ast_cc_monitor *monitor); +int sig_pri_cc_monitor_unsuspend(struct ast_cc_monitor *monitor); +int sig_pri_cc_monitor_status_rsp(struct ast_cc_monitor *monitor, enum ast_device_state devstate); +int sig_pri_cc_monitor_cancel_available_timer(struct ast_cc_monitor *monitor, int *sched_id); +void sig_pri_cc_monitor_destructor(void *monitor_pvt); + +int sig_pri_load(const char *cc_type_name); +void sig_pri_unload(void); + #endif /* _SIG_PRI_H */ diff --git a/channels/sip/include/sip.h b/channels/sip/include/sip.h index ce87f0f23..1d900eb58 100644 --- a/channels/sip/include/sip.h +++ b/channels/sip/include/sip.h @@ -153,7 +153,7 @@ * \todo This string should be set dynamically. We only support REFER and SUBSCRIBE if we have * allowsubscribe and allowrefer on in sip.conf. */ -#define ALLOWED_METHODS "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO" +#define ALLOWED_METHODS "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH" /*! \brief SIP Extensions we support * \note This should be generated based on the previous array @@ -239,6 +239,7 @@ */ /*@{*/ #define SIP_OUTGOING (1 << 0) /*!< D: Direction of the last transaction in this dialog */ +#define SIP_OFFER_CC (1 << 1) /*!< D: Offer CC on subsequent responses */ #define SIP_RINGING (1 << 2) /*!< D: Have sent 180 ringing */ #define SIP_PROGRESS_SENT (1 << 3) /*!< D: Have sent 183 message progress */ #define SIP_NEEDREINVITE (1 << 4) /*!< D: Do we need to send another reinvite? */ @@ -415,7 +416,8 @@ enum subscriptiontype { DIALOG_INFO_XML, CPIM_PIDF_XML, PIDF_XML, - MWI_NOTIFICATION + MWI_NOTIFICATION, + CALL_COMPLETION, }; /*! \brief The number of media types in enum \ref media_type below. */ @@ -930,6 +932,7 @@ struct sip_pvt { AST_STRING_FIELD(url); /*!< URL to be sent with next message to peer */ AST_STRING_FIELD(parkinglot); /*!< Parkinglot */ AST_STRING_FIELD(engine); /*!< RTP engine to use */ + AST_STRING_FIELD(dialstring); /*!< The dialstring used to call this SIP endpoint */ ); char via[128]; /*!< Via: header */ struct sip_socket socket; /*!< The socket used for this dialog */ @@ -1066,6 +1069,8 @@ struct sip_pvt { * The large-scale changes would be a good idea for implementing during an SDP rewrite. */ struct offered_media offered_media[OFFERED_MEDIA_COUNT]; + struct ast_cc_config_params *cc_params; + struct sip_epa_entry *epa_entry; }; /*! \brief sip packet - raw format for outbound packets that are sent or scheduled for transmission @@ -1197,6 +1202,7 @@ struct sip_peer { /*XXX Seems like we suddenly have two flags with the same content. Why? To be continued... */ enum sip_peer_type type; /*!< Distinguish between "user" and "peer" types. This is used solely for CLI and manager commands */ unsigned int disallowed_methods; + struct ast_cc_config_params *cc_params; }; /*! @@ -1286,4 +1292,359 @@ struct sip_subscription_mwi { struct ast_dnsmgr_entry *dnsmgr; /*!< DNS refresh manager for subscription */ struct sockaddr_in us; /*!< Who the server thinks we are */ }; + +/*! + * SIP PUBLISH support! + * PUBLISH support was added to chan_sip due to its use in the call-completion + * event package. In order to suspend and unsuspend monitoring of a called party, + * a PUBLISH message must be sent. Rather than try to hack in PUBLISH transmission + * and reception solely for the purposes of handling call-completion-related messages, + * an effort has been made to create a generic framework for handling PUBLISH messages. + * + * There are two main components to the effort, the event publication agent (EPA) and + * the event state compositor (ESC). Both of these terms appear in RFC 3903, and the + * implementation in Asterisk conforms to the defintions there. An EPA is a UAC that + * transmits PUBLISH requests. An ESC is a UAS that receives PUBLISH requests and + * acts appropriately based on the content of those requests. + * + * ESC: + * The main structure in chan_sip is the event_state_compositor. There is an + * event_state_compositor structure for each event package supported (as of Nov 2009 + * this is only the call-completion package). The structure contains data which is + * intrinsic to the event package itself, such as the name of the package and a set + * of callbacks for handling incoming PUBLISH requests. In addition, the + * event_state_compositor struct contains an ao2_container of sip_esc_entries. + * + * A sip_esc_entry corresponds to an entity which has sent a PUBLISH to Asterisk. We are + * able to match the incoming PUBLISH to a sip_esc_entry using the Sip-If-Match header + * of the message. Of course, if none is present, then a new sip_esc_entry will be created. + * + * Once it is determined what type of PUBLISH request has come in (from RFC 3903, it may + * be an initial, modify, refresh, or remove), then the event package-specific callbacks + * may be called. If your event package doesn't need to take any specific action for a + * specific PUBLISH type, it is perfectly safe to not define the callback at all. The callback + * only needs to take care of application-specific information. If there is a problem, it is + * up to the callback to take care of sending an appropriate 4xx or 5xx response code. In such + * a case, the callback should return -1. This will tell the function that called the handler + * that an appropriate error response has been sent. If the callback returns 0, however, then + * the caller of the callback will generate a new entity tag and send a 200 OK response. + * + * ESC entries are reference-counted, however as an implementor of a specific event package, + * this should be transparent, since the reference counts are handled by the general ESC + * framework. + * + * EPA: + * The event publication agent in chan_sip is structured quite a bit differently than the + * ESC. With an ESC, an appropriate entry has to be found based on the contents of an incoming + * PUBLISH message. With an EPA, the application interested in sending the PUBLISH can maintain + * a reference to the appropriate EPA entry instead. Similarly, when matching a PUBLISH response + * to an appropriate EPA entry, the sip_pvt can maintain a reference to the corresponding + * EPA entry. The result of this train of thought is that there is no compelling reason to + * maintain a container of these entries. + * + * Instead, there is only the sip_epa_entry structure. Every sip_epa_entry has an entity tag + * that it maintains so that subsequent PUBLISH requests will be identifiable by the ESC on + * the far end. In addition, there is a static_data field which contains information that is + * common to all sip_epa_entries for a specific event package. This static data includes the + * name of the event package and callbacks for handling specific responses for outgoing PUBLISHes. + * Also, there is a field for pointing to instance-specific data. This can include the current + * published state or other identifying information that is specific to an instance of an EPA + * entry of a particular event package. + * + * When an application wishes to send a PUBLISH request, it simply will call create_epa_entry, + * followed by transmit_publish in order to send the PUBLISH. That's all that is necessary. + * Like with ESC entries, sip_epa_entries are reference counted. Unlike ESC entries, though, + * sip_epa_entries reference counts have to be maintained to some degree by the application making + * use of the sip_epa_entry. The application will acquire a reference to the EPA entry when it + * calls create_epa_entry. When the application has finished using the EPA entry (which may not + * be until after several PUBLISH transactions have taken place) it must use ao2_ref to decrease + * the reference count by 1. + */ + +/*! + * \brief The states that can be represented in a SIP call-completion PUBLISH + */ +enum sip_cc_publish_state { + /*! Closed, i.e. unavailable */ + CC_CLOSED, + /*! Open, i.e. available */ + CC_OPEN, +}; + +/*! + * \brief The states that can be represented in a SIP call-completion NOTIFY + */ +enum sip_cc_notify_state { + /*! Queued, i.e. unavailable */ + CC_QUEUED, + /*! Ready, i.e. available */ + CC_READY, +}; + +/*! + * \brief The types of PUBLISH messages defined in RFC 3903 + */ +enum sip_publish_type { + /*! + * \brief Unknown + * + * \details + * This actually is not defined in RFC 3903. We use this as a constant + * to indicate that an incoming PUBLISH does not fit into any of the + * other categories and is thus invalid. + */ + SIP_PUBLISH_UNKNOWN, + /*! + * \brief Initial + * + * \details + * The first PUBLISH sent. This will contain a non-zero Expires header + * as well as a body that indicates the current state of the endpoint + * that has sent the message. The initial PUBLISH is the only type + * of PUBLISH to not contain a Sip-If-Match header in it. + */ + SIP_PUBLISH_INITIAL, + /*! + * \brief Refresh + * + * \details + * Used to keep a published state from expiring. This will contain a + * non-zero Expires header but no body since its purpose is not to + * update state. + */ + SIP_PUBLISH_REFRESH, + /*! + * \brief Modify + * + * \details + * Used to change state from its previous value. This will contain + * a body updating the published state. May or may not contain an + * Expires header. + */ + SIP_PUBLISH_MODIFY, + /*! + * \brief Remove + * + * \details + * Used to remove published state from an ESC. This will contain + * an Expires header set to 0 and likely no body. + */ + SIP_PUBLISH_REMOVE, +}; + +/*! + * Data which is the same for all instances of an EPA for a + * particular event package + */ +struct epa_static_data { + /*! The event type */ + enum subscriptiontype event; + /*! + * The name of the event as it would + * appear in a SIP message + */ + const char *name; + /*! + * The callback called when a 200 OK is received on an outbound PUBLISH + */ + void (*handle_ok)(struct sip_pvt *, struct sip_request *, struct sip_epa_entry *); + /*! + * The callback called when an error response is received on an outbound PUBLISH + */ + void (*handle_error)(struct sip_pvt *, const int resp, struct sip_request *, struct sip_epa_entry *); + /*! + * Destructor to call to clean up instance data + */ + void (*destructor)(void *instance_data); +}; + +/*! + * \brief backend for an event publication agent + */ +struct epa_backend { + const struct epa_static_data *static_data; + AST_LIST_ENTRY(epa_backend) next; +}; + +struct sip_epa_entry { + /*! + * When we are going to send a publish, we need to + * know the type of PUBLISH to send. + */ + enum sip_publish_type publish_type; + /*! + * When we send a PUBLISH, we have to be + * sure to include the entity tag that we + * received in the previous response. + */ + char entity_tag[SIPBUFSIZE]; + /*! + * The destination to which this EPA should send + * PUBLISHes. This may be the name of a SIP peer + * or a hostname. + */ + char destination[SIPBUFSIZE]; + /*! + * The body of the most recently-sent PUBLISH message. + * This is useful for situations such as authentication, + * in which we must send a message identical to the + * one previously sent + */ + char body[SIPBUFSIZE]; + /*! + * Every event package has some constant data and + * callbacks that all instances will share. This + * data resides in this field. + */ + const struct epa_static_data *static_data; + /*! + * In addition to the static data that all instances + * of sip_epa_entry will have, each instance will + * require its own instance-specific data. + */ + void *instance_data; +}; + +/*! + * \brief Instance data for a Call completion EPA entry + */ +struct cc_epa_entry { + /*! + * The core ID of the CC transaction + * for which this EPA entry belongs. This + * essentially acts as a unique identifier + * for the entry and is used in the hash + * and comparison functions + */ + int core_id; + /*! + * We keep the last known state of the + * device in question handy in case + * it needs to be known by a third party. + * Also, in the case where for some reason + * we get asked to transmit state that we + * already sent, we can just ignore the + * request. + */ + enum sip_cc_publish_state current_state; +}; + +struct event_state_compositor; + +/*! + * \brief common ESC items for all event types + * + * The entity_id field serves as a means by which + * A specific entry may be found. + */ +struct sip_esc_entry { + /*! + * The name of the party who + * sent us the PUBLISH. This will more + * than likely correspond to a peer name. + * + * This field's utility isn't really that + * great. It's mainly just a user-recognizable + * handle that can be printed in debug messages. + */ + const char *device_name; + /*! + * The event package for which this esc_entry + * exists. Most of the time this isn't really + * necessary since you'll have easy access to the + * ESC which contains this entry. However, in + * some circumstances, we won't have the ESC + * available. + */ + const char *event; + /*! + * The entity ID used when corresponding + * with the EPA on the other side. As the + * ESC, we generate an entity ID for each + * received PUBLISH and store it in this + * structure. + */ + char entity_tag[30]; + /*! + * The ID for the scheduler. We schedule + * destruction of a sip_esc_entry when we + * receive a PUBLISH. The destruction is + * scheduled for the duration received in + * the Expires header. + */ + int sched_id; + /*! + * Each ESC entry will be for a specific + * event type. Those entries will need to + * carry data which is intrinsic to the + * ESC entry but which is specific to + * the event package + */ + void *event_specific_data; +}; + +typedef int (* const esc_publish_callback)(struct sip_pvt *, struct sip_request *, struct event_state_compositor *, struct sip_esc_entry *); + +/*! + * \brief Callbacks for SIP ESCs + * + * \details + * The names of the callbacks are self-explanatory. The + * corresponding handler is called whenever the specific + * type of PUBLISH is received. + */ +struct sip_esc_publish_callbacks { + const esc_publish_callback initial_handler; + const esc_publish_callback refresh_handler; + const esc_publish_callback modify_handler; + const esc_publish_callback remove_handler; +}; + +struct sip_cc_agent_pvt { + int offer_timer_id; + /* A copy of the original call's Call-ID. + * We use this as a search key when attempting + * to find a particular sip_pvt. + */ + char original_callid[SIPBUFSIZE]; + /* A copy of the exten called originally. + * We use this to set the proper extension + * to dial during the recall since the incoming + * request URI is one that was generated just + * for the recall + */ + char original_exten[SIPBUFSIZE]; + /* A reference to the dialog which we will + * be sending a NOTIFY on when it comes time + * to send one + */ + struct sip_pvt *subscribe_pvt; + /* When we send a NOTIFY, we include a URI + * that should be used by the caller when he + * wishes to send a PUBLISH or INVITE to us. + * We store that URI here. + */ + char notify_uri[SIPBUFSIZE]; + /* When we advertise call completion to a caller, + * we provide a URI for the caller to use when + * he sends us a SUBSCRIBE. We store it for matching + * purposes when we receive the SUBSCRIBE from the + * caller. + */ + char subscribe_uri[SIPBUFSIZE]; + char is_available; +}; + +struct sip_monitor_instance { + AST_DECLARE_STRING_FIELDS( + AST_STRING_FIELD(subscribe_uri); + AST_STRING_FIELD(notify_uri); + AST_STRING_FIELD(peername); + AST_STRING_FIELD(device_name); + ); + int core_id; + struct sip_pvt *subscription_pvt; + struct sip_epa_entry *suspension_entry; +}; + #endif diff --git a/configs/ccss.conf.sample b/configs/ccss.conf.sample new file mode 100644 index 000000000..420e4367b --- /dev/null +++ b/configs/ccss.conf.sample @@ -0,0 +1,150 @@ +[general] +; There is only a single option that may be defined in this file. +; The cc_max_requests option is a global limit on the number of +; CC requests that may be in the Asterisk system at any time. +; +;cc_max_requests = 20 +; +; +;============================================ +; PLEASE READ THIS!!! +; The options described below should NOT be +; set in this file. Rather, they should be +; set per-device in a channel driver +; configuration file. +; PLEASE READ THIS!!! +;=========================================== +; +;--------------------------------------------------------------------- +; Timers +;--------------------------------------------------------------------- +;There are three configurable timers for all types of CC: the +;cc_offer_timer, the ccbs_available_timer, and the ccnr_available_timer. +;In addition, when using a generic agent, there is a fourth timer, +;the cc_recall_timer. All timers are configured in seconds, and the +;values shown below are the defaults. +; +;When a caller is offered CCBS or CCNR, the cc_offer_timer will +;be started. If the caller does not request CC before the +;cc_offer_timer expires, then the caller will be unable to request +;CC for this call. +; +;cc_offer_timer = 20 +; +;Once a caller has requested CC, then either the ccbs_available_timer +;or the ccnr_available_timer will run, depending on the service +;requested. The reason why there are two separate timers for CCBS +;and CCNR is that it is reasonable to want to have a shorter timeout +;configured for CCBS than for CCNR. If the available timer expires +;before the called party becomes available, then the CC attempt +;will have failed and monitoring of the called party will stop. +; +;ccbs_available_timer = 4800 +;ccnr_available_timer = 7200 +; +; When using a generic agent, the original caller is called back +; when one of the original called parties becomes available. The +; cc_recall_timer tells Asterisk how long it should let the original +; caller's phone ring before giving up. Please note that this parameter +; only affects operation when using a generic agent. +; +;cc_recall_timer = 20 +;--------------------------------------------------------------------- +; Policies +;--------------------------------------------------------------------- +; Policy settings tell Asterisk how to behave and what sort of +; resources to allocate in order to facilitate CC. There are two +; settings to control the actions Asterisk will take. +; +; The cc_agent_policy describes the behavior that Asterisk will +; take when communicating with the caller during CC. There are +; three possible options. +; +;never: Never offer CC to the caller. Setting the cc_agent_policy +; to this value is the way to disable CC for a call. +; +;generic: A generic CC agent is one which uses no protocol-specific +; mechanisms to offer CC to the caller. Instead, the caller +; requests CC using a dialplan function. Due to internal +; restrictions, you should only use a generic CC agent on +; phones (i.e. not "trunks"). If you are using phones which +; do not support a protocol-specific method of using CC, then +; generic CC agents are what you should use. +; +;native: A native CC agent is one which uses protocol-specific +; signaling to offer CC to the caller and accept CC requests +; from the caller. The supported protocols for native CC +; agents are SIP, ISDN ETSI PTP, ISDN ETSI PTMP, and Q.SIG +;cc_agent_policy=never +; +; The cc_monitor_policy describes the behavior that Asterisk will +; take when communicating with the called party during CC. There +; are four possible options. +; +;never: Analogous to the cc_agent_policy setting. We will never +; attempt to request CC services on this interface. +; +;generic: Analogous to the cc_agent_policy setting. We will monitor +; the called party's progress using protocol-agnostic +; capabilities. Like with generic CC agents, generic CC +; monitors should only be used for phones. +; +;native: Analogous to the cc_agent_policy setting. We will use +; protocol-specific methods to request CC from this interface +; and to monitor the interface for availability. +; +;accept: If an interface is set to "accept," then we will accept +; protocol-specific CC offers from the caller and use +; a native CC monitor for the remainder of the CC transaction. +; However, if the interface does not offer protocol-specific +; CC, then we will fall back to using a generic CC monitor +; instead. This is a good setting to use for phones for which +; you do not know if they support protocol-specific CC +; methodologies. +;cc_monitor_policy=never +; +; +;--------------------------------------------------------------------- +; Limits +;--------------------------------------------------------------------- +; +; The use of CC requires Asterisk to potentially use more memory than +; some administrators would like. As such, it is a good idea to limit +; the number of CC requests that can be in the system at a given time. +; The values shown below are the defaults. +; +; The cc_max_agents setting limits the number of outstanding CC +; requests a caller may have at any given time. Please note that due +; to implementation restrictions, this setting is ignored when using +; generic CC agents. Generic CC agents may only have one outstanding +; CC request. +; +;cc_max_agents = 5 +; +; The cc_max_monitors setting limits the number of outstanding CC +; requests can be made to a specific interface at a given time. +; +;cc_max_monitors = 5 +; +;--------------------------------------------------------------------- +; Other +;--------------------------------------------------------------------- +; +; When using a generic CC agent, the caller who requested CC will be +; called back when a called party becomes available. When the caller +; answers his phone, the administrator may opt to have a macro run. +; What this macro does is up to the administrator. By default there +; is no callback macro configured. +; +;cc_callback_macro= +; +; When using an ISDN phone and a generic CC agent, Asterisk is unable +; to determine the dialstring that should be used when calling back +; the original caller. Furthermore, if you desire to use any dialstring- +; specific options, such as distinctive ring, you must set this +; configuration option. For non-ISDN phones, it is not necessary to +; set this, since Asterisk can determine the dialstring to use since +; it is identical to the name of the calling device. By default, there +; is no cc_agent_dialstring set. +; +;cc_agent_dialstring= diff --git a/configs/chan_dahdi.conf.sample b/configs/chan_dahdi.conf.sample index fb0d06931..30229b5fc 100644 --- a/configs/chan_dahdi.conf.sample +++ b/configs/chan_dahdi.conf.sample @@ -85,10 +85,11 @@ ;service_message_support=yes ; Enable service message support for channel. Must be set after switchtype. ; -; PRI Reverse Charging Indication: Indicate to the called party that the -; call will be reverse charged. To enable, prefix the dialed number with one -; of the following letters: -; C - Reverse Charge Indication Requested +; Dialing options for ISDN (i.e., Dial(DAHDI/g1/exten/options)): +; R Reverse Charge Indication +; Indicate to the called party that the call will be reverse charged. +; K(n) Keypad digits n +; Send out the specified digits as keypad digits. ; ; PRI Dialplan: The ISDN-level Type Of Number (TON) or numbering plan, used for ; the dialed number. For most installations, leaving this as 'unknown' (the @@ -236,9 +237,52 @@ ; May vary in other ISDN standards (Q.931 1993 : 90000 ms) ; T313: Wait for CONNECT acknowledge, CPE side only (default 3000 ms) ; +; T-RESPONSE: Maximum time to wait for a typical APDU response. (default 4000 ms) +; This is an implementation timer when the standard does not specify one. +; T-ACTIVATE: Request supervision timeout. (default 10000 ms) +; T-RETENTION: Maximum time to wait for user A to activate call-completion. (default 30000 ms) +; Used by ETSI PTP, ETSI PTMP, and Q.SIG as the cc_offer_timer. +; T-CCBS1: T-STATUS timer equivalent for CC user A status. (default 4000 ms) +; T-CCBS2: Maximum time the CCBS service will be active (default 45 min in ms) +; T-CCBS3: Maximum time to wait for user A to respond to user B availability. (default 20000 ms) +; T-CCBS5: Network B CCBS supervision timeout. (default 60 min in ms) +; T-CCBS6: Network A CCBS supervision timeout. (default 60 min in ms) +; T-CCNR2: Maximum time the CCNR service will be active (default 180 min in ms) +; T-CCNR5: Network B CCNR supervision timeout. (default 195 min in ms) +; T-CCNR6: Network A CCNR supervision timeout. (default 195 min in ms) +; CC-T1: Q.SIG CC request supervision timeout. (default 30000 ms) +; CCBS-T2: Q.SIG CCBS supervision timeout. (default 60 min in ms) +; CCNR-T2: Q.SIG CCNR supervision timeout. (default 195 min in ms) +; CC-T3: Q.SIG CC Maximum time to wait for user A to respond to user B availability. (default 30000 ms) +; ;pritimer => t200,1000 ;pritimer => t313,4000 ; +; CC PTMP recall mode: +; specific - Only the CC original party A can participate in the CC callback +; global - Other compatible endpoints on the PTMP line can be party A in the CC callback +; +; cc_ptmp_recall_mode cannot be changed on a reload. +; +;cc_ptmp_recall_mode = specific +; +; CC Q.SIG Party A (requester) retain signaling link option +; retain Require that the signaling link be retained. +; release Request that the signaling link be released. +; do_not_care The responder is free to choose if the signaling link will be retained. +; +;cc_qsig_signaling_link_req = retain +; +; CC Q.SIG Party B (responder) retain signaling link option +; retain Prefer that the signaling link be retained. +; release Prefer that the signaling link be released. +; +;cc_qsig_signaling_link_rsp = retain +; +; See ccss.conf.sample for more options. The timers described by ccss.conf.sample +; are not used by ISDN for the native protocol since they are defined by the +; standards and set by pritimer above. +; ; To enable transmission of facility-based ISDN supplementary services (such ; as caller name from CPE over facility), enable this option. ; Cannot be changed on a reload. @@ -267,6 +311,10 @@ ; fxo_ks: FXO (Kewl Start) ; pri_cpe: PRI signalling, CPE side ; pri_net: PRI signalling, Network side +; bri_cpe: BRI PTP signalling, CPE side +; bri_net: BRI PTP signalling, Network side +; bri_cpe_ptmp: BRI PTMP signalling, CPE side +; bri_net_ptmp: BRI PTMP signalling, Network side ; sf: SF (Inband Tone) Signalling ; sf_w: SF Wink ; sf_featd: SF Feature Group D (The fake, Adtran style, DTMF) diff --git a/configs/manager.conf.sample b/configs/manager.conf.sample index 229db2dac..078d17932 100644 --- a/configs/manager.conf.sample +++ b/configs/manager.conf.sample @@ -84,6 +84,7 @@ bindaddr = 0.0.0.0 ; Write authorization permits you to send commands and get back responses. The ; following classes exist: ; +; all - All event classes below (including any we may have missed). ; system - General information about the system and ability to run system ; management commands, such as Shutdown, Restart, and Reload. ; call - Information about channels and ability to set information in a @@ -100,6 +101,8 @@ bindaddr = 0.0.0.0 ; cdr - Output of cdr_manager, if loaded. Read-only. ; dialplan - Receive NewExten and VarSet events. Read-only. ; originate - Permission to originate new calls. Write-only. +; agi - Output AGI commands executed. Input AGI command to execute. +; cc - Call Completion events. Read-only. ; ;read = system,call,log,verbose,agent,user,config,dtmf,reporting,cdr,dialplan ;write = system,call,agent,user,config,command,reporting,originate diff --git a/configure.ac b/configure.ac index 7a4cbb8eb..9e774683b 100644 --- a/configure.ac +++ b/configure.ac @@ -335,6 +335,7 @@ AST_EXT_LIB_SETUP([PGSQL], [PostgreSQL], [postgres]) AST_EXT_LIB_SETUP([POPT], [popt], [popt]) AST_EXT_LIB_SETUP([PORTAUDIO], [PortAudio], [portaudio]) AST_EXT_LIB_SETUP([PRI], [ISDN PRI], [pri]) +AST_EXT_LIB_SETUP_DEPENDENT([PRI_CCSS], [ISDN PRI call completion supplementary service], [PRI], [pri]) AST_EXT_LIB_SETUP_DEPENDENT([PRI_SUBADDR], [ISDN PRI subaddressing], [PRI], [pri]) AST_EXT_LIB_SETUP_DEPENDENT([PRI_CALL_HOLD], [ISDN PRI call hold], [PRI], [pri]) AST_EXT_LIB_SETUP_DEPENDENT([PRI_CALL_REROUTING], [ISDN PRI call rerouting and call deflection], [PRI], [pri]) @@ -1543,6 +1544,7 @@ AST_EXT_LIB_CHECK([POPT], [popt], [poptStrerror], [popt.h]) AST_EXT_LIB_CHECK([PORTAUDIO], [portaudio], [Pa_GetDeviceCount], [portaudio.h]) AST_EXT_LIB_CHECK([PRI], [pri], [pri_connected_line_update], [libpri.h]) +AST_EXT_LIB_CHECK([PRI_CCSS], [pri], [pri_cc_enable], [libpri.h]) AST_EXT_LIB_CHECK([PRI_SUBADDR], [pri], [pri_sr_set_called_subaddress], [libpri.h]) AST_EXT_LIB_CHECK([PRI_CALL_HOLD], [pri], [pri_hold_enable], [libpri.h]) AST_EXT_LIB_CHECK([PRI_CALL_REROUTING], [pri], [pri_reroute_enable], [libpri.h]) diff --git a/doc/tex/asterisk.tex b/doc/tex/asterisk.tex index 8097d14ed..0427389b1 100644 --- a/doc/tex/asterisk.tex +++ b/doc/tex/asterisk.tex @@ -147,6 +147,9 @@ reference purposes. \chapter{Security Framework} \input{security-events.tex} +\chapter{Call Completion Supplementary Services} + \input{ccss.tex} + \chapter{Development} \section{Backtrace} \input{backtrace.tex} diff --git a/doc/tex/ccss.tex b/doc/tex/ccss.tex new file mode 100644 index 000000000..cfe07cbe0 --- /dev/null +++ b/doc/tex/ccss.tex @@ -0,0 +1,414 @@ +\section{Introduction} + + A new feature for Asterisk 1.8 is Call Completion Supplementary +Services. This document aims to explain the system and how to use it. +In addition, this document examines some potential troublesome points +which administrators may come across during their deployment of the +feature. + +\section{What is CCSS?} + + Call Completion Supplementary Services (often abbreviated "CCSS" or +simply "CC") allow for a caller to let Asterisk automatically alert him +when a called party has become available, given that a previous call to +that party failed for some reason. The two services offered are Call +Completion on Busy Subscriber (CCBS) and Call Completion on No Response +(CCNR). + To illustrate, let's say that Alice attempts to call Bob. Bob is +currently on a phone call with Carol, though, so Alice hears a busy +signal. In this situation, assuming that Asterisk has been configured +to allow for such activity, Alice would be able to request CCBS. Once +Bob has finished his phone call, Alice will be alerted. Alice can then +attempt to call Bob again. + +\section{Glossary of Terms} + + In this document, we will use some terms which may require +clarification. Most of these terms are specific to Asterisk, and are by +no means standard. + +\begin{itemize} +\item CCBS: Call Completion on Busy Subscriber. When a call fails because the +recipient's phone is busy, the caller will have the opportunity to +request CCBS. When the recipient's phone is no longer busy, the caller +will be alerted. The means by which the caller is alerted is dependent +upon the type of agent used by the caller. + +\item CCNR: Call Completion on No Response. When a call fails because the +recipient does not answer the phone, the caller will have the opportun- +ity to request CCNR. When the recipient's phone becomes busy and then +is no longer busy, the caller will be alerted. The means by which the +caller is alerted is dependent upon the type of the agent used by the +caller. + +\item Agent: The agent is the entity within Asterisk that communicates with +and acts on behalf of the calling party. + +\item Monitor: The monitor is the entity within Asterisk that communicates +with and monitors the status of the called party. + +\item Generic Agent: A generic agent is an agent that uses protocol-agnostic +methods to communicate with the caller. Generic agents should only be +used for phones, and never should be used for "trunks." + +\item Generic Monitor: A generic monitor is a monitor that uses protocol- +agnostic methods to monitor the status of the called party. Like with +generic agents, generic monitors should only be used for phones. + +\item Native Agent: The opposite of a generic agent. A native agent uses +protocol-specific messages to communicate with the calling party. +Native agents may be used for both phones and trunks, but it must be +known ahead of time that the device with which Asterisk is communica- +ting supports the necessary signaling. + +\item Native Monitor: The opposite of a generic monitor. A native monitor +uses protocol-specific messages to subscribe to and receive notifica- +tion of the status of the called party. Native monitors may be used +for both phones and trunks, but it must be known ahead of time that +the device with which Asterisk is communicating supports the +necessary signaling. + +\item Offer: An offer of CC refers to the notification received by the caller +that he may request CC. + +\item Request: When the caller decides that he would like to subscribe to CC, +he will make a request for CC. Furthermore, the term may refer to any +outstanding requests made by callers. + +\item Recall: When the caller attempts to call the recipient after being +alerted that the recipient is available, this action is referred to +as a "recall." +\end{itemize} + +\section{The CC Process} + +\subsection{The Initial Call} + + The only requirement for the use of CC is to configure an agent for +the caller and a monitor for at least one recipient of the call. +This is controlled using the cc\_agent\_policy for the caller and the +cc\_monitor\_policy for the recipient. For more information about these +configuration settings, see configs/samples/ccss.conf.sample. If the +agent for the caller is set to something other than "never" and at +least one recipient has his monitor set to something other than +"never," then CC will be offered to the caller at the end of the +call. + + Once the initial call has been hung up, the configured +cc\_offer\_timer for the caller will be started. If the caller wishes to +request CC for the previous call, he must do so before the timer +expires. + +\subsection{Requesting CC} + + Requesting CC is done differently depending on the type of agent +the caller is using. + + With generic agents, the CallCompletionRequest application must be +called in order to request CC. There are two different ways in which +this may be called. It may either be called before the caller hangs up +during the initial call, or the caller may hang up from the initial +call and dial an extension which calls the CallCompletionRequest +application. If the second method is used, then the caller will +have until the cc\_offer\_timer expires to request CC. + + With native agents, the method for requesting CC is dependent upon +the technology being used, coupled with the make of equipment. It may +be possible to request CC using a programmable key on a phone or by +clicking a button on a console. If you are using equipment which can +natively support CC but do not know the means by which to request it, +then contact the equipment manufacturer for more information. + +\subsection{Cancelling CC} + + CC may be canceled after it has been requested. The method by which +this is accomplished differs based on the type of agent the calling +party uses. + + When using a generic agent, the dialplan application +CallRequestCancel is used to cancel CC. When using a native monitor, +the method by which CC is cancelled depends on the protocol used. +Likely, this will be done using a button on a phone. + + Keep in mind that if CC is cancelled, it cannot be un-cancelled. + +\subsection{Monitoring the Called Party} + + Once the caller has requested CC, then Asterisk's job is to monitor +the progress of the called parties. It is at this point that Asterisk +allocates the necessary resources to monitor the called parties. + + A generic monitor uses Asterisk's device state subsystem in order +to determine when the called party has become available. For both CCBS +and CCNR, Asterisk simply waits for the phone's state to change to +a "not in use" state from a different state. Once this happens, then +Asterisk will consider the called party to be available and will alert +the caller. + + A native monitor relies on the network to send a protocol-specific +message when the called party has become available. When Asterisk +receives such a message, it will consider the called party to be +available and will alert the caller. + + Note that since a single caller may dial multiple parties, a monitor +is used for each called party. It is within reason that different called +parties will use different types of monitors for the same CC request. + +\subsection{Alerting the Caller} + + Once Asterisk has determined that the called party has become available +the time comes for Asterisk to alert the caller that the called party has +become available. The method by which this is done differs based on the +type of agent in use. + + If a generic agent is used, then Asterisk will originate a call to +the calling party. Upon answering the call, if a callback macro has +been configured, then that macro will be executed on the calling +party's channel. After the macro has completed, an outbound call +will be issued to the parties involved in the original call. + + If a native agent is used, then Asterisk will send an appropriate +notification message to the calling party to alert it that it may now +attempt its recall. How this is presented to the caller is dependent +upon the protocol and equipment that the caller is using. It is +possible that the calling party's phone will ring and a recall will +be triggered upon answering the phone, or it may be that the user +has a specific button that he may press to initiate a recall. + +\subsection{If the Caller is unavailable} + + When the called party has become available, it is possible that +when Asterisk attempts to alert the calling party of the called party's +availability, the calling party itself will have become unavailable. +If this is the case, then Asterisk will suspend monitoring of the +called party and will instead monitor the availability of the calling +party. The monitoring procedure for the calling party is the same +as is used in the section "Monitoring the Called Party." In other +words, the method by which the calling party is monitored is dependent +upon the type of agent used by the caller. + + Once Asterisk has determined that the calling party has become +available again, Asterisk will then move back to the process used +in the section "Monitoring the Called Party." + +\subsection{The CC recall} + + The calling party will make its recall to the same extension +that was dialed. Asterisk will provide a channel variable, +CC\_INTERFACES, to be used as an argument to the Dial application +for CC recalls. It is strongly recommended that you use this +channel variable during a CC recall. Listed are two reasons: + +\begin{itemize} +\item The dialplan may be written in such a way that the dialed +destintations are dynamically generated. With such a dialplan, it +cannot be guaranteed that the same interfaces will be recalled. +\item For calling destinations with native CC monitors, it may be +necessary to dial a special string in order to notify the channel +driver that the number being dialed is actually part of a CC recall. +\end{itemize} + + Note that even if your call gets routed through local channels, +the CC\_INTERFACES variable will be populated with the appropriate +values for that specific extension. + When the called parties are dialed, it is expected that a called +party will answer, since Asterisk had previously determined that the +party was available. However, it is possible that the called party +may choose not to respond to the call, or he could have become busy +again. In such a situation, the calling party must re-request CC if +he wishes to still be alerted when the calling party has become +available. + +\section{Miscellaneous Information and Tips} + +\begin{itemize} +\item Be aware when using a generic agent that the max\_cc\_agents +configuration parameter is ignored. The main driving reason for +this is that the mechanism for cancelling CC when using a generic +agent would become much more potentially confusing to execute. By +limiting a calling party to having a single request, there is only +ever a single request to be cancelled, making the process simple. + +\item Keep in mind that no matter what CC agent type is being used, +a CC request can only be made for the latest call issued. + +\item If available timers are running on multiple called parties, +it is possible that one of the timers may expire before the others +do. If such a situation occurs, then the interface on which the +timer expired will cease to be monitored. If, though, one of the +other called parties becomes available before his available timer +expires, the called party whose available timer had previously +expired will still be included in the CC\_INTERFACES channel +variable on the recall. + +\item It is strongly recommended that lots of thought is placed +into the settings of the CC timers. Our general recommendation is +that timers for phones should be set shorter than those for trunks. +The reason for this is that it makes it less likely for a link in +the middle of a network to cause CC to fail. + +\item CC can potentially be a memory hog if used irresponsibly. The +following are recommendations to help curb the amount of resources +required by the CC engine. First, limit the maximum number of +CC requests in the system using the cc\_max\_requests option in +ccss.conf. Second, set the cc\_offer\_timer low for your callers. Since +it is likely that most calls will not result in a CC request, it is +a good idea to set this value to something low so that information +for calls does not stick around in memory for long. The final thing +that can be done is to conditionally set the cc\_agent\_policy to +"never" using the CALLCOMPLETION dialplan function. By doing this, +no CC information will be kept around after the call completes. + +\item It is possible to request CCNR on answered calls. The reason +for this is that it is impossible to know whether a call that is +answered has actually been answered by a person or by something +such as voicemail or some other IVR. + +\item Not all channel drivers have had the ability to set CC config +parameters in their configuration files added yet. At the time of +this writing (2009 Oct), only chan\_sip has had this ability added, with +short-term plans to add this to chan\_dahdi as well. It is +possible to set CC configuration parameters for other channel types, +though. For these channel types, the setting of the parameters can +only be accomplished using the CALLCOMPLETION dialplan function. + +\item It is documented in many places that generic agents and monitors +can only be used for phones. In most cases, however, Asterisk has no +way of distinguishing between a phone and a trunk itself. The result +is that Asterisk will happily let you violate the advice given and +allow you to set up a trunk with a generic monitor or agent. While this +will not cause anything catastrophic to occur, the behavior will most +definitely not be what you want. + +\item At the time of this writing (2009 Oct), Asterisk is the only +known SIP stack to write an implementation of +draft-ietf-bliss-call-completion-04. As a result, it is recommended +that for your SIP phones, use a generic agent and monitor. For SIP +trunks, you will only be able to use CC if the other end is +terminated by another Asterisk server running version 1.8 or later. + +\item If the Dial application is called multiple times by a single +extension, CC will only be offered to the caller for the parties called +by the first instantiation of Dial. + +\item If a phone forwards a call, then CC may only be requested for +the phone that executed the call forward. CC may not be requested +for the phone to which the call was forwarded. + +\item CC is currently only supported by the Dial application. Queue, +Followme, and Page do not support CC because it is not particularly +useful for those applications. + +\item Generic CC relies heavily on accurate device state reporting. In +particular, when using SIP phones it is vital to be sure that device +state is updated properly when using them. In order to facilitate proper +device state handling, be sure to set callcounter=yes for all peers and +to set limitonpeers=yes in the general section of sip.conf + +\item When using SIP CC (i.e. native CC over SIP), it is important that +your minexpiry and maxexpiry values allow for available timers to run +as little or as long as they are configured. When an Asterisk server +requests call completion over SIP, it sends a SUBSCRIBE message with +an Expires header set to the number of seconds that the available +timer should run. If the Asterisk server that receives this SUBSCRIBE +has a maxexpiry set lower than what is in the received Expires header, +then the available timer will only run for maxexpiry seconds. + +\item As with all Asterisk components, CC is not perfect. If you should +find a bug or wish to enhance the feature, please open an issue on +https://issues.asterisk.org. If writing an enhancement, please be sure +to include a patch for the enhancement, or else the issue will be +closed. + +\end{itemize} + +\section{Simple Example of generic call completion} + +The following is an incredibly bare-bones example sip.conf +and dialplan to show basic usage of generic call completion. +It is likely that if you have a more complex setup, you will +need to make use of items like the CALLCOMPLETION dialplan +function or the CC\_INTERFACES channel variable. + +First, let's establish a very simple sip.conf to use for this + +\begin{verbatim} +[Mark] +context=phone_calls +cc_agent_policy=generic +cc_monitor_policy=generic +;We will accept defaults for the rest of the cc parameters +;We also are not concerned with other SIP details for this +;example + +[Richard] +context=phone_calls +cc_agent_policy=generic +cc_monitor_policy=generic +\end{verbatim} + +Now, let's write a simple dialplan + +\begin{verbatim} +[phone_calls] + +exten => 1000,1,Dial(SIP/Mark,20) +exten => 1000,n,Hangup + +exten => 2000,1,Dial(SIP/Richard,20) +exten => 2000,n,Hangup + +exten => 30,1,CallCompletionRequest +exten => 30,n,Hangup + +exten => 31,1,CallCompletionCancel +exten => 31,n,Hangup +\end{verbatim} + +\begin{itemize} +\item Scenario 1: +Mark picks up his phone and dials Richard by dialing 2000. Richard is +currently on a call, so Mark hears a busy signal. Mark then hangs up, +picks up the phone and dials 30 to call the CallCompletionRequest +application. After some time, Richard finishes his call and hangs up. +Mark is automatically called back by Asterisk. When Mark picks up his +phone, Asterisk will dial extension 2000 for him. + +\item Scenario 2: +Richard picks up his phone and dials Mark by dialing 1000. Mark has stepped +away from his desk, and so he is unable to answer the phone within the +20 second dial timeout. Richard hangs up, picks the phone back up and then +dials 30 to request call completion. Mark gets back to his desk and dials +somebody's number. When Mark finishes the call, Asterisk detects that Mark's +phone has had some activity and has become available again and rings Richard's +phone. Once Richard picks up, Asterisk automatically dials exteision 1000 for +him. + +\item Scenario 3: +Much like scenario 1, Mark calls Richard and Richard is busy. Mark hangs up, +picks the phone back up and then dials 30 to request call completion. After +a little while, Mark realizes he doesn't actually need to talk to Richard, so +he dials 31 to cancel call completion. When Richard becomes free, Mark will +not automatically be redialed by Asterisk. + +\item Scenario 4: +Richard calls Mark, but Mark is busy. About thirty seconds later, Richard decides +that he should perhaps request call completion. However, since Richard's phone +has the default cc\_offer\_timer of 20 seconds, he has run out of time to +request call completion. He instead must attempt to dial Mark again manually. If +Mark is still busy, Richard can attempt to request call completion on this second +call instead. + +\item Scenario 5: +Mark calls Richard, and Richard is busy. Mark requests call completion. Richard +does not finish his current call for another 2 hours (7200 seconds). Since Mark +has the default ccbs\_available\_timer of 4800 seconds set, Mark will not be +automatically recalled by Asterisk when Richard finishes his call. + +\item Scenario 6: +Mark calls Richard, and Richard does not respond within the 20 second dial timeout. +Mark requests call completion. Richard does not use his phone again for another +4 hours (144000 seconds). Since Mark has the default ccnr\_available\_timer +of 7200 seconds set, Mark will not be automatically recalled by Asterisk when +Richard finishes his call. +\end{itemize} diff --git a/funcs/func_callcompletion.c b/funcs/func_callcompletion.c new file mode 100644 index 000000000..191667bb0 --- /dev/null +++ b/funcs/func_callcompletion.c @@ -0,0 +1,114 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2010, Digium, Inc. + * + * Mark Michelson <mmichelson@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * \brief Call Completion Supplementary Services implementation + * \author Mark Michelson <mmichelson@digium.com> + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/module.h" +#include "asterisk/channel.h" +#include "asterisk/ccss.h" +#include "asterisk/pbx.h" + +/*** DOCUMENTATION + <function name="CALLCOMPLETION" language="en_US"> + <synopsis> + Get or set a call completion configuration parameter for a channel. + </synopsis> + <syntax> + <parameter name="option" required="true"> + <para>The allowable options are:</para> + <enumlist> + <enum name="cc_agent_policy" /> + <enum name="cc_monitor_policy" /> + <enum name="cc_offer_timer" /> + <enum name="ccnr_available_timer" /> + <enum name="ccbs_available_timer" /> + <enum name="cc_recall_timer" /> + <enum name="cc_max_agents" /> + <enum name="cc_max_monitors" /> + <enum name="cc_callback_macro" /> + <enum name="cc_agent_dialstring" /> + </enumlist> + </parameter> + </syntax> + <description> + <para>The CALLCOMPLETION function can be used to get or set a call + completion configuration parameter for a channel. Note that setting + a configuration parameter will only change the parameter for the + duration of the call.</para> + </description> + </function> + ***/ + +static int acf_cc_read(struct ast_channel *chan, const char *name, char *data, + char *buf, size_t buf_len) +{ + struct ast_cc_config_params *cc_params; + int res; + + ast_channel_lock(chan); + if (!(cc_params = ast_channel_get_cc_config_params(chan))) { + ast_channel_unlock(chan); + return -1; + } + + res = ast_cc_get_param(cc_params, data, buf, buf_len); + ast_channel_unlock(chan); + return res; +} + +static int acf_cc_write(struct ast_channel *chan, const char *cmd, char *data, + const char *value) +{ + struct ast_cc_config_params *cc_params; + int res; + + ast_channel_lock(chan); + if (!(cc_params = ast_channel_get_cc_config_params(chan))) { + ast_channel_unlock(chan); + return -1; + } + + res = ast_cc_set_param(cc_params, data, value); + ast_channel_unlock(chan); + return res; +} + +static struct ast_custom_function cc_function = { + .name = "CALLCOMPLETION", + .read = acf_cc_read, + .write = acf_cc_write, +}; + +static int unload_module(void) +{ + return ast_custom_function_unregister(&cc_function); +} + +static int load_module(void) +{ + return ast_custom_function_register(&cc_function) == 0 ? AST_MODULE_LOAD_SUCCESS : AST_MODULE_LOAD_DECLINE; +} + +AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Call Control Configuration Function"); diff --git a/include/asterisk/ccss.h b/include/asterisk/ccss.h new file mode 100644 index 000000000..c2d7ec850 --- /dev/null +++ b/include/asterisk/ccss.h @@ -0,0 +1,1582 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2010, Digium, Inc. + * + * Mark Michelson <mmichelson@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * \brief Call Completion Supplementary Services API + * \author Mark Michelson <mmichelson@digium.com> + */ + +#ifndef _ASTERISK_CCSS_H +#define _ASTERISK_CCSS_H + +#include "asterisk.h" + +#include "asterisk/linkedlists.h" +#include "asterisk/devicestate.h" + +enum ast_cc_service_type { + /* No Service available/requested */ + AST_CC_NONE, + /* Call Completion Busy Subscriber */ + AST_CC_CCBS, + /* Call Completion No Response */ + AST_CC_CCNR, + /* Call Completion Not Logged In (currently SIP only) */ + AST_CC_CCNL, +}; + +/*! + * \since 1.8 + * \brief The various possibilities for cc_agent_policy values + */ +enum ast_cc_agent_policies { + /*! Never offer CCSS to the caller */ + AST_CC_AGENT_NEVER, + /*! Offer CCSS using native signaling */ + AST_CC_AGENT_NATIVE, + /*! Use generic agent for caller */ + AST_CC_AGENT_GENERIC, +}; + +/*! + * \brief agent flags that can alter core behavior + */ +enum ast_cc_agent_flags { + /* Some agent types allow for a caller to + * request CC without reaching the CC_CALLER_OFFERED + * state. In other words, the caller can request + * CC while he is still on the phone from the failed + * call. The generic agent is an agent which allows + * for this behavior. + */ + AST_CC_AGENT_SKIP_OFFER = (1 << 0), +}; + +/*! + * \since 1.8 + * \brief The various possibilities for cc_monitor_policy values + */ +enum ast_cc_monitor_policies { + /*! Never accept CCSS offers from callee */ + AST_CC_MONITOR_NEVER, + /* CCSS only available if callee offers it through signaling */ + AST_CC_MONITOR_NATIVE, + /*! Always use CCSS generic monitor for callee + * Note that if callee offers CCSS natively, we still + * will use a generic CCSS monitor if this is set + */ + AST_CC_MONITOR_GENERIC, + /*! Accept native CCSS offers, but if no offer is present, + * use a generic CCSS monitor + */ + AST_CC_MONITOR_ALWAYS, +}; + +/* Forward declaration. Struct is in main/ccss.c */ +struct ast_cc_config_params; + +/*! + * \since 1.8 + * \brief Queue an AST_CONTROL_CC frame + * + * \note + * Since this function calls ast_queue_frame, the channel will be + * locked during the course of this function. + * + * \param chan The channel onto which to queue the frame + * \param monitor_type The type of monitor to use when CC is requested + * \param dialstring The dial string used to call the device + * \param service The type of CC service the device is willing to offer + * \param private_data If a native monitor is being used, and some channel-driver-specific private + * data has been allocated, then this parameter should contain a pointer to that data. If using a generic + * monitor, this parameter should remain NULL. Note that if this function should fail at some point, + * it is the responsibility of the caller to free the private data upon return. + * \retval 0 Success + * \retval -1 Error + */ +int ast_queue_cc_frame(struct ast_channel *chan, const char * const monitor_type, + const char * const dialstring, enum ast_cc_service_type service, void *private_data); + +/*! + * \brief Allocate and initialize an ast_cc_config_params structure + * + * \note + * Reasonable default values are chosen for the parameters upon allocation. + * + * \retval NULL Unable to allocate the structure + * \retval non-NULL A pointer to the newly allocated and initialized structure + */ +struct ast_cc_config_params *__ast_cc_config_params_init(const char *file, int line, const char *function); + +/*! + * \brief Allocate and initialize an ast_cc_config_params structure + * + * \note + * Reasonable default values are chosen for the parameters upon allocation. + * + * \retval NULL Unable to allocate the structure + * \retval non-NULL A pointer to the newly allocated and initialized structure + */ +#define ast_cc_config_params_init() __ast_cc_config_params_init(__FILE__, __LINE__, __PRETTY_FUNCTION__) + +/*! + * \brief Free memory from CCSS configuration params + * + * \note + * Just a call to ast_free for now... + * + * \param params Pointer to structure whose memory we need to free + * \retval void + */ +void ast_cc_config_params_destroy(struct ast_cc_config_params *params); + +/*! + * \brief set a CCSS configuration parameter, given its name + * + * \note + * Useful when parsing config files when used in conjunction + * with ast_ccss_is_cc_config_param. + * + * \param params The parameter structure to set the value on + * \param name The name of the cc parameter + * \param value The value of the parameter + * \retval 0 Success + * \retval -1 Failure + */ +int ast_cc_set_param(struct ast_cc_config_params *params, const char * const name, + const char * value); + +/*! + * \brief get a CCSS configuration parameter, given its name + * + * \note + * Useful when reading input as a string, like from dialplan or + * manager. + * + * \param params The CCSS configuration from which to get the value + * \param name The name of the CCSS parameter we want + * \param buf A preallocated buffer to hold the value + * \param buf_len The size of buf + * \retval 0 Success + * \retval -1 Failure + */ +int ast_cc_get_param(struct ast_cc_config_params *params, const char * const name, + char *buf, size_t buf_len); + +/*! + * \since 1.8 + * \brief Is this a CCSS configuration parameter? + * \param name Name of configuration option being parsed. + * \retval 1 Yes, this is a CCSS configuration parameter. + * \retval 0 No, this is not a CCSS configuration parameter. + */ +int ast_cc_is_config_param(const char * const name); + +/*! + * \since 1.8 + * \brief copy CCSS configuration parameters from one structure to another + * + * \details + * For now, this is a simple memcpy, but this function is necessary since + * the size of an ast_cc_config_params structure is unknown outside of + * main/ccss.c. Also, this allows for easier expansion of the function in + * case it becomes more complex than just a memcpy. + * + * \param src The structure from which data is copied + * \param dest The structure to which data is copied + * \retval -1 Copy failed (no way for this to happen yet) + * \retval 0 Copy succeeded + */ +void ast_cc_copy_config_params(struct ast_cc_config_params *dest, const struct ast_cc_config_params *src); + +/*! + * \since 1.8 + * \brief Get the cc_agent_policy + * \param config The configuration to retrieve the policy from + * \return The current cc_agent_policy for this configuration + */ +enum ast_cc_agent_policies ast_get_cc_agent_policy(struct ast_cc_config_params *config); + +/*! + * \since 1.8 + * \brief Set the cc_agent_policy + * \param config The configuration to set the cc_agent_policy on + * \param value The new cc_agent_policy we want to change to + * \retval 0 Success + * \retval -1 Failure (likely due to bad input) + */ +int ast_set_cc_agent_policy(struct ast_cc_config_params *config, enum ast_cc_agent_policies value); + +/*! + * \since 1.8 + * \brief Get the cc_monitor_policy + * \param config The configuration to retrieve the cc_monitor_policy from + * \return The cc_monitor_policy retrieved from the configuration + */ +enum ast_cc_monitor_policies ast_get_cc_monitor_policy(struct ast_cc_config_params *config); + +/*! + * \since 1.8 + * \brief Set the cc_monitor_policy + * \param config The configuration to set the cc_monitor_policy on + * \param value The new cc_monitor_policy we want to change to + * \retval 0 Success + * \retval -1 Failure (likely due to bad input) + */ +int ast_set_cc_monitor_policy(struct ast_cc_config_params *config, enum ast_cc_monitor_policies value); + +/*! + * \since 1.8 + * \brief Get the cc_offer_timer + * \param config The configuration to retrieve the cc_offer_timer from + * \return The cc_offer_timer from this configuration + */ +unsigned int ast_get_cc_offer_timer(struct ast_cc_config_params *config); + +/*! + * \since 1.8 + * \brief Set the cc_offer_timer + * \param config The configuration to set the cc_offer_timer on + * \param value The new cc_offer_timer we want to change to + * \retval void + */ +void ast_set_cc_offer_timer(struct ast_cc_config_params *config, unsigned int value); + +/*! + * \since 1.8 + * \brief Get the ccnr_available_timer + * \param config The configuration to retrieve the ccnr_available_timer from + * \return The ccnr_available_timer from this configuration + */ +unsigned int ast_get_ccnr_available_timer(struct ast_cc_config_params *config); + +/*! + * \since 1.8 + * \brief Set the ccnr_available_timer + * \param config The configuration to set the ccnr_available_timer on + * \param value The new ccnr_available_timer we want to change to + * \retval void + */ +void ast_set_ccnr_available_timer(struct ast_cc_config_params *config, unsigned int value); + +/*! + * \since 1.8 + * \brief Get the cc_recall_timer + * \param config The configuration to retrieve the cc_recall_timer from + * \return The cc_recall_timer from this configuration + */ +unsigned int ast_get_cc_recall_timer(struct ast_cc_config_params *config); + +/*! + * \since 1.8 + * \brief Set the cc_recall_timer + * \param config The configuration to set the cc_recall_timer on + * \param value The new cc_recall_timer we want to change to + * \retval void + */ +void ast_set_cc_recall_timer(struct ast_cc_config_params *config, unsigned int value); + +/*! + * \since 1.8 + * \brief Get the ccbs_available_timer + * \param config The configuration to retrieve the ccbs_available_timer from + * \return The ccbs_available_timer from this configuration + */ +unsigned int ast_get_ccbs_available_timer(struct ast_cc_config_params *config); + +/*! + * \since 1.8 + * \brief Set the ccbs_available_timer + * \param config The configuration to set the ccbs_available_timer on + * \param value The new ccbs_available_timer we want to change to + * \retval void + */ +void ast_set_ccbs_available_timer(struct ast_cc_config_params *config, unsigned int value); + +/*! + * \since 1.8 + * \brief Get the cc_agent_dialstring + * \param config The configuration to retrieve the cc_agent_dialstring from + * \return The cc_agent_dialstring from this configuration + */ +const char *ast_get_cc_agent_dialstring(struct ast_cc_config_params *config); + +/*! + * \since 1.8 + * \brief Set the cc_agent_dialstring + * \param config The configuration to set the cc_agent_dialstring on + * \param value The new cc_agent_dialstring we want to change to + * \retval void + */ +void ast_set_cc_agent_dialstring(struct ast_cc_config_params *config, const char *const value); + +/*! + * \since 1.8 + * \brief Get the cc_max_agents + * \param config The configuration to retrieve the cc_max_agents from + * \return The cc_max_agents from this configuration + */ +unsigned int ast_get_cc_max_agents(struct ast_cc_config_params *config); + +/*! + * \since 1.8 + * \brief Set the cc_max_agents + * \param config The configuration to set the cc_max_agents on + * \param value The new cc_max_agents we want to change to + * \retval void + */ +void ast_set_cc_max_agents(struct ast_cc_config_params *config, unsigned int value); + +/*! + * \since 1.8 + * \brief Get the cc_max_monitors + * \param config The configuration to retrieve the cc_max_monitors from + * \return The cc_max_monitors from this configuration + */ +unsigned int ast_get_cc_max_monitors(struct ast_cc_config_params *config); + +/*! + * \since 1.8 + * \brief Set the cc_max_monitors + * \param config The configuration to set the cc_max_monitors on + * \param value The new cc_max_monitors we want to change to + * \retval void + */ +void ast_set_cc_max_monitors(struct ast_cc_config_params *config, unsigned int value); + +/*! + * \since 1.8 + * \brief Get the name of the callback_macro + * \param config The configuration to retrieve the callback_macro from + * \return The callback_macro name + */ +const char *ast_get_cc_callback_macro(struct ast_cc_config_params *config); + +/*! + * \since 1.8 + * \brief Set the callback_macro name + * \param config The configuration to set the callback_macro on + * \param value The new callback macro we want to change to + * \retval void + */ +void ast_set_cc_callback_macro(struct ast_cc_config_params *config, const char * const value); + +/* END CONFIGURATION FUNCTIONS */ + +/* BEGIN AGENT/MONITOR REGISTRATION API */ + +struct ast_cc_monitor_callbacks; + +/*! + * \since 1.8 + * \brief Register a set of monitor callbacks with the core + * + * \details + * This is made so that at monitor creation time, the proper callbacks + * may be installed and the proper .init callback may be called for the + * monitor to establish private data. + * + * \param callbacks The callbacks used by the monitor implementation + * \retval 0 Successfully registered + * \retval -1 Failure to register + */ +int ast_cc_monitor_register(const struct ast_cc_monitor_callbacks *callbacks); + +/*! + * \since 1.8 + * \brief Unregister a set of monitor callbacks with the core + * + * \details + * If a module which makes use of a CC monitor is unloaded, then it may + * unregister its monitor callbacks with the core. + * + * \param callbacks The callbacks used by the monitor implementation + * \retval 0 Successfully unregistered + * \retval -1 Failure to unregister + */ +void ast_cc_monitor_unregister(const struct ast_cc_monitor_callbacks *callbacks); + +struct ast_cc_agent_callbacks; + +/*! + * \since 1.8 + * \brief Register a set of agent callbacks with the core + * + * \details + * This is made so that at agent creation time, the proper callbacks + * may be installed and the proper .init callback may be called for the + * monitor to establish private data. + * + * \param callbacks The callbacks used by the agent implementation + * \retval 0 Successfully registered + * \retval -1 Failure to register + */ +int ast_cc_agent_register(const struct ast_cc_agent_callbacks *callbacks); + +/*! + * \since 1.8 + * \brief Unregister a set of agent callbacks with the core + * + * \details + * If a module which makes use of a CC agent is unloaded, then it may + * unregister its agent callbacks with the core. + * + * \param callbacks The callbacks used by the agent implementation + * \retval 0 Successfully unregistered + * \retval -1 Failure to unregister + */ +void ast_cc_agent_unregister(const struct ast_cc_agent_callbacks *callbacks); + +/* END AGENT/MONITOR REGISTRATION API */ + +/* BEGIN SECTION ON MONITORS AND MONITOR CALLBACKS */ + +/*! + * It is recommended that monitors use a pointer to + * an ast_cc_monitor_callbacks::type when creating + * an AST_CONTROL_CC frame. Since the generic monitor + * callbacks are opaque and channel drivers will wish + * to use that, this string is made globally available + * for all to use + */ +#define AST_CC_GENERIC_MONITOR_TYPE "generic" + +/*! + * Used to determine which type + * of monitor an ast_cc_device_monitor + * is. + */ +enum ast_cc_monitor_class { + AST_CC_DEVICE_MONITOR, + AST_CC_EXTENSION_MONITOR, +}; + +/*! + * \internal + * \brief An item in a CC interface tree. + * + * These are the individual items in an interface tree. + * The key difference between this structure and the ast_cc_interface + * is that this structure contains data which is intrinsic to the item's + * placement in the tree, such as who its parent is. + */ +struct ast_cc_monitor { + /*! + * Information regarding the interface. + */ + struct ast_cc_interface *interface; + /*! + * Every interface has an id that uniquely identifies it. It is + * formed by incrementing a counter. + */ + unsigned int id; + /*! + * The ID of this monitor's parent. If this monitor is at the + * top of the tree, then his parent will be 0. + */ + unsigned int parent_id; + /*! + * The instance of the CC core to which this monitor belongs + */ + int core_id; + /*! + * The type of call completion service offered by a device. + */ + enum ast_cc_service_type service_offered; + /*! + * \brief Name that should be used to recall specified interface + * + * \details + * When issuing a CC recall, some technologies will require + * that a name other than the device name is dialed. For instance, + * with SIP, a specific URI will be used which chan_sip will be able + * to recognize as being a CC recall. Similarly, ISDN will need a specific + * dial string to know that the call is a recall. + */ + char *dialstring; + /*! + * The ID of the available timer used by the current monitor + */ + int available_timer_id; + /*! + * Monitor callbacks + */ + const struct ast_cc_monitor_callbacks *callbacks; + /*! + * \brief Data that is private to a monitor technology + * + * Most channel drivers that implement CC monitors will have to + * allocate data that the CC core does not care about but which + * is vital to the operation of the monitor. This data is stored + * in this pointer so that the channel driver may use it as + * needed + */ + void *private_data; + AST_LIST_ENTRY(ast_cc_monitor) next; +}; + +/*! + * \brief Callbacks defined by CC monitors + * + * \note + * Every callback is called with the list of monitors locked. There + * are several public API calls that also will try to lock this lock. + * These public functions have a note in their doxygen stating so. + * As such, pay attention to the lock order you establish in these callbacks + * to ensure that you do not violate the lock order when calling + * the functions in this file with lock order notices. + */ +struct ast_cc_monitor_callbacks { + /*! + * \brief Type of monitor the callbacks belong to. + * + * \note + * Examples include "generic" and "SIP" + */ + const char *type; + /*! + * \brief Request CCSS. + * + * \param monitor CC core monitor control. + * \param available_timer_id The scheduler ID for the available timer. + * Will never be NULL for a device monitor. + * + * \details + * Perform whatever steps are necessary in order to request CC. + * In addition, the monitor implementation is responsible for + * starting the available timer in this callback. + * + * \retval 0 on success + * \retval -1 on failure. + */ + int (*request_cc)(struct ast_cc_monitor *monitor, int *available_timer_id); + /*! + * \brief Suspend monitoring. + * + * \param monitor CC core monitor control. + * + * \details + * Implementers must perform the necessary steps to suspend + * monitoring. + * + * \retval 0 on success + * \retval -1 on failure. + */ + int (*suspend)(struct ast_cc_monitor *monitor); + /*! + * \brief Status response to an ast_cc_monitor_status_request(). + * + * \param monitor CC core monitor control. + * \param devstate Current status of a Party A device. + * + * \details + * Alert a monitor as to the status of the agent for which + * the monitor had previously requested a status request. + * + * \note Zero or more responses may come as a result. + * + * \retval 0 on success + * \retval -1 on failure. + */ + int (*status_response)(struct ast_cc_monitor *monitor, enum ast_device_state devstate); + /*! + * \brief Unsuspend monitoring. + * + * \param monitor CC core monitor control. + * + * \details + * Perform the necessary steps to unsuspend monitoring. + * + * \retval 0 on success + * \retval -1 on failure. + */ + int (*unsuspend)(struct ast_cc_monitor *monitor); + /*! + * \brief Cancel the running available timer. + * + * \param monitor CC core monitor control. + * \param sched_id Available timer scheduler id to cancel. + * Will never be NULL for a device monitor. + * + * \details + * In most cases, this function will likely consist of just a + * call to AST_SCHED_DEL. It might have been possible to do this + * within the core, but unfortunately the mixture of sched_thread + * and sched usage in Asterisk prevents such usage. + * + * \retval 0 on success + * \retval -1 on failure. + */ + int (*cancel_available_timer)(struct ast_cc_monitor *monitor, int *sched_id); + /*! + * \brief Destroy private data on the monitor. + * + * \param private_data The private data pointer from the monitor. + * + * \details + * Implementers of this callback are responsible for destroying + * all heap-allocated data in the monitor's private_data pointer, including + * the private_data itself. + */ + void (*destructor)(void *private_data); +}; + +/*! + * \since 1.8 + * \brief Scheduler callback for available timer expiration + * + * \note + * When arming the available timer from within a device monitor, you MUST + * use this function as the callback for the scheduler. + * + * \param data A reference to the CC monitor on which the timer was running. + */ +int ast_cc_available_timer_expire(const void *data); + +/* END SECTION ON MONITORS AND MONITOR CALLBACKS */ + +/* BEGIN API FOR IN-CALL CC HANDLING */ + +/*! + * \since 1.8 + * + * \brief Mark the channel to ignore further CC activity. + * + * \details + * When a CC-capable application, such as Dial, has finished + * with all CC processing for a channel and knows that any further + * CC processing should be ignored, this function should be called. + * + * \param chan The channel for which further CC processing should be ignored. + * \retval void + */ +void ast_ignore_cc(struct ast_channel *chan); + +/*! + * \since 1.8 + * + * \brief Properly react to a CC control frame. + * + * \details + * When a CC-capable application, such as Dial, receives a frame + * of type AST_CONTROL_CC, then it may call this function in order + * to have the device which sent the frame added to the tree of interfaces + * which is kept on the inbound channel. + * + * \param inbound The inbound channel + * \param outbound The outbound channel (The one from which the CC frame was read) + * \param frame_data The ast_frame's data.ptr field. + * \retval void + */ +void ast_handle_cc_control_frame(struct ast_channel *inbound, struct ast_channel *outbound, void *frame_data); + +/*! + * \since 1.8 + * + * \brief Start the CC process on a call. + * + * \details + * Whenever a CC-capable application, such as Dial, wishes to + * engage in CC activity, it initiates the process by calling this + * function. If the CC core should discover that a previous application + * has called ast_ignore_cc on this channel or a "parent" channel, then + * the value of the ignore_cc integer passed in will be set nonzero. + * + * The ignore_cc parameter is a convenience parameter. It can save an + * application the trouble of trying to call CC APIs when it knows that + * it should just ignore further attempts at CC actions. + * + * \param chan The inbound channel calling the CC-capable application. + * \param[out] ignore_cc Will be set non-zero if no further CC actions need to be taken + * \retval 0 Success + * \retval -1 Failure + */ +int ast_cc_call_init(struct ast_channel *chan, int *ignore_cc); + +/*! + * \since 1.8 + * + * \brief Add a child dialstring to an extension monitor + * + * Whenever we request a channel, the parent extension monitor needs + * to store the dialstring of the device requested. The reason is so + * that we can call the device back during the recall even if we are + * not monitoring the device. + * + * \param incoming The caller's channel + * \param dialstring The dialstring used when requesting the outbound channel + * \param device_name The device name associated with the requested outbound channel + * \retval void + */ +void ast_cc_extension_monitor_add_dialstring(struct ast_channel *incoming, const char * const dialstring, const char * const device_name); + +/*! + * \since 1.8 + * \brief Check if the incoming CC request is within the bounds + * set by the cc_max_requests configuration option + * + * \details + * It is recommended that an entity which receives an incoming + * CC request calls this function before calling + * ast_cc_agent_accept_request. This way, immediate feedback can be + * given to the caller about why his request was rejected. + * + * If this is not called and a state change to CC_CALLER_REQUESTED + * is made, then the core will still not allow for the request + * to succeed. However, if done this way, it may not be obvious + * to the requestor why the request failed. + * + * \retval 0 Not within the limits. Fail. + * \retval non-zero Within the limits. Success. + */ +int ast_cc_request_is_within_limits(void); + +/*! + * \since 1.8 + * \brief Get the core id for the current call + * + * \details + * The main use of this function is for channel drivers + * who queue an AST_CONTROL_CC frame. A channel driver may + * call this function in order to get the core_id for what + * may become a CC request. This way, when monitor functions + * are called which use a core_id as a means of identification, + * the channel driver will have saved this information. + * + * The channel given to this function may be an inbound or outbound + * channel. Both will have the necessary info on it. + * + * \param chan The channel from which to get the core_id. + * \retval core_id on success + * \retval -1 Failure + */ +int ast_cc_get_current_core_id(struct ast_channel *chan); + +/* END API FOR IN-CALL CC HANDLING */ + +/*! + * \brief Structure with information about an outbound interface + * + * \details + * This structure is first created when an outbound interface indicates that + * it is capable of accepting a CC request. It is stored in a "tree" on a datastore on + * the caller's channel. Once an agent structure is created, the agent gains + * a reference to the tree of interfaces. If CC is requested, then the + * interface tree on the agent is converted into a tree of monitors. Each + * monitor will contain a pointer to an individual ast_cc_interface. Finally, + * the tree of interfaces is also present on a second datastore during a + * CC recall so that the CC_INTERFACES channel variable may be properly + * populated. + */ +struct ast_cc_interface { + /* What class of monitor is being offered here + */ + enum ast_cc_monitor_class monitor_class; + /*! + * \brief The type of monitor that should be used for this interface + * + * \details + * This will be something like "extension" "generic" or "SIP". + * This should point to a static const char *, so there is + * no reason to make a new copy. + */ + const char *monitor_type; + /*! + * The configuration parameters used for this interface + */ + struct ast_cc_config_params *config_params; + /* The name of the interface/extension. local channels will + * have 'exten@context' for a name. Other channel types will + * have 'tech/device' for a name. + */ + char device_name[1]; +}; + +/* BEGIN STRUCTURES FOR AGENTS */ + +struct ast_cc_agent { + /*! + * Which instance of the core state machine does this + * agent pertain to? + */ + unsigned int core_id; + /*! + * Callback functions needed for specific agent + * implementations + */ + const struct ast_cc_agent_callbacks *callbacks; + /*! + * Configuration parameters that affect this + * agent's operation. + */ + struct ast_cc_config_params *cc_params; + /*! + * \brief Flags for agent operation + * + * \details + * There are some attributes of certain agent types + * that can alter the behavior of certain CC functions. + * For a list of these flags, see the ast_cc_agent_flags + * enum + */ + unsigned int flags; + /*! Data specific to agent implementation */ + void *private_data; + /*! The name of the device which this agent + * represents/communicates with + */ + char device_name[1]; +}; + +struct ast_cc_agent_callbacks { + /*! + * \brief Type of agent the callbacks belong to. + * + * \note + * Examples are "SIP" "ISDN" and "generic" + */ + const char *type; + /*! + * \brief CC agent initialization. + * + * \param agent CC core agent control. + * \param chan Original channel the agent will attempt to recall. + * + * \details + * This callback is called when the CC core + * is initialized. Agents should allocate + * any private data necessary for the + * call and assign it to the private_data + * on the agent. Additionally, if any ast_cc_agent_flags + * are pertinent to the specific agent type, they should + * be set in this function as well. + * + * \retval 0 on success. + * \retval -1 on error. + */ + int (*init)(struct ast_cc_agent *agent, struct ast_channel *chan); + /*! + * \brief Start the offer timer. + * + * \param agent CC core agent control. + * + * \details + * This is called by the core when the caller hangs up after + * a call for which CC may be requested. The agent should + * begin the timer as configured. + * + * The primary reason why this functionality is left to + * the specific agent implementations is due to the differing + * use of schedulers throughout the code. Some channel drivers + * may already have a scheduler context they wish to use, and + * amongst those, some may use the ast_sched API while others + * may use the ast_sched_thread API, which are incompatible. + * + * \retval 0 on success. + * \retval -1 on error. + */ + int (*start_offer_timer)(struct ast_cc_agent *agent); + /*! + * \brief Stop the offer timer. + * + * \param agent CC core agent control. + * + * \details + * This callback is called by the CC core when the caller + * has requested CC. + * + * \retval 0 on success. + * \retval -1 on error. + */ + int (*stop_offer_timer)(struct ast_cc_agent *agent); + /*! + * \brief Acknowledge CC request. + * + * \param agent CC core agent control. + * + * \details + * When the core receives knowledge that a called + * party has accepted a CC request, it will call + * this callback. + * + * The duty of this is to accept a CC request from + * the caller by acknowledging receipt of that request. + */ + void (*ack)(struct ast_cc_agent *agent); + /*! + * \brief Request the status of the agent's device. + * + * \param agent CC core agent control. + * + * \details + * Asynchronous request for the status of any caller + * which may be a valid caller for the CC transaction. + * Status responses should be made using the + * ast_cc_status_response function. + * + * \retval 0 on success. + * \retval -1 on error. + */ + int (*status_request)(struct ast_cc_agent *agent); + /*! + * \brief Request for an agent's phone to stop ringing. + * + * \param agent CC core agent control. + * + * \details + * The usefulness of this is quite limited. The only specific + * known case for this is if Asterisk requests CC over an ISDN + * PTMP link as the TE side. If other phones are in the same + * recall group as the Asterisk server, and one of those phones + * picks up the recall notice, then Asterisk will receive a + * "stop ringing" notification from the NT side of the PTMP + * link. This indication needs to be passed to the phone + * on the other side of the Asterisk server which originally + * placed the call so that it will stop ringing. Since the + * phone may be of any type, it is necessary to have a callback + * that the core can know about. + * + * \retval 0 on success. + * \retval -1 on error. + */ + int (*stop_ringing)(struct ast_cc_agent *agent); + /*! + * \brief Let the caller know that the callee has become free + * but that the caller cannot attempt to call back because + * he is either busy or there is congestion on his line. + * + * \param agent CC core agent control. + * + * \details + * This is something that really only affects a scenario where + * a phone places a call over ISDN PTMP to Asterisk, who then + * connects over PTMP again to the ISDN network. For most agent + * types, there is no need to implement this callback at all + * because they don't really need to actually do anything in + * this situation. If you're having trouble understanding what + * the purpose of this callback is, then you can be safe simply + * not implementing it. + * + * \retval 0 on success. + * \retval -1 on error. + */ + int (*party_b_free)(struct ast_cc_agent *agent); + /*! + * \brief Begin monitoring a busy device. + * + * \param agent CC core agent control. + * + * \details + * The core will call this callback if the callee becomes + * available but the caller has reported that he is busy. + * The agent should begin monitoring the caller's device. + * When the caller becomes available again, the agent should + * call ast_cc_agent_caller_available. + * + * \retval 0 on success. + * \retval -1 on error. + */ + int (*start_monitoring)(struct ast_cc_agent *agent); + /*! + * \brief Alert the caller that it is time to try recalling. + * + * \param agent CC core agent control. + * + * \details + * The core will call this function when it receives notice + * that a monitored party has become available. + * + * The agent's job is to send a message to the caller to + * notify it of such a change. If the agent is able to + * discern that the caller is currently unavailable, then + * the agent should react by calling the ast_cc_caller_unavailable + * function. + * + * \retval 0 on success. + * \retval -1 on error. + */ + int (*callee_available)(struct ast_cc_agent *agent); + /*! + * \brief Destroy private data on the agent. + * + * \param agent CC core agent control. + * + * \details + * The core will call this function upon completion + * or failure of CC. + * + * \note + * The agent private_data pointer may be NULL if the agent + * constructor failed. + */ + void (*destructor)(struct ast_cc_agent *agent); +}; + +/*! + * \brief Call a callback on all agents of a specific type + * + * \details + * Since the container of CC core instances is private, and so + * are the items which the container contains, we have to provide + * an ao2_callback-like method so that a specific agent may be + * found or so that an operation can be made on all agents of + * a particular type. The first three arguments should be familiar + * to anyone who has used ao2_callback. The final argument is the + * type of agent you wish to have the callback called on. + * + * \note Since agents are refcounted, and this function returns + * a reference to the agent, it is imperative that you decrement + * the refcount of the agent once you have finished using it. + * + * \param flags astobj2 search flags + * \param function an ao2 callback function to call + * \param arg the argument to the callback function + * \param type The type of agents to call the callback on + */ +struct ast_cc_agent *ast_cc_agent_callback(int flags, ao2_callback_fn *function, void *arg, const char * const type); + +/* END STRUCTURES FOR AGENTS */ + +/* BEGIN STATE CHANGE API */ + +/*! + * \since 1.8 + * \brief Offer CC to a caller + * + * \details + * This function is called from ast_hangup if the caller is + * eligible to be offered call completion service. + * + * \param caller_chan The calling channel + * \retval -1 Error + * \retval 0 Success + */ +int ast_cc_offer(struct ast_channel *caller_chan); + +/*! + * \since 1.8 + * \brief Accept inbound CC request + * + * \details + * When a caller requests CC, this function should be called to let + * the core know that the request has been accepted. + * + * \param core_id core_id of the CC transaction + * \param debug optional string to print for debugging purposes + * \retval 0 Success + * \retval -1 Failure + */ +int __attribute__((format(printf, 2, 3))) ast_cc_agent_accept_request(int core_id, const char * const debug, ...); + +/*! + * \since 1.8 + * \brief Indicate that an outbound entity has accepted our CC request + * + * \details + * When we receive confirmation that an outbound device has accepted the + * CC request we sent it, this function must be called. + * + * \param core_id core_id of the CC transaction + * \param debug optional string to print for debugging purposes + * \retval 0 Success + * \retval -1 Failure + */ +int __attribute__((format(printf, 2, 3))) ast_cc_monitor_request_acked(int core_id, const char * const debug, ...); + +/*! + * \since 1.8 + * \brief Indicate that the caller is busy + * + * \details + * When the callee makes it known that he is available, the core + * will let the caller's channel driver know that it may attempt + * to let the caller know to attempt a recall. If the channel + * driver can detect, though, that the caller is busy, then + * the channel driver should call this function to let the CC + * core know. + * + * \param core_id core_id of the CC transaction + * \param debug optional string to print for debugging purposes + * \retval 0 Success + * \retval -1 Failure + */ +int __attribute__((format(printf, 2, 3))) ast_cc_agent_caller_busy(int core_id, const char * const debug, ...); + +/*! + * \since 1.8 + * \brief Indicate that a previously unavailable caller has become available + * + * \details + * If a monitor is suspended due to a caller becoming unavailable, then this + * function should be called to indicate that the caller has become available. + * + * \param core_id core_id of the CC transaction + * \param debug optional string to print for debugging purposes + * \retval 0 Success + * \retval -1 Failure + */ +int __attribute__((format(printf, 2, 3))) ast_cc_agent_caller_available(int core_id, const char * const debug, ...); + +/*! + * \since 1.8 + * \brief Tell the CC core that a caller is currently recalling + * + * \details + * The main purpose of this is so that the core can alert the monitor + * to stop its available timer since the caller has begun its recall + * phase. + * + * \param core_id core_id of the CC transaction + * \param debug optional string to print for debugging purposes + * \retval 0 Success + * \retval -1 Failure + */ +int __attribute__((format(printf, 2, 3))) ast_cc_agent_recalling(int core_id, const char * const debug, ...); + +/*! + * \since 1.8 + * \brief Indicate recall has been acknowledged + * + * \details + * When we receive confirmation that an endpoint has responded to our + * CC recall, we call this function. + * + * \param chan The inbound channel making the CC recall + * \param debug optional string to print for debugging purposes + * \retval 0 Success + * \retval -1 Failure + */ +int __attribute__((format(printf, 2, 3))) ast_cc_completed(struct ast_channel *chan, const char * const debug, ...); + +/*! + * \since 1.8 + * \brief Indicate failure has occurred + * + * \details + * If at any point a failure occurs, this is the function to call + * so that the core can initiate cleanup procedures. + * + * \param core_id core_id of the CC transaction + * \param debug optional string to print for debugging purposes + * \retval 0 Success + * \retval -1 Failure + */ +int __attribute__((format(printf, 2, 3))) ast_cc_failed(int core_id, const char * const debug, ...); + +/*! + * \since 1.8 + * \brief Indicate that a failure has occurred on a specific monitor + * + * \details + * If a monitor should detect that a failure has occurred when communicating + * with its endpoint, then ast_cc_monitor_failed should be called. The big + * difference between ast_cc_monitor_failed and ast_cc_failed is that ast_cc_failed + * indicates a global failure for a CC transaction, where as ast_cc_monitor_failed + * is localized to a particular monitor. When ast_cc_failed is called, the entire + * CC transaction is torn down. When ast_cc_monitor_failed is called, only the + * monitor on which the failure occurred is pruned from the tree of monitors. + * + * If there are no more devices left to monitor when this function is called, + * then the core will fail the CC transaction globally. + * + * \param core_id The core ID for the CC transaction + * \param monitor_name The name of the monitor on which the failure occurred + * \param debug A debug message to print to the CC log + * \return void + */ +int __attribute__((format(printf, 3, 4))) ast_cc_monitor_failed(int core_id, const char * const monitor_name, const char * const debug, ...); + +/* END STATE CHANGE API */ + +/*! + * The following are all functions which are required due to the unique + * case where Asterisk is acting as the NT side of an ISDN PTMP + * connection to the caller and as the TE side of an ISDN PTMP connection + * to the callee. In such a case, there are several times where the + * PTMP monitor needs information from the agent in order to formulate + * the appropriate messages to send. + */ + +/*! + * \brief Request the status of a caller or callers. + * + * \details + * When an ISDN PTMP monitor senses that the callee has become + * available, it needs to know the current status of the caller + * in order to determine the appropriate response to send to + * the caller. In order to do this, the monitor calls this function. + * Responses will arrive asynchronously. + * + * \note Zero or more responses may come as a result. + * + * \param core_id The core ID of the CC transaction + * + * \retval 0 Successfully requested status + * \retval -1 Failed to request status + */ +int ast_cc_monitor_status_request(int core_id); + +/*! + * \brief Response with a caller's current status + * + * \details + * When an ISDN PTMP monitor requests the caller's status, the + * agent must respond to the request using this function. For + * simplicity it is recommended that the devstate parameter + * be one of AST_DEVICE_INUSE or AST_DEVICE_NOT_INUSE. + * + * \param core_id The core ID of the CC transaction + * \param devstate The current state of the caller to which the agent pertains + * \retval 0 Successfully responded with our status + * \retval -1 Failed to respond with our status + */ +int ast_cc_agent_status_response(int core_id, enum ast_device_state devstate); + +/*! + * \brief Alert a caller to stop ringing + * + * \details + * When an ISDN PTMP monitor becomes available, it is assumed + * that the agent will then cause the caller's phone to ring. In + * some cases, this is literally what happens. In other cases, it may + * be that the caller gets a visible indication on his phone that he + * may attempt to recall the callee. If multiple callers are recalled + * (since it may be possible to have a group of callers configured as + * a single party A), and one of those callers picks up his phone, then + * the ISDN PTMP monitor will alert the other callers to stop ringing. + * The agent's stop_ringing callback will be called, and it is up to the + * agent's driver to send an appropriate message to make his caller + * stop ringing. + * + * \param core_id The core ID of the CC transaction + * \retval 0 Successfully requested for the phone to stop ringing + * \retval -1 Could not request for the phone to stop ringing + */ +int ast_cc_monitor_stop_ringing(int core_id); + +/*! + * \brief Alert a caller that though the callee has become free, the caller + * himself is not and may not call back. + * + * \details + * When an ISDN PTMP monitor senses that his monitored party has become + * available, he will request the status of the called party. If he determines + * that the caller is currently not available, then he will call this function + * so that an appropriate message is sent to the caller. + * + * Yes, you just read that correctly. The callee asks the caller what his + * current status is, and if the caller is currently unavailable, the monitor + * must send him a message anyway. WTF? + * + * This function results in the agent's party_b_free callback being called. + * It is most likely that you will not need to actually implement the + * party_b_free callback in an agent because it is not likely that you will + * need to or even want to send a caller a message indicating the callee's + * status if the caller himself is not also free. + * + * \param core_id The core ID of the CC transaction + * \retval 0 Successfully alerted the core that party B is free + * \retval -1 Could not alert the core that party B is free + */ +int ast_cc_monitor_party_b_free(int core_id); + +/* BEGIN API FOR USE WITH/BY MONITORS */ + +/*! + * \since 1.8 + * \brief Return the number of outstanding CC requests to a specific device + * + * \note + * This function will lock the list of monitors stored on every instance of + * the CC core. Callers of this function should be aware of this and avoid + * any potential lock ordering problems. + * + * \param name The name of the monitored device + * \param type The type of the monitored device (e.g. "generic") + * \return The number of CC requests for the monitor + */ +int ast_cc_monitor_count(const char * const name, const char * const type); + +/*! + * \since 1.8 + * \brief Alert the core that a device being monitored has become available. + * + * \note + * The code in the core will take care of making sure that the information gets passed + * up the ladder correctly. + * + * \param core_id The core ID of the corresponding CC transaction + * \retval 0 Request successfully queued + * \retval -1 Request could not be queued + */ +int __attribute__((format(printf, 2, 3))) ast_cc_monitor_callee_available(const int core_id, const char * const debug, ...); + +/* END API FOR USE WITH/BY MONITORS */ + +/* BEGIN API TO BE USED ON CC RECALL */ + +/*! + * \since 1.8 + * \brief Set up a CC recall datastore on a channel + * + * \details + * Implementers of protocol-specific CC agents will need to call this + * function in order for the channel to have the necessary interfaces + * to recall. + * + * This function must be called by the implementer once it has been detected + * that an inbound call is a cc_recall. After allocating the channel, call this + * function, followed by ast_cc_set_cc_interfaces_chanvar. While it would be nice to + * be able to have the core do this automatically, it just cannot be done given + * the current architecture. + */ +int ast_setup_cc_recall_datastore(struct ast_channel *chan, const int core_id); + +/*! + * \since 1.8 + * \brief Decide if a call to a particular channel is a CC recall + * + * \details + * When a CC recall happens, it is important on the called side to + * know that the call is a CC recall and not a normal call. This function + * will determine first if the call in question is a CC recall. Then it + * will determine based on the chan parameter if the channel is being + * called is being recalled. + * + * As a quick example, let's say a call is placed to SIP/1000 and SIP/1000 + * is currently on the phone. The caller requests CCBS. SIP/1000 finishes + * his call, and so the caller attempts to recall. Now, the dialplan + * administrator has set up this second call so that not only is SIP/1000 + * called, but also SIP/2000 is called. If SIP/1000's channel were passed + * to this function, the return value would be non-zero, but if SIP/2000's + * channel were passed into this function, then the return would be 0 since + * SIP/2000 was not one of the original devices dialed. + * + * \note + * This function may be called on a calling channel as well to + * determine if it is part of a CC recall. + * + * \note + * This function will lock the channel as well as the list of monitors + * on the channel datastore, though the locks are not held at the same time. Be + * sure that you have no potential lock order issues here. + * + * \param chan The channel to check + * \param core_id[out] If this is a valid CC recall, the core_id of the failed call + * will be placed in this output parameter + * \param monitor_type Clarify which type of monitor type we are looking for if this + * is happening on a called channel. For incoming channels, this parameter is not used. + * \retval 0 Either this is not a recall or it is but this channel is not part of the recall + * \retval non-zero This is a recall and the channel in question is directly involved. + */ +int ast_cc_is_recall(struct ast_channel *chan, int *core_id, const char * const monitor_type); + +/*! + * \since 1.8 + * \brief Get the associated monitor given the device name and core_id + * + * \details + * The function ast_cc_is_recall is helpful for determining if a call to + * a specific channel is a recall. However, once you have determined that + * this is a recall, you will most likely need access to the private data + * within the associated monitor. This function is what one uses to get + * that monitor. + * + * \note + * This function locks the list of monitors that correspond to the core_id + * passed in. Be sure that you have no potential lock order issues when + * calling this function. + * + * \param core_id The core ID to which this recall corresponds. This likely will + * have been obtained using the ast_cc_is_recall function + * \param device_name Which device to find the monitor for. + * + * \retval NULL Appropriate monitor does not exist + * \retval non-NULL The monitor to use for this recall + */ +struct ast_cc_monitor *ast_cc_get_monitor_by_recall_core_id(const int core_id, const char * const device_name); + +/*! + * \since 1.8 + * \brief Set the first level CC_INTERFACES channel variable for a channel. + * + * \note + * Implementers of protocol-specific CC agents should call this function after + * calling ast_setup_cc_recall_datastore. + * + * \note + * This function will lock the channel as well as the list of monitors stored + * on the channel's CC recall datastore, though neither are held at the same + * time. Callers of this function should be aware of potential lock ordering + * problems that may arise. + * + * \details + * The CC_INTERFACES channel variable will have the interfaces that should be + * called back for a specific PBX instance. + * + * \param chan The channel to set the CC_INTERFACES variable on + */ +int ast_cc_agent_set_interfaces_chanvar(struct ast_channel *chan); + +/*! + * \since 1.8 + * \brief Set the CC_INTERFACES channel variable for a channel using an + * extension@context as a starting point + * + * \details + * The CC_INTERFACES channel variable will have the interfaces that should be + * called back for a specific PBX instance. This version of the function is used + * mainly by chan_local, wherein we need to set CC_INTERFACES based on an extension + * and context that appear in the middle of the tree of dialed interfaces + * + * \note + * This function will lock the channel as well as the list of monitors stored + * on the channel's CC recall datastore, though neither are held at the same + * time. Callers of this function should be aware of potential lock ordering + * problems that may arise. + * + * \param chan The channel to set the CC_INTERFACES variable on + * \param extension The name of the extension for which we're setting the variable. + * This should be in the form of "exten@context" + */ +int ast_set_cc_interfaces_chanvar(struct ast_channel *chan, const char * const extension); + +/*! + * \since 1.8 + * \brief Make CCBS available in the case that ast_call fails + * + * In some situations, notably if a call-limit is reached in SIP, ast_call will fail + * due to Asterisk's knowing that the desired device is currently busy. In such a situation, + * CCBS should be made available to the caller. + * + * One caveat is that this may only be used if generic monitoring is being used. The reason + * is that since Asterisk determined that the device was busy without actually placing a call to it, + * the far end will have no idea what call we are requesting call completion for if we were to send + * a call completion request. + */ +void ast_cc_call_failed(struct ast_channel *incoming, struct ast_channel *outgoing, const char * const dialstring); + +/*! + * \since 1.8 + * \brief Callback made from ast_cc_callback for certain channel types + * + * \param inbound Incoming asterisk channel. + * \param cc_params The CC configuration parameters for the outbound target + * \param monitor_type The type of monitor to use when CC is requested + * \param device_name The name of the outbound target device. + * \param dialstring The dial string used when calling this specific interface + * \param private_data If a native monitor is being used, and some channel-driver-specific private + * data has been allocated, then this parameter should contain a pointer to that data. If using a generic + * monitor, this parameter should remain NULL. Note that if this function should fail at some point, + * it is the responsibility of the caller to free the private data upon return. + * + * \details + * For channel types that fail ast_request when the device is busy, we call into the + * channel driver with ast_cc_callback. This is the callback that is called in that + * case for each device found which could have been returned by ast_request. + * + * This function creates a CC control frame payload, simulating the act of reading + * it from the nonexistent outgoing channel's frame queue. We then handle this + * simulated frame just as we would a normal CC frame which had actually been queued + * by the channel driver. + */ +void ast_cc_busy_interface(struct ast_channel *inbound, struct ast_cc_config_params *cc_params, + const char *monitor_type, const char * const device_name, const char * const dialstring, void *private_data); + +/*! + * \since 1.8 + * \brief Create a CC Control frame + * + * \details + * chan_dahdi is weird. It doesn't seem to actually queue frames when it needs to tell + * an application something. Instead it wakes up, tells the application that it has data + * ready, and then based on set flags, creates the proper frame type. For chan_dahdi, we + * provide this function. It provides us the data we need, and we'll make its frame for it. + * + * \param chan A channel involved in the call. What we want is on a datastore on both incoming and outgoing so either may be provided + * \param cc_params The CC configuration parameters for the outbound target + * \param monitor_type The type of monitor to use when CC is requested + * \param device_name The name of the outbound target device. + * \param dialstring The dial string used when calling this specific interface + * \param service What kind of CC service is being offered. (CCBS/CCNR/etc...) + * \param private_data If a native monitor is being used, and some channel-driver-specific private + * data has been allocated, then this parameter should contain a pointer to that data. If using a generic + * monitor, this parameter should remain NULL. Note that if this function should fail at some point, + * it is the responsibility of the caller to free the private data upon return. + * \param[out] frame. The frame we will be returning to the caller. It is vital that ast_frame_free be called on this frame since the + * payload will be allocated on the heap. + * \retval -1 Failure. At some point there was a failure. Do not attempt to use the frame in this case. + * \retval 0 Success + */ +int ast_cc_build_frame(struct ast_channel *chan, struct ast_cc_config_params *cc_params, + const char *monitor_type, const char * const device_name, + const char * const dialstring, enum ast_cc_service_type service, void *private_data, + struct ast_frame *frame); + + +/*! + * \brief Callback made from ast_cc_callback for certain channel types + * \since 1.8 + * + * \param chan A channel involved in the call. What we want is on a datastore on both incoming and outgoing so either may be provided + * \param cc_params The CC configuration parameters for the outbound target + * \param monitor_type The type of monitor to use when CC is requested + * \param device_name The name of the outbound target device. + * \param dialstring The dial string used when calling this specific interface + * \param private_data If a native monitor is being used, and some channel-driver-specific private + * data has been allocated, then this parameter should contain a pointer to that data. If using a generic + * monitor, this parameter should remain NULL. Note that if this function should fail at some point, + * it is the responsibility of the caller to free the private data upon return. + * + * \details + * For channel types that fail ast_request when the device is busy, we call into the + * channel driver with ast_cc_callback. This is the callback that is called in that + * case for each device found which could have been returned by ast_request. + * + * \return Nothing + */ +typedef void (*ast_cc_callback_fn)(struct ast_channel *chan, struct ast_cc_config_params *cc_params, + const char *monitor_type, const char * const device_name, const char * const dialstring, void *private_data); + +/*! + * \since 1.8 + * \brief Run a callback for potential matching destinations. + * + * \note + * See the explanation in ast_channel_tech::cc_callback for more + * details. + * + * \param tech Channel technology to use + * \param dest Channel/group/peer or whatever the specific technology uses + * \param callback Function to call when a target is reached + * \retval Always 0, I guess. + */ +int ast_cc_callback(struct ast_channel *inbound, const char * const tech, const char * const dest, ast_cc_callback_fn callback); + +/*! + * \since 1.8 + * \brief Initialize CCSS + * + * Performs startup routines necessary for CC operation. + * + * \retval 0 Success + * \retval nonzero Failure + */ +int ast_cc_init(void); + +#endif /* _ASTERISK_CCSS_H */ diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index a32486a39..119dc42e8 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -148,6 +148,8 @@ extern "C" { #include "asterisk/linkedlists.h" #include "asterisk/stringfields.h" #include "asterisk/datastore.h" +#include "asterisk/channelstate.h" +#include "asterisk/ccss.h" #define DATASTORE_INHERIT_FOREVER INT_MAX @@ -507,6 +509,29 @@ struct ast_channel_tech { /*! \brief Get the unique identifier for the PVT, i.e. SIP call-ID for SIP */ const char * (* get_pvt_uniqueid)(struct ast_channel *chan); + + /*! \brief Call a function with cc parameters as a function parameter + * + * \details + * This is a highly specialized callback that is not likely to be needed in many + * channel drivers. When dealing with a busy channel, for instance, most channel + * drivers will successfully return a channel to the requester. Once called, the channel + * can then queue a busy frame when it receives an appropriate message from the far end. + * In such a case, the channel driver has the opportunity to also queue a CC frame. + * The parameters for the CC channel can be retrieved from the channel structure. + * + * For other channel drivers, notably those that deal with "dumb" phones, the channel + * driver will not return a channel when one is requested. In such a scenario, there is never + * an opportunity for the channel driver to queue a CC frame since the channel is never + * called. Furthermore, it is not possible to retrieve the CC configuration parameters + * for the desired channel because no channel is ever allocated or returned to the + * requester. In such a case, call completion may still be a viable option. What we do is + * pass the same string that the requester used originally to request the channel to the + * channel driver. The channel driver can then find any potential channels/devices that + * match the input and return call the designated callback with the device's call completion + * parameters as a parameter. + */ + int (* cc_callback)(struct ast_channel *inbound, const char *dest, ast_cc_callback_fn callback); }; struct ast_epoll_data; @@ -535,27 +560,6 @@ enum ast_channel_adsicpe { }; /*! - * \brief ast_channel states - * - * \note Bits 0-15 of state are reserved for the state (up/down) of the line - * Bits 16-32 of state are reserved for flags - */ -enum ast_channel_state { - AST_STATE_DOWN, /*!< Channel is down and available */ - AST_STATE_RESERVED, /*!< Channel is down, but reserved */ - AST_STATE_OFFHOOK, /*!< Channel is off hook */ - AST_STATE_DIALING, /*!< Digits (or equivalent) have been dialed */ - AST_STATE_RING, /*!< Line is ringing */ - AST_STATE_RINGING, /*!< Remote end is ringing */ - AST_STATE_UP, /*!< Line is up */ - AST_STATE_BUSY, /*!< Line is busy */ - AST_STATE_DIALING_OFFHOOK, /*!< Digits (or equivalent) have been dialed while offhook */ - AST_STATE_PRERING, /*!< Channel has detected an incoming call and is waiting for ring */ - - AST_STATE_MUTE = (1 << 16), /*!< Do not transmit voice data */ -}; - -/*! * \brief Possible T38 states on channels */ enum ast_t38_state { @@ -950,9 +954,6 @@ int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore */ struct ast_datastore *ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid); -/*! \brief Change the state of a channel */ -int ast_setstate(struct ast_channel *chan, enum ast_channel_state); - /*! * \brief Create a channel structure * \since 1.8 @@ -2756,6 +2757,95 @@ void ast_channel_queue_redirecting_update(struct ast_channel *chan, const struct * '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); + +#include "asterisk/ccss.h" + +/*! + * \since 1.8 + * \brief Set up datastore with CCSS parameters for a channel + * + * \note + * If base_params is NULL, the channel will get the default + * values for all CCSS parameters. + * + * \details + * This function makes use of datastore operations on the channel, so + * it is important to lock the channel before calling this function. + * + * \param chan The channel to create the datastore on + * \param base_params CCSS parameters we wish to copy into the channel + * \retval 0 Success + * \retval -1 Failure + */ +int ast_channel_cc_params_init(struct ast_channel *chan, + const struct ast_cc_config_params *base_params); + +/*! + * \since 1.8 + * \brief Get the CCSS parameters from a channel + * + * \details + * This function makes use of datastore operations on the channel, so + * it is important to lock the channel before calling this function. + * + * \param chan Channel to retrieve parameters from + * \retval NULL Failure + * \retval non-NULL The parameters desired + */ +struct ast_cc_config_params *ast_channel_get_cc_config_params(struct ast_channel *chan); + + +/*! + * \since 1.8 + * \brief Get a device name given its channel structure + * + * \details + * A common practice in Asterisk is to determine the device being talked + * to by dissecting the channel name. For certain channel types, this is not + * accurate. For instance, an ISDN channel is named based on what B channel is + * used, not the device being communicated with. + * + * This function interfaces with a channel tech's queryoption callback to + * retrieve the name of the device being communicated with. If the channel does not + * implement this specific option, then the traditional method of using the channel + * name is used instead. + * + * \param chan The channel to retrieve the information from + * \param device_name[out] The buffer to place the device's name into + * \param name_buffer_length The allocated space for the device_name + * \return 0 always + */ +int ast_channel_get_device_name(struct ast_channel *chan, char *device_name, size_t name_buffer_length); + +/*! + * \since 1.8 + * \brief Find the appropriate CC agent type to use given a channel + * + * \details + * During call completion, we will need to create a call completion agent structure. To + * figure out the type of agent to construct, we need to ask the channel driver for the + * appropriate type. + * + * Prior to adding this function, the call completion core attempted to figure this + * out for itself by stripping the technology off the channel's name. However, in the + * case of chan_dahdi, there are multiple agent types registered, and so simply searching + * for an agent type called "DAHDI" is not possible. In a case where multiple agent types + * are defined, the channel driver must have a queryoption callback defined in its + * channel_tech, and the queryoption callback must handle AST_OPTION_CC_AGENT_TYPE + * + * If a channel driver does not have a queryoption callback or if the queryoption callback + * does not handle AST_OPTION_CC_AGENT_TYPE, then the old behavior of using the technology + * portion of the channel name is used instead. This is perfectly suitable for channel drivers + * whose channel technologies are a one-to-one match with the agent types defined within. + * + * Note that this function is only called when the agent policy on a given channel is set + * to "native." Generic agents' type can be determined automatically by the core. + * + * \param chan The channel for which we wish to retrieve the agent type + * \param[out] agent_type The type of agent the channel driver wants us to use + * \param size The size of the buffer to write to + */ +int ast_channel_get_cc_agent_type(struct ast_channel *chan, char *agent_type, size_t size); #if defined(__cplusplus) || defined(c_plusplus) } #endif diff --git a/include/asterisk/channelstate.h b/include/asterisk/channelstate.h new file mode 100644 index 000000000..f5f7392dd --- /dev/null +++ b/include/asterisk/channelstate.h @@ -0,0 +1,53 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2010, Digium, Inc. + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * \brief Channel states + * \par See also: + * \arg \ref Def_Channel + * \arg \ref channel_drivers + */ + +#ifndef __AST_CHANNELSTATE_H__ +#define __AST_CHANNELSTATE_H__ + +#include "asterisk.h" + +/*! + * \brief ast_channel states + * + * \note Bits 0-15 of state are reserved for the state (up/down) of the line + * Bits 16-32 of state are reserved for flags + */ +enum ast_channel_state { + AST_STATE_DOWN, /*!< Channel is down and available */ + AST_STATE_RESERVED, /*!< Channel is down, but reserved */ + AST_STATE_OFFHOOK, /*!< Channel is off hook */ + AST_STATE_DIALING, /*!< Digits (or equivalent) have been dialed */ + AST_STATE_RING, /*!< Line is ringing */ + AST_STATE_RINGING, /*!< Remote end is ringing */ + AST_STATE_UP, /*!< Line is up */ + AST_STATE_BUSY, /*!< Line is busy */ + AST_STATE_DIALING_OFFHOOK, /*!< Digits (or equivalent) have been dialed while offhook */ + AST_STATE_PRERING, /*!< Channel has detected an incoming call and is waiting for ring */ + + AST_STATE_MUTE = (1 << 16), /*!< Do not transmit voice data */ +}; + +/*! \brief Change the state of a channel */ +int ast_setstate(struct ast_channel *chan, enum ast_channel_state); + +#endif /* __AST_CHANNELSTATE_H__ */ diff --git a/include/asterisk/devicestate.h b/include/asterisk/devicestate.h index 4c516870e..2a53ebb46 100644 --- a/include/asterisk/devicestate.h +++ b/include/asterisk/devicestate.h @@ -37,7 +37,7 @@ #ifndef _ASTERISK_DEVICESTATE_H #define _ASTERISK_DEVICESTATE_H -#include "asterisk/channel.h" +#include "asterisk/channelstate.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h index 30119d49b..68a0c7eb6 100644 --- a/include/asterisk/frame.h +++ b/include/asterisk/frame.h @@ -324,7 +324,8 @@ enum ast_control_frame_type { AST_CONTROL_CONNECTED_LINE = 22,/*!< Indicate connected line has changed */ AST_CONTROL_REDIRECTING = 23, /*!< Indicate redirecting id has changed */ AST_CONTROL_T38_PARAMETERS = 24, /*! T38 state change request/notification with parameters */ - AST_CONTROL_SRCCHANGE = 25, /*!< Media source has changed and requires a new RTP SSRC */ + AST_CONTROL_CC = 25, /*!< Indication that Call completion service is possible */ + AST_CONTROL_SRCCHANGE = 26, /*!< Media source has changed and requires a new RTP SSRC */ }; enum ast_control_t38 { @@ -433,6 +434,12 @@ enum ast_control_transfer { /*! Get or set the fax tone detection state of the channel */ #define AST_OPTION_FAX_DETECT 15 +/*! Get the device name from the channel */ +#define AST_OPTION_DEVICE_NAME 16 + +/*! Get the CC agent type from the channel */ +#define AST_OPTION_CC_AGENT_TYPE 17 + struct oprmode { struct ast_channel *peer; int mode; diff --git a/include/asterisk/manager.h b/include/asterisk/manager.h index 91c43d3fe..dd7c160f4 100644 --- a/include/asterisk/manager.h +++ b/include/asterisk/manager.h @@ -82,6 +82,7 @@ #define EVENT_FLAG_ORIGINATE (1 << 12) /* Originate a call to an extension */ #define EVENT_FLAG_AGI (1 << 13) /* AGI events */ #define EVENT_FLAG_HOOKRESPONSE (1 << 14) /* Hook Response */ +#define EVENT_FLAG_CC (1 << 15) /* Call Completion events */ /*@} */ /*! \brief Export manager structures */ diff --git a/include/asterisk/rtp_engine.h b/include/asterisk/rtp_engine.h index 770f4d2f5..e7b809d4c 100644 --- a/include/asterisk/rtp_engine.h +++ b/include/asterisk/rtp_engine.h @@ -70,6 +70,7 @@ extern "C" { #endif #include "asterisk/astobj2.h" +#include "asterisk/frame.h" /* Maximum number of payloads supported */ #define AST_RTP_MAX_PT 256 diff --git a/include/asterisk/xml.h b/include/asterisk/xml.h index 5c78cc9c0..2c30986cc 100644 --- a/include/asterisk/xml.h +++ b/include/asterisk/xml.h @@ -45,6 +45,14 @@ int ast_xml_finish(void); */ struct ast_xml_doc *ast_xml_open(char *filename); +/*! \brief Open an XML document that resides in memory. + * \param buffer The address where the document is stored + * \size The number of bytes in the document + * \retval NULL on error. + * \retval The ast_xml_doc reference to the open document. + */ +struct ast_xml_doc *ast_xml_read_memory(char *buffer, size_t size); + /*! \brief Close an already open document and free the used * structure. * \retval doc The document reference. @@ -90,6 +98,8 @@ const char *ast_xml_get_attribute(struct ast_xml_node *node, const char *attrnam * \retval The node on success. */ struct ast_xml_node *ast_xml_find_element(struct ast_xml_node *root_node, const char *name, const char *attrname, const char *attrvalue); +struct ast_xml_ns *ast_xml_find_namespace(struct ast_xml_doc *doc, struct ast_xml_node *node, const char *ns_name); +const char *ast_xml_get_ns_href(struct ast_xml_ns *ns); /*! \brief Get an element content string. * \param node Node from where to get the string. diff --git a/main/asterisk.c b/main/asterisk.c index 8f557f736..57ccc3f63 100644 --- a/main/asterisk.c +++ b/main/asterisk.c @@ -140,6 +140,7 @@ int daemon(int, int); /* defined in libresolv of all places */ #include "asterisk/buildinfo.h" #include "asterisk/xmldoc.h" #include "asterisk/poll-compat.h" +#include "asterisk/ccss.h" #include "asterisk/test.h" #include "../defaults.h" @@ -3684,6 +3685,11 @@ int main(int argc, char *argv[]) exit(1); } + if (ast_cc_init()) { + printf("%s", term_quit()); + exit(1); + } + if ((moduleresult = load_modules(0))) { /* Load modules */ printf("%s", term_quit()); exit(moduleresult == -2 ? 2 : 1); diff --git a/main/ccss.c b/main/ccss.c new file mode 100644 index 000000000..4e616011b --- /dev/null +++ b/main/ccss.c @@ -0,0 +1,4157 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2010, Digium, Inc. + * + * Mark Michelson <mmichelson@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * \brief Call Completion Supplementary Services implementation + * \author Mark Michelson <mmichelson@digium.com> + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/astobj2.h" +#include "asterisk/strings.h" +#include "asterisk/ccss.h" +#include "asterisk/channel.h" +#include "asterisk/pbx.h" +#include "asterisk/utils.h" +#include "asterisk/taskprocessor.h" +#include "asterisk/event.h" +#include "asterisk/module.h" +#include "asterisk/app.h" +#include "asterisk/cli.h" +#include "asterisk/manager.h" +#include "asterisk/causes.h" + +/*** DOCUMENTATION + <application name="CallCompletionRequest" language="en_US"> + <synopsis> + Request call completion service for previous call + </synopsis> + <syntax /> + <description> + <para>Request call completion service for a previously failed + call attempt.</para> + </description> + </application> + <application name="CallCompletionCancel" language="en_US"> + <synopsis> + Cancel call completion service + </synopsis> + <syntax /> + <description> + <para>Cancel a Call Completion Request.</para> + </description> + </application> + ***/ + +/* These are some file-scoped variables. It would be + * nice to define them closer to their first usage, but since + * they are used in many places throughout the file, defining + * them here at the top is easiest. + */ + +/*! + * The sched_thread ID used for all generic CC timeouts + */ +static struct ast_sched_thread *cc_sched_thread; +/*! + * Counter used to create core IDs for CC calls. Each new + * core ID is created by atomically adding 1 to the core_id_counter + */ +static int core_id_counter; +/*! + * Taskprocessor from which all CC agent and monitor callbacks + * are called. + */ +static struct ast_taskprocessor *cc_core_taskprocessor; +/*! + * Name printed on all CC log messages. + */ +static const char *CC_LOGGER_LEVEL_NAME = "CC"; +/*! + * Logger level registered by the CC core. + */ +static int cc_logger_level; +/*! + * Parsed configuration value for cc_max_requests + */ +static unsigned int global_cc_max_requests; +/*! + * The current number of CC requests in the system + */ +static int cc_request_count; + +#define cc_ref(obj, debug) ({ao2_t_ref((obj), +1, (debug)); (obj);}) +#define cc_unref(obj, debug) ({ao2_t_ref((obj), -1, (debug)); NULL;}) + +/*! + * \since 1.8 + * \internal + * \brief A structure for holding the configuration parameters + * relating to CCSS + */ +struct ast_cc_config_params { + enum ast_cc_agent_policies cc_agent_policy; + enum ast_cc_monitor_policies cc_monitor_policy; + unsigned int cc_offer_timer; + unsigned int ccnr_available_timer; + unsigned int ccbs_available_timer; + unsigned int cc_recall_timer; + unsigned int cc_max_agents; + unsigned int cc_max_monitors; + char cc_callback_macro[AST_MAX_EXTENSION]; + char cc_agent_dialstring[AST_MAX_EXTENSION]; +}; + +/*! + * \since 1.8 + * \brief The states used in the CCSS core state machine + * + * For more information, see doc/CCSS_architecture.pdf + */ +enum cc_state { + /*! Entered when it is determined that CCSS may be used for the call */ + CC_AVAILABLE, + /*! Entered when a CCSS agent has offered CCSS to a caller */ + CC_CALLER_OFFERED, + /*! Entered when a CCSS agent confirms that a caller has + * requested CCSS */ + CC_CALLER_REQUESTED, + /*! Entered when a CCSS monitor confirms acknowledgment of an + * outbound CCSS request */ + CC_ACTIVE, + /*! Entered when a CCSS monitor alerts the core that the called party + * has become available */ + CC_CALLEE_READY, + /*! Entered when a CCSS agent alerts the core that the calling party + * may not be recalled because he is unavailable + */ + CC_CALLER_BUSY, + /*! Entered when a CCSS agent alerts the core that the calling party + * is attempting to recall the called party + */ + CC_RECALLING, + /*! Entered when an application alerts the core that the calling party's + * recall attempt has had a call progress response indicated + */ + CC_COMPLETE, + /*! Entered any time that something goes wrong during the process, thus + * resulting in the failure of the attempted CCSS transaction. Note also + * that cancellations of CC are treated as failures. + */ + CC_FAILED, +}; + +/*! + * \brief The payload for an AST_CONTROL_CC frame + * + * \details + * This contains all the necessary data regarding + * a called device so that the CC core will be able + * to allocate the proper monitoring resources. + */ +struct cc_control_payload { + /*! + * \brief The type of monitor to allocate. + * + * \details + * The type of monitor to allocate. This is a string which corresponds + * to a set of monitor callbacks registered. Examples include "generic" + * and "SIP" + * + * \note This really should be an array of characters in case this payload + * is sent accross an IAX2 link. However, this would not make too much sense + * given this type may not be recognized by the other end. + * Protection may be necessary to prevent it from being transmitted. + * + * In addition the following other problems are also possible: + * 1) Endian issues with the integers/enums stored in the config_params. + * 2) Alignment padding issues for the element types. + */ + const char *monitor_type; + /*! + * \brief Private data allocated by the callee + * + * \details + * All channel drivers that monitor endpoints will need to allocate + * data that is not usable by the CC core. In most cases, some or all + * of this data is allocated at the time that the channel driver offers + * CC to the caller. There are many opportunities for failures to occur + * between when a channel driver offers CC and when a monitor is actually + * allocated to watch the endpoint. For this reason, the channel driver + * must give the core a pointer to the private data that was allocated so + * that the core can call back into the channel driver to destroy it if + * a failure occurs. If no private data has been allocated at the time that + * CC is offered, then it is perfectly acceptable to pass NULL for this + * field. + */ + void *private_data; + /*! + * \brief Service offered by the endpoint + * + * \details + * This indicates the type of call completion service offered by the + * endpoint. This data is not crucial to the machinations of the CC core, + * but it is helpful for debugging purposes. + */ + enum ast_cc_service_type service; + /*! + * \brief Configuration parameters used by this endpoint + * + * \details + * Each time an endpoint offers call completion, it must provide its call + * completion configuration parameters. This is because settings may be different + * depending on the circumstances. + */ + struct ast_cc_config_params config_params; + /*! + * \brief ID of parent extension + * + * \details + * This is the only datum that the CC core derives on its own and is not + * provided by the offerer of CC. This provides the core with information on + * which extension monitor is the most immediate parent of this device. + */ + int parent_interface_id; + /*! + * \brief Name of device to be monitored + * + * \details + * The device name by which this monitored endpoint will be referred in the + * CC core. It is highly recommended that this device name is derived by using + * the function ast_channel_get_device_name. + */ + char device_name[AST_CHANNEL_NAME]; + /*! + * \brief Recall dialstring + * + * \details + * Certain channel drivers (DAHDI in particular) will require that a special + * dialstring be used to indicate that the outgoing call is to interpreted as + * a CC recall. If the channel driver has such a requirement, then this is + * where that special recall dialstring is placed. If no special dialstring + * is to be used, then the channel driver must provide the original dialstring + * used to call this endpoint. + */ + char dialstring[AST_CHANNEL_NAME]; +}; + +/*! + * \brief The "tree" of interfaces that is dialed. + * + * \details + * Though this is a linked list, it is logically treated + * as a tree of monitors. Each monitor has an id and a parent_id + * associated with it. The id is a unique ID for that monitor, and + * the parent_id is the unique ID of the monitor's parent in the + * tree. The tree is structured such that all of a parent's children + * will appear after the parent in the tree. However, it cannot be + * guaranteed exactly where after the parent the children are. + * + * The tree is reference counted since several threads may need + * to use it, and it may last beyond the lifetime of a single + * thread. + */ +AST_LIST_HEAD(cc_monitor_tree, ast_cc_monitor); + +static const int CC_CORE_INSTANCES_BUCKETS = 17; +static struct ao2_container *cc_core_instances; + +struct cc_core_instance { + /*! + * Unique identifier for this instance of the CC core. + */ + int core_id; + /*! + * The current state for this instance of the CC core. + */ + enum cc_state current_state; + /*! + * The CC agent in use for this call + */ + struct ast_cc_agent *agent; + /*! + * Reference to the monitor tree formed during the initial call + */ + struct cc_monitor_tree *monitors; +}; + +/*! + * \internal + * \brief Request that the core change states + * \param state The state to which we wish to change + * \param core_id The unique identifier for this instance of the CCSS core state machine + * \param debug Optional message explaining the reason for the state change + * \param ap varargs list + * \retval 0 State change successfully queued + * \retval -1 Unable to queue state change request + */ +static int __attribute__((format(printf, 3, 0))) cc_request_state_change(enum cc_state state, const int core_id, const char *debug, va_list ap); + +/*! + * \internal + * \brief create a new instance of the CC core and an agent for the calling channel + * + * This function will check to make sure that the incoming channel + * is allowed to request CC by making sure that the incoming channel + * has not exceeded its maximum number of allowed agents. + * + * Should that check pass, the core instance is created, and then the + * agent for the channel. + * + * \param caller_chan The incoming channel for this particular call + * \param called_tree A reference to the tree of called devices. The agent + * will gain a reference to this tree as well + * \param core_id The core_id that this core_instance will assume + * \retval NULL Failed to create the core instance either due to memory allocation + * errors or due to the agent count for the caller being too high + * \retval non-NULL A reference to the newly created cc_core_instance + */ +static struct cc_core_instance *cc_core_init_instance(struct ast_channel *caller_chan, + struct cc_monitor_tree *called_tree, const int core_id, struct cc_control_payload *cc_data); + +static const struct { + enum ast_cc_service_type service; + const char *service_string; +} cc_service_to_string_map[] = { + {AST_CC_NONE, "NONE"}, + {AST_CC_CCBS, "CCBS"}, + {AST_CC_CCNR, "CCNR"}, + {AST_CC_CCNL, "CCNL"}, +}; + +static const struct { + enum cc_state state; + const char *state_string; +} cc_state_to_string_map[] = { + {CC_AVAILABLE, "CC is available"}, + {CC_CALLER_OFFERED, "CC offered to caller"}, + {CC_CALLER_REQUESTED, "CC requested by caller"}, + {CC_ACTIVE, "CC accepted by callee"}, + {CC_CALLEE_READY, "Callee has become available"}, + {CC_CALLER_BUSY, "Callee was ready, but caller is now unavailable"}, + {CC_RECALLING, "Caller is attempting to recall"}, + {CC_COMPLETE, "Recall complete"}, + {CC_FAILED, "CC has failed"}, +}; + +static const char *cc_state_to_string(enum cc_state state) +{ + return cc_state_to_string_map[state].state_string; +} + +static const char *cc_service_to_string(enum ast_cc_service_type service) +{ + return cc_service_to_string_map[service].service_string; +} + +static int cc_core_instance_hash_fn(const void *obj, const int flags) +{ + const struct cc_core_instance *core_instance = obj; + return core_instance->core_id; +} + +static int cc_core_instance_cmp_fn(void *obj, void *arg, int flags) +{ + struct cc_core_instance *core_instance1 = obj; + struct cc_core_instance *core_instance2 = arg; + + return core_instance1->core_id == core_instance2->core_id ? CMP_MATCH | CMP_STOP : 0; +} + +static struct cc_core_instance *find_cc_core_instance(const int core_id) +{ + struct cc_core_instance finder = {.core_id = core_id,}; + + return ao2_t_find(cc_core_instances, &finder, OBJ_POINTER, "Finding a core_instance"); +} + +struct cc_callback_helper { + ao2_callback_fn *function; + void *args; + const char *type; +}; + +static int cc_agent_callback_helper(void *obj, void *args, int flags) +{ + struct cc_core_instance *core_instance = obj; + struct cc_callback_helper *helper = args; + + if (strcmp(core_instance->agent->callbacks->type, helper->type)) { + return 0; + } + + return helper->function(core_instance->agent, helper->args, flags); +} + +struct ast_cc_agent *ast_cc_agent_callback(int flags, ao2_callback_fn *function, void *args, const char * const type) +{ + struct cc_callback_helper helper = {.function = function, .args = args, .type = type}; + struct cc_core_instance *core_instance; + if ((core_instance = ao2_t_callback(cc_core_instances, flags, cc_agent_callback_helper, &helper, + "Calling provided agent callback function"))) { + struct ast_cc_agent *agent = cc_ref(core_instance->agent, "An outside entity needs the agent"); + cc_unref(core_instance, "agent callback done with the core_instance"); + return agent; + } + return NULL; +} + +enum match_flags { + /* Only match agents that have not yet + * made a CC request + */ + MATCH_NO_REQUEST = (1 << 0), + /* Only match agents that have made + * a CC request + */ + MATCH_REQUEST = (1 << 1), +}; + +/* ao2_callbacks for cc_core_instances */ + +/*! + * \internal + * \brief find a core instance based on its agent + * + * The match flags tell whether we wish to find core instances + * that have a monitor or core instances that do not. Core instances + * with no monitor are core instances for which a caller has not yet + * requested CC. Core instances with a monitor are ones for which the + * caller has requested CC. + */ +static int match_agent(void *obj, void *arg, void *data, int flags) +{ + struct cc_core_instance *core_instance = obj; + const char *name = arg; + unsigned long match_flags = *(unsigned long *)data; + int possible_match = 0; + + if ((match_flags & MATCH_NO_REQUEST) && core_instance->current_state < CC_CALLER_REQUESTED) { + possible_match = 1; + } + + if ((match_flags & MATCH_REQUEST) && core_instance->current_state >= CC_CALLER_REQUESTED) { + possible_match = 1; + } + + if (!possible_match) { + return 0; + } + + if (!strcmp(core_instance->agent->device_name, name)) { + return CMP_MATCH | CMP_STOP; + } + return 0; +} + +struct count_agents_cb_data { + int count; + int core_id_exception; +}; + +/*! + * \internal + * \brief Count the number of agents a specific interface is using + * + * We're only concerned with the number of agents that have requested + * CC, so we restrict our search to core instances which have a non-NULL + * monitor pointer + */ +static int count_agents_cb(void *obj, void *arg, void *data, int flags) +{ + struct cc_core_instance *core_instance = obj; + const char *name = arg; + struct count_agents_cb_data *cb_data = data; + + if (cb_data->core_id_exception == core_instance->core_id) { + ast_log_dynamic_level(cc_logger_level, "Found agent with core_id %d but not counting it toward total\n", core_instance->core_id); + return 0; + } + + if (core_instance->current_state >= CC_CALLER_REQUESTED && !strcmp(core_instance->agent->device_name, name)) { + cb_data->count++; + } + return 0; +} + +static const unsigned int CC_OFFER_TIMER_DEFAULT = 20u; +static const unsigned int CCNR_AVAILABLE_TIMER_DEFAULT = 7200u; +static const unsigned int CCBS_AVAILABLE_TIMER_DEFAULT = 4800u; +static const unsigned int CC_RECALL_TIMER_DEFAULT = 20u; +static const unsigned int CC_MAX_AGENTS_DEFAULT = 5u; +static const unsigned int CC_MAX_MONITORS_DEFAULT = 5u; +static const unsigned int GLOBAL_CC_MAX_REQUESTS_DEFAULT = 20u; + +struct ast_cc_config_params *__ast_cc_config_params_init(const char *file, int line, const char *function) +{ +#if defined(__AST_DEBUG_MALLOC) + struct ast_cc_config_params *params = __ast_calloc(1, sizeof(*params), file, line, function); +#else + struct ast_cc_config_params *params = ast_calloc(1, sizeof(*params)); +#endif + + if (!params) { + return NULL; + } + + /* Yeah, I could use the get/set functions, but what's the point since + * I have direct access to the structure fields in this file. + */ + params->cc_agent_policy = AST_CC_AGENT_NEVER; + params->cc_monitor_policy = AST_CC_MONITOR_NEVER; + params->cc_offer_timer = CC_OFFER_TIMER_DEFAULT; + params->ccnr_available_timer = CCNR_AVAILABLE_TIMER_DEFAULT; + params->ccbs_available_timer = CCBS_AVAILABLE_TIMER_DEFAULT; + params->cc_recall_timer = CC_RECALL_TIMER_DEFAULT; + params->cc_max_agents = CC_MAX_AGENTS_DEFAULT; + params->cc_max_monitors = CC_MAX_MONITORS_DEFAULT; + /* No need to set cc_callback_macro since calloc will 0 it out anyway */ + return params; +} + +void ast_cc_config_params_destroy(struct ast_cc_config_params *params) +{ + ast_free(params); +} + +static enum ast_cc_agent_policies str_to_agent_policy(const char * const value) +{ + if (!strcasecmp(value, "never")) { + return AST_CC_AGENT_NEVER; + } else if (!strcasecmp(value, "native")) { + return AST_CC_AGENT_NATIVE; + } else if (!strcasecmp(value, "generic")) { + return AST_CC_AGENT_GENERIC; + } else { + ast_log(LOG_WARNING, "%s is an invalid value for cc_agent_policy. Switching to 'never'\n", value); + return AST_CC_AGENT_NEVER; + } +} + +static enum ast_cc_monitor_policies str_to_monitor_policy(const char * const value) +{ + if (!strcasecmp(value, "never")) { + return AST_CC_MONITOR_NEVER; + } else if (!strcasecmp(value, "native")) { + return AST_CC_MONITOR_NATIVE; + } else if (!strcasecmp(value, "generic")) { + return AST_CC_MONITOR_GENERIC; + } else if (!strcasecmp(value, "always")) { + return AST_CC_MONITOR_ALWAYS; + } else { + ast_log(LOG_WARNING, "%s is an invalid value for cc_monitor_policy. Switching to 'never'\n", value); + return AST_CC_MONITOR_NEVER; + } +} + +static const char *agent_policy_to_str(enum ast_cc_agent_policies policy) +{ + switch (policy) { + case AST_CC_AGENT_NEVER: + return "never"; + case AST_CC_AGENT_NATIVE: + return "native"; + case AST_CC_AGENT_GENERIC: + return "generic"; + default: + /* This should never happen... */ + return ""; + } +} + +static const char *monitor_policy_to_str(enum ast_cc_monitor_policies policy) +{ + switch (policy) { + case AST_CC_MONITOR_NEVER: + return "never"; + case AST_CC_MONITOR_NATIVE: + return "native"; + case AST_CC_MONITOR_GENERIC: + return "generic"; + case AST_CC_MONITOR_ALWAYS: + return "always"; + default: + /* This should never happen... */ + return ""; + } +} +int ast_cc_get_param(struct ast_cc_config_params *params, const char * const name, + char *buf, size_t buf_len) +{ + const char *value = NULL; + if (!strcasecmp(name, "cc_callback_macro")) { + value = ast_get_cc_callback_macro(params); + } else if (!strcasecmp(name, "cc_agent_policy")) { + value = agent_policy_to_str(ast_get_cc_agent_policy(params)); + } else if (!strcasecmp(name, "cc_monitor_policy")) { + value = monitor_policy_to_str(ast_get_cc_monitor_policy(params)); + } else if (!strcasecmp(name, "cc_agent_dialstring")) { + value = ast_get_cc_agent_dialstring(params); + } + + if (!ast_strlen_zero(value)) { + ast_copy_string(buf, value, buf_len); + return 0; + } + + /* The rest of these are all ints of some sort and require some + * snprintf-itude + */ + + if (!strcasecmp(name, "cc_offer_timer")) { + snprintf(buf, buf_len, "%u", ast_get_cc_offer_timer(params)); + } else if (!strcasecmp(name, "ccnr_available_timer")) { + snprintf(buf, buf_len, "%u", ast_get_ccnr_available_timer(params)); + } else if (!strcasecmp(name, "ccbs_available_timer")) { + snprintf(buf, buf_len, "%u", ast_get_ccbs_available_timer(params)); + } else if (!strcasecmp(name, "cc_max_agents")) { + snprintf(buf, buf_len, "%u", ast_get_cc_max_agents(params)); + } else if (!strcasecmp(name, "cc_max_monitors")) { + snprintf(buf, buf_len, "%u", ast_get_cc_max_monitors(params)); + } else if (!strcasecmp(name, "cc_recall_timer")) { + snprintf(buf, buf_len, "%u", ast_get_cc_recall_timer(params)); + } else { + ast_log(LOG_WARNING, "%s is not a valid CC parameter. Ignoring.\n", name); + return -1; + } + + return 0; +} + +int ast_cc_set_param(struct ast_cc_config_params *params, const char * const name, + const char * const value) +{ + unsigned int value_as_uint; + if (!strcasecmp(name, "cc_agent_policy")) { + return ast_set_cc_agent_policy(params, str_to_agent_policy(value)); + } else if (!strcasecmp(name, "cc_monitor_policy")) { + return ast_set_cc_monitor_policy(params, str_to_monitor_policy(value)); + } else if (!strcasecmp(name, "cc_agent_dialstring")) { + ast_set_cc_agent_dialstring(params, value); + } else if (!strcasecmp(name, "cc_callback_macro")) { + ast_set_cc_callback_macro(params, value); + return 0; + } + + if (!sscanf(value, "%30u", &value_as_uint) == 1) { + return -1; + } + + if (!strcasecmp(name, "cc_offer_timer")) { + ast_set_cc_offer_timer(params, value_as_uint); + } else if (!strcasecmp(name, "ccnr_available_timer")) { + ast_set_ccnr_available_timer(params, value_as_uint); + } else if (!strcasecmp(name, "ccbs_available_timer")) { + ast_set_ccbs_available_timer(params, value_as_uint); + } else if (!strcasecmp(name, "cc_max_agents")) { + ast_set_cc_max_agents(params, value_as_uint); + } else if (!strcasecmp(name, "cc_max_monitors")) { + ast_set_cc_max_monitors(params, value_as_uint); + } else if (!strcasecmp(name, "cc_recall_timer")) { + ast_set_cc_recall_timer(params, value_as_uint); + } else { + ast_log(LOG_WARNING, "%s is not a valid CC parameter. Ignoring.\n", name); + return -1; + } + + return 0; +} + +int ast_cc_is_config_param(const char * const name) +{ + return (!strcasecmp(name, "cc_agent_policy") || + !strcasecmp(name, "cc_monitor_policy") || + !strcasecmp(name, "cc_offer_timer") || + !strcasecmp(name, "ccnr_available_timer") || + !strcasecmp(name, "ccbs_available_timer") || + !strcasecmp(name, "cc_max_agents") || + !strcasecmp(name, "cc_max_monitors") || + !strcasecmp(name, "cc_callback_macro") || + !strcasecmp(name, "cc_agent_dialstring") || + !strcasecmp(name, "cc_recall_timer")); +} + +void ast_cc_copy_config_params(struct ast_cc_config_params *dest, const struct ast_cc_config_params *src) +{ + *dest = *src; +} + +enum ast_cc_agent_policies ast_get_cc_agent_policy(struct ast_cc_config_params *config) +{ + return config->cc_agent_policy; +} + +int ast_set_cc_agent_policy(struct ast_cc_config_params *config, enum ast_cc_agent_policies value) +{ + /* Screw C and its weak type checking for making me have to do this + * validation at runtime. + */ + if (value < AST_CC_AGENT_NEVER || value > AST_CC_AGENT_GENERIC) { + return -1; + } + config->cc_agent_policy = value; + return 0; +} + +enum ast_cc_monitor_policies ast_get_cc_monitor_policy(struct ast_cc_config_params *config) +{ + return config->cc_monitor_policy; +} + +int ast_set_cc_monitor_policy(struct ast_cc_config_params *config, enum ast_cc_monitor_policies value) +{ + /* Screw C and its weak type checking for making me have to do this + * validation at runtime. + */ + if (value < AST_CC_MONITOR_NEVER || value > AST_CC_MONITOR_ALWAYS) { + return -1; + } + config->cc_monitor_policy = value; + return 0; +} + +unsigned int ast_get_cc_offer_timer(struct ast_cc_config_params *config) +{ + return config->cc_offer_timer; +} + +void ast_set_cc_offer_timer(struct ast_cc_config_params *config, unsigned int value) +{ + /* 0 is an unreasonable value for any timer. Stick with the default */ + if (value == 0) { + ast_log(LOG_WARNING, "0 is an invalid value for cc_offer_timer. Retaining value as %u\n", config->cc_offer_timer); + return; + } + config->cc_offer_timer = value; +} + +unsigned int ast_get_ccnr_available_timer(struct ast_cc_config_params *config) +{ + return config->ccnr_available_timer; +} + +void ast_set_ccnr_available_timer(struct ast_cc_config_params *config, unsigned int value) +{ + /* 0 is an unreasonable value for any timer. Stick with the default */ + if (value == 0) { + ast_log(LOG_WARNING, "0 is an invalid value for ccnr_available_timer. Retaining value as %u\n", config->ccnr_available_timer); + return; + } + config->ccnr_available_timer = value; +} + +unsigned int ast_get_cc_recall_timer(struct ast_cc_config_params *config) +{ + return config->cc_recall_timer; +} + +void ast_set_cc_recall_timer(struct ast_cc_config_params *config, unsigned int value) +{ + /* 0 is an unreasonable value for any timer. Stick with the default */ + if (value == 0) { + ast_log(LOG_WARNING, "0 is an invalid value for ccnr_available_timer. Retaining value as %u\n", config->cc_recall_timer); + return; + } + config->cc_recall_timer = value; +} + +unsigned int ast_get_ccbs_available_timer(struct ast_cc_config_params *config) +{ + return config->ccbs_available_timer; +} + +void ast_set_ccbs_available_timer(struct ast_cc_config_params *config, unsigned int value) +{ + /* 0 is an unreasonable value for any timer. Stick with the default */ + if (value == 0) { + ast_log(LOG_WARNING, "0 is an invalid value for ccbs_available_timer. Retaining value as %u\n", config->ccbs_available_timer); + return; + } + config->ccbs_available_timer = value; +} + +const char *ast_get_cc_agent_dialstring(struct ast_cc_config_params *config) +{ + return config->cc_agent_dialstring; +} + +void ast_set_cc_agent_dialstring(struct ast_cc_config_params *config, const char *const value) +{ + if (ast_strlen_zero(value)) { + config->cc_agent_dialstring[0] = '\0'; + } else { + ast_copy_string(config->cc_agent_dialstring, value, sizeof(config->cc_agent_dialstring)); + } +} + +unsigned int ast_get_cc_max_agents(struct ast_cc_config_params *config) +{ + return config->cc_max_agents; +} + +void ast_set_cc_max_agents(struct ast_cc_config_params *config, unsigned int value) +{ + config->cc_max_agents = value; +} + +unsigned int ast_get_cc_max_monitors(struct ast_cc_config_params *config) +{ + return config->cc_max_monitors; +} + +void ast_set_cc_max_monitors(struct ast_cc_config_params *config, unsigned int value) +{ + config->cc_max_monitors = value; +} + +const char *ast_get_cc_callback_macro(struct ast_cc_config_params *config) +{ + return config->cc_callback_macro; +} + +void ast_set_cc_callback_macro(struct ast_cc_config_params *config, const char * const value) +{ + if (ast_strlen_zero(value)) { + config->cc_callback_macro[0] = '\0'; + } else { + ast_copy_string(config->cc_callback_macro, value, sizeof(config->cc_callback_macro)); + } +} + +struct cc_monitor_backend { + AST_LIST_ENTRY(cc_monitor_backend) next; + const struct ast_cc_monitor_callbacks *callbacks; +}; + +AST_RWLIST_HEAD_STATIC(cc_monitor_backends, cc_monitor_backend); + +int ast_cc_monitor_register(const struct ast_cc_monitor_callbacks *callbacks) +{ + struct cc_monitor_backend *backend = ast_calloc(1, sizeof(*backend)); + + if (!backend) { + return -1; + } + + backend->callbacks = callbacks; + + AST_RWLIST_WRLOCK(&cc_monitor_backends); + AST_RWLIST_INSERT_TAIL(&cc_monitor_backends, backend, next); + AST_RWLIST_UNLOCK(&cc_monitor_backends); + return 0; +} + +static const struct ast_cc_monitor_callbacks *find_monitor_callbacks(const char * const type) +{ + struct cc_monitor_backend *backend; + const struct ast_cc_monitor_callbacks *callbacks = NULL; + + AST_RWLIST_RDLOCK(&cc_monitor_backends); + AST_RWLIST_TRAVERSE(&cc_monitor_backends, backend, next) { + if (!strcmp(backend->callbacks->type, type)) { + ast_log_dynamic_level(cc_logger_level, "Returning monitor backend %s\n", backend->callbacks->type); + callbacks = backend->callbacks; + break; + } + } + AST_RWLIST_UNLOCK(&cc_monitor_backends); + return callbacks; +} + +void ast_cc_monitor_unregister(const struct ast_cc_monitor_callbacks *callbacks) +{ + struct cc_monitor_backend *backend; + AST_RWLIST_WRLOCK(&cc_monitor_backends); + AST_RWLIST_TRAVERSE_SAFE_BEGIN(&cc_monitor_backends, backend, next) { + if (backend->callbacks == callbacks) { + AST_RWLIST_REMOVE_CURRENT(next); + ast_free(backend); + break; + } + } + AST_RWLIST_TRAVERSE_SAFE_END; + AST_RWLIST_UNLOCK(&cc_monitor_backends); +} + +struct cc_agent_backend { + AST_LIST_ENTRY(cc_agent_backend) next; + const struct ast_cc_agent_callbacks *callbacks; +}; + +AST_RWLIST_HEAD_STATIC(cc_agent_backends, cc_agent_backend); + +int ast_cc_agent_register(const struct ast_cc_agent_callbacks *callbacks) +{ + struct cc_agent_backend *backend = ast_calloc(1, sizeof(*backend)); + + if (!backend) { + return -1; + } + + backend->callbacks = callbacks; + AST_RWLIST_WRLOCK(&cc_agent_backends); + AST_RWLIST_INSERT_TAIL(&cc_agent_backends, backend, next); + AST_RWLIST_UNLOCK(&cc_agent_backends); + return 0; +} + +void ast_cc_agent_unregister(const struct ast_cc_agent_callbacks *callbacks) +{ + struct cc_agent_backend *backend; + AST_RWLIST_WRLOCK(&cc_agent_backends); + AST_RWLIST_TRAVERSE_SAFE_BEGIN(&cc_agent_backends, backend, next) { + if (backend->callbacks == callbacks) { + AST_RWLIST_REMOVE_CURRENT(next); + ast_free(backend); + break; + } + } + AST_RWLIST_TRAVERSE_SAFE_END; + AST_RWLIST_UNLOCK(&cc_agent_backends); +} + +static const struct ast_cc_agent_callbacks *find_agent_callbacks(struct ast_channel *chan) +{ + struct cc_agent_backend *backend; + const struct ast_cc_agent_callbacks *callbacks = NULL; + struct ast_cc_config_params *cc_params; + char type[32]; + + cc_params = ast_channel_get_cc_config_params(chan); + if (!cc_params) { + return NULL; + } + switch (ast_get_cc_agent_policy(cc_params)) { + case AST_CC_AGENT_GENERIC: + ast_copy_string(type, "generic", sizeof(type)); + break; + case AST_CC_AGENT_NATIVE: + ast_channel_get_cc_agent_type(chan, type, sizeof(type)); + break; + default: + ast_log_dynamic_level(cc_logger_level, "Not returning agent callbacks since this channel is configured not to have a CC agent\n"); + return NULL; + } + + AST_RWLIST_RDLOCK(&cc_agent_backends); + AST_RWLIST_TRAVERSE(&cc_agent_backends, backend, next) { + if (!strcmp(backend->callbacks->type, type)) { + ast_log_dynamic_level(cc_logger_level, "Returning agent backend %s\n", backend->callbacks->type); + callbacks = backend->callbacks; + break; + } + } + AST_RWLIST_UNLOCK(&cc_agent_backends); + return callbacks; +} + +static int cc_generic_monitor_request_cc(struct ast_cc_monitor *monitor, int *available_timer_id); +static int cc_generic_monitor_suspend(struct ast_cc_monitor *monitor); +static int cc_generic_monitor_status_response(struct ast_cc_monitor *monitor, enum ast_device_state devstate); +static int cc_generic_monitor_unsuspend(struct ast_cc_monitor *monitor); +static int cc_generic_monitor_cancel_available_timer(struct ast_cc_monitor *monitor, int *sched_id); +static void cc_generic_monitor_destructor(void *private_data); + +static struct ast_cc_monitor_callbacks generic_monitor_cbs = { + .type = "generic", + .request_cc = cc_generic_monitor_request_cc, + .suspend = cc_generic_monitor_suspend, + .status_response = cc_generic_monitor_status_response, + .unsuspend = cc_generic_monitor_unsuspend, + .cancel_available_timer = cc_generic_monitor_cancel_available_timer, + .destructor = cc_generic_monitor_destructor, +}; + +struct ao2_container *generic_monitors; + +struct generic_monitor_instance { + int core_id; + int is_suspended; + int monitoring; + AST_LIST_ENTRY(generic_monitor_instance) next; +}; + +struct generic_monitor_instance_list { + const char *device_name; + enum ast_device_state current_state; + struct ast_event_sub *sub; + AST_LIST_HEAD_NOLOCK(, generic_monitor_instance) list; +}; + +/*! + * \brief private data for generic device monitor + */ +struct generic_monitor_pvt { + /*! + * We need the device name during destruction so we + * can find the appropriate item to destroy. + */ + const char *device_name; + /*! + * We need the core ID for similar reasons. Once we + * find the appropriate item in our ao2_container, we + * need to remove the appropriate cc_monitor from the + * list of monitors. + */ + int core_id; +}; + +static int generic_monitor_hash_fn(const void *obj, const int flags) +{ + const struct generic_monitor_instance_list *generic_list = obj; + return ast_str_hash(generic_list->device_name); +} + +static int generic_monitor_cmp_fn(void *obj, void *arg, int flags) +{ + const struct generic_monitor_instance_list *generic_list1 = obj; + const struct generic_monitor_instance_list *generic_list2 = arg; + + return !strcmp(generic_list1->device_name, generic_list2->device_name) ? CMP_MATCH | CMP_STOP : 0; +} + +static struct generic_monitor_instance_list *find_generic_monitor_instance_list(const char * const device_name) +{ + struct generic_monitor_instance_list finder = {.device_name = device_name}; + + return ao2_t_find(generic_monitors, &finder, OBJ_POINTER, "Finding generic monitor instance list"); +} + +static void generic_monitor_instance_list_destructor(void *obj) +{ + struct generic_monitor_instance_list *generic_list = obj; + struct generic_monitor_instance *generic_instance; + + generic_list->sub = ast_event_unsubscribe(generic_list->sub); + while ((generic_instance = AST_LIST_REMOVE_HEAD(&generic_list->list, next))) { + ast_free(generic_instance); + } + ast_free((char *)generic_list->device_name); +} + +static void generic_monitor_devstate_cb(const struct ast_event *event, void *userdata); +static struct generic_monitor_instance_list *create_new_generic_list(struct ast_cc_monitor *monitor) +{ + struct generic_monitor_instance_list *generic_list = ao2_t_alloc(sizeof(*generic_list), + generic_monitor_instance_list_destructor, "allocate generic monitor instance list"); + + if (!generic_list) { + return NULL; + } + + if (!(generic_list->device_name = ast_strdup(monitor->interface->device_name))) { + cc_unref(generic_list, "Failed to strdup the monitor's device name"); + return NULL; + } + + if (!(generic_list->sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, generic_monitor_devstate_cb, + "Requesting CC", NULL, AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, + monitor->interface->device_name, AST_EVENT_IE_END))) { + cc_unref(generic_list, "Failed to subscribe to device state"); + return NULL; + } + generic_list->current_state = ast_device_state(monitor->interface->device_name); + ao2_t_link(generic_monitors, generic_list, "linking new generic monitor instance list"); + return generic_list; +} + +struct generic_tp_cb_data { + const char *device_name; + enum ast_device_state new_state; +}; + +static int generic_monitor_devstate_tp_cb(void *data) +{ + struct generic_tp_cb_data *gtcd = data; + enum ast_device_state new_state = gtcd->new_state; + enum ast_device_state previous_state = gtcd->new_state; + const char *monitor_name = gtcd->device_name; + struct generic_monitor_instance_list *generic_list; + struct generic_monitor_instance *generic_instance; + + if (!(generic_list = find_generic_monitor_instance_list(monitor_name))) { + /* The most likely cause for this is that we destroyed the monitor in the + * time between subscribing to its device state and the time this executes. + * Not really a big deal. + */ + ast_free((char *) gtcd->device_name); + ast_free(gtcd); + return 0; + } + + if (generic_list->current_state == new_state) { + /* The device state hasn't actually changed, so we don't really care */ + cc_unref(generic_list, "Kill reference of generic list in devstate taskprocessor callback"); + ast_free((char *) gtcd->device_name); + ast_free(gtcd); + return 0; + } + + previous_state = generic_list->current_state; + generic_list->current_state = new_state; + + if ((new_state == AST_DEVICE_NOT_INUSE || new_state == AST_DEVICE_UNKNOWN) && + (previous_state == AST_DEVICE_INUSE || previous_state == AST_DEVICE_UNAVAILABLE || + previous_state == AST_DEVICE_BUSY)) { + AST_LIST_TRAVERSE(&generic_list->list, generic_instance, next) { + if (!generic_instance->is_suspended && generic_instance->monitoring) { + generic_instance->monitoring = 0; + ast_cc_monitor_callee_available(generic_instance->core_id, "Generic monitored party has become available"); + break; + } + } + } + cc_unref(generic_list, "Kill reference of generic list in devstate taskprocessor callback"); + ast_free((char *) gtcd->device_name); + ast_free(gtcd); + return 0; +} + +static void generic_monitor_devstate_cb(const struct ast_event *event, void *userdata) +{ + /* Wow, it's cool that we've picked up on a state change, but we really want + * the actual work to be done in the core's taskprocessor execution thread + * so that all monitor operations can be serialized. Locks?! We don't need + * no steenkin' locks! + */ + struct generic_tp_cb_data *gtcd = ast_calloc(1, sizeof(*gtcd)); + + if (!gtcd) { + return; + } + + if (!(gtcd->device_name = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE)))) { + ast_free(gtcd); + return; + } + gtcd->new_state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE); + + if (ast_taskprocessor_push(cc_core_taskprocessor, generic_monitor_devstate_tp_cb, gtcd)) { + ast_free((char *)gtcd->device_name); + ast_free(gtcd); + } +} + +int ast_cc_available_timer_expire(const void *data) +{ + struct ast_cc_monitor *monitor = (struct ast_cc_monitor *) data; + int res; + monitor->available_timer_id = -1; + res = ast_cc_monitor_failed(monitor->core_id, monitor->interface->device_name, "Available timer expired for monitor"); + cc_unref(monitor, "Unref reference from scheduler\n"); + return res; +} + +static int cc_generic_monitor_request_cc(struct ast_cc_monitor *monitor, int *available_timer_id) +{ + struct generic_monitor_instance_list *generic_list; + struct generic_monitor_instance *generic_instance; + struct generic_monitor_pvt *gen_mon_pvt; + enum ast_cc_service_type service = monitor->service_offered; + int when; + + /* First things first. Native channel drivers will have their private data allocated + * at the time that they tell the core that they can offer CC. Generic is quite a bit + * different, and we wait until this point to allocate our private data. + */ + if (!(gen_mon_pvt = ast_calloc(1, sizeof(*gen_mon_pvt)))) { + return -1; + } + + if (!(gen_mon_pvt->device_name = ast_strdup(monitor->interface->device_name))) { + ast_free(gen_mon_pvt); + return -1; + } + + gen_mon_pvt->core_id = monitor->core_id; + + monitor->private_data = gen_mon_pvt; + + if (!(generic_list = find_generic_monitor_instance_list(monitor->interface->device_name))) { + if (!(generic_list = create_new_generic_list(monitor))) { + return -1; + } + } + + if (!(generic_instance = ast_calloc(1, sizeof(*generic_instance)))) { + /* The generic monitor destructor will take care of the appropriate + * deallocations + */ + cc_unref(generic_list, "Generic monitor instance failed to allocate"); + return -1; + } + generic_instance->core_id = monitor->core_id; + generic_instance->monitoring = 1; + AST_LIST_INSERT_TAIL(&generic_list->list, generic_instance, next); + when = service == AST_CC_CCBS ? ast_get_ccbs_available_timer(monitor->interface->config_params) : + ast_get_ccnr_available_timer(monitor->interface->config_params); + + *available_timer_id = ast_sched_thread_add(cc_sched_thread, when * 1000, + ast_cc_available_timer_expire, cc_ref(monitor, "Give the scheduler a monitor reference")); + if (*available_timer_id == -1) { + cc_unref(monitor, "Failed to schedule available timer. (monitor)"); + cc_unref(generic_list, "Failed to schedule available timer. (generic_list)"); + return -1; + } + ast_cc_monitor_request_acked(monitor->core_id, "Generic monitor for %s subscribed to device state.", + monitor->interface->device_name); + cc_unref(generic_list, "Finished with monitor instance reference in request cc callback"); + return 0; +} + +static int cc_generic_monitor_suspend(struct ast_cc_monitor *monitor) +{ + struct generic_monitor_instance_list *generic_list; + struct generic_monitor_instance *generic_instance; + enum ast_device_state state = ast_device_state(monitor->interface->device_name); + + if (!(generic_list = find_generic_monitor_instance_list(monitor->interface->device_name))) { + return -1; + } + + /* First we need to mark this particular monitor as being suspended. */ + AST_LIST_TRAVERSE(&generic_list->list, generic_instance, next) { + if (generic_instance->core_id == monitor->core_id) { + generic_instance->is_suspended = 1; + break; + } + } + + /* If the device being suspended is currently in use, then we don't need to + * take any further actions + */ + if (state != AST_DEVICE_NOT_INUSE && state != AST_DEVICE_UNKNOWN) { + cc_unref(generic_list, "Device is in use. Nothing to do. Unref generic list."); + return 0; + } + + /* If the device is not in use, though, then it may be possible to report the + * device's availability using a different monitor which is monitoring the + * same device + */ + + AST_LIST_TRAVERSE(&generic_list->list, generic_instance, next) { + if (!generic_instance->is_suspended) { + ast_cc_monitor_callee_available(generic_instance->core_id, "Generic monitored party has become available"); + break; + } + } + cc_unref(generic_list, "Done with generic list in suspend callback"); + return 0; +} + +static int cc_generic_monitor_status_response(struct ast_cc_monitor *monitor, enum ast_device_state devstate) +{ + /* The generic monitor will never issue a status request of the other side's agent. + * If this somehow gets called, something really fishy is going on. + */ + ast_log(LOG_WARNING, "Why has a generic monitor's status_response callback been called? CoreID is %d\n", monitor->core_id); + return 0; +} + +static int cc_generic_monitor_unsuspend(struct ast_cc_monitor *monitor) +{ + struct generic_monitor_instance *generic_instance; + struct generic_monitor_instance_list *generic_list = find_generic_monitor_instance_list(monitor->interface->device_name); + enum ast_device_state state = ast_device_state(monitor->interface->device_name); + + if (!generic_list) { + return -1; + } + /* If the device is currently available, we can immediately announce + * its availability + */ + if (state == AST_DEVICE_NOT_INUSE || state == AST_DEVICE_UNKNOWN) { + ast_cc_monitor_callee_available(monitor->core_id, "Generic monitored party has become available"); + } + + /* In addition, we need to mark this generic_monitor_instance as not being suspended anymore */ + AST_LIST_TRAVERSE(&generic_list->list, generic_instance, next) { + if (generic_instance->core_id == monitor->core_id) { + generic_instance->is_suspended = 0; + generic_instance->monitoring = 1; + break; + } + } + cc_unref(generic_list, "Done with generic list in cc_generic_monitor_unsuspend"); + return 0; +} + +static int cc_generic_monitor_cancel_available_timer(struct ast_cc_monitor *monitor, int *sched_id) +{ + ast_assert(sched_id != NULL); + + if (*sched_id == -1) { + return 0; + } + + ast_log_dynamic_level(cc_logger_level, "Core %d: Canceling generic monitor available timer for monitor %s\n", + monitor->core_id, monitor->interface->device_name); + if (!ast_sched_thread_del(cc_sched_thread, *sched_id)) { + cc_unref(monitor, "Remove scheduler's reference to the monitor"); + } + *sched_id = -1; + return 0; +} + +static void cc_generic_monitor_destructor(void *private_data) +{ + struct generic_monitor_pvt *gen_mon_pvt = private_data; + struct generic_monitor_instance_list *generic_list; + struct generic_monitor_instance *generic_instance; + + if (!private_data) { + /* If the private data is NULL, that means that the monitor hasn't even + * been created yet, but that the destructor was called. While this sort + * of behavior is useful for native monitors, with a generic one, there is + * nothing in particular to do. + */ + return; + } + + ast_log_dynamic_level(cc_logger_level, "Core %d: Destroying generic monitor %s\n", + gen_mon_pvt->core_id, gen_mon_pvt->device_name); + + if (!(generic_list = find_generic_monitor_instance_list(gen_mon_pvt->device_name))) { + /* If there's no generic list, that means that the monitor is being destroyed + * before we actually got to request CC. Not a biggie. Same in the situation + * below if the list traversal should complete without finding an entry. + */ + ast_free((char *)gen_mon_pvt->device_name); + ast_free(gen_mon_pvt); + return; + } + + AST_LIST_TRAVERSE_SAFE_BEGIN(&generic_list->list, generic_instance, next) { + if (generic_instance->core_id == gen_mon_pvt->core_id) { + AST_LIST_REMOVE_CURRENT(next); + ast_free(generic_instance); + break; + } + } + AST_LIST_TRAVERSE_SAFE_END; + + if (AST_LIST_EMPTY(&generic_list->list)) { + /* No more monitors with this device name exist. Time to unlink this + * list from the container + */ + ao2_t_unlink(generic_monitors, generic_list, "Generic list is empty. Unlink it from the container"); + } + cc_unref(generic_list, "Done with generic list in generic monitor destructor"); + ast_free((char *)gen_mon_pvt->device_name); + ast_free(gen_mon_pvt); +} + +static void cc_interface_destroy(void *data) +{ + struct ast_cc_interface *interface = data; + ast_log_dynamic_level(cc_logger_level, "Destroying cc interface %s\n", interface->device_name); + ast_cc_config_params_destroy(interface->config_params); +} + +/*! + * \brief Data regarding an extension monitor's child's dialstrings + * + * \details + * In developing CCSS, we had most aspects of its operation finished, + * but there was one looming problem that we had failed to get right. + * In our design document, we stated that when a CC recall occurs, all + * endpoints that had been dialed originally would be called back. + * Unfortunately, our implementation only allowed for devices which had + * active monitors to inhabit the CC_INTERFACES channel variable, thus + * making the automated recall only call monitored devices. + * + * Devices that were not CC-capable, or devices which failed CC at some + * point during the process would not make it into the CC_INTERFACES + * channel variable. This struct is meant as a remedy for the problem. + */ +struct extension_child_dialstring { + /*! + * \brief the original dialstring used to call a particular device + * + * \details + * When someone dials a particular endpoint, the dialstring used in + * the dialplan is copied into this buffer. What's important here is + * that this is the ORIGINAL dialstring, not the dialstring saved on + * a device monitor. The dialstring on a device monitor is what should + * be used when recalling that device. The two dialstrings may not be + * the same. + * + * By keeping a copy of the original dialstring used, we can fall back + * to using it if the device either does not ever offer CC or if the + * device at some point fails for some reason, such as a timer expiration. + */ + char original_dialstring[AST_CHANNEL_NAME]; + /*! + * \brief The name of the device being dialed + * + * \details + * This serves mainly as a key when searching for a particular dialstring. + * For instance, let's say that we have called device SIP/400@somepeer. This + * device offers call completion, but then due to some unforeseen circumstance, + * this device backs out and makes CC unavailable. When that happens, we need + * to find the dialstring that corresponds to that device, and we use the + * stored device name as a way to find it. + * + * Note that there is one particular case where the device name stored here + * will be empty. This is the case where we fail to request a channel, but we + * still can make use of generic call completion. In such a case, since we never + * were able to request the channel, we can't find what its device name is. In + * this case, however, it is not important because the dialstring is guaranteed + * to be the same both here and in the device monitor. + */ + char device_name[AST_CHANNEL_NAME]; + /*! + * \brief Is this structure valid for use in CC_INTERFACES? + * + * \details + * When this structure is first created, all information stored here is planned + * to be used, so we set the is_valid flag. However, if a device offers call + * completion, it will potentially have its own dialstring to use for the recall, + * so we find this structure and clear the is_valid flag. By clearing the is_valid + * flag, we won't try to populate the CC_INTERFACES variable with the dialstring + * stored in this struct. Now, if later, the device which had offered CC should fail, + * perhaps due to a timer expiration, then we need to re-set the is_valid flag. This + * way, we still will end up placing a call to the device again, and the dialstring + * used will be the same as was originally used. + */ + int is_valid; + AST_LIST_ENTRY(extension_child_dialstring) next; +}; + +/*! + * \brief Private data for an extension monitor + */ +struct extension_monitor_pvt { + AST_LIST_HEAD_NOLOCK(, extension_child_dialstring) child_dialstrings; +}; + +static void cc_extension_monitor_destructor(void *private_data) +{ + struct extension_monitor_pvt *extension_pvt = private_data; + struct extension_child_dialstring *child_dialstring; + + /* This shouldn't be possible, but I'm paranoid */ + if (!extension_pvt) { + return; + } + + while ((child_dialstring = AST_LIST_REMOVE_HEAD(&extension_pvt->child_dialstrings, next))) { + ast_free(child_dialstring); + } + ast_free(extension_pvt); +} + +static void cc_monitor_destroy(void *data) +{ + struct ast_cc_monitor *monitor = data; + /* During the monitor creation process, it is possible for this + * function to be called prior to when callbacks are assigned + * to the monitor. Also, extension monitors do not have callbacks + * assigned to them, so we wouldn't want to segfault when we try + * to destroy one of them. + */ + ast_log_dynamic_level(cc_logger_level, "Core %d: Calling destructor for monitor %s\n", + monitor->core_id, monitor->interface->device_name); + if (monitor->interface->monitor_class == AST_CC_EXTENSION_MONITOR) { + cc_extension_monitor_destructor(monitor->private_data); + } + if (monitor->callbacks) { + monitor->callbacks->destructor(monitor->private_data); + } + cc_unref(monitor->interface, "Unreffing tree's reference to interface"); + ast_free(monitor->dialstring); +} + +static void cc_interface_tree_destroy(void *data) +{ + struct cc_monitor_tree *cc_interface_tree = data; + struct ast_cc_monitor *monitor; + while ((monitor = AST_LIST_REMOVE_HEAD(cc_interface_tree, next))) { + if (monitor->callbacks) { + monitor->callbacks->cancel_available_timer(monitor, &monitor->available_timer_id); + } + cc_unref(monitor, "Destroying all monitors"); + } + AST_LIST_HEAD_DESTROY(cc_interface_tree); +} + +/*! + * This counter is used for assigning unique ids + * to CC-enabled dialed interfaces. + */ +static int dialed_cc_interface_counter; + +/*! + * \internal + * \brief data stored in CC datastore + * + * The datastore creates a list of interfaces that were + * dialed, including both extensions and devices. In addition + * to the intrinsic data of the tree, some extra information + * is needed for use by app_dial. + */ +struct dialed_cc_interfaces { + /*! + * This value serves a dual-purpose. When dial starts, if the + * dialed_cc_interfaces datastore currently exists on the calling + * channel, then the dial_parent_id will serve as a means of + * letting the new extension cc_monitor we create know + * who his parent is. This value will be the extension + * cc_monitor that dialed the local channel that resulted + * in the new Dial app being called. + * + * In addition, once an extension cc_monitor is created, + * the dial_parent_id will be changed to the id of that newly + * created interface. This way, device interfaces created from + * receiving AST_CONTROL_CC frames can use this field to determine + * who their parent extension interface should be. + */ + unsigned int dial_parent_id; + /*! + * Identifier for the potential CC request that may be made + * based on this call. Even though an instance of the core may + * not be made (since the caller may not request CC), we allocate + * a new core_id at the beginning of the call so that recipient + * channel drivers can have the information handy just in case + * the caller does end up requesting CC. + */ + int core_id; + /*! + * When a new Dial application is started, and the datastore + * already exists on the channel, we can determine if we + * should be adding any new interface information to tree. + */ + char ignore; + /*! + * When it comes time to offer CC to the caller, we only want to offer + * it to the original incoming channel. For nested Dials and outbound + * channels, it is incorrect to attempt such a thing. This flag indicates + * if the channel to which this datastore is attached may be legally + * offered CC when the call is finished. + */ + char is_original_caller; + /*! + * Reference-counted "tree" of interfaces. + */ + struct cc_monitor_tree *interface_tree; +}; + +/*! + * \internal + * \brief Destructor function for cc_interfaces datastore + * + * This function will free the actual datastore and drop + * the refcount for the monitor tree by one. In cases + * where CC can actually be used, this unref will not + * result in the destruction of the monitor tree, because + * the CC core will still have a reference. + * + * \param data The dialed_cc_interfaces struct to destroy + */ +static void dialed_cc_interfaces_destroy(void *data) +{ + struct dialed_cc_interfaces *cc_interfaces = data; + cc_unref(cc_interfaces->interface_tree, "Unref dial's ref to monitor tree"); + ast_free(cc_interfaces); +} + +/*! + * \internal + * \brief Duplicate callback for cc_interfaces datastore + * + * Integers are copied by value, but the monitor tree + * is done via a shallow copy and a bump of the refcount. + * This way, sub-Dials will be appending interfaces onto + * the same list as this call to Dial. + * + * \param data The old dialed_cc_interfaces we want to copy + * \retval NULL Could not allocate memory for new dialed_cc_interfaces + * \retval non-NULL The new copy of the dialed_cc_interfaces + */ +static void *dialed_cc_interfaces_duplicate(void *data) +{ + struct dialed_cc_interfaces *old_cc_interfaces = data; + struct dialed_cc_interfaces *new_cc_interfaces = ast_calloc(1, sizeof(*new_cc_interfaces)); + if (!new_cc_interfaces) { + return NULL; + } + new_cc_interfaces->ignore = old_cc_interfaces->ignore; + new_cc_interfaces->dial_parent_id = old_cc_interfaces->dial_parent_id; + new_cc_interfaces->is_original_caller = 0; + cc_ref(old_cc_interfaces->interface_tree, "New ref due to duplication of monitor tree"); + new_cc_interfaces->core_id = old_cc_interfaces->core_id; + new_cc_interfaces->interface_tree = old_cc_interfaces->interface_tree; + return new_cc_interfaces; +} + +/*! + * \internal + * \brief information regarding the dialed_cc_interfaces datastore + * + * The dialed_cc_interfaces datastore is responsible for keeping track + * of what CC-enabled interfaces have been dialed by the caller. For + * more information regarding the actual structure of the tree, see + * the documentation provided in include/asterisk/ccss.h + */ +static const struct ast_datastore_info dialed_cc_interfaces_info = { + .type = "Dial CC Interfaces", + .duplicate = dialed_cc_interfaces_duplicate, + .destroy = dialed_cc_interfaces_destroy, +}; + +static struct extension_monitor_pvt *extension_monitor_pvt_init(void) +{ + struct extension_monitor_pvt *ext_pvt = ast_calloc(1, sizeof(*ext_pvt)); + if (!ext_pvt) { + return NULL; + } + AST_LIST_HEAD_INIT_NOLOCK(&ext_pvt->child_dialstrings); + return ext_pvt; +} + +void ast_cc_extension_monitor_add_dialstring(struct ast_channel *incoming, const char * const dialstring, const char * const device_name) +{ + struct ast_datastore *cc_datastore; + struct dialed_cc_interfaces *cc_interfaces; + struct ast_cc_monitor *monitor; + struct extension_monitor_pvt *extension_pvt; + struct extension_child_dialstring *child_dialstring; + struct cc_monitor_tree *interface_tree; + int id; + + ast_channel_lock(incoming); + if (!(cc_datastore = ast_channel_datastore_find(incoming, &dialed_cc_interfaces_info, NULL))) { + ast_channel_unlock(incoming); + return; + } + + cc_interfaces = cc_datastore->data; + interface_tree = cc_interfaces->interface_tree; + id = cc_interfaces->dial_parent_id; + ast_channel_unlock(incoming); + + AST_LIST_LOCK(interface_tree); + AST_LIST_TRAVERSE(interface_tree, monitor, next) { + if (monitor->id == id) { + break; + } + } + + if (!monitor) { + AST_LIST_UNLOCK(interface_tree); + return; + } + + extension_pvt = monitor->private_data; + if (!(child_dialstring = ast_calloc(1, sizeof(*child_dialstring)))) { + AST_LIST_UNLOCK(interface_tree); + return; + } + ast_copy_string(child_dialstring->original_dialstring, dialstring, sizeof(child_dialstring->original_dialstring)); + ast_copy_string(child_dialstring->device_name, device_name, sizeof(child_dialstring->device_name)); + child_dialstring->is_valid = 1; + AST_LIST_INSERT_TAIL(&extension_pvt->child_dialstrings, child_dialstring, next); + AST_LIST_UNLOCK(interface_tree); +} + +static void cc_extension_monitor_change_is_valid(struct cc_core_instance *core_instance, unsigned int parent_id, const char * const device_name, int is_valid) +{ + struct ast_cc_monitor *monitor_iter; + struct extension_monitor_pvt *extension_pvt; + struct extension_child_dialstring *child_dialstring; + + AST_LIST_TRAVERSE(core_instance->monitors, monitor_iter, next) { + if (monitor_iter->id == parent_id) { + break; + } + } + + if (!monitor_iter) { + return; + } + extension_pvt = monitor_iter->private_data; + + AST_LIST_TRAVERSE(&extension_pvt->child_dialstrings, child_dialstring, next) { + if (!strcmp(child_dialstring->device_name, device_name)) { + child_dialstring->is_valid = is_valid; + break; + } + } +} + +/*! + * \internal + * \brief Allocate and initialize an "extension" interface for CC purposes + * + * When app_dial starts, this function is called in order to set up the + * information about the extension in which this Dial is occurring. Any + * devices dialed will have this particular cc_monitor as a parent. + * + * \param exten Extension from which Dial is occurring + * \param context Context to which exten belongs + * \param parent_id What should we set the parent_id of this interface to? + * \retval NULL Memory allocation failure + * \retval non-NULL The newly-created cc_monitor for the extension + */ +static struct ast_cc_monitor *cc_extension_monitor_init(const char * const exten, const char * const context, const unsigned int parent_id) +{ + struct ast_str *str = ast_str_alloca(2 * AST_MAX_EXTENSION); + struct ast_cc_interface *cc_interface; + struct ast_cc_monitor *monitor; + + ast_str_set(&str, 0, "%s@%s", exten, context); + + if (!(cc_interface = ao2_t_alloc(sizeof(*cc_interface) + ast_str_strlen(str), cc_interface_destroy, + "Allocating new ast_cc_interface"))) { + return NULL; + } + + if (!(monitor = ao2_t_alloc(sizeof(*monitor), cc_monitor_destroy, "Allocating new ast_cc_monitor"))) { + cc_unref(cc_interface, "failed to allocate the monitor, so unref the interface"); + return NULL; + } + + if (!(monitor->private_data = extension_monitor_pvt_init())) { + cc_unref(monitor, "Failed to initialize extension monitor private data. uref monitor"); + cc_unref(cc_interface, "Failed to initialize extension monitor private data. unref cc_interface"); + } + + monitor->id = ast_atomic_fetchadd_int(&dialed_cc_interface_counter, +1); + monitor->parent_id = parent_id; + cc_interface->monitor_type = "extension"; + cc_interface->monitor_class = AST_CC_EXTENSION_MONITOR; + strcpy(cc_interface->device_name, ast_str_buffer(str)); + monitor->interface = cc_interface; + ast_log_dynamic_level(cc_logger_level, "Created an extension cc interface for '%s' with id %d and parent %d\n", cc_interface->device_name, monitor->id, monitor->parent_id); + return monitor; +} + +/*! + * \internal + * \brief allocate dialed_cc_interfaces datastore and initialize fields + * + * This function is called when Situation 1 occurs in ast_cc_call_init. + * See that function for more information on what Situation 1 is. + * + * In this particular case, we have to do a lot of memory allocation in order + * to create the datastore, the data for the datastore, the tree of interfaces + * that we'll be adding to, and the initial extension interface for this Dial + * attempt. + * + * \param chan The channel onto which the datastore should be added. + * \retval -1 An error occurred + * \retval 0 Success + */ +static int cc_interfaces_datastore_init(struct ast_channel *chan) { + struct dialed_cc_interfaces *interfaces; + struct ast_cc_monitor *monitor; + struct ast_datastore *dial_cc_datastore; + + /*XXX This may be a bit controversial. In an attempt to not allocate + * extra resources, I make sure that a future request will be within + * limits. The problem here is that it is reasonable to think that + * even if we're not within the limits at this point, we may be by + * the time the requestor will have made his request. This may be + * deleted at some point. + */ + if (!ast_cc_request_is_within_limits()) { + return 0; + } + + if (!(interfaces = ast_calloc(1, sizeof(*interfaces)))) { + return -1; + } + + if (!(monitor = cc_extension_monitor_init(S_OR(chan->macroexten, chan->exten), S_OR(chan->macrocontext, chan->context), 0))) { + ast_free(interfaces); + return -1; + } + + if (!(dial_cc_datastore = ast_datastore_alloc(&dialed_cc_interfaces_info, NULL))) { + cc_unref(monitor, "Could not allocate the dialed interfaces datastore. Unreffing monitor"); + ast_free(interfaces); + return -1; + } + + if (!(interfaces->interface_tree = ao2_t_alloc(sizeof(*interfaces->interface_tree), cc_interface_tree_destroy, + "Allocate monitor tree"))) { + ast_datastore_free(dial_cc_datastore); + cc_unref(monitor, "Could not allocate monitor tree on dialed interfaces datastore. Unreffing monitor"); + ast_free(interfaces); + return -1; + } + + /* Finally, all that allocation is done... */ + AST_LIST_HEAD_INIT(interfaces->interface_tree); + AST_LIST_INSERT_TAIL(interfaces->interface_tree, monitor, next); + cc_ref(monitor, "List's reference to extension monitor"); + dial_cc_datastore->data = interfaces; + dial_cc_datastore->inheritance = DATASTORE_INHERIT_FOREVER; + interfaces->dial_parent_id = monitor->id; + interfaces->core_id = monitor->core_id = ast_atomic_fetchadd_int(&core_id_counter, +1); + interfaces->is_original_caller = 1; + ast_channel_lock(chan); + ast_channel_datastore_add(chan, dial_cc_datastore); + ast_channel_unlock(chan); + cc_unref(monitor, "Unreffing allocation's reference"); + return 0; +} + +/*! + * \internal + * \brief Call a monitor's destructor before the monitor has been allocated + * \since 1.8 + * + * \param monitor_type The type of monitor callbacks to use when calling the destructor + * \param private_data Data allocated by a channel driver that must be freed + * + * \details + * I'll admit, this is a bit evil. + * + * When a channel driver determines that it can offer a call completion service to + * a caller, it is very likely that the channel driver will need to allocate some + * data so that when the time comes to request CC, the channel driver will have the + * necessary data at hand. + * + * The problem is that there are many places where failures may occur before the monitor + * has been properly allocated and had its callbacks assigned to it. If one of these + * failures should occur, then we still need to let the channel driver know that it + * must destroy the data that it allocated. + * + * \return Nothing + */ +static void call_destructor_with_no_monitor(const char * const monitor_type, void *private_data) +{ + const struct ast_cc_monitor_callbacks *monitor_callbacks = find_monitor_callbacks(monitor_type); + + if (!monitor_callbacks) { + return; + } + + monitor_callbacks->destructor(private_data); +} + +/*! + * \internal + * \brief Allocate and intitialize a device cc_monitor + * + * For all intents and purposes, this is the same as + * cc_extension_monitor_init, except that there is only + * a single parameter used for naming the interface. + * + * This function is called when handling AST_CONTROL_CC frames. + * The device has reported that CC is possible, so we add it + * to the interface_tree. + * + * Note that it is not necessarily erroneous to add the same + * device to the tree twice. If the same device is called by + * two different extension during the same call, then + * that is a legitimate situation. Of course, I'm pretty sure + * the dialed_interfaces global datastore will not allow that + * to happen anyway. + * + * \param device_name The name of the device being added to the tree + * \param dialstring The dialstring used to dial the device being added + * \param parent_id The parent of this new tree node. + * \retval NULL Memory allocation failure + * \retval non-NULL The new ast_cc_interface created. + */ +static struct ast_cc_monitor *cc_device_monitor_init(const char * const device_name, const char * const dialstring, const struct cc_control_payload *cc_data, int core_id) +{ + struct ast_cc_interface *cc_interface; + struct ast_cc_monitor *monitor; + size_t device_name_len = strlen(device_name); + int parent_id = cc_data->parent_interface_id; + + if (!(cc_interface = ao2_t_alloc(sizeof(*cc_interface) + device_name_len, cc_interface_destroy, + "Allocating new ast_cc_interface"))) { + return NULL; + } + + if (!(cc_interface->config_params = ast_cc_config_params_init())) { + cc_unref(cc_interface, "Failed to allocate config params, unref interface"); + return NULL; + } + + if (!(monitor = ao2_t_alloc(sizeof(*monitor), cc_monitor_destroy, "Allocating new ast_cc_monitor"))) { + cc_unref(cc_interface, "Failed to allocate monitor, unref interface"); + return NULL; + } + + if (!(monitor->dialstring = ast_strdup(dialstring))) { + cc_unref(monitor, "Failed to copy dialable name. Unref monitor"); + cc_unref(cc_interface, "Failed to copy dialable name"); + return NULL; + } + + if (!(monitor->callbacks = find_monitor_callbacks(cc_data->monitor_type))) { + cc_unref(monitor, "Failed to find monitor callbacks. Unref monitor"); + cc_unref(cc_interface, "Failed to find monitor callbacks"); + return NULL; + } + + strcpy(cc_interface->device_name, device_name); + monitor->id = ast_atomic_fetchadd_int(&dialed_cc_interface_counter, +1); + monitor->parent_id = parent_id; + monitor->core_id = core_id; + monitor->service_offered = cc_data->service; + monitor->private_data = cc_data->private_data; + cc_interface->monitor_type = cc_data->monitor_type; + cc_interface->monitor_class = AST_CC_DEVICE_MONITOR; + monitor->interface = cc_interface; + monitor->available_timer_id = -1; + ast_cc_copy_config_params(cc_interface->config_params, &cc_data->config_params); + ast_log_dynamic_level(cc_logger_level, "Core %d: Created a device cc interface for '%s' with id %d and parent %d\n", + monitor->core_id, cc_interface->device_name, monitor->id, monitor->parent_id); + return monitor; +} + +/*! + * \details + * Unless we are ignoring CC for some reason, we will always + * call this function when we read an AST_CONTROL_CC frame + * from an outbound channel. + * + * This function will call cc_device_monitor_init to + * create the new cc_monitor for the device from which + * we read the frame. In addition, the new device will be added + * to the monitor tree on the dialed_cc_interfaces datastore + * on the inbound channel. + * + * If this is the first AST_CONTROL_CC frame that we have handled + * for this call, then we will also initialize the CC core for + * this call. + */ +void ast_handle_cc_control_frame(struct ast_channel *inbound, struct ast_channel *outbound, void *frame_data) +{ + char *device_name; + char *dialstring; + struct ast_cc_monitor *monitor; + struct ast_datastore *cc_datastore; + struct dialed_cc_interfaces *cc_interfaces; + struct cc_control_payload *cc_data = frame_data; + struct cc_core_instance *core_instance; + + device_name = cc_data->device_name; + dialstring = cc_data->dialstring; + + ast_channel_lock(inbound); + if (!(cc_datastore = ast_channel_datastore_find(inbound, &dialed_cc_interfaces_info, NULL))) { + ast_log(LOG_WARNING, "Unable to retrieve CC datastore while processing CC frame from '%s'. CC services will be unavailable.\n", device_name); + ast_channel_unlock(inbound); + call_destructor_with_no_monitor(cc_data->monitor_type, cc_data->private_data); + return; + } + + cc_interfaces = cc_datastore->data; + + if (cc_interfaces->ignore) { + ast_channel_unlock(inbound); + call_destructor_with_no_monitor(cc_data->monitor_type, cc_data->private_data); + return; + } + + if (!cc_interfaces->is_original_caller) { + /* If the is_original_caller is not set on the *inbound* channel, then + * it must be a local channel. As such, we do not want to create a core instance + * or an agent for the local channel. Instead, we want to pass this along to the + * other side of the local channel so that the original caller can benefit. + */ + ast_channel_unlock(inbound); + ast_indicate_data(inbound, AST_CONTROL_CC, cc_data, sizeof(*cc_data)); + return; + } + + core_instance = find_cc_core_instance(cc_interfaces->core_id); + if (!core_instance) { + core_instance = cc_core_init_instance(inbound, cc_interfaces->interface_tree, + cc_interfaces->core_id, cc_data); + if (!core_instance) { + cc_interfaces->ignore = 1; + ast_channel_unlock(inbound); + call_destructor_with_no_monitor(cc_data->monitor_type, cc_data->private_data); + return; + } + } + + ast_channel_unlock(inbound); + + /* Yeah this kind of sucks, but luckily most people + * aren't dialing thousands of interfaces on every call + * + * This traversal helps us to not create duplicate monitors in + * case a device queues multiple CC control frames. + */ + AST_LIST_LOCK(cc_interfaces->interface_tree); + AST_LIST_TRAVERSE(cc_interfaces->interface_tree, monitor, next) { + if (!strcmp(monitor->interface->device_name, device_name)) { + ast_log_dynamic_level(cc_logger_level, "Core %d: Device %s sent us multiple CC control frames. Ignoring those beyond the first.\n", + core_instance->core_id, device_name); + AST_LIST_UNLOCK(cc_interfaces->interface_tree); + cc_unref(core_instance, "Returning early from ast_handle_cc_control_frame. Unref core_instance"); + call_destructor_with_no_monitor(cc_data->monitor_type, cc_data->private_data); + return; + } + } + AST_LIST_UNLOCK(cc_interfaces->interface_tree); + + if (!(monitor = cc_device_monitor_init(device_name, dialstring, cc_data, core_instance->core_id))) { + ast_log(LOG_WARNING, "Unable to create CC device interface for '%s'. CC services will be unavailable on this interface.\n", device_name); + cc_unref(core_instance, "Returning early from ast_handle_cc_control_frame. Unref core_instance"); + call_destructor_with_no_monitor(cc_data->monitor_type, cc_data->private_data); + return; + } + + AST_LIST_LOCK(cc_interfaces->interface_tree); + cc_ref(monitor, "monitor tree's reference to the monitor"); + AST_LIST_INSERT_TAIL(cc_interfaces->interface_tree, monitor, next); + AST_LIST_UNLOCK(cc_interfaces->interface_tree); + + cc_extension_monitor_change_is_valid(core_instance, monitor->parent_id, monitor->interface->device_name, 0); + + manager_event(EVENT_FLAG_CC, "CCAvailable", + "CoreID: %d\r\n" + "Callee: %s\r\n" + "Service: %s\r\n", + cc_interfaces->core_id, device_name, cc_service_to_string(cc_data->service) + ); + + cc_unref(core_instance, "Done with core_instance after handling CC control frame"); + cc_unref(monitor, "Unref reference from allocating monitor"); +} + +int ast_cc_call_init(struct ast_channel *chan, int *ignore_cc) +{ + /* There are three situations to deal with here: + * + * 1. The channel does not have a dialed_cc_interfaces datastore on + * it. This means that this is the first time that Dial has + * been called. We need to create/initialize the datastore. + * + * 2. The channel does have a cc_interface datastore on it and + * the "ignore" indicator is 0. This means that a Local channel + * was called by a "parent" dial. We can check the datastore's + * parent field to see who the root of this particular dial tree + * is. + * + * 3. The channel does have a cc_interface datastore on it and + * the "ignore" indicator is 1. This means that a second Dial call + * is being made from an extension. In this case, we do not + * want to make any additions/modifications to the datastore. We + * will instead set a flag to indicate that CCSS is completely + * disabled for this Dial attempt. + */ + + struct ast_datastore *cc_interfaces_datastore; + struct dialed_cc_interfaces *interfaces; + struct ast_cc_monitor *monitor; + struct ast_cc_config_params *cc_params; + + ast_channel_lock(chan); + + cc_params = ast_channel_get_cc_config_params(chan); + if (!cc_params) { + ast_channel_unlock(chan); + return -1; + } + if (ast_get_cc_agent_policy(cc_params) == AST_CC_AGENT_NEVER) { + /* We can't offer CC to this caller anyway, so don't bother with CC on this call + */ + *ignore_cc = 1; + ast_channel_unlock(chan); + ast_log_dynamic_level(cc_logger_level, "Agent policy for %s is 'never'. CC not possible\n", chan->name); + return 0; + } + + if (!(cc_interfaces_datastore = ast_channel_datastore_find(chan, &dialed_cc_interfaces_info, NULL))) { + /* Situation 1 has occurred */ + ast_channel_unlock(chan); + return cc_interfaces_datastore_init(chan); + } + interfaces = cc_interfaces_datastore->data; + ast_channel_unlock(chan); + + if (interfaces->ignore) { + /* Situation 3 has occurred */ + *ignore_cc = 1; + ast_log_dynamic_level(cc_logger_level, "Datastore is present with ignore flag set. Ignoring CC offers on this call\n"); + return 0; + } + + /* Situation 2 has occurred */ + if (!(monitor = cc_extension_monitor_init(S_OR(chan->macroexten, chan->exten), + S_OR(chan->macrocontext, chan->context), interfaces->dial_parent_id))) { + return -1; + } + monitor->core_id = interfaces->core_id; + AST_LIST_LOCK(interfaces->interface_tree); + cc_ref(monitor, "monitor tree's reference to the monitor"); + AST_LIST_INSERT_TAIL(interfaces->interface_tree, monitor, next); + AST_LIST_UNLOCK(interfaces->interface_tree); + interfaces->dial_parent_id = monitor->id; + cc_unref(monitor, "Unref monitor's allocation reference"); + return 0; +} + +int ast_cc_request_is_within_limits(void) +{ + return cc_request_count < global_cc_max_requests; +} + +int ast_cc_get_current_core_id(struct ast_channel *chan) +{ + struct ast_datastore *datastore; + struct dialed_cc_interfaces *cc_interfaces; + int core_id_return; + + ast_channel_lock(chan); + if (!(datastore = ast_channel_datastore_find(chan, &dialed_cc_interfaces_info, NULL))) { + ast_channel_unlock(chan); + return -1; + } + + cc_interfaces = datastore->data; + core_id_return = cc_interfaces->ignore ? -1 : cc_interfaces->core_id; + ast_channel_unlock(chan); + return core_id_return; + +} + +static long count_agents(const char * const caller, const int core_id_exception) +{ + struct count_agents_cb_data data = {.core_id_exception = core_id_exception,}; + + ao2_t_callback_data(cc_core_instances, OBJ_NODATA, count_agents_cb, (char *)caller, &data, "Counting agents"); + ast_log_dynamic_level(cc_logger_level, "Counted %d agents\n", data.count); + return data.count; +} + +static void kill_duplicate_offers(char *caller) +{ + unsigned long match_flags = MATCH_NO_REQUEST; + ao2_t_callback_data(cc_core_instances, OBJ_UNLINK | OBJ_NODATA, match_agent, caller, &match_flags, "Killing duplicate offers"); +} + +static void check_callback_sanity(const struct ast_cc_agent_callbacks *callbacks) +{ + ast_assert(callbacks->init != NULL); + ast_assert(callbacks->start_offer_timer != NULL); + ast_assert(callbacks->stop_offer_timer != NULL); + ast_assert(callbacks->ack != NULL); + ast_assert(callbacks->status_request != NULL); + ast_assert(callbacks->start_monitoring != NULL); + ast_assert(callbacks->callee_available != NULL); + ast_assert(callbacks->destructor != NULL); +} + +static void agent_destroy(void *data) +{ + struct ast_cc_agent *agent = data; + + if (agent->callbacks) { + agent->callbacks->destructor(agent); + } + ast_cc_config_params_destroy(agent->cc_params); +} + +static struct ast_cc_agent *cc_agent_init(struct ast_channel *caller_chan, + const char * const caller_name, const int core_id, + struct cc_monitor_tree *interface_tree) +{ + struct ast_cc_agent *agent; + struct ast_cc_config_params *cc_params; + + if (!(agent = ao2_t_alloc(sizeof(*agent) + strlen(caller_name), agent_destroy, + "Allocating new ast_cc_agent"))) { + return NULL; + } + + agent->core_id = core_id; + strcpy(agent->device_name, caller_name); + + cc_params = ast_channel_get_cc_config_params(caller_chan); + if (!cc_params) { + cc_unref(agent, "Could not get channel config params."); + return NULL; + } + if (!(agent->cc_params = ast_cc_config_params_init())) { + cc_unref(agent, "Could not init agent config params."); + return NULL; + } + ast_cc_copy_config_params(agent->cc_params, cc_params); + + if (!(agent->callbacks = find_agent_callbacks(caller_chan))) { + cc_unref(agent, "Could not find agent callbacks."); + return NULL; + } + check_callback_sanity(agent->callbacks); + + if (agent->callbacks->init(agent, caller_chan)) { + cc_unref(agent, "Agent init callback failed."); + return NULL; + } + ast_log_dynamic_level(cc_logger_level, "Core %d: Created an agent for caller %s\n", + agent->core_id, agent->device_name); + return agent; +} + +/* Generic agent callbacks */ +static int cc_generic_agent_init(struct ast_cc_agent *agent, struct ast_channel *chan); +static int cc_generic_agent_start_offer_timer(struct ast_cc_agent *agent); +static int cc_generic_agent_stop_offer_timer(struct ast_cc_agent *agent); +static void cc_generic_agent_ack(struct ast_cc_agent *agent); +static int cc_generic_agent_status_request(struct ast_cc_agent *agent); +static int cc_generic_agent_stop_ringing(struct ast_cc_agent *agent); +static int cc_generic_agent_start_monitoring(struct ast_cc_agent *agent); +static int cc_generic_agent_recall(struct ast_cc_agent *agent); +static void cc_generic_agent_destructor(struct ast_cc_agent *agent); + +static struct ast_cc_agent_callbacks generic_agent_callbacks = { + .type = "generic", + .init = cc_generic_agent_init, + .start_offer_timer = cc_generic_agent_start_offer_timer, + .stop_offer_timer = cc_generic_agent_stop_offer_timer, + .ack = cc_generic_agent_ack, + .status_request = cc_generic_agent_status_request, + .stop_ringing = cc_generic_agent_stop_ringing, + .start_monitoring = cc_generic_agent_start_monitoring, + .callee_available = cc_generic_agent_recall, + .destructor = cc_generic_agent_destructor, +}; + +struct cc_generic_agent_pvt { + /*! + * Subscription to device state + * + * Used in the CC_CALLER_BUSY state. The + * generic agent will subscribe to the + * device state of the caller in order to + * determine when we may move on + */ + struct ast_event_sub *sub; + /*! + * Scheduler id of offer timer. + */ + int offer_timer_id; + /*! + * Caller ID number + * + * When we re-call the caller, we need + * to provide this information to + * ast_request_and_dial so that the + * information will be present in the + * call to the callee + */ + char cid_num[AST_CHANNEL_NAME]; + /*! + * Caller ID name + * + * See the description of cid_num. + * The same applies here, except this + * is the caller's name. + */ + char cid_name[AST_CHANNEL_NAME]; + /*! + * Extension dialed + * + * The original extension dialed. This is used + * so that when performing a recall, we can + * call the proper extension. + */ + char exten[AST_CHANNEL_NAME]; + /*! + * Context dialed + * + * The original context dialed. This is used + * so that when performaing a recall, we can + * call into the proper context + */ + char context[AST_CHANNEL_NAME]; +}; + +static int cc_generic_agent_init(struct ast_cc_agent *agent, struct ast_channel *chan) +{ + struct cc_generic_agent_pvt *generic_pvt = ast_calloc(1, sizeof(*generic_pvt)); + + if (!generic_pvt) { + return -1; + } + + generic_pvt->offer_timer_id = -1; + ast_copy_string(generic_pvt->cid_num, chan->cid.cid_num, sizeof(generic_pvt->cid_num)); + ast_copy_string(generic_pvt->cid_name, chan->cid.cid_name, sizeof(generic_pvt->cid_name)); + ast_copy_string(generic_pvt->exten, S_OR(chan->macroexten, chan->exten), sizeof(generic_pvt->exten)); + ast_copy_string(generic_pvt->context, S_OR(chan->macrocontext, chan->context), sizeof(generic_pvt->context)); + agent->private_data = generic_pvt; + ast_set_flag(agent, AST_CC_AGENT_SKIP_OFFER); + return 0; +} + +static int offer_timer_expire(const void *data) +{ + const struct ast_cc_agent *agent = data; + struct cc_generic_agent_pvt *agent_pvt = agent->private_data; + ast_log_dynamic_level(cc_logger_level, "Core %d: Queuing change request because offer timer has expired.\n", + agent->core_id); + agent_pvt->offer_timer_id = -1; + ast_cc_failed(agent->core_id, "Generic agent %s offer timer expired", agent->device_name); + cc_unref((struct ast_cc_agent *)agent, "Remove scheduler's reference to the agent"); + return 0; +} + +static int cc_generic_agent_start_offer_timer(struct ast_cc_agent *agent) +{ + int when; + int sched_id; + struct cc_generic_agent_pvt *generic_pvt = agent->private_data; + + ast_assert(cc_sched_thread != NULL); + ast_assert(agent->cc_params != NULL); + + when = ast_get_cc_offer_timer(agent->cc_params) * 1000; + ast_log_dynamic_level(cc_logger_level, "Core %d: About to schedule offer timer expiration for %d ms\n", + agent->core_id, when); + if ((sched_id = ast_sched_thread_add(cc_sched_thread, when, offer_timer_expire, cc_ref(agent, "Give scheduler an agent ref"))) == -1) { + return -1; + } + generic_pvt->offer_timer_id = sched_id; + return 0; +} + +static int cc_generic_agent_stop_offer_timer(struct ast_cc_agent *agent) +{ + struct cc_generic_agent_pvt *generic_pvt = agent->private_data; + + if (generic_pvt->offer_timer_id != -1) { + if (!ast_sched_thread_del(cc_sched_thread, generic_pvt->offer_timer_id)) { + cc_unref(agent, "Remove scheduler's reference to the agent"); + } + generic_pvt->offer_timer_id = -1; + } + return 0; +} + +static void cc_generic_agent_ack(struct ast_cc_agent *agent) +{ + /* The generic agent doesn't have to do anything special to + * acknowledge a CC request. Just return. + */ + return; +} + +static int cc_generic_agent_status_request(struct ast_cc_agent *agent) +{ + ast_cc_agent_status_response(agent->core_id, ast_device_state(agent->device_name)); + return 0; +} + +static int cc_generic_agent_stop_ringing(struct ast_cc_agent *agent) +{ + struct ast_channel *recall_chan = ast_channel_get_by_name_prefix(agent->device_name, strlen(agent->device_name)); + + if (!recall_chan) { + return 0; + } + + ast_softhangup(recall_chan, AST_SOFTHANGUP_EXPLICIT); + return 0; +} + +static int generic_agent_devstate_unsubscribe(void *data) +{ + struct ast_cc_agent *agent = data; + struct cc_generic_agent_pvt *generic_pvt = agent->private_data; + + if (generic_pvt->sub != NULL) { + generic_pvt->sub = ast_event_unsubscribe(generic_pvt->sub); + } + cc_unref(agent, "Done unsubscribing from devstate"); + return 0; +} + +static void generic_agent_devstate_cb(const struct ast_event *event, void *userdata) +{ + struct ast_cc_agent *agent = userdata; + + /* We can't unsubscribe from device state events here because it causes a deadlock */ + if (ast_taskprocessor_push(cc_core_taskprocessor, generic_agent_devstate_unsubscribe, + cc_ref(agent, "ref agent for device state unsubscription"))) { + cc_unref(agent, "Unref agent unsubscribing from devstate failed"); + } + ast_cc_agent_caller_available(agent->core_id, "%s is no longer busy", agent->device_name); +} + +static int cc_generic_agent_start_monitoring(struct ast_cc_agent *agent) +{ + struct cc_generic_agent_pvt *generic_pvt = agent->private_data; + struct ast_str *str = ast_str_alloca(128); + + ast_assert(generic_pvt->sub == NULL); + ast_str_set(&str, 0, "Starting to monitor %s device state since it is busy\n", agent->device_name); + + if (!(generic_pvt->sub = ast_event_subscribe( + AST_EVENT_DEVICE_STATE, generic_agent_devstate_cb, ast_str_buffer(str), agent, + AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, agent->device_name, + AST_EVENT_IE_STATE, AST_EVENT_IE_PLTYPE_UINT, AST_DEVICE_NOT_INUSE, + AST_EVENT_IE_END))) { + return -1; + } + return 0; +} + +static void *generic_recall(void *data) +{ + struct ast_cc_agent *agent = data; + struct cc_generic_agent_pvt *generic_pvt = agent->private_data; + const char *interface = S_OR(ast_get_cc_agent_dialstring(agent->cc_params), ast_strdupa(agent->device_name)); + const char *tech; + char *target; + int reason; + struct ast_channel *chan; + const char *callback_macro = ast_get_cc_callback_macro(agent->cc_params); + unsigned int recall_timer = ast_get_cc_recall_timer(agent->cc_params) * 1000; + + tech = interface; + if ((target = strchr(interface, '/'))) { + *target++ = '\0'; + } + if (!(chan = ast_request_and_dial(tech, AST_FORMAT_SLINEAR, NULL, target, recall_timer, &reason, generic_pvt->cid_num, generic_pvt->cid_name))) { + /* Hmm, no channel. Sucks for you, bud. + */ + ast_log_dynamic_level(cc_logger_level, "Core %d: Failed to call back %s for reason %d\n", + agent->core_id, agent->device_name, reason); + ast_cc_failed(agent->core_id, "Failed to call back device %s/%s", tech, target); + return NULL; + } + if (!ast_strlen_zero(callback_macro)) { + ast_log_dynamic_level(cc_logger_level, "Core %d: There's a callback macro configured for agent %s\n", + agent->core_id, agent->device_name); + if (ast_app_run_macro(NULL, chan, callback_macro, NULL)) { + ast_cc_failed(agent->core_id, "Callback macro to %s failed. Maybe a hangup?", agent->device_name); + ast_hangup(chan); + return NULL; + } + } + /* We have a channel. It's time now to set up the datastore of recalled CC interfaces. + * This will be a common task for all recall functions. If it were possible, I'd have + * the core do it automatically, but alas I cannot. Instead, I will provide a public + * function to do so. + */ + ast_setup_cc_recall_datastore(chan, agent->core_id); + ast_cc_agent_set_interfaces_chanvar(chan); + + ast_copy_string(chan->exten, generic_pvt->exten, sizeof(chan->exten)); + ast_copy_string(chan->context, generic_pvt->context, sizeof(chan->context)); + chan->priority = 1; + ast_cc_agent_recalling(agent->core_id, "Generic agent %s is recalling", agent->device_name); + ast_pbx_start(chan); + return NULL; +} + +static int cc_generic_agent_recall(struct ast_cc_agent *agent) +{ + pthread_t clotho; + enum ast_device_state current_state = ast_device_state(agent->device_name); + + if (current_state != AST_DEVICE_NOT_INUSE && current_state != AST_DEVICE_UNKNOWN) { + /* We can't try to contact the device right now because he's not available + * Let the core know he's busy. + */ + ast_cc_agent_caller_busy(agent->core_id, "Generic agent caller %s is busy", agent->device_name); + return 0; + } + ast_pthread_create_detached_background(&clotho, NULL, generic_recall, agent); + return 0; +} + +static void cc_generic_agent_destructor(struct ast_cc_agent *agent) +{ + struct cc_generic_agent_pvt *agent_pvt = agent->private_data; + + if (!agent_pvt) { + /* The agent constructor probably failed. */ + return; + } + + cc_generic_agent_stop_offer_timer(agent); + if (agent_pvt->sub) { + agent_pvt->sub = ast_event_unsubscribe(agent_pvt->sub); + } + + ast_free(agent_pvt); +} + +static void cc_core_instance_destructor(void *data) +{ + struct cc_core_instance *core_instance = data; + ast_log_dynamic_level(cc_logger_level, "Core %d: Destroying core instance\n", core_instance->core_id); + if (core_instance->agent) { + cc_unref(core_instance->agent, "Core instance is done with the agent now"); + } + if (core_instance->monitors) { + core_instance->monitors = cc_unref(core_instance->monitors, "Core instance is done with interface list"); + } +} + +static struct cc_core_instance *cc_core_init_instance(struct ast_channel *caller_chan, + struct cc_monitor_tree *called_tree, const int core_id, struct cc_control_payload *cc_data) +{ + char caller[AST_CHANNEL_NAME]; + struct cc_core_instance *core_instance; + struct ast_cc_config_params *cc_params; + long agent_count; + int recall_core_id; + + ast_channel_get_device_name(caller_chan, caller, sizeof(caller)); + cc_params = ast_channel_get_cc_config_params(caller_chan); + if (!cc_params) { + ast_log_dynamic_level(cc_logger_level, "Could not get CC parameters for %s\n", + caller); + return NULL; + } + /* First, we need to kill off other pending CC offers from caller. If the caller is going + * to request a CC service, it may only be for the latest call he made. + */ + if (ast_get_cc_agent_policy(cc_params) == AST_CC_AGENT_GENERIC) { + kill_duplicate_offers(caller); + } + + ast_cc_is_recall(caller_chan, &recall_core_id, NULL); + agent_count = count_agents(caller, recall_core_id); + if (agent_count >= ast_get_cc_max_agents(cc_params)) { + ast_log_dynamic_level(cc_logger_level, "Caller %s already has the maximum number of agents configured\n", caller); + return NULL; + } + + /* Generic agents can only have a single outstanding CC request per caller. */ + if (agent_count > 0 && ast_get_cc_agent_policy(cc_params) == AST_CC_AGENT_GENERIC) { + ast_log_dynamic_level(cc_logger_level, "Generic agents can only have a single outstanding request\n"); + return NULL; + } + + /* Next, we need to create the core instance for this call */ + if (!(core_instance = ao2_t_alloc(sizeof(*core_instance), cc_core_instance_destructor, "Creating core instance for CC"))) { + return NULL; + } + + core_instance->core_id = core_id; + if (!(core_instance->agent = cc_agent_init(caller_chan, caller, core_instance->core_id, called_tree))) { + cc_unref(core_instance, "Couldn't allocate agent, unref core_instance"); + return NULL; + } + + core_instance->monitors = cc_ref(called_tree, "Core instance getting ref to monitor tree"); + + ao2_t_link(cc_core_instances, core_instance, "Link core instance into container"); + + return core_instance; +} + +struct cc_state_change_args { + enum cc_state state; + int core_id; + char debug[1]; +}; + +static int is_state_change_valid(enum cc_state current_state, const enum cc_state new_state, struct ast_cc_agent *agent) +{ + int is_valid = 0; + switch (new_state) { + case CC_AVAILABLE: + ast_log_dynamic_level(cc_logger_level, "Core %d: Asked to change to state %d? That should never happen.\n", + agent->core_id, new_state); + break; + case CC_CALLER_OFFERED: + if (current_state == CC_AVAILABLE) { + is_valid = 1; + } + break; + case CC_CALLER_REQUESTED: + if (current_state == CC_CALLER_OFFERED || + (current_state == CC_AVAILABLE && ast_test_flag(agent, AST_CC_AGENT_SKIP_OFFER))) { + is_valid = 1; + } + break; + case CC_ACTIVE: + if (current_state == CC_CALLER_REQUESTED || current_state == CC_CALLER_BUSY) { + is_valid = 1; + } + break; + case CC_CALLEE_READY: + if (current_state == CC_ACTIVE) { + is_valid = 1; + } + break; + case CC_CALLER_BUSY: + if (current_state == CC_CALLEE_READY) { + is_valid = 1; + } + break; + case CC_RECALLING: + if (current_state == CC_CALLEE_READY) { + is_valid = 1; + } + break; + case CC_COMPLETE: + if (current_state == CC_RECALLING) { + is_valid = 1; + } + break; + case CC_FAILED: + is_valid = 1; + break; + default: + ast_log_dynamic_level(cc_logger_level, "Core %d: Asked to change to unknown state %d\n", + agent->core_id, new_state); + break; + } + + return is_valid; +} + +static int cc_available(struct cc_core_instance *core_instance, struct cc_state_change_args *args, enum cc_state previous_state) +{ + /* This should never happen... */ + ast_log(LOG_WARNING, "Someone requested to change to CC_AVAILABLE? Ignoring.\n"); + return -1; +} + +static int cc_caller_offered(struct cc_core_instance *core_instance, struct cc_state_change_args *args, enum cc_state previous_state) +{ + if (core_instance->agent->callbacks->start_offer_timer(core_instance->agent)) { + ast_cc_failed(core_instance->core_id, "Failed to start the offer timer for %s\n", + core_instance->agent->device_name); + return -1; + } + manager_event(EVENT_FLAG_CC, "CCOfferTimerStart", + "CoreID: %d\r\n" + "Caller: %s\r\n" + "Expires: %u\r\n", + core_instance->core_id, core_instance->agent->device_name, core_instance->agent->cc_params->cc_offer_timer); + ast_log_dynamic_level(cc_logger_level, "Core %d: Started the offer timer for the agent %s!\n", + core_instance->core_id, core_instance->agent->device_name); + return 0; +} + +/*! + * \brief check if the core instance has any device monitors + * + * In any case where we end up removing a device monitor from the + * list of device monitors, it is important to see what the state + * of the list is afterwards. If we find that we only have extension + * monitors left, then no devices are actually being monitored. + * In such a case, we need to declare that CC has failed for this + * call. This function helps those cases to determine if they should + * declare failure. + * + * \param core_instance The core instance we are checking for the existence + * of device monitors + * \retval 0 No device monitors exist on this core_instance + * \retval 1 There is still at least 1 device monitor remaining + */ +static int has_device_monitors(struct cc_core_instance *core_instance) +{ + struct ast_cc_monitor *iter; + int res = 0; + + AST_LIST_TRAVERSE(core_instance->monitors, iter, next) { + if (iter->interface->monitor_class == AST_CC_DEVICE_MONITOR) { + res = 1; + break; + } + } + + return res; +} + +static void request_cc(struct cc_core_instance *core_instance) +{ + struct ast_cc_monitor *monitor_iter; + AST_LIST_LOCK(core_instance->monitors); + AST_LIST_TRAVERSE_SAFE_BEGIN(core_instance->monitors, monitor_iter, next) { + if (monitor_iter->interface->monitor_class == AST_CC_DEVICE_MONITOR) { + if (monitor_iter->callbacks->request_cc(monitor_iter, &monitor_iter->available_timer_id)) { + AST_LIST_REMOVE_CURRENT(next); + cc_extension_monitor_change_is_valid(core_instance, monitor_iter->parent_id, + monitor_iter->interface->device_name, 1); + cc_unref(monitor_iter, "request_cc failed. Unref list's reference to monitor"); + } else { + manager_event(EVENT_FLAG_CC, "CCRequested", + "CoreID: %d\r\n" + "Caller: %s\r\n" + "Callee: %s\r\n", + core_instance->core_id, core_instance->agent->device_name, monitor_iter->interface->device_name); + } + } + } + AST_LIST_TRAVERSE_SAFE_END; + + if (!has_device_monitors(core_instance)) { + ast_cc_failed(core_instance->core_id, "All device monitors failed to request CC"); + } + AST_LIST_UNLOCK(core_instance->monitors); +} + +static int cc_caller_requested(struct cc_core_instance *core_instance, struct cc_state_change_args *args, enum cc_state previous_state) +{ + if (!ast_cc_request_is_within_limits()) { + ast_log(LOG_WARNING, "Cannot request CC since there is no more room for requests\n"); + ast_cc_failed(core_instance->core_id, "Too many requests in the system"); + return -1; + } + core_instance->agent->callbacks->stop_offer_timer(core_instance->agent); + request_cc(core_instance); + return 0; +} + +static void unsuspend(struct cc_core_instance *core_instance) +{ + struct ast_cc_monitor *monitor_iter; + AST_LIST_LOCK(core_instance->monitors); + AST_LIST_TRAVERSE_SAFE_BEGIN(core_instance->monitors, monitor_iter, next) { + if (monitor_iter->interface->monitor_class == AST_CC_DEVICE_MONITOR) { + if (monitor_iter->callbacks->unsuspend(monitor_iter)) { + AST_LIST_REMOVE_CURRENT(next); + cc_extension_monitor_change_is_valid(core_instance, monitor_iter->parent_id, + monitor_iter->interface->device_name, 1); + cc_unref(monitor_iter, "unsuspend failed. Unref list's reference to monitor"); + } + } + } + AST_LIST_TRAVERSE_SAFE_END; + + if (!has_device_monitors(core_instance)) { + ast_cc_failed(core_instance->core_id, "All device monitors failed to unsuspend CC"); + } + AST_LIST_UNLOCK(core_instance->monitors); +} + +static int cc_active(struct cc_core_instance *core_instance, struct cc_state_change_args *args, enum cc_state previous_state) +{ + /* Either + * 1. Callee accepted CC request, call agent's ack callback. + * 2. Caller became available, call agent's stop_monitoring callback and + * call monitor's unsuspend callback. + */ + if (previous_state == CC_CALLER_REQUESTED) { + core_instance->agent->callbacks->ack(core_instance->agent); + manager_event(EVENT_FLAG_CC, "CCRequestAcknowledged", + "CoreID: %d\r\n" + "Caller: %s\r\n", + core_instance->core_id, core_instance->agent->device_name); + } else if (previous_state == CC_CALLER_BUSY) { + manager_event(EVENT_FLAG_CC, "CCCallerStopMonitoring", + "CoreID: %d\r\n" + "Caller: %s\r\n", + core_instance->core_id, core_instance->agent->device_name); + unsuspend(core_instance); + } + /* Not possible for previous_state to be anything else due to the is_state_change_valid check at the beginning */ + return 0; +} + +static int cc_callee_ready(struct cc_core_instance *core_instance, struct cc_state_change_args *args, enum cc_state previous_state) +{ + core_instance->agent->callbacks->callee_available(core_instance->agent); + return 0; +} + +static void suspend(struct cc_core_instance *core_instance) +{ + struct ast_cc_monitor *monitor_iter; + AST_LIST_LOCK(core_instance->monitors); + AST_LIST_TRAVERSE_SAFE_BEGIN(core_instance->monitors, monitor_iter, next) { + if (monitor_iter->interface->monitor_class == AST_CC_DEVICE_MONITOR) { + if (monitor_iter->callbacks->suspend(monitor_iter)) { + AST_LIST_REMOVE_CURRENT(next); + cc_extension_monitor_change_is_valid(core_instance, monitor_iter->parent_id, + monitor_iter->interface->device_name, 1); + cc_unref(monitor_iter, "suspend failed. Unref list's reference to monitor"); + } + } + } + AST_LIST_TRAVERSE_SAFE_END; + + if (!has_device_monitors(core_instance)) { + ast_cc_failed(core_instance->core_id, "All device monitors failed to suspend CC"); + } + AST_LIST_UNLOCK(core_instance->monitors); +} + +static int cc_caller_busy(struct cc_core_instance *core_instance, struct cc_state_change_args *args, enum cc_state previous_state) +{ + /* Callee was available, but caller was busy, call agent's begin_monitoring callback + * and call monitor's suspend callback. + */ + suspend(core_instance); + core_instance->agent->callbacks->start_monitoring(core_instance->agent); + manager_event(EVENT_FLAG_CC, "CCCallerStartMonitoring", + "CoreID: %d\r\n" + "Caller: %s\r\n", + core_instance->core_id, core_instance->agent->device_name); + return 0; +} + +static void cancel_available_timer(struct cc_core_instance *core_instance) +{ + struct ast_cc_monitor *monitor_iter; + AST_LIST_LOCK(core_instance->monitors); + AST_LIST_TRAVERSE_SAFE_BEGIN(core_instance->monitors, monitor_iter, next) { + if (monitor_iter->interface->monitor_class == AST_CC_DEVICE_MONITOR) { + if (monitor_iter->callbacks->cancel_available_timer(monitor_iter, &monitor_iter->available_timer_id)) { + AST_LIST_REMOVE_CURRENT(next); + cc_extension_monitor_change_is_valid(core_instance, monitor_iter->parent_id, + monitor_iter->interface->device_name, 1); + cc_unref(monitor_iter, "cancel_available_timer failed. Unref list's reference to monitor"); + } + } + } + AST_LIST_TRAVERSE_SAFE_END; + + if (!has_device_monitors(core_instance)) { + ast_cc_failed(core_instance->core_id, "All device monitors failed to cancel their available timers"); + } + AST_LIST_UNLOCK(core_instance->monitors); +} + +static int cc_recalling(struct cc_core_instance *core_instance, struct cc_state_change_args *args, enum cc_state previous_state) +{ + /* Both caller and callee are available, call agent's recall callback + */ + cancel_available_timer(core_instance); + manager_event(EVENT_FLAG_CC, "CCCallerRecalling", + "CoreID: %d\r\n" + "Caller: %s\r\n", + core_instance->core_id, core_instance->agent->device_name); + return 0; +} + +static int cc_complete(struct cc_core_instance *core_instance, struct cc_state_change_args *args, enum cc_state previous_state) +{ + /* Recall has made progress, call agent and monitor destructor functions + */ + manager_event(EVENT_FLAG_CC, "CCRecallComplete", + "CoreID: %d\r\n" + "Caller: %s\r\n", + core_instance->core_id, core_instance->agent->device_name); + ao2_t_unlink(cc_core_instances, core_instance, "Unlink core instance since CC recall has completed"); + return 0; +} + +static int cc_failed(struct cc_core_instance *core_instance, struct cc_state_change_args *args, enum cc_state previous_state) +{ + /* Something along the way failed, call agent and monitor destructor functions + */ + manager_event(EVENT_FLAG_CC, "CCFailure", + "CoreID: %d\r\n" + "Caller: %s\r\n" + "Reason: %s\r\n", + core_instance->core_id, core_instance->agent->device_name, args->debug); + ao2_t_unlink(cc_core_instances, core_instance, "Unlink core instance since CC failed"); + return 0; +} + +static int (* const state_change_funcs [])(struct cc_core_instance *, struct cc_state_change_args *, enum cc_state previous_state) = { + [CC_AVAILABLE] = cc_available, + [CC_CALLER_OFFERED] = cc_caller_offered, + [CC_CALLER_REQUESTED] = cc_caller_requested, + [CC_ACTIVE] = cc_active, + [CC_CALLEE_READY] = cc_callee_ready, + [CC_CALLER_BUSY] = cc_caller_busy, + [CC_RECALLING] = cc_recalling, + [CC_COMPLETE] = cc_complete, + [CC_FAILED] = cc_failed, +}; + +static int cc_do_state_change(void *datap) +{ + struct cc_state_change_args *args = datap; + struct cc_core_instance *core_instance; + enum cc_state previous_state; + int res; + + ast_log_dynamic_level(cc_logger_level, "Core %d: State change to %d requested. Reason: %s\n", + args->core_id, args->state, args->debug); + + if (!(core_instance = find_cc_core_instance(args->core_id))) { + ast_log_dynamic_level(cc_logger_level, "Core %d: Unable to find core instance.\n", args->core_id); + ast_free(args); + return -1; + } + + if (!is_state_change_valid(core_instance->current_state, args->state, core_instance->agent)) { + ast_log_dynamic_level(cc_logger_level, "Core %d: Invalid state change requested. Cannot go from %s to %s\n", + args->core_id, cc_state_to_string(core_instance->current_state), cc_state_to_string(args->state)); + ast_free(args); + cc_unref(core_instance, "Unref core instance from when it was found earlier"); + return -1; + } + + /* We can change to the new state now. */ + previous_state = core_instance->current_state; + core_instance->current_state = args->state; + res = state_change_funcs[core_instance->current_state](core_instance, args, previous_state); + + ast_free(args); + cc_unref(core_instance, "Unref since state change has completed"); /* From ao2_find */ + return res; +} + +static int cc_request_state_change(enum cc_state state, const int core_id, const char *debug, va_list ap) +{ + int res; + int debuglen; + char dummy[1]; + va_list aq; + struct cc_state_change_args *args; + /* This initial call to vsnprintf is simply to find what the + * size of the string needs to be + */ + va_copy(aq, ap); + /* We add 1 to the result since vsnprintf's return does not + * include the terminating null byte + */ + debuglen = vsnprintf(dummy, sizeof(dummy), debug, aq) + 1; + va_end(aq); + + if (!(args = ast_calloc(1, sizeof(*args) + debuglen))) { + return -1; + } + + args->state = state; + args->core_id = core_id; + vsnprintf(args->debug, debuglen, debug, ap); + + res = ast_taskprocessor_push(cc_core_taskprocessor, cc_do_state_change, args); + if (res) { + ast_free(args); + } + return res; +} + +struct cc_recall_ds_data { + int core_id; + char ignore; + char nested; + struct cc_monitor_tree *interface_tree; +}; + +static void *cc_recall_ds_duplicate(void *data) +{ + struct cc_recall_ds_data *old_data = data; + struct cc_recall_ds_data *new_data = ast_calloc(1, sizeof(*new_data)); + + if (!new_data) { + return NULL; + } + new_data->interface_tree = cc_ref(old_data->interface_tree, "Bump refcount of monitor tree for recall datastore duplicate"); + new_data->core_id = old_data->core_id; + new_data->nested = 1; + return new_data; +} + +static void cc_recall_ds_destroy(void *data) +{ + struct cc_recall_ds_data *recall_data = data; + recall_data->interface_tree = cc_unref(recall_data->interface_tree, "Unref recall monitor tree"); + ast_free(recall_data); +} + +static struct ast_datastore_info recall_ds_info = { + .type = "cc_recall", + .duplicate = cc_recall_ds_duplicate, + .destroy = cc_recall_ds_destroy, +}; + +int ast_setup_cc_recall_datastore(struct ast_channel *chan, const int core_id) +{ + struct ast_datastore *recall_datastore = ast_datastore_alloc(&recall_ds_info, NULL); + struct cc_recall_ds_data *recall_data; + struct cc_core_instance *core_instance; + + if (!recall_datastore) { + return -1; + } + + if (!(recall_data = ast_calloc(1, sizeof(*recall_data)))) { + ast_datastore_free(recall_datastore); + return -1; + } + + if (!(core_instance = find_cc_core_instance(core_id))) { + ast_free(recall_data); + ast_datastore_free(recall_datastore); + return -1; + } + + recall_data->interface_tree = cc_ref(core_instance->monitors, + "Bump refcount for monitor tree for recall datastore"); + recall_data->core_id = core_id; + recall_datastore->data = recall_data; + recall_datastore->inheritance = DATASTORE_INHERIT_FOREVER; + ast_channel_lock(chan); + ast_channel_datastore_add(chan, recall_datastore); + ast_channel_unlock(chan); + cc_unref(core_instance, "Recall datastore set up. No need for core_instance ref"); + return 0; +} + +int ast_cc_is_recall(struct ast_channel *chan, int *core_id, const char * const monitor_type) +{ + struct ast_datastore *recall_datastore; + struct cc_recall_ds_data *recall_data; + struct cc_monitor_tree *interface_tree; + char device_name[AST_CHANNEL_NAME]; + struct ast_cc_monitor *device_monitor; + int core_id_candidate; + + ast_assert(core_id != NULL); + + *core_id = -1; + + ast_channel_lock(chan); + if (!(recall_datastore = ast_channel_datastore_find(chan, &recall_ds_info, NULL))) { + /* Obviously not a recall if the datastore isn't present */ + ast_channel_unlock(chan); + return 0; + } + + recall_data = recall_datastore->data; + + if (recall_data->ignore) { + /* Though this is a recall, the call to this particular interface is not part of the + * recall either because this is a call forward or because this is not the first + * invocation of Dial during this call + */ + ast_channel_unlock(chan); + return 0; + } + + if (!recall_data->nested) { + /* If the nested flag is not set, then this means that + * the channel passed to this function is the caller making + * the recall. This means that we shouldn't look through + * the monitor tree for the channel because it shouldn't be + * there. However, this is a recall though, so return true. + */ + *core_id = recall_data->core_id; + ast_channel_unlock(chan); + return 1; + } + + if (ast_strlen_zero(monitor_type)) { + /* If someone passed a NULL or empty monitor type, then it is clear + * the channel they passed in was an incoming channel, and so searching + * the list of dialed interfaces is not going to be helpful. Just return + * false immediately. + */ + ast_channel_unlock(chan); + return 0; + } + + interface_tree = recall_data->interface_tree; + ast_channel_get_device_name(chan, device_name, sizeof(device_name)); + /* We grab the value of the recall_data->core_id so that we + * can unlock the channel before we start looking through the + * interface list. That way we don't have to worry about a possible + * clash between the channel lock and the monitor tree lock. + */ + core_id_candidate = recall_data->core_id; + ast_channel_unlock(chan); + + /* + * Now we need to find out if the channel device name + * is in the list of interfaces in the called tree. + */ + AST_LIST_LOCK(interface_tree); + AST_LIST_TRAVERSE(interface_tree, device_monitor, next) { + if (!strcmp(device_monitor->interface->device_name, device_name) && + !strcmp(device_monitor->interface->monitor_type, monitor_type)) { + /* BOOM! Device is in the tree! We have a winner! */ + *core_id = core_id_candidate; + AST_LIST_UNLOCK(interface_tree); + return 1; + } + } + AST_LIST_UNLOCK(interface_tree); + return 0; +} + +struct ast_cc_monitor *ast_cc_get_monitor_by_recall_core_id(const int core_id, const char * const device_name) +{ + struct cc_core_instance *core_instance = find_cc_core_instance(core_id); + struct ast_cc_monitor *monitor_iter; + + if (!core_instance) { + return NULL; + } + + AST_LIST_LOCK(core_instance->monitors); + AST_LIST_TRAVERSE(core_instance->monitors, monitor_iter, next) { + if (!strcmp(monitor_iter->interface->device_name, device_name)) { + /* Found a monitor. */ + cc_ref(monitor_iter, "Hand the requester of the monitor a reference"); + break; + } + } + AST_LIST_UNLOCK(core_instance->monitors); + cc_unref(core_instance, "Done with core instance ref in ast_cc_get_monitor_by_recall_core_id"); + return monitor_iter; +} + +/*! + * \internal + * \brief uniquely append a dialstring to our CC_INTERFACES chanvar string. + * + * We will only append a string if it has not already appeared in our channel + * variable earlier. We ensure that we don't erroneously match substrings by + * adding an ampersand to the end of our potential dialstring and searching for + * it plus the ampersand in our variable. + * + * It's important to note that once we have built the full CC_INTERFACES string, + * there will be an extra ampersand at the end which must be stripped off by + * the caller of this function. + * + * \param str An ast_str holding what we will add to CC_INTERFACES + * \param dialstring A new dialstring to add + * \retval void + */ +static void cc_unique_append(struct ast_str *str, const char * const dialstring) +{ + char dialstring_search[AST_CHANNEL_NAME]; + + snprintf(dialstring_search, sizeof(dialstring_search), "%s%c", dialstring, '&'); + if (strstr(ast_str_buffer(str), dialstring_search)) { + return; + } + ast_str_append(&str, 0, "%s", dialstring_search); +} + +/*! + * \internal + * \brief Build the CC_INTERFACES channel variable + * + * The method used is to traverse the child dialstrings in the + * passed-in extension monitor, adding any that have the is_valid + * flag set. Then, traverse the monitors, finding all children + * of the starting extension monitor and adding their dialstrings + * as well. + * + * \param starting_point The extension monitor that is the parent to all + * monitors whose dialstrings should be added to CC_INTERFACES + * \param str Where we will store CC_INTERFACES + * \retval void + */ +static void build_cc_interfaces_chanvar(struct ast_cc_monitor *starting_point, struct ast_str *str) +{ + struct extension_monitor_pvt *extension_pvt; + struct extension_child_dialstring *child_dialstring; + struct ast_cc_monitor *monitor_iter = starting_point; + int top_level_id = starting_point->id; + + /* First we need to take all of the is_valid child_dialstrings from + * the extension monitor we found and add them to the CC_INTERFACES + * chanvar + */ + extension_pvt = starting_point->private_data; + AST_LIST_TRAVERSE(&extension_pvt->child_dialstrings, child_dialstring, next) { + if (child_dialstring->is_valid) { + cc_unique_append(str, child_dialstring->original_dialstring); + } + } + + /* And now we get the dialstrings from each of the device monitors */ + while ((monitor_iter = AST_LIST_NEXT(monitor_iter, next))) { + if (monitor_iter->parent_id == top_level_id) { + cc_unique_append(str, monitor_iter->dialstring); + } + } + + /* str will have an extra '&' tacked onto the end of it, so we need + * to get rid of that. + */ + ast_str_truncate(str, ast_str_strlen(str) - 1); +} + +int ast_cc_agent_set_interfaces_chanvar(struct ast_channel *chan) +{ + struct ast_datastore *recall_datastore; + struct cc_monitor_tree *interface_tree; + struct ast_cc_monitor *monitor; + struct cc_recall_ds_data *recall_data; + struct ast_str *str = ast_str_create(64); + int core_id; + + if (!str) { + return -1; + } + + ast_channel_lock(chan); + if (!(recall_datastore = ast_channel_datastore_find(chan, &recall_ds_info, NULL))) { + ast_channel_unlock(chan); + ast_free(str); + return -1; + } + recall_data = recall_datastore->data; + interface_tree = recall_data->interface_tree; + core_id = recall_data->core_id; + ast_channel_unlock(chan); + + AST_LIST_LOCK(interface_tree); + monitor = AST_LIST_FIRST(interface_tree); + build_cc_interfaces_chanvar(monitor, str); + AST_LIST_UNLOCK(interface_tree); + + pbx_builtin_setvar_helper(chan, "CC_INTERFACES", ast_str_buffer(str)); + ast_log_dynamic_level(cc_logger_level, "Core %d: CC_INTERFACES set to %s\n", + core_id, ast_str_buffer(str)); + + ast_free(str); + return 0; +} + +int ast_set_cc_interfaces_chanvar(struct ast_channel *chan, const char * const extension) +{ + struct ast_datastore *recall_datastore; + struct cc_monitor_tree *interface_tree; + struct ast_cc_monitor *monitor_iter; + struct cc_recall_ds_data *recall_data; + struct ast_str *str = ast_str_create(64); + int core_id; + + if (!str) { + return -1; + } + + ast_channel_lock(chan); + if (!(recall_datastore = ast_channel_datastore_find(chan, &recall_ds_info, NULL))) { + ast_channel_unlock(chan); + ast_free(str); + return -1; + } + recall_data = recall_datastore->data; + interface_tree = recall_data->interface_tree; + core_id = recall_data->core_id; + ast_channel_unlock(chan); + + AST_LIST_LOCK(interface_tree); + AST_LIST_TRAVERSE(interface_tree, monitor_iter, next) { + if (!strcmp(monitor_iter->interface->device_name, extension)) { + break; + } + } + + if (!monitor_iter) { + /* We couldn't find this extension. This may be because + * we have been directed into an unexpected extension because + * the admin has changed a CC_INTERFACES variable at some point. + */ + AST_LIST_UNLOCK(interface_tree); + ast_free(str); + return -1; + } + + build_cc_interfaces_chanvar(monitor_iter, str); + AST_LIST_UNLOCK(interface_tree); + + pbx_builtin_setvar_helper(chan, "CC_INTERFACES", ast_str_buffer(str)); + ast_log_dynamic_level(cc_logger_level, "Core %d: CC_INTERFACES set to %s\n", + core_id, ast_str_buffer(str)); + + ast_free(str); + return 0; +} + +void ast_ignore_cc(struct ast_channel *chan) +{ + struct ast_datastore *cc_datastore; + struct ast_datastore *cc_recall_datastore; + struct dialed_cc_interfaces *cc_interfaces; + struct cc_recall_ds_data *recall_cc_data; + + ast_channel_lock(chan); + if ((cc_datastore = ast_channel_datastore_find(chan, &dialed_cc_interfaces_info, NULL))) { + cc_interfaces = cc_datastore->data; + cc_interfaces->ignore = 1; + } + + if ((cc_recall_datastore = ast_channel_datastore_find(chan, &recall_ds_info, NULL))) { + recall_cc_data = cc_recall_datastore->data; + recall_cc_data->ignore = 1; + } + ast_channel_unlock(chan); +} + +static __attribute__((format(printf, 2, 3))) int cc_offer(const int core_id, const char * const debug, ...) +{ + va_list ap; + int res; + + va_start(ap, debug); + res = cc_request_state_change(CC_CALLER_OFFERED, core_id, debug, ap); + va_end(ap); + return res; +} + +int ast_cc_offer(struct ast_channel *caller_chan) +{ + int core_id; + int res = -1; + struct ast_datastore *datastore; + struct dialed_cc_interfaces *cc_interfaces; + char cc_is_offerable; + + ast_channel_lock(caller_chan); + if (!(datastore = ast_channel_datastore_find(caller_chan, &dialed_cc_interfaces_info, NULL))) { + ast_channel_unlock(caller_chan); + return res; + } + + cc_interfaces = datastore->data; + cc_is_offerable = cc_interfaces->is_original_caller; + core_id = cc_interfaces->core_id; + ast_channel_unlock(caller_chan); + + if (cc_is_offerable) { + res = cc_offer(core_id, "CC offered to caller %s", caller_chan->name); + } + return res; +} + +int ast_cc_agent_accept_request(int core_id, const char * const debug, ...) +{ + va_list ap; + int res; + + va_start(ap, debug); + res = cc_request_state_change(CC_CALLER_REQUESTED, core_id, debug, ap); + va_end(ap); + return res; +} + +int ast_cc_monitor_request_acked(int core_id, const char * const debug, ...) +{ + va_list ap; + int res; + + va_start(ap, debug); + res = cc_request_state_change(CC_ACTIVE, core_id, debug, ap); + va_end(ap); + return res; +} + +int ast_cc_monitor_callee_available(const int core_id, const char * const debug, ...) +{ + va_list ap; + int res; + + va_start(ap, debug); + res = cc_request_state_change(CC_CALLEE_READY, core_id, debug, ap); + va_end(ap); + return res; +} + +int ast_cc_agent_caller_busy(int core_id, const char * debug, ...) +{ + va_list ap; + int res; + + va_start(ap, debug); + res = cc_request_state_change(CC_CALLER_BUSY, core_id, debug, ap); + va_end(ap); + return res; +} + +int ast_cc_agent_caller_available(int core_id, const char * const debug, ...) +{ + va_list ap; + int res; + + va_start(ap, debug); + res = cc_request_state_change(CC_ACTIVE, core_id, debug, ap); + va_end(ap); + return res; +} + +int ast_cc_agent_recalling(int core_id, const char * const debug, ...) +{ + va_list ap; + int res; + + va_start(ap, debug); + res = cc_request_state_change(CC_RECALLING, core_id, debug, ap); + va_end(ap); + return res; +} + +int ast_cc_completed(struct ast_channel *chan, const char * const debug, ...) +{ + struct ast_datastore *recall_datastore; + struct cc_recall_ds_data *recall_data; + int core_id; + va_list ap; + int res; + + ast_channel_lock(chan); + if (!(recall_datastore = ast_channel_datastore_find(chan, &recall_ds_info, NULL))) { + /* Silly! Why did you call this function if there's no recall DS? */ + ast_channel_unlock(chan); + return -1; + } + recall_data = recall_datastore->data; + if (recall_data->nested || recall_data->ignore) { + /* If this is being called from a nested Dial, it is too + * early to determine if the recall has actually completed. + * The outermost dial is the only one with the authority to + * declare the recall to be complete. + * + * Similarly, if this function has been called when the + * recall has progressed beyond the first dial, this is not + * a legitimate time to declare the recall to be done. In fact, + * that should have been done already. + */ + ast_channel_unlock(chan); + return -1; + } + core_id = recall_data->core_id; + ast_channel_unlock(chan); + va_start(ap, debug); + res = cc_request_state_change(CC_COMPLETE, core_id, debug, ap); + va_end(ap); + return res; +} + +int ast_cc_failed(int core_id, const char * const debug, ...) +{ + va_list ap; + int res; + + va_start(ap, debug); + res = cc_request_state_change(CC_FAILED, core_id, debug, ap); + va_end(ap); + return res; +} + +struct ast_cc_monitor_failure_data { + const char *device_name; + char *debug; + int core_id; +}; + +static int cc_monitor_failed(void *data) +{ + struct ast_cc_monitor_failure_data *failure_data = data; + struct cc_core_instance *core_instance; + struct ast_cc_monitor *monitor_iter; + + core_instance = find_cc_core_instance(failure_data->core_id); + if (!core_instance) { + /* Core instance no longer exists or invalid core_id. */ + ast_log_dynamic_level(cc_logger_level, + "Core %d: Could not find core instance for device %s '%s'\n", + failure_data->core_id, failure_data->device_name, failure_data->debug); + ast_free((char *) failure_data->device_name); + ast_free((char *) failure_data->debug); + ast_free(failure_data); + return -1; + } + + AST_LIST_LOCK(core_instance->monitors); + AST_LIST_TRAVERSE_SAFE_BEGIN(core_instance->monitors, monitor_iter, next) { + if (monitor_iter->interface->monitor_class == AST_CC_DEVICE_MONITOR) { + if (!strcmp(monitor_iter->interface->device_name, failure_data->device_name)) { + AST_LIST_REMOVE_CURRENT(next); + cc_extension_monitor_change_is_valid(core_instance, monitor_iter->parent_id, + monitor_iter->interface->device_name, 1); + monitor_iter->callbacks->cancel_available_timer(monitor_iter, &monitor_iter->available_timer_id); + manager_event(EVENT_FLAG_CC, "CCMonitorFailed", + "CoreID: %d\r\n" + "Callee: %s\r\n", + monitor_iter->core_id, monitor_iter->interface->device_name); + cc_unref(monitor_iter, "Monitor reported failure. Unref list's reference."); + } + } + } + AST_LIST_TRAVERSE_SAFE_END; + + if (!has_device_monitors(core_instance)) { + ast_cc_failed(core_instance->core_id, "All monitors have failed\n"); + } + AST_LIST_UNLOCK(core_instance->monitors); + cc_unref(core_instance, "Finished with core_instance in cc_monitor_failed\n"); + + ast_free((char *) failure_data->device_name); + ast_free((char *) failure_data->debug); + ast_free(failure_data); + return 0; +} + +int ast_cc_monitor_failed(int core_id, const char *const monitor_name, const char * const debug, ...) +{ + struct ast_cc_monitor_failure_data *failure_data; + int res; + va_list ap; + + if (!(failure_data = ast_calloc(1, sizeof(*failure_data)))) { + return -1; + } + + if (!(failure_data->device_name = ast_strdup(monitor_name))) { + ast_free(failure_data); + return -1; + } + + va_start(ap, debug); + if (ast_vasprintf(&failure_data->debug, debug, ap) == -1) { + va_end(ap); + ast_free((char *)failure_data->device_name); + ast_free(failure_data); + return -1; + } + va_end(ap); + + failure_data->core_id = core_id; + + res = ast_taskprocessor_push(cc_core_taskprocessor, cc_monitor_failed, failure_data); + if (res) { + ast_free((char *)failure_data->device_name); + ast_free((char *)failure_data->debug); + ast_free(failure_data); + } + return res; +} + +static int cc_status_request(void *data) +{ + struct cc_core_instance *core_instance= data; + int res; + + res = core_instance->agent->callbacks->status_request(core_instance->agent); + cc_unref(core_instance, "Status request finished. Unref core instance"); + return res; +} + +int ast_cc_monitor_status_request(int core_id) +{ + int res; + struct cc_core_instance *core_instance = find_cc_core_instance(core_id); + + if (!core_instance) { + return -1; + } + + res = ast_taskprocessor_push(cc_core_taskprocessor, cc_status_request, core_instance); + if (res) { + cc_unref(core_instance, "Unref core instance. ast_taskprocessor_push failed"); + } + return res; +} + +static int cc_stop_ringing(void *data) +{ + struct cc_core_instance *core_instance = data; + int res = 0; + + if (core_instance->agent->callbacks->stop_ringing) { + res = core_instance->agent->callbacks->stop_ringing(core_instance->agent); + } + /* If an agent is being asked to stop ringing, then he needs to be prepared if for + * whatever reason he needs to be called back again. The proper state to be in to + * detect such a circumstance is the CC_ACTIVE state. + * + * We get to this state using the slightly unintuitive method of calling + * ast_cc_monitor_request_acked because it gets us to the proper state. + */ + ast_cc_monitor_request_acked(core_instance->core_id, "Agent %s asked to stop ringing. Be prepared to be recalled again.", + core_instance->agent->device_name); + cc_unref(core_instance, "Stop ringing finished. Unref core_instance"); + return res; +} + +int ast_cc_monitor_stop_ringing(int core_id) +{ + int res; + struct cc_core_instance *core_instance = find_cc_core_instance(core_id); + + if (!core_instance) { + return -1; + } + + res = ast_taskprocessor_push(cc_core_taskprocessor, cc_stop_ringing, core_instance); + if (res) { + cc_unref(core_instance, "Unref core instance. ast_taskprocessor_push failed"); + } + return res; +} + +static int cc_party_b_free(void *data) +{ + struct cc_core_instance *core_instance = data; + int res = 0; + + if (core_instance->agent->callbacks->party_b_free) { + res = core_instance->agent->callbacks->party_b_free(core_instance->agent); + } + cc_unref(core_instance, "Party B free finished. Unref core_instance"); + return res; +} + +int ast_cc_monitor_party_b_free(int core_id) +{ + int res; + struct cc_core_instance *core_instance = find_cc_core_instance(core_id); + + if (!core_instance) { + return -1; + } + + res = ast_taskprocessor_push(cc_core_taskprocessor, cc_party_b_free, core_instance); + if (res) { + cc_unref(core_instance, "Unref core instance. ast_taskprocessor_push failed"); + } + return res; +} + +struct cc_status_response_args { + struct cc_core_instance *core_instance; + enum ast_device_state devstate; +}; + +static int cc_status_response(void *data) +{ + struct cc_status_response_args *args = data; + struct cc_core_instance *core_instance = args->core_instance; + struct ast_cc_monitor *monitor_iter; + enum ast_device_state devstate = args->devstate; + + ast_free(args); + + AST_LIST_LOCK(core_instance->monitors); + AST_LIST_TRAVERSE(core_instance->monitors, monitor_iter, next) { + if (monitor_iter->interface->monitor_class == AST_CC_DEVICE_MONITOR && + monitor_iter->callbacks->status_response) { + monitor_iter->callbacks->status_response(monitor_iter, devstate); + } + } + AST_LIST_UNLOCK(core_instance->monitors); + cc_unref(core_instance, "Status response finished. Unref core instance"); + return 0; +} + +int ast_cc_agent_status_response(int core_id, enum ast_device_state devstate) +{ + struct cc_status_response_args *args; + struct cc_core_instance *core_instance; + int res; + + args = ast_calloc(1, sizeof(*args)); + if (!args) { + return -1; + } + + core_instance = find_cc_core_instance(core_id); + if (!core_instance) { + ast_free(args); + return -1; + } + + args->core_instance = core_instance; + args->devstate = devstate; + + res = ast_taskprocessor_push(cc_core_taskprocessor, cc_status_response, args); + if (res) { + cc_unref(core_instance, "Unref core instance. ast_taskprocessor_push failed"); + ast_free(args); + } + return res; +} + +static int cc_build_payload(struct ast_channel *chan, struct ast_cc_config_params *cc_params, + const char *monitor_type, const char * const device_name, const char * dialstring, + enum ast_cc_service_type service, void *private_data, struct cc_control_payload *payload) +{ + struct ast_datastore *datastore; + struct dialed_cc_interfaces *cc_interfaces; + int dial_parent_id; + + ast_channel_lock(chan); + datastore = ast_channel_datastore_find(chan, &dialed_cc_interfaces_info, NULL); + if (!datastore) { + ast_channel_unlock(chan); + return -1; + } + cc_interfaces = datastore->data; + dial_parent_id = cc_interfaces->dial_parent_id; + ast_channel_unlock(chan); + + payload->monitor_type = monitor_type; + payload->private_data = private_data; + payload->service = service; + ast_cc_copy_config_params(&payload->config_params, cc_params); + payload->parent_interface_id = dial_parent_id; + ast_copy_string(payload->device_name, device_name, sizeof(payload->device_name)); + ast_copy_string(payload->dialstring, dialstring, sizeof(payload->dialstring)); + return 0; +} + +int ast_queue_cc_frame(struct ast_channel *chan, const char *monitor_type, + const char * const dialstring, enum ast_cc_service_type service, void *private_data) +{ + struct ast_frame frame = {0,}; + char device_name[AST_CHANNEL_NAME]; + int retval; + struct ast_cc_config_params *cc_params; + + cc_params = ast_channel_get_cc_config_params(chan); + if (!cc_params) { + return -1; + } + ast_channel_get_device_name(chan, device_name, sizeof(device_name)); + if (ast_cc_monitor_count(device_name, monitor_type) >= ast_get_cc_max_monitors(cc_params)) { + ast_log(LOG_NOTICE, "Not queuing a CC frame for device %s since it already has its maximum monitors allocated\n", device_name); + return -1; + } + + if (ast_cc_build_frame(chan, cc_params, monitor_type, device_name, dialstring, service, private_data, &frame)) { + /* Frame building failed. We can't use this. */ + return -1; + } + retval = ast_queue_frame(chan, &frame); + ast_frfree(&frame); + return retval; +} + +int ast_cc_build_frame(struct ast_channel *chan, struct ast_cc_config_params *cc_params, + const char *monitor_type, const char * const device_name, + const char * const dialstring, enum ast_cc_service_type service, void *private_data, + struct ast_frame *frame) +{ + struct cc_control_payload *payload = ast_calloc(1, sizeof(*payload)); + + if (!payload) { + return -1; + } + if (cc_build_payload(chan, cc_params, monitor_type, device_name, dialstring, service, private_data, payload)) { + /* Something screwed up, we can't make a frame with this */ + ast_free(payload); + return -1; + } + frame->frametype = AST_FRAME_CONTROL; + frame->subclass.integer = AST_CONTROL_CC; + frame->data.ptr = payload; + frame->datalen = sizeof(*payload); + frame->mallocd = AST_MALLOCD_DATA; + return 0; +} + +void ast_cc_call_failed(struct ast_channel *incoming, struct ast_channel *outgoing, const char * const dialstring) +{ + char device_name[AST_CHANNEL_NAME]; + struct cc_control_payload payload; + struct ast_cc_config_params *cc_params; + + if (outgoing->hangupcause != AST_CAUSE_BUSY && outgoing->hangupcause != AST_CAUSE_CONGESTION) { + /* It doesn't make sense to try to offer CCBS to the caller if the reason for ast_call + * failing is something other than busy or congestion + */ + return; + } + + cc_params = ast_channel_get_cc_config_params(outgoing); + if (!cc_params) { + return; + } + if (ast_get_cc_monitor_policy(cc_params) != AST_CC_MONITOR_GENERIC) { + /* This sort of CCBS only works if using generic CC. For native, we would end up sending + * a CC request for a non-existent call. The far end will reject this every time + */ + return; + } + + ast_channel_get_device_name(outgoing, device_name, sizeof(device_name)); + if (cc_build_payload(outgoing, cc_params, AST_CC_GENERIC_MONITOR_TYPE, device_name, + dialstring, AST_CC_CCBS, NULL, &payload)) { + /* Something screwed up, we can't make a frame with this */ + return; + } + ast_handle_cc_control_frame(incoming, outgoing, &payload); +} + +void ast_cc_busy_interface(struct ast_channel *inbound, struct ast_cc_config_params *cc_params, + const char *monitor_type, const char * const device_name, const char * const dialstring, void *private_data) +{ + struct cc_control_payload payload; + if (cc_build_payload(inbound, cc_params, monitor_type, device_name, dialstring, AST_CC_CCBS, private_data, &payload)) { + /* Something screwed up. Don't try to handle this payload */ + call_destructor_with_no_monitor(monitor_type, private_data); + return; + } + ast_handle_cc_control_frame(inbound, NULL, &payload); +} + +int ast_cc_callback(struct ast_channel *inbound, const char * const tech, const char * const dest, ast_cc_callback_fn callback) +{ + const struct ast_channel_tech *chantech = ast_get_channel_tech(tech); + + if (chantech && chantech->cc_callback) { + chantech->cc_callback(inbound, dest, callback); + } + + return 0; +} + +static const char *ccreq_app = "CallCompletionRequest"; + +static int ccreq_exec(struct ast_channel *chan, const char *data) +{ + struct cc_core_instance *core_instance; + char device_name[AST_CHANNEL_NAME]; + unsigned long match_flags; + int res; + + ast_channel_get_device_name(chan, device_name, sizeof(device_name)); + + match_flags = MATCH_NO_REQUEST; + if (!(core_instance = ao2_t_callback_data(cc_core_instances, 0, match_agent, device_name, &match_flags, "Find core instance for CallCompletionRequest"))) { + ast_log_dynamic_level(cc_logger_level, "Couldn't find a core instance for caller %s\n", device_name); + return -1; + } + + ast_log_dynamic_level(cc_logger_level, "Core %d: Found core_instance for caller %s\n", + core_instance->core_id, device_name); + + if (strcmp(core_instance->agent->callbacks->type, "generic")) { + ast_log_dynamic_level(cc_logger_level, "Core %d: CallCompletionRequest is only for generic agent types.\n", + core_instance->core_id); + pbx_builtin_setvar_helper(chan, "CC_REQUEST_RESULT", "FAIL"); + cc_unref(core_instance, "Unref core_instance since CallCompletionRequest was called with native agent"); + return 0; + } + + if (!ast_cc_request_is_within_limits()) { + ast_log_dynamic_level(cc_logger_level, "Core %d: CallCompletionRequest failed. Too many requests in the system\n", + core_instance->core_id); + ast_cc_failed(core_instance->core_id, "Too many CC requests\n"); + pbx_builtin_setvar_helper(chan, "CC_REQUEST_RESULT", "FAIL"); + cc_unref(core_instance, "Unref core_instance since too many CC requests"); + return 0; + } + + res = ast_cc_agent_accept_request(core_instance->core_id, "CallCompletionRequest called by caller %s for core_id %d", device_name, core_instance->core_id); + pbx_builtin_setvar_helper(chan, "CC_REQUEST_RESULT", res ? "FAIL" : "SUCCESS"); + cc_unref(core_instance, "Done with CallCompletionRequest"); + return res; +} + +static const char *cccancel_app = "CallCompletionCancel"; + +static int cccancel_exec(struct ast_channel *chan, const char *data) +{ + struct cc_core_instance *core_instance; + char device_name[AST_CHANNEL_NAME]; + unsigned long match_flags; + int res; + + ast_channel_get_device_name(chan, device_name, sizeof(device_name)); + + match_flags = MATCH_REQUEST; + if (!(core_instance = ao2_t_callback_data(cc_core_instances, 0, match_agent, device_name, &match_flags, "Find core instance for CallCompletionCancel"))) { + ast_log(LOG_WARNING, "Cannot find CC transaction to cancel for caller %s\n", device_name); + return -1; + } + + if (strcmp(core_instance->agent->callbacks->type, "generic")) { + ast_log(LOG_WARNING, "CallCompletionCancel may only be used for calles with a generic agent\n"); + cc_unref(core_instance, "Unref core instance found during CallCompletionCancel"); + return -1; + } + res = ast_cc_failed(core_instance->core_id, "Call completion request Cancelled for core ID %d by caller %s", + core_instance->core_id, device_name); + cc_unref(core_instance, "Unref core instance found during CallCompletionCancel"); + return res; +} + +struct count_monitors_cb_data { + const char *device_name; + const char *monitor_type; + int count; +}; + +static int count_monitors_cb(void *obj, void *arg, int flags) +{ + struct cc_core_instance *core_instance = obj; + struct count_monitors_cb_data *cb_data = arg; + const char *device_name = cb_data->device_name; + const char *monitor_type = cb_data->monitor_type; + struct ast_cc_monitor *monitor_iter; + + AST_LIST_LOCK(core_instance->monitors); + AST_LIST_TRAVERSE(core_instance->monitors, monitor_iter, next) { + if (!strcmp(monitor_iter->interface->device_name, device_name) && + !strcmp(monitor_iter->interface->monitor_type, monitor_type)) { + cb_data->count++; + break; + } + } + AST_LIST_UNLOCK(core_instance->monitors); + return 0; +} + +int ast_cc_monitor_count(const char * const name, const char * const type) +{ + struct count_monitors_cb_data data = {.device_name = name, .monitor_type = type,}; + + ao2_t_callback(cc_core_instances, OBJ_NODATA, count_monitors_cb, &data, "Counting agents"); + ast_log_dynamic_level(cc_logger_level, "Counted %d monitors\n", data.count); + return data.count; +} + +static void initialize_cc_max_requests(void) +{ + struct ast_config *cc_config; + const char *cc_max_requests_str; + struct ast_flags config_flags = {0,}; + char *endptr; + + cc_config = ast_config_load2("ccss.conf", "ccss", config_flags); + if (!cc_config || cc_config == CONFIG_STATUS_FILEINVALID) { + ast_log(LOG_WARNING, "Could not find valid ccss.conf file. Using cc_max_requests default\n"); + global_cc_max_requests = GLOBAL_CC_MAX_REQUESTS_DEFAULT; + return; + } + + if (!(cc_max_requests_str = ast_variable_retrieve(cc_config, "general", "cc_max_requests"))) { + ast_config_destroy(cc_config); + ast_log(LOG_WARNING, "No cc_max_requests defined. Using default\n"); + global_cc_max_requests = GLOBAL_CC_MAX_REQUESTS_DEFAULT; + return; + } + + global_cc_max_requests = strtol(cc_max_requests_str, &endptr, 10); + + if (!ast_strlen_zero(endptr)) { + ast_log(LOG_WARNING, "Invalid input given for cc_max_requests. Using default\n"); + global_cc_max_requests = GLOBAL_CC_MAX_REQUESTS_DEFAULT; + } + + ast_config_destroy(cc_config); + return; +} + +static void cc_cli_print_monitor_stats(struct ast_cc_monitor *monitor, int fd, int parent_id) +{ + struct ast_cc_monitor *child_monitor_iter = monitor; + if (!monitor) { + return; + } + + ast_cli(fd, "\t\t|-->%s", monitor->interface->device_name); + if (monitor->interface->monitor_class == AST_CC_DEVICE_MONITOR) { + ast_cli(fd, "(%s)", cc_service_to_string(monitor->service_offered)); + } + ast_cli(fd, "\n"); + + while ((child_monitor_iter = AST_LIST_NEXT(child_monitor_iter, next))) { + if (child_monitor_iter->parent_id == monitor->id) { + cc_cli_print_monitor_stats(child_monitor_iter, fd, child_monitor_iter->id); + } + } +} + +static int print_stats_cb(void *obj, void *arg, int flags) +{ + int *cli_fd = arg; + struct cc_core_instance *core_instance = obj; + + ast_cli(*cli_fd, "%d\t\t%s\t\t%s\n", core_instance->core_id, core_instance->agent->device_name, + cc_state_to_string(core_instance->current_state)); + AST_LIST_LOCK(core_instance->monitors); + cc_cli_print_monitor_stats(AST_LIST_FIRST(core_instance->monitors), *cli_fd, 0); + AST_LIST_UNLOCK(core_instance->monitors); + return 0; +} + +static int cc_cli_output_status(void *data) +{ + int *cli_fd = data; + int count = ao2_container_count(cc_core_instances); + + if (!count) { + ast_cli(*cli_fd, "There are currently no active call completion transactions\n"); + } else { + ast_cli(*cli_fd, "%d Call completion transactions\n", count); + ast_cli(*cli_fd, "Core ID\t\tCaller\t\t\t\tStatus\n"); + ast_cli(*cli_fd, "----------------------------------------------------------------------------\n"); + ao2_t_callback(cc_core_instances, OBJ_NODATA, print_stats_cb, cli_fd, "Printing stats to CLI"); + } + ast_free(cli_fd); + return 0; +} + +static char *handle_cc_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + int *cli_fd; + + switch (cmd) { + case CLI_INIT: + e->command = "cc report status"; + e->usage = + "Usage: cc report status\n" + " Report the current status of any ongoing CC transactions\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if (a->argc != 3) { + return CLI_SHOWUSAGE; + } + + cli_fd = ast_malloc(sizeof(*cli_fd)); + if (!cli_fd) { + return CLI_FAILURE; + } + + *cli_fd = a->fd; + + if (ast_taskprocessor_push(cc_core_taskprocessor, cc_cli_output_status, cli_fd)) { + ast_free(cli_fd); + return CLI_FAILURE; + } + return CLI_SUCCESS; +} + +static int kill_cores(void *obj, void *arg, int flags) +{ + int *core_id = arg; + struct cc_core_instance *core_instance = obj; + + if (!core_id || (core_instance->core_id == *core_id)) { + ast_cc_failed(core_instance->core_id, "CC transaction canceled administratively\n"); + } + return 0; +} + +static char *complete_core_id(const char *line, const char *word, int pos, int state) +{ + int which = 0; + int wordlen = strlen(word); + char *ret = NULL; + struct ao2_iterator core_iter = ao2_iterator_init(cc_core_instances, 0); + struct cc_core_instance *core_instance; + + for (; (core_instance = ao2_t_iterator_next(&core_iter, "Next core instance")); + cc_unref(core_instance, "CLI tab completion iteration")) { + char core_id_str[20]; + snprintf(core_id_str, sizeof(core_id_str), "%d", core_instance->core_id); + if (!strncmp(word, core_id_str, wordlen) && ++which > state) { + ret = ast_strdup(core_id_str); + cc_unref(core_instance, "Found a matching core ID for CLI tab-completion"); + break; + } + } + ao2_iterator_destroy(&core_iter); + + return ret; +} + +static char *handle_cc_kill(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + static const char * const option[] = { "core", "all", NULL }; + + switch (cmd) { + case CLI_INIT: + e->command = "cc cancel"; + e->usage = + "Usage: cc cancel can be used in two ways.\n" + " 1. 'cc cancel core [core ID]' will cancel the CC transaction with\n" + " core ID equal to the specified core ID.\n" + " 2. 'cc cancel all' will cancel all active CC transactions.\n"; + return NULL; + case CLI_GENERATE: + if (a->pos == 2) { + return ast_cli_complete(a->word, option, a->n); + } + if (a->pos == 3) { + return complete_core_id(a->line, a->word, a->pos, a->n); + } + return NULL; + } + + if (a->argc == 4) { + int core_id; + char *endptr; + if (strcasecmp(a->argv[2], "core")) { + return CLI_SHOWUSAGE; + } + core_id = strtol(a->argv[3], &endptr, 10); + if ((errno != 0 && core_id == 0) || (endptr == a->argv[3])) { + return CLI_SHOWUSAGE; + } + ao2_t_callback(cc_core_instances, OBJ_NODATA, kill_cores, &core_id, "CLI Killing Core Id"); + } else if (a->argc == 3) { + if (strcasecmp(a->argv[2], "all")) { + return CLI_SHOWUSAGE; + } + ao2_t_callback(cc_core_instances, OBJ_NODATA, kill_cores, NULL, "CLI Killing all CC cores"); + } else { + return CLI_SHOWUSAGE; + } + + return CLI_SUCCESS; +} + +static struct ast_cli_entry cc_cli[] = { + AST_CLI_DEFINE(handle_cc_status, "Reports CC stats"), + AST_CLI_DEFINE(handle_cc_kill, "Kill a CC transaction"), +}; + +int ast_cc_init(void) +{ + int res; + + if (!(cc_core_instances = ao2_t_container_alloc(CC_CORE_INSTANCES_BUCKETS, + cc_core_instance_hash_fn, cc_core_instance_cmp_fn, + "Create core instance container"))) { + return -1; + } + if (!(generic_monitors = ao2_t_container_alloc(CC_CORE_INSTANCES_BUCKETS, + generic_monitor_hash_fn, generic_monitor_cmp_fn, + "Create generic monitor container"))) { + return -1; + } + if (!(cc_core_taskprocessor = ast_taskprocessor_get("CCSS core", TPS_REF_DEFAULT))) { + return -1; + } + if (!(cc_sched_thread = ast_sched_thread_create())) { + return -1; + } + res = ast_register_application2(ccreq_app, ccreq_exec, NULL, NULL, NULL); + res |= ast_register_application2(cccancel_app, cccancel_exec, NULL, NULL, NULL); + res |= ast_cc_monitor_register(&generic_monitor_cbs); + res |= ast_cc_agent_register(&generic_agent_callbacks); + ast_cli_register_multiple(cc_cli, ARRAY_LEN(cc_cli)); + cc_logger_level = ast_logger_register_level(CC_LOGGER_LEVEL_NAME); + dialed_cc_interface_counter = 1; + initialize_cc_max_requests(); + return res; +} diff --git a/main/channel.c b/main/channel.c index 5be973a49..03c657cff 100644 --- a/main/channel.c +++ b/main/channel.c @@ -2239,6 +2239,7 @@ int ast_hangup(struct ast_channel *chan) } ast_channel_unlock(chan); + ast_cc_offer(chan); ast_manager_event(chan, EVENT_FLAG_CALL, "Hangup", "Channel: %s\r\n" "Uniqueid: %s\r\n" @@ -3611,6 +3612,7 @@ static int attribute_const is_visible_indication(enum ast_control_frame_type con case AST_CONTROL_TRANSFER: case AST_CONTROL_T38_PARAMETERS: case _XXX_AST_CONTROL_T38: + case AST_CONTROL_CC: break; case AST_CONTROL_CONGESTION: @@ -3754,6 +3756,7 @@ int ast_indicate_data(struct ast_channel *chan, int _condition, case AST_CONTROL_TRANSFER: case AST_CONTROL_CONNECTED_LINE: case AST_CONTROL_REDIRECTING: + case AST_CONTROL_CC: /* Nothing left to do for these. */ res = 0; break; @@ -4477,6 +4480,7 @@ struct ast_channel *__ast_request_and_dial(const char *type, format_t format, co case AST_CONTROL_SRCCHANGE: case AST_CONTROL_CONNECTED_LINE: case AST_CONTROL_REDIRECTING: + case AST_CONTROL_CC: case -1: /* Ignore -- just stopping indications */ break; @@ -7444,6 +7448,107 @@ int ast_channel_connected_line_macro(struct ast_channel *autoservice_chan, struc return retval; } +static void *channel_cc_params_copy(void *data) +{ + const struct ast_cc_config_params *src = data; + struct ast_cc_config_params *dest = ast_cc_config_params_init(); + if (!dest) { + return NULL; + } + ast_cc_copy_config_params(dest, src); + return dest; +} + +static void channel_cc_params_destroy(void *data) +{ + struct ast_cc_config_params *cc_params = data; + ast_cc_config_params_destroy(cc_params); +} + +static const struct ast_datastore_info cc_channel_datastore_info = { + .type = "Call Completion", + .duplicate = channel_cc_params_copy, + .destroy = channel_cc_params_destroy, +}; + +int ast_channel_cc_params_init(struct ast_channel *chan, + const struct ast_cc_config_params *base_params) +{ + struct ast_cc_config_params *cc_params; + struct ast_datastore *cc_datastore; + + if (!(cc_params = ast_cc_config_params_init())) { + return -1; + } + + if (!(cc_datastore = ast_datastore_alloc(&cc_channel_datastore_info, NULL))) { + ast_cc_config_params_destroy(cc_params); + return -1; + } + + if (base_params) { + ast_cc_copy_config_params(cc_params, base_params); + } + cc_datastore->data = cc_params; + ast_channel_datastore_add(chan, cc_datastore); + return 0; +} + +struct ast_cc_config_params *ast_channel_get_cc_config_params(struct ast_channel *chan) +{ + struct ast_datastore *cc_datastore; + + if (!(cc_datastore = ast_channel_datastore_find(chan, &cc_channel_datastore_info, NULL))) { + /* If we can't find the datastore, it almost definitely means that the channel type being + * used has not had its driver modified to parse CC config parameters. The best action + * to take here is to create the parameters on the spot with the defaults set. + */ + if (ast_channel_cc_params_init(chan, NULL)) { + return NULL; + } + if (!(cc_datastore = ast_channel_datastore_find(chan, &cc_channel_datastore_info, NULL))) { + /* Should be impossible */ + return NULL; + } + } + + ast_assert(cc_datastore->data != NULL); + return cc_datastore->data; +} + +int ast_channel_get_device_name(struct ast_channel *chan, char *device_name, size_t name_buffer_length) +{ + int len = name_buffer_length; + char *dash; + if (!ast_channel_queryoption(chan, AST_OPTION_DEVICE_NAME, device_name, &len, 0)) { + return 0; + } + + /* Dang. Do it the old-fashioned way */ + ast_copy_string(device_name, chan->name, name_buffer_length); + if ((dash = strrchr(device_name, '-'))) { + *dash = '\0'; + } + + return 0; +} + +int ast_channel_get_cc_agent_type(struct ast_channel *chan, char *agent_type, size_t size) +{ + int len = size; + char *slash; + + if (!ast_channel_queryoption(chan, AST_OPTION_CC_AGENT_TYPE, agent_type, &len, 0)) { + return 0; + } + + ast_copy_string(agent_type, chan->name, size); + if ((slash = strchr(agent_type, '/'))) { + *slash = '\0'; + } + return 0; +} + /* DO NOT PUT ADDITIONAL FUNCTIONS BELOW THIS BOUNDARY * * ONLY FUNCTIONS FOR PROVIDING BACKWARDS ABI COMPATIBILITY BELONG HERE diff --git a/main/manager.c b/main/manager.c index f811987f7..0456990b4 100644 --- a/main/manager.c +++ b/main/manager.c @@ -980,6 +980,7 @@ static const struct permalias { { EVENT_FLAG_DIALPLAN, "dialplan" }, { EVENT_FLAG_ORIGINATE, "originate" }, { EVENT_FLAG_AGI, "agi" }, + { EVENT_FLAG_CC, "cc" }, { INT_MAX, "all" }, { 0, "none" }, }; diff --git a/main/xml.c b/main/xml.c index 36e7dd812..0f93abc54 100644 --- a/main/xml.c +++ b/main/xml.c @@ -23,6 +23,7 @@ #include "asterisk.h" #include "asterisk/xml.h" +#include "asterisk/logger.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision$") @@ -67,6 +68,25 @@ struct ast_xml_doc *ast_xml_open(char *filename) return (struct ast_xml_doc *) doc; } +struct ast_xml_doc *ast_xml_read_memory(char *buffer, size_t size) +{ + xmlDoc *doc; + + if (!buffer) { + return NULL; + } + + if (!(doc = xmlParseMemory(buffer, (int) size))) { + /* process xinclude elements. */ + if (xmlXIncludeProcess(doc) < 0) { + xmlFreeDoc(doc); + return NULL; + } + } + + return (struct ast_xml_doc *) doc; +} + void ast_xml_close(struct ast_xml_doc *doc) { if (!doc) { @@ -164,6 +184,16 @@ struct ast_xml_node *ast_xml_find_element(struct ast_xml_node *root_node, const return NULL; } +struct ast_xml_ns *ast_xml_find_namespace(struct ast_xml_doc *doc, struct ast_xml_node *node, const char *ns_name) { + xmlNsPtr ns = xmlSearchNs((xmlDocPtr) doc, (xmlNodePtr) node, (xmlChar *) ns_name); + return (struct ast_xml_ns *) ns; +} + +const char *ast_xml_get_ns_href(struct ast_xml_ns *ns) +{ + return (const char *) ((xmlNsPtr) ns)->href; +} + const char *ast_xml_get_text(struct ast_xml_node *node) { if (!node) { |