diff options
87 files changed, 6919 insertions, 365 deletions
@@ -7,6 +7,7 @@ === and the other UPGRADE files for older releases. === ====================================================================== + ------------------------------------------------------------------------------ --- Functionality changes from Asterisk 1.6.2 to Asterisk 1.6.3 ------------- ------------------------------------------------------------------------------ @@ -161,8 +162,21 @@ Logger users of this channel in the tree have been converted to LOG_NOTICE or removed (in cases where the same message was already generated to another channel). +Channel Event Logging +--------------------- + * A new interface, CEL, is introduced here. CEL logs single events, much like + the AMI, but it differs from the AMI in that it logs to db backends much + like CDR does; is based on the event subsystem introduced by Russell, and + can share in all its benefits; allows multiple backends to operate like CDR; + is specialized to event data that would be of concern to billing sytems, + like CDR. Backends for logging and accounting calls have been produced, + but a new CDR backend is still in development. + CDR --- + * 'linkedid' and 'peeraccount' are new CDR fields available to CDR officianados. + linkedid is based on uniqueID, but spreads to other channels as transfers, dials, + etc are performed. Thus the peices of CDR can be grouped into multilegged sets. * Multiple files and formats can now be specified in cdr_custom.conf. Calendaring for Asterisk @@ -293,7 +293,7 @@ endif # value directly to ASTCFLAGS ASTCFLAGS+=$(MALLOC_DEBUG)$(OPTIONS) -MOD_SUBDIRS:=channels pbx apps codecs formats cdr bridges funcs tests main res $(LOCAL_MOD_SUBDIRS) +MOD_SUBDIRS:=channels pbx apps codecs formats cdr cel bridges funcs tests main res $(LOCAL_MOD_SUBDIRS) OTHER_SUBDIRS:=utils agi SUBDIRS:=$(OTHER_SUBDIRS) $(MOD_SUBDIRS) SUBDIRS_INSTALL:=$(SUBDIRS:%=%-install) @@ -574,6 +574,8 @@ bininstall: _all installdirs $(SUBDIRS_INSTALL) mkdir -p $(DESTDIR)$(ASTDATADIR)/documentation/thirdparty mkdir -p $(DESTDIR)$(ASTLOGDIR)/cdr-csv mkdir -p $(DESTDIR)$(ASTLOGDIR)/cdr-custom + mkdir -p $(DESTDIR)$(ASTLOGDIR)/cel-csv + mkdir -p $(DESTDIR)$(ASTLOGDIR)/cel-custom mkdir -p $(DESTDIR)$(ASTDATADIR)/keys mkdir -p $(DESTDIR)$(ASTDATADIR)/firmware mkdir -p $(DESTDIR)$(ASTDATADIR)/firmware/iax diff --git a/apps/app_celgenuserevent.c b/apps/app_celgenuserevent.c new file mode 100644 index 000000000..da1c246ef --- /dev/null +++ b/apps/app_celgenuserevent.c @@ -0,0 +1,98 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2008, 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 Generate User-Defined CEL event + * + * \author Steve Murphy + * + * \ingroup applications + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/module.h" +#include "asterisk/app.h" +#include "asterisk/channel.h" +#include "asterisk/cel.h" + +/*** DOCUMENTATION + <application name="CELGenUserEvent" language="en_US"> + <synopsis> + Generates a CEL User Defined Event. + </synopsis> + <syntax> + <parameter name="event-name" required="true"> + <argument name="event-name" required="true"> + </argument> + </parameter> + </syntax> + <description> + <para>A CEL event will be immediately generated by this channel, with the supplied name for a type.</para> + </description> + </application> + ***/ + +static char *app = "CELGenUserEvent"; + +static int celgenuserevent_exec(struct ast_channel *chan, const char *data) +{ + int res = 0; + char *parse; + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(event); + AST_APP_ARG(extra); + ); + + if (ast_strlen_zero(data)) { + return 0; + } + + parse = ast_strdupa(data); + AST_STANDARD_APP_ARGS(args, parse); + + ast_cel_report_event(chan, AST_CEL_USER_DEFINED, args.event, args.extra, NULL); + return res; +} + +static int unload_module(void) +{ + int res; + + res = ast_unregister_application(app); + + ast_module_user_hangup_all(); + + return res; +} + +static int load_module(void) +{ + int res = ast_register_application_xml(app, celgenuserevent_exec); + if (res) { + return AST_MODULE_LOAD_DECLINE; + } else { + return AST_MODULE_LOAD_SUCCESS; + } +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Generate an User-Defined CEL event", + .load = load_module, + .unload = unload_module, + ); diff --git a/apps/app_chanisavail.c b/apps/app_chanisavail.c index b1c426af5..c1b33b938 100644 --- a/apps/app_chanisavail.c +++ b/apps/app_chanisavail.c @@ -159,7 +159,7 @@ static int chanavail_exec(struct ast_channel *chan, const char *data) snprintf(trychan, sizeof(trychan), "%s/%s",cur,number); status = inuse = ast_device_state(trychan); } - if ((inuse <= 1) && (tempchan = ast_request(tech, chan->nativeformats, number, &status))) { + if ((inuse <= 1) && (tempchan = ast_request(tech, chan->nativeformats, chan, number, &status))) { ast_str_append(&tmp_availchan, 0, "%s%s", ast_str_strlen(tmp_availchan) ? "&" : "", tempchan->name); snprintf(tmp, sizeof(tmp), "%s/%s", tech, number); diff --git a/apps/app_confbridge.c b/apps/app_confbridge.c index bb68f365a..446cc097f 100644 --- a/apps/app_confbridge.c +++ b/apps/app_confbridge.c @@ -559,7 +559,7 @@ static int play_sound_file(struct conference_bridge *conference_bridge, const ch if (!(conference_bridge->playback_chan)) { int cause; - if (!(conference_bridge->playback_chan = ast_request("Bridge", AST_FORMAT_SLINEAR, "", &cause))) { + if (!(conference_bridge->playback_chan = ast_request("Bridge", AST_FORMAT_SLINEAR, NULL, "", &cause))) { ast_mutex_unlock(&conference_bridge->playback_lock); return -1; } diff --git a/apps/app_dial.c b/apps/app_dial.c index a9522652b..92d16576b 100644 --- a/apps/app_dial.c +++ b/apps/app_dial.c @@ -61,6 +61,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/stringfields.h" #include "asterisk/global_datastores.h" #include "asterisk/dsp.h" +#include "asterisk/cel.h" /*** DOCUMENTATION <application name="Dial" language="en_US"> @@ -756,6 +757,9 @@ static void do_forward(struct chanlist *o, stuff = tmpchan; tech = "Local"; } + + ast_cel_report_event(in, AST_CEL_FORWARD, NULL, c->call_forward, NULL); + /* Before processing channel, go ahead and check for forwarding */ ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, c->name); /* If we have been told to ignore forwards, just set this channel to null and continue processing extensions normally */ @@ -765,7 +769,7 @@ static void do_forward(struct chanlist *o, cause = AST_CAUSE_BUSY; } else { /* Setup parameters */ - c = o->chan = ast_request(tech, in->nativeformats, stuff, &cause); + c = o->chan = ast_request(tech, in->nativeformats, in, stuff, &cause); if (c) { if (single) ast_channel_make_compatible(o->chan, in); @@ -1872,7 +1876,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast AST_LIST_UNLOCK(dialed_interfaces); } - tc = ast_request(tech, chan->nativeformats, numsubst, &cause); + tc = ast_request(tech, chan->nativeformats, chan, numsubst, &cause); if (!tc) { /* If we can't, just go on to the next call */ ast_log(LOG_WARNING, "Unable to create channel of type '%s' (cause %d - %s)\n", @@ -1921,7 +1925,9 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast tc->cid.cid_tns = chan->cid.cid_tns; - ast_string_field_set(tc, accountcode, chan->accountcode); + if (!ast_strlen_zero(chan->accountcode)) { + ast_string_field_set(tc, peeraccount, chan->accountcode); + } tc->cdrflags = chan->cdrflags; if (ast_strlen_zero(tc->musicclass)) ast_string_field_set(tc, musicclass, chan->musicclass); diff --git a/apps/app_directed_pickup.c b/apps/app_directed_pickup.c index 69015a090..9301395c8 100644 --- a/apps/app_directed_pickup.c +++ b/apps/app_directed_pickup.c @@ -41,6 +41,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/app.h" #include "asterisk/features.h" #include "asterisk/callerid.h" +#include "asterisk/cel.h" #define PICKUPMARK "PICKUPMARK" @@ -95,6 +96,7 @@ static int pickup_do(struct ast_channel *chan, struct ast_channel *target) struct ast_party_connected_line connected_caller; ast_debug(1, "Call pickup on '%s' by '%s'\n", target->name, chan->name); + ast_cel_report_event(target, AST_CEL_PICKUP, NULL, NULL, chan); connected_caller = target->connected; connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; diff --git a/apps/app_followme.c b/apps/app_followme.c index 504ff7c76..67648331b 100644 --- a/apps/app_followme.c +++ b/apps/app_followme.c @@ -831,7 +831,7 @@ static void findmeexec(struct fm_args *tpargs) return; } - outbound = ast_request("Local", ast_best_codec(caller->nativeformats), dialarg, &dg); + outbound = ast_request("Local", ast_best_codec(caller->nativeformats), caller, dialarg, &dg); if (outbound) { ast_set_callerid(outbound, caller->cid.cid_num, caller->cid.cid_name, caller->cid.cid_num); ast_channel_inherit_variables(tpargs->chan, outbound); diff --git a/apps/app_meetme.c b/apps/app_meetme.c index bfb61a399..23abe82b2 100644 --- a/apps/app_meetme.c +++ b/apps/app_meetme.c @@ -1176,7 +1176,7 @@ static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin cnf->dahdiconf = dahdic.confno; /* Setup a new channel for playback of audio files */ - cnf->chan = ast_request("DAHDI", AST_FORMAT_SLINEAR, "pseudo", NULL); + cnf->chan = ast_request("DAHDI", AST_FORMAT_SLINEAR, chan, "pseudo", NULL); if (cnf->chan) { ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR); ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR); @@ -2202,7 +2202,7 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int c } ast_mutex_lock(&conf->recordthreadlock); - if ((conf->recordthread == AST_PTHREADT_NULL) && (confflags & CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("DAHDI", AST_FORMAT_SLINEAR, "pseudo", NULL)))) { + if ((conf->recordthread == AST_PTHREADT_NULL) && (confflags & CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("DAHDI", AST_FORMAT_SLINEAR, chan, "pseudo", NULL)))) { ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR); ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR); dahdic.chan = 0; diff --git a/apps/app_minivm.c b/apps/app_minivm.c index ac48aa50f..c4e786698 100644 --- a/apps/app_minivm.c +++ b/apps/app_minivm.c @@ -1299,8 +1299,7 @@ static int sendmail(struct minivm_template *template, struct minivm_account *vmu return -1; } /* Allocate channel used for chanvar substitution */ - ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "%s", ""); - + ast = ast_dummy_channel_alloc(); snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60); @@ -1461,9 +1460,8 @@ static int sendmail(struct minivm_template *template, struct minivm_account *vmu ast_safe_system(tmp2); ast_debug(1, "Sent message to %s with command '%s' - %s\n", vmu->email, global_mailcmd, template->attachment ? "(media attachment)" : ""); ast_debug(3, "Actual command used: %s\n", tmp2); - if (ast) { + if (ast) ast = ast_channel_release(ast); - } ast_free(str1); ast_free(str2); return 0; diff --git a/apps/app_parkandannounce.c b/apps/app_parkandannounce.c index d8858b64e..4ae525101 100644 --- a/apps/app_parkandannounce.c +++ b/apps/app_parkandannounce.c @@ -145,7 +145,7 @@ static int parkandannounce_exec(struct ast_channel *chan, const char *data) snprintf(buf, sizeof(buf), "%d", lot); oh.parent_channel = chan; oh.vars = ast_variable_new("_PARKEDAT", buf, ""); - dchan = __ast_request_and_dial(dialtech, AST_FORMAT_SLINEAR, args.dial, 30000, &outstate, chan->cid.cid_num, chan->cid.cid_name, &oh); + dchan = __ast_request_and_dial(dialtech, AST_FORMAT_SLINEAR, chan, args.dial, 30000, &outstate, chan->cid.cid_num, chan->cid.cid_name, &oh); if (dchan) { if (dchan->_state == AST_STATE_UP) { diff --git a/apps/app_queue.c b/apps/app_queue.c index bebe543b8..aa3564a87 100644 --- a/apps/app_queue.c +++ b/apps/app_queue.c @@ -95,6 +95,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/global_datastores.h" #include "asterisk/taskprocessor.h" #include "asterisk/callerid.h" +#include "asterisk/cel.h" /*! * \par Please read before modifying this file. @@ -2656,7 +2657,7 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies location = ""; /* Request the peer */ - tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status); + tmp->chan = ast_request(tech, qe->chan->nativeformats, qe->chan, location, &status); if (!tmp->chan) { /* If we can't, just go on to the next call */ if (qe->chan->cdr) ast_cdr_busy(qe->chan->cdr); @@ -3138,10 +3139,13 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte stuff = tmpchan; tech = "Local"; } + + ast_cel_report_event(in, AST_CEL_FORWARD, NULL, o->chan->call_forward, NULL); + /* Before processing channel, go ahead and check for forwarding */ ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", inchan_name, tech, stuff, ochan_name); /* Setup parameters */ - o->chan = ast_request(tech, in->nativeformats, stuff, &status); + o->chan = ast_request(tech, in->nativeformats, in, stuff, &status); if (!o->chan) { ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff); o->stillgoing = 0; @@ -7483,7 +7487,8 @@ static int load_module(void) ast_log(LOG_WARNING, "devicestate taskprocessor reference failed - devicestate notifications will not occur\n"); } - if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, NULL, AST_EVENT_IE_END))) { + /* in the following subscribe call, do I use DEVICE_STATE, or DEVICE_STATE_CHANGE? */ + if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, "AppQueue Device state", NULL, AST_EVENT_IE_END))) { res = -1; } diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c index 51b78e254..b058ab182 100644 --- a/apps/app_voicemail.c +++ b/apps/app_voicemail.c @@ -4105,7 +4105,7 @@ static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, in if (!ast_strlen_zero(fromstring)) { struct ast_channel *ast; - if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) { + if ((ast = ast_dummy_channel_alloc())) { char *ptr; prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag); ast_str_substitute_variables(&str1, 0, ast, fromstring); @@ -4151,7 +4151,7 @@ static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, in if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) { char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject; struct ast_channel *ast; - if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) { + if ((ast = ast_dummy_channel_alloc())) { prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag); ast_str_substitute_variables(&str1, 0, ast, e_subj); if (check_mime(ast_str_buffer(str1))) { @@ -4234,7 +4234,7 @@ static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, in if (emailbody || vmu->emailbody) { char* e_body = vmu->emailbody ? vmu->emailbody : emailbody; struct ast_channel *ast; - if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) { + if ((ast = ast_dummy_channel_alloc())) { prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag); ast_str_substitute_variables(&str1, 0, ast, e_body); fprintf(p, "%s" ENDL, ast_str_buffer(str1)); @@ -4434,7 +4434,7 @@ static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char if (!ast_strlen_zero(pagerfromstring)) { struct ast_channel *ast; - if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) { + if ((ast = ast_dummy_channel_alloc())) { char *ptr; prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag); ast_str_substitute_variables(&str1, 0, ast, pagerfromstring); @@ -4479,7 +4479,7 @@ static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char if (!ast_strlen_zero(pagersubject)) { struct ast_channel *ast; - if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) { + if ((ast = ast_dummy_channel_alloc())) { prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag); ast_str_substitute_variables(&str1, 0, ast, pagersubject); if (check_mime(ast_str_buffer(str1))) { @@ -4512,7 +4512,7 @@ static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char ast_strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm); if (pagerbody) { struct ast_channel *ast; - if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) { + if ((ast = ast_dummy_channel_alloc())) { prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag); ast_str_substitute_variables(&str1, 0, ast, pagerbody); fprintf(p, "%s" ENDL, ast_str_buffer(str1)); @@ -10254,11 +10254,11 @@ static void mwi_sub_event_cb(const struct ast_event *event, void *userdata) static void start_poll_thread(void) { - mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, NULL, + mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, "Voicemail MWI subscription", NULL, AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI, AST_EVENT_IE_END); - mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, NULL, + mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, "Voicemail MWI subscription", NULL, AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI, AST_EVENT_IE_END); diff --git a/bridges/bridge_builtin_features.c b/bridges/bridge_builtin_features.c index a61421c79..67da6489b 100644 --- a/bridges/bridge_builtin_features.c +++ b/bridges/bridge_builtin_features.c @@ -75,7 +75,7 @@ static struct ast_channel *dial_transfer(const struct ast_channel *caller, const snprintf(destination, sizeof(destination), "%s@%s", exten, context); /* Now we request that chan_local prepare to call the destination */ - if (!(chan = ast_request("Local", caller->nativeformats, destination, &cause))) { + if (!(chan = ast_request("Local", caller->nativeformats, caller, destination, &cause))) { return NULL; } diff --git a/cdr/cdr_custom.c b/cdr/cdr_custom.c index c045241d9..0a88f3d65 100644 --- a/cdr/cdr_custom.c +++ b/cdr/cdr_custom.c @@ -124,7 +124,7 @@ static int custom_log(struct ast_cdr *cdr) return -1; } - dummy = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Substitution/%p", cdr); + dummy = ast_dummy_channel_alloc(); if (!dummy) { ast_log(LOG_ERROR, "Unable to allocate channel for variable subsitution.\n"); diff --git a/cdr/cdr_manager.c b/cdr/cdr_manager.c index 83e6054dd..49e99796a 100644 --- a/cdr/cdr_manager.c +++ b/cdr/cdr_manager.c @@ -156,7 +156,7 @@ static int manager_log(struct ast_cdr *cdr) buf[0] = '\0'; ast_rwlock_rdlock(&customfields_lock); if (customfields && ast_str_strlen(customfields)) { - struct ast_channel *dummy = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Substitution/%p", cdr); + struct ast_channel *dummy = ast_dummy_channel_alloc(); if (!dummy) { ast_log(LOG_ERROR, "Unable to allocate channel for variable substitution.\n"); return 0; diff --git a/cdr/cdr_sqlite.c b/cdr/cdr_sqlite.c index 8eecee453..a5eb54f22 100644 --- a/cdr/cdr_sqlite.c +++ b/cdr/cdr_sqlite.c @@ -184,7 +184,7 @@ static int load_module(void) if (!db) { ast_log(LOG_ERROR, "cdr_sqlite: %s\n", zErr); ast_free(zErr); - return -1; + return AST_MODULE_LOAD_DECLINE; } /* is the table there? */ @@ -203,14 +203,14 @@ static int load_module(void) res = ast_cdr_register(name, ast_module_info->description, sqlite_log); if (res) { ast_log(LOG_ERROR, "Unable to register SQLite CDR handling\n"); - return -1; + return AST_MODULE_LOAD_DECLINE; } - return 0; + return AST_MODULE_LOAD_SUCCESS; err: if (db) sqlite_close(db); - return -1; + return AST_MODULE_LOAD_DECLINE; } AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "SQLite CDR Backend"); diff --git a/cdr/cdr_sqlite3_custom.c b/cdr/cdr_sqlite3_custom.c index fd2d3e476..6f1961c51 100644 --- a/cdr/cdr_sqlite3_custom.c +++ b/cdr/cdr_sqlite3_custom.c @@ -241,7 +241,7 @@ static int sqlite3_log(struct ast_cdr *cdr) struct ast_channel *dummy; struct ast_str *value_string = ast_str_create(1024); - dummy = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Substitution/%p", cdr); + dummy = ast_dummy_channel_alloc(); if (!dummy) { ast_log(LOG_ERROR, "Unable to allocate channel for variable subsitution.\n"); ast_free(value_string); diff --git a/cel/Makefile b/cel/Makefile new file mode 100644 index 000000000..5ac6d89af --- /dev/null +++ b/cel/Makefile @@ -0,0 +1,20 @@ +# +# Asterisk -- A telephony toolkit for Linux. +# +# Makefile for CEL backends +# +# Copyright (C) 1999-2008, Digium, Inc. +# +# This program is free software, distributed under the terms of +# the GNU General Public License +# + +-include $(ASTTOPDIR)/menuselect.makeopts $(ASTTOPDIR)/menuselect.makedeps + +MODULE_PREFIX=cel +MENUSELECT_CATEGORY=CEL +MENUSELECT_DESCRIPTION=Channel Event Logging + +all: _all + +include $(ASTTOPDIR)/Makefile.moddir_rules diff --git a/cel/cel_adaptive_odbc.c b/cel/cel_adaptive_odbc.c new file mode 100644 index 000000000..984f0590f --- /dev/null +++ b/cel/cel_adaptive_odbc.c @@ -0,0 +1,771 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2008 Digium + * + * Adapted from cdr_adaptive_odbc: + * Tilghman Lesher <cdr_adaptive_odbc__v1@the-tilghman.com> + * by Steve Murphy + * + * 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 Adaptive ODBC CEL backend + * + * \author Tilghman Lesher <cdr_adaptive_odbc__v1@the-tilghman.com> + * \ingroup cel_drivers + */ + +/*** MODULEINFO + <depend>generic_odbc</depend> + <depend>ltdl</depend> + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include <sys/types.h> +#include <time.h> + +#include <sql.h> +#include <sqlext.h> +#include <sqltypes.h> + +#include "asterisk/config.h" +#include "asterisk/channel.h" +#include "asterisk/lock.h" +#include "asterisk/linkedlists.h" +#include "asterisk/res_odbc.h" +#include "asterisk/cel.h" +#include "asterisk/module.h" + +#define CONFIG "cel_adaptive_odbc.conf" +static struct ast_event_sub *event_sub = NULL; + +/* Optimization to reduce number of memory allocations */ +static int maxsize = 512, maxsize2 = 512; + +struct columns { + char *name; + char *celname; + char *filtervalue; + char *staticvalue; + SQLSMALLINT type; + SQLINTEGER size; + SQLSMALLINT decimals; + SQLSMALLINT radix; + SQLSMALLINT nullable; + SQLINTEGER octetlen; + AST_LIST_ENTRY(columns) list; +}; + +struct tables { + char *connection; + char *table; + unsigned int usegmtime:1; + AST_LIST_HEAD_NOLOCK(odbc_columns, columns) columns; + AST_RWLIST_ENTRY(tables) list; +}; + +static AST_RWLIST_HEAD_STATIC(odbc_tables, tables); + +static int load_config(void) +{ + struct ast_config *cfg; + struct ast_variable *var; + const char *tmp, *catg; + struct tables *tableptr; + struct columns *entry; + struct odbc_obj *obj; + char columnname[80]; + char connection[40]; + char table[40]; + int lenconnection, lentable, usegmtime = 0; + SQLLEN sqlptr; + int res = 0; + SQLHSTMT stmt = NULL; + struct ast_flags config_flags = { 0 }; /* Part of our config comes from the database */ + + cfg = ast_config_load(CONFIG, config_flags); + if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) { + ast_log(LOG_WARNING, "Unable to load " CONFIG ". No adaptive ODBC CEL records!\n"); + return -1; + } + + for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) { + var = ast_variable_browse(cfg, catg); + if (!var) + continue; + + if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "connection"))) { + ast_log(LOG_WARNING, "No connection parameter found in '%s'. Skipping.\n", catg); + continue; + } + ast_copy_string(connection, tmp, sizeof(connection)); + lenconnection = strlen(connection); + + if (!ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "usegmtime"))) { + usegmtime = ast_true(tmp); + } + + /* When loading, we want to be sure we can connect. */ + obj = ast_odbc_request_obj(connection, 1); + if (!obj) { + ast_log(LOG_WARNING, "No such connection '%s' in the '%s' section of " CONFIG ". Check res_odbc.conf.\n", connection, catg); + continue; + } + + if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "table"))) { + ast_log(LOG_NOTICE, "No table name found. Assuming 'cel'.\n"); + tmp = "cel"; + } + ast_copy_string(table, tmp, sizeof(table)); + lentable = strlen(table); + + res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt); + if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { + ast_log(LOG_WARNING, "SQL Alloc Handle failed on connection '%s'!\n", connection); + ast_odbc_release_obj(obj); + continue; + } + + res = SQLColumns(stmt, NULL, 0, NULL, 0, (unsigned char *)table, SQL_NTS, (unsigned char *)"%", SQL_NTS); + if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { + ast_log(LOG_ERROR, "Unable to query database columns on connection '%s'. Skipping.\n", connection); + ast_odbc_release_obj(obj); + continue; + } + + tableptr = ast_calloc(sizeof(char), sizeof(*tableptr) + lenconnection + 1 + lentable + 1); + if (!tableptr) { + ast_log(LOG_ERROR, "Out of memory creating entry for table '%s' on connection '%s'\n", table, connection); + ast_odbc_release_obj(obj); + res = -1; + break; + } + + tableptr->usegmtime = usegmtime; + tableptr->connection = (char *)tableptr + sizeof(*tableptr); + tableptr->table = (char *)tableptr + sizeof(*tableptr) + lenconnection + 1; + ast_copy_string(tableptr->connection, connection, lenconnection + 1); + ast_copy_string(tableptr->table, table, lentable + 1); + + ast_verb(3, "Found adaptive CEL table %s@%s.\n", tableptr->table, tableptr->connection); + + /* Check for filters first */ + for (var = ast_variable_browse(cfg, catg); var; var = var->next) { + if (strncmp(var->name, "filter", 6) == 0) { + char *celvar = ast_strdupa(var->name + 6); + celvar = ast_strip(celvar); + ast_verb(3, "Found filter %s for cel variable %s in %s@%s\n", var->value, celvar, tableptr->table, tableptr->connection); + + entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(celvar) + 1 + strlen(var->value) + 1); + if (!entry) { + ast_log(LOG_ERROR, "Out of memory creating filter entry for CEL variable '%s' in table '%s' on connection '%s'\n", celvar, table, connection); + res = -1; + break; + } + + /* NULL column entry means this isn't a column in the database */ + entry->name = NULL; + entry->celname = (char *)entry + sizeof(*entry); + entry->filtervalue = (char *)entry + sizeof(*entry) + strlen(celvar) + 1; + strcpy(entry->celname, celvar); + strcpy(entry->filtervalue, var->value); + + AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list); + } + } + + while ((res = SQLFetch(stmt)) != SQL_NO_DATA && res != SQL_ERROR) { + char *celvar = "", *staticvalue = ""; + + SQLGetData(stmt, 4, SQL_C_CHAR, columnname, sizeof(columnname), &sqlptr); + + /* Is there an alias for this column? */ + + /* NOTE: This seems like a non-optimal parse method, but I'm going + * for user configuration readability, rather than fast parsing. We + * really don't parse this file all that often, anyway. + */ + for (var = ast_variable_browse(cfg, catg); var; var = var->next) { + if (strncmp(var->name, "alias", 5) == 0 && strcasecmp(var->value, columnname) == 0) { + char *alias = ast_strdupa(var->name + 5); + celvar = ast_strip(alias); + ast_verb(3, "Found alias %s for column %s in %s@%s\n", celvar, columnname, tableptr->table, tableptr->connection); + break; + } else if (strncmp(var->name, "static", 6) == 0 && strcasecmp(var->value, columnname) == 0) { + char *item = ast_strdupa(var->name + 6); + item = ast_strip(item); + if (item[0] == '"' && item[strlen(item) - 1] == '"') { + /* Remove surrounding quotes */ + item[strlen(item) - 1] = '\0'; + item++; + } + staticvalue = item; + } + } + + entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(columnname) + 1 + strlen(celvar) + 1 + strlen(staticvalue) + 1); + if (!entry) { + ast_log(LOG_ERROR, "Out of memory creating entry for column '%s' in table '%s' on connection '%s'\n", columnname, table, connection); + res = -1; + break; + } + entry->name = (char *)entry + sizeof(*entry); + strcpy(entry->name, columnname); + + if (!ast_strlen_zero(celvar)) { + entry->celname = entry->name + strlen(columnname) + 1; + strcpy(entry->celname, celvar); + } else { /* Point to same place as the column name */ + entry->celname = (char *)entry + sizeof(*entry); + } + + if (!ast_strlen_zero(staticvalue)) { + entry->staticvalue = entry->celname + strlen(entry->celname) + 1; + strcpy(entry->staticvalue, staticvalue); + } + + SQLGetData(stmt, 5, SQL_C_SHORT, &entry->type, sizeof(entry->type), NULL); + SQLGetData(stmt, 7, SQL_C_LONG, &entry->size, sizeof(entry->size), NULL); + SQLGetData(stmt, 9, SQL_C_SHORT, &entry->decimals, sizeof(entry->decimals), NULL); + SQLGetData(stmt, 10, SQL_C_SHORT, &entry->radix, sizeof(entry->radix), NULL); + SQLGetData(stmt, 11, SQL_C_SHORT, &entry->nullable, sizeof(entry->nullable), NULL); + SQLGetData(stmt, 16, SQL_C_LONG, &entry->octetlen, sizeof(entry->octetlen), NULL); + + /* Specification states that the octenlen should be the maximum number of bytes + * returned in a char or binary column, but it seems that some drivers just set + * it to NULL. (Bad Postgres! No biscuit!) */ + if (entry->octetlen == 0) + entry->octetlen = entry->size; + + ast_verb(10, "Found %s column with type %hd with len %ld, octetlen %ld, and numlen (%hd,%hd)\n", entry->name, entry->type, (long) entry->size, (long) entry->octetlen, entry->decimals, entry->radix); + /* Insert column info into column list */ + AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list); + res = 0; + } + + SQLFreeHandle(SQL_HANDLE_STMT, stmt); + ast_odbc_release_obj(obj); + + if (AST_LIST_FIRST(&(tableptr->columns))) + AST_RWLIST_INSERT_TAIL(&odbc_tables, tableptr, list); + else + ast_free(tableptr); + } + return res; +} + +static int free_config(void) +{ + struct tables *table; + struct columns *entry; + while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) { + while ((entry = AST_LIST_REMOVE_HEAD(&(table->columns), list))) { + ast_free(entry); + } + ast_free(table); + } + return 0; +} + +static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data) +{ + int res, i; + char *sql = data; + SQLHSTMT stmt; + SQLINTEGER nativeerror = 0, numfields = 0; + SQLSMALLINT diagbytes = 0; + unsigned char state[10], diagnostic[256]; + + res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt); + if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { + ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n"); + return NULL; + } + + res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS); + if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { + ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql); + SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes); + for (i = 0; i < numfields; i++) { + SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes); + ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes); + if (i > 10) { + ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields); + break; + } + } + SQLFreeHandle (SQL_HANDLE_STMT, stmt); + return NULL; + } + + return stmt; +} + +#define LENGTHEN_BUF1(size) \ + do { \ + /* Lengthen buffer, if necessary */ \ + if (ast_str_strlen(sql) + size + 1 > ast_str_size(sql)) { \ + if (ast_str_make_space(&sql, ((ast_str_size(sql) + size + 1) / 512 + 1) * 512) != 0) { \ + ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CEL '%s:%s' failed.\n", tableptr->connection, tableptr->table); \ + ast_free(sql); \ + ast_free(sql2); \ + AST_RWLIST_UNLOCK(&odbc_tables); \ + return; \ + } \ + } \ + } while (0) + +#define LENGTHEN_BUF2(size) \ + do { \ + if (ast_str_strlen(sql2) + size + 1 > ast_str_size(sql2)) { \ + if (ast_str_make_space(&sql2, ((ast_str_size(sql2) + size + 3) / 512 + 1) * 512) != 0) { \ + ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CEL '%s:%s' failed.\n", tableptr->connection, tableptr->table); \ + ast_free(sql); \ + ast_free(sql2); \ + AST_RWLIST_UNLOCK(&odbc_tables); \ + return; \ + } \ + } \ + } while (0) + +static void odbc_log(const struct ast_event *event, void *userdata) +{ + struct tables *tableptr; + struct columns *entry; + struct odbc_obj *obj; + struct ast_str *sql = ast_str_create(maxsize), *sql2 = ast_str_create(maxsize2); + char *tmp; + char colbuf[1024], *colptr; + SQLHSTMT stmt = NULL; + SQLLEN rows = 0; + struct ast_cel_event_record record = { + .version = AST_CEL_EVENT_RECORD_VERSION, + }; + + if (ast_cel_fill_record(event, &record)) { + return; + } + + if (!sql || !sql2) { + if (sql) + ast_free(sql); + if (sql2) + ast_free(sql2); + return; + } + + if (AST_RWLIST_RDLOCK(&odbc_tables)) { + ast_log(LOG_ERROR, "Unable to lock table list. Insert CEL(s) failed.\n"); + ast_free(sql); + ast_free(sql2); + return; + } + + AST_LIST_TRAVERSE(&odbc_tables, tableptr, list) { + int first = 1; + ast_str_set(&sql, 0, "INSERT INTO %s (", tableptr->table); + ast_str_set(&sql2, 0, " VALUES ("); + + /* No need to check the connection now; we'll handle any failure in prepare_and_execute */ + if (!(obj = ast_odbc_request_obj(tableptr->connection, 0))) { + ast_log(LOG_WARNING, "cel_adaptive_odbc: Unable to retrieve database handle for '%s:%s'. CEL failed: %s\n", tableptr->connection, tableptr->table, ast_str_buffer(sql)); + continue; + } + + AST_LIST_TRAVERSE(&(tableptr->columns), entry, list) { + int datefield = 0; + if (strcasecmp(entry->celname, "eventtime") == 0) { + datefield = 1; + } + + /* Check if we have a similarly named variable */ + if (entry->staticvalue) { + colptr = ast_strdupa(entry->staticvalue); + } else if (datefield) { + struct timeval date_tv = record.event_time; + struct ast_tm tm = { 0, }; + ast_localtime(&date_tv, &tm, tableptr->usegmtime ? "UTC" : NULL); + ast_strftime(colbuf, sizeof(colbuf), "%Y-%m-%d %H:%M:%S", &tm); + colptr = colbuf; + } else { + if (strcmp(entry->celname, "userdeftype") == 0) { + strncpy(colbuf, record.user_defined_name, sizeof(colbuf)); + } else if (strcmp(entry->celname, "cid_name") == 0) { + strncpy(colbuf, record.caller_id_name, sizeof(colbuf)); + } else if (strcmp(entry->celname, "cid_num") == 0) { + strncpy(colbuf, record.caller_id_num, sizeof(colbuf)); + } else if (strcmp(entry->celname, "cid_ani") == 0) { + strncpy(colbuf, record.caller_id_ani, sizeof(colbuf)); + } else if (strcmp(entry->celname, "cid_rdnis") == 0) { + strncpy(colbuf, record.caller_id_rdnis, sizeof(colbuf)); + } else if (strcmp(entry->celname, "cid_dnid") == 0) { + strncpy(colbuf, record.caller_id_dnid, sizeof(colbuf)); + } else if (strcmp(entry->celname, "exten") == 0) { + strncpy(colbuf, record.extension, sizeof(colbuf)); + } else if (strcmp(entry->celname, "context") == 0) { + strncpy(colbuf, record.context, sizeof(colbuf)); + } else if (strcmp(entry->celname, "channame") == 0) { + strncpy(colbuf, record.channel_name, sizeof(colbuf)); + } else if (strcmp(entry->celname, "appname") == 0) { + strncpy(colbuf, record.application_name, sizeof(colbuf)); + } else if (strcmp(entry->celname, "appdata") == 0) { + strncpy(colbuf, record.application_data, sizeof(colbuf)); + } else if (strcmp(entry->celname, "accountcode") == 0) { + strncpy(colbuf, record.account_code, sizeof(colbuf)); + } else if (strcmp(entry->celname, "peeraccount") == 0) { + strncpy(colbuf, record.peer_account, sizeof(colbuf)); + } else if (strcmp(entry->celname, "uniqueid") == 0) { + strncpy(colbuf, record.unique_id, sizeof(colbuf)); + } else if (strcmp(entry->celname, "linkedid") == 0) { + strncpy(colbuf, record.linked_id, sizeof(colbuf)); + } else if (strcmp(entry->celname, "userfield") == 0) { + strncpy(colbuf, record.user_field, sizeof(colbuf)); + } else if (strcmp(entry->celname, "peer") == 0) { + strncpy(colbuf, record.peer, sizeof(colbuf)); + } else if (strcmp(entry->celname, "amaflags") == 0) { + snprintf(colbuf, sizeof(colbuf), "%d", record.amaflag); + } else { + colbuf[0] = 0; + } + colptr = colbuf; + } + + if (colptr) { + /* Check first if the column filters this entry. Note that this + * is very specifically NOT ast_strlen_zero(), because the filter + * could legitimately specify that the field is blank, which is + * different from the field being unspecified (NULL). */ + if (entry->filtervalue && strcasecmp(colptr, entry->filtervalue) != 0) { + ast_verb(4, "CEL column '%s' with value '%s' does not match filter of" + " '%s'. Cancelling this CEL.\n", + entry->celname, colptr, entry->filtervalue); + goto early_release; + } + + /* Only a filter? */ + if (ast_strlen_zero(entry->name)) + continue; + + LENGTHEN_BUF1(strlen(entry->name)); + + switch (entry->type) { + case SQL_CHAR: + case SQL_VARCHAR: + case SQL_LONGVARCHAR: + case SQL_BINARY: + case SQL_VARBINARY: + case SQL_LONGVARBINARY: + case SQL_GUID: + /* For these two field names, get the rendered form, instead of the raw + * form (but only when we're dealing with a character-based field). + */ + if (strcasecmp(entry->name, "eventtype") == 0) { + snprintf(colbuf, sizeof(colbuf), "%s", record.event_name); + } + + /* Truncate too-long fields */ + if (entry->type != SQL_GUID) { + if (strlen(colptr) > entry->octetlen) { + colptr[entry->octetlen] = '\0'; + } + } + + ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name); + LENGTHEN_BUF2(strlen(colptr)); + + /* Encode value, with escaping */ + ast_str_append(&sql2, 0, "%s'", first ? "" : ","); + for (tmp = colptr; *tmp; tmp++) { + if (*tmp == '\'') { + ast_str_append(&sql2, 0, "''"); + } else if (*tmp == '\\' && ast_odbc_backslash_is_escape(obj)) { + ast_str_append(&sql2, 0, "\\\\"); + } else { + ast_str_append(&sql2, 0, "%c", *tmp); + } + } + ast_str_append(&sql2, 0, "'"); + break; + case SQL_TYPE_DATE: + { + int year = 0, month = 0, day = 0; + if (sscanf(colptr, "%d-%d-%d", &year, &month, &day) != 3 || year <= 0 || + month <= 0 || month > 12 || day < 0 || day > 31 || + ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) || + (month == 2 && year % 400 == 0 && day > 29) || + (month == 2 && year % 100 == 0 && day > 28) || + (month == 2 && year % 4 == 0 && day > 29) || + (month == 2 && year % 4 != 0 && day > 28)) { + ast_log(LOG_WARNING, "CEL variable %s is not a valid date ('%s').\n", entry->name, colptr); + continue; + } + + if (year > 0 && year < 100) { + year += 2000; + } + + ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name); + LENGTHEN_BUF2(17); + ast_str_append(&sql2, 0, "%s{ d '%04d-%02d-%02d' }", first ? "" : ",", year, month, day); + } + break; + case SQL_TYPE_TIME: + { + int hour = 0, minute = 0, second = 0; + int count = sscanf(colptr, "%d:%d:%d", &hour, &minute, &second); + + if ((count != 2 && count != 3) || hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > 59) { + ast_log(LOG_WARNING, "CEL variable %s is not a valid time ('%s').\n", entry->name, colptr); + continue; + } + + ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name); + LENGTHEN_BUF2(15); + ast_str_append(&sql2, 0, "%s{ t '%02d:%02d:%02d' }", first ? "" : ",", hour, minute, second); + } + break; + case SQL_TYPE_TIMESTAMP: + case SQL_TIMESTAMP: + { + int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0; + int count = sscanf(colptr, "%d-%d-%d %d:%d:%d", &year, &month, &day, &hour, &minute, &second); + + if ((count != 3 && count != 5 && count != 6) || year <= 0 || + month <= 0 || month > 12 || day < 0 || day > 31 || + ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) || + (month == 2 && year % 400 == 0 && day > 29) || + (month == 2 && year % 100 == 0 && day > 28) || + (month == 2 && year % 4 == 0 && day > 29) || + (month == 2 && year % 4 != 0 && day > 28) || + hour > 23 || minute > 59 || second > 59 || hour < 0 || minute < 0 || second < 0) { + ast_log(LOG_WARNING, "CEL variable %s is not a valid timestamp ('%s').\n", entry->name, colptr); + continue; + } + + if (year > 0 && year < 100) { + year += 2000; + } + + ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name); + LENGTHEN_BUF2(26); + ast_str_append(&sql2, 0, "%s{ ts '%04d-%02d-%02d %02d:%02d:%02d' }", first ? "" : ",", year, month, day, hour, minute, second); + } + break; + case SQL_INTEGER: + { + int integer = 0; + if (strcasecmp(entry->name, "eventtype") == 0) { + integer = (int) record.event_type; + } else if (sscanf(colptr, "%d", &integer) != 1) { + ast_log(LOG_WARNING, "CEL variable %s is not an integer.\n", entry->name); + continue; + } + + ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name); + LENGTHEN_BUF2(12); + ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer); + } + break; + case SQL_BIGINT: + { + long long integer = 0; + if (strcasecmp(entry->name, "eventtype") == 0) { + integer = (long long) record.event_type; + } else if (sscanf(colptr, "%lld", &integer) != 1) { + ast_log(LOG_WARNING, "CEL variable %s is not an integer.\n", entry->name); + continue; + } + + ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name); + LENGTHEN_BUF2(24); + ast_str_append(&sql2, 0, "%s%lld", first ? "" : ",", integer); + } + break; + case SQL_SMALLINT: + { + short integer = 0; + if (strcasecmp(entry->name, "eventtype") == 0) { + integer = (short) record.event_type; + } else if (sscanf(colptr, "%hd", &integer) != 1) { + ast_log(LOG_WARNING, "CEL variable %s is not an integer.\n", entry->name); + continue; + } + + ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name); + LENGTHEN_BUF2(6); + ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer); + } + break; + case SQL_TINYINT: + { + char integer = 0; + if (strcasecmp(entry->name, "eventtype") == 0) { + integer = (char) record.event_type; + } else if (sscanf(colptr, "%hhd", &integer) != 1) { + ast_log(LOG_WARNING, "CEL variable %s is not an integer.\n", entry->name); + continue; + } + + ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name); + LENGTHEN_BUF2(4); + ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer); + } + break; + case SQL_BIT: + { + char integer = 0; + if (strcasecmp(entry->name, "eventtype") == 0) { + integer = (char) record.event_type; + } else if (sscanf(colptr, "%hhd", &integer) != 1) { + ast_log(LOG_WARNING, "CEL variable %s is not an integer.\n", entry->name); + continue; + } + if (integer != 0) + integer = 1; + + ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name); + LENGTHEN_BUF2(2); + ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer); + } + break; + case SQL_NUMERIC: + case SQL_DECIMAL: + { + double number = 0.0; + if (strcasecmp(entry->name, "eventtype") == 0) { + number = (double)record.event_type; + } else if (sscanf(colptr, "%lf", &number) != 1) { + ast_log(LOG_WARNING, "CEL variable %s is not an numeric type.\n", entry->name); + continue; + } + + ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name); + LENGTHEN_BUF2(entry->decimals); + ast_str_append(&sql2, 0, "%s%*.*lf", first ? "" : ",", entry->decimals, entry->radix, number); + } + break; + case SQL_FLOAT: + case SQL_REAL: + case SQL_DOUBLE: + { + double number = 0.0; + if (strcasecmp(entry->name, "eventtype") == 0) { + number = (double) record.event_type; + } else if (sscanf(colptr, "%lf", &number) != 1) { + ast_log(LOG_WARNING, "CEL variable %s is not an numeric type.\n", entry->name); + continue; + } + + ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name); + LENGTHEN_BUF2(entry->decimals); + ast_str_append(&sql2, 0, "%s%lf", first ? "" : ",", number); + } + break; + default: + ast_log(LOG_WARNING, "Column type %d (field '%s:%s:%s') is unsupported at this time.\n", entry->type, tableptr->connection, tableptr->table, entry->name); + continue; + } + first = 0; + } + } + + /* Concatenate the two constructed buffers */ + LENGTHEN_BUF1(ast_str_strlen(sql2)); + ast_str_append(&sql, 0, ")"); + ast_str_append(&sql2, 0, ")"); + ast_str_append(&sql, 0, "%s", ast_str_buffer(sql2)); + + ast_verb(11, "[%s]\n", ast_str_buffer(sql)); + + stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, ast_str_buffer(sql)); + if (stmt) { + SQLRowCount(stmt, &rows); + SQLFreeHandle(SQL_HANDLE_STMT, stmt); + } + if (rows == 0) { + ast_log(LOG_WARNING, "cel_adaptive_odbc: Insert failed on '%s:%s'. CEL failed: %s\n", tableptr->connection, tableptr->table, ast_str_buffer(sql)); + } +early_release: + ast_odbc_release_obj(obj); + } + AST_RWLIST_UNLOCK(&odbc_tables); + + /* Next time, just allocate buffers that are that big to start with. */ + if (ast_str_strlen(sql) > maxsize) { + maxsize = ast_str_strlen(sql); + } + if (ast_str_strlen(sql2) > maxsize2) { + maxsize2 = ast_str_strlen(sql2); + } + + ast_free(sql); + ast_free(sql2); +} + +static int unload_module(void) +{ + if (event_sub) { + event_sub = ast_event_unsubscribe(event_sub); + } + if (AST_RWLIST_WRLOCK(&odbc_tables)) { + event_sub = ast_event_subscribe(AST_EVENT_CEL, odbc_log, "Adaptive ODBC CEL backend", NULL, AST_EVENT_IE_END); + if (!event_sub) { + ast_log(LOG_ERROR, "cel_adaptive_odbc: Unable to subscribe to CEL events\n"); + } + ast_log(LOG_ERROR, "Unable to lock column list. Unload failed.\n"); + return -1; + } + + free_config(); + AST_RWLIST_UNLOCK(&odbc_tables); + return 0; +} + +static int load_module(void) +{ + if (AST_RWLIST_WRLOCK(&odbc_tables)) { + ast_log(LOG_ERROR, "Unable to lock column list. Load failed.\n"); + return 0; + } + load_config(); + AST_RWLIST_UNLOCK(&odbc_tables); + event_sub = ast_event_subscribe(AST_EVENT_CEL, odbc_log, "Adaptive ODBC CEL backend", NULL, AST_EVENT_IE_END); + if (!event_sub) { + ast_log(LOG_ERROR, "cel_odbc: Unable to subscribe to CEL events\n"); + } + return AST_MODULE_LOAD_SUCCESS; +} + +static int reload(void) +{ + if (AST_RWLIST_WRLOCK(&odbc_tables)) { + ast_log(LOG_ERROR, "Unable to lock column list. Reload failed.\n"); + return -1; + } + + free_config(); + load_config(); + AST_RWLIST_UNLOCK(&odbc_tables); + return AST_MODULE_LOAD_SUCCESS; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Adaptive ODBC CEL backend", + .load = load_module, + .unload = unload_module, + .reload = reload, +); + diff --git a/cel/cel_custom.c b/cel/cel_custom.c new file mode 100644 index 000000000..37a741935 --- /dev/null +++ b/cel/cel_custom.c @@ -0,0 +1,216 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2009, Digium, Inc. + * + * Steve Murphy <murf@digium.com> + * much borrowed from cdr code (cdr_custom.c), author Mark Spencer + * + * 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 Custom Comma Separated Value CEL records. + * + * \author Steve Murphy <murf@digium.com> + * + * \arg See also \ref AstCEL + * + * Logs in LOG_DIR/cel_custom + * \ingroup cel_drivers + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/paths.h" +#include "asterisk/channel.h" +#include "asterisk/cel.h" +#include "asterisk/module.h" +#include "asterisk/config.h" +#include "asterisk/pbx.h" +#include "asterisk/utils.h" +#include "asterisk/lock.h" +#include "asterisk/threadstorage.h" +#include "asterisk/strings.h" + +#define CUSTOM_LOG_DIR "/cel_custom" +#define CONFIG "cel_custom.conf" + +AST_THREADSTORAGE(custom_buf); + +static const char name[] = "cel-custom"; + +struct cel_config { + AST_DECLARE_STRING_FIELDS( + AST_STRING_FIELD(filename); + AST_STRING_FIELD(format); + ); + ast_mutex_t lock; + AST_RWLIST_ENTRY(cel_config) list; +}; + +static struct ast_event_sub *event_sub = NULL; + +static AST_RWLIST_HEAD_STATIC(sinks, cel_config); + +static void free_config(void) +{ + struct cel_config *sink; + while ((sink = AST_RWLIST_REMOVE_HEAD(&sinks, list))) { + ast_mutex_destroy(&sink->lock); + ast_free(sink); + } +} + +static int load_config(void) +{ + struct ast_config *cfg; + struct ast_variable *var; + struct ast_flags config_flags = { 0 }; + int res = 0; + + cfg = ast_config_load(CONFIG, config_flags); + if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) { + ast_log(LOG_ERROR, "Unable to load " CONFIG ". Not logging CEL to custom CSVs.\n"); + return -1; + } + + var = ast_variable_browse(cfg, "mappings"); + while (var) { + if (!ast_strlen_zero(var->name) && !ast_strlen_zero(var->value)) { + struct cel_config *sink = ast_calloc_with_stringfields(1, struct cel_config, 1024); + + if (!sink) { + ast_log(LOG_ERROR, "Unable to allocate memory for configuration settings.\n"); + res = -2; + break; + } + + ast_string_field_build(sink, format, "%s\n", var->value); + ast_string_field_build(sink, filename, "%s/%s/%s", ast_config_AST_LOG_DIR, name, var->name); + ast_mutex_init(&sink->lock); + + AST_RWLIST_INSERT_TAIL(&sinks, sink, list); + } else { + ast_log(LOG_NOTICE, "Mapping must have both a filename and a format at line %d\n", var->lineno); + } + var = var->next; + } + ast_config_destroy(cfg); + + return res; +} + +static void custom_log(const struct ast_event *event, void *userdata) +{ + struct ast_channel *dummy; + struct ast_str *str; + struct cel_config *config; + + /* Batching saves memory management here. Otherwise, it's the same as doing an allocation and free each time. */ + if (!(str = ast_str_thread_get(&custom_buf, 16))) { + return; + } + + dummy = ast_cel_fabricate_channel_from_event(event); + + if (!dummy) { + ast_log(LOG_ERROR, "Unable to fabricate channel from CEL event.\n"); + return; + } + + AST_RWLIST_RDLOCK(&sinks); + + AST_LIST_TRAVERSE(&sinks, config, list) { + FILE *out; + + ast_str_substitute_variables(&str, 0, dummy, config->format); + + /* Even though we have a lock on the list, we could be being chased by + another thread and this lock ensures that we won't step on anyone's + toes. Once each CEL backend gets it's own thread, this lock can be + removed. */ + ast_mutex_lock(&config->lock); + + /* Because of the absolutely unconditional need for the + highest reliability possible in writing billing records, + we open write and close the log file each time */ + if ((out = fopen(config->filename, "a"))) { + fputs(ast_str_buffer(str), out); + fflush(out); /* be particularly anal here */ + fclose(out); + } else { + ast_log(LOG_ERROR, "Unable to re-open master file %s : %s\n", config->filename, strerror(errno)); + } + + ast_mutex_unlock(&config->lock); + } + + AST_RWLIST_UNLOCK(&sinks); + + ast_channel_release(dummy); +} + +static int unload_module(void) +{ + if (event_sub) { + event_sub = ast_event_unsubscribe(event_sub); + } + + if (AST_RWLIST_WRLOCK(&sinks)) { + event_sub = ast_event_subscribe(AST_EVENT_CEL, custom_log, "CEL Custom CSV Logging", + NULL, AST_EVENT_IE_END); + ast_log(LOG_ERROR, "Unable to lock sink list. Unload failed.\n"); + return -1; + } + + free_config(); + AST_RWLIST_UNLOCK(&sinks); + return 0; +} + +static enum ast_module_load_result load_module(void) +{ + if (AST_RWLIST_WRLOCK(&sinks)) { + ast_log(LOG_ERROR, "Unable to lock sink list. Load failed.\n"); + return AST_MODULE_LOAD_FAILURE; + } + + load_config(); + AST_RWLIST_UNLOCK(&sinks); + + event_sub = ast_event_subscribe(AST_EVENT_CEL, custom_log, "CEL Custom CSV Logging", + NULL, AST_EVENT_IE_END); + return AST_MODULE_LOAD_SUCCESS; +} + +static int reload(void) +{ + if (AST_RWLIST_WRLOCK(&sinks)) { + ast_log(LOG_ERROR, "Unable to lock sink list. Load failed.\n"); + return AST_MODULE_LOAD_FAILURE; + } + + free_config(); + load_config(); + AST_RWLIST_UNLOCK(&sinks); + return AST_MODULE_LOAD_SUCCESS; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Customizable Comma Separated Values CEL Backend", + .load = load_module, + .unload = unload_module, + .reload = reload, + ); + diff --git a/cel/cel_manager.c b/cel/cel_manager.c new file mode 100644 index 000000000..a57c31ebe --- /dev/null +++ b/cel/cel_manager.c @@ -0,0 +1,175 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2008 - 2009, Digium, Inc. + * + * Steve Murphy <murf@digium.com> + * who freely borrowed code from the cdr equivalents + * (see cdr/cdr_manager.c) + * + * 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 Asterisk Channel Event records. + * + * See also + * \arg \ref AstCDR + * \arg \ref AstAMI + * \arg \ref Config_ami + * \ingroup cel_drivers + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/channel.h" +#include "asterisk/cel.h" +#include "asterisk/module.h" +#include "asterisk/logger.h" +#include "asterisk/utils.h" +#include "asterisk/manager.h" +#include "asterisk/config.h" + +static const char DATE_FORMAT[] = "%Y-%m-%d %T"; + +static const char CONF_FILE[] = "cel.conf"; + +static int enablecel; + +static struct ast_event_sub *event_sub; + +static void manager_log(const struct ast_event *event, void *userdata) +{ + struct ast_tm timeresult; + char start_time[80] = ""; + struct ast_cel_event_record record = { + .version = AST_CEL_EVENT_RECORD_VERSION, + }; + + if (ast_cel_fill_record(event, &record)) { + return; + } + + if (!enablecel) { + return; + } + + ast_localtime(&record.event_time, &timeresult, NULL); + ast_strftime(start_time, sizeof(start_time), DATE_FORMAT, &timeresult); + + manager_event(EVENT_FLAG_CALL, "CEL", + "EventName: %s\r\n" + "AccountCode: %s\r\n" + "CallerIDnum: %s\r\n" + "CallerIDname: %s\r\n" + "CallerIDani: %s\r\n" + "CallerIDrdnis: %s\r\n" + "CallerIDdnid: %s\r\n" + "Exten: %s\r\n" + "Context: %s\r\n" + "Channel: %s\r\n" + "Application: %s\r\n" + "AppData: %s\r\n" + "EventTime: %s\r\n" + "AMAFlags: %s\r\n" + "UniqueID: %s\r\n" + "LinkedID: %s\r\n" + "Userfield: %s\r\n" + "Peer: %s\r\n", + record.event_name, record.account_code, record.caller_id_num, + record.caller_id_name, record.caller_id_ani, record.caller_id_rdnis, + record.caller_id_dnid, record.extension, record.context, record.channel_name, + record.application_name, record.application_data, start_time, + ast_cel_get_ama_flag_name(record.amaflag), record.unique_id, record.linked_id, + record.user_field, record.peer); +} + +static int load_config(int reload) +{ + const char *cat = NULL; + struct ast_config *cfg; + struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; + struct ast_variable *v; + int newenablecel = 0; + + cfg = ast_config_load(CONF_FILE, config_flags); + if (cfg == CONFIG_STATUS_FILEUNCHANGED) { + return 0; + } + + if (!cfg) { + ast_log(LOG_WARNING, "Failed to load configuration file. CEL manager Module not activated.\n"); + enablecel = 0; + return -1; + } + + while ((cat = ast_category_browse(cfg, cat))) { + if (strcasecmp(cat, "manager")) { + continue; + } + + for (v = ast_variable_browse(cfg, cat); v; v = v->next) { + if (!strcasecmp(v->name, "enabled")) { + newenablecel = ast_true(v->value); + } else { + ast_log(LOG_NOTICE, "Unknown option '%s' specified " + "for cel_manager.\n", v->name); + } + } + } + + ast_config_destroy(cfg); + + if (enablecel && !newenablecel) { + if (event_sub) { + event_sub = ast_event_unsubscribe(event_sub); + } + } else if (!enablecel && newenablecel) { + event_sub = ast_event_subscribe(AST_EVENT_CEL, manager_log, "Manager Event Logging", NULL, AST_EVENT_IE_END); + if (!event_sub) { + ast_log(LOG_ERROR, "Unable to register Asterisk Call Manager CEL handling\n"); + } + } + enablecel = newenablecel; + + return 0; +} + +static int unload_module(void) +{ + if (event_sub) { + event_sub = ast_event_unsubscribe(event_sub); + } + return 0; +} + +static int load_module(void) +{ + if (load_config(0)) { + return AST_MODULE_LOAD_DECLINE; + } + + return AST_MODULE_LOAD_SUCCESS; +} + +static int reload(void) +{ + return load_config(1); +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Asterisk Manager Interface CEL Backend", + .load = load_module, + .unload = unload_module, + .reload = reload, +); diff --git a/cel/cel_pgsql.c b/cel/cel_pgsql.c new file mode 100644 index 000000000..8b10261ee --- /dev/null +++ b/cel/cel_pgsql.c @@ -0,0 +1,565 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2008 + * + * Steve Murphy - adapted to CEL, from: + * Matthew D. Hardeman <mhardemn@papersoft.com> + * Adapted from the MySQL CDR logger originally by James Sharp + * + * Modified April, 2007; Dec, 2008 + * Steve Murphy <murf@digium.com> + + * Modified September 2003 + * Matthew D. Hardeman <mhardemn@papersoft.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 PostgreSQL CEL logger + * + * \author Steve Murphy <murf@digium.com> + * \extref PostgreSQL http://www.postgresql.org/ + * + * See also + * \arg \ref Config_cel + * \arg http://www.postgresql.org/ + * \ingroup cel_drivers + */ + +/*** MODULEINFO + <depend>pgsql</depend> + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include <libpq-fe.h> + +#include "asterisk/config.h" +#include "asterisk/options.h" +#include "asterisk/channel.h" +#include "asterisk/cel.h" +#include "asterisk/module.h" +#include "asterisk/logger.h" +#include "asterisk.h" + +#define DATE_FORMAT "%Y-%m-%d %T" + +static char *config = "cel_pgsql.conf"; +static char *pghostname = NULL, *pgdbname = NULL, *pgdbuser = NULL, *pgpassword = NULL, *pgdbport = NULL, *table = NULL; +static int connected = 0; +static int maxsize = 512, maxsize2 = 512; + +AST_MUTEX_DEFINE_STATIC(pgsql_lock); + +static PGconn *conn = NULL; +static PGresult *result = NULL; +static struct ast_event_sub *event_sub = NULL; + +struct columns { + char *name; + char *type; + int len; + unsigned int notnull:1; + unsigned int hasdefault:1; + AST_RWLIST_ENTRY(columns) list; +}; + +static AST_RWLIST_HEAD_STATIC(psql_columns, columns); + +#define LENGTHEN_BUF1(size) \ + do { \ + /* Lengthen buffer, if necessary */ \ + if (ast_str_strlen(sql) + size + 1 > ast_str_size(sql)) { \ + if (ast_str_make_space(&sql, ((ast_str_size(sql) + size + 3) / 512 + 1) * 512) != 0) { \ + ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CDR failed.\n"); \ + ast_free(sql); \ + ast_free(sql2); \ + AST_RWLIST_UNLOCK(&psql_columns); \ + return; \ + } \ + } \ + } while (0) + +#define LENGTHEN_BUF2(size) \ + do { \ + if (ast_str_strlen(sql2) + size + 1 > ast_str_size(sql2)) { \ + if (ast_str_make_space(&sql2, ((ast_str_size(sql2) + size + 3) / 512 + 1) * 512) != 0) { \ + ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CDR failed.\n"); \ + ast_free(sql); \ + ast_free(sql2); \ + AST_RWLIST_UNLOCK(&psql_columns); \ + return; \ + } \ + } \ + } while (0) + +static void pgsql_log(const struct ast_event *event, void *userdata) +{ + struct ast_tm tm; + char timestr[128]; + char *pgerror; + struct ast_cel_event_record record = { + .version = AST_CEL_EVENT_RECORD_VERSION, + }; + + if (ast_cel_fill_record(event, &record)) { + return; + } + + ast_mutex_lock(&pgsql_lock); + + ast_localtime(&record.event_time, &tm, NULL); + ast_strftime(timestr, sizeof(timestr), DATE_FORMAT, &tm); + + if ((!connected) && pghostname && pgdbuser && pgpassword && pgdbname) { + conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword); + if (PQstatus(conn) != CONNECTION_BAD) { + connected = 1; + } else { + pgerror = PQerrorMessage(conn); + ast_log(LOG_ERROR, "cel_pgsql: Unable to connect to database server %s. Calls will not be logged!\n", pghostname); + ast_log(LOG_ERROR, "cel_pgsql: Reason: %s\n", pgerror); + PQfinish(conn); + conn = NULL; + } + } + if (connected) { + struct columns *cur; + struct ast_str *sql = ast_str_create(maxsize), *sql2 = ast_str_create(maxsize2); + char buf[257], escapebuf[513]; + const char *value; + int first = 1; + + if (!sql || !sql2) { + if (sql) { + ast_free(sql); + } + if (sql2) { + ast_free(sql2); + } + return; + } + + ast_str_set(&sql, 0, "INSERT INTO %s (", table); + ast_str_set(&sql2, 0, " VALUES ("); + +#define SEP (first ? "" : ",") + + AST_RWLIST_RDLOCK(&psql_columns); + AST_RWLIST_TRAVERSE(&psql_columns, cur, list) { + LENGTHEN_BUF1(strlen(cur->name) + 2); + ast_str_append(&sql, 0, "%s\"%s\"", first ? "" : ",", cur->name); + + if (strcmp(cur->name, "eventtime") == 0) { + if (strncmp(cur->type, "int", 3) == 0) { + LENGTHEN_BUF2(13); + ast_str_append(&sql2, 0, "%s%ld", SEP, record.event_time.tv_sec); + } else if (strncmp(cur->type, "float", 5) == 0) { + LENGTHEN_BUF2(31); + ast_str_append(&sql2, 0, "%s%f", + SEP, + (double) record.event_time.tv_sec + + (double) record.event_time.tv_usec / 1000000.0); + } else { + /* char, hopefully */ + LENGTHEN_BUF2(31); + ast_localtime(&record.event_time, &tm, NULL); + ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm); + ast_str_append(&sql2, 0, "%s'%s'", SEP, buf); + } + } else if (strcmp(cur->name, "eventtype") == 0) { + if (cur->type[0] == 'i') { + /* Get integer, no need to escape anything */ + LENGTHEN_BUF2(5); + ast_str_append(&sql2, 0, "%s%d", SEP, (int) record.event_type); + } else if (strncmp(cur->type, "float", 5) == 0) { + LENGTHEN_BUF2(31); + ast_str_append(&sql2, 0, "%s%f", SEP, (double) record.event_type); + } else { + /* Char field, probably */ + LENGTHEN_BUF2(strlen(record.event_name) + 1); + ast_str_append(&sql2, 0, "%s'%s'", SEP, record.event_name); + } + } else if (strcmp(cur->name, "amaflags") == 0) { + if (strncmp(cur->type, "int", 3) == 0) { + /* Integer, no need to escape anything */ + LENGTHEN_BUF2(13); + ast_str_append(&sql2, 0, "%s%d", SEP, record.amaflag); + } else { + /* Although this is a char field, there are no special characters in the values for these fields */ + LENGTHEN_BUF2(31); + ast_str_append(&sql2, 0, "%s'%d'", SEP, record.amaflag); + } + } else { + /* Arbitrary field, could be anything */ + if (strcmp(cur->name, "userdeftype") == 0) { + value = record.user_defined_name; + } else if (strcmp(cur->name, "cid_name") == 0) { + value = record.caller_id_name; + } else if (strcmp(cur->name, "cid_num") == 0) { + value = record.caller_id_num; + } else if (strcmp(cur->name, "cid_ani") == 0) { + value = record.caller_id_ani; + } else if (strcmp(cur->name, "cid_rdnis") == 0) { + value = record.caller_id_rdnis; + } else if (strcmp(cur->name, "cid_dnid") == 0) { + value = record.caller_id_dnid; + } else if (strcmp(cur->name, "exten") == 0) { + value = record.extension; + } else if (strcmp(cur->name, "context") == 0) { + value = record.context; + } else if (strcmp(cur->name, "channame") == 0) { + value = record.channel_name; + } else if (strcmp(cur->name, "appname") == 0) { + value = record.application_name; + } else if (strcmp(cur->name, "appdata") == 0) { + value = record.application_data; + } else if (strcmp(cur->name, "accountcode") == 0) { + value = record.account_code; + } else if (strcmp(cur->name, "peeraccount") == 0) { + value = record.peer_account; + } else if (strcmp(cur->name, "uniqueid") == 0) { + value = record.unique_id; + } else if (strcmp(cur->name, "linkedid") == 0) { + value = record.linked_id; + } else if (strcmp(cur->name, "userfield") == 0) { + value = record.user_field; + } else if (strcmp(cur->name, "peer") == 0) { + value = record.peer; + } else { + value = ""; + } + if (strncmp(cur->type, "int", 3) == 0) { + long long whatever; + if (value && sscanf(value, "%lld", &whatever) == 1) { + LENGTHEN_BUF2(26); + ast_str_append(&sql2, 0, "%s%lld", SEP, whatever); + } else { + LENGTHEN_BUF2(2); + ast_str_append(&sql2, 0, "%s0", SEP); + } + } else if (strncmp(cur->type, "float", 5) == 0) { + long double whatever; + if (value && sscanf(value, "%Lf", &whatever) == 1) { + LENGTHEN_BUF2(51); + ast_str_append(&sql2, 0, "%s%30Lf", SEP, whatever); + } else { + LENGTHEN_BUF2(2); + ast_str_append(&sql2, 0, "%s0", SEP); + } + /* XXX Might want to handle dates, times, and other misc fields here XXX */ + } else { + if (value) { + PQescapeStringConn(conn, escapebuf, value, strlen(value), NULL); + } else { + escapebuf[0] = '\0'; + } + LENGTHEN_BUF2(strlen(escapebuf) + 3); + ast_str_append(&sql2, 0, "%s'%s'", SEP, escapebuf); + } + } + first = 0; + } + AST_RWLIST_UNLOCK(&psql_columns); + LENGTHEN_BUF1(ast_str_strlen(sql2) + 2); + ast_str_append(&sql, 0, ")%s)", ast_str_buffer(sql2)); + ast_verb(11, "[%s]\n", ast_str_buffer(sql)); + + ast_debug(2, "inserting a CEL record.\n"); + /* Test to be sure we're still connected... */ + /* If we're connected, and connection is working, good. */ + /* Otherwise, attempt reconnect. If it fails... sorry... */ + if (PQstatus(conn) == CONNECTION_OK) { + connected = 1; + } else { + ast_log(LOG_ERROR, "Connection was lost... attempting to reconnect.\n"); + PQreset(conn); + if (PQstatus(conn) == CONNECTION_OK) { + ast_log(LOG_ERROR, "Connection reestablished.\n"); + connected = 1; + } else { + pgerror = PQerrorMessage(conn); + ast_log(LOG_ERROR, "Unable to reconnect to database server %s. Calls will not be logged!\n", pghostname); + ast_log(LOG_ERROR, "Reason: %s\n", pgerror); + PQfinish(conn); + conn = NULL; + connected = 0; + ast_mutex_unlock(&pgsql_lock); + ast_free(sql); + ast_free(sql2); + return; + } + } + result = PQexec(conn, ast_str_buffer(sql)); + if (PQresultStatus(result) != PGRES_COMMAND_OK) { + pgerror = PQresultErrorMessage(result); + ast_log(LOG_ERROR, "Failed to insert call detail record into database!\n"); + ast_log(LOG_ERROR, "Reason: %s\n", pgerror); + ast_log(LOG_ERROR, "Connection may have been lost... attempting to reconnect.\n"); + PQreset(conn); + if (PQstatus(conn) == CONNECTION_OK) { + ast_log(LOG_ERROR, "Connection reestablished.\n"); + connected = 1; + PQclear(result); + result = PQexec(conn, ast_str_buffer(sql)); + if (PQresultStatus(result) != PGRES_COMMAND_OK) { + pgerror = PQresultErrorMessage(result); + ast_log(LOG_ERROR, "HARD ERROR! Attempted reconnection failed. DROPPING CALL RECORD!\n"); + ast_log(LOG_ERROR, "Reason: %s\n", pgerror); + } + } + ast_mutex_unlock(&pgsql_lock); + PQclear(result); + ast_free(sql); + ast_free(sql2); + return; + } + ast_mutex_unlock(&pgsql_lock); + } +} + +static int my_unload_module(void) +{ + struct columns *current; + if (event_sub) { + event_sub = ast_event_unsubscribe(event_sub); + } + if (conn) { + PQfinish(conn); + } + if (pghostname) { + ast_free(pghostname); + } + if (pgdbname) { + ast_free(pgdbname); + } + if (pgdbuser) { + ast_free(pgdbuser); + } + if (pgpassword) { + ast_free(pgpassword); + } + if (pgdbport) { + ast_free(pgdbport); + } + if (table) { + ast_free(table); + } + AST_RWLIST_WRLOCK(&psql_columns); + while ((current = AST_RWLIST_REMOVE_HEAD(&psql_columns, list))) { + ast_free(current); + } + AST_RWLIST_UNLOCK(&psql_columns); + return 0; +} + +static int unload_module(void) +{ + return my_unload_module(); +} + +static int process_my_load_module(struct ast_config *cfg) +{ + struct ast_variable *var; + char *pgerror; + const char *tmp; + PGresult *result; + struct columns *cur; + + if (!(var = ast_variable_browse(cfg, "global"))) { + ast_log(LOG_WARNING,"CEL pgsql config file missing global section.\n"); + return AST_MODULE_LOAD_DECLINE; + } + if (!(tmp = ast_variable_retrieve(cfg,"global","hostname"))) { + ast_log(LOG_WARNING,"PostgreSQL server hostname not specified. Assuming unix socket connection\n"); + tmp = ""; /* connect via UNIX-socket by default */ + } + if (pghostname) + ast_free(pghostname); + if (!(pghostname = ast_strdup(tmp))) { + ast_log(LOG_WARNING,"PostgreSQL Ran out of memory copying host info\n"); + return AST_MODULE_LOAD_DECLINE; + } + if (!(tmp = ast_variable_retrieve(cfg, "global", "dbname"))) { + ast_log(LOG_WARNING,"PostgreSQL database not specified. Assuming asterisk\n"); + tmp = "asteriskceldb"; + } + if (pgdbname) + ast_free(pgdbname); + if (!(pgdbname = ast_strdup(tmp))) { + ast_log(LOG_WARNING,"PostgreSQL Ran out of memory copying dbname info\n"); + return AST_MODULE_LOAD_DECLINE; + } + if (!(tmp = ast_variable_retrieve(cfg, "global", "user"))) { + ast_log(LOG_WARNING,"PostgreSQL database user not specified. Assuming asterisk\n"); + tmp = "asterisk"; + } + if (pgdbuser) + ast_free(pgdbuser); + if (!(pgdbuser = ast_strdup(tmp))) { + ast_log(LOG_WARNING,"PostgreSQL Ran out of memory copying user info\n"); + return AST_MODULE_LOAD_DECLINE; + } + if (!(tmp = ast_variable_retrieve(cfg, "global", "password"))) { + ast_log(LOG_WARNING, "PostgreSQL database password not specified. Assuming blank\n"); + tmp = ""; + } + if (pgpassword) + ast_free(pgpassword); + if (!(pgpassword = ast_strdup(tmp))) { + ast_log(LOG_WARNING,"PostgreSQL Ran out of memory copying password info\n"); + return AST_MODULE_LOAD_DECLINE; + } + if (!(tmp = ast_variable_retrieve(cfg,"global","port"))) { + ast_log(LOG_WARNING,"PostgreSQL database port not specified. Using default 5432.\n"); + tmp = "5432"; + } + if (pgdbport) + ast_free(pgdbport); + if (!(pgdbport = ast_strdup(tmp))) { + ast_log(LOG_WARNING,"PostgreSQL Ran out of memory copying port info\n"); + return AST_MODULE_LOAD_DECLINE; + } + if (!(tmp = ast_variable_retrieve(cfg, "global", "table"))) { + ast_log(LOG_WARNING,"CEL table not specified. Assuming cel\n"); + tmp = "cel"; + } + if (table) + ast_free(table); + if (!(table = ast_strdup(tmp))) { + return AST_MODULE_LOAD_DECLINE; + } + if (option_debug) { + if (ast_strlen_zero(pghostname)) { + ast_debug(3, "cel_pgsql: using default unix socket\n"); + } else { + ast_debug(3, "cel_pgsql: got hostname of %s\n", pghostname); + } + ast_debug(3, "cel_pgsql: got port of %s\n", pgdbport); + ast_debug(3, "cel_pgsql: got user of %s\n", pgdbuser); + ast_debug(3, "cel_pgsql: got dbname of %s\n", pgdbname); + ast_debug(3, "cel_pgsql: got password of %s\n", pgpassword); + ast_debug(3, "cel_pgsql: got sql table name of %s\n", table); + } + + conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword); + if (PQstatus(conn) != CONNECTION_BAD) { + char sqlcmd[512]; + char *fname, *ftype, *flen, *fnotnull, *fdef; + char *tableptr; + int i, rows; + + ast_debug(1, "Successfully connected to PostgreSQL database.\n"); + connected = 1; + + /* Remove any schema name from the table */ + if ((tableptr = strrchr(table, '.'))) { + tableptr++; + } else { + tableptr = table; + } + + /* Query the columns */ + snprintf(sqlcmd, sizeof(sqlcmd), "select a.attname, t.typname, a.attlen, a.attnotnull, d.adsrc from pg_class c, pg_type t, pg_attribute a left outer join pg_attrdef d on a.atthasdef and d.adrelid = a.attrelid and d.adnum = a.attnum where c.oid = a.attrelid and a.atttypid = t.oid and (a.attnum > 0) and c.relname = '%s' order by c.relname, attnum", tableptr); + result = PQexec(conn, sqlcmd); + if (PQresultStatus(result) != PGRES_TUPLES_OK) { + pgerror = PQresultErrorMessage(result); + ast_log(LOG_ERROR, "Failed to query database columns: %s\n", pgerror); + PQclear(result); + unload_module(); + return AST_MODULE_LOAD_DECLINE; + } + + rows = PQntuples(result); + for (i = 0; i < rows; i++) { + fname = PQgetvalue(result, i, 0); + ftype = PQgetvalue(result, i, 1); + flen = PQgetvalue(result, i, 2); + fnotnull = PQgetvalue(result, i, 3); + fdef = PQgetvalue(result, i, 4); + ast_verb(4, "Found column '%s' of type '%s'\n", fname, ftype); + cur = ast_calloc(1, sizeof(*cur) + strlen(fname) + strlen(ftype) + 2); + if (cur) { + sscanf(flen, "%d", &cur->len); + cur->name = (char *)cur + sizeof(*cur); + cur->type = (char *)cur + sizeof(*cur) + strlen(fname) + 1; + strcpy(cur->name, fname); + strcpy(cur->type, ftype); + if (*fnotnull == 't') { + cur->notnull = 1; + } else { + cur->notnull = 0; + } + if (!ast_strlen_zero(fdef)) { + cur->hasdefault = 1; + } else { + cur->hasdefault = 0; + } + AST_RWLIST_INSERT_TAIL(&psql_columns, cur, list); + } + } + PQclear(result); + } else { + pgerror = PQerrorMessage(conn); + ast_log(LOG_ERROR, "cel_pgsql: Unable to connect to database server %s. CALLS WILL NOT BE LOGGED!!\n", pghostname); + ast_log(LOG_ERROR, "cel_pgsql: Reason: %s\n", pgerror); + connected = 0; + } + return AST_MODULE_LOAD_SUCCESS; +} + +static int my_load_module(int reload) +{ + struct ast_config *cfg; + int res; + struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; + + if ((cfg = ast_config_load(config, config_flags)) == NULL || cfg == CONFIG_STATUS_FILEINVALID) { + ast_log(LOG_WARNING, "Unable to load config for PostgreSQL CEL's: %s\n", config); + return AST_MODULE_LOAD_DECLINE; + } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) { + return AST_MODULE_LOAD_SUCCESS; + } + + res = process_my_load_module(cfg); + ast_config_destroy(cfg); + + event_sub = ast_event_subscribe(AST_EVENT_CEL, pgsql_log, "CEL PGSQL backend", NULL, AST_EVENT_IE_END); + + if (!event_sub) { + ast_log(LOG_WARNING, "Unable to subscribe to CEL events for pgsql\n"); + return AST_MODULE_LOAD_DECLINE; + } + + return AST_MODULE_LOAD_SUCCESS; +} + +static int load_module(void) +{ + return my_load_module(0); +} + +static int reload(void) +{ + my_unload_module(); + return my_load_module(1); +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "PostgreSQL CEL Backend", + .load = load_module, + .unload = unload_module, + .reload = reload, +); diff --git a/cel/cel_radius.c b/cel/cel_radius.c new file mode 100644 index 000000000..c44044f6e --- /dev/null +++ b/cel/cel_radius.c @@ -0,0 +1,254 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2005, Digium, Inc. + * + * Mark Spencer <markster@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 RADIUS CEL Support + * \author Philippe Sultan + * \extref The Radius Client Library - http://developer.berlios.de/projects/radiusclient-ng/ + * + * \arg See also \ref AstCEL + * \ingroup cel_drivers + */ + +/*** MODULEINFO + <depend>radius</depend> + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Rev$") + +#include <radiusclient-ng.h> + +#include "asterisk/channel.h" +#include "asterisk/cel.h" +#include "asterisk/module.h" +#include "asterisk/logger.h" +#include "asterisk/utils.h" +#include "asterisk/options.h" + +/*! ISO 8601 standard format */ +#define DATE_FORMAT "%Y-%m-%d %T %z" + +#define VENDOR_CODE 22736 + +enum { + PW_AST_ACCT_CODE = 101, + PW_AST_CIDNUM = 102, + PW_AST_CIDNAME = 103, + PW_AST_CIDANI = 104, + PW_AST_CIDRDNIS = 105, + PW_AST_CIDDNID = 106, + PW_AST_EXTEN = 107, + PW_AST_CONTEXT = 108, + PW_AST_CHANNAME = 109, + PW_AST_APPNAME = 110, + PW_AST_APPDATA = 111, + PW_AST_EVENT_TIME = 112, + PW_AST_AMA_FLAGS = 113, + PW_AST_UNIQUE_ID = 114, + PW_AST_USER_NAME = 115, + PW_AST_LINKED_ID = 116, +}; + +enum { + /*! Log dates and times in UTC */ + RADIUS_FLAG_USEGMTIME = (1 << 0), + /*! Log Unique ID */ + RADIUS_FLAG_LOGUNIQUEID = (1 << 1), + /*! Log User Field */ + RADIUS_FLAG_LOGUSERFIELD = (1 << 2) +}; + +static char *cel_config = "cel.conf"; + +static char radiuscfg[PATH_MAX] = "/etc/radiusclient-ng/radiusclient.conf"; + +static struct ast_flags global_flags = { RADIUS_FLAG_USEGMTIME | RADIUS_FLAG_LOGUNIQUEID | RADIUS_FLAG_LOGUSERFIELD }; + +static rc_handle *rh = NULL; +static struct ast_event_sub *event_sub = NULL; + +#define ADD_VENDOR_CODE(x,y) (rc_avpair_add(rh, send, x, &y, strlen(y), VENDOR_CODE)) + +static int build_radius_record(VALUE_PAIR **send, struct ast_cel_event_record *record) +{ + int recordtype = PW_STATUS_STOP; + struct ast_tm tm; + char timestr[128]; + char *amaflags; + + if (!rc_avpair_add(rh, send, PW_ACCT_STATUS_TYPE, &recordtype, 0, 0)) { + return -1; + } + /* Account code */ + if (!ADD_VENDOR_CODE(PW_AST_ACCT_CODE, record->account_code)) { + return -1; + } + /* Source */ + if (!ADD_VENDOR_CODE(PW_AST_CIDNUM, record->caller_id_num)) { + return -1; + } + /* Destination */ + if (!ADD_VENDOR_CODE(PW_AST_EXTEN, record->extension)) { + return -1; + } + /* Destination context */ + if (!ADD_VENDOR_CODE(PW_AST_CONTEXT, record->context)) { + return -1; + } + /* Caller ID */ + if (!ADD_VENDOR_CODE(PW_AST_CIDNAME, record->caller_id_name)) { + return -1; + } + /* Caller ID ani */ + if (!ADD_VENDOR_CODE(PW_AST_CIDANI, record->caller_id_ani)) { + return -1; + } + /* Caller ID rdnis */ + if (!ADD_VENDOR_CODE(PW_AST_CIDRDNIS, record->caller_id_rdnis)) { + return -1; + } + /* Caller ID dnid */ + if (!ADD_VENDOR_CODE(PW_AST_CIDDNID, record->caller_id_dnid)) { + return -1; + } + /* Channel */ + if (!ADD_VENDOR_CODE(PW_AST_CHANNAME, record->channel_name)) { + return -1; + } + /* Last Application */ + if (!ADD_VENDOR_CODE(PW_AST_APPNAME, record->application_name)) { + return -1; + } + /* Last Data */ + if (!ADD_VENDOR_CODE(PW_AST_APPDATA, record->application_data)) { + return -1; + } + /* Event Time */ + ast_localtime(&record->event_time, &tm, + ast_test_flag(&global_flags, RADIUS_FLAG_USEGMTIME) ? "GMT" : NULL); + ast_strftime(timestr, sizeof(timestr), DATE_FORMAT, &tm); + if (!rc_avpair_add(rh, send, PW_AST_EVENT_TIME, timestr, strlen(timestr), VENDOR_CODE)) { + return -1; + } + /* AMA Flags */ + amaflags = ast_strdupa(ast_cel_get_ama_flag_name(record->amaflag)); + if (!rc_avpair_add(rh, send, PW_AST_AMA_FLAGS, amaflags, strlen(amaflags), VENDOR_CODE)) { + return -1; + } + if (ast_test_flag(&global_flags, RADIUS_FLAG_LOGUNIQUEID)) { + /* Unique ID */ + if (!ADD_VENDOR_CODE(PW_AST_UNIQUE_ID, record->unique_id)) { + return -1; + } + } + /* LinkedID */ + if (!ADD_VENDOR_CODE(PW_AST_LINKED_ID, record->linked_id)) { + return -1; + } + /* Setting Acct-Session-Id & User-Name attributes for proper generation + of Acct-Unique-Session-Id on server side */ + /* Channel */ + if (!rc_avpair_add(rh, send, PW_USER_NAME, &record->channel_name, + strlen(record->channel_name), 0)) { + return -1; + } + return 0; +} + +static void radius_log(const struct ast_event *event, void *userdata) +{ + int result = ERROR_RC; + VALUE_PAIR *send = NULL; + struct ast_cel_event_record record = { + .version = AST_CEL_EVENT_RECORD_VERSION, + }; + + if (ast_cel_fill_record(event, &record)) { + return; + } + + if (build_radius_record(&send, &record)) { + if (option_debug) { + ast_log(LOG_DEBUG, "Unable to create RADIUS record. CEL not recorded!\n"); + } + goto return_cleanup; + } + + result = rc_acct(rh, 0, send); + if (result != OK_RC) { + ast_log(LOG_ERROR, "Failed to record Radius CEL record!\n"); + } + +return_cleanup: + if (send) { + rc_avpair_free(send); + } +} + +static int unload_module(void) +{ + if (event_sub) { + event_sub = ast_event_unsubscribe(event_sub); + } + return AST_MODULE_LOAD_SUCCESS; +} + +static int load_module(void) +{ + struct ast_config *cfg; + struct ast_flags config_flags = { 0 }; + const char *tmp; + + if ((cfg = ast_config_load(cel_config, config_flags))) { + ast_set2_flag(&global_flags, ast_true(ast_variable_retrieve(cfg, "radius", "usegmtime")), RADIUS_FLAG_USEGMTIME); + if ((tmp = ast_variable_retrieve(cfg, "radius", "radiuscfg"))) { + ast_copy_string(radiuscfg, tmp, sizeof(radiuscfg)); + } + ast_config_destroy(cfg); + } else { + return AST_MODULE_LOAD_DECLINE; + } + + /* start logging */ + rc_openlog("asterisk"); + + /* read radiusclient-ng config file */ + if (!(rh = rc_read_config(radiuscfg))) { + ast_log(LOG_NOTICE, "Cannot load radiusclient-ng configuration file %s.\n", radiuscfg); + return AST_MODULE_LOAD_DECLINE; + } + + /* read radiusclient-ng dictionaries */ + if (rc_read_dictionary(rh, rc_conf_str(rh, "dictionary"))) { + ast_log(LOG_NOTICE, "Cannot load radiusclient-ng dictionary file.\n"); + return AST_MODULE_LOAD_DECLINE; + } + + event_sub = ast_event_subscribe(AST_EVENT_CEL, radius_log, "CEL Radius Logging", NULL, AST_EVENT_IE_END); + + if (!event_sub) { + return AST_MODULE_LOAD_DECLINE; + } else { + return AST_MODULE_LOAD_SUCCESS; + } +} + +AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "RADIUS CEL Backend"); diff --git a/cel/cel_sqlite3_custom.c b/cel/cel_sqlite3_custom.c new file mode 100644 index 000000000..c37641ba8 --- /dev/null +++ b/cel/cel_sqlite3_custom.c @@ -0,0 +1,364 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2007, Digium, Inc. + * + * Steve Murphy <murf@digium.com> borrowed code from cdr, + * Mark Spencer <markster@digium.com> and others. + * + * 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 Custom SQLite3 CEL records. + * + * \author Adapted by Steve Murphy <murf@digium.com> from + * Alejandro Rios <alejandro.rios@avatar.com.co> and + * Russell Bryant <russell@digium.com> from + * cdr_mysql_custom by Edward Eastman <ed@dm3.co.uk>, + * and cdr_sqlite by Holger Schurig <hs4233@mail.mn-solutions.de> + * + * + * \arg See also \ref AstCEL + * + * + * \ingroup cel_drivers + */ + +/*** MODULEINFO + <depend>sqlite3</depend> + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include <sqlite3.h> + +#include "asterisk/paths.h" +#include "asterisk/channel.h" +#include "asterisk/cel.h" +#include "asterisk/module.h" +#include "asterisk/config.h" +#include "asterisk/pbx.h" +#include "asterisk/logger.h" +#include "asterisk/utils.h" +#include "asterisk/cli.h" +#include "asterisk/options.h" +#include "asterisk/stringfields.h" + +AST_MUTEX_DEFINE_STATIC(lock); + +static const char config_file[] = "cel_sqlite3_custom.conf"; + +static const char name[] = "cel_sqlite3_custom"; +static sqlite3 *db = NULL; + +static char table[80]; +/*! XXX \bug Handling of this var is crash prone on reloads */ +static char *columns; +static struct ast_event_sub *event_sub = NULL; + +struct values { + char *expression; + AST_LIST_ENTRY(values) list; +}; + +static AST_LIST_HEAD_STATIC(sql_values, values); + +static void free_config(void); + +static int load_column_config(const char *tmp) +{ + char *col = NULL; + char *cols = NULL, *save = NULL; + char *escaped = NULL; + struct ast_str *column_string = NULL; + + if (ast_strlen_zero(tmp)) { + ast_log(LOG_WARNING, "Column names not specified. Module not loaded.\n"); + return -1; + } + if (!(column_string = ast_str_create(1024))) { + ast_log(LOG_ERROR, "Out of memory creating temporary buffer for column list for table '%s.'\n", table); + return -1; + } + if (!(save = cols = ast_strdup(tmp))) { + ast_log(LOG_ERROR, "Out of memory creating temporary buffer for column list for table '%s.'\n", table); + ast_free(column_string); + return -1; + } + while ((col = strsep(&cols, ","))) { + col = ast_strip(col); + escaped = sqlite3_mprintf("%q", col); + if (!escaped) { + ast_log(LOG_ERROR, "Out of memory creating entry for column '%s' in table '%s.'\n", col, table); + ast_free(column_string); + ast_free(save); + return -1; + } + ast_str_append(&column_string, 0, "%s%s", ast_str_strlen(column_string) ? "," : "", escaped); + sqlite3_free(escaped); + } + if (!(columns = ast_strdup(ast_str_buffer(column_string)))) { + ast_log(LOG_ERROR, "Out of memory copying columns string for table '%s.'\n", table); + ast_free(column_string); + ast_free(save); + return -1; + } + ast_free(column_string); + ast_free(save); + + return 0; +} + +static int load_values_config(const char *tmp) +{ + char *val = NULL; + char *vals = NULL, *save = NULL; + struct values *value = NULL; + + if (ast_strlen_zero(tmp)) { + ast_log(LOG_WARNING, "Values not specified. Module not loaded.\n"); + return -1; + } + if (!(save = vals = ast_strdup(tmp))) { + ast_log(LOG_ERROR, "Out of memory creating temporary buffer for value '%s'\n", tmp); + return -1; + } + while ((val = strsep(&vals, ","))) { + /* Strip the single quotes off if they are there */ + val = ast_strip_quoted(val, "'", "'"); + value = ast_calloc(sizeof(char), sizeof(*value) + strlen(val) + 1); + if (!value) { + ast_log(LOG_ERROR, "Out of memory creating entry for value '%s'\n", val); + ast_free(save); + return -1; + } + value->expression = (char *) value + sizeof(*value); + ast_copy_string(value->expression, val, strlen(val) + 1); + AST_LIST_INSERT_TAIL(&sql_values, value, list); + } + ast_free(save); + + return 0; +} + +static int load_config(int reload) +{ + struct ast_config *cfg; + struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; + struct ast_variable *mappingvar; + const char *tmp; + + if ((cfg = ast_config_load(config_file, config_flags)) == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) { + ast_log(LOG_WARNING, "Failed to %sload configuration file. %s\n", + reload ? "re" : "", reload ? "" : "Module not activated."); + return -1; + } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) { + return 0; + } + + if (reload) { + free_config(); + } + + if (!(mappingvar = ast_variable_browse(cfg, "master"))) { + /* Nothing configured */ + ast_config_destroy(cfg); + return -1; + } + + /* Mapping must have a table name */ + if (!ast_strlen_zero(tmp = ast_variable_retrieve(cfg, "master", "table"))) { + ast_copy_string(table, tmp, sizeof(table)); + } else { + ast_log(LOG_WARNING, "Table name not specified. Assuming cel.\n"); + strcpy(table, "cel"); + } + + /* Columns */ + if (load_column_config(ast_variable_retrieve(cfg, "master", "columns"))) { + ast_config_destroy(cfg); + free_config(); + return -1; + } + + /* Values */ + if (load_values_config(ast_variable_retrieve(cfg, "master", "values"))) { + ast_config_destroy(cfg); + free_config(); + return -1; + } + + ast_verb(3, "Logging CEL records to table '%s' in 'master.db'\n", table); + + ast_config_destroy(cfg); + + return 0; +} + +static void free_config(void) +{ + struct values *value; + + if (db) { + sqlite3_close(db); + db = NULL; + } + + if (columns) { + ast_free(columns); + columns = NULL; + } + + while ((value = AST_LIST_REMOVE_HEAD(&sql_values, list))) { + ast_free(value); + } +} + +static void sqlite3_log(const struct ast_event *event, void *userdata) +{ + char *error = NULL; + char *sql = NULL; + int count = 0; + + if (db == NULL) { + /* Should not have loaded, but be failsafe. */ + return; + } + + ast_mutex_lock(&lock); + + { /* Make it obvious that only sql should be used outside of this block */ + char *escaped; + char subst_buf[2048]; + struct values *value; + struct ast_channel *dummy; + struct ast_str *value_string = ast_str_create(1024); + + dummy = ast_cel_fabricate_channel_from_event(event); + if (!dummy) { + ast_log(LOG_ERROR, "Unable to fabricate channel from CEL event.\n"); + ast_free(value_string); + ast_mutex_unlock(&lock); + return; + } + AST_LIST_TRAVERSE(&sql_values, value, list) { + pbx_substitute_variables_helper(dummy, value->expression, subst_buf, sizeof(subst_buf) - 1); + escaped = sqlite3_mprintf("%q", subst_buf); + ast_str_append(&value_string, 0, "%s'%s'", ast_str_strlen(value_string) ? "," : "", escaped); + sqlite3_free(escaped); + } + sql = sqlite3_mprintf("INSERT INTO %q (%s) VALUES (%s)", table, columns, ast_str_buffer(value_string)); + ast_debug(1, "About to log: %s\n", sql); + dummy = ast_channel_release(dummy); + ast_free(value_string); + } + + /* XXX This seems awful arbitrary... */ + for (count = 0; count < 5; count++) { + int res = sqlite3_exec(db, sql, NULL, NULL, &error); + if (res != SQLITE_BUSY && res != SQLITE_LOCKED) { + break; + } + usleep(200); + } + + ast_mutex_unlock(&lock); + + if (error) { + ast_log(LOG_ERROR, "%s. SQL: %s.\n", error, sql); + sqlite3_free(error); + } + + if (sql) { + sqlite3_free(sql); + } + + return; +} + +static int unload_module(void) +{ + if (event_sub) { + event_sub = ast_event_unsubscribe(event_sub); + } + + free_config(); + + return 0; +} + +static int load_module(void) +{ + char *error; + char filename[PATH_MAX]; + int res; + char *sql; + + if (load_config(0)) { + return AST_MODULE_LOAD_DECLINE; + } + + /* is the database there? */ + snprintf(filename, sizeof(filename), "%s/master.db", ast_config_AST_LOG_DIR); + res = sqlite3_open(filename, &db); + if (res != SQLITE_OK) { + ast_log(LOG_ERROR, "Could not open database %s.\n", filename); + free_config(); + return AST_MODULE_LOAD_DECLINE; + } + + /* is the table there? */ + sql = sqlite3_mprintf("SELECT COUNT(AcctId) FROM %q;", table); + res = sqlite3_exec(db, sql, NULL, NULL, NULL); + sqlite3_free(sql); + if (res != SQLITE_OK) { + /* We don't use %q for the column list here since we already escaped when building it */ + sql = sqlite3_mprintf("CREATE TABLE %q (AcctId INTEGER PRIMARY KEY, %s)", table, columns); + res = sqlite3_exec(db, sql, NULL, NULL, &error); + sqlite3_free(sql); + if (res != SQLITE_OK) { + ast_log(LOG_WARNING, "Unable to create table '%s': %s.\n", table, error); + sqlite3_free(error); + free_config(); + return AST_MODULE_LOAD_DECLINE; + } + } + + event_sub = ast_event_subscribe(AST_EVENT_CEL, sqlite3_log, "CEL sqlite3 custom backend", NULL, AST_EVENT_IE_END); + if (!event_sub) { + ast_log(LOG_ERROR, "Unable to register custom SQLite3 CEL handling\n"); + free_config(); + return AST_MODULE_LOAD_DECLINE; + } + + return AST_MODULE_LOAD_SUCCESS; +} + +static int reload(void) +{ + int res = 0; + + ast_mutex_lock(&lock); + res = load_config(1); + ast_mutex_lock(&lock); + + return res; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "SQLite3 Custom CEL Module", + .load = load_module, + .unload = unload_module, + .reload = reload, +); diff --git a/cel/cel_tds.c b/cel/cel_tds.c new file mode 100644 index 000000000..138a9cdd3 --- /dev/null +++ b/cel/cel_tds.c @@ -0,0 +1,587 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2008, 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 FreeTDS CEL logger + * + * See also + * \arg \ref Config_cdr + * \arg http://www.freetds.org/ + * \ingroup cdr_drivers + */ + +/*! \verbatim + * + * Table Structure for `cdr` + * + +CREATE TABLE [dbo].[cdr] ( + [accountcode] [varchar] (20) NULL , + [cidname] [varchar] (80) NULL , + [cidnum] [varchar] (80) NULL , + [cidani] [varchar] (80) NULL , + [cidrdnis] [varchar] (80) NULL , + [ciddnid] [varchar] (80) NULL , + [exten] [varchar] (80) NULL , + [context] [varchar] (80) NULL , + [channame] [varchar] (80) NULL , + [appname] [varchar] (80) NULL , + [appdata] [varchar] (80) NULL , + [eventtime] [datetime] NULL , + [eventtype] [varchar] (32) NULL , + [uniqueid] [varchar] (32) NULL , + [linkedid] [varchar] (32) NULL , + [amaflags] [varchar] (16) NULL , + [userfield] [varchar] (32) NULL , + [peer] [varchar] (32) NULL +) ON [PRIMARY] + +\endverbatim + +*/ + +/*** MODULEINFO + <depend>freetds</depend> + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include <time.h> +#include <math.h> + +#include "asterisk/config.h" +#include "asterisk/channel.h" +#include "asterisk/cel.h" +#include "asterisk/module.h" +#include "asterisk/logger.h" + +#include <sqlfront.h> +#include <sybdb.h> + +#ifdef FREETDS_PRE_0_62 +#warning "You have older TDS, you should upgrade!" +#endif + +#define DATE_FORMAT "%Y/%m/%d %T" + +static char *config = "cel_tds.conf"; + +static struct ast_event_sub *event_sub = NULL; + +struct cel_tds_config { + AST_DECLARE_STRING_FIELDS( + AST_STRING_FIELD(connection); + AST_STRING_FIELD(database); + AST_STRING_FIELD(username); + AST_STRING_FIELD(password); + AST_STRING_FIELD(table); + AST_STRING_FIELD(charset); + AST_STRING_FIELD(language); + ); + DBPROCESS *dbproc; + unsigned int connected:1; +}; + +AST_MUTEX_DEFINE_STATIC(tds_lock); + +static struct cel_tds_config *settings; + +static char *anti_injection(const char *, int); +static void get_date(char *, size_t len, struct timeval); + +static int execute_and_consume(DBPROCESS *dbproc, const char *fmt, ...) + __attribute__((format(printf, 2, 3))); + +static int mssql_connect(void); +static int mssql_disconnect(void); + +static void tds_log(const struct ast_event *event, void *userdata) +{ + char start[80]; + char *accountcode_ai, *clidnum_ai, *exten_ai, *context_ai, *clid_ai, *channel_ai, *app_ai, *appdata_ai, *uniqueid_ai, *linkedid_ai, *cidani_ai, *cidrdnis_ai, *ciddnid_ai, *peer_ai, *userfield_ai; + RETCODE erc; + int attempt = 1; + struct ast_cel_event_record record = { + .version = AST_CEL_EVENT_RECORD_VERSION, + }; + + if (ast_cel_fill_record(event, &record)) { + return; + } + + ast_mutex_lock(&tds_lock); + + accountcode_ai = anti_injection(record.account_code, 20); + clidnum_ai = anti_injection(record.caller_id_num, 80); + clid_ai = anti_injection(record.caller_id_name, 80); + cidani_ai = anti_injection(record.caller_id_ani, 80); + cidrdnis_ai = anti_injection(record.caller_id_rdnis, 80); + ciddnid_ai = anti_injection(record.caller_id_dnid, 80); + exten_ai = anti_injection(record.extension, 80); + context_ai = anti_injection(record.context, 80); + channel_ai = anti_injection(record.channel_name, 80); + app_ai = anti_injection(record.application_name, 80); + appdata_ai = anti_injection(record.application_data, 80); + uniqueid_ai = anti_injection(record.unique_id, 32); + linkedid_ai = anti_injection(record.linked_id, 32); + userfield_ai = anti_injection(record.user_field, 32); + peer_ai = anti_injection(record.peer, 32); + + get_date(start, sizeof(start), record.event_time); + +retry: + /* Ensure that we are connected */ + if (!settings->connected) { + ast_log(LOG_NOTICE, "Attempting to reconnect to %s (Attempt %d)\n", settings->connection, attempt); + if (mssql_connect()) { + /* Connect failed */ + if (attempt++ < 3) { + goto retry; + } + goto done; + } + } + + erc = dbfcmd(settings->dbproc, + "INSERT INTO %s " + "(" + "accountcode," + "cidnum," + "cidname," + "cidani," + "cidrdnis," + "ciddnid," + "exten," + "context," + "channel," + "appname," + "appdata," + "eventtime," + "eventtype," + "amaflags, " + "uniqueid," + "linkedid," + "userfield," + "peer" + ") " + "VALUES " + "(" + "'%s'," /* accountcode */ + "'%s'," /* clidnum */ + "'%s'," /* clid */ + "'%s'," /* cid-ani */ + "'%s'," /* cid-rdnis */ + "'%s'," /* cid-dnid */ + "'%s'," /* exten */ + "'%s'," /* context */ + "'%s'," /* channel */ + "'%s'," /* app */ + "'%s'," /* appdata */ + "%s, " /* eventtime */ + "'%s'," /* eventtype */ + "'%s'," /* amaflags */ + "'%s'," /* uniqueid */ + "'%s'," /* linkedid */ + "'%s'," /* userfield */ + "'%s'" /* peer */ + ")", + settings->table, accountcode_ai, clidnum_ai, clid_ai, cidani_ai, cidrdnis_ai, + ciddnid_ai, exten_ai, context_ai, channel_ai, app_ai, appdata_ai, start, + record.event_name, ast_cel_get_ama_flag_name(record.amaflag), uniqueid_ai, linkedid_ai, + userfield_ai, peer_ai); + + if (erc == FAIL) { + if (attempt++ < 3) { + ast_log(LOG_NOTICE, "Failed to build INSERT statement, retrying...\n"); + mssql_disconnect(); + goto retry; + } else { + ast_log(LOG_ERROR, "Failed to build INSERT statement, no CEL was logged.\n"); + goto done; + } + } + + if (dbsqlexec(settings->dbproc) == FAIL) { + if (attempt++ < 3) { + ast_log(LOG_NOTICE, "Failed to execute INSERT statement, retrying...\n"); + mssql_disconnect(); + goto retry; + } else { + ast_log(LOG_ERROR, "Failed to execute INSERT statement, no CEL was logged.\n"); + goto done; + } + } + + /* Consume any results we might get back (this is more of a sanity check than + * anything else, since an INSERT shouldn't return results). */ + while (dbresults(settings->dbproc) != NO_MORE_RESULTS) { + while (dbnextrow(settings->dbproc) != NO_MORE_ROWS); + } + +done: + ast_mutex_unlock(&tds_lock); + + free(accountcode_ai); + free(clidnum_ai); + free(clid_ai); + free(cidani_ai); + free(cidrdnis_ai); + free(ciddnid_ai); + free(exten_ai); + free(context_ai); + free(channel_ai); + free(app_ai); + free(appdata_ai); + free(uniqueid_ai); + free(linkedid_ai); + free(userfield_ai); + free(peer_ai); + + return; +} + +static char *anti_injection(const char *str, int len) +{ + /* Reference to http://www.nextgenss.com/papers/advanced_sql_injection.pdf */ + char *buf; + char *buf_ptr, *srh_ptr; + char *known_bad[] = {"select", "insert", "update", "delete", "drop", ";", "--", "\0"}; + int idx; + + if (!(buf = ast_calloc(1, len + 1))) { + ast_log(LOG_ERROR, "Out of memory\n"); + return NULL; + } + + buf_ptr = buf; + + /* Escape single quotes */ + for (; *str && strlen(buf) < len; str++) { + if (*str == '\'') { + *buf_ptr++ = '\''; + } + *buf_ptr++ = *str; + } + *buf_ptr = '\0'; + + /* Erase known bad input */ + for (idx = 0; *known_bad[idx]; idx++) { + while ((srh_ptr = strcasestr(buf, known_bad[idx]))) { + memmove(srh_ptr, srh_ptr + strlen(known_bad[idx]), strlen(srh_ptr + strlen(known_bad[idx])) + 1); + } + } + return buf; +} + +static void get_date(char *dateField, size_t len, struct timeval when) +{ + /* To make sure we have date variable if not insert null to SQL */ + if (!ast_tvzero(when)) { + struct ast_tm tm; + ast_localtime(&when, &tm, NULL); + ast_strftime(dateField, len, "'" DATE_FORMAT "'", &tm); + } else { + ast_copy_string(dateField, "null", len); + } +} + +static int execute_and_consume(DBPROCESS *dbproc, const char *fmt, ...) +{ + va_list ap; + char *buffer; + + va_start(ap, fmt); + if (ast_vasprintf(&buffer, fmt, ap) < 0) { + va_end(ap); + return 1; + } + va_end(ap); + + if (dbfcmd(dbproc, buffer) == FAIL) { + free(buffer); + return 1; + } + + free(buffer); + + if (dbsqlexec(dbproc) == FAIL) { + return 1; + } + + /* Consume the result set (we don't really care about the result, though) */ + while (dbresults(dbproc) != NO_MORE_RESULTS) { + while (dbnextrow(dbproc) != NO_MORE_ROWS); + } + + return 0; +} + +static int mssql_disconnect(void) +{ + if (settings->dbproc) { + dbclose(settings->dbproc); + settings->dbproc = NULL; + } + settings->connected = 0; + + return 0; +} + +static int mssql_connect(void) +{ + LOGINREC *login; + + if ((login = dblogin()) == NULL) { + ast_log(LOG_ERROR, "Unable to allocate login structure for db-lib\n"); + return -1; + } + + DBSETLAPP(login, "TSQL"); + DBSETLUSER(login, (char *) settings->username); + DBSETLPWD(login, (char *) settings->password); + + if (!ast_strlen_zero(settings->charset)) { + DBSETLCHARSET(login, (char *) settings->charset); + } + + if (!ast_strlen_zero(settings->language)) { + DBSETLNATLANG(login, (char *) settings->language); + } + + if ((settings->dbproc = dbopen(login, (char *) settings->connection)) == NULL) { + ast_log(LOG_ERROR, "Unable to connect to %s\n", settings->connection); + dbloginfree(login); + return -1; + } + + dbloginfree(login); + + if (dbuse(settings->dbproc, (char *) settings->database) == FAIL) { + ast_log(LOG_ERROR, "Unable to select database %s\n", settings->database); + goto failed; + } + + if (execute_and_consume(settings->dbproc, "SELECT 1 FROM [%s]", settings->table)) { + ast_log(LOG_ERROR, "Unable to find table '%s'\n", settings->table); + goto failed; + } + + settings->connected = 1; + + return 0; + +failed: + dbclose(settings->dbproc); + settings->dbproc = NULL; + return -1; +} + +static int tds_unload_module(void) +{ + if (event_sub) { + event_sub = ast_event_unsubscribe(event_sub); + } + + if (settings) { + ast_mutex_lock(&tds_lock); + mssql_disconnect(); + ast_mutex_unlock(&tds_lock); + + ast_string_field_free_memory(settings); + ast_free(settings); + } + + dbexit(); + + return 0; +} + +static int tds_error_handler(DBPROCESS *dbproc, int severity, int dberr, int oserr, char *dberrstr, char *oserrstr) +{ + ast_log(LOG_ERROR, "%s (%d)\n", dberrstr, dberr); + + if (oserr != DBNOERR) { + ast_log(LOG_ERROR, "%s (%d)\n", oserrstr, oserr); + } + + return INT_CANCEL; +} + +static int tds_message_handler(DBPROCESS *dbproc, DBINT msgno, int msgstate, int severity, char *msgtext, char *srvname, char *procname, int line) +{ + ast_debug(1, "Msg %d, Level %d, State %d, Line %d\n", msgno, severity, msgstate, line); + ast_log(LOG_NOTICE, "%s\n", msgtext); + + return 0; +} + +static int tds_load_module(int reload) +{ + struct ast_config *cfg; + const char *ptr = NULL; + struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; + + cfg = ast_config_load(config, config_flags); + if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) { + ast_log(LOG_NOTICE, "Unable to load TDS config for CELs: %s\n", config); + return 0; + } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) { + return 0; + } + + if (!ast_variable_browse(cfg, "global")) { + /* nothing configured */ + ast_config_destroy(cfg); + ast_log(LOG_NOTICE, "cel_tds has no global category, nothing to configure.\n"); + return 0; + } + + ast_mutex_lock(&tds_lock); + + /* Clear out any existing settings */ + ast_string_field_init(settings, 0); + + ptr = ast_variable_retrieve(cfg, "global", "connection"); + if (ptr) { + ast_string_field_set(settings, connection, ptr); + } else { + ast_log(LOG_ERROR, "Failed to connect: Database connection name not specified.\n"); + goto failed; + } + + ptr = ast_variable_retrieve(cfg, "global", "dbname"); + if (ptr) { + ast_string_field_set(settings, database, ptr); + } else { + ast_log(LOG_ERROR, "Failed to connect: Database dbname not specified.\n"); + goto failed; + } + + ptr = ast_variable_retrieve(cfg, "global", "user"); + if (ptr) { + ast_string_field_set(settings, username, ptr); + } else { + ast_log(LOG_ERROR, "Failed to connect: Database dbuser not specified.\n"); + goto failed; + } + + ptr = ast_variable_retrieve(cfg, "global", "password"); + if (ptr) { + ast_string_field_set(settings, password, ptr); + } else { + ast_log(LOG_ERROR, "Failed to connect: Database password not specified.\n"); + goto failed; + } + + ptr = ast_variable_retrieve(cfg, "global", "charset"); + if (ptr) { + ast_string_field_set(settings, charset, ptr); + } + + ptr = ast_variable_retrieve(cfg, "global", "language"); + if (ptr) { + ast_string_field_set(settings, language, ptr); + } + + ptr = ast_variable_retrieve(cfg, "global", "table"); + if (ptr) { + ast_string_field_set(settings, table, ptr); + } else { + ast_log(LOG_NOTICE, "Table name not specified, using 'cel' by default.\n"); + ast_string_field_set(settings, table, "cel"); + } + + mssql_disconnect(); + + if (mssql_connect()) { + /* We failed to connect (mssql_connect takes care of logging it) */ + goto failed; + } + + ast_mutex_unlock(&tds_lock); + ast_config_destroy(cfg); + + return 1; + +failed: + ast_mutex_unlock(&tds_lock); + ast_config_destroy(cfg); + + return 0; +} + +static int reload(void) +{ + return tds_load_module(1); +} + +static int load_module(void) +{ + if (dbinit() == FAIL) { + ast_log(LOG_ERROR, "Failed to initialize FreeTDS db-lib\n"); + return AST_MODULE_LOAD_DECLINE; + } + + dberrhandle(tds_error_handler); + dbmsghandle(tds_message_handler); + + settings = ast_calloc(1, sizeof(*settings)); + + if (!settings || ast_string_field_init(settings, 256)) { + if (settings) { + ast_free(settings); + settings = NULL; + } + dbexit(); + return AST_MODULE_LOAD_DECLINE; + } + + if (!tds_load_module(0)) { + ast_string_field_free_memory(settings); + ast_free(settings); + settings = NULL; + dbexit(); + ast_log(LOG_WARNING,"cel_tds module had config problems; declining load\n"); + return AST_MODULE_LOAD_DECLINE; + } + + /* Register MSSQL CEL handler */ + event_sub = ast_event_subscribe(AST_EVENT_CEL, tds_log, "CEL TDS logging backend", NULL, AST_EVENT_IE_END); + if (!event_sub) { + ast_log(LOG_ERROR, "Unable to register MSSQL CEL handling\n"); + ast_string_field_free_memory(settings); + ast_free(settings); + settings = NULL; + dbexit(); + return AST_MODULE_LOAD_DECLINE; + } + + return AST_MODULE_LOAD_SUCCESS; +} + +static int unload_module(void) +{ + return tds_unload_module(); +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "FreeTDS CEL Backend", + .load = load_module, + .unload = unload_module, + .reload = reload, +); diff --git a/channels/chan_agent.c b/channels/chan_agent.c index f8205d4cd..a31a12fdf 100644 --- a/channels/chan_agent.c +++ b/channels/chan_agent.c @@ -310,7 +310,7 @@ static AST_LIST_HEAD_STATIC(agents, agent_pvt); /*!< Holds the list of agents (l } while(0) /*--- Forward declarations */ -static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause); +static struct ast_channel *agent_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause); static int agent_devicestate(void *data); static int agent_digit_begin(struct ast_channel *ast, char digit); static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration); @@ -986,7 +986,7 @@ static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct } /*! \brief Create new agent channel */ -static struct ast_channel *agent_new(struct agent_pvt *p, int state) +static struct ast_channel *agent_new(struct agent_pvt *p, int state, const char *linkedid) { struct ast_channel *tmp; int alreadylocked; @@ -997,9 +997,9 @@ static struct ast_channel *agent_new(struct agent_pvt *p, int state) } #endif if (p->pending) - tmp = ast_channel_alloc(0, state, 0, 0, "", p->chan ? p->chan->exten:"", p->chan ? p->chan->context:"", 0, "Agent/P%s-%d", p->agent, (int) ast_random() & 0xffff); + tmp = ast_channel_alloc(0, state, 0, 0, "", p->chan ? p->chan->exten:"", p->chan ? p->chan->context:"", linkedid, 0, "Agent/P%s-%d", p->agent, (int) ast_random() & 0xffff); else - tmp = ast_channel_alloc(0, state, 0, 0, "", p->chan ? p->chan->exten:"", p->chan ? p->chan->context:"", 0, "Agent/%s", p->agent); + tmp = ast_channel_alloc(0, state, 0, 0, "", p->chan ? p->chan->exten:"", p->chan ? p->chan->context:"", linkedid, 0, "Agent/%s", p->agent); if (!tmp) { ast_log(LOG_WARNING, "Unable to allocate agent channel structure\n"); return NULL; @@ -1249,7 +1249,7 @@ static int check_availability(struct agent_pvt *newlyavailable, int needlock) if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) { ast_debug(1, "Call '%s' looks like a winner for agent '%s'\n", p->owner->name, newlyavailable->agent); /* We found a pending call, time to merge */ - chan = agent_new(newlyavailable, AST_STATE_DOWN); + chan = agent_new(newlyavailable, AST_STATE_DOWN, p->owner ? p->owner->linkedid : NULL); parent = p->owner; p->abouttograb = 1; ast_mutex_unlock(&p->lock); @@ -1334,7 +1334,7 @@ static int check_beep(struct agent_pvt *newlyavailable, int needlock) } /*! \brief Part of the Asterisk PBX interface */ -static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause) +static struct ast_channel *agent_request(const char *type, int format, const struct ast_channel* requestor, void *data, int *cause) { struct agent_pvt *p; struct ast_channel *chan = NULL; @@ -1367,7 +1367,7 @@ static struct ast_channel *agent_request(const char *type, int format, void *dat /* Agent must be registered, but not have any active call, and not be in a waiting state */ if (!p->owner && p->chan) { /* Fixed agent */ - chan = agent_new(p, AST_STATE_DOWN); + chan = agent_new(p, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL); } if (chan) { ast_mutex_unlock(&p->lock); @@ -1390,7 +1390,7 @@ static struct ast_channel *agent_request(const char *type, int format, void *dat /* Agent must be registered, but not have any active call, and not be in a waiting state */ if (!p->owner && p->chan) { /* Could still get a fixed agent */ - chan = agent_new(p, AST_STATE_DOWN); + chan = agent_new(p, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL); } if (chan) { ast_mutex_unlock(&p->lock); @@ -1409,7 +1409,7 @@ static struct ast_channel *agent_request(const char *type, int format, void *dat ast_debug(1, "Creating place holder for '%s'\n", s); p = add_agent(data, 1); p->group = groupmatch; - chan = agent_new(p, AST_STATE_DOWN); + chan = agent_new(p, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL); if (!chan) ast_log(LOG_WARNING, "Weird... Fix this to drop the unused pending agent\n"); } else { diff --git a/channels/chan_alsa.c b/channels/chan_alsa.c index e1fe2b3fe..48d718682 100644 --- a/channels/chan_alsa.c +++ b/channels/chan_alsa.c @@ -130,7 +130,7 @@ static int writedev = -1; static int autoanswer = 1; -static struct ast_channel *alsa_request(const char *type, int format, void *data, int *cause); +static struct ast_channel *alsa_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause); static int alsa_digit(struct ast_channel *c, char digit, unsigned int duration); static int alsa_text(struct ast_channel *c, const char *text); static int alsa_hangup(struct ast_channel *c); @@ -532,11 +532,11 @@ static int alsa_indicate(struct ast_channel *chan, int cond, const void *data, s return res; } -static struct ast_channel *alsa_new(struct chan_alsa_pvt *p, int state) +static struct ast_channel *alsa_new(struct chan_alsa_pvt *p, int state, const char *linkedid) { struct ast_channel *tmp = NULL; - if (!(tmp = ast_channel_alloc(1, state, 0, 0, "", p->exten, p->context, 0, "ALSA/%s", indevname))) + if (!(tmp = ast_channel_alloc(1, state, 0, 0, "", p->exten, p->context, linkedid, 0, "ALSA/%s", indevname))) return NULL; tmp->tech = &alsa_tech; @@ -565,7 +565,7 @@ static struct ast_channel *alsa_new(struct chan_alsa_pvt *p, int state) return tmp; } -static struct ast_channel *alsa_request(const char *type, int fmt, void *data, int *cause) +static struct ast_channel *alsa_request(const char *type, int fmt, const struct ast_channel *requestor, void *data, int *cause) { int oldformat = fmt; struct ast_channel *tmp = NULL; @@ -580,7 +580,7 @@ static struct ast_channel *alsa_request(const char *type, int fmt, void *data, i if (alsa.owner) { ast_log(LOG_NOTICE, "Already have a call on the ALSA channel\n"); *cause = AST_CAUSE_BUSY; - } else if (!(tmp = alsa_new(&alsa, AST_STATE_DOWN))) { + } else if (!(tmp = alsa_new(&alsa, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL))) { ast_log(LOG_WARNING, "Unable to create new ALSA channel\n"); } @@ -833,7 +833,7 @@ static char *console_dial(struct ast_cli_entry *e, int cmd, struct ast_cli_args ast_copy_string(alsa.exten, mye, sizeof(alsa.exten)); ast_copy_string(alsa.context, myc, sizeof(alsa.context)); hookstate = 1; - alsa_new(&alsa, AST_STATE_RINGING); + alsa_new(&alsa, AST_STATE_RINGING, NULL); } else ast_cli(a->fd, "No such extension '%s' in context '%s'\n", mye, myc); } diff --git a/channels/chan_bridge.c b/channels/chan_bridge.c index be41cd86c..be823cd32 100644 --- a/channels/chan_bridge.c +++ b/channels/chan_bridge.c @@ -46,7 +46,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/app.h" #include "asterisk/bridging.h" -static struct ast_channel *bridge_request(const char *type, int format, void *data, int *cause); +static struct ast_channel *bridge_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause); static int bridge_call(struct ast_channel *ast, char *dest, int timeout); static int bridge_hangup(struct ast_channel *ast); static struct ast_frame *bridge_read(struct ast_channel *ast); @@ -189,7 +189,7 @@ static int bridge_hangup(struct ast_channel *ast) } /*! \brief Called when we want to place a call somewhere, but not actually call it... yet */ -static struct ast_channel *bridge_request(const char *type, int format, void *data, int *cause) +static struct ast_channel *bridge_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause) { struct bridge_pvt *p = NULL; @@ -199,11 +199,11 @@ static struct ast_channel *bridge_request(const char *type, int format, void *da } /* Try to grab two Asterisk channels to use as input and output channels */ - if (!(p->input = ast_channel_alloc(1, AST_STATE_UP, 0, 0, "", "", "", 0, "Bridge/%p-input", p))) { + if (!(p->input = ast_channel_alloc(1, AST_STATE_UP, 0, 0, "", "", "", requestor ? requestor->linkedid : NULL, 0, "Bridge/%p-input", p))) { ast_free(p); return NULL; } - if (!(p->output = ast_channel_alloc(1, AST_STATE_UP, 0, 0, "", "", "", 0, "Bridge/%p-output", p))) { + if (!(p->output = ast_channel_alloc(1, AST_STATE_UP, 0, 0, "", "", "", requestor ? requestor->linkedid : NULL, 0, "Bridge/%p-output", p))) { p->input = ast_channel_release(p->input); ast_free(p); return NULL; diff --git a/channels/chan_console.c b/channels/chan_console.c index 091fcf1b1..55be76895 100644 --- a/channels/chan_console.c +++ b/channels/chan_console.c @@ -189,8 +189,8 @@ static struct ast_jb_conf default_jbconf = { static struct ast_jb_conf global_jbconf; /*! Channel Technology Callbacks @{ */ -static struct ast_channel *console_request(const char *type, int format, - void *data, int *cause); +static struct ast_channel *console_request(const char *type, int format, + const struct ast_channel *requestor, void *data, int *cause); static int console_digit_begin(struct ast_channel *c, char digit); static int console_digit_end(struct ast_channel *c, char digit, unsigned int duration); static int console_text(struct ast_channel *c, const char *text); @@ -413,12 +413,12 @@ static int stop_stream(struct console_pvt *pvt) /*! * \note Called with the pvt struct locked */ -static struct ast_channel *console_new(struct console_pvt *pvt, const char *ext, const char *ctx, int state) +static struct ast_channel *console_new(struct console_pvt *pvt, const char *ext, const char *ctx, int state, const char *linkedid) { struct ast_channel *chan; if (!(chan = ast_channel_alloc(1, state, pvt->cid_num, pvt->cid_name, NULL, - ext, ctx, 0, "Console/%s", pvt->name))) { + ext, ctx, linkedid, 0, "Console/%s", pvt->name))) { return NULL; } @@ -447,7 +447,7 @@ static struct ast_channel *console_new(struct console_pvt *pvt, const char *ext, return chan; } -static struct ast_channel *console_request(const char *type, int format, void *data, int *cause) +static struct ast_channel *console_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause) { int oldformat = format; struct ast_channel *chan = NULL; @@ -471,7 +471,7 @@ static struct ast_channel *console_request(const char *type, int format, void *d } console_pvt_lock(pvt); - chan = console_new(pvt, NULL, NULL, AST_STATE_DOWN); + chan = console_new(pvt, NULL, NULL, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL); console_pvt_unlock(pvt); if (!chan) @@ -833,7 +833,7 @@ static char *cli_console_dial(struct ast_cli_entry *e, int cmd, struct ast_cli_a if (ast_exists_extension(NULL, myc, mye, 1, NULL)) { console_pvt_lock(pvt); pvt->hookstate = 1; - console_new(pvt, mye, myc, AST_STATE_RINGING); + console_new(pvt, mye, myc, AST_STATE_RINGING, NULL); console_pvt_unlock(pvt); } else ast_cli(a->fd, "No such extension '%s' in context '%s'\n", mye, myc); diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c index e1c3348f4..a57e22169 100644 --- a/channels/chan_dahdi.c +++ b/channels/chan_dahdi.c @@ -1,7 +1,7 @@ /* * Asterisk -- An open source telephony toolkit. * - * Copyright (C) 1999 - 2006, Digium, Inc. + * Copyright (C) 1999 - 2008, Digium, Inc. * * Mark Spencer <markster@digium.com> * @@ -91,6 +91,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/adsi.h" #include "asterisk/cli.h" #include "asterisk/cdr.h" +#include "asterisk/cel.h" #include "asterisk/features.h" #include "asterisk/musiconhold.h" #include "asterisk/say.h" @@ -1388,7 +1389,7 @@ static struct dahdi_chan_conf dahdi_chan_conf_default(void) } -static struct ast_channel *dahdi_request(const char *type, int format, void *data, int *cause); +static struct ast_channel *dahdi_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause); static int dahdi_digit_begin(struct ast_channel *ast, char digit); static int dahdi_digit_end(struct ast_channel *ast, char digit, unsigned int duration); static int dahdi_sendtext(struct ast_channel *c, const char *text); @@ -1978,14 +1979,14 @@ static void my_swap_subchannels(void *pvt, enum analog_sub a, struct ast_channel return; } -static struct ast_channel *dahdi_new(struct dahdi_pvt *, int, int, int, int, int); +static struct ast_channel *dahdi_new(struct dahdi_pvt *, int, int, int, int, int, const char *); -static struct ast_channel *my_new_analog_ast_channel(void *pvt, int state, int startpbx, enum analog_sub sub) +static struct ast_channel *my_new_analog_ast_channel(void *pvt, int state, int startpbx, enum analog_sub sub, const struct ast_channel *requestor) { struct dahdi_pvt *p = pvt; int dsub = analogsub_to_dahdisub(sub); - return dahdi_new(p, state, startpbx, dsub, 0, 0); + return dahdi_new(p, state, startpbx, dsub, 0, 0, requestor ? requestor->linkedid : ""); } #if defined(HAVE_PRI) || defined(HAVE_SS7) @@ -2000,7 +2001,7 @@ static int dahdi_setlaw(int dfd, int law) #endif #ifdef HAVE_PRI -static struct ast_channel *my_new_pri_ast_channel(void *pvt, int state, int startpbx, enum sig_pri_law law, int transfercapability, char *exten) +static struct ast_channel *my_new_pri_ast_channel(void *pvt, int state, int startpbx, enum sig_pri_law law, int transfercapability, char *exten, const struct ast_channel *requestor) { struct dahdi_pvt *p = pvt; int audio; @@ -2028,7 +2029,7 @@ static struct ast_channel *my_new_pri_ast_channel(void *pvt, int state, int star newlaw = DAHDI_LAW_MULAW; break; } - return dahdi_new(p, state, startpbx, SUB_REAL, newlaw, transfercapability); + return dahdi_new(p, state, startpbx, SUB_REAL, newlaw, transfercapability, requestor ? requestor->linkedid : ""); } #endif @@ -2595,7 +2596,6 @@ static void dahdi_queue_frame(struct dahdi_pvt *p, struct ast_frame *f, void *da #endif } -static struct ast_channel *dahdi_new(struct dahdi_pvt *, int, int, int, int, int); #ifdef HAVE_OPENR2 static int dahdi_r2_answer(struct dahdi_pvt *p) @@ -2764,7 +2764,7 @@ static void dahdi_r2_on_call_offered(openr2_chan_t *r2chan, const char *ani, con } if (!p->mfcr2_accept_on_offer) { /* The user wants us to start the PBX thread right away without accepting the call first */ - c = dahdi_new(p, AST_STATE_RING, 1, SUB_REAL, DAHDI_LAW_ALAW, 0); + c = dahdi_new(p, AST_STATE_RING, 1, SUB_REAL, DAHDI_LAW_ALAW, 0, NULL); if (c) { dahdi_r2_update_monitor_count(p->mfcr2, 0); /* Done here, don't disable reading now since we still need to generate MF tones to accept @@ -2815,7 +2815,7 @@ static void dahdi_r2_on_call_accepted(openr2_chan_t *r2chan, openr2_call_mode_t } return; } - c = dahdi_new(p, AST_STATE_RING, 1, SUB_REAL, DAHDI_LAW_ALAW, 0); + c = dahdi_new(p, AST_STATE_RING, 1, SUB_REAL, DAHDI_LAW_ALAW, 0, NULL); if (c) { dahdi_r2_update_monitor_count(p->mfcr2, 0); /* chan_dahdi will take care of reading from now on in the PBX thread, tell the @@ -6747,7 +6747,7 @@ static struct ast_frame *dahdi_handle_event(struct ast_channel *ast) goto winkflashdone; } /* Make new channel */ - chan = dahdi_new(p, AST_STATE_RESERVED, 0, SUB_THREEWAY, 0, 0); + chan = dahdi_new(p, AST_STATE_RESERVED, 0, SUB_THREEWAY, 0, 0, 0); if (p->dahditrcallerid) { if (!p->origcid_num) p->origcid_num = ast_strdup(p->cid_num); @@ -7102,6 +7102,12 @@ static struct ast_frame *__dahdi_exception(struct ast_channel *ast) return f; } f = dahdi_handle_event(ast); + + /* tell the cdr this zap device hung up */ + if (f == NULL) { + ast_set_hangupsource(ast, ast->name, 0); + } + return f; } @@ -7722,7 +7728,7 @@ 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) +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; int deflaw; @@ -7749,7 +7755,7 @@ static struct ast_channel *dahdi_new(struct dahdi_pvt *i, int state, int startpb } y++; } while (x < 3); - tmp = ast_channel_alloc(0, state, i->cid_num, i->cid_name, i->accountcode, i->exten, i->context, i->amaflags, "DAHDI/%s", ast_str_buffer(chan_name)); + 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)); if (!tmp) return NULL; tmp->tech = &dahdi_tech; @@ -9158,7 +9164,7 @@ static void *mwi_thread(void *data) restore_gains(mtd->pvt); mtd->pvt->ringt = mtd->pvt->ringt_base; - if ((chan = dahdi_new(mtd->pvt, AST_STATE_RING, 0, SUB_REAL, 0, 0))) { + if ((chan = dahdi_new(mtd->pvt, AST_STATE_RING, 0, SUB_REAL, 0, 0, NULL))) { int result; if (analog_lib_handles(mtd->pvt->sig, mtd->pvt->radio, mtd->pvt->oprmode)) { result = analog_ss_thread_start(mtd->pvt->sig_pvt, chan); @@ -9483,7 +9489,7 @@ static int handle_init_event(struct dahdi_pvt *i, int event) dahdi_enable_ec(i); /* The channel is immediately up. Start right away */ res = tone_zone_play_tone(i->subs[SUB_REAL].dfd, DAHDI_TONE_RINGTONE); - chan = dahdi_new(i, AST_STATE_RING, 1, SUB_REAL, 0, 0); + chan = dahdi_new(i, AST_STATE_RING, 1, SUB_REAL, 0, 0, NULL); if (!chan) { ast_log(LOG_WARNING, "Unable to start PBX on channel %d\n", i->channel); res = tone_zone_play_tone(i->subs[SUB_REAL].dfd, DAHDI_TONE_CONGESTION); @@ -9492,7 +9498,7 @@ static int handle_init_event(struct dahdi_pvt *i, int event) } } else { /* Check for callerid, digits, etc */ - chan = dahdi_new(i, AST_STATE_RESERVED, 0, SUB_REAL, 0, 0); + chan = dahdi_new(i, AST_STATE_RESERVED, 0, SUB_REAL, 0, 0, NULL); if (chan) { if (has_voicemail(i)) res = tone_zone_play_tone(i->subs[SUB_REAL].dfd, DAHDI_TONE_STUTTER); @@ -9535,9 +9541,9 @@ static int handle_init_event(struct dahdi_pvt *i, int event) case SIG_SF: /* Check for callerid, digits, etc */ if (i->cid_start == CID_START_POLARITY_IN) { - chan = dahdi_new(i, AST_STATE_PRERING, 0, SUB_REAL, 0, 0); + chan = dahdi_new(i, AST_STATE_PRERING, 0, SUB_REAL, 0, 0, NULL); } else { - chan = dahdi_new(i, AST_STATE_RING, 0, SUB_REAL, 0, 0); + chan = dahdi_new(i, AST_STATE_RING, 0, SUB_REAL, 0, 0, NULL); } if (!chan) { @@ -9639,7 +9645,7 @@ static int handle_init_event(struct dahdi_pvt *i, int event) ast_verb(2, "Starting post polarity " "CID detection on channel %d\n", i->channel); - chan = dahdi_new(i, AST_STATE_PRERING, 0, SUB_REAL, 0, 0); + chan = dahdi_new(i, AST_STATE_PRERING, 0, SUB_REAL, 0, 0, NULL); if (!chan) { ast_log(LOG_WARNING, "Cannot allocate new structure on channel %d\n", i->channel); } else if (ast_pthread_create_detached(&threadid, NULL, analog_ss_thread, chan)) { @@ -10723,7 +10729,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf, strsep(&context, "@"); if (ast_strlen_zero(context)) context = "default"; - tmp->mwi_event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, NULL, + tmp->mwi_event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, "Dahdi MWI subscription", NULL, AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox, AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context, AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_EXISTS, @@ -11005,7 +11011,7 @@ static struct dahdi_pvt *duplicate_pseudo(struct dahdi_pvt *src) return p; } -static struct ast_channel *dahdi_request(const char *type, int format, void *data, int *cause) +static struct ast_channel *dahdi_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause) { ast_group_t groupmatch = 0; int channelmatch = -1; @@ -11140,15 +11146,15 @@ static struct ast_channel *dahdi_request(const char *type, int format, void *dat } p->outgoing = 1; if (analog_lib_handles(p->sig, p->radio, p->oprmode)) { - tmp = analog_request(p->sig_pvt, &callwait); - } + tmp = analog_request(p->sig_pvt, &callwait, requestor); + } #ifdef HAVE_PRI else if (p->sig == SIG_PRI || p->sig == SIG_BRI || p->sig == SIG_BRI_PTMP) { - tmp = sig_pri_request(p->sig_pvt, SIG_PRI_DEFLAW); + tmp = sig_pri_request(p->sig_pvt, SIG_PRI_DEFLAW, requestor); } #endif else { - tmp = dahdi_new(p, AST_STATE_RESERVED, 0, p->owner ? SUB_CALLWAIT : SUB_REAL, 0, 0); + tmp = dahdi_new(p, AST_STATE_RESERVED, 0, p->owner ? SUB_CALLWAIT : SUB_REAL, 0, 0, requestor ? requestor->linkedid : ""); } /* Make special notes */ @@ -11370,7 +11376,7 @@ static void ss7_start_call(struct dahdi_pvt *p, struct dahdi_ss7 *linkset) } ast_mutex_unlock(&linkset->lock); - c = dahdi_new(p, AST_STATE_RING, 1, SUB_REAL, law, 0); + c = dahdi_new(p, AST_STATE_RING, 1, SUB_REAL, law, 0, NULL); if (!c) { ast_log(LOG_WARNING, "Unable to start PBX on CIC %d\n", p->cic); diff --git a/channels/chan_gtalk.c b/channels/chan_gtalk.c index bfa0915f6..6ad6ed904 100644 --- a/channels/chan_gtalk.c +++ b/channels/chan_gtalk.c @@ -166,7 +166,7 @@ static int global_capability = AST_FORMAT_ULAW | AST_FORMAT_ALAW | AST_FORMAT_GS AST_MUTEX_DEFINE_STATIC(gtalklock); /*!< Protect the interface list (of gtalk_pvt's) */ /* Forward declarations */ -static struct ast_channel *gtalk_request(const char *type, int format, void *data, int *cause); +static struct ast_channel *gtalk_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause); static int gtalk_digit(struct ast_channel *ast, char digit, unsigned int duration); static int gtalk_digit_begin(struct ast_channel *ast, char digit); static int gtalk_digit_end(struct ast_channel *ast, char digit, unsigned int duration); @@ -986,7 +986,7 @@ static struct gtalk_pvt *gtalk_alloc(struct gtalk *client, const char *us, const } /*! \brief Start new gtalk channel */ -static struct ast_channel *gtalk_new(struct gtalk *client, struct gtalk_pvt *i, int state, const char *title) +static struct ast_channel *gtalk_new(struct gtalk *client, struct gtalk_pvt *i, int state, const char *title, const char *linkedid) { struct ast_channel *tmp; int fmt; @@ -997,7 +997,7 @@ static struct ast_channel *gtalk_new(struct gtalk *client, struct gtalk_pvt *i, n2 = title; else n2 = i->us; - tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, client->accountcode, i->exten, client->context, client->amaflags, "Gtalk/%s-%04lx", n2, ast_random() & 0xffff); + tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, linkedid, client->accountcode, i->exten, client->context, client->amaflags, "Gtalk/%s-%04lx", n2, ast_random() & 0xffff); if (!tmp) { ast_log(LOG_WARNING, "Unable to allocate Gtalk channel structure!\n"); return NULL; @@ -1191,7 +1191,7 @@ static int gtalk_newcall(struct gtalk *client, ikspak *pak) return -1; } - chan = gtalk_new(client, p, AST_STATE_DOWN, pak->from->user); + chan = gtalk_new(client, p, AST_STATE_DOWN, pak->from->user, NULL); if (!chan) { gtalk_free_pvt(client, p); return -1; @@ -1634,7 +1634,7 @@ static int gtalk_hangup(struct ast_channel *ast) } /*! \brief Part of PBX interface */ -static struct ast_channel *gtalk_request(const char *type, int format, void *data, int *cause) +static struct ast_channel *gtalk_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause) { struct gtalk_pvt *p = NULL; struct gtalk *client = NULL; @@ -1673,7 +1673,7 @@ static struct ast_channel *gtalk_request(const char *type, int format, void *dat ASTOBJ_WRLOCK(client); p = gtalk_alloc(client, strchr(sender, '@') ? sender : client->connection->jid->full, strchr(to, '@') ? to : client->user, NULL); if (p) - chan = gtalk_new(client, p, AST_STATE_DOWN, to); + chan = gtalk_new(client, p, AST_STATE_DOWN, to, requestor ? requestor->linkedid : NULL); ASTOBJ_UNLOCK(client); return chan; diff --git a/channels/chan_h323.c b/channels/chan_h323.c index fd329538a..a4135a976 100644 --- a/channels/chan_h323.c +++ b/channels/chan_h323.c @@ -230,7 +230,7 @@ static void delete_users(void); static void delete_aliases(void); static void prune_peers(void); -static struct ast_channel *oh323_request(const char *type, int format, void *data, int *cause); +static struct ast_channel *oh323_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause); static int oh323_digit_begin(struct ast_channel *c, char digit); static int oh323_digit_end(struct ast_channel *c, char digit, unsigned int duration); static int oh323_call(struct ast_channel *c, char *dest, int timeout); @@ -994,7 +994,7 @@ static int __oh323_rtp_create(struct oh323_pvt *pvt) } /*! \brief Private structure should be locked on a call */ -static struct ast_channel *__oh323_new(struct oh323_pvt *pvt, int state, const char *host) +static struct ast_channel *__oh323_new(struct oh323_pvt *pvt, int state, const char *host, const char *linkedid) { struct ast_channel *ch; char *cid_num, *cid_name; @@ -1012,7 +1012,7 @@ static struct ast_channel *__oh323_new(struct oh323_pvt *pvt, int state, const c /* Don't hold a oh323_pvt lock while we allocate a chanel */ ast_mutex_unlock(&pvt->lock); - ch = ast_channel_alloc(1, state, cid_num, cid_name, pvt->accountcode, pvt->exten, pvt->context, pvt->amaflags, "H323/%s", host); + ch = ast_channel_alloc(1, state, cid_num, cid_name, pvt->accountcode, pvt->exten, pvt->context, linkedid, pvt->amaflags, "H323/%s", host); /* Update usage counter */ ast_module_ref(ast_module_info->self); ast_mutex_lock(&pvt->lock); @@ -1717,7 +1717,7 @@ static int create_addr(struct oh323_pvt *pvt, char *opeer) return 0; } } -static struct ast_channel *oh323_request(const char *type, int format, void *data, int *cause) +static struct ast_channel *oh323_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause) { int oldformat; struct oh323_pvt *pvt; @@ -1793,7 +1793,7 @@ static struct ast_channel *oh323_request(const char *type, int format, void *dat ast_mutex_unlock(&caplock); ast_mutex_lock(&pvt->lock); - tmpc = __oh323_new(pvt, AST_STATE_DOWN, tmp1); + tmpc = __oh323_new(pvt, AST_STATE_DOWN, tmp1, requestor ? requestor->linkedid : NULL); ast_mutex_unlock(&pvt->lock); if (!tmpc) { oh323_destroy(pvt); @@ -2277,7 +2277,7 @@ static int answer_call(unsigned call_reference, const char *token) } /* allocate a channel and tell asterisk about it */ - c = __oh323_new(pvt, AST_STATE_RINGING, pvt->cd.call_token); + c = __oh323_new(pvt, AST_STATE_RINGING, pvt->cd.call_token, NULL); /* And release when done */ ast_mutex_unlock(&pvt->lock); diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c index 89ec84335..3b6902e13 100644 --- a/channels/chan_iax2.c +++ b/channels/chan_iax2.c @@ -1104,7 +1104,7 @@ static int send_command_final(struct chan_iax2_pvt *, char, int, unsigned int, c static int send_command_immediate(struct chan_iax2_pvt *, char, int, unsigned int, const unsigned char *, int, int); static int send_command_locked(unsigned short callno, char, int, unsigned int, const unsigned char *, int, int); static int send_command_transfer(struct chan_iax2_pvt *, char, int, unsigned int, const unsigned char *, int); -static struct ast_channel *iax2_request(const char *type, int format, void *data, int *cause); +static struct ast_channel *iax2_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause); static struct ast_frame *iax2_read(struct ast_channel *c); static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int temponly); static struct iax2_user *build_user(const char *name, struct ast_variable *v, struct ast_variable *alt, int temponly); @@ -4516,7 +4516,7 @@ static int iax2_getpeertrunk(struct sockaddr_in sin) } /*! \brief Create new call, interface with the PBX core */ -static struct ast_channel *ast_iax2_new(int callno, int state, int capability) +static struct ast_channel *ast_iax2_new(int callno, int state, int capability, const char *linkedid) { struct ast_channel *tmp; struct chan_iax2_pvt *i; @@ -4529,7 +4529,7 @@ static struct ast_channel *ast_iax2_new(int callno, int state, int capability) /* Don't hold call lock */ ast_mutex_unlock(&iaxsl[callno]); - tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, i->accountcode, i->exten, i->context, i->amaflags, "IAX2/%s-%d", i->host, i->callno); + tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, i->accountcode, i->exten, i->context, linkedid, i->amaflags, "IAX2/%s-%d", i->host, i->callno); ast_mutex_lock(&iaxsl[callno]); if (i != iaxs[callno]) { if (tmp) { @@ -8003,8 +8003,8 @@ static int iax_park(struct ast_channel *chan1, struct ast_channel *chan2) struct iax_dual *d; struct ast_channel *chan1m, *chan2m; pthread_t th; - chan1m = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, chan2->accountcode, chan1->exten, chan1->context, chan1->amaflags, "Parking/%s", chan1->name); - chan2m = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, chan2->accountcode, chan2->exten, chan2->context, chan2->amaflags, "IAXPeer/%s",chan2->name); + chan1m = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, chan2->accountcode, chan1->exten, chan1->context, chan1->linkedid, chan1->amaflags, "Parking/%s", chan1->name); + chan2m = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, chan2->accountcode, chan2->exten, chan2->context, chan2->linkedid, chan2->amaflags, "IAXPeer/%s", chan2->name); if (chan2m && chan1m) { /* Make formats okay */ chan1m->readformat = chan1->readformat; @@ -8512,6 +8512,35 @@ static struct ast_custom_function iaxvar_function = { .write = acf_iaxvar_write, }; +static void set_hangup_source_and_cause(int callno, unsigned char causecode) +{ + int locked = 0; + struct ast_channel *owner=NULL; + + do { + if (ast_channel_trylock(iaxs[callno]->owner)) { + DEADLOCK_AVOIDANCE(&iaxsl[callno]); + } + else { + locked = 1; + owner = iaxs[callno]->owner; + } + } + while (!locked && iaxs[callno] && iaxs[callno]->owner); + + if (iaxs[callno] && iaxs[callno]->owner) { + if (causecode) { + iaxs[callno]->owner->hangupcause = causecode; + } + ast_set_hangupsource(iaxs[callno]->owner, iaxs[callno]->owner->name, 0); + ast_channel_unlock(owner); + } + if (locked) { + ast_channel_unlock(owner); + } +} + + static int socket_process(struct iax2_thread *thread) { struct sockaddr_in sin; @@ -8853,7 +8882,7 @@ static int socket_process(struct iax2_thread *thread) (f.frametype == AST_FRAME_IAX)) { if (ast_test_flag64(iaxs[fr->callno], IAX_DELAYPBXSTART)) { ast_clear_flag64(iaxs[fr->callno], IAX_DELAYPBXSTART); - if (!ast_iax2_new(fr->callno, AST_STATE_RING, iaxs[fr->callno]->chosenformat)) { + if (!ast_iax2_new(fr->callno, AST_STATE_RING, iaxs[fr->callno]->chosenformat, NULL)) { ast_mutex_unlock(&iaxsl[fr->callno]); return 1; } @@ -9276,17 +9305,28 @@ retryowner: case IAX_COMMAND_HANGUP: ast_set_flag64(iaxs[fr->callno], IAX_ALREADYGONE); ast_debug(1, "Immediately destroying %d, having received hangup\n", fr->callno); - /* Set hangup cause according to remote */ - if (ies.causecode && iaxs[fr->callno]->owner) - iaxs[fr->callno]->owner->hangupcause = ies.causecode; + /* Set hangup cause according to remote and hangupsource */ + if (iaxs[fr->callno]->owner) { + set_hangup_source_and_cause(fr->callno, ies.causecode); + if (!iaxs[fr->callno]) { + ast_mutex_unlock(&iaxsl[fr->callno]); + return 1; + } + } + /* Send ack immediately, before we destroy */ send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno); iax2_destroy(fr->callno); break; case IAX_COMMAND_REJECT: - /* Set hangup cause according to remote */ - if (ies.causecode && iaxs[fr->callno]->owner) - iaxs[fr->callno]->owner->hangupcause = ies.causecode; + /* Set hangup cause according to remote and hangup source */ + if (iaxs[fr->callno]->owner) { + set_hangup_source_and_cause(fr->callno, ies.causecode); + if (!iaxs[fr->callno]) { + ast_mutex_unlock(&iaxsl[fr->callno]); + return 1; + } + } if (!ast_test_flag64(iaxs[fr->callno], IAX_PROVISION)) { if (iaxs[fr->callno]->owner && authdebug) @@ -9668,7 +9708,7 @@ retryowner2: using_prefs); ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED); - if (!(c = ast_iax2_new(fr->callno, AST_STATE_RING, format))) + if (!(c = ast_iax2_new(fr->callno, AST_STATE_RING, format, NULL))) iax2_destroy(fr->callno); else if (ies.vars) { struct ast_datastore *variablestore; @@ -9737,7 +9777,7 @@ immediatedial: ast_verb(3, "Accepting DIAL from %s, formats = 0x%x\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat); ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED); send_command(iaxs[fr->callno], AST_FRAME_CONTROL, AST_CONTROL_PROGRESS, 0, NULL, 0, -1); - if (!(c = ast_iax2_new(fr->callno, AST_STATE_RING, iaxs[fr->callno]->peerformat))) + if (!(c = ast_iax2_new(fr->callno, AST_STATE_RING, iaxs[fr->callno]->peerformat, NULL))) iax2_destroy(fr->callno); else if (ies.vars) { struct ast_datastore *variablestore; @@ -10570,7 +10610,7 @@ static void free_context(struct iax2_context *con) } } -static struct ast_channel *iax2_request(const char *type, int format, void *data, int *cause) +static struct ast_channel *iax2_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause) { int callno; int res; @@ -10622,7 +10662,7 @@ static struct ast_channel *iax2_request(const char *type, int format, void *data if (cai.found) ast_string_field_set(iaxs[callno], host, pds.peer); - c = ast_iax2_new(callno, AST_STATE_DOWN, cai.capability); + c = ast_iax2_new(callno, AST_STATE_DOWN, cai.capability, requestor ? requestor->linkedid : NULL); ast_mutex_unlock(&iaxsl[callno]); @@ -11086,7 +11126,7 @@ static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, st strsep(&context, "@"); if (ast_strlen_zero(context)) context = "default"; - peer->mwi_event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, NULL, + peer->mwi_event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, "IAX MWI subscription", NULL, AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox, AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context, AST_EVENT_IE_END); diff --git a/channels/chan_jingle.c b/channels/chan_jingle.c index 01909f1f8..da98986eb 100644 --- a/channels/chan_jingle.c +++ b/channels/chan_jingle.c @@ -168,7 +168,7 @@ static int global_capability = AST_FORMAT_ULAW | AST_FORMAT_ALAW | AST_FORMAT_GS AST_MUTEX_DEFINE_STATIC(jinglelock); /*!< Protect the interface list (of jingle_pvt's) */ /* Forward declarations */ -static struct ast_channel *jingle_request(const char *type, int format, void *data, int *cause); +static struct ast_channel *jingle_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause); static int jingle_digit_begin(struct ast_channel *ast, char digit); static int jingle_digit_end(struct ast_channel *ast, char digit, unsigned int duration); static int jingle_call(struct ast_channel *ast, char *dest, int timeout); @@ -789,7 +789,7 @@ static struct jingle_pvt *jingle_alloc(struct jingle *client, const char *from, } /*! \brief Start new jingle channel */ -static struct ast_channel *jingle_new(struct jingle *client, struct jingle_pvt *i, int state, const char *title) +static struct ast_channel *jingle_new(struct jingle *client, struct jingle_pvt *i, int state, const char *title, const char *linkedid) { struct ast_channel *tmp; int fmt; @@ -800,7 +800,7 @@ static struct ast_channel *jingle_new(struct jingle *client, struct jingle_pvt * str = title; else str = i->them; - tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, "", "", "", 0, "Jingle/%s-%04lx", str, ast_random() & 0xffff); + tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, "", "", "", linkedid, 0, "Jingle/%s-%04lx", str, ast_random() & 0xffff); if (!tmp) { ast_log(LOG_WARNING, "Unable to allocate Jingle channel structure!\n"); return NULL; @@ -980,7 +980,7 @@ static int jingle_newcall(struct jingle *client, ikspak *pak) ast_log(LOG_WARNING, "Unable to allocate jingle structure!\n"); return -1; } - chan = jingle_new(client, p, AST_STATE_DOWN, pak->from->user); + chan = jingle_new(client, p, AST_STATE_DOWN, pak->from->user, NULL); if (!chan) { jingle_free_pvt(client, p); return -1; @@ -1457,7 +1457,7 @@ static int jingle_hangup(struct ast_channel *ast) } /*! \brief Part of PBX interface */ -static struct ast_channel *jingle_request(const char *request_type, int format, void *data, int *cause) +static struct ast_channel *jingle_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause) { struct jingle_pvt *p = NULL; struct jingle *client = NULL; @@ -1495,7 +1495,7 @@ static struct ast_channel *jingle_request(const char *request_type, int format, ASTOBJ_WRLOCK(client); p = jingle_alloc(client, to, NULL); if (p) - chan = jingle_new(client, p, AST_STATE_DOWN, to); + chan = jingle_new(client, p, AST_STATE_DOWN, to, requestor ? requestor->linkedid : NULL); ASTOBJ_UNLOCK(client); return chan; diff --git a/channels/chan_local.c b/channels/chan_local.c index 329b9828e..df1a2c454 100644 --- a/channels/chan_local.c +++ b/channels/chan_local.c @@ -60,7 +60,7 @@ static struct ast_jb_conf g_jb_conf = { .impl = "", }; -static struct ast_channel *local_request(const char *type, int format, void *data, int *cause); +static struct ast_channel *local_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause); static int local_digit_begin(struct ast_channel *ast, char digit); static int local_digit_end(struct ast_channel *ast, char digit, unsigned int duration); static int local_call(struct ast_channel *ast, char *dest, int timeout); @@ -780,7 +780,7 @@ static struct local_pvt *local_alloc(const char *data, int format) } /*! \brief Start new local channel */ -static struct ast_channel *local_new(struct local_pvt *p, int state) +static struct ast_channel *local_new(struct local_pvt *p, int state, const char *linkedid) { struct ast_channel *tmp = NULL, *tmp2 = NULL; int randnum = ast_random() & 0xffff, fmt = 0; @@ -798,8 +798,8 @@ static struct ast_channel *local_new(struct local_pvt *p, int state) ama = p->owner->amaflags; else ama = 0; - if (!(tmp = ast_channel_alloc(1, state, 0, 0, t, p->exten, p->context, ama, "Local/%s@%s-%04x;1", p->exten, p->context, randnum)) - || !(tmp2 = ast_channel_alloc(1, AST_STATE_RING, 0, 0, t, p->exten, p->context, ama, "Local/%s@%s-%04x;2", p->exten, p->context, randnum))) { + if (!(tmp = ast_channel_alloc(1, state, 0, 0, t, p->exten, p->context, linkedid, ama, "Local/%s@%s-%04x;1", p->exten, p->context, randnum)) + || !(tmp2 = ast_channel_alloc(1, AST_STATE_RING, 0, 0, t, p->exten, p->context, linkedid, ama, "Local/%s@%s-%04x;2", p->exten, p->context, randnum))) { if (tmp) { tmp = ast_channel_release(tmp); } @@ -843,14 +843,14 @@ static struct ast_channel *local_new(struct local_pvt *p, int state) } /*! \brief Part of PBX interface */ -static struct ast_channel *local_request(const char *type, int format, void *data, int *cause) +static struct ast_channel *local_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause) { struct local_pvt *p = NULL; struct ast_channel *chan = NULL; /* Allocate a new private structure and then Asterisk channel */ if ((p = local_alloc(data, format))) { - if (!(chan = local_new(p, AST_STATE_DOWN))) { + if (!(chan = local_new(p, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL))) { AST_LIST_LOCK(&locals); AST_LIST_REMOVE(&locals, p, list); AST_LIST_UNLOCK(&locals); diff --git a/channels/chan_mgcp.c b/channels/chan_mgcp.c index be38e87b6..d682c76bb 100644 --- a/channels/chan_mgcp.c +++ b/channels/chan_mgcp.c @@ -418,7 +418,7 @@ static void dump_cmd_queues(struct mgcp_endpoint *p, struct mgcp_subchannel *sub static char *mgcp_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); static int reload_config(int reload); -static struct ast_channel *mgcp_request(const char *type, int format, void *data, int *cause); +static struct ast_channel *mgcp_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause); static int mgcp_call(struct ast_channel *ast, char *dest, int timeout); static int mgcp_hangup(struct ast_channel *ast); static int mgcp_answer(struct ast_channel *ast); @@ -1466,13 +1466,13 @@ static int mgcp_indicate(struct ast_channel *ast, int ind, const void *data, siz return res; } -static struct ast_channel *mgcp_new(struct mgcp_subchannel *sub, int state) +static struct ast_channel *mgcp_new(struct mgcp_subchannel *sub, int state, const char *linkedid) { struct ast_channel *tmp; struct mgcp_endpoint *i = sub->parent; int fmt; - tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, i->accountcode, i->exten, i->context, i->amaflags, "MGCP/%s@%s-%d", i->name, i->parent->name, sub->id); + tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, linkedid, i->accountcode, i->exten, i->context, i->amaflags, "MGCP/%s@%s-%d", i->name, i->parent->name, sub->id); if (tmp) { tmp->tech = &mgcp_tech; tmp->nativeformats = i->capability; @@ -2967,7 +2967,7 @@ static void handle_hd_hf(struct mgcp_subchannel *sub, char *ev) #else transmit_notify_request(sub, "G/rt"); #endif - c = mgcp_new(sub, AST_STATE_RING); + c = mgcp_new(sub, AST_STATE_RING, NULL); if (!c) { ast_log(LOG_WARNING, "Unable to start PBX on channel %s@%s\n", p->name, p->parent->name); transmit_notify_request(sub, "G/cg"); @@ -2979,7 +2979,7 @@ static void handle_hd_hf(struct mgcp_subchannel *sub, char *ev) } else { transmit_notify_request(sub, "L/dl"); } - c = mgcp_new(sub, AST_STATE_DOWN); + c = mgcp_new(sub, AST_STATE_DOWN, NULL); if (c) { if (ast_pthread_create_detached(&t, NULL, mgcp_ss, c)) { ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno)); @@ -3489,7 +3489,7 @@ static int restart_monitor(void) return 0; } -static struct ast_channel *mgcp_request(const char *type, int format, void *data, int *cause) +static struct ast_channel *mgcp_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause) { int oldformat; struct mgcp_subchannel *sub; @@ -3533,7 +3533,7 @@ static struct ast_channel *mgcp_request(const char *type, int format, void *data ast_mutex_unlock(&sub->lock); return NULL; } - tmpc = mgcp_new(sub->owner ? sub->next : sub, AST_STATE_DOWN); + tmpc = mgcp_new(sub->owner ? sub->next : sub, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL); ast_mutex_unlock(&sub->lock); if (!tmpc) ast_log(LOG_WARNING, "Unable to make channel for '%s'\n", tmp); @@ -3726,7 +3726,7 @@ static struct mgcp_gateway *build_gateway(char *cat, struct ast_variable *v) strsep(&cntx, "@"); if (ast_strlen_zero(cntx)) cntx = "default"; - e->mwi_event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, NULL, + e->mwi_event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, "MGCP MWI subscription", NULL, AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mbox, AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, cntx, AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_EXISTS, diff --git a/channels/chan_misdn.c b/channels/chan_misdn.c index 44e096d3d..3a11f74f1 100644 --- a/channels/chan_misdn.c +++ b/channels/chan_misdn.c @@ -657,7 +657,7 @@ static int *misdn_ports; static void chan_misdn_log(int level, int port, char *tmpl, ...) __attribute__((format(printf, 3, 4))); -static struct ast_channel *misdn_new(struct chan_list *cl, int state, char *exten, char *callerid, int format, int port, int c); +static struct ast_channel *misdn_new(struct chan_list *cl, int state, char *exten, char *callerid, int format, const char *linkedid, int port, int c); static void send_digit_to_chan(struct chan_list *cl, char digit); static void hangup_chan(struct chan_list *ch); @@ -7468,7 +7468,7 @@ static struct chan_list *init_chan_list(int orig) return cl; } -static struct ast_channel *misdn_request(const char *type, int format, void *data, int *cause) +static struct ast_channel *misdn_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause) { struct ast_channel *ast; char group[BUFFERSIZE + 1] = ""; @@ -7694,7 +7694,7 @@ static struct ast_channel *misdn_request(const char *type, int format, void *dat } cl->bc = newbc; - ast = misdn_new(cl, AST_STATE_RESERVED, args.ext, NULL, format, port, channel); + ast = misdn_new(cl, AST_STATE_RESERVED, args.ext, NULL, format, requestor ? requestor->linkedid : NULL, port, channel); if (!ast) { ast_log(LOG_ERROR, "Could not create Asterisk channel for Dial(%s)\n", dial_str); return NULL; @@ -7799,7 +7799,7 @@ static void update_name(struct ast_channel *tmp, int port, int c) } } -static struct ast_channel *misdn_new(struct chan_list *chlist, int state, char *exten, char *callerid, int format, int port, int c) +static struct ast_channel *misdn_new(struct chan_list *chlist, int state, char *exten, char *callerid, int format, const char *linkedid, int port, int c) { struct ast_channel *tmp; char *cid_name = 0, *cid_num = 0; @@ -7821,7 +7821,7 @@ static struct ast_channel *misdn_new(struct chan_list *chlist, int state, char ast_callerid_parse(callerid, &cid_name, &cid_num); } - tmp = ast_channel_alloc(1, state, cid_num, cid_name, "", exten, "", 0, "%s/%s%d-u%d", misdn_type, c ? "" : "tmp", chan_offset + c, glob_channel++); + tmp = ast_channel_alloc(1, state, cid_num, cid_name, "", exten, "", linkedid, 0, "%s/%s%d-u%d", misdn_type, c ? "" : "tmp", chan_offset + c, glob_channel++); if (tmp) { chan_misdn_log(2, 0, " --> * NEW CHANNEL dialed:%s caller:%s\n", exten, callerid); @@ -8436,7 +8436,7 @@ static void misdn_cc_pbx_notify(long record_id, const struct misdn_cc_notify *no /* Create a channel to notify with */ snprintf(id_str, sizeof(id_str), "%ld", record_id); chan = ast_channel_alloc(0, AST_STATE_DOWN, id_str, NULL, NULL, - notify->exten, notify->context, 0, + notify->exten, notify->context, NULL, 0, "mISDN-CC/%ld-%X", record_id, (unsigned) ++sequence); if (!chan) { ast_log(LOG_ERROR, "Unable to allocate channel!\n"); @@ -9581,7 +9581,7 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) ch->l3id = bc->l3_id; ch->addr = bc->addr; - chan = misdn_new(ch, AST_STATE_RESERVED, bc->dialed.number, bc->caller.number, AST_FORMAT_ALAW, bc->port, bc->channel); + chan = misdn_new(ch, AST_STATE_RESERVED, bc->dialed.number, bc->caller.number, AST_FORMAT_ALAW, NULL, bc->port, bc->channel); if (!chan) { misdn_lib_send_event(bc,EVENT_RELEASE_COMPLETE); ast_log(LOG_ERROR, "cb_events: misdn_new failed !\n"); diff --git a/channels/chan_multicast_rtp.c b/channels/chan_multicast_rtp.c index 589fa1008..d2bbc6875 100644 --- a/channels/chan_multicast_rtp.c +++ b/channels/chan_multicast_rtp.c @@ -52,7 +52,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") static const char tdesc[] = "Multicast RTP Paging Channel Driver"; /* Forward declarations */ -static struct ast_channel *multicast_rtp_request(const char *type, int format, void *data, int *cause); +static struct ast_channel *multicast_rtp_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause); static int multicast_rtp_call(struct ast_channel *ast, char *dest, int timeout); static int multicast_rtp_hangup(struct ast_channel *ast); static struct ast_frame *multicast_rtp_read(struct ast_channel *ast); @@ -107,7 +107,7 @@ static int multicast_rtp_hangup(struct ast_channel *ast) } /*! \brief Function called when we should prepare to call the destination */ -static struct ast_channel *multicast_rtp_request(const char *type, int format, void *data, int *cause) +static struct ast_channel *multicast_rtp_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause) { char *tmp = ast_strdupa(data), *multicast_type = tmp, *destination, *control; struct ast_rtp_instance *instance; @@ -140,7 +140,7 @@ static struct ast_channel *multicast_rtp_request(const char *type, int format, v goto failure; } - if (!(chan = ast_channel_alloc(1, AST_STATE_DOWN, "", "", "", "", "", 0, "MulticastRTP/%p", instance))) { + if (!(chan = ast_channel_alloc(1, AST_STATE_DOWN, "", "", "", "", "", requestor ? requestor->linkedid : "", 0, "MulticastRTP/%p", instance))) { ast_rtp_instance_destroy(instance); goto failure; } diff --git a/channels/chan_nbs.c b/channels/chan_nbs.c index 8729f1074..89c5f647c 100644 --- a/channels/chan_nbs.c +++ b/channels/chan_nbs.c @@ -66,7 +66,7 @@ struct nbs_pvt { struct ast_module_user *u; /*! for holding a reference to this module */ }; -static struct ast_channel *nbs_request(const char *type, int format, void *data, int *cause); +static struct ast_channel *nbs_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause); static int nbs_call(struct ast_channel *ast, char *dest, int timeout); static int nbs_hangup(struct ast_channel *ast); static struct ast_frame *nbs_xread(struct ast_channel *ast); @@ -219,10 +219,10 @@ static int nbs_xwrite(struct ast_channel *ast, struct ast_frame *frame) return 0; } -static struct ast_channel *nbs_new(struct nbs_pvt *i, int state) +static struct ast_channel *nbs_new(struct nbs_pvt *i, int state, const char *linkedid) { struct ast_channel *tmp; - tmp = ast_channel_alloc(1, state, 0, 0, "", "s", context, 0, "NBS/%s", i->stream); + tmp = ast_channel_alloc(1, state, 0, 0, "", "s", context, linkedid, 0, "NBS/%s", i->stream); if (tmp) { tmp->tech = &nbs_tech; ast_channel_set_fd(tmp, 0, nbs_fd(i->nbs)); @@ -251,7 +251,7 @@ static struct ast_channel *nbs_new(struct nbs_pvt *i, int state) } -static struct ast_channel *nbs_request(const char *type, int format, void *data, int *cause) +static struct ast_channel *nbs_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause) { int oldformat; struct nbs_pvt *p; @@ -265,7 +265,7 @@ static struct ast_channel *nbs_request(const char *type, int format, void *data, } p = nbs_alloc(data); if (p) { - tmp = nbs_new(p, AST_STATE_DOWN); + tmp = nbs_new(p, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL); if (!tmp) nbs_destroy(p); } diff --git a/channels/chan_oss.c b/channels/chan_oss.c index b3ff44883..9b2201a48 100644 --- a/channels/chan_oss.c +++ b/channels/chan_oss.c @@ -332,7 +332,8 @@ static struct chan_oss_pvt oss_default = { static int setformat(struct chan_oss_pvt *o, int mode); -static struct ast_channel *oss_request(const char *type, int format, void *data, int *cause); +static struct ast_channel *oss_request(const char *type, int format, const struct ast_channel *requestor, + void *data, int *cause); static int oss_digit_begin(struct ast_channel *c, char digit); static int oss_digit_end(struct ast_channel *c, char digit, unsigned int duration); static int oss_text(struct ast_channel *c, const char *text); @@ -787,11 +788,11 @@ static int oss_indicate(struct ast_channel *c, int cond, const void *data, size_ /*! * \brief allocate a new channel. */ -static struct ast_channel *oss_new(struct chan_oss_pvt *o, char *ext, char *ctx, int state) +static struct ast_channel *oss_new(struct chan_oss_pvt *o, char *ext, char *ctx, int state, const char *linkedid) { struct ast_channel *c; - c = ast_channel_alloc(1, state, o->cid_num, o->cid_name, "", ext, ctx, 0, "Console/%s", o->device + 5); + c = ast_channel_alloc(1, state, o->cid_num, o->cid_name, "", ext, ctx, linkedid, 0, "Console/%s", o->device + 5); if (c == NULL) return NULL; c->tech = &oss_tech; @@ -830,7 +831,7 @@ static struct ast_channel *oss_new(struct chan_oss_pvt *o, char *ext, char *ctx, return c; } -static struct ast_channel *oss_request(const char *type, int format, void *data, int *cause) +static struct ast_channel *oss_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause) { struct ast_channel *c; struct chan_oss_pvt *o; @@ -858,7 +859,7 @@ static struct ast_channel *oss_request(const char *type, int format, void *data, *cause = AST_CAUSE_BUSY; return NULL; } - c = oss_new(o, NULL, NULL, AST_STATE_DOWN); + c = oss_new(o, NULL, NULL, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL); if (c == NULL) { ast_log(LOG_WARNING, "Unable to create new OSS channel\n"); return NULL; @@ -1117,7 +1118,7 @@ static char *console_dial(struct ast_cli_entry *e, int cmd, struct ast_cli_args myc = o->ctx; if (ast_exists_extension(NULL, myc, mye, 1, NULL)) { o->hookstate = 1; - oss_new(o, mye, myc, AST_STATE_RINGING); + oss_new(o, mye, myc, AST_STATE_RINGING, NULL); } else ast_cli(a->fd, "No such extension '%s' in context '%s'\n", mye, myc); if (s) diff --git a/channels/chan_phone.c b/channels/chan_phone.c index dd1578cb8..d3131210c 100644 --- a/channels/chan_phone.c +++ b/channels/chan_phone.c @@ -150,7 +150,7 @@ static struct phone_pvt { static char cid_num[AST_MAX_EXTENSION]; static char cid_name[AST_MAX_EXTENSION]; -static struct ast_channel *phone_request(const char *type, int format, void *data, int *cause); +static struct ast_channel *phone_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause); static int phone_digit_begin(struct ast_channel *ast, char digit); static int phone_digit_end(struct ast_channel *ast, char digit, unsigned int duration); static int phone_call(struct ast_channel *ast, char *dest, int timeout); @@ -844,11 +844,11 @@ static int phone_write(struct ast_channel *ast, struct ast_frame *frame) return 0; } -static struct ast_channel *phone_new(struct phone_pvt *i, int state, char *cntx) +static struct ast_channel *phone_new(struct phone_pvt *i, int state, char *cntx, const char *linkedid) { struct ast_channel *tmp; struct phone_codec_data queried_codec; - tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, "", i->ext, i->context, 0, "Phone/%s", i->dev + 5); + tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, "", i->ext, i->context, linkedid, 0, "Phone/%s", i->dev + 5); if (tmp) { tmp->tech = cur_tech; ast_channel_set_fd(tmp, 0, i->fd); @@ -941,14 +941,14 @@ static void phone_check_exception(struct phone_pvt *i) !phonee.bits.dtmf_ready) && ast_exists_extension(NULL, i->context, i->ext, 1, i->cid_num)) { /* It's a valid extension in its context, get moving! */ - phone_new(i, AST_STATE_RING, i->context); + phone_new(i, AST_STATE_RING, i->context, NULL); /* No need to restart monitor, we are the monitor */ } else if (!ast_canmatch_extension(NULL, i->context, i->ext, 1, i->cid_num)) { /* There is nothing in the specified extension that can match anymore. Try the default */ if (ast_exists_extension(NULL, "default", i->ext, 1, i->cid_num)) { /* Check the default, too... */ - phone_new(i, AST_STATE_RING, "default"); + phone_new(i, AST_STATE_RING, "default", NULL); /* XXX This should probably be justified better XXX */ } else if (!ast_canmatch_extension(NULL, "default", i->ext, 1, i->cid_num)) { /* It's not a valid extension, give a busy signal */ @@ -966,7 +966,7 @@ static void phone_check_exception(struct phone_pvt *i) offhook = ioctl(i->fd, PHONE_HOOKSTATE); if (offhook) { if (i->mode == MODE_IMMEDIATE) { - phone_new(i, AST_STATE_RING, i->context); + phone_new(i, AST_STATE_RING, i->context, NULL); } else if (i->mode == MODE_DIALTONE) { ast_module_ref(ast_module_info->self); /* Reset the extension */ @@ -1002,7 +1002,7 @@ static void phone_check_exception(struct phone_pvt *i) } if (phonee.bits.pstn_ring) { ast_verbose("Unit is ringing\n"); - phone_new(i, AST_STATE_RING, i->context); + phone_new(i, AST_STATE_RING, i->context, NULL); } if (phonee.bits.caller_id) ast_verbose("We have caller ID\n"); @@ -1212,7 +1212,7 @@ static struct phone_pvt *mkif(const char *iface, int mode, int txgain, int rxgai return tmp; } -static struct ast_channel *phone_request(const char *type, int format, void *data, int *cause) +static struct ast_channel *phone_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause) { int oldformat; struct phone_pvt *p; @@ -1232,7 +1232,7 @@ static struct ast_channel *phone_request(const char *type, int format, void *dat if (strncmp(name, p->dev + 5, length) == 0 && !isalnum(name[length])) { if (!p->owner) { - tmp = phone_new(p, AST_STATE_DOWN, p->context); + tmp = phone_new(p, AST_STATE_DOWN, p->context, requestor ? requestor->linkedid : NULL); break; } else *cause = AST_CAUSE_BUSY; diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 4a7d00ef9..00042ebe5 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -272,6 +272,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/event.h" #include "asterisk/tcptls.h" #include "asterisk/stun.h" +#include "asterisk/cel.h" /*** DOCUMENTATION <application name="SIPDtmfMode" language="en_US"> @@ -2339,7 +2340,7 @@ enum t38_action_flag { in coming releases. */ /*--- PBX interface functions */ -static struct ast_channel *sip_request_call(const char *type, int format, void *data, int *cause); +static struct ast_channel *sip_request_call(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause); static int sip_devicestate(void *data); static int sip_sendtext(struct ast_channel *ast, const char *text); static int sip_call(struct ast_channel *ast, char *dest, int timeout); @@ -5096,6 +5097,7 @@ static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer) ast_string_field_set(dialog, mohinterpret, peer->mohinterpret); ast_string_field_set(dialog, tohost, peer->tohost); ast_string_field_set(dialog, fullcontact, peer->fullcontact); + ast_string_field_set(dialog, accountcode, peer->accountcode); ast_string_field_set(dialog, context, peer->context); ast_string_field_set(dialog, cid_num, peer->cid_num); ast_string_field_set(dialog, cid_name, peer->cid_name); @@ -6518,7 +6520,7 @@ static int sip_indicate(struct ast_channel *ast, int condition, const void *data and from handle_request_invite for inbound channels */ -static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *title) +static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *title, const char *linkedid) { struct ast_channel *tmp; struct ast_variable *v = NULL; @@ -6546,7 +6548,7 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *tit sip_pvt_unlock(i); /* Don't hold a sip pvt lock while we allocate a channel */ - tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, i->accountcode, i->exten, i->context, i->amaflags, "SIP/%s-%08x", my_name, (int)(long) i); + tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, i->accountcode, i->exten, i->context, linkedid, i->amaflags, "SIP/%s-%08x", my_name, (int)(long) i); } if (!tmp) { @@ -17936,16 +17938,20 @@ static void handle_response_invite(struct sip_pvt *p, int resp, const char *rest /* First we ACK */ xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE); ast_log(LOG_WARNING, "Received response: \"Forbidden\" from '%s'\n", get_header(&p->initreq, "From")); - if (!req->ignore && p->owner) + if (!req->ignore && p->owner) { + ast_set_hangupsource(p->owner, p->owner->name, 0); ast_queue_control(p->owner, AST_CONTROL_CONGESTION); + } pvt_set_needdestroy(p, "received 403 response"); sip_alreadygone(p); break; case 404: /* Not found */ xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE); - if (p->owner && !req->ignore) + if (p->owner && !req->ignore) { + ast_set_hangupsource(p->owner, p->owner->name, 0); ast_queue_control(p->owner, AST_CONTROL_CONGESTION); + } sip_alreadygone(p); break; @@ -18983,8 +18989,8 @@ static int sip_park(struct ast_channel *chan1, struct ast_channel *chan2, struct /* Chan2m: The transferer, chan1m: The transferee */ pthread_t th; - transferee = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, chan1->accountcode, chan1->exten, chan1->context, chan1->amaflags, "Parking/%s", chan1->name); - transferer = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, chan2->accountcode, chan2->exten, chan2->context, chan2->amaflags, "SIPPeer/%s", chan2->name); + transferee = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, chan1->accountcode, chan1->exten, chan1->context, chan1->linkedid, chan1->amaflags, "Parking/%s", chan1->name); + transferer = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, chan2->accountcode, chan2->exten, chan2->context, chan2->linkedid, chan2->amaflags, "SIPPeer/%s", chan2->name); if ((!transferer) || (!transferee)) { if (transferee) { transferee->hangupcause = AST_CAUSE_SWITCH_CONGESTION; @@ -20305,7 +20311,7 @@ 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)); + c = sip_new(p, AST_STATE_DOWN, S_OR(p->peername, NULL), NULL); *recount = 1; /* Save Record-Route for any later requests we make on this dialogue */ @@ -20625,6 +20631,7 @@ static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual * struct sip_pvt *targetcall_pvt; struct ast_party_connected_line connected_to_transferee; struct ast_party_connected_line connected_to_target; + char transferer_linkedid[32]; /* Check if the call ID of the replaces header does exist locally */ if (!(targetcall_pvt = get_sip_pvt_byid_locked(transferer->refer->replaces_callid, transferer->refer->replaces_callid_totag, @@ -20685,6 +20692,8 @@ static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual * ast_set_flag(&transferer->flags[0], SIP_DEFER_BYE_ON_TRANSFER); /* Delay hangup */ + ast_copy_string(transferer_linkedid, transferer->owner->linkedid, sizeof(transferer_linkedid)); + /* Perform the transfer */ manager_event(EVENT_FLAG_CALL, "Transfer", "TransferMethod: SIP\r\nTransferType: Attended\r\nChannel: %s\r\nUniqueid: %s\r\nSIP-Callid: %s\r\nTargetChannel: %s\r\nTargetUniqueid: %s\r\n", transferer->owner->name, @@ -20712,6 +20721,14 @@ static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual * /* Transfer succeeded! */ const char *xfersound = pbx_builtin_getvar_helper(target.chan1, "ATTENDED_TRANSFER_COMPLETE_SOUND"); + while (ast_channel_trylock(target.chan1)) { + sip_pvt_unlock(targetcall_pvt); + sched_yield(); + sip_pvt_lock(targetcall_pvt); + } + ast_cel_report_event(target.chan1, AST_CEL_ATTENDEDTRANSFER, NULL, transferer_linkedid, target.chan2); + ast_channel_unlock(target.chan1); + /* Tell transferer that we're done. */ transmit_notify_with_sipfrag(transferer, seqno, "200 OK", TRUE); append_history(transferer, "Xfer", "Refer succeeded"); @@ -21075,6 +21092,17 @@ static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, int p->refer->refer_to, p->refer->refer_to_context); /* Success - we have a new channel */ ast_debug(3, "%s transfer succeeded. Telling transferer.\n", p->refer->attendedtransfer? "Attended" : "Blind"); + + while (ast_channel_trylock(current.chan1)) { + sip_pvt_unlock(p); + sched_yield(); + sip_pvt_lock(p); + } + + /* XXX - what to we put in CEL 'extra' for attended transfers to external systems? NULL for now */ + ast_cel_report_event(current.chan1, p->refer->attendedtransfer? AST_CEL_ATTENDEDTRANSFER : AST_CEL_BLINDTRANSFER, NULL, p->refer->attendedtransfer ? NULL : p->refer->refer_to, current.chan2); + ast_channel_unlock(current.chan1); + transmit_notify_with_sipfrag(p, seqno, "200 Ok", TRUE); if (p->refer->localtransfer) p->refer->status = REFER_200OK; @@ -21126,8 +21154,10 @@ static int handle_request_cancel(struct sip_pvt *p, struct sip_request *req) update_call_counter(p, DEC_CALL_LIMIT); stop_media_flows(p); /* Immediately stop RTP, VRTP and UDPTL as applicable */ - if (p->owner) + if (p->owner) { + ast_set_hangupsource(p->owner, p->owner->name, 0); ast_queue_hangup(p->owner); + } else sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); if (p->initreq.len > 0) { @@ -21392,6 +21422,7 @@ static int handle_request_bye(struct sip_pvt *p, struct sip_request *req) ast_queue_hangup_with_cause(p->owner, AST_CAUSE_PROTOCOL_ERROR); } } else if (p->owner) { + ast_set_hangupsource(p->owner, p->owner->name, 0); ast_queue_hangup(p->owner); ast_debug(3, "Received bye, issuing owner hangup\n"); } else { @@ -21421,7 +21452,7 @@ static void add_peer_mwi_subs(struct sip_peer *peer) struct sip_mailbox *mailbox; AST_LIST_TRAVERSE(&peer->mailboxes, mailbox, entry) { - mailbox->event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, peer, + mailbox->event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, "SIP mbox event", peer, AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox->mailbox, AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, S_OR(mailbox->context, "default"), AST_EVENT_IE_END); @@ -23248,7 +23279,7 @@ static int sip_devicestate(void *data) * or SIP/host!dnid * \endverbatim */ -static struct ast_channel *sip_request_call(const char *type, int format, void *data, int *cause) +static struct ast_channel *sip_request_call(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause) { struct sip_pvt *p; struct ast_channel *tmpc = NULL; @@ -23394,7 +23425,7 @@ static struct ast_channel *sip_request_call(const char *type, int format, void * p->prefcodec = oldformat; /* Format for this call */ p->jointcapability = oldformat; sip_pvt_lock(p); - tmpc = sip_new(p, AST_STATE_DOWN, host); /* Place the call */ + tmpc = sip_new(p, AST_STATE_DOWN, host, requestor ? requestor->linkedid : NULL); /* Place the call */ if (sip_cfg.callevents) manager_event(EVENT_FLAG_SYSTEM, "ChannelUpdate", "Channel: %s\r\nChanneltype: %s\r\nSIPcallid: %s\r\nSIPfullcontact: %s\r\nPeername: %s\r\n", diff --git a/channels/chan_skinny.c b/channels/chan_skinny.c index 7271a2ee0..d88739b86 100644 --- a/channels/chan_skinny.c +++ b/channels/chan_skinny.c @@ -1371,9 +1371,9 @@ struct skinnysession { AST_LIST_ENTRY(skinnysession) list; }; +static struct ast_channel *skinny_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause); static AST_LIST_HEAD_STATIC(sessions, skinnysession); -static struct ast_channel *skinny_request(const char *type, int format, void *data, int *cause); static int skinny_devicestate(void *data); static int skinny_call(struct ast_channel *ast, char *dest, int timeout); static int skinny_hangup(struct ast_channel *ast); @@ -4348,7 +4348,7 @@ static int skinny_indicate(struct ast_channel *ast, int ind, const void *data, s return 0; } -static struct ast_channel *skinny_new(struct skinny_line *l, int state) +static struct ast_channel *skinny_new(struct skinny_line *l, int state, const char *linkedid) { struct ast_channel *tmp; struct skinny_subchannel *sub; @@ -4361,7 +4361,7 @@ static struct ast_channel *skinny_new(struct skinny_line *l, int state) return NULL; } - tmp = ast_channel_alloc(1, state, l->cid_num, l->cid_name, l->accountcode, l->exten, l->context, l->amaflags, "Skinny/%s@%s-%d", l->name, d->name, callnums); + tmp = ast_channel_alloc(1, state, l->cid_num, l->cid_name, l->accountcode, l->exten, l->context, linkedid, l->amaflags, "Skinny/%s@%s-%d", l->name, d->name, callnums); if (!tmp) { ast_log(LOG_WARNING, "Unable to allocate channel structure\n"); return NULL; @@ -4544,7 +4544,7 @@ static int handle_transfer_button(struct skinny_subchannel *sub) if (!sub->onhold) { skinny_hold(sub); } - c = skinny_new(l, AST_STATE_DOWN); + c = skinny_new(l, AST_STATE_DOWN, NULL); if (c) { newsub = c->tech_pvt; /* point the sub and newsub at each other so we know they are related */ @@ -4822,7 +4822,7 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession break; } - c = skinny_new(l, AST_STATE_DOWN); + c = skinny_new(l, AST_STATE_DOWN, NULL); if (!c) { ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name); } else { @@ -4860,7 +4860,7 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession } if (!sub || !sub->owner) - c = skinny_new(l, AST_STATE_DOWN); + c = skinny_new(l, AST_STATE_DOWN, NULL); else c = sub->owner; @@ -4920,7 +4920,7 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession ast_verb(1, "Received Stimulus: Voicemail(%d/%d)\n", instance, callreference); if (!sub || !sub->owner) { - c = skinny_new(l, AST_STATE_DOWN); + c = skinny_new(l, AST_STATE_DOWN, NULL); } else { c = sub->owner; } @@ -5005,7 +5005,7 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession ast_verb(1, "Received Stimulus: Forward All(%d/%d)\n", instance, callreference); if (!sub || !sub->owner) { - c = skinny_new(l, AST_STATE_DOWN); + c = skinny_new(l, AST_STATE_DOWN, NULL); } else { c = sub->owner; } @@ -5022,7 +5022,7 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession ast_verb(1, "Received Stimulus: Forward Busy (%d/%d)\n", instance, callreference); if (!sub || !sub->owner) { - c = skinny_new(l, AST_STATE_DOWN); + c = skinny_new(l, AST_STATE_DOWN, NULL); } else { c = sub->owner; } @@ -5040,7 +5040,7 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession #if 0 /* Not sure how to handle this yet */ if (!sub || !sub->owner) { - c = skinny_new(l, AST_STATE_DOWN); + c = skinny_new(l, AST_STATE_DOWN, NULL); } else { c = sub->owner; } @@ -5091,7 +5091,7 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession if (sub && sub->owner) { ast_debug(1, "Current subchannel [%s] already has owner\n", sub->owner->name); } else { - c = skinny_new(l, AST_STATE_DOWN); + c = skinny_new(l, AST_STATE_DOWN, NULL); if (c) { sub = c->tech_pvt; l->activesub = sub; @@ -5189,7 +5189,7 @@ static int handle_offhook_message(struct skinny_req *req, struct skinnysession * if (sub && sub->owner) { ast_debug(1, "Current sub [%s] already has owner\n", sub->owner->name); } else { - c = skinny_new(l, AST_STATE_DOWN); + c = skinny_new(l, AST_STATE_DOWN, NULL); if (c) { sub = c->tech_pvt; l->activesub = sub; @@ -5678,7 +5678,7 @@ static int handle_enbloc_call_message(struct skinny_req *req, struct skinnysessi l = sub->parent; } - c = skinny_new(l, AST_STATE_DOWN); + c = skinny_new(l, AST_STATE_DOWN, NULL); if(!c) { ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name); @@ -5792,7 +5792,7 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse } if (!sub || !sub->owner) { - c = skinny_new(l, AST_STATE_DOWN); + c = skinny_new(l, AST_STATE_DOWN, NULL); } else { c = sub->owner; } @@ -5828,7 +5828,7 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse ast_verb(1, "Received Softkey Event: New Call(%d/%d)\n", instance, callreference); /* New Call ALWAYS gets a new sub-channel */ - c = skinny_new(l, AST_STATE_DOWN); + c = skinny_new(l, AST_STATE_DOWN, NULL); sub = c->tech_pvt; /* transmit_ringer_mode(d, SKINNY_RING_OFF); @@ -5898,7 +5898,7 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse ast_verb(1, "Received Softkey Event: Forward All(%d/%d)\n", instance, callreference); if (!sub || !sub->owner) { - c = skinny_new(l, AST_STATE_DOWN); + c = skinny_new(l, AST_STATE_DOWN, NULL); } else { c = sub->owner; } @@ -5916,7 +5916,7 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse ast_verb(1, "Received Softkey Event: Forward Busy (%d/%d)\n", instance, callreference); if (!sub || !sub->owner) { - c = skinny_new(l, AST_STATE_DOWN); + c = skinny_new(l, AST_STATE_DOWN, NULL); } else { c = sub->owner; } @@ -5935,7 +5935,7 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse #if 0 /* Not sure how to handle this yet */ if (!sub || !sub->owner) { - c = skinny_new(l, AST_STATE_DOWN); + c = skinny_new(l, AST_STATE_DOWN, NULL); } else { c = sub->owner; } @@ -6565,7 +6565,7 @@ static int skinny_devicestate(void *data) return get_devicestate(l); } -static struct ast_channel *skinny_request(const char *type, int format, void *data, int *cause) +static struct ast_channel *skinny_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause) { int oldformat; @@ -6592,7 +6592,7 @@ static struct ast_channel *skinny_request(const char *type, int format, void *da return NULL; } ast_verb(3, "skinny_request(%s)\n", tmp); - tmpc = skinny_new(l, AST_STATE_DOWN); + tmpc = skinny_new(l, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL); if (!tmpc) { ast_log(LOG_WARNING, "Unable to make channel for '%s'\n", tmp); } @@ -7020,7 +7020,7 @@ static struct ast_channel *skinny_request(const char *type, int format, void *da strsep(&cfg_context, "@"); if (ast_strlen_zero(cfg_context)) cfg_context = "default"; - l->mwi_event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, l, + l->mwi_event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, "skinny MWI subsciption", l, AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, cfg_mailbox, AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, cfg_context, AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_EXISTS, diff --git a/channels/chan_unistim.c b/channels/chan_unistim.c index 27e8e0cb2..bb90301f9 100644 --- a/channels/chan_unistim.c +++ b/channels/chan_unistim.c @@ -670,13 +670,13 @@ static const char tdesc[] = "UNISTIM Channel Driver"; static const char channel_type[] = "USTM"; /*! Protos */ -static struct ast_channel *unistim_new(struct unistim_subchannel *sub, int state); +static struct ast_channel *unistim_new(struct unistim_subchannel *sub, int state, const char *linkedid); static int load_module(void); static int reload(void); static int unload_module(void); static int reload_config(void); static void show_main_page(struct unistimsession *pte); -static struct ast_channel *unistim_request(const char *type, int format, +static struct ast_channel *unistim_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause); static int unistim_call(struct ast_channel *ast, char *dest, int timeout); static int unistim_hangup(struct ast_channel *ast); @@ -2363,7 +2363,7 @@ static void HandleCallOutgoing(struct unistimsession *s) return; } if (!sub->owner) { /* A call is already in progress ? */ - c = unistim_new(sub, AST_STATE_DOWN); /* No, starting a new one */ + c = unistim_new(sub, AST_STATE_DOWN, NULL); /* No, starting a new one */ if (c) { /* Need to start RTP before calling ast_pbx_run */ if (!sub->rtp) @@ -2411,7 +2411,7 @@ static void HandleCallOutgoing(struct unistimsession *s) } send_tone(s, 0, 0); /* Make new channel */ - c = unistim_new(p->subs[SUB_THREEWAY], AST_STATE_DOWN); + c = unistim_new(p->subs[SUB_THREEWAY], AST_STATE_DOWN, NULL); if (!c) { ast_log(LOG_WARNING, "Cannot allocate new structure on channel %p\n", p); return; @@ -4422,7 +4422,7 @@ static int unistim_send_mwi_to_peer(struct unistimsession *s, unsigned int tick) /*--- unistim_new: Initiate a call in the UNISTIM channel */ /* called from unistim_request (calls from the pbx ) */ -static struct ast_channel *unistim_new(struct unistim_subchannel *sub, int state) +static struct ast_channel *unistim_new(struct unistim_subchannel *sub, int state, const char *linkedid) { struct ast_channel *tmp; struct unistim_line *l; @@ -4438,7 +4438,7 @@ static struct ast_channel *unistim_new(struct unistim_subchannel *sub, int state } l = sub->parent; tmp = ast_channel_alloc(1, state, l->cid_num, NULL, l->accountcode, l->exten, - l->context, l->amaflags, "%s@%s-%d", l->name, l->parent->name, sub->subtype); + l->context, linkedid, l->amaflags, "%s@%s-%d", l->name, l->parent->name, sub->subtype); if (unistimdebug) ast_verb(0, "unistim_new sub=%d (%p) chan=%p\n", sub->subtype, sub, tmp); if (!tmp) { @@ -4617,7 +4617,7 @@ static int restart_monitor(void) /*--- unistim_request: PBX interface function ---*/ /* UNISTIM calls initiated by the PBX arrive here */ -static struct ast_channel *unistim_request(const char *type, int format, void *data, +static struct ast_channel *unistim_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause) { int oldformat; @@ -4660,7 +4660,7 @@ static struct ast_channel *unistim_request(const char *type, int format, void *d return NULL; } sub->parent->capability = format; - tmpc = unistim_new(sub, AST_STATE_DOWN); + tmpc = unistim_new(sub, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL); if (!tmpc) ast_log(LOG_WARNING, "Unable to make channel for '%s'\n", tmp); if (unistimdebug) diff --git a/channels/chan_usbradio.c b/channels/chan_usbradio.c index 2a829a685..b8e9d70a9 100644 --- a/channels/chan_usbradio.c +++ b/channels/chan_usbradio.c @@ -666,8 +666,9 @@ static char *usbradio_active; /* the active device */ static int setformat(struct chan_usbradio_pvt *o, int mode); -static struct ast_channel *usbradio_request(const char *type, int format, void *data -, int *cause); +static struct ast_channel *usbradio_request(const char *type, int format, + const struct ast_channel *requestor, + void *data, int *cause); static int usbradio_digit_begin(struct ast_channel *c, char digit); static int usbradio_digit_end(struct ast_channel *c, char digit, unsigned int duration); static int usbradio_text(struct ast_channel *c, const char *text); @@ -2186,11 +2187,11 @@ static int usbradio_indicate(struct ast_channel *c, int cond, const void *data, /* * allocate a new channel. */ -static struct ast_channel *usbradio_new(struct chan_usbradio_pvt *o, char *ext, char *ctx, int state) +static struct ast_channel *usbradio_new(struct chan_usbradio_pvt *o, char *ext, char *ctx, int state, const char *linkedid) { struct ast_channel *c; - c = ast_channel_alloc(1, state, o->cid_num, o->cid_name, "", ext, ctx, 0, "Radio/%s", o->name); + c = ast_channel_alloc(1, state, o->cid_num, o->cid_name, "", ext, ctx, linkedid, 0, "Radio/%s", o->name); if (c == NULL) return NULL; c->tech = &usbradio_tech; @@ -2229,7 +2230,7 @@ static struct ast_channel *usbradio_new(struct chan_usbradio_pvt *o, char *ext, } /* */ -static struct ast_channel *usbradio_request(const char *type, int format, void *data, int *cause) +static struct ast_channel *usbradio_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause) { struct ast_channel *c; struct chan_usbradio_pvt *o = find_desc(data); @@ -2254,7 +2255,7 @@ static struct ast_channel *usbradio_request(const char *type, int format, void * *cause = AST_CAUSE_BUSY; return NULL; } - c = usbradio_new(o, NULL, NULL, AST_STATE_DOWN); + c = usbradio_new(o, NULL, NULL, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL); if (c == NULL) { ast_log(LOG_WARNING, "Unable to create new usb channel\n"); return NULL; diff --git a/channels/chan_vpb.cc b/channels/chan_vpb.cc index e611ab097..fa82437c0 100644 --- a/channels/chan_vpb.cc +++ b/channels/chan_vpb.cc @@ -329,9 +329,9 @@ static struct vpb_pvt { } *iflist = NULL; -static struct ast_channel *vpb_new(struct vpb_pvt *i, enum ast_channel_state state, const char *context); +static struct ast_channel *vpb_new(struct vpb_pvt *i, enum ast_channel_state state, const char *context, const char *linkedid); static void *do_chanreads(void *pvt); -static struct ast_channel *vpb_request(const char *type, int format, void *data, int *cause); +static struct ast_channel *vpb_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause); static int vpb_digit_begin(struct ast_channel *ast, char digit); static int vpb_digit_end(struct ast_channel *ast, char digit, unsigned int duration); static int vpb_call(struct ast_channel *ast, char *dest, int timeout); @@ -1116,7 +1116,7 @@ static inline int monitor_handle_notowned(struct vpb_pvt *p, VPB_EVENT *e) break; case VPB_RING: if (p->mode == MODE_FXO) /* FXO port ring, start * */ { - vpb_new(p, AST_STATE_RING, p->context); + vpb_new(p, AST_STATE_RING, p->context, NULL); if (UsePolarityCID != 1) { if (p->callerid_type == 1) { ast_verb(4, "Using VPB Caller ID\n"); @@ -1140,7 +1140,7 @@ static inline int monitor_handle_notowned(struct vpb_pvt *p, VPB_EVENT *e) case VPB_STATION_OFFHOOK: if (p->mode == MODE_IMMEDIATE) { - vpb_new(p,AST_STATE_RING, p->context); + vpb_new(p,AST_STATE_RING, p->context, NULL); } else { ast_verb(4, "%s: handle_notowned: playing dialtone\n", p->dev); playtone(p->handle, &Dialtone); @@ -1185,7 +1185,7 @@ static inline int monitor_handle_notowned(struct vpb_pvt *p, VPB_EVENT *e) if (ast_exists_extension(NULL, p->context, p->ext, 1, p->callerid)){ ast_verb(4, "%s: handle_notowned: DTMF IDD timer out, matching on [%s] in [%s]\n", p->dev, p->ext, p->context); - vpb_new(p, AST_STATE_RING, p->context); + vpb_new(p, AST_STATE_RING, p->context, NULL); } } else if (e->data == p->ring_timer_id) { /* We didnt get another ring in time! */ @@ -1261,11 +1261,11 @@ static inline int monitor_handle_notowned(struct vpb_pvt *p, VPB_EVENT *e) vpb_timer_start(p->dtmfidd_timer); } else { ast_verb(4, "%s: handle_notowned: Matched on [%s] in [%s]\n", p->dev, p->ext , p->context); - vpb_new(p, AST_STATE_UP, p->context); + vpb_new(p, AST_STATE_UP, p->context, NULL); } } else if (!ast_canmatch_extension(NULL, p->context, p->ext, 1, p->callerid)) { if (ast_exists_extension(NULL, "default", p->ext, 1, p->callerid)) { - vpb_new(p, AST_STATE_UP, "default"); + vpb_new(p, AST_STATE_UP, "default", NULL); } else if (!ast_canmatch_extension(NULL, "default", p->ext, 1, p->callerid)) { ast_verb(4, "%s: handle_notowned: can't match anything in %s or default\n", p->dev, p->context); playtone(p->handle, &Busytone); @@ -2466,7 +2466,7 @@ static void *do_chanreads(void *pvt) return NULL; } -static struct ast_channel *vpb_new(struct vpb_pvt *me, enum ast_channel_state state, const char *context) +static struct ast_channel *vpb_new(struct vpb_pvt *me, enum ast_channel_state state, const char *context, const char *linkedid) { struct ast_channel *tmp; char cid_num[256]; @@ -2478,7 +2478,7 @@ static struct ast_channel *vpb_new(struct vpb_pvt *me, enum ast_channel_state st } ast_verb(4, "%s: New call for context [%s]\n", me->dev, context); - tmp = ast_channel_alloc(1, state, 0, 0, "", me->ext, me->context, 0, "%s", me->dev); + tmp = ast_channel_alloc(1, state, 0, 0, "", me->ext, me->context, linkedid, 0, "%s", me->dev); if (tmp) { if (use_ast_ind == 1){ tmp->tech = &vpb_tech_indicate; @@ -2541,7 +2541,7 @@ static struct ast_channel *vpb_new(struct vpb_pvt *me, enum ast_channel_state st return tmp; } -static struct ast_channel *vpb_request(const char *type, int format, void *vdata, int *cause) +static struct ast_channel *vpb_request(const char *type, int format, const struct ast_channel *requestor, void *vdata, int *cause) { int oldformat; struct vpb_pvt *p; @@ -2573,13 +2573,13 @@ static struct ast_channel *vpb_request(const char *type, int format, void *vdata if (group == -1) { if (strncmp(s, p->dev + 4, sizeof p->dev) == 0) { if (!p->owner) { - tmp = vpb_new(p, AST_STATE_DOWN, p->context); + tmp = vpb_new(p, AST_STATE_DOWN, p->context, requestor ? requestor->linkedid : NULL); break; } } } else { if ((p->group == group) && (!p->owner)) { - tmp = vpb_new(p, AST_STATE_DOWN, p->context); + tmp = vpb_new(p, AST_STATE_DOWN, p->context, requestor ? requestor->linkedid : NULL); break; } } diff --git a/channels/sig_analog.c b/channels/sig_analog.c index da7b713e0..79fc27606 100644 --- a/channels/sig_analog.c +++ b/channels/sig_analog.c @@ -349,12 +349,12 @@ static int analog_play_tone(struct analog_pvt *p, enum analog_sub sub, enum anal return -1; } -static struct ast_channel * analog_new_ast_channel(struct analog_pvt *p, int state, int startpbx, enum analog_sub sub) +static struct ast_channel * analog_new_ast_channel(struct analog_pvt *p, int state, int startpbx, enum analog_sub sub, const struct ast_channel *requestor) { struct ast_channel *c; if (p->calls->new_ast_channel) - c = p->calls->new_ast_channel(p->chan_pvt, state, startpbx, sub); + c = p->calls->new_ast_channel(p->chan_pvt, state, startpbx, sub, requestor); else return NULL; @@ -568,7 +568,7 @@ static int analog_update_conf(struct analog_pvt *p) return 0; } -struct ast_channel * analog_request(struct analog_pvt *p, int *callwait) +struct ast_channel * analog_request(struct analog_pvt *p, int *callwait, const struct ast_channel *requestor) { ast_log(LOG_DEBUG, "%s %d\n", __FUNCTION__, p->channel); *callwait = (p->owner != NULL); @@ -580,7 +580,7 @@ struct ast_channel * analog_request(struct analog_pvt *p, int *callwait) } } - return analog_new_ast_channel(p, AST_STATE_RESERVED, 0, p->owner ? ANALOG_SUB_CALLWAIT : ANALOG_SUB_REAL); + return analog_new_ast_channel(p, AST_STATE_RESERVED, 0, p->owner ? ANALOG_SUB_CALLWAIT : ANALOG_SUB_REAL, requestor); } int analog_available(struct analog_pvt *p, int channelmatch, ast_group_t groupmatch, int *busy, int *channelmatched, int *groupmatched) @@ -2623,7 +2623,7 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_ goto winkflashdone; } /* Make new channel */ - chan = analog_new_ast_channel(p, AST_STATE_RESERVED, 0, ANALOG_SUB_THREEWAY); + chan = analog_new_ast_channel(p, AST_STATE_RESERVED, 0, ANALOG_SUB_THREEWAY, NULL); if (p->dahditrcallerid) { if (!p->origcid_num) p->origcid_num = ast_strdup(p->cid_num); @@ -3000,7 +3000,7 @@ int analog_handle_init_event(struct analog_pvt *i, int event) analog_set_echocanceller(i, 1); /* The channel is immediately up. Start right away */ res = analog_play_tone(i, ANALOG_SUB_REAL, ANALOG_TONE_RINGTONE); - chan = analog_new_ast_channel(i, AST_STATE_RING, 1, ANALOG_SUB_REAL); + chan = analog_new_ast_channel(i, AST_STATE_RING, 1, ANALOG_SUB_REAL, NULL); if (!chan) { ast_log(LOG_WARNING, "Unable to start PBX on channel %d\n", i->channel); res = analog_play_tone(i, ANALOG_SUB_REAL, ANALOG_TONE_CONGESTION); @@ -3009,7 +3009,7 @@ int analog_handle_init_event(struct analog_pvt *i, int event) } } else { /* Check for callerid, digits, etc */ - chan = analog_new_ast_channel(i, AST_STATE_RESERVED, 0, ANALOG_SUB_REAL); + chan = analog_new_ast_channel(i, AST_STATE_RESERVED, 0, ANALOG_SUB_REAL, NULL); i->ss_astchan = chan; if (chan) { if (analog_has_voicemail(i)) @@ -3053,9 +3053,9 @@ int analog_handle_init_event(struct analog_pvt *i, int event) case ANALOG_SIG_SF: /* Check for callerid, digits, etc */ if (i->cid_start == ANALOG_CID_START_POLARITY_IN) { - chan = analog_new_ast_channel(i, AST_STATE_PRERING, 0, ANALOG_SUB_REAL); + chan = analog_new_ast_channel(i, AST_STATE_PRERING, 0, ANALOG_SUB_REAL, NULL); } else { - chan = analog_new_ast_channel(i, AST_STATE_RING, 0, ANALOG_SUB_REAL); + chan = analog_new_ast_channel(i, AST_STATE_RING, 0, ANALOG_SUB_REAL, NULL); } i->ss_astchan = chan; if (chan && ast_pthread_create(&threadid, &attr, __analog_ss_thread, i)) { @@ -3153,7 +3153,7 @@ int analog_handle_init_event(struct analog_pvt *i, int event) ast_verbose(VERBOSE_PREFIX_2 "Starting post polarity " "CID detection on channel %d\n", i->channel); - chan = analog_new_ast_channel(i, AST_STATE_PRERING, 0, ANALOG_SUB_REAL); + chan = analog_new_ast_channel(i, AST_STATE_PRERING, 0, ANALOG_SUB_REAL, NULL); i->ss_astchan = chan; if (chan && ast_pthread_create(&threadid, &attr, __analog_ss_thread, i)) { ast_log(LOG_WARNING, "Unable to start simple switch thread on channel %d\n", i->channel); diff --git a/channels/sig_analog.h b/channels/sig_analog.h index 24c4d6c8f..661179881 100644 --- a/channels/sig_analog.h +++ b/channels/sig_analog.h @@ -163,7 +163,7 @@ struct analog_callback { /*! This function is for swapping of the owners with the underlying subs. Typically it means you need to change the fds * of the new owner to be the fds of the sub specified, for each of the two subs given */ void (* const swap_subs)(void *pvt, enum analog_sub a, struct ast_channel *new_a_owner, enum analog_sub b, struct ast_channel *new_b_owner); - struct ast_channel * (* const new_ast_channel)(void *pvt, int state, int startpbx, enum analog_sub sub); + struct ast_channel * (* const new_ast_channel)(void *pvt, int state, int startpbx, enum analog_sub sub, const struct ast_channel *requestor); /* Add the given sub to a conference */ int (* const conf_add)(void *pvt, enum analog_sub sub); @@ -300,7 +300,7 @@ int analog_answer(struct analog_pvt *p, struct ast_channel *ast); struct ast_frame *analog_exception(struct analog_pvt *p, struct ast_channel *ast); -struct ast_channel * analog_request(struct analog_pvt *p, int *callwait); +struct ast_channel * analog_request(struct analog_pvt *p, int *callwait, const struct ast_channel *requestor); int analog_available(struct analog_pvt *p, int channelmatch, ast_group_t groupmatch, int *busy, int *channelmatched, int *groupmatched); diff --git a/channels/sig_pri.c b/channels/sig_pri.c index 1a648a124..f2eeb230e 100644 --- a/channels/sig_pri.c +++ b/channels/sig_pri.c @@ -143,12 +143,12 @@ static int sig_pri_play_tone(struct sig_pri_chan *p, enum sig_pri_tone tone) return -1; } -static struct ast_channel *sig_pri_new_ast_channel(struct sig_pri_chan *p, int state, int startpbx, int ulaw, int transfercapability, char *exten) +static struct ast_channel *sig_pri_new_ast_channel(struct sig_pri_chan *p, int state, int startpbx, int ulaw, int transfercapability, char *exten, const struct ast_channel *requestor) { struct ast_channel *c; if (p->calls->new_ast_channel) - c = p->calls->new_ast_channel(p->chan_pvt, state, startpbx, ulaw, transfercapability, exten); + c = p->calls->new_ast_channel(p->chan_pvt, state, startpbx, ulaw, transfercapability, exten, requestor); else return NULL; @@ -160,11 +160,11 @@ static struct ast_channel *sig_pri_new_ast_channel(struct sig_pri_chan *p, int s return c; } -struct ast_channel *sig_pri_request(struct sig_pri_chan *p, enum sig_pri_law law) +struct ast_channel *sig_pri_request(struct sig_pri_chan *p, enum sig_pri_law law, const struct ast_channel *requestor) { ast_log(LOG_DEBUG, "%s %d\n", __FUNCTION__, p->channel); - return sig_pri_new_ast_channel(p, AST_STATE_RESERVED, 0, law, 0, p->exten); + return sig_pri_new_ast_channel(p, AST_STATE_RESERVED, 0, law, 0, p->exten, requestor); } int pri_is_up(struct sig_pri_pri *pri) @@ -704,7 +704,7 @@ static void *pri_dchannel(void *vpri) if (ast_tvdiff_ms(ast_tvnow(), lastidle) > 1000) { /* Don't create a new idle call more than once per second */ snprintf(idlen, sizeof(idlen), "%d/%s", pri->pvts[nextidle]->channel, pri->idledial); - idle = sig_pri_request(pri->pvts[nextidle], AST_FORMAT_ULAW); + idle = sig_pri_request(pri->pvts[nextidle], AST_FORMAT_ULAW, NULL); if (idle) { pri->pvts[nextidle]->isidlecall = 1; if (ast_pthread_create_background(&p, NULL, do_idle_thread, idle)) { @@ -1140,7 +1140,7 @@ static void *pri_dchannel(void *vpri) /* Release the PRI lock while we create the channel */ ast_mutex_unlock(&pri->lock); - c = sig_pri_new_ast_channel(pri->pvts[chanpos], AST_STATE_RESERVED, 0, (e->ring.layer1 = PRI_LAYER_1_ALAW) ? SIG_PRI_ALAW : SIG_PRI_ULAW, e->ring.ctype, pri->pvts[chanpos]->exten); + c = sig_pri_new_ast_channel(pri->pvts[chanpos], AST_STATE_RESERVED, 0, (e->ring.layer1 = PRI_LAYER_1_ALAW) ? SIG_PRI_ALAW : SIG_PRI_ULAW, e->ring.ctype, pri->pvts[chanpos]->exten, NULL); sig_pri_unlock_private(pri->pvts[chanpos]); @@ -1187,7 +1187,7 @@ static void *pri_dchannel(void *vpri) } else { ast_mutex_unlock(&pri->lock); /* Release PRI lock while we create the channel */ - c = sig_pri_new_ast_channel(pri->pvts[chanpos], AST_STATE_RING, 1, (e->ring.layer1 == PRI_LAYER_1_ALAW) ? SIG_PRI_ALAW : SIG_PRI_ULAW, e->ring.ctype, pri->pvts[chanpos]->exten); + c = sig_pri_new_ast_channel(pri->pvts[chanpos], AST_STATE_RING, 1, (e->ring.layer1 == PRI_LAYER_1_ALAW) ? SIG_PRI_ALAW : SIG_PRI_ULAW, e->ring.ctype, pri->pvts[chanpos]->exten, NULL); if (c) { char calledtonstr[10]; diff --git a/channels/sig_pri.h b/channels/sig_pri.h index 2c9f7b6d7..80cfdd190 100644 --- a/channels/sig_pri.h +++ b/channels/sig_pri.h @@ -64,7 +64,7 @@ struct sig_pri_callback { int (* const set_echocanceller)(void *pvt, int enable); int (* const train_echocanceller)(void *pvt); - struct ast_channel * (* const new_ast_channel)(void *pvt, int state, int startpbx, enum sig_pri_law law, int transfercapability, char *exten); + struct ast_channel * (* const new_ast_channel)(void *pvt, int state, int startpbx, enum sig_pri_law law, int transfercapability, char *exten, const struct ast_channel *chan); void (* const fixup_chans)(void *old_chan, void *new_chan); @@ -239,7 +239,7 @@ void pri_event_alarm(struct sig_pri_pri *pri, int index, int before_start_pri); void pri_event_noalarm(struct sig_pri_pri *pri, int index, int before_start_pri); -struct ast_channel *sig_pri_request(struct sig_pri_chan *p, enum sig_pri_law law); +struct ast_channel *sig_pri_request(struct sig_pri_chan *p, enum sig_pri_law law, const struct ast_channel *requestor); struct sig_pri_chan *sig_pri_chan_new(void *pvt_data, struct sig_pri_callback *callback, struct sig_pri_pri *pri, int logicalspan, int channo); diff --git a/configs/cel.conf.sample b/configs/cel.conf.sample new file mode 100644 index 000000000..66d63d695 --- /dev/null +++ b/configs/cel.conf.sample @@ -0,0 +1,103 @@ +; +; Asterisk Channel Event Logging +; +; Channel Event Logging is a mechanism to provide fine-grained event information +; that can be used to generate billing information. Such event information can +; be recorded to databases and files via pluggable backend modules. +; + +[general] + +; CEL Activation +; +; Use the 'enable' keyword to turn CEL on or off. +; +; Accepted values: yes and no +; Default value: no + +;enable=yes + +; Application Tracking +; +; Use the 'apps' keyword to specify the list of applications for which you want +; to receive CEL events. This is a comma separated list of Asterisk dialplan +; applications, such as Dial, Queue, and Park. +; +; Accepted values: A comma separated list of Asterisk dialplan applications +; Default value: none +; +; Note: You may also use 'all' which will result in CEL events being reported +; for all Asterisk applications. This may affect Asterisk's performance +; significantly. + +apps=dial,park + +; Event Tracking +; +; Use the 'events' keyword to specify the list of events which you want to be +; raised when they occur. This is a comma separated list of the values in the +; table below. +; +; Accepted values: A comma separated list of one or more of the following: +; ALL -- Generate entries on all events +; CHAN_START -- The time a channel was created +; CHAN_END -- The time a channel was terminated +; ANSWER -- The time a channel was answered (ie, phone taken off-hook) +; HANGUP -- The time at which a hangup occurred +; CONF_ENTER -- The time a channel was connected into a conference room +; CONF_EXIT -- The time a channel was removed from a conference room +; CONF_START -- The time the first person enters a conference room +; CONF_END -- The time the last person left a conference room (and +; turned out the lights?) +; APP_START -- The time a tracked application was started +; APP_END -- the time a tracked application ended +; PARK_START -- The time a call was parked +; PARK_END -- Unpark event +; BRIDGE_START -- The time a bridge is started +; BRIDGE_END -- The time a bridge is ended +; 3WAY_START -- When a 3-way conference starts (usually via attended xfer) +; 3WAY_END -- When one or all exit a 3-way conference +; BLINDTRANSFER -- When a blind transfer is initiated +; ATTENDEDTRANSFER -- When an attended transfer is initiated +; TRANSFER -- Generic transfer initiated; not used yet...? +; HOOKFLASH -- So far, when a hookflash event occurs on a DAHDI +; interface +; USER_EVENT -- Triggered from the dialplan, and has a name given by the +; user +; +; Default value: none +; (Track no events) + +events=APP_START,CHAN_START,CHAN_END,ANSWER,HANGUP,BRIDGE_START,BRIDGE_END + +; Date Format +; +; Use the 'dateformat' keyword to specify the date format used when CEL events +; are raised. +; +; Accepted values: A strftime format string (see man strftime) +; +; Example: "%F %T" +; -> This gives the date and time in the format "2009-06-23 17:02:35" +; +; If this option is not specified, the default format is "<seconds>.<microseconds>" +; since epoch. The microseconds field will always be 6 digits in length, meaning it +; may have leading zeros. +; +;dateformat = %F %T + +; +; Asterisk Manager Interface (AMI) CEL Backend +; + +[manager] + +; AMI Backend Activation +; +; Use the 'enable' keyword to turn CEL logging to the Asterisk Manager Interface +; on or off. +; +; Accepted values: yes and no +; Default value: no + +;enabled=yes diff --git a/configs/cel_adaptive_odbc.conf.sample b/configs/cel_adaptive_odbc.conf.sample new file mode 100644 index 000000000..a909efe04 --- /dev/null +++ b/configs/cel_adaptive_odbc.conf.sample @@ -0,0 +1,106 @@ +; +; This configuration defines the connections and tables for which CEL records may +; be populated. Each context specifies a different CEL table to be used. +; +; The columns in the tables should match up word-for-word (case-insensitive) +; to the CEL variables set in the dialplan. The natural advantage to this +; system is that beyond setting up the configuration file to tell you what +; tables to look at, there isn't anything more to do beyond creating the +; columns for the fields that you want, and populating the corresponding +; CEL variables in the dialplan. +; +; Please note that after adding columns to the database, it is necessary to +; reload this module to get the new column names and types read. +; +; Warning: if you specify two contexts with exactly the same connection and +; table names, you will get duplicate records in that table. So be careful. +; +; CEL FIELDS: +; eventtype +; CEL_CHANNEL_START = 1 +; CEL_CHANNEL_END = 2 +; CEL_HANGUP = 3 +; CEL_ANSWER = 4 +; CEL_APP_START = 5 +; CEL_APP_END = 6 +; CEL_BRIDGE_START = 7 +; CEL_BRIDGE_END = 8 +; CEL_CONF_START = 9 +; CEL_CONF_END = 10 +; CEL_PARK_START = 11 +; CEL_PARK_END = 12 +; CEL_BLINDTRANSFER = 13 +; CEL_ATTENDEDTRANSFER = 14 +; CEL_TRANSFER = 15 +; CEL_HOOKFLASH = 16 +; CEL_3WAY_START = 17 +; CEL_3WAY_END = 18 +; CEL_CONF_ENTER = 19 +; CEL_CONF_EXIT = 20 +; CEL_USER_DEFINED = 21 +; CEL_LINKEDID_END = 22 +; CEL_BRIDGE_UPDATE = 23 +; CEL_PICKUP = 24 +; CEL_FORWARD = 25 +; eventtime (timeval, includes microseconds) +; userdeftype (set only if eventtype == USER_DEFINED) +; cid_name +; cid_num +; cid_ani +; cid_rdnis +; cid_dnid +; exten +; context +; channame +; appname +; appdata +; accountcode +; peeraccount +; uniqueid +; linkedid +; amaflag (an int) +; userfield +; peer + + +;[first] +;connection=mysql1 +;table=cel + +;[second] +;connection=mysql1 +;table=extracel + +;[third] +;connection=sqlserver +;table=AsteriskCEL +;usegmtime=yes ; defaults to no +;alias src => source +;alias channel => source_channel +;alias dst => dest +;alias dstchannel => dest_channel +; +; Any filter specified MUST match exactly or the CDR will be discarded +;filter accountcode => somename +;filter src => 123 +; +; Additionally, we now support setting static values per column. Reason +; for this is to allow different sections to specify different values for +; a certain named column, presumably separated by filters. +;static "Some Special Value" => identifier_code + + +; On Wednesday 10 September 2008 21:11:16 Tilghman Lesher wrote: +; (this module patterned after the CDR module) +; I thought that the sample cdr_adaptive_odbc.conf was rather clear, but +; apparently not. The point of this module is to allow you log whatever you +; like in terms of the CDR variables. Do you want to log uniqueid? Then simply +; ensure that your table has that column. If you don't want the column, ensure +; that it does not exist in the table structure. If you'd like to call uniqueid +; something else in your table, simply provide an alias in the configuration +; file that maps the standard CDR field name (uniqueid) to whatever column +; name you like. + +At the current time, channel variables are not published with the events. +If you wish to store variables, put them in the channel userfield and +extract them from there. diff --git a/configs/cel_custom.conf.sample b/configs/cel_custom.conf.sample new file mode 100644 index 000000000..0c0ca1c1a --- /dev/null +++ b/configs/cel_custom.conf.sample @@ -0,0 +1,20 @@ +; +; Asterisk Channel Event Logging - Custom CSV Backend +; +; This is the configuration file for the customizable CSV backend for CEL +; logging. +; +; In order to create custom CSV logs for CEL, uncomment the template below +; (Master.csv) and start Asterisk. Once CEL events are generated, a file will +; appear in the following location: +; +; /var/log/asterisk/cel-custom/Master.csv +; +; (Note that /var/log/asterisk is the default and may differ on your system) +; +; You can also create more than one template if desired. All logs will appear +; in the cel-custom directory under your Asterisk logs directory. +; + +[mappings] +;Master.csv => "${eventtype}","${eventtime}","${CALLERID(name)}","${CALLERID(num)}","${CALLERID(ANI)}","${CALLERID(RDNIS)}","${CALLERID(DNID)}","${CHANNEL(exten)}","${CHANNEL(context)}","${CHANNEL(channame)}","${CHANNEL(appname)}","${CHANNEL(appdata)}","${CHANNEL(amaflags)}","${CHANNEL(accountcode)}","${CHANNEL(uniqueid)}","${CHANNEL(linkedid)}","${CHANNEL(peer)}","${CHANNEL(userfield)}" diff --git a/configs/cel_pgsql.conf.sample b/configs/cel_pgsql.conf.sample new file mode 100644 index 000000000..446105435 --- /dev/null +++ b/configs/cel_pgsql.conf.sample @@ -0,0 +1,58 @@ +; Sample Asterisk config file for CEL logging to PostgresSQL +; +; CEL field names: +; +; eventtype +; CEL_CHANNEL_START = 1 +; CEL_CHANNEL_END = 2 +; CEL_HANGUP = 3 +; CEL_ANSWER = 4 +; CEL_APP_START = 5 +; CEL_APP_END = 6 +; CEL_BRIDGE_START = 7 +; CEL_BRIDGE_END = 8 +; CEL_CONF_START = 9 +; CEL_CONF_END = 10 +; CEL_PARK_START = 11 +; CEL_PARK_END = 12 +; CEL_BLINDTRANSFER = 13 +; CEL_ATTENDEDTRANSFER = 14 +; CEL_TRANSFER = 15 +; CEL_HOOKFLASH = 16 +; CEL_3WAY_START = 17 +; CEL_3WAY_END = 18 +; CEL_CONF_ENTER = 19 +; CEL_CONF_EXIT = 20 +; CEL_USER_DEFINED = 21 +; CEL_LINKEDID_END = 22 +; CEL_BRIDGE_UPDATE = 23 +; CEL_PICKUP = 24 +; CEL_FORWARD = 25 +; eventtime (timeval, includes microseconds) +; userdeftype (set only if eventtype == USER_DEFINED) +; cid_name +; cid_num +; cid_ani +; cid_rdnis +; cid_dnid +; exten +; context +; channame +; appname +; appdata +; accountcode +; peeraccount +; uniqueid +; linkedid +; amaflag (an int) +; userfield +; peer + + +[global] +;hostname=localhost +;port=5432 +;dbname=asterisk +;password=password +;user=postgres +;table=cel ;SQL table where CEL's will be inserted diff --git a/configs/cel_sqlite3_custom.conf.sample b/configs/cel_sqlite3_custom.conf.sample new file mode 100644 index 000000000..a6ee11e8d --- /dev/null +++ b/configs/cel_sqlite3_custom.conf.sample @@ -0,0 +1,7 @@ +; +; Mappings for sqlite3 config file +; +;[master] ; currently, only file "master.db" is supported, with only one table at a time. +;table => cel +;columns => eventtype, eventtime, cidname, cidnum, cidani, cidrdnis, ciddnid, context, exten, channame, appname, appdata, amaflags, accountcode, uniqueid, userfield, peer +;values => '${eventtype}','${eventtime}','${CALLERID(name)}','${CALLERID(num)}','${CALLERID(ANI)}','${CALLERID(RDNIS)}','${CALLERID(DNID)}','${CHANNEL(context)}','${CHANNEL(exten)}','${CHANNEL(channame)}','${CHANNEL(appname)}','${CHANNEL(appdata)}','${CHANNEL(amaflags)}','${CHANNEL(accountcode)}','${CHANNEL(uniqueid)}','${CHANNEL(userfield)}','${CHANNEL(peer)}' diff --git a/configs/cel_tds.conf.sample b/configs/cel_tds.conf.sample new file mode 100644 index 000000000..399093b47 --- /dev/null +++ b/configs/cel_tds.conf.sample @@ -0,0 +1,69 @@ +; +; Asterisk Channel Event Logging (CEL) - FreeTDS Backend +; + +;[global] + +; Connection +; +; Use the 'connection' keyword to specify one of the instance names from your +; 'freetds.conf' file. Note that 'freetds.conf' is not an Asterisk +; configuration file, but one specific to the FreeTDS library. See the FreeTDS +; documentation on 'freetds.conf' for more information: +; +; http://www.freetds.org/userguide/freetdsconf.htm +; +; Accepted values: One of the connections specified in freetds.conf + +;connection=ConnectionFromFreeTDSConf + +; Database Name +; +; The 'dbname' keyword specifies the database name to use when logging CEL +; records. +; +; Accepted values: Any valid database name + +;dbname=MalicoHN + +; Database Table Name +; +; The 'table' keyword identifies which database table is used to log CEL +; records. +; +; Accepted value: Any valid table name +; Default value: If not specified, a table named 'cel' is assumed + +;table=cel + +; Credentials +; +; The 'username' and 'password' keywords specify the user credentials that +; Asterisk should use when connecting to the database. +; +; Accepted value: Any valid username and password + +;username=mangUsr +;password= + +; Language +; +; The 'language' keyword changes the language which are used for error and +; information messages returned by SQL Server. Each database and user has their +; own default value, and this default can be overriden here. +; +; Accepted value: Any language installed on the target SQL Server. +; Default value: Server default + +;language=us_english + +; Character Set +; +; The 'charset' setting is used to change the character set used when connecting +; to the database server. Each database and database user has their own +; character set setting, and this default can be overriden here. +; +; Accepted value: Any valid character set available on the target server. +; Default value: Server setting + +;charset=BIG5 diff --git a/doc/tex/asterisk.tex b/doc/tex/asterisk.tex index f3a77a52b..e9c9b3275 100644 --- a/doc/tex/asterisk.tex +++ b/doc/tex/asterisk.tex @@ -117,6 +117,10 @@ reference purposes. \input{billing.tex} \input{cdrdriver.tex} +\chapter{CEL: Channel Event Logging} +\input{cel-doc.tex} +\input{celdriver.tex} + \chapter{Voicemail} \section{ODBC Storage} \label{odbcstorage} diff --git a/doc/tex/cel-doc.tex b/doc/tex/cel-doc.tex new file mode 100644 index 000000000..fdda68c37 --- /dev/null +++ b/doc/tex/cel-doc.tex @@ -0,0 +1,958 @@ + +\section{Design Goals} + +CEL, or Channel Event Logging, has been written with the hopes that it will help +solve some of the problems that were difficult to address in CDR records. Some +difficulties in CDR generation are the fact that the CDR record stores three +events: the "Start" time, the "Answer" time, and the "End" time. Billing time is +usually the difference between "Answer" and "End", and total call duration was +the difference in time from "Start" to "End". The trouble with this direct and +simple approach is the fact that calls can be transferred, put on hold, +conferenced, forwarded, etc. In general, those doing billing applications in +Asterisk find they have to do all sorts of very creative things to overcome the +shortcomings of CDR records, often supplementing the CDR records with AGI +scripts and manager event filters. + +The fundamental assumption is that the Channel is the fundamental communication +object in asterisk, which basically provides a communication channel between two +communication ports. It makes sense to have an event system aimed at recording +important events on channels. Each event is attached to a channel, like ANSWER +or HANGUP. Some events are meant to connect two or more channels, like the +BRIDGE\_START event. Some events, like BLINDTRANSFER, are initiated by one +channel, but affect two others. These events use the Peer field, like BRIDGE +would, to point to the target channel. + +The design philosophy of CEL is to generate event data that can grouped together +to form a billing record. This may not be a simple task, but we hope to provide +a few different examples that could be used as a basis for those involved in +this effort. + +There are definite parallels between Manager events and CEL events, but there +are some differences. Some events that are generated by CEL are not generated +by the Manager interface (yet). CEL is optimized for databases, and Manager +events are not. The focus of CEL is billing. The Manager interface is targeted +to real-time monitoring and control of asterisk. + +To give the reader a feel for the complexities involved in billing, please take +note of the following sequence of events: + +Remember that 150, 151, and 152 are all Zap extension numbers, and their +respective devices are Zap/50, Zap/51, and Zap/52. + +152 dials 151; 151 answers. 152 parks 151; 152 hangs up. 150 picks up the park +(dials 701). 150 and 151 converse. 151 flashes hook; dials 152, talks to 152, +then 151 flashes hook again for 3-way conference. 151 converses with the other +two for a while, then hangs up. 150 and 152 keep conversing, then hang up. 150 +hangs up first.(not that it matters). + +This sequence of actions will generate the following annotated list of 42 CEL +events: + +{\it Note that the actual CEL events below are in CSV format and do not include + the ;;; and text after that which gives a description of what the event + represents.} + +\begin{astlisting} +"EV\_CHAN\_START","2007-05-09 12:46:16","fxs.52","152","","","","s","extension","Zap/52-1","","","DOCUMENTATION","","1178736376.3","","" ;;; 152 takes the phone off-hook + +"EV\_APP\_START","2007-05-09 12:46:18","fxs.52","152","152","","","151","extension","Zap/52-1","Dial","Zap/51|30|TtWw","DOCUMENTATION","","1178736376.3" ;;; 152 finishes dialing 151 + +"EV\_CHAN\_START","2007-05-09 12:46:18","fxs.51","151","","","","s","extension","Zap/51-1","","","DOCUMENTATION","","1178736378.4","","" ;;; 151 channel created, starts ringing + +{\it (151 is ringing)} + +"EV\_ANSWER","2007-05-09 12:46:19","","151","152","","","151","extension","Zap/51-1","AppDial","(Outgoing Line)","DOCUMENTATION","","1178736378.4","","" ;;; 151 answers + +"EV\_ANSWER","2007-05-09 12:46:19","fxs.52","152","152","","","151","extension","Zap/52-1","Dial","Zap/51|30|TtWw","DOCUMENTATION","","1178736376.3","","" ;;; so does 152 (???) + +"EV\_BRIDGE\_START","2007-05-09 12:46:20","fxs.52","152","152","","","151","extension","Zap/52-1","Dial","Zap/51|30|TtWw","DOCUMENTATION","","1178736376.3","","Zap/51-1" ;;; 152 and 151 are bridged + +{\it (151 and 152 are conversing)} + +"EV\_BRIDGE\_END","2007-05-09 12:46:25","fxs.52","152","152","","","151","extension","Zap/52-1","Dial","Zap/51|30|TtWw","DOCUMENTATION","","1178736376.3","","" ;;; after 5 seconds, the bridge ends (152 dials \#700?) + +"EV\_BRIDGE\_START","2007-05-09 12:46:25","fxs.52","152","152","","","151","extension","Zap/52-1","Dial","Zap/51|30|TtWw","DOCUMENTATION","","1178736376.3","","Zap/51-1" ;;; extraneous 0-second bridge? + +"EV\_BRIDGE\_END","2007-05-09 12:46:25","fxs.52","152","152","","","151","extension","Zap/52-1","Dial","Zap/51|30|TtWw","DOCUMENTATION","","1178736376.3","","" ;;; + +"EV\_PARK\_START","2007-05-09 12:46:27","","151","152","","","","extension","Zap/51-1","Parked Call","","DOCUMENTATION","","1178736378.4","","" ;;; 151 is parked + +"EV\_HANGUP","2007-05-09 12:46:29","fxs.52","152","152","","","h","extension","Zap/52-1","","","DOCUMENTATION","","1178736376.3" ,"","" ;;; 152 hangs up 2 sec later + +"EV\_CHAN\_END","2007-05-09 12:46:29","fxs.52","152","152","","","h","extension","Zap/52-1","","","DOCUMENTATION","","1178736376.3","","" ;;; 152's channel goes away + +{\it (151 is parked and listening to MOH! now, 150 picks up, and dials 701)} + +"EV\_CHAN\_START","2007-05-09 12:47:08","fxs.50","150","","","","s","extension","Zap/50-1","","","DOCUMENTATION","","1178736428.5","","" ;;; 150 picks up the phone, dials 701 + +"EV\_PARK\_END","2007-05-09 12:47:11","","151","152","","","","extension","Zap/51-1","Parked Call","","DOCUMENTATION","","1178736378.4","","" ;;; 151's park comes to end + +"EV\_ANSWER","2007-05-09 12:47:11","fxs.50","150","150","","","701","extension","Zap/50-1","ParkedCall","701","DOCUMENTATION","","1178736428.5","","" ;;; 150 gets answer (twice) + +"EV\_ANSWER","2007-05-09 12:47:12","fxs.50","150","150","","","701","extension","Zap/50-1","ParkedCall","701","DOCUMENTATION","","1178736428.5","","" ;;; + +"EV\_BRIDGE\_START","2007-05-09 12:47:12","fxs.50","150","150","","","701","extension","Zap/50-1","ParkedCall","701","DOCUMENTATION","","1178736428.5","","Zap/51-1" ;;; bridge begins between 150 and recently parked 151 + +{\it (150 and 151 are conversing, then 151 hits flash)} + +"EV\_CHAN\_START","2007-05-09 12:47:51","fxs.51","151","","","","s","extension","Zap/51-2","","","DOCUMENTATION","","1178736471.6","","" ;;; 39 seconds later, 51-2 channel is created. (151 flashes hook) + +"EV\_HOOKFLASH","2007-05-09 12:47:51","","151","152","","","","extension","Zap/51-1","Bridged Call","Zap/50-1","DOCUMENTATION","","1178736378.4","","Zap/51-2" ;;; a marker to record that 151 flashed the hook + +"EV\_BRIDGE\_END","2007-05-09 12:47:51","fxs.50","150","150","","","701","extension","Zap/50-1","ParkedCall","701","DOCUMENTATION","","1178736428.5","","Zap/51-1" ;;; bridge ends between 150 and 151 + +"EV\_BRIDGE\_START","2007-05-09 12:47:51","fxs.50","150","150","","","701","extension","Zap/50-1","ParkedCall","701","DOCUMENTATION","","1178736428.5","","Zap/51-1" ;;; 0-second bridge from 150 to ? 150 gets no sound at all + +"EV\_BRIDGE\_END","2007-05-09 12:47:51","fxs.50","150","150","","","701","extension","Zap/50-1","ParkedCall","701","DOCUMENTATION","","1178736428.5","","Zap/51-1" ;;; + +"EV\_BRIDGE\_START","2007-05-09 12:47:51","fxs.50","150","150","","","701","extension","Zap/50-1","ParkedCall","701","DOCUMENTATION","","1178736428.5","","Zap/51-1" ;;; bridge start on 150 + +{\it (151 has dialtone after hitting flash; dials 152)} + +"EV\_APP\_START","2007-05-09 12:47:55","fxs.51","151","151","","","152","extension","Zap/51-2","Dial","Zap/52|30|TtWw","DOCUMENTATION","","1178736471.6","","" ;;; 151-2 dials 152 after 4 seconds + +"EV\_CHAN\_START","2007-05-09 12:47:55","fxs.52","152","","","","s","extension","Zap/52-1","","","DOCUMENTATION","","1178736475.7" ,"","" ;;; 152 channel created to ring 152. + +{\it (152 ringing)} + +"EV\_ANSWER","2007-05-09 12:47:58","","152","151","","","152","extension","Zap/52-1","AppDial","(Outgoing Line)","DOCUMENTATION","","1178736475.7","","" ;;; 3 seconds later, 152 answers + +"EV\_ANSWER","2007-05-09 12:47:58","fxs.51","151","151","","","152","extension","Zap/51-2","Dial","Zap/52|30|TtWw","DOCUMENTATION","","1178736471.6","","" ;;; ... and 151-2 also answers + +"EV\_BRIDGE\_START","2007-05-09 12:47:59","fxs.51","151","151","","","152","extension","Zap/51-2","Dial","Zap/52|30|TtWw","DOCUMENTATION","","1178736471.6","","Zap/51-1" ;;; 1 second later, bridge formed betw. 151-2 and 151 + +{\it (152 answers, 151 and 152 convering; 150 is listening to silence; 151 hits flash again... to start a 3way)} + +"EV\_3WAY\_START","2007-05-09 12:48:58","","151","152","","","","extension","Zap/51-1","Bridged Call","Zap/50-1","DOCUMENTATION","","1178736378.4","","Zap/51-2" ;;; another hook-flash to begin a 3-way conference + +"EV\_BRIDGE\_END","2007-05-09 12:48:58","fxs.50","150","150","","","701","extension","Zap/50-1","ParkedCall","701","DOCUMENTATION","","1178736428.5","","Zap/51-1" ;;; -- almost 1 minute later, the bridge ends (151 flashes hook again) + +"EV\_BRIDGE\_START","2007-05-09 12:48:58","fxs.50","150","150","","","701","extension","Zap/50-1","ParkedCall","701","DOCUMENTATION","","1178736428.5","","Zap/51-1" ;;; 0-second bridge at 150. (3 way conf formed) + +"EV\_BRIDGE\_END","2007-05-09 12:48:58","fxs.50","150","150","","","701","extension","Zap/50-1","ParkedCall","701","DOCUMENTATION","","1178736428.5","","Zap/51-1" ;;; + +"EV\_BRIDGE\_START","2007-05-09 12:48:58","fxs.50","150","150","","","701","extension","Zap/50-1","ParkedCall","701","DOCUMENTATION","","1178736428.5","","Zap/51-1" ;;; bridge starts for 150 + +{\it (3way now, then 151 hangs up.)} + +"EV\_BRIDGE\_END","2007-05-09 12:49:26","fxs.50","150","150","","","701","extension","Zap/50-1","ParkedCall","701","DOCUMENTATION","","1178736428.5","","Zap/51-1" ;;; 28 seconds later, bridge ends + +"EV\_HANGUP","2007-05-09 12:49:26","","151","152","","","","extension","Zap/51-1","Bridged Call","Zap/50-1","DOCUMENTATION","","1178736378.4","","" ;;; 151 hangs up, leaves 150 and 152 connected + +"EV\_CHAN\_END","2007-05-09 12:49:26","","151","152","","","","extension","Zap/51-1","Bridged Call","Zap/50-1","DOCUMENTATION","","1178736378.4","","" ;;; 151 channel ends + +"EV\_CHAN\_END","2007-05-09 12:49:26","fxs.51","151","151","","","h","extension","Zap/51-2<ZOMBIE>","","","DOCUMENTATION","","1178736428.5","","" ;;; 152-2 channel ends (zombie) + +{\it (just 150 and 152 now)} + +"EV\_BRIDGE\_END","2007-05-09 12:50:13","fxs.50","150","150","","","152","extension","Zap/50-1","Dial","Zap/52|30|TtWw","DOCUMENTATION","","1178736471.6","","" ;;; 47 sec later, the bridge from 150 to 152 ends + +"EV\_HANGUP","2007-05-09 12:50:13","","152","151","","","","extension","Zap/52-1","Bridged Call","Zap/50-1","DOCUMENTATION","","1178736475.7","","" ;;; 152 hangs up + +"EV\_CHAN\_END","2007-05-09 12:50:13","","152","151","","","","extension","Zap/52-1","Bridged Call","Zap/50-1","DOCUMENTATION","","1178736475.7","","" ;;; 152 channel ends + +"EV\_HANGUP","2007-05-09 12:50:13","fxs.50","150","150","","","h","extension","Zap/50-1","","","DOCUMENTATION","","1178736471.6","","" ;;; 150 hangs up + +"EV\_CHAN\_END","2007-05-09 12:50:13","fxs.50","150","150","","","h","extension","Zap/50-1","","","DOCUMENTATION","","1178736471.6","","" ;;; 150 ends +\end{astlisting} + +In terms of Manager events, the above Events correspond to the following 80 +Manager events: + +\begin{astlisting} +\begin{verbatim} +Event: Newchannel +Privilege: call,all +Channel: Zap/52-1 +State: Rsrvd +CallerIDNum: 152 +CallerIDName: fxs.52 +Uniqueid: 1178801102.5 + +Event: Newcallerid +Privilege: call,all +Channel: Zap/52-1 +CallerIDNum: 152 +CallerIDName: fxs.52 +Uniqueid: 1178801102.5 +CID-CallingPres: 0 (Presentation Allowed, Not Screened) + +Event: Newcallerid +Privilege: call,all +Channel: Zap/52-1 +CallerIDNum: 152 +CallerIDName: fxs.52 +Uniqueid: 1178801102.5 +CID-CallingPres: 0 (Presentation Allowed, Not Screened) + +Event: Newstate +Privilege: call,all +Channel: Zap/52-1 +State: Ring +CallerIDNum: 152 +CallerIDName: fxs.52 +Uniqueid: 1178801102.5 + +Event: Newexten +Privilege: call,all +Channel: Zap/52-1 +Context: extension +Extension: 151 +Priority: 1 +Application: Set +AppData: CDR(myvar)=zingo +Uniqueid: 1178801102.5 + +Event: Newexten +Privilege: call,all +Channel: Zap/52-1 +Context: extension +Extension: 151 +Priority: 2 +Application: Dial +AppData: Zap/51|30|TtWw +Uniqueid: 1178801102.5 + +Event: Newchannel +Privilege: call,all +Channel: Zap/51-1 +State: Rsrvd +CallerIDNum: 151 +CallerIDName: fxs.51 +Uniqueid: 1178801108.6 + +Event: Newstate +Privilege: call,all +Channel: Zap/51-1 +State: Ringing +CallerIDNum: 152 +CallerIDName: fxs.52 +Uniqueid: 1178801108.6 + +Event: Dial +Privilege: call,all +SubEvent: Begin +Source: Zap/52-1 +Destination: Zap/51-1 +CallerIDNum: 152 +CallerIDName: fxs.52 +SrcUniqueID: 1178801102.5 +DestUniqueID: 1178801108.6 + +Event: Newcallerid +Privilege: call,all +Channel: Zap/51-1 +CallerIDNum: 151 +CallerIDName: <Unknown> +Uniqueid: 1178801108.6 +CID-CallingPres: 0 (Presentation Allowed, Not Screened) + +Event: Newstate +Privilege: call,all +Channel: Zap/52-1 +State: Ringing +CallerIDNum: 152 +CallerIDName: fxs.52 +Uniqueid: 1178801102.5 + +Event: Newstate +Privilege: call,all +Channel: Zap/51-1 +State: Up +CallerIDNum: 151 +CallerIDName: <unknown> +Uniqueid: 1178801108.6 + +Event: Newstate +Privilege: call,all +Channel: Zap/52-1 +State: Up +CallerIDNum: 152 +CallerIDName: fxs.52 +Uniqueid: 1178801102.5 + +Event: Link +Privilege: call,all +Channel1: Zap/52-1 +Channel2: Zap/51-1 +Uniqueid1: 1178801102.5 +Uniqueid2: 1178801108.6 +CallerID1: 152 +CallerID2: 151 + +Event: Unlink +Privilege: call,all +Channel1: Zap/52-1 +Channel2: Zap/51-1 +Uniqueid1: 1178801102.5 +Uniqueid2: 1178801108.6 +CallerID1: 152 +CallerID2: 151 + +Event: Link +Privilege: call,all +Channel1: Zap/52-1 +Channel2: Zap/51-1 +Uniqueid1: 1178801102.5 +Uniqueid2: 1178801108.6 +CallerID1: 152 +CallerID2: 151 + +Event: Unlink +Privilege: call,all +Channel1: Zap/52-1 +Channel2: Zap/51-1 +Uniqueid1: 1178801102.5 +Uniqueid2: 1178801108.6 +CallerID1: 152 +CallerID2: 151 + +Event: ParkedCall +Privilege: call,all +Exten: 701 +Channel: Zap/51-1 +From: Zap/52-1 +Timeout: 45 +CallerIDNum: 151 +CallerIDName: <unknown> + +Event: Dial +Privilege: call,all +SubEvent: End +Channel: Zap/52-1 +DialStatus: ANSWER + +Event: Newexten +Privilege: call,all +Channel: Zap/52-1 +Context: extension +Extension: h +Priority: 1 +Application: Goto +AppData: label1 +Uniqueid: 1178801102.5 + +Event: Newexten +Privilege: call,all +Channel: Zap/52-1 +Context: extension +Extension: h +Priority: 4 +Application: Goto +AppData: label2 +Uniqueid: 1178801102.5 + +Event: Newexten +Privilege: call,all +Channel: Zap/52-1 +Context: extension +Extension: h +Priority: 2 +Application: NoOp +AppData: In Hangup! myvar is zingo and accountcode is billsec is 26 and duration is 40 and end is 2007-05-10 06:45:42. +Uniqueid: 1178801102.5 + +Event: Newexten +Privilege: call,all +Channel: Zap/52-1 +Context: extension +Extension: h +Priority: 3 +Application: Goto +AppData: label3 +Uniqueid: 1178801102.5 + +Event: Newexten +Privilege: call,all +Channel: Zap/52-1 +Context: extension +Extension: h +Priority: 5 +Application: NoOp +AppData: More Hangup message after hopping around" +Uniqueid: 1178801102.5 + +Event: Hangup +Privilege: call,all +Channel: Zap/52-1 +Uniqueid: 1178801102.5 +Cause: 16 +Cause-txt: Normal Clearing + +Event: Newchannel +Privilege: call,all +Channel: Zap/50-1 +State: Rsrvd +CallerIDNum: 150 +CallerIDName: fxs.50 +Uniqueid: 1178801162.7 + +Event: Newcallerid +Privilege: call,all +Channel: Zap/50-1 +CallerIDNum: 150 +CallerIDName: fxs.50 +Uniqueid: 1178801162.7 +CID-CallingPres: 0 (Presentation Allowed, Not Screened) + +Event: Newcallerid +Privilege: call,all +Channel: Zap/50-1 +CallerIDNum: 150 +CallerIDName: fxs.50 +Uniqueid: 1178801162.7 +CID-CallingPres: 0 (Presentation Allowed, Not Screened) + +Event: Newstate +Privilege: call,all +Channel: Zap/50-1 +State: Ring +CallerIDNum: 150 +CallerIDName: fxs.50 +Uniqueid: 1178801162.7 + +Event: Newexten +Privilege: call,all +Channel: Zap/50-1 +Context: extension +Extension: 701 +Priority: 1 +Application: ParkedCall +AppData: 701 +Uniqueid: 1178801162.7 + +Event: UnParkedCall +Privilege: call,all +Exten: 701 +Channel: Zap/51-1 +From: Zap/50-1 +CallerIDNum: 151 +CallerIDName: <unknown> + +Event: Newstate +Privilege: call,all +Channel: Zap/50-1 +State: Up +CallerIDNum: 150 +CallerIDName: fxs.50 +Uniqueid: 1178801162.7 + +Event: Link +Privilege: call,all +Channel1: Zap/50-1 +Channel2: Zap/51-1 +Uniqueid1: 1178801162.7 +Uniqueid2: 1178801108.6 +CallerID1: 150 +CallerID2: 151 + +Event: Newchannel +Privilege: call,all +Channel: Zap/51-2 +State: Rsrvd +CallerIDNum: 151 +CallerIDName: fxs.51 +Uniqueid: 1178801218.8 + +Event: Unlink +Privilege: call,all +Channel1: Zap/50-1 +Channel2: Zap/51-1 +Uniqueid1: 1178801162.7 +Uniqueid2: 1178801108.6 +CallerID1: 150 +CallerID2: 151 + +Event: Link +Privilege: call,all +Channel1: Zap/50-1 +Channel2: Zap/51-1 +Uniqueid1: 1178801162.7 +Uniqueid2: 1178801108.6 +CallerID1: 150 +CallerID2: 151 + +Event: Unlink +Privilege: call,all +Channel1: Zap/50-1 +Channel2: Zap/51-1 +Uniqueid1: 1178801162.7 +Uniqueid2: 1178801108.6 +CallerID1: 150 +CallerID2: 151 + +Event: Link +Privilege: call,all +Channel1: Zap/50-1 +Channel2: Zap/51-1 +Uniqueid1: 1178801162.7 +Uniqueid2: 1178801108.6 +CallerID1: 150 +CallerID2: 151 + +Event: Newcallerid +Privilege: call,all +Channel: Zap/51-2 +CallerIDNum: 151 +CallerIDName: fxs.51 +Uniqueid: 1178801218.8 +CID-CallingPres: 0 (Presentation Allowed, Not Screened) + +Event: Newcallerid +Privilege: call,all +Channel: Zap/51-2 +CallerIDNum: 151 +CallerIDName: fxs.51 +Uniqueid: 1178801218.8 +CID-CallingPres: 0 (Presentation Allowed, Not Screened) + +Event: Newstate +Privilege: call,all +Channel: Zap/51-2 +State: Ring +CallerIDNum: 151 +CallerIDName: fxs.51 +Uniqueid: 1178801218.8 + +Event: Newexten +Privilege: call,all +Channel: Zap/51-2 +Context: extension +Extension: 152 +Priority: 1 +Application: Set +AppData: CDR(myvar)=zingo +Uniqueid: 1178801218.8 + +Event: Newexten +Privilege: call,all +Channel: Zap/51-2 +Context: extension +Extension: 152 +Priority: 2 +Application: Dial +AppData: Zap/52|30|TtWw +Uniqueid: 1178801218.8 + +Event: Newchannel +Privilege: call,all +Channel: Zap/52-1 +State: Rsrvd +CallerIDNum: 152 +CallerIDName: fxs.52 +Uniqueid: 1178801223.9 + +Event: Newstate +Privilege: call,all +Channel: Zap/52-1 +State: Ringing +CallerIDNum: 151 +CallerIDName: fxs.51 +Uniqueid: 1178801223.9 + +Event: Dial +Privilege: call,all +SubEvent: Begin +Source: Zap/51-2 +Destination: Zap/52-1 +CallerIDNum: 151 +CallerIDName: fxs.51 +SrcUniqueID: 1178801218.8 +DestUniqueID: 1178801223.9 + +Event: Newcallerid +Privilege: call,all +Channel: Zap/52-1 +CallerIDNum: 152 +CallerIDName: <Unknown> +Uniqueid: 1178801223.9 +CID-CallingPres: 0 (Presentation Allowed, Not Screened) + +Event: Newstate +Privilege: call,all +Channel: Zap/51-2 +State: Ringing +CallerIDNum: 151 +CallerIDName: fxs.51 +Uniqueid: 1178801218.8 + +Event: Newstate +Privilege: call,all +Channel: Zap/52-1 +State: Up +CallerIDNum: 152 +CallerIDName: <unknown> +Uniqueid: 1178801223.9 + +Event: Newstate +Privilege: call,all +Channel: Zap/51-2 +State: Up +CallerIDNum: 151 +CallerIDName: fxs.51 +Uniqueid: 1178801218.8 + +Event: Link +Privilege: call,all +Channel1: Zap/51-2 +Channel2: Zap/52-1 +Uniqueid1: 1178801218.8 +Uniqueid2: 1178801223.9 +CallerID1: 151 +CallerID2: 152 + +Event: Unlink +Privilege: call,all +Channel1: Zap/50-1 +Channel2: Zap/51-1 +Uniqueid1: 1178801162.7 +Uniqueid2: 1178801108.6 +CallerID1: 150 +CallerID2: 151 + +Event: Link +Privilege: call,all +Channel1: Zap/50-1 +Channel2: Zap/51-1 +Uniqueid1: 1178801162.7 +Uniqueid2: 1178801108.6 +CallerID1: 150 +CallerID2: 151 + +Event: Unlink +Privilege: call,all +Channel1: Zap/50-1 +Channel2: Zap/51-1 +Uniqueid1: 1178801162.7 +Uniqueid2: 1178801108.6 +CallerID1: 150 +CallerID2: 151 + +Event: Link +Privilege: call,all +Channel1: Zap/50-1 +Channel2: Zap/51-1 +Uniqueid1: 1178801162.7 +Uniqueid2: 1178801108.6 +CallerID1: 150 +CallerID2: 151 + +Event: Unlink +Privilege: call,all +Channel1: Zap/50-1 +Channel2: Zap/51-1 +Uniqueid1: 1178801162.7 +Uniqueid2: 1178801108.6 +CallerID1: 150 +CallerID2: 151 + +Event: Hangup +Privilege: call,all +Channel: Zap/51-1 +Uniqueid: 1178801108.6 +Cause: 16 +Cause-txt: Normal Clearing + +Event: Newexten +Privilege: call,all +Channel: Zap/50-1 +Context: extension +Extension: h +Priority: 1 +Application: Goto +AppData: label1 +Uniqueid: 1178801162.7 + +Event: Newexten +Privilege: call,all +Channel: Zap/50-1 +Context: extension +Extension: h +Priority: 4 +Application: Goto +AppData: label2 +Uniqueid: 1178801162.7 + +Event: Newexten +Privilege: call,all +Channel: Zap/50-1 +Context: extension +Extension: h +Priority: 2 +Application: NoOp +AppData: In Hangup! myvar is and accountcode is billsec is 0 and duration is 0 and end is 2007-05-10 06:48:37. +Uniqueid: 1178801162.7 + +Event: Newexten +Privilege: call,all +Channel: Zap/50-1 +Context: extension +Extension: h +Priority: 3 +Application: Goto +AppData: label3 +Uniqueid: 1178801162.7 + +Event: Newexten +Privilege: call,all +Channel: Zap/50-1 +Context: extension +Extension: h +Priority: 5 +Application: NoOp +AppData: More Hangup message after hopping around" +Uniqueid: 1178801162.7 + +Event: Masquerade +Privilege: call,all +Clone: Zap/50-1 +CloneState: Up +Original: Zap/51-2 +OriginalState: Up + +Event: Rename +Privilege: call,all +Oldname: Zap/50-1 +Newname: Zap/50-1<MASQ> +Uniqueid: 1178801162.7 + +Event: Rename +Privilege: call,all +Oldname: Zap/51-2 +Newname: Zap/50-1 +Uniqueid: 1178801218.8 + +Event: Rename +Privilege: call,all +Oldname: Zap/50-1<MASQ> +Newname: Zap/51-2<ZOMBIE> +Uniqueid: 1178801162.7 + +Event: Hangup +Privilege: call,all +Channel: Zap/51-2<ZOMBIE> +Uniqueid: 1178801162.7 +Cause: 0 +Cause-txt: Unknown + +Event: Unlink +Privilege: call,all +Channel1: Zap/50-1 +Channel2: Zap/52-1 +Uniqueid1: 1178801218.8 +Uniqueid2: 1178801223.9 +CallerID1: 150 +CallerID2: 152 + +Event: Hangup +Privilege: call,all +Channel: Zap/52-1 +Uniqueid: 1178801223.9 +Cause: 16 +Cause-txt: Normal Clearing + +Event: Dial +Privilege: call,all +SubEvent: End +Channel: Zap/50-1 +DialStatus: ANSWER + +Event: Newexten +Privilege: call,all +Channel: Zap/50-1 +Context: extension +Extension: h +Priority: 1 +Application: Goto +AppData: label1 +Uniqueid: 1178801218.8 + +Event: Newexten +Privilege: call,all +Channel: Zap/50-1 +Context: extension +Extension: h +Priority: 4 +Application: Goto +AppData: label2 +Uniqueid: 1178801218.8 + +Event: Newexten +Privilege: call,all +Channel: Zap/50-1 +Context: extension +Extension: h +Priority: 2 +Application: NoOp +AppData: In Hangup! myvar is and accountcode is billsec is 90 and duration is 94 and end is 2007-05-10 06:48:37. +Uniqueid: 1178801218.8 + +Event: Newexten +Privilege: call,all +Channel: Zap/50-1 +Context: extension +Extension: h +Priority: 3 +Application: Goto +AppData: label3 +Uniqueid: 1178801218.8 + +Event: Newexten +Privilege: call,all +Channel: Zap/50-1 +Context: extension +Extension: h +Priority: 5 +Application: NoOp +AppData: More Hangup message after hopping around" +Uniqueid: 1178801218.8 + +Event: Hangup +Privilege: call,all +Channel: Zap/50-1 +Uniqueid: 1178801218.8 +Cause: 16 +Cause-txt: Normal Clearing +\end{verbatim} +\end{astlisting} + +And, humorously enough, the above 80 manager events, or 42 CEL events, +correspond to the following two CDR records (at the moment!): + +\begin{astlisting} +""fxs.52" <152>","152","h","extension","Zap/52-1","Zap/51-1","NoOp","More Hangup message after hopping around"","2007-05-09 17:35:56","2007-05-09 17:36:20","2007-05-09 17:36:36","40","16","ANSWERED","DOCUMENTATION","","1178753756.0","" + +""fxs.50" <150>","150","152","extension","Zap/50-1","Zap/51-1","NoOp","More Hangup message after hopping around"","2007-05-09 17:37:59","2007-05-09 17:38:06","2007-05-09 17:39:11","72","65","ANSWERED","DOCUMENTATION","","1178753871.3","" +\end{astlisting} + + +\section{Events \& Fields} + +While CDRs and the Manager are basically both event tracking mechanisms, CEL +tries to track only those events that might pertain to billing issues. + +See table~\ref{event_table} for a list of events raised by CEL and +table~\ref{field_table} for the list of fields passed for each CEL event. + +\begin{table}[h] + \begin{tabular}{ | l | p{10cm} | } + \hline + Event & Description \\ \hline \hline + CHAN\_START & The time a channel was created \\ \hline + CHAN\_END & The time a channel was terminated \\ \hline + ANSWER & The time a channel was answered (ie, phone taken off-hook, etc) \\ \hline + HANGUP & The time at which a hangup occurred. \\ \hline + CONF\_ENTER & The time a channel was connected into a conference room \\ \hline + CONF\_EXIT & The time a channel was removed from a conference room \\ \hline + CONF\_START & The time the first person enters a conference \\ \hline + CONF\_END & The time the last person left a conf (and turned out the lights?) \\ \hline + APP\_START & The time a tracked application was started \\ \hline + APP\_END & the time a tracked application ended \\ \hline + PARK\_START & The time a call was parked \\ \hline + PARK\_END & unpark event \\ \hline + BRIDGE\_START & The time a bridge is started \\ \hline + BRIDGE\_END & The time a bridge is ended \\ \hline + 3WAY\_START & When a 3-way conf starts (usually via attended xfer) \\ \hline + 3WAY\_END & When one or all exit a 3-way conf \\ \hline + BLINDTRANSFER & When a blind transfer is initiated \\ \hline + ATTENDEDTRANSFER & When an attended transfer is initiated \\ \hline + TRANSFER & Generic transfer initiated; not used yet...? \\ \hline + HOOKFLASH & So far, when a hookflash event occurs on a Zap interface \\ \hline + USER\_EVENT & these are triggered from the dialplan, and have a name given by the user. \\ + \hline + \end{tabular} + \caption{List of CEL Events} + \label{event_table} +\end{table} + +\begin{table}[h] + \begin{tabular}{ | l | p{10cm} | } + \hline + Field & Description \\ \hline \hline + eventtype & The name of the event; see the above list; each is prefixed with "EV\_". \\ \hline + eventtime & The time the event happened \\ \hline + cidname & CID name field \\ \hline + cidnum & CID number field \\ \hline + cidani & CID ANI field \\ \hline + cidrdnis & CID RDNIS field \\ \hline + ciddnid & CID DNID field \\ \hline + exten & The extension in the dialplan \\ \hline + context & The context in the dialplan \\ \hline + channame & The name assigned to the channel in which the event took place \\ \hline + appname & The name of the current application \\ \hline + appdata & The arguments that will be handed to that application \\ \hline + amaflags & The AMA flags associated with the event; user assignable. \\ \hline + accountcode & A user assigned datum (string) \\ \hline + uniqueid & Each Channel instance gets a unique ID associated with it. \\ \hline + userfield & A user assigned datum (string) \\ \hline + linkedid & the per-call id, spans several events, possibly. \\ \hline + peer & For bridge or other 2-channel events, this would be the other channel name \\ + \hline + \end{tabular} + \caption{List of CEL Event Fields} + \label{field_table} +\end{table} + +\section{Applications \& Functions} + +\subsection{CEL Function} + +**** THIS IS NO LONGER TRUE. REWRITE. **** + +The CEL function parallels the CDR function, for fetching values from the +channel or event. It has some notable notable differences, though! For +instance, CEL data is not stored on the channel. Well, not much of it, anyway! +You can use the CEL function to set the amaflags, accountcode, and userfield, +which are stored on the channel. + +Channel variables are not available for reading from the CEL function, nor can +any variable name other than what's in the list, be set. CDRs have a structure +attached to the channel, where the CDR function could access the values stored +there, or set the values there. CDRs could store their own variable lists, but +CEL has no such storage. There is no reason to store any event information, as +they are immediately output to the various backends at the time they are +generated. + +See the description for the CEL function from the CLI: core show function CEL + +Here is a list of all the available channel field names: +\begin{verbatim} + cidname userfield + cidnum amaflags + cidani cidrdnis + ciddnid appdata + exten accountcode + context uniqueid + channame appname + peer eventtime + eventtype +\end{verbatim} + +\subsection{CELGenUserEvent Application} + +This application allows the dialplan to insert custom events into the event +stream. + +For more information, in the CLI, type: core show application CELGenUserEvent + +Its arguments take this format: + +\begin{verbatim} + CELGenUserEvent(eventname) +\end{verbatim} + +Please note that there is no restrictions on the name supplied. If it happens to +match a standard CEL event name, it will look like that event was +generated. This could be a blessing or a curse! + +\section{Configuration Files} + +\begin{itemize} +\item cel.conf +\end{itemize} + +\section{Generating Billing Information} + +*** This is the Next Big Task *** + + diff --git a/doc/tex/celdriver.tex b/doc/tex/celdriver.tex new file mode 100644 index 000000000..19224d674 --- /dev/null +++ b/doc/tex/celdriver.tex @@ -0,0 +1,447 @@ +\section{Storage Backends} + +Right now, the CEL package will support CSV, Customized CSV, ODBC, PGSQL, TDS, +Sqlite3, and Radius back ends. See the doc/celdriver.tex file +for how to use these back ends. + +\subsection{Microsoft SQL Server} + + Asterisk can currently store Channel Events into an MSSQL database in + two different ways: cel\_odbc or cel\_tds + + Channel Event Records can be stored using unixODBC (which requires + the FreeTDS package) [cel\_odbc] or directly by using just the + FreeTDS package [cel\_tds] The following provide some + examples known to get asterisk working with mssql. + + NOTE: Only choose one db connector. + +\subsubsection{ODBC using cel\_odbc} + Compile, configure, and install the latest unixODBC package: +\begin{verbatim} + tar -zxvf unixODBC-2.2.9.tar.gz && + cd unixODBC-2.2.9 && + ./configure --sysconfdir=/etc --prefix=/usr --disable-gui && + make && + make install +\end{verbatim} + + Compile, configure, and install the latest FreeTDS package: +\begin{verbatim} + tar -zxvf freetds-0.62.4.tar.gz && + cd freetds-0.62.4 && + ./configure --prefix=/usr --with-tdsver=7.0 \ + --with-unixodbc=/usr/lib && + make && make install +\end{verbatim} + + Compile, or recompile, asterisk so that it will now add support + for cel\_odbc. +\begin{verbatim} + make clean && ./configure --with-odbc && + make update && + make && + make install +\end{verbatim} + + Setup odbc configuration files. These are working examples + from my system. You will need to modify for your setup. + You are not required to store usernames or passwords here. + +\begin{verbatim} + /etc/odbcinst.ini + [FreeTDS] + Description = FreeTDS ODBC driver for MSSQL + Driver = /usr/lib/libtdsodbc.so + Setup = /usr/lib/libtdsS.so + FileUsage = 1 + + /etc/odbc.ini + [MSSQL-asterisk] + description = Asterisk ODBC for MSSQL + driver = FreeTDS + server = 192.168.1.25 + port = 1433 + database = voipdb + tds_version = 7.0 + language = us_english +\end{verbatim} + + Only install one database connector. Do not confuse asterisk + by using both ODBC (cel\_odbc) and FreeTDS (cel\_tds). + This command will erase the contents of cel\_tds.conf +\begin{verbatim} + [ -f /etc/asterisk/cel_tds.conf ] > /etc/asterisk/cel_tds.conf +\end{verbatim} + NOTE: unixODBC requires the freeTDS package, but asterisk does + not call freeTDS directly. + + Now set up cel\_odbc configuration files. These are working samples + from my system. You will need to modify for your setup. Define + your usernames and passwords here, secure file as well. +\begin{verbatim} + /etc/asterisk/cel_odbc.conf + [global] + dsn=MSSQL-asterisk + username=voipdbuser + password=voipdbpass + loguniqueid=yes +\end{verbatim} + And finally, create the 'cel' table in your mssql database. +\begin{verbatim} + CREATE TABLE cel ( + [eventtype] [varchar] (30) NOT NULL , + [eventtime] [datetime] NOT NULL , + [cidname] [varchar] (80) NOT NULL , + [cidnum] [varchar] (80) NOT NULL , + [cidani] [varchar] (80) NOT NULL , + [cidrdnis] [varchar] (80) NOT NULL , + [ciddnid] [varchar] (80) NOT NULL , + [exten] [varchar] (80) NOT NULL , + [context] [varchar] (80) NOT NULL , + [channame] [varchar] (80) NOT NULL , + [appname] [varchar] (80) NOT NULL , + [appdata] [varchar] (80) NOT NULL , + [amaflags] [int] NOT NULL , + [accountcode] [varchar] (20) NOT NULL , + [uniqueid] [varchar] (32) NOT NULL , + [peer] [varchar] (80) NOT NULL , + [userfield] [varchar] (255) NOT NULL + ) +\end{verbatim} + Start asterisk in verbose mode, you should see that asterisk + logs a connection to the database and will now record every + desired channel event at the moment it occurs. + +\subsubsection{FreeTDS, using cel\_tds} + Compile, configure, and install the latest FreeTDS package: +\begin{verbatim} + tar -zxvf freetds-0.62.4.tar.gz && + cd freetds-0.62.4 && + ./configure --prefix=/usr --with-tdsver=7.0 + make && + make install +\end{verbatim} + Compile, or recompile, asterisk so that it will now add support + for cel\_tds. +\begin{verbatim} + make clean && ./configure --with-tds && + make update && + make && + make install +\end{verbatim} + Only install one database connector. Do not confuse asterisk + by using both ODBC (cel\_odbc) and FreeTDS (cel\_tds). + This command will erase the contents of cel\_odbc.conf +\begin{verbatim} + [ -f /etc/asterisk/cel_odbc.conf ] > /etc/asterisk/cel_odbc.conf +\end{verbatim} + Setup cel\_tds configuration files. These are working samples + from my system. You will need to modify for your setup. Define + your usernames and passwords here, secure file as well. +\begin{verbatim} + /etc/asterisk/cel_tds.conf + [global] + hostname=192.168.1.25 + port=1433 + dbname=voipdb + user=voipdbuser + password=voipdpass + charset=BIG5 +\end{verbatim} + And finally, create the 'cel' table in your mssql database. +\begin{verbatim} + CREATE TABLE cel ( + [eventtype] [varchar] (30) NULL , + [eventtime] [datetime] NULL , + [cidname] [varchar] (80) NULL , + [cidnum] [varchar] (80) NULL , + [cidani] [varchar] (80) NULL , + [cidrdnis] [varchar] (80) NULL , + [ciddnid] [varchar] (80) NULL , + [exten] [varchar] (80) NULL , + [context] [varchar] (80) NULL , + [channame] [varchar] (80) NULL , + [appname] [varchar] (80) NULL , + [appdata] [varchar] (80) NULL , + [amaflags] [varchar] (16) NULL , + [accountcode] [varchar] (20) NULL , + [uniqueid] [varchar] (32) NULL , + [userfield] [varchar] (255) NULL , + [peer] [varchar] (80) NULL + ) +\end{verbatim} + Start asterisk in verbose mode, you should see that asterisk + logs a connection to the database and will now record every + call to the database when it's complete. + + +\subsection{MySQL} + +Using MySQL for Channel Event records is supported by using ODBC and the cel\_odbc module. + +\subsection{PostreSQL} + If you want to go directly to postgresql database, and have the cel\_pgsql.so + compiled you can use the following sample setup. + On Debian, before compiling asterisk, just install libpqxx-dev. + Other distros will likely have a similiar package. + + Once you have the compile done, + copy the sample cel\_pgsql.conf file or create your own. + + Here is a sample: +\begin{verbatim} + /etc/asterisk/cel_pgsql.conf + ; Sample Asterisk config file for CEL logging to PostgresSQL + [global] + hostname=localhost + port=5432 + dbname=asterisk + password=password + user=postgres + table=cel +\end{verbatim} + Now create a table in postgresql for your cels + +\begin{verbatim} + CREATE TABLE cel ( + eventtype varchar (30) NOT NULL , + eventtime time NOT NULL , + cidname varchar (80) NOT NULL , + cidnum varchar (80) NOT NULL , + cidani varchar (80) NOT NULL , + cidrdnis varchar (80) NOT NULL , + ciddnis varchar (80) NOT NULL , + exten varchar (80) NOT NULL , + context varchar (80) NOT NULL , + channame varchar (80) NOT NULL , + appname varchar (80) NOT NULL , + appdata varchar (80) NOT NULL , + amaflags int NOT NULL , + accountcode varchar (20) NOT NULL , + uniqueid varchar (32) NOT NULL , + userfield varchar (255) NOT NULL , + peer varchar (80) NOT NULL + ); +\end{verbatim} + +\subsection{SQLite 3} + +SQLite version 3 is supported in cel\_sqlite3\_custom. + +\subsection{RADIUS} + +\subsubsection{What is needed} + +\begin{itemize} + \item FreeRADIUS server + \item Radiusclient-ng library + \item Asterisk PBX +\end{itemize} + +\begin{figure}[h] +\begin{center} +\setlength{\unitlength}{4cm} +\begin{picture}(3,.75) +\put(0,0){\line(0,1){.75}} +\put(0,.75){\line(1,0){1.5}} +\put(1.5,0){\line(0,1){.75}} +\put(0,0){\line(1,0){1.5}} +\put(.1,.4){\makebox(1.3,.3){Asterisk PBX}} +\put(.1,.4){\line(1,0){1.3}} +\put(.1,.1){\line(1,0){1.3}} +\put(.1,.1){\line(0,1){.3}} +\put(1.4,.1){\line(0,1){.3}} +\put(.1,.1){\makebox(1.3,.3){RADIUS Client}} +\put(1.8,0){\line(0,1){.5}} +\put(1.8,.5){\line(1,0){1.1}} +\put(1.8,0){\line(1,0){1.1}} +\put(2.9,0){\line(0,1){.5}} +\put(1.8,.275){\makebox(1.1,.1){RADIUS Server}} +\put(1.8,.125){\makebox(1.1,.1){$(FreeRADIUS)$}} +\thicklines +\put(1.4,.3){\vector(1,0){.4}} +\put(1.8,.2){\vector(-1,0){.4}} +\thinlines +\end{picture} +\end{center} +\caption{Asterisk/RADIUS Integration} +\end{figure} + +\subsubsection{Installation of the Radiusclient library} + Installation: +\begin{verbatim} + Download the sources from: + + http://developer.berlios.de/projects/radiusclient-ng/ + + Untar the source tarball. + root@localhost:/usr/local/src# tar xvfz radiusclient-ng-0.5.5.1.tar.gz + + Compile and install the library. + root@localhost:/usr/local/src# cd radiusclient-ng-0.5.5.1 + root@localhost:/usr/local/src/radiusclient-ng-0.5.5.1# ./configure + root@localhost:/usr/local/src/radiusclient-ng-0.5.5.1# make + root@localhost:/usr/local/src/radiusclient-ng-0.5.5.1# make install +\end{verbatim} + +\subsubsection{Configuration of the Radiusclient library} + + By default all the configuration files of the radiusclient library will + be in /usr/local/etc/radiusclient-ng directory. + + File "radiusclient.conf" + Open the file and find lines containing the following: + + authserver localhost + + This is the hostname or IP address of the RADIUS server used for + authentication. You will have to change this unless the server is + running on the same host as your Asterisk PBX. + + acctserver localhost + This is the hostname or IP address of the RADIUS server used for + accounting. You will have to change this unless the server is running + on the same host as your Asterisk PBX. + + File "servers" + + RADIUS protocol uses simple access control mechanism based on shared + secrets that allows RADIUS servers to limit access from RADIUS clients. + + A RADIUS server is configured with a secret string and only RADIUS + clients that have the same secret will be accepted. + + You need to configure a shared secret for each server you have + configured in radiusclient.conf file in the previous step. The shared + secrets are stored in /usr/local/etc/radiusclient-ng/servers file. + + Each line contains hostname of a RADIUS server and shared secret + used in communication with that server. The two values are separated + by white spaces. Configure shared secrets for every RADIUS server you + are going to use. + + File "dictionary" + + Asterisk uses some attributes that are not included in the + dictionary of radiusclient library, therefore it is necessary to add + them. A file called dictionary.digium (kept in the contrib dir) + was created to list all new attributes used by Asterisk. + Add to the end of the main dictionary file + /usr/local/etc/radiusclient-ng/dictionary + the line: +\begin{verbatim} + \$INCLUDE /path/to/dictionary.digium +\end{verbatim} + +\subsubsection{Install FreeRADIUS Server (Version 1.1.1)} + + Download sources tarball from: + + http://freeradius.org/ + + Untar, configure, build, and install the server: + +\begin{verbatim} + root@localhost:/usr/local/src# tar xvfz freeradius-1.1.1.tar.gz + root@localhost:/usr/local/src# cd freeradius-1.1.1 + root@localhost"/usr/local/src/freeradius-1.1.1# ./configure + root@localhost"/usr/local/src/freeradius-1.1.1# make + root@localhost"/usr/local/src/freeradius-1.1.1# make install +\end{verbatim} + + All the configuration files of FreeRADIUS server will be in + /usr/local/etc/raddb directory. + + +\subsubsection{Configuration of the FreeRADIUS Server} + + There are several files that have to be modified to configure the + RADIUS server. These are presented next. + + File "clients.conf" + + File /usr/local/etc/raddb/clients.conf contains description of + RADIUS clients that are allowed to use the server. For each of the + clients you need to specify its hostname or IP address and also a + shared secret. The shared secret must be the same string you configured + in radiusclient library. + + Example: +\begin{verbatim} + client myhost { + secret = mysecret + shortname = foo + } +\end{verbatim} + + This fragment allows access from RADIUS clients on "myhost" if they use + "mysecret" as the shared secret. + The file already contains an entry for localhost (127.0.0.1), so if you + are running the RADIUS server on the same host as your Asterisk server, + then modify the existing entry instead, replacing the default password. + + File "dictionary" + + Note : as of version 1.1.2, the dictionary.digium file ships with FreeRADIUS. + The following procedure brings the dictionary.digium file to previous versions + of FreeRADIUS. + + File /usr/local/etc/raddb/dictionary contains the dictionary of + FreeRADIUS server. You have to add the same dictionary file + (dictionary.digium), which you added to the dictionary of radiusclient-ng + library. You can include it into the main file, adding the following line at the + end of file '/usr/local/etc/raddb/dictionary': + + \$INCLUDE /path/to/dictionary.digium + + That will include the same new attribute definitions that are used + in radiusclient-ng library so the client and server will understand each + other. + + +\subsubsection{Asterisk Accounting Configuration} + + Compilation and installation: + + The module will be compiled as long as the radiusclient-ng + library has been detected on your system. + + By default FreeRADIUS server will log all accounting requests into + /usr/local/var/log/radius/radacct directory in form of plain text files. + The server will create one file for each hostname in the directory. The + following example shows how the log files look like. + + Asterisk now generates Call Detail Records. See /include/asterisk/cel.h + for all the fields which are recorded. By default, records in comma + separated values will be created in /var/log/asterisk/cel-csv. + + The configuration file for cel\_radius.so module is : + + /etc/asterisk/cel.conf + This is where you can set CEL related parameters as well as the path to + the radiusclient-ng library configuration file. + + +\subsubsection{Logged Values} +\begin{verbatim} + "Asterisk-Acc-Code", The account name of detail records + "Asterisk-CidName", + "Asterisk-CidNum", + "Asterisk-Cidani", + "Asterisk-Cidrdnis", + "Asterisk-Ciddnid", + "Asterisk-Exten", + "Asterisk-Context", The destination context + "Asterisk-Channame", The channel name + "Asterisk-Appname", Last application run on the channel + "Asterisk-App-Data", Argument to the last channel + "Asterisk-Event-Time", + "Asterisk-Event-Type", + "Asterisk-AMA-Flags", DOCUMENTATION, BILL, IGNORE etc, specified on + a per channel basis like accountcode. + "Asterisk-Unique-ID", Unique call identifier + "Asterisk-User-Field" User field set via SetCELUserField + "Asterisk-Peer" Name of the Peer for 2-channel events (like bridge) + +\end{verbatim} diff --git a/funcs/func_cdr.c b/funcs/func_cdr.c index 2e55d7a0b..e7f14af2a 100644 --- a/funcs/func_cdr.c +++ b/funcs/func_cdr.c @@ -241,6 +241,8 @@ static int cdr_write(struct ast_channel *chan, const char *cmd, char *parse, if (!strcasecmp(args.variable, "accountcode")) /* the 'l' flag doesn't apply to setting the accountcode, userfield, or amaflags */ ast_cdr_setaccount(chan, value); + else if (!strcasecmp(args.variable, "peeraccount")) + ast_cdr_setpeeraccount(chan, value); else if (!strcasecmp(args.variable, "userfield")) ast_cdr_setuserfield(chan, value); else if (!strcasecmp(args.variable, "amaflags")) diff --git a/funcs/func_channel.c b/funcs/func_channel.c index 5d81ad980..ec97df9d6 100644 --- a/funcs/func_channel.c +++ b/funcs/func_channel.c @@ -29,6 +29,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include <regex.h> +#include <ctype.h> #include "asterisk/module.h" #include "asterisk/channel.h" @@ -271,13 +272,62 @@ static int func_channel_read(struct ast_channel *chan, const char *function, locked_copy_string(chan, buf, ast_state2str(chan->_state), len); else if (!strcasecmp(data, "channeltype")) locked_copy_string(chan, buf, chan->tech->type, len); - else if (!strcasecmp(data, "transfercapability")) + else if (!strcasecmp(data, "accountcode")) + locked_copy_string(chan, buf, chan->accountcode, len); + else if (!strcasecmp(data, "peeraccount")) + locked_copy_string(chan, buf, chan->peeraccount, len); + else if (!strcasecmp(data, "hangupsource")) + locked_copy_string(chan, buf, chan->hangupsource, len); + else if (!strcasecmp(data, "appname") && chan->appl) + locked_copy_string(chan, buf, chan->appl, len); + else if (!strcasecmp(data, "appdata") && chan->data) + locked_copy_string(chan, buf, chan->data, len); + else if (!strcasecmp(data, "exten") && chan->data) + locked_copy_string(chan, buf, chan->exten, len); + else if (!strcasecmp(data, "context") && chan->data) + locked_copy_string(chan, buf, chan->context, len); + else if (!strcasecmp(data, "userfield") && chan->data) + locked_copy_string(chan, buf, chan->userfield, len); + else if (!strcasecmp(data, "channame") && chan->data) + locked_copy_string(chan, buf, chan->name, len); + else if (!strcasecmp(data, "linkedid")) { + ast_channel_lock(chan); + if (ast_strlen_zero(chan->linkedid)) { + /* fall back on the channel's uniqueid if linkedid is unset */ + ast_copy_string(buf, chan->uniqueid, len); + } + else { + ast_copy_string(buf, chan->linkedid, len); + } + ast_channel_unlock(chan); + } else if (!strcasecmp(data, "peer")) { + struct ast_channel *p; + ast_channel_lock(chan); + p = ast_bridged_channel(chan); + if (p || chan->tech || chan->cdr) /* dummy channel? if so, we hid the peer name in the language */ + ast_copy_string(buf, (p ? p->name : ""), len); + else { + /* a dummy channel can still pass along bridged peer info via + the BRIDGEPEER variable */ + const char *pname = pbx_builtin_getvar_helper(chan, "BRIDGEPEER"); + if (!ast_strlen_zero(pname)) + ast_copy_string(buf, pname, len); /* a horrible kludge, but... how else? */ + else + buf[0] = 0; + } + ast_channel_unlock(chan); + } else if (!strcasecmp(data, "uniqueid")) { + locked_copy_string(chan, buf, chan->uniqueid, len); + } else if (!strcasecmp(data, "transfercapability")) locked_copy_string(chan, buf, transfercapability_table[chan->transfercapability & 0x1f], len); else if (!strcasecmp(data, "callgroup")) { char groupbuf[256]; locked_copy_string(chan, buf, ast_print_group(groupbuf, sizeof(groupbuf), chan->callgroup), len); - } else if (!chan->tech->func_channel_read - || chan->tech->func_channel_read(chan, function, data, buf, len)) { + } else if (!strcasecmp(data, "amaflags")) { + char amabuf[256]; + snprintf(amabuf,sizeof(amabuf), "%d", chan->amaflags); + locked_copy_string(chan, buf, amabuf, len); + } else if (!chan->tech || !chan->tech->func_channel_read || chan->tech->func_channel_read(chan, function, data, buf, len)) { ast_log(LOG_WARNING, "Unknown or unavailable item requested: '%s'\n", data); ret = -1; } @@ -297,12 +347,33 @@ static int func_channel_write(struct ast_channel *chan, const char *function, locked_string_field_set(chan, parkinglot, value); else if (!strcasecmp(data, "musicclass")) locked_string_field_set(chan, musicclass, value); + else if (!strcasecmp(data, "accountcode")) + locked_string_field_set(chan, accountcode, value); + else if (!strcasecmp(data, "userfield")) + locked_string_field_set(chan, userfield, value); + else if (!strcasecmp(data, "amaflags")) { + ast_channel_lock(chan); + if(isdigit(*value)) { + sscanf(value, "%d", &chan->amaflags); + } else if (!strcasecmp(value,"OMIT")){ + chan->amaflags = 1; + } else if (!strcasecmp(value,"BILLING")){ + chan->amaflags = 2; + } else if (!strcasecmp(value,"DOCUMENTATION")){ + chan->amaflags = 3; + } + ast_channel_unlock(chan); + } else if (!strcasecmp(data, "peeraccount")) + locked_string_field_set(chan, peeraccount, value); + else if (!strcasecmp(data, "hangupsource")) + /* XXX - should we be forcing this here? */ + ast_set_hangupsource(chan, value, 0); #ifdef CHANNEL_TRACE else if (!strcasecmp(data, "trace")) { ast_channel_lock(chan); if (ast_true(value)) ret = ast_channel_trace_enable(chan); - else if (ast_false(value)) + else if (ast_false(value)) ret = ast_channel_trace_disable(chan); else { ret = -1; diff --git a/funcs/func_odbc.c b/funcs/func_odbc.c index 8bb744f62..c8b65eb22 100644 --- a/funcs/func_odbc.c +++ b/funcs/func_odbc.c @@ -238,7 +238,7 @@ static int acf_odbc_write(struct ast_channel *chan, const char *cmd, char *s, co } if (!chan) { - if ((chan = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/func_odbc"))) + if ((chan = ast_channel_alloc(0, AST_STATE_DOWN, "", "", "", "", "", "", 0, "Bogus/func_odbc"))) bogus_chan = 1; } @@ -414,7 +414,7 @@ static int acf_odbc_read(struct ast_channel *chan, const char *cmd, char *s, cha } if (!chan) { - if ((chan = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/func_odbc"))) { + if ((chan = ast_channel_alloc(0, AST_STATE_DOWN, "", "", "", "", "", "", 0, "Bogus/func_odbc"))) { bogus_chan = 1; } } @@ -1051,7 +1051,7 @@ static char *cli_odbc_read(struct ast_cli_entry *e, int cmd, struct ast_cli_args /* Evaluate function */ char_args = ast_strdupa(a->argv[3]); - chan = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/func_odbc"); + chan = ast_dummy_channel_alloc(); AST_STANDARD_APP_ARGS(args, char_args); for (i = 0; i < args.argc; i++) { @@ -1254,7 +1254,7 @@ static char *cli_odbc_write(struct ast_cli_entry *e, int cmd, struct ast_cli_arg char_args = ast_strdupa(a->argv[3]); char_values = ast_strdupa(a->argv[4]); - chan = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/func_odbc"); + chan = ast_dummy_channel_alloc(); AST_STANDARD_APP_ARGS(args, char_args); for (i = 0; i < args.argc; i++) { diff --git a/include/asterisk/_private.h b/include/asterisk/_private.h index e60ba0690..fc65a463c 100644 --- a/include/asterisk/_private.h +++ b/include/asterisk/_private.h @@ -42,6 +42,8 @@ int ast_timing_init(void); /*!< Provided by timing.c */ int ast_indications_init(void); /*!< Provided by indications.c */ int ast_indications_reload(void);/*!< Provided by indications.c */ void ast_stun_init(void); /*!< Provided by stun.c */ +int ast_cel_engine_init(void); /*!< Provided by cel.c */ +int ast_cel_engine_reload(void); /*!< Provided by cel.c */ /*! * \brief Reload asterisk modules. diff --git a/include/asterisk/cdr.h b/include/asterisk/cdr.h index 1f71039eb..21b701459 100644 --- a/include/asterisk/cdr.h +++ b/include/asterisk/cdr.h @@ -94,16 +94,20 @@ struct ast_cdr { /*! Total time call is up, in seconds */ long int billsec; /*! What happened to the call */ - long int disposition; + long int disposition; /*! What flags to use */ - long int amaflags; + long int amaflags; /*! What account number to use */ - char accountcode[AST_MAX_ACCOUNT_CODE]; + char accountcode[AST_MAX_ACCOUNT_CODE]; + /*! Account number of the last person we talked to */ + char peeraccount[AST_MAX_ACCOUNT_CODE]; /*! flags */ - unsigned int flags; + unsigned int flags; /*! Unique Channel Identifier * 150 = 127 (max systemname) + "-" + 10 (epoch timestamp) + "." + 10 (monotonically incrementing integer) + NULL */ char uniqueid[150]; + /* Linked group Identifier */ + char linkedid[32]; /*! User field */ char userfield[AST_MAX_USER_FIELD]; @@ -353,6 +357,9 @@ void ast_cdr_merge(struct ast_cdr *to, struct ast_cdr *from); /*! \brief Set account code, will generate AMI event */ int ast_cdr_setaccount(struct ast_channel *chan, const char *account); +/*! \brief Set the peer account */ +int ast_cdr_setpeeraccount(struct ast_channel *chan, const char *account); + /*! \brief Set AMA flags for channel */ int ast_cdr_setamaflags(struct ast_channel *chan, const char *amaflags); diff --git a/include/asterisk/cel.h b/include/asterisk/cel.h new file mode 100644 index 000000000..14f886db2 --- /dev/null +++ b/include/asterisk/cel.h @@ -0,0 +1,276 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2008 - 2009, 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 Call Event Logging API + */ + +#ifndef __AST_CEL_H__ +#define __AST_CEL_H__ + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#include "asterisk/event.h" + +/*! \brief AMA Flags */ +enum ast_cel_ama_flag { + AST_CEL_AMA_FLAG_OMIT, + AST_CEL_AMA_FLAG_BILLING, + AST_CEL_AMA_FLAG_DOCUMENTATION, + /*! \brief Must be final entry */ + AST_CEL_AMA_FLAG_TOTAL, +}; + +/*! + * \brief CEL event types + */ +enum ast_cel_event_type { + /*! \brief channel birth */ + AST_CEL_CHANNEL_START = 1, + /*! \brief channel end */ + AST_CEL_CHANNEL_END = 2, + /*! \brief hangup terminates connection */ + AST_CEL_HANGUP = 3, + /*! \brief A ringing phone is answered */ + AST_CEL_ANSWER = 4, + /*! \brief an app starts */ + AST_CEL_APP_START = 5, + /*! \brief an app ends */ + AST_CEL_APP_END = 6, + /*! \brief a bridge is established */ + AST_CEL_BRIDGE_START = 7, + /*! \brief a bridge is torn down */ + AST_CEL_BRIDGE_END = 8, + /*! \brief a conference is started */ + AST_CEL_CONF_START = 9, + /*! \brief a conference is ended */ + AST_CEL_CONF_END = 10, + /*! \brief a channel is parked */ + AST_CEL_PARK_START = 11, + /*! \brief channel out of the park */ + AST_CEL_PARK_END = 12, + /*! \brief a transfer occurs */ + AST_CEL_BLINDTRANSFER = 13, + /*! \brief a transfer occurs */ + AST_CEL_ATTENDEDTRANSFER = 14, + /*! \brief a transfer occurs */ + AST_CEL_TRANSFER = 15, + /*! \brief a 3-way conference, usually part of a transfer */ + AST_CEL_HOOKFLASH = 16, + /*! \brief a 3-way conference, usually part of a transfer */ + AST_CEL_3WAY_START = 17, + /*! \brief a 3-way conference, usually part of a transfer */ + AST_CEL_3WAY_END = 18, + /*! \brief channel enters a conference */ + AST_CEL_CONF_ENTER = 19, + /*! \brief channel exits a conference */ + AST_CEL_CONF_EXIT = 20, + /*! \brief a user-defined event, the event name field should be set */ + AST_CEL_USER_DEFINED = 21, + /*! \brief the last channel with the given linkedid is retired */ + AST_CEL_LINKEDID_END = 22, + /*! \brief a masquerade happened to alter the participants on a bridge */ + AST_CEL_BRIDGE_UPDATE = 23, + /*! \brief a directed pickup was performed on this channel */ + AST_CEL_PICKUP = 24, + /*! \brief this call was forwarded somewhere else */ + AST_CEL_FORWARD = 25, +}; + +/*! + * \brief Check to see if CEL is enabled + * + * \since 1.6.3 + * + * \retval zero not enabled + * \retval non-zero enabled + */ +unsigned int ast_cel_check_enabled(void); + +/*! + * \brief Allocate a CEL record + * + * \since 1.6.3 + * + * \note The CEL record must be destroyed with ast_cel_destroy(). + * + * \retval non-NULL an allocated ast_cel structure + * \retval NULL error + */ +struct ast_cel *ast_cel_alloc(void); + +/*! + * \brief Destroy a CEL record. + * + * \param cel the record to destroy + * + * \since 1.6.3 + * + * \return nothing. + */ +void ast_cel_destroy(struct ast_cel *cel); + +/*! + * \brief Get the name of a CEL event type + * + * \param type the type to get the name of + * + * \since 1.6.3 + * + * \return the string representation of the type + */ +const char *ast_cel_get_type_name(enum ast_cel_event_type type); + +/*! + * \brief Get the event type from a string + * + * \param name the event type name as a string + * + * \since 1.6.3 + * + * \return the ast_cel_event_type given by the string + */ +enum ast_cel_event_type ast_cel_str_to_event_type(const char *name); + +/*! + * \brief Convert AMA flag to printable string + * + * \param[in] flag the flag to convert to a string + * + * \since 1.6.3 + * + * \return the string representation of the flag + */ +const char *ast_cel_get_ama_flag_name(enum ast_cel_ama_flag flag); + +/*! + * \brief Check and potentially retire a Linked ID + * + * \param chan channel that is being destroyed or its linkedid is changing + * + * \since 1.6.3 + * + * If at least one CEL backend is looking for CEL_LINKEDID_END + * events, this function will check if the given channel is the last + * active channel with that linkedid, and if it is, emit a + * CEL_LINKEDID_END event. + * + * \return nothing + */ +void ast_cel_check_retire_linkedid(struct ast_channel *chan); + +/*! + * \brief Create a fake channel from data in a CEL event + * + * This function creates a fake channel containing the serialized channel data + * in the given cel event. It must be released with ast_channel_release. + * + * \param event the CEL event + * + * \since 1.6.3 + * + * \return a channel with the data filled in, or NULL on error + * + * \todo This function is \b very expensive, especially given that some CEL backends + * use it on \b every CEL event. This function really needs to go away at + * some point. + */ +struct ast_channel *ast_cel_fabricate_channel_from_event(const struct ast_event *event); + +/*! + * \brief Report a channel event + * + * \param chan This argument is required. This is the primary channel associated with + * this channel event. + * \param event_type This is the type of call event being reported. + * \param userdefevname This is an optional custom name for the call event. + * \param extra This is an optional opaque field that will go into the "CEL_EXTRA" + * information element of the call event. + * \param peer2 All CEL events contain a "peer name" information element. The first + * place the code will look to get a peer name is from the bridged channel to + * chan. If chan has no bridged channel and peer2 is specified, then the name + * of peer2 will go into the "peer name" field. If neither are available, the + * peer name field will be blank. + * + * \since 1.6.3 + * + * \pre chan and peer2 are both unlocked + * + * \retval 0 success + * \retval non-zero failure + */ +int ast_cel_report_event(struct ast_channel *chan, enum ast_cel_event_type event_type, + const char *userdefevname, const char *extra, struct ast_channel *peer2); + +/*! + * \brief Helper struct for getting the fields out of a CEL event + */ +struct ast_cel_event_record { + /*! + * \brief struct ABI version + * \note This \b must be incremented when the struct changes. + */ + #define AST_CEL_EVENT_RECORD_VERSION 2 + /*! + * \brief struct ABI version + * \note This \b must stay as the first member. + */ + uint32_t version; + enum ast_cel_event_type event_type; + struct timeval event_time; + const char *event_name; + const char *user_defined_name; + const char *caller_id_name; + const char *caller_id_num; + const char *caller_id_ani; + const char *caller_id_rdnis; + const char *caller_id_dnid; + const char *extension; + const char *context; + const char *channel_name; + const char *application_name; + const char *application_data; + const char *account_code; + const char *peer_account; + const char *unique_id; + const char *linked_id; + uint amaflag; + const char *user_field; + const char *peer; + const char *extra; +}; + +/*! + * \brief Fill in an ast_cel_event_record from a CEL event + * + * \param[in] event the CEL event + * \param[out] r the ast_cel_event_record to fill in + * + * \since 1.6.3 + * + * \retval 0 success + * \retval non-zero failure + */ +int ast_cel_fill_record(const struct ast_event *event, struct ast_cel_event_record *r); + +#if defined(__cplusplus) || defined(c_plusplus) +} +#endif + +#endif /* __AST_CEL_H__ */ diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index d35601d42..8976997e5 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -361,7 +361,7 @@ struct ast_channel_tech { int properties; /*!< Technology Properties */ /*! \brief Requester - to set up call data structures (pvt's) */ - struct ast_channel *(* const requester)(const char *type, int format, void *data, int *cause); + struct ast_channel *(* const requester)(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause); int (* const devicestate)(void *data); /*!< Devicestate call back */ @@ -612,9 +612,13 @@ struct ast_channel { AST_STRING_FIELD(language); /*!< Language requested for voice prompts */ AST_STRING_FIELD(musicclass); /*!< Default music class */ AST_STRING_FIELD(accountcode); /*!< Account code for billing */ + AST_STRING_FIELD(peeraccount); /*!< Peer account code for billing */ + AST_STRING_FIELD(userfield); /*!< Userfield for CEL billing */ AST_STRING_FIELD(call_forward); /*!< Where to forward to if asked to dial on this interface */ AST_STRING_FIELD(uniqueid); /*!< Unique Channel Identifier */ + AST_STRING_FIELD(linkedid); /*!< Linked Channel Identifier -- gets propagated by linkage */ AST_STRING_FIELD(parkinglot); /*! Default parking lot, if empty, default parking lot */ + AST_STRING_FIELD(hangupsource); /*! Who is responsible for hanging up this channel */ AST_STRING_FIELD(dialcontext); /*!< Dial: Extension context that we were called from */ ); @@ -913,17 +917,30 @@ int ast_setstate(struct ast_channel *chan, enum ast_channel_state); * \note By default, new channels are set to the "s" extension * and "default" context. */ -struct ast_channel * attribute_malloc __attribute__((format(printf, 12, 13))) +struct ast_channel * attribute_malloc __attribute__((format(printf, 13, 14))) __ast_channel_alloc(int needqueue, int state, const char *cid_num, const char *cid_name, const char *acctcode, const char *exten, const char *context, - const int amaflag, const char *file, int line, - const char *function, const char *name_fmt, ...); + const char *linkedid, const int amaflag, + const char *file, int line, const char *function, + const char *name_fmt, ...); -#define ast_channel_alloc(needqueue, state, cid_num, cid_name, acctcode, exten, context, amaflag, ...) \ - __ast_channel_alloc(needqueue, state, cid_num, cid_name, acctcode, exten, context, amaflag, \ +#define ast_channel_alloc(needqueue, state, cid_num, cid_name, acctcode, exten, context, linkedid, amaflag, ...) \ + __ast_channel_alloc(needqueue, state, cid_num, cid_name, acctcode, exten, context, linkedid, amaflag, \ __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__) +/*! + * \brief Create a fake channel structure + * + * \retval NULL failure + * \retval non-NULL successfully allocated channel + * + * \note This function should ONLY be used to create a fake channel + * that can then be populated with data for use in variable + * substitution when a real channel does not exist. + */ +struct ast_channel *ast_dummy_channel_alloc(void); + /*! * \brief Queue one or more frames to a channel's frame queue * @@ -1035,7 +1052,7 @@ void ast_change_name(struct ast_channel *chan, const char *newname); */ struct ast_channel *ast_channel_release(struct ast_channel *chan); -/*! +/*! * \brief Requests a channel * * \param type type of channel to request @@ -1050,7 +1067,7 @@ struct ast_channel *ast_channel_release(struct ast_channel *chan); * \retval NULL failure * \retval non-NULL channel on success */ -struct ast_channel *ast_request(const char *type, int format, void *data, int *status); +struct ast_channel *ast_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *status); /*! * \brief Request a channel of a given type, with data as optional information used @@ -1067,7 +1084,7 @@ struct ast_channel *ast_request(const char *type, int format, void *data, int *s * \return Returns an ast_channel on success or no answer, NULL on failure. Check the value of chan->_state * to know if the call was answered or not. */ -struct ast_channel *ast_request_and_dial(const char *type, int format, void *data, +struct ast_channel *ast_request_and_dial(const char *type, int format, const struct ast_channel *requestor, void *data, int timeout, int *reason, const char *cid_num, const char *cid_name); /*! @@ -1084,7 +1101,7 @@ struct ast_channel *ast_request_and_dial(const char *type, int format, void *dat * \return Returns an ast_channel on success or no answer, NULL on failure. Check the value of chan->_state * to know if the call was answered or not. */ -struct ast_channel *__ast_request_and_dial(const char *type, int format, void *data, +struct ast_channel *__ast_request_and_dial(const char *type, int format, const struct ast_channel *requestor, void *data, int timeout, int *reason, const char *cid_num, const char *cid_name, struct outgoing_helper *oh); /*! @@ -1188,6 +1205,16 @@ int ast_softhangup(struct ast_channel *chan, int cause); */ int ast_softhangup_nolock(struct ast_channel *chan, int cause); +/*! \brief Set the source of the hangup in this channel and it's bridge + * \param chan channel to set the field on + * \param source a string describing the source of the hangup for this channel + * + * Hangupsource is generally the channel name that caused the bridge to be + * hung up, but it can also be other things such as "dialplan/agi" + * This can then be logged in the CDR or CEL + */ +void ast_set_hangupsource(struct ast_channel *chan, const char *source, int force); + /*! \brief Check to see if a channel is needing hang up * \param chan channel on which to check for hang up * This function determines if the channel is being requested to be hung up. @@ -2281,6 +2308,12 @@ struct ast_channel *ast_channel_get_by_exten(const char *exten, const char *cont /*! @} End channel search functions. */ /*! + \brief propagate the linked id between chan and peer + */ +void ast_channel_set_linkgroup(struct ast_channel *chan, struct ast_channel *peer); + + +/*! * \since 1.6.3 * \brief Copy the source caller information to the destination caller. * diff --git a/include/asterisk/event.h b/include/asterisk/event.h index c2afc57ef..8c23c949c 100644 --- a/include/asterisk/event.h +++ b/include/asterisk/event.h @@ -111,7 +111,7 @@ typedef void (*ast_event_cb_t)(const struct ast_event *event, void *userdata); * pointer to the peer. */ struct ast_event_sub *ast_event_subscribe(enum ast_event_type event_type, - ast_event_cb_t cb, void *userdata, ...); + ast_event_cb_t cb, char *description, void *userdata, ...); /*! * \brief Allocate a subscription, but do not activate it @@ -242,6 +242,15 @@ int ast_event_sub_activate(struct ast_event_sub *sub); struct ast_event_sub *ast_event_unsubscribe(struct ast_event_sub *event_sub); /*! + * \brief Get description for a subscription + * + * \param sub subscription + * + * \return string description of the subscription + */ +const char *ast_event_subscriber_get_description(struct ast_event_sub *sub); + +/*! * \brief Check if subscribers exist * * \param event_type This is the type of event that the caller would like to diff --git a/include/asterisk/event_defs.h b/include/asterisk/event_defs.h index 9bebfb69d..99edb6f55 100644 --- a/include/asterisk/event_defs.h +++ b/include/asterisk/event_defs.h @@ -48,8 +48,10 @@ enum ast_event_type { /*! The state of a device has changed on _one_ server. This should not be used * directly, in general. Use AST_EVENT_DEVICE_STATE instead. */ AST_EVENT_DEVICE_STATE_CHANGE = 0x06, + /*! Channel Event Logging events */ + AST_EVENT_CEL = 0x07, /*! Number of event types. This should be the last event type + 1 */ - AST_EVENT_TOTAL = 0x07, + AST_EVENT_TOTAL = 0x08, }; /*! \brief Event Information Element types */ @@ -92,7 +94,7 @@ enum ast_event_ie_type { * Used by: AST_EVENT_SUB * Payload type: UINT (ast_event_ie_type) */ - AST_EVENT_IE_EXISTS = 0x06, + AST_EVENT_IE_EXISTS = 0x6, /*! * \brief Device Name * Used by AST_EVENT_DEVICE_STATE_CHANGE @@ -113,13 +115,151 @@ enum ast_event_ie_type { * Payload type: str */ AST_EVENT_IE_CONTEXT = 0x09, - /*! - * \brief Entity ID - * Used by All events - * Payload type: RAW - * This IE indicates which server the event originated from - */ - AST_EVENT_IE_EID = 0x0A, + /*! + * \brief Channel Event Type + * Used by: AST_EVENT_CEL + * Payload type: UINT + */ + AST_EVENT_IE_CEL_EVENT_TYPE = 0x0a, + /*! + * \brief Channel Event Time (seconds) + * Used by: AST_EVENT_CEL + * Payload type: UINT + */ + AST_EVENT_IE_CEL_EVENT_TIME = 0x0b, + /*! + * \brief Channel Event Time (micro-seconds) + * Used by: AST_EVENT_CEL + * Payload type: UINT + */ + AST_EVENT_IE_CEL_EVENT_TIME_USEC = 0x0c, + /*! + * \brief Channel Event User Event Name + * Used by: AST_EVENT_CEL + * Payload type: STR + */ + AST_EVENT_IE_CEL_USEREVENT_NAME = 0x0d, + /*! + * \brief Channel Event CID name + * Used by: AST_EVENT_CEL + * Payload type: STR + */ + AST_EVENT_IE_CEL_CIDNAME = 0x0e, + /*! + * \brief Channel Event CID num + * Used by: AST_EVENT_CEL + * Payload type: STR + */ + AST_EVENT_IE_CEL_CIDNUM = 0x0f, + /*! + * \brief Channel Event extension name + * Used by: AST_EVENT_CEL + * Payload type: STR + */ + AST_EVENT_IE_CEL_EXTEN = 0x10, + /*! + * \brief Channel Event context name + * Used by: AST_EVENT_CEL + * Payload type: STR + */ + AST_EVENT_IE_CEL_CONTEXT = 0x11, + /*! + * \brief Channel Event channel name + * Used by: AST_EVENT_CEL + * Payload type: STR + */ + AST_EVENT_IE_CEL_CHANNAME = 0x12, + /*! + * \brief Channel Event app name + * Used by: AST_EVENT_CEL + * Payload type: STR + */ + AST_EVENT_IE_CEL_APPNAME = 0x13, + /*! + * \brief Channel Event app args/data + * Used by: AST_EVENT_CEL + * Payload type: STR + */ + AST_EVENT_IE_CEL_APPDATA = 0x14, + /*! + * \brief Channel Event AMA flags + * Used by: AST_EVENT_CEL + * Payload type: UINT + */ + AST_EVENT_IE_CEL_AMAFLAGS = 0x15, + /*! + * \brief Channel Event AccountCode + * Used by: AST_EVENT_CEL + * Payload type: STR + */ + AST_EVENT_IE_CEL_ACCTCODE = 0x16, + /*! + * \brief Channel Event UniqueID + * Used by: AST_EVENT_CEL + * Payload type: STR + */ + AST_EVENT_IE_CEL_UNIQUEID = 0x17, + /*! + * \brief Channel Event Userfield + * Used by: AST_EVENT_CEL + * Payload type: STR + */ + AST_EVENT_IE_CEL_USERFIELD = 0x18, + /*! + * \brief Channel Event CID ANI field + * Used by: AST_EVENT_CEL + * Payload type: STR + */ + AST_EVENT_IE_CEL_CIDANI = 0x19, + /*! + * \brief Channel Event CID RDNIS field + * Used by: AST_EVENT_CEL + * Payload type: STR + */ + AST_EVENT_IE_CEL_CIDRDNIS = 0x1a, + /*! + * \brief Channel Event CID dnid + * Used by: AST_EVENT_CEL + * Payload type: STR + */ + AST_EVENT_IE_CEL_CIDDNID = 0x1b, + /*! + * \brief Channel Event Peer -- for Things involving multiple channels, like BRIDGE + * Used by: AST_EVENT_CEL + * Payload type: STR + */ + AST_EVENT_IE_CEL_PEER = 0x1c, + /*! + * \brief Channel Event LinkedID + * Used by: AST_EVENT_CEL + * Payload type: STR + */ + AST_EVENT_IE_CEL_LINKEDID = 0x1d, + /*! + * \brief Channel Event peeraccount + * Used by: AST_EVENT_CEL + * Payload type: STR + */ + AST_EVENT_IE_CEL_PEERACCT = 0x1e, + /*! + * \brief Channel Event extra data + * Used by: AST_EVENT_CEL + * Payload type: STR + */ + AST_EVENT_IE_CEL_EXTRA = 0x1f, + /*! + * \brief Description + * Used by: AST_EVENT_SUB, AST_EVENT_UNSUB + * Payload type: STR + */ + AST_EVENT_IE_DESCRIPTION = 0x20, + /*! + * \brief Entity ID + * Used by All events + * Payload type: RAW + * This IE indicates which server the event originated from + */ + AST_EVENT_IE_EID = 0x21, }; #define AST_EVENT_IE_MAX AST_EVENT_IE_EID diff --git a/include/asterisk/utils.h b/include/asterisk/utils.h index abbeba2ab..6f9684409 100644 --- a/include/asterisk/utils.h +++ b/include/asterisk/utils.h @@ -27,6 +27,7 @@ #include <time.h> /* we want to override localtime_r */ #include <unistd.h> +#include <string.h> #include "asterisk/lock.h" #include "asterisk/time.h" diff --git a/main/asterisk.c b/main/asterisk.c index e83534787..b497a64a5 100644 --- a/main/asterisk.c +++ b/main/asterisk.c @@ -118,6 +118,7 @@ int daemon(int, int); /* defined in libresolv of all places */ #include "asterisk/term.h" #include "asterisk/manager.h" #include "asterisk/cdr.h" +#include "asterisk/cel.h" #include "asterisk/pbx.h" #include "asterisk/enum.h" #include "asterisk/http.h" @@ -3599,6 +3600,11 @@ int main(int argc, char *argv[]) exit(1); } + if (ast_cel_engine_init()) { + printf("%s", term_quit()); + exit(1); + } + if (ast_device_state_engine_init()) { printf("%s", term_quit()); exit(1); diff --git a/main/cdr.c b/main/cdr.c index 610c2ab31..0658ead75 100644 --- a/main/cdr.c +++ b/main/cdr.c @@ -271,8 +271,12 @@ void ast_cdr_getvar(struct ast_cdr *cdr, const char *name, char **ret, char *wor } } else if (!strcasecmp(name, "accountcode")) ast_copy_string(workspace, cdr->accountcode, workspacelen); + else if (!strcasecmp(name, "peeraccount")) + ast_copy_string(workspace, cdr->peeraccount, workspacelen); else if (!strcasecmp(name, "uniqueid")) ast_copy_string(workspace, cdr->uniqueid, workspacelen); + else if (!strcasecmp(name, "linkedid")) + ast_copy_string(workspace, cdr->linkedid, workspacelen); else if (!strcasecmp(name, "userfield")) ast_copy_string(workspace, cdr->userfield, workspacelen); else if ((varbuf = ast_cdr_getvar_internal(cdr, name, recur))) @@ -287,7 +291,7 @@ void ast_cdr_getvar(struct ast_cdr *cdr, const char *name, char **ret, char *wor /* readonly cdr variables */ static const char * const cdr_readonly_vars[] = { "clid", "src", "dst", "dcontext", "channel", "dstchannel", "lastapp", "lastdata", "start", "answer", "end", "duration", - "billsec", "disposition", "amaflags", "accountcode", "uniqueid", + "billsec", "disposition", "amaflags", "accountcode", "uniqueid", "linkedid", "userfield", NULL }; /*! Set a CDR channel variable \note You can't set the CDR variables that belong to the actual CDR record, like "billsec". @@ -298,9 +302,6 @@ int ast_cdr_setvar(struct ast_cdr *cdr, const char *name, const char *value, int struct varshead *headp; int x; - if (!cdr) /* don't die if the cdr is null */ - return -1; - for (x = 0; cdr_readonly_vars[x]; x++) { if (!strcasecmp(name, cdr_readonly_vars[x])) { ast_log(LOG_ERROR, "Attempt to set the '%s' read-only variable!.\n", name); @@ -644,6 +645,9 @@ void ast_cdr_merge(struct ast_cdr *to, struct ast_cdr *from) if (ast_test_flag(from, AST_CDR_FLAG_LOCKED) || (ast_strlen_zero(to->accountcode) && !ast_strlen_zero(from->accountcode))) { ast_copy_string(to->accountcode, from->accountcode, sizeof(to->accountcode)); } + if (ast_test_flag(from, AST_CDR_FLAG_LOCKED) || (ast_strlen_zero(to->peeraccount) && !ast_strlen_zero(from->peeraccount))) { + ast_copy_string(to->peeraccount, from->peeraccount, sizeof(to->peeraccount)); + } if (ast_test_flag(from, AST_CDR_FLAG_LOCKED) || (ast_strlen_zero(to->userfield) && !ast_strlen_zero(from->userfield))) { ast_copy_string(to->userfield, from->userfield, sizeof(to->userfield)); } @@ -856,11 +860,14 @@ int ast_cdr_init(struct ast_cdr *cdr, struct ast_channel *c) cdr->disposition = (c->_state == AST_STATE_UP) ? AST_CDR_ANSWERED : AST_CDR_NOANSWER; cdr->amaflags = c->amaflags ? c->amaflags : ast_default_amaflags; ast_copy_string(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode)); + ast_copy_string(cdr->peeraccount, c->peeraccount, sizeof(cdr->peeraccount)); /* Destination information */ ast_copy_string(cdr->dst, S_OR(c->macroexten,c->exten), sizeof(cdr->dst)); ast_copy_string(cdr->dcontext, S_OR(c->macrocontext,c->context), sizeof(cdr->dcontext)); /* Unique call identifier */ ast_copy_string(cdr->uniqueid, c->uniqueid, sizeof(cdr->uniqueid)); + /* Linked call identifier */ + ast_copy_string(cdr->linkedid, c->linkedid, sizeof(cdr->linkedid)); } } return 0; @@ -938,9 +945,11 @@ char *ast_cdr_flags2str(int flag) int ast_cdr_setaccount(struct ast_channel *chan, const char *account) { struct ast_cdr *cdr = chan->cdr; - char buf[BUFSIZ/2] = ""; - if (!ast_strlen_zero(chan->accountcode)) - ast_copy_string(buf, chan->accountcode, sizeof(buf)); + const char *old_acct = ""; + + if (!ast_strlen_zero(chan->accountcode)) { + old_acct = ast_strdupa(chan->accountcode); + } ast_string_field_set(chan, accountcode, account); for ( ; cdr ; cdr = cdr->next) { @@ -949,8 +958,39 @@ int ast_cdr_setaccount(struct ast_channel *chan, const char *account) } } - /* Signal change of account code to manager */ - manager_event(EVENT_FLAG_CALL, "NewAccountCode", "Channel: %s\r\nUniqueid: %s\r\nAccountCode: %s\r\nOldAccountCode: %s\r\n", chan->name, chan->uniqueid, chan->accountcode, buf); + manager_event(EVENT_FLAG_CALL, "NewAccountCode", + "Channel: %s\r\n" + "Uniqueid: %s\r\n" + "AccountCode: %s\r\n" + "OldAccountCode: %s\r\n", + chan->name, chan->uniqueid, chan->accountcode, old_acct); + + return 0; +} + +int ast_cdr_setpeeraccount(struct ast_channel *chan, const char *account) +{ + struct ast_cdr *cdr = chan->cdr; + const char *old_acct = ""; + + if (!ast_strlen_zero(chan->peeraccount)) { + old_acct = ast_strdupa(chan->peeraccount); + } + + ast_string_field_set(chan, peeraccount, account); + for ( ; cdr ; cdr = cdr->next) { + if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) { + ast_copy_string(cdr->peeraccount, chan->peeraccount, sizeof(cdr->peeraccount)); + } + } + + manager_event(EVENT_FLAG_CALL, "NewPeerAccount", + "Channel: %s\r\n" + "Uniqueid: %s\r\n" + "PeerAccount: %s\r\n" + "OldPeerAccount: %s\r\n", + chan->name, chan->uniqueid, chan->peeraccount, old_acct); + return 0; } @@ -1005,7 +1045,9 @@ int ast_cdr_update(struct ast_channel *c) /* Copy account code et-al */ ast_copy_string(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode)); - + ast_copy_string(cdr->peeraccount, c->peeraccount, sizeof(cdr->peeraccount)); + ast_copy_string(cdr->linkedid, c->linkedid, sizeof(cdr->linkedid)); + /* Destination information */ /* XXX privilege macro* ? */ ast_copy_string(cdr->dst, S_OR(c->macroexten, c->exten), sizeof(cdr->dst)); ast_copy_string(cdr->dcontext, S_OR(c->macrocontext, c->context), sizeof(cdr->dcontext)); diff --git a/main/cel.c b/main/cel.c new file mode 100644 index 000000000..26b0fd9fa --- /dev/null +++ b/main/cel.c @@ -0,0 +1,652 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2007 - 2009, 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 Event Logging API + * + * \author Steve Murphy <murf@digium.com> + * \author Russell Bryant <russell@digium.com> + * + * \todo Do thorough testing of all transfer methods to ensure that BLINDTRANSFER, + * ATTENDEDTRANSFER, BRIDGE_START, and BRIDGE_END events are all reported + * as expected. + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/_private.h" + +#include "asterisk/channel.h" +#include "asterisk/pbx.h" +#include "asterisk/cel.h" +#include "asterisk/logger.h" +#include "asterisk/linkedlists.h" +#include "asterisk/utils.h" +#include "asterisk/config.h" +#include "asterisk/cli.h" +#include "asterisk/astobj2.h" + +/*! Is the CEL subsystem enabled ? */ +static unsigned char cel_enabled; + +/*! \brief CEL is off by default */ +static const unsigned char CEL_ENALBED_DEFAULT = 0; + +/*! + * \brief which events we want to track + * + * \note bit field, up to 64 events + */ +static int64_t eventset; + +/*! + * \brief Maximum possible CEL event IDs + * \note This limit is currently imposed by the eventset definition + */ +#define CEL_MAX_EVENT_IDS 64 + +/*! + * \brief Track no events by default. + */ +static const int64_t CEL_DEFAULT_EVENTS = 0; + +/*! + * \brief Number of buckets for the appset container + */ +static const int NUM_APP_BUCKETS = 97; + +/*! + * \brief Container of Asterisk application names + * + * The apps in this container are the applications that were specified + * in the configuration as applications that CEL events should be generated + * for when they start and end on a channel. + */ +static struct ao2_container *appset; + +/*! + * \brief Configured date format for event timestamps + */ +static char cel_dateformat[256]; + +/*! + * \brief Map of ast_cel_event_type to strings + */ +static const char const *cel_event_types[CEL_MAX_EVENT_IDS] = { + [0] = "ALL", + [AST_CEL_CHANNEL_START] = "CHAN_START", + [AST_CEL_CHANNEL_END] = "CHAN_END", + [AST_CEL_ANSWER] = "ANSWER", + [AST_CEL_HANGUP] = "HANGUP", + [AST_CEL_APP_START] = "APP_START", + [AST_CEL_APP_END] = "APP_END", + [AST_CEL_BRIDGE_START] = "BRIDGE_START", + [AST_CEL_BRIDGE_END] = "BRIDGE_END", + [AST_CEL_BRIDGE_UPDATE] = "BRIDGE_UPDATE", + [AST_CEL_CONF_START] = "CONF_START", + [AST_CEL_CONF_END] = "CONF_END", + [AST_CEL_PARK_START] = "PARK_START", + [AST_CEL_PARK_END] = "PARK_END", + [AST_CEL_TRANSFER] = "TRANSFER", + [AST_CEL_USER_DEFINED] = "USER_DEFINED", + [AST_CEL_CONF_ENTER] = "CONF_ENTER", + [AST_CEL_CONF_EXIT] = "CONF_EXIT", + [AST_CEL_BLINDTRANSFER] = "BLINDTRANSFER", + [AST_CEL_ATTENDEDTRANSFER] = "ATTENDEDTRANSFER", + [AST_CEL_PICKUP] = "PICKUP", + [AST_CEL_FORWARD] = "FORWARD", + [AST_CEL_3WAY_START] = "3WAY_START", + [AST_CEL_3WAY_END] = "3WAY_END", + [AST_CEL_HOOKFLASH] = "HOOKFLASH", + [AST_CEL_LINKEDID_END] = "LINKEDID_END", +}; + +/*! + * \brief Map of ast_cel_ama_flags to strings + */ +static const char const *cel_ama_flags[AST_CEL_AMA_FLAG_TOTAL] = { + [AST_CEL_AMA_FLAG_OMIT] = "OMIT", + [AST_CEL_AMA_FLAG_BILLING] = "BILLING", + [AST_CEL_AMA_FLAG_DOCUMENTATION] = "DOCUMENTATION", +}; + +unsigned int ast_cel_check_enabled(void) +{ + return cel_enabled; +} + +static int print_app(void *obj, void *arg, int flags) +{ + struct ast_cli_args *a = arg; + + ast_cli(a->fd, "CEL Tracking Application: %s\n", (const char *) obj); + + return 0; +} + +static void print_cel_sub(const struct ast_event *event, void *data) +{ + struct ast_cli_args *a = data; + + ast_cli(a->fd, "CEL Event Subscriber: %s\n", + ast_event_get_ie_str(event, AST_EVENT_IE_DESCRIPTION)); +} + +static char *handle_cli_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + unsigned int i; + struct ast_event_sub *sub; + + switch (cmd) { + case CLI_INIT: + e->command = "cel show status"; + e->usage = + "Usage: cel show status\n" + " Displays the Channel Event Logging system status.\n"; + return NULL; + case CLI_GENERATE: + return NULL; + case CLI_HANDLER: + break; + } + + if (a->argc > 3) { + return CLI_SHOWUSAGE; + } + + ast_cli(a->fd, "CEL Logging: %s\n", cel_enabled ? "Enabled" : "Disabled"); + + if (!cel_enabled) { + return CLI_SUCCESS; + } + + for (i = 0; i < (sizeof(eventset) * 8); i++) { + const char *name; + + if (!(eventset & ((int64_t) 1 << i))) { + continue; + } + + name = ast_cel_get_type_name(i); + if (strcasecmp(name, "Unknown")) { + ast_cli(a->fd, "CEL Tracking Event: %s\n", name); + } + } + + ao2_callback(appset, OBJ_NODATA, print_app, a); + + if (!(sub = ast_event_subscribe_new(AST_EVENT_SUB, print_cel_sub, a))) { + return CLI_FAILURE; + } + ast_event_sub_append_ie_uint(sub, AST_EVENT_IE_EVENTTYPE, AST_EVENT_CEL); + ast_event_report_subs(sub); + ast_event_sub_destroy(sub); + sub = NULL; + + return CLI_SUCCESS; +} + +static struct ast_cli_entry cli_status = AST_CLI_DEFINE(handle_cli_status, "Display the CEL status"); + +enum ast_cel_event_type ast_cel_str_to_event_type(const char *name) +{ + unsigned int i; + + for (i = 0; i < ARRAY_LEN(cel_event_types); i++) { + if (!cel_event_types[i]) { + continue; + } + + if (!strcasecmp(name, cel_event_types[i])) { + return i; + } + } + + return -1; +} + +static int ast_cel_track_event(enum ast_cel_event_type et) +{ + return (eventset & ((int64_t) 1 << et)); +} + +static void parse_events(const char *val) +{ + char *events = ast_strdupa(val); + char *cur_event; + + while ((cur_event = strsep(&events, ","))) { + enum ast_cel_event_type event_type; + + cur_event = ast_strip(cur_event); + if (ast_strlen_zero(cur_event)) { + continue; + } + + event_type = ast_cel_str_to_event_type(cur_event); + + if (event_type == 0) { + /* All events */ + eventset = (int64_t) -1; + } else if (event_type == -1) { + ast_log(LOG_WARNING, "Unknown event name '%s'\n", + cur_event); + } else { + eventset |= ((int64_t) 1 << event_type); + } + } +} + +static void parse_apps(const char *val) +{ + char *apps = ast_strdupa(val); + char *cur_app; + + if (!ast_cel_track_event(AST_CEL_APP_START) && !ast_cel_track_event(AST_CEL_APP_END)) { + ast_log(LOG_WARNING, "An apps= config line, but not tracking APP events\n"); + return; + } + + while ((cur_app = strsep(&apps, ","))) { + char *app; + + cur_app = ast_strip(cur_app); + if (ast_strlen_zero(cur_app)) { + continue; + } + + if (!(app = ao2_alloc(strlen(cur_app) + 1, NULL))) { + continue; + } + strcpy(app, cur_app); + + ao2_link(appset, app); + ao2_ref(app, -1); + app = NULL; + } +} + +AST_MUTEX_DEFINE_STATIC(reload_lock); + +static int do_reload(void) +{ + struct ast_config *config; + const char *enabled_value; + const char *val; + int res = 0; + struct ast_flags config_flags = { 0, }; + const char *s; + + ast_mutex_lock(&reload_lock); + + /* Reset all settings before reloading configuration */ + cel_enabled = CEL_ENALBED_DEFAULT; + eventset = CEL_DEFAULT_EVENTS; + *cel_dateformat = '\0'; + ao2_callback(appset, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL); + + config = ast_config_load2("cel.conf", "cel", config_flags); + + if (config == CONFIG_STATUS_FILEMISSING) { + config = NULL; + goto return_cleanup; + } + + if ((enabled_value = ast_variable_retrieve(config, "general", "enable"))) { + cel_enabled = ast_true(enabled_value); + } + + if (!cel_enabled) { + goto return_cleanup; + } + + /* get the date format for logging */ + if ((s = ast_variable_retrieve(config, "general", "dateformat"))) { + ast_copy_string(cel_dateformat, s, sizeof(cel_dateformat)); + } + + if ((val = ast_variable_retrieve(config, "general", "events"))) { + parse_events(val); + } + + if ((val = ast_variable_retrieve(config, "general", "apps"))) { + parse_apps(val); + } + +return_cleanup: + ast_verb(3, "CEL logging %sabled.\n", cel_enabled ? "en" : "dis"); + + ast_mutex_unlock(&reload_lock); + + if (config) { + ast_config_destroy(config); + } + + return res; +} + +const char *ast_cel_get_type_name(enum ast_cel_event_type type) +{ + return S_OR(cel_event_types[type], "Unknown"); +} + +const char *ast_cel_get_ama_flag_name(enum ast_cel_ama_flag flag) +{ + return S_OR(cel_ama_flags[flag], "Unknown"); +} + +/* called whenever a channel is destroyed or a linkedid is changed to + * potentially emit a CEL_LINKEDID_END event */ + +struct channel_find_data { + const struct ast_channel *chan; + const char *linkedid; +}; + +static int linkedid_match(void *obj, void *arg, void *data, int flags) +{ + struct ast_channel *c = obj; + struct channel_find_data *find_dat = data; + int res; + + ast_channel_lock(c); + res = (c != find_dat->chan && c->linkedid && !strcmp(find_dat->linkedid, c->linkedid)); + ast_channel_unlock(c); + + return res ? CMP_MATCH | CMP_STOP : 0; +} + +void ast_cel_check_retire_linkedid(struct ast_channel *chan) +{ + const char *linkedid = chan->linkedid; + struct channel_find_data find_dat; + + /* make sure we need to do all this work */ + + if (!ast_strlen_zero(linkedid) && ast_cel_track_event(AST_CEL_LINKEDID_END)) { + struct ast_channel *tmp = NULL; + find_dat.chan = chan; + find_dat.linkedid = linkedid; + if ((tmp = ast_channel_callback(linkedid_match, NULL, &find_dat, 0))) { + tmp = ast_channel_unref(tmp); + } else { + ast_cel_report_event(chan, AST_CEL_LINKEDID_END, NULL, NULL, NULL); + } + } +} + +struct ast_channel *ast_cel_fabricate_channel_from_event(const struct ast_event *event) +{ + struct varshead *headp; + struct ast_var_t *newvariable; + char timebuf[30]; + struct ast_channel *tchan; + struct ast_cel_event_record record = { + .version = AST_CEL_EVENT_RECORD_VERSION, + }; + + /* do not call ast_channel_alloc because this is not really a real channel */ + if (!(tchan = ast_dummy_channel_alloc())) { + return NULL; + } + + headp = &tchan->varshead; + + /* first, get the variables from the event */ + if (ast_cel_fill_record(event, &record)) { + ast_channel_release(tchan); + return NULL; + } + + /* next, fill the channel with their data */ + if ((newvariable = ast_var_assign("eventtype", record.event_name))) { + AST_LIST_INSERT_HEAD(headp, newvariable, entries); + } + + if (ast_strlen_zero(cel_dateformat)) { + snprintf(timebuf, sizeof(timebuf), "%ld.%06ld", record.event_time.tv_sec, record.event_time.tv_usec); + } else { + struct ast_tm tm; + ast_localtime(&record.event_time, &tm, NULL); + ast_strftime(timebuf, sizeof(timebuf), cel_dateformat, &tm); + } + + if ((newvariable = ast_var_assign("eventtime", timebuf))) { + AST_LIST_INSERT_HEAD(headp, newvariable, entries); + } + + if ((newvariable = ast_var_assign("eventextra", record.extra))) { + AST_LIST_INSERT_HEAD(headp, newvariable, entries); + } + + tchan->cid.cid_name = ast_strdup(record.caller_id_name); + tchan->cid.cid_num = ast_strdup(record.caller_id_num); + tchan->cid.cid_ani = ast_strdup(record.caller_id_ani); + tchan->cid.cid_rdnis = ast_strdup(record.caller_id_rdnis); + tchan->cid.cid_dnid = ast_strdup(record.caller_id_dnid); + + ast_copy_string(tchan->exten, record.extension, sizeof(tchan->exten)); + ast_copy_string(tchan->context, record.context, sizeof(tchan->context)); + ast_string_field_set(tchan, name, record.channel_name); + ast_string_field_set(tchan, uniqueid, record.unique_id); + ast_string_field_set(tchan, linkedid, record.linked_id); + ast_string_field_set(tchan, accountcode, record.account_code); + ast_string_field_set(tchan, peeraccount, record.peer_account); + ast_string_field_set(tchan, userfield, record.user_field); + + pbx_builtin_setvar_helper(tchan, "BRIDGEPEER", record.peer); + + tchan->appl = ast_strdup(record.application_name); + tchan->data = ast_strdup(record.application_data); + tchan->amaflags = record.amaflag; + + return tchan; +} + +int ast_cel_report_event(struct ast_channel *chan, enum ast_cel_event_type event_type, + const char *userdefevname, const char *extra, struct ast_channel *peer2) +{ + struct timeval eventtime; + struct ast_event *ev; + const char *peername = ""; + struct ast_channel *peer; + + ast_channel_lock(chan); + peer = ast_bridged_channel(chan); + if (peer) { + ast_channel_ref(peer); + } + ast_channel_unlock(chan); + + /* Make sure a reload is not occurring while we're checking to see if this + * is an event that we care about. We could lose an important event in this + * process otherwise. */ + ast_mutex_lock(&reload_lock); + + if (!cel_enabled || !ast_cel_track_event(event_type)) { + ast_mutex_unlock(&reload_lock); + return 0; + } + + if (event_type == AST_CEL_APP_START || event_type == AST_CEL_APP_END) { + char *app; + if (!(app = ao2_find(appset, (char *) chan->appl, OBJ_POINTER))) { + ast_mutex_unlock(&reload_lock); + return 0; + } + ao2_ref(app, -1); + } + + ast_mutex_unlock(&reload_lock); + + if (peer) { + ast_channel_lock(peer); + peername = ast_strdupa(peer->name); + ast_channel_unlock(peer); + } else if (peer2) { + ast_channel_lock(peer2); + peername = ast_strdupa(peer2->name); + ast_channel_unlock(peer2); + } + + if (!userdefevname) { + userdefevname = ""; + } + + if (!extra) { + extra = ""; + } + + eventtime = ast_tvnow(); + + ast_channel_lock(chan); + + ev = ast_event_new(AST_EVENT_CEL, + AST_EVENT_IE_CEL_EVENT_TYPE, AST_EVENT_IE_PLTYPE_UINT, event_type, + AST_EVENT_IE_CEL_EVENT_TIME, AST_EVENT_IE_PLTYPE_UINT, eventtime.tv_sec, + AST_EVENT_IE_CEL_EVENT_TIME_USEC, AST_EVENT_IE_PLTYPE_UINT, eventtime.tv_usec, + AST_EVENT_IE_CEL_USEREVENT_NAME, AST_EVENT_IE_PLTYPE_STR, userdefevname, + AST_EVENT_IE_CEL_CIDNAME, AST_EVENT_IE_PLTYPE_STR, S_OR(chan->cid.cid_name, ""), + AST_EVENT_IE_CEL_CIDNUM, AST_EVENT_IE_PLTYPE_STR, S_OR(chan->cid.cid_num, ""), + AST_EVENT_IE_CEL_CIDANI, AST_EVENT_IE_PLTYPE_STR, S_OR(chan->cid.cid_ani, ""), + AST_EVENT_IE_CEL_CIDRDNIS, AST_EVENT_IE_PLTYPE_STR, S_OR(chan->cid.cid_rdnis, ""), + AST_EVENT_IE_CEL_CIDDNID, AST_EVENT_IE_PLTYPE_STR, S_OR(chan->cid.cid_dnid, ""), + AST_EVENT_IE_CEL_EXTEN, AST_EVENT_IE_PLTYPE_STR, chan->exten, + AST_EVENT_IE_CEL_CONTEXT, AST_EVENT_IE_PLTYPE_STR, chan->context, + AST_EVENT_IE_CEL_CHANNAME, AST_EVENT_IE_PLTYPE_STR, chan->name, + AST_EVENT_IE_CEL_APPNAME, AST_EVENT_IE_PLTYPE_STR, S_OR(chan->appl, ""), + AST_EVENT_IE_CEL_APPDATA, AST_EVENT_IE_PLTYPE_STR, S_OR(chan->data, ""), + AST_EVENT_IE_CEL_AMAFLAGS, AST_EVENT_IE_PLTYPE_UINT, chan->amaflags, + AST_EVENT_IE_CEL_ACCTCODE, AST_EVENT_IE_PLTYPE_STR, chan->accountcode, + AST_EVENT_IE_CEL_PEERACCT, AST_EVENT_IE_PLTYPE_STR, chan->peeraccount, + AST_EVENT_IE_CEL_UNIQUEID, AST_EVENT_IE_PLTYPE_STR, chan->uniqueid, + AST_EVENT_IE_CEL_LINKEDID, AST_EVENT_IE_PLTYPE_STR, chan->linkedid, + AST_EVENT_IE_CEL_USERFIELD, AST_EVENT_IE_PLTYPE_STR, chan->userfield, + AST_EVENT_IE_CEL_EXTRA, AST_EVENT_IE_PLTYPE_STR, extra, + AST_EVENT_IE_CEL_PEER, AST_EVENT_IE_PLTYPE_STR, peername, + AST_EVENT_IE_END); + + ast_channel_unlock(chan); + + if (peer) { + peer = ast_channel_unref(peer); + } + + if (ev && ast_event_queue(ev)) { + ast_event_destroy(ev); + return -1; + } + + return 0; +} + +int ast_cel_fill_record(const struct ast_event *e, struct ast_cel_event_record *r) +{ + if (r->version != AST_CEL_EVENT_RECORD_VERSION) { + ast_log(LOG_ERROR, "Module ABI mismatch for ast_cel_event_record. " + "Please ensure all modules were compiled for " + "this version of Asterisk.\n"); + return -1; + } + + r->event_type = ast_event_get_ie_uint(e, AST_EVENT_IE_CEL_EVENT_TYPE); + + r->event_time.tv_sec = ast_event_get_ie_uint(e, AST_EVENT_IE_CEL_EVENT_TIME); + r->event_time.tv_usec = ast_event_get_ie_uint(e, AST_EVENT_IE_CEL_EVENT_TIME_USEC); + + r->user_defined_name = ""; + + if (r->event_type == AST_CEL_USER_DEFINED) { + r->user_defined_name = ast_event_get_ie_str(e, AST_EVENT_IE_CEL_USEREVENT_NAME); + r->event_name = r->user_defined_name; + } else { + r->event_name = ast_cel_get_type_name(r->event_type); + } + + r->caller_id_name = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_CIDNAME), ""); + r->caller_id_num = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_CIDNUM), ""); + r->caller_id_ani = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_CIDANI), ""); + r->caller_id_rdnis = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_CIDRDNIS), ""); + r->caller_id_dnid = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_CIDDNID), ""); + r->extension = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_EXTEN), ""); + r->context = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_CONTEXT), ""); + r->channel_name = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_CHANNAME), ""); + r->application_name = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_APPNAME), ""); + r->application_data = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_APPDATA), ""); + r->account_code = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_ACCTCODE), ""); + r->peer_account = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_ACCTCODE), ""); + r->unique_id = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_UNIQUEID), ""); + r->linked_id = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_LINKEDID), ""); + r->amaflag = ast_event_get_ie_uint(e, AST_EVENT_IE_CEL_AMAFLAGS); + r->user_field = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_USERFIELD), ""); + r->peer = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_PEER), ""); + r->extra = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_EXTRA), ""); + + return 0; +} + +static int app_hash(const void *obj, const int flags) +{ + return ast_str_case_hash((const char *) obj); +} + +static int app_cmp(void *obj, void *arg, int flags) +{ + const char *app1 = obj, *app2 = arg; + + return !strcasecmp(app1, app2) ? CMP_MATCH | CMP_STOP : 0; +} + +static void ast_cel_engine_term(void) +{ + if (appset) { + ao2_ref(appset, -1); + appset = NULL; + } +} + +int ast_cel_engine_init(void) +{ + if (!(appset = ao2_container_alloc(NUM_APP_BUCKETS, app_hash, app_cmp))) { + return -1; + } + + if (do_reload()) { + ao2_ref(appset, -1); + appset = NULL; + return -1; + } + + if (ast_cli_register(&cli_status)) { + ao2_ref(appset, -1); + appset = NULL; + return -1; + } + + ast_register_atexit(ast_cel_engine_term); + + return 0; +} + +int ast_cel_engine_reload(void) +{ + return do_reload(); +} + diff --git a/main/channel.c b/main/channel.c index fdfe9e941..62377f897 100644 --- a/main/channel.c +++ b/main/channel.c @@ -45,6 +45,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/cli.h" #include "asterisk/translate.h" #include "asterisk/manager.h" +#include "asterisk/cel.h" #include "asterisk/chanvars.h" #include "asterisk/linkedlists.h" #include "asterisk/indications.h" @@ -62,6 +63,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/audiohook.h" #include "asterisk/timing.h" #include "asterisk/autochan.h" +#include "asterisk/stringfields.h" #ifdef HAVE_EPOLL #include <sys/epoll.h> @@ -777,13 +779,14 @@ static const struct ast_channel_tech null_tech = { }; static void ast_channel_destructor(void *obj); +static void ast_dummy_channel_destructor(void *obj); /*! \brief Create a new channel structure */ -static struct ast_channel * attribute_malloc __attribute__((format(printf, 12, 0))) +static struct ast_channel * attribute_malloc __attribute__((format(printf, 13, 0))) __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char *cid_name, const char *acctcode, const char *exten, const char *context, - const int amaflag, const char *file, int line, const char *function, - const char *name_fmt, va_list ap1, va_list ap2) + const char *linkedid, const int amaflag, const char *file, int line, + const char *function, const char *name_fmt, va_list ap1, va_list ap2) { struct ast_channel *tmp; int x; @@ -902,7 +905,14 @@ alertpipe_failed: ast_string_field_build(tmp, uniqueid, "%s-%li.%d", ast_config_AST_SYSTEM_NAME, (long) time(NULL), ast_atomic_fetchadd_int(&uniqueint, 1)); } - + + if (!ast_strlen_zero(linkedid)) { + ast_string_field_set(tmp, linkedid, linkedid); + } + else { + ast_string_field_set(tmp, linkedid, tmp->uniqueid); + } + if (!ast_strlen_zero(name_fmt)) { /* Almost every channel is calling this function, and setting the name via the ast_string_field_build() call. * And they all use slightly different formats for their name string. @@ -938,11 +948,13 @@ alertpipe_failed: strcpy(tmp->exten, "s"); tmp->priority = 1; - + tmp->cdr = ast_cdr_alloc(); ast_cdr_init(tmp->cdr, tmp); ast_cdr_start(tmp->cdr); - + + ast_cel_report_event(tmp, AST_CEL_CHANNEL_START, NULL, NULL, NULL); + headp = &tmp->varshead; AST_LIST_HEAD_INIT_NOLOCK(headp); @@ -990,8 +1002,9 @@ alertpipe_failed: struct ast_channel *__ast_channel_alloc(int needqueue, int state, const char *cid_num, const char *cid_name, const char *acctcode, const char *exten, const char *context, - const int amaflag, const char *file, int line, - const char *function, const char *name_fmt, ...) + const char *linkedid, const int amaflag, + const char *file, int line, const char *function, + const char *name_fmt, ...) { va_list ap1, ap2; struct ast_channel *result; @@ -999,13 +1012,45 @@ struct ast_channel *__ast_channel_alloc(int needqueue, int state, const char *ci va_start(ap1, name_fmt); va_start(ap2, name_fmt); result = __ast_channel_alloc_ap(needqueue, state, cid_num, cid_name, acctcode, exten, context, - amaflag, file, line, function, name_fmt, ap1, ap2); + linkedid, amaflag, file, line, function, name_fmt, ap1, ap2); va_end(ap1); va_end(ap2); return result; } +/* only do the minimum amount of work needed here to make a channel + * structure that can be used to expand channel vars */ +struct ast_channel *ast_dummy_channel_alloc(void) +{ + struct ast_channel *tmp; + struct varshead *headp; + +#if defined(REF_DEBUG) + if (!(tmp = __ao2_alloc_debug(sizeof(*tmp), ast_dummy_channel_destructor, "", file, line, function, 1))) { + return NULL; + } +#elif defined(__AST_DEBUG_MALLOC) + if (!(tmp = __ao2_alloc_debug(sizeof(*tmp), ast_dummy_channel_destructor, "", file, line, function, 0))) { + return NULL; + } +#else + if (!(tmp = ao2_alloc(sizeof(*tmp), ast_dummy_channel_destructor))) { + return NULL; + } +#endif + + if ((ast_string_field_init(tmp, 128))) { + ast_channel_unref(tmp); + return NULL; + } + + headp = &tmp->varshead; + AST_LIST_HEAD_INIT_NOLOCK(headp); + + return tmp; +} + static int __ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin, int head, struct ast_frame *after) { struct ast_frame *f; @@ -1693,6 +1738,9 @@ static void ast_channel_destructor(void *obj) headp = &chan->varshead; + ast_cel_report_event(chan, AST_CEL_CHANNEL_END, NULL, NULL, NULL); + ast_cel_check_retire_linkedid(chan); + /* Get rid of each of the data stores on the channel */ while ((datastore = AST_LIST_REMOVE_HEAD(&chan->datastores, entry))) /* Free the data store */ @@ -1782,6 +1830,30 @@ static void ast_channel_destructor(void *obj) ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, name); } +/*! \brief Free a dummy channel structure */ +static void ast_dummy_channel_destructor(void *obj) +{ + struct ast_channel *chan = obj; + struct ast_var_t *vardata; + struct varshead *headp; + + headp = &chan->varshead; + + free_cid(&chan->cid); + + /* loop over the variables list, freeing all data and deleting list items */ + /* no need to lock the list, as the channel is already locked */ + while ((vardata = AST_LIST_REMOVE_HEAD(headp, entries))) + ast_var_delete(vardata); + + if (chan->cdr) { + ast_cdr_discard(chan->cdr); + chan->cdr = NULL; + } + + ast_string_field_free_memory(chan); +} + struct ast_datastore *ast_channel_datastore_alloc(const struct ast_datastore_info *info, const char *uid) { return ast_datastore_alloc(info, uid); @@ -1964,10 +2036,30 @@ static void free_translation(struct ast_channel *clonechan) clonechan->rawreadformat = clonechan->nativeformats; } +void ast_set_hangupsource(struct ast_channel *chan, const char *source, int force) +{ + struct ast_channel *bridge; + + ast_channel_lock(chan); + if (force || ast_strlen_zero(chan->hangupsource)) { + ast_string_field_set(chan, hangupsource, source); + } + bridge = ast_bridged_channel(chan); + ast_channel_unlock(chan); + + if (bridge && (force || ast_strlen_zero(bridge->hangupsource))) { + ast_channel_lock(bridge); + ast_string_field_set(chan, hangupsource, source); + ast_channel_unlock(bridge); + } +} + /*! \brief Hangup a channel */ int ast_hangup(struct ast_channel *chan) { int res = 0; + struct ast_cdr *cdr = NULL; + char extra_str[64]; /* used for cel logging below */ /* Don't actually hang up a channel that will masquerade as someone else, or if someone is going to masquerade as us */ @@ -2012,12 +2104,21 @@ int ast_hangup(struct ast_channel *chan) sched_context_destroy(chan->sched); chan->sched = NULL; } - + if (chan->generatordata) /* Clear any tone stuff remaining */ if (chan->generator && chan->generator->release) chan->generator->release(chan, chan->generatordata); chan->generatordata = NULL; chan->generator = NULL; + + snprintf(extra_str, sizeof(extra_str), "%d,%s,%s", chan->hangupcause, chan->hangupsource, S_OR(pbx_builtin_getvar_helper(chan, "DIALSTATUS"), "")); + ast_cel_report_event(chan, AST_CEL_HANGUP, NULL, extra_str, NULL); + + if (chan->cdr) { /* End the CDR if it hasn't already */ + ast_cdr_end(chan->cdr); + cdr = chan->cdr; + chan->cdr = NULL; + } if (ast_test_flag(chan, AST_FLAG_BLOCKING)) { ast_log(LOG_WARNING, "Hard hangup called by thread %ld on %s, while fd " "is blocked by thread %ld in procedure %s! Expect a failure\n", @@ -2093,9 +2194,11 @@ int ast_raw_answer(struct ast_channel *chan, int cdr_answer) if (cdr_answer) { ast_cdr_answer(chan->cdr); } + ast_cel_report_event(chan, AST_CEL_ANSWER, NULL, NULL, NULL); ast_channel_unlock(chan); break; case AST_STATE_UP: + ast_cel_report_event(chan, AST_CEL_ANSWER, NULL, NULL, NULL); /* Calling ast_cdr_answer when it it has previously been called * is essentially a no-op, so it is safe. */ @@ -3081,6 +3184,7 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio) /* Answer the CDR */ ast_setstate(chan, AST_STATE_UP); /* removed a call to ast_cdr_answer(chan->cdr) from here. */ + ast_cel_report_event(chan, AST_CEL_ANSWER, NULL, NULL, NULL); } } break; @@ -4049,7 +4153,7 @@ struct ast_channel *ast_call_forward(struct ast_channel *caller, struct ast_chan data = tmpchan; type = "Local"; } - if (!(new = ast_request(type, format, data, &cause))) { + if (!(new = ast_request(type, format, orig, data, &cause))) { ast_log(LOG_NOTICE, "Unable to create channel for call forward to '%s/%s' (cause = %d)\n", type, data, cause); handle_cause(cause, outstate); ast_hangup(orig); @@ -4103,7 +4207,7 @@ struct ast_channel *ast_call_forward(struct ast_channel *caller, struct ast_chan return new; } -struct ast_channel *__ast_request_and_dial(const char *type, int format, void *data, int timeout, int *outstate, const char *cid_num, const char *cid_name, struct outgoing_helper *oh) +struct ast_channel *__ast_request_and_dial(const char *type, int format, const struct ast_channel *requestor, void *data, int timeout, int *outstate, const char *cid_num, const char *cid_name, struct outgoing_helper *oh) { int dummy_outstate; int cause = 0; @@ -4117,7 +4221,7 @@ struct ast_channel *__ast_request_and_dial(const char *type, int format, void *d else outstate = &dummy_outstate; /* make outstate always a valid pointer */ - chan = ast_request(type, format, data, &cause); + chan = ast_request(type, format, requestor, data, &cause); if (!chan) { ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data); handle_cause(cause, outstate); @@ -4238,12 +4342,12 @@ struct ast_channel *__ast_request_and_dial(const char *type, int format, void *d return chan; } -struct ast_channel *ast_request_and_dial(const char *type, int format, void *data, int timeout, int *outstate, const char *cidnum, const char *cidname) +struct ast_channel *ast_request_and_dial(const char *type, int format, const struct ast_channel *requestor, void *data, int timeout, int *outstate, const char *cidnum, const char *cidname) { - return __ast_request_and_dial(type, format, data, timeout, outstate, cidnum, cidname, NULL); + return __ast_request_and_dial(type, format, requestor, data, timeout, outstate, cidnum, cidname, NULL); } -struct ast_channel *ast_request(const char *type, int format, void *data, int *cause) +struct ast_channel *ast_request(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause) { struct chanlist *chan; struct ast_channel *c; @@ -4284,10 +4388,10 @@ struct ast_channel *ast_request(const char *type, int format, void *data, int *c AST_RWLIST_UNLOCK(&backends); if (!chan->tech->requester) return NULL; - - if (!(c = chan->tech->requester(type, capabilities | videoformat | textformat, data, cause))) + + if (!(c = chan->tech->requester(type, capabilities | videoformat | textformat, requestor, data, cause))) return NULL; - + /* no need to generate a Newchannel event here; it is done in the channel_alloc call */ return c; } @@ -4666,6 +4770,162 @@ static void clone_variables(struct ast_channel *original, struct ast_channel *cl } } + + +/* return the oldest of two linkedids. linkedid is derived from + uniqueid which is formed like this: [systemname-]ctime.seq + + The systemname, and the dash are optional, followed by the epoch + time followed by an integer sequence. Note that this is not a + decimal number, since 1.2 is less than 1.11 in uniqueid land. + + To compare two uniqueids, we parse out the integer values of the + time and the sequence numbers and compare them, with time trumping + sequence. +*/ +static const char *oldest_linkedid(const char *a, const char *b) +{ + const char *satime, *saseq; + const char *sbtime, *sbseq; + const char *dash; + + unsigned int atime, aseq, btime, bseq; + + if (ast_strlen_zero(a)) + return b; + + if (ast_strlen_zero(b)) + return a; + + satime = a; + sbtime = b; + + /* jump over the system name */ + if ((dash = strrchr(satime, '-'))) { + satime = dash+1; + } + if ((dash = strrchr(sbtime, '-'))) { + sbtime = dash+1; + } + + /* the sequence comes after the '.' */ + saseq = strchr(satime, '.'); + sbseq = strchr(sbtime, '.'); + if (!saseq || !sbseq) + return NULL; + saseq++; + sbseq++; + + /* convert it all to integers */ + atime = atoi(satime); /* note that atoi is ignoring the '.' after the time string */ + btime = atoi(sbtime); /* note that atoi is ignoring the '.' after the time string */ + aseq = atoi(saseq); + bseq = atoi(sbseq); + + /* and finally compare */ + if (atime == btime) { + return (aseq < bseq) ? a : b; + } + else { + return (atime < btime) ? a : b; + } +} + +/*! Set the channel's linkedid to the given string, and also check to + * see if the channel's old linkedid is now being retired */ +static void ast_channel_change_linkedid(struct ast_channel *chan, const char *linkedid) +{ + /* if the linkedid for this channel is being changed from something, check... */ + if (!ast_strlen_zero(chan->linkedid) && 0 != strcmp(chan->linkedid, linkedid)) { + ast_cel_check_retire_linkedid(chan); + } + + ast_string_field_set(chan, linkedid, linkedid); +} + + +/*! + \brief Propagate the oldest linkedid between associated channels + +*/ +void ast_channel_set_linkgroup(struct ast_channel *chan, struct ast_channel *peer) +{ + const char* linkedid=NULL; + struct ast_channel *bridged; + + linkedid = oldest_linkedid(chan->linkedid, peer->linkedid); + linkedid = oldest_linkedid(linkedid, chan->uniqueid); + linkedid = oldest_linkedid(linkedid, peer->uniqueid); + if (chan->_bridge) { + bridged = ast_bridged_channel(chan); + if (bridged != peer) { + linkedid = oldest_linkedid(linkedid, bridged->linkedid); + linkedid = oldest_linkedid(linkedid, bridged->uniqueid); + } + } + if (peer->_bridge) { + bridged = ast_bridged_channel(peer); + if (bridged != chan) { + linkedid = oldest_linkedid(linkedid, bridged->linkedid); + linkedid = oldest_linkedid(linkedid, bridged->uniqueid); + } + } + + /* just in case setting a stringfield to itself causes problems */ + linkedid = ast_strdupa(linkedid); + + ast_channel_change_linkedid(chan, linkedid); + ast_channel_change_linkedid(peer, linkedid); + if (chan->_bridge) { + bridged = ast_bridged_channel(chan); + if (bridged != peer) { + ast_channel_change_linkedid(bridged, linkedid); + } + } + if (peer->_bridge) { + bridged = ast_bridged_channel(peer); + if (bridged != chan) { + ast_channel_change_linkedid(bridged, linkedid); + } + } +} + +/* copy accountcode and peeraccount across during a link */ +static void ast_set_owners_and_peers(struct ast_channel *chan1, + struct ast_channel *chan2) +{ + if (!ast_strlen_zero(chan1->accountcode) && ast_strlen_zero(chan2->peeraccount)) { + ast_log(LOG_DEBUG, "setting peeraccount to %s for %s from data on channel %s\n", + chan1->accountcode, chan2->name, chan1->name); + ast_string_field_set(chan2, peeraccount, chan1->accountcode); + } + if (!ast_strlen_zero(chan2->accountcode) && ast_strlen_zero(chan1->peeraccount)) { + ast_log(LOG_DEBUG, "setting peeraccount to %s for %s from data on channel %s\n", + chan2->accountcode, chan1->name, chan2->name); + ast_string_field_set(chan1, peeraccount, chan2->accountcode); + } + if (!ast_strlen_zero(chan1->peeraccount) && ast_strlen_zero(chan2->accountcode)) { + ast_log(LOG_DEBUG, "setting accountcode to %s for %s from data on channel %s\n", + chan1->peeraccount, chan2->name, chan1->name); + ast_string_field_set(chan2, accountcode, chan1->peeraccount); + } + if (!ast_strlen_zero(chan2->peeraccount) && ast_strlen_zero(chan1->accountcode)) { + ast_log(LOG_DEBUG, "setting accountcode to %s for %s from data on channel %s\n", + chan2->peeraccount, chan1->name, chan2->name); + ast_string_field_set(chan1, accountcode, chan2->peeraccount); + } + if (0 != strcmp(chan1->accountcode, chan2->peeraccount)) { + ast_log(LOG_DEBUG, "changing peeraccount from %s to %s on %s to match channel %s\n", + chan2->peeraccount, chan1->peeraccount, chan2->name, chan1->name); + ast_string_field_set(chan2, peeraccount, chan1->accountcode); + } + if (0 != strcmp(chan2->accountcode, chan1->peeraccount)) { + ast_log(LOG_DEBUG, "changing peeraccount from %s to %s on %s to match channel %s\n", + chan1->peeraccount, chan2->peeraccount, chan1->name, chan2->name); + ast_string_field_set(chan1, peeraccount, chan2->accountcode); + } +} + /*! * \pre chan is locked */ @@ -4749,7 +5009,14 @@ int ast_do_masquerade(struct ast_channel *original) /* Mangle the name of the clone channel */ ast_change_name(clonechan, masqn); - /* Swap the technologies */ + /* share linked id's */ + ast_channel_set_linkgroup(original, clonechan); + + /* Notify any managers of the change, first the masq then the other */ + manager_event(EVENT_FLAG_CALL, "Rename", "Channel: %s\r\nNewname: %s\r\nUniqueid: %s\r\n", newn, masqn, clonechan->uniqueid); + manager_event(EVENT_FLAG_CALL, "Rename", "Channel: %s\r\nNewname: %s\r\nUniqueid: %s\r\n", orig, newn, original->uniqueid); + + /* Swap the technologies */ t = original->tech; original->tech = clonechan->tech; clonechan->tech = t; @@ -4876,7 +5143,7 @@ int ast_do_masquerade(struct ast_channel *original) /* XXX What about blocking, softhangup, blocker, and lock and blockproc? XXX */ /* Application and data remain the same */ /* Clone exception becomes real one, as with fdno */ - ast_set_flag(original, ast_test_flag(clonechan, AST_FLAG_OUTGOING | AST_FLAG_EXCEPTION)); + ast_copy_flags(original, clonechan, AST_FLAG_EXCEPTION | AST_FLAG_OUTGOING); original->fdno = clonechan->fdno; /* Schedule context remains the same */ /* Stream stuff stays the same */ @@ -4916,6 +5183,14 @@ int ast_do_masquerade(struct ast_channel *original) /* Copy the music class */ ast_string_field_set(original, musicclass, clonechan->musicclass); + /* copy over accuntcode and set peeraccount across the bridge */ + ast_string_field_set(original, accountcode, S_OR(clonechan->accountcode, "")); + if (original->_bridge) { + /* XXX - should we try to lock original->_bridge here? */ + ast_string_field_set(original->_bridge, peeraccount, S_OR(clonechan->accountcode, "")); + ast_cel_report_event(original, AST_CEL_BRIDGE_UPDATE, NULL, NULL, NULL); + } + ast_debug(1, "Putting channel %s in %d/%d formats\n", original->name, wformat, rformat); /* Okay. Last thing is to let the channel driver know about all this mess, so he @@ -5417,6 +5692,7 @@ enum ast_bridge_result ast_channel_bridge(struct ast_channel *c0, struct ast_cha c0->_bridge = c1; c1->_bridge = c0; + ast_set_owners_and_peers(c0, c1); o0nativeformats = c0->nativeformats; o1nativeformats = c1->nativeformats; @@ -5555,6 +5831,8 @@ enum ast_bridge_result ast_channel_bridge(struct ast_channel *c0, struct ast_cha "CallerID1: %s\r\n" "CallerID2: %s\r\n", c0->name, c1->name, c0->uniqueid, c1->uniqueid, S_OR(c0->cid.cid_num, "<unknown>"), S_OR(c1->cid.cid_num, "<unknown>")); + ast_cel_report_event(c0, AST_CEL_BRIDGE_END, NULL, NULL, NULL); + ast_debug(1, "Returning from native bridge, channels: %s, %s\n", c0->name, c1->name); ast_clear_flag(c0, AST_FLAG_NBRIDGE); @@ -5565,7 +5843,6 @@ enum ast_bridge_result ast_channel_bridge(struct ast_channel *c0, struct ast_cha c0->_bridge = NULL; c1->_bridge = NULL; - return res; } else { ast_clear_flag(c0, AST_FLAG_NBRIDGE); @@ -5592,6 +5869,7 @@ enum ast_bridge_result ast_channel_bridge(struct ast_channel *c0, struct ast_cha if (ast_channel_make_compatible(c0, c1)) { ast_log(LOG_WARNING, "Can't make %s and %s compatible\n", c0->name, c1->name); manager_bridge_event(0, 1, c0, c1); + /* ast_cel_report_event(c0, AST_CEL_BRIDGE_END, NULL, NULL, NULL); */ return AST_BRIDGE_FAILED; } o0nativeformats = c0->nativeformats; @@ -5619,6 +5897,7 @@ enum ast_bridge_result ast_channel_bridge(struct ast_channel *c0, struct ast_cha c0->_bridge = NULL; c1->_bridge = NULL; + ast_cel_report_event(c0, AST_CEL_BRIDGE_END, NULL, NULL, NULL); manager_event(EVENT_FLAG_CALL, "Unlink", "Channel1: %s\r\n" "Channel2: %s\r\n" @@ -6747,15 +7026,17 @@ int ast_channel_connected_line_macro(struct ast_channel *autoservice_chan, struc * newly compiled modules will call __ast_channel_alloc() via the macros in channel.h */ #undef ast_channel_alloc -struct ast_channel __attribute__((format(printf, 9, 10))) +struct ast_channel __attribute__((format(printf, 10, 11))) *ast_channel_alloc(int needqueue, int state, const char *cid_num, const char *cid_name, const char *acctcode, const char *exten, const char *context, - const int amaflag, const char *name_fmt, ...); + const char *linkedid, const int amaflag, + const char *name_fmt, ...); struct ast_channel *ast_channel_alloc(int needqueue, int state, const char *cid_num, const char *cid_name, const char *acctcode, const char *exten, const char *context, - const int amaflag, const char *name_fmt, ...) + const char *linkedid, const int amaflag, + const char *name_fmt, ...) { va_list ap1, ap2; struct ast_channel *result; @@ -6764,7 +7045,7 @@ struct ast_channel *ast_channel_alloc(int needqueue, int state, const char *cid_ va_start(ap1, name_fmt); va_start(ap2, name_fmt); result = __ast_channel_alloc_ap(needqueue, state, cid_num, cid_name, acctcode, exten, context, - amaflag, __FILE__, __LINE__, __FUNCTION__, name_fmt, ap1, ap2); + linkedid, amaflag, __FILE__, __LINE__, __FUNCTION__, name_fmt, ap1, ap2); va_end(ap1); va_end(ap2); diff --git a/main/cli.c b/main/cli.c index a822d1eb8..bdf8c1129 100644 --- a/main/cli.c +++ b/main/cli.c @@ -781,9 +781,9 @@ static char *handle_chanlist(struct ast_cli_entry *e, int cmd, struct ast_cli_ar { #define FORMAT_STRING "%-20.20s %-20.20s %-7.7s %-30.30s\n" #define FORMAT_STRING2 "%-20.20s %-20.20s %-7.7s %-30.30s\n" -#define CONCISE_FORMAT_STRING "%s!%s!%s!%d!%s!%s!%s!%s!%s!%d!%s!%s!%s\n" -#define VERBOSE_FORMAT_STRING "%-20.20s %-20.20s %-16.16s %4d %-7.7s %-12.12s %-25.25s %-15.15s %8.8s %-11.11s %-20.20s\n" -#define VERBOSE_FORMAT_STRING2 "%-20.20s %-20.20s %-16.16s %-4.4s %-7.7s %-12.12s %-25.25s %-15.15s %8.8s %-11.11s %-20.20s\n" +#define CONCISE_FORMAT_STRING "%s!%s!%s!%d!%s!%s!%s!%s!%s!%s!%d!%s!%s!%s\n" +#define VERBOSE_FORMAT_STRING "%-20.20s %-20.20s %-16.16s %4d %-7.7s %-12.12s %-25.25s %-15.15s %8.8s %-11.11s %-11.11s %-20.20s\n" +#define VERBOSE_FORMAT_STRING2 "%-20.20s %-20.20s %-16.16s %-4.4s %-7.7s %-12.12s %-25.25s %-15.15s %8.8s %-11.11s %-11.11s %-20.20s\n" struct ast_channel *c = NULL; int numchans = 0, concise = 0, verbose = 0, count = 0; @@ -824,7 +824,7 @@ static char *handle_chanlist(struct ast_cli_entry *e, int cmd, struct ast_cli_ar ast_cli(a->fd, FORMAT_STRING2, "Channel", "Location", "State", "Application(Data)"); else if (verbose) ast_cli(a->fd, VERBOSE_FORMAT_STRING2, "Channel", "Context", "Extension", "Priority", "State", "Application", "Data", - "CallerID", "Duration", "Accountcode", "BridgedTo"); + "CallerID", "Duration", "Accountcode", "PeerAccount", "BridgedTo"); } if (!count && !(iter = ast_channel_iterator_all_new(0))) { @@ -857,6 +857,7 @@ static char *handle_chanlist(struct ast_cli_entry *e, int cmd, struct ast_cli_ar S_OR(c->data, ""), /* XXX different from verbose ? */ S_OR(c->cid.cid_num, ""), S_OR(c->accountcode, ""), + S_OR(c->peeraccount, ""), c->amaflags, durbuf, bc ? bc->name : "(None)", @@ -868,6 +869,7 @@ static char *handle_chanlist(struct ast_cli_entry *e, int cmd, struct ast_cli_ar S_OR(c->cid.cid_num, ""), durbuf, S_OR(c->accountcode, ""), + S_OR(c->peeraccount, ""), bc ? bc->name : "(None)"); } else { char locbuf[40] = "(None)"; @@ -1355,6 +1357,7 @@ static char *handle_showchan(struct ast_cli_entry *e, int cmd, struct ast_cli_ar " Name: %s\n" " Type: %s\n" " UniqueID: %s\n" + " LinkedID: %s\n" " Caller ID: %s\n" " Caller ID Name: %s\n" " DNID Digits: %s\n" @@ -1382,7 +1385,7 @@ static char *handle_showchan(struct ast_cli_entry *e, int cmd, struct ast_cli_ar " Application: %s\n" " Data: %s\n" " Blocking in: %s\n", - c->name, c->tech->type, c->uniqueid, + c->name, c->tech->type, c->uniqueid, c->linkedid, S_OR(c->cid.cid_num, "(N/A)"), S_OR(c->cid.cid_name, "(N/A)"), S_OR(c->cid.cid_dnid, "(N/A)"), diff --git a/main/devicestate.c b/main/devicestate.c index 17178faf2..564aecfc9 100644 --- a/main/devicestate.c +++ b/main/devicestate.c @@ -818,7 +818,7 @@ int ast_enable_distributed_devstate(void) } devstate_collector.event_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE_CHANGE, - devstate_change_collector_cb, NULL, AST_EVENT_IE_END); + devstate_change_collector_cb, "devicestate_engine_enable_distributed", NULL, AST_EVENT_IE_END); if (!devstate_collector.event_sub) { ast_log(LOG_ERROR, "Failed to create subscription for the device state change collector\n"); diff --git a/main/dial.c b/main/dial.c index 1f65f50c7..4c57cb12d 100644 --- a/main/dial.c +++ b/main/dial.c @@ -262,7 +262,7 @@ static int begin_dial_channel(struct ast_dial_channel *channel, struct ast_chann ast_copy_string(numsubst, channel->device, sizeof(numsubst)); /* If we fail to create our owner channel bail out */ - if (!(channel->owner = ast_request(channel->tech, chan ? chan->nativeformats : AST_FORMAT_AUDIO_MASK, numsubst, &channel->cause))) + if (!(channel->owner = ast_request(channel->tech, chan ? chan->nativeformats : AST_FORMAT_AUDIO_MASK, chan, numsubst, &channel->cause))) return -1; channel->owner->appl = "AppDial2"; diff --git a/main/event.c b/main/event.c index 3370cf8b3..6a70a6741 100644 --- a/main/event.c +++ b/main/event.c @@ -119,6 +119,7 @@ struct ast_event_ie_val { struct ast_event_sub { enum ast_event_type type; ast_event_cb_t cb; + char description[64]; void *userdata; uint32_t uniqueid; AST_LIST_HEAD_NOLOCK(, ast_event_ie_val) ie_vals; @@ -195,6 +196,7 @@ static struct event_name { { AST_EVENT_UNSUB, "Unsubscription" }, { AST_EVENT_DEVICE_STATE, "DeviceState" }, { AST_EVENT_DEVICE_STATE_CHANGE, "DeviceStateChange" }, + { AST_EVENT_CEL, "CEL" }, }; /*! @@ -206,16 +208,38 @@ static struct ie_map { const char *name; } ie_maps[] = { { 0, 0, "" }, - { AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, "NewMessages" }, - { AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, "OldMessages" }, - { AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, "Mailbox" }, - { AST_EVENT_IE_UNIQUEID, AST_EVENT_IE_PLTYPE_UINT, "UniqueID" }, - { AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, "EventType" }, - { AST_EVENT_IE_EXISTS, AST_EVENT_IE_PLTYPE_UINT, "Exists" }, - { AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, "Device" }, - { AST_EVENT_IE_STATE, AST_EVENT_IE_PLTYPE_UINT, "State" }, - { AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, "Context" }, - { AST_EVENT_IE_EID, AST_EVENT_IE_PLTYPE_RAW, "EntityID" }, + { AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, "NewMessages" }, + { AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, "OldMessages" }, + { AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, "Mailbox" }, + { AST_EVENT_IE_UNIQUEID, AST_EVENT_IE_PLTYPE_UINT, "UniqueID" }, + { AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, "EventType" }, + { AST_EVENT_IE_EXISTS, AST_EVENT_IE_PLTYPE_UINT, "Exists" }, + { AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, "Device" }, + { AST_EVENT_IE_STATE, AST_EVENT_IE_PLTYPE_UINT, "State" }, + { AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, "Context" }, + { AST_EVENT_IE_EID, AST_EVENT_IE_PLTYPE_RAW, "EntityID" }, + { AST_EVENT_IE_CEL_EVENT_TYPE, AST_EVENT_IE_PLTYPE_UINT, "CELEventType" }, + { AST_EVENT_IE_CEL_EVENT_TIME, AST_EVENT_IE_PLTYPE_UINT, "CELEventTime" }, + { AST_EVENT_IE_CEL_EVENT_TIME_USEC, AST_EVENT_IE_PLTYPE_UINT, "CELEventTimeUSec" }, + { AST_EVENT_IE_CEL_USEREVENT_NAME, AST_EVENT_IE_PLTYPE_UINT, "CELUserEventName" }, + { AST_EVENT_IE_CEL_CIDNAME, AST_EVENT_IE_PLTYPE_STR, "CELCIDName" }, + { AST_EVENT_IE_CEL_CIDNUM, AST_EVENT_IE_PLTYPE_STR, "CELCIDNum" }, + { AST_EVENT_IE_CEL_EXTEN, AST_EVENT_IE_PLTYPE_STR, "CELExten" }, + { AST_EVENT_IE_CEL_CONTEXT, AST_EVENT_IE_PLTYPE_STR, "CELContext" }, + { AST_EVENT_IE_CEL_CHANNAME, AST_EVENT_IE_PLTYPE_STR, "CELChanName" }, + { AST_EVENT_IE_CEL_APPNAME, AST_EVENT_IE_PLTYPE_STR, "CELAppName" }, + { AST_EVENT_IE_CEL_APPDATA, AST_EVENT_IE_PLTYPE_STR, "CELAppData" }, + { AST_EVENT_IE_CEL_AMAFLAGS, AST_EVENT_IE_PLTYPE_STR, "CELAMAFlags" }, + { AST_EVENT_IE_CEL_ACCTCODE, AST_EVENT_IE_PLTYPE_UINT, "CELAcctCode" }, + { AST_EVENT_IE_CEL_UNIQUEID, AST_EVENT_IE_PLTYPE_STR, "CELUniqueID" }, + { AST_EVENT_IE_CEL_USERFIELD, AST_EVENT_IE_PLTYPE_STR, "CELUserField" }, + { AST_EVENT_IE_CEL_CIDANI, AST_EVENT_IE_PLTYPE_STR, "CELCIDani" }, + { AST_EVENT_IE_CEL_CIDRDNIS, AST_EVENT_IE_PLTYPE_STR, "CELCIDrdnis" }, + { AST_EVENT_IE_CEL_CIDDNID, AST_EVENT_IE_PLTYPE_STR, "CELCIDdnid" }, + { AST_EVENT_IE_CEL_PEER, AST_EVENT_IE_PLTYPE_STR, "CELPeer" }, + { AST_EVENT_IE_CEL_LINKEDID, AST_EVENT_IE_PLTYPE_STR, "CELLinkedID" }, + { AST_EVENT_IE_CEL_PEERACCT, AST_EVENT_IE_PLTYPE_STR, "CELPeerAcct" }, + { AST_EVENT_IE_CEL_EXTRA, AST_EVENT_IE_PLTYPE_STR, "CELExtra" }, }; const char *ast_event_get_type_name(const struct ast_event *event) @@ -535,8 +559,9 @@ static struct ast_event *gen_sub_event(struct ast_event_sub *sub) struct ast_event *event; event = ast_event_new(AST_EVENT_SUB, - AST_EVENT_IE_UNIQUEID, AST_EVENT_IE_PLTYPE_UINT, sub->uniqueid, - AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, sub->type, + AST_EVENT_IE_UNIQUEID, AST_EVENT_IE_PLTYPE_UINT, sub->uniqueid, + AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, sub->type, + AST_EVENT_IE_DESCRIPTION, AST_EVENT_IE_PLTYPE_STR, sub->description, AST_EVENT_IE_END); if (!event) @@ -773,7 +798,7 @@ int ast_event_sub_activate(struct ast_event_sub *sub) } struct ast_event_sub *ast_event_subscribe(enum ast_event_type type, ast_event_cb_t cb, - void *userdata, ...) + char *description, void *userdata, ...) { va_list ap; enum ast_event_ie_type ie_type; @@ -783,6 +808,8 @@ struct ast_event_sub *ast_event_subscribe(enum ast_event_type type, ast_event_cb return NULL; } + ast_copy_string(sub->description, description, sizeof(sub->description)); + va_start(ap, userdata); for (ie_type = va_arg(ap, enum ast_event_type); ie_type != AST_EVENT_IE_END; @@ -843,6 +870,11 @@ void ast_event_sub_destroy(struct ast_event_sub *sub) ast_free(sub); } +const char *ast_event_subscriber_get_description(struct ast_event_sub *sub) +{ + return sub ? sub->description : NULL; +} + struct ast_event_sub *ast_event_unsubscribe(struct ast_event_sub *sub) { struct ast_event *event; @@ -856,8 +888,9 @@ struct ast_event_sub *ast_event_unsubscribe(struct ast_event_sub *sub) AST_EVENT_IE_END) != AST_EVENT_SUB_NONE) { event = ast_event_new(AST_EVENT_UNSUB, - AST_EVENT_IE_UNIQUEID, AST_EVENT_IE_PLTYPE_UINT, sub->uniqueid, - AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, sub->type, + AST_EVENT_IE_UNIQUEID, AST_EVENT_IE_PLTYPE_UINT, sub->uniqueid, + AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, sub->type, + AST_EVENT_IE_DESCRIPTION, AST_EVENT_IE_PLTYPE_STR, sub->description, AST_EVENT_IE_END); if (event) { @@ -1330,6 +1363,7 @@ int ast_event_queue(struct ast_event *event) if (ast_event_check_subscriber(host_event_type, AST_EVENT_IE_END) == AST_EVENT_SUB_NONE) { ast_event_destroy(event); + ast_log(LOG_NOTICE, "Event destroyed, no subscriber\n"); return 0; } diff --git a/main/features.c b/main/features.c index 22e3a2b65..bb716fa80 100644 --- a/main/features.c +++ b/main/features.c @@ -55,6 +55,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/audiohook.h" #include "asterisk/global_datastores.h" #include "asterisk/astobj2.h" +#include "asterisk/cel.h" /*** DOCUMENTATION <application name="Bridge" language="en_US"> @@ -426,7 +427,7 @@ static void check_goto_on_transfer(struct ast_channel *chan) goto_on_transfer = ast_strdupa(val); - if (!(xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "%s", chan->name))) + if (!(xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", chan->linkedid, 0, "%s", chan->name))) return; for (x = goto_on_transfer; x && *x; x++) { @@ -808,6 +809,8 @@ static int park_call_full(struct ast_channel *chan, struct ast_channel *peer, st pthread_kill(parking_thread, SIGURG); ast_verb(2, "Parked %s on %d (lot %s). Will timeout back to extension [%s] %s, %d in %d seconds\n", pu->chan->name, pu->parkingnum, pu->parkinglot->name, pu->context, pu->exten, pu->priority, (pu->parkingtime/1000)); + ast_cel_report_event(pu->chan, AST_CEL_PARK_START, NULL, pu->parkinglot->name, peer); + if (peer) { event_from = peer->name; } else { @@ -895,7 +898,7 @@ static int masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, i } /* Make a new, fake channel that we'll use to masquerade in the real one */ - if (!(chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, rchan->accountcode, rchan->exten, rchan->context, rchan->amaflags, "Parked/%s",rchan->name))) { + if (!(chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, rchan->accountcode, rchan->exten, rchan->context, rchan->linkedid, rchan->amaflags, "Parked/%s",rchan->name))) { ast_log(LOG_WARNING, "Unable to create parked channel\n"); return -1; } @@ -1345,6 +1348,7 @@ static int builtin_blindtransfer(struct ast_channel *chan, struct ast_channel *p } /*! \todo XXX Maybe we should have another message here instead of invalid extension XXX */ } else if (ast_exists_extension(transferee, transferer_real_context, xferto, 1, transferer->cid.cid_num)) { + ast_cel_report_event(transferer, AST_CEL_BLINDTRANSFER, NULL, xferto, transferee); pbx_builtin_setvar_helper(transferer, "BLINDTRANSFER", transferee->name); pbx_builtin_setvar_helper(transferee, "BLINDTRANSFER", transferer->name); res=finishup(transferee); @@ -1561,6 +1565,9 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st ast_party_connected_line_free(&connected_line); return AST_FEATURE_RETURN_SUCCESS; } + + ast_cel_report_event(transferee, AST_CEL_ATTENDEDTRANSFER, NULL, NULL, newchan); + if (check_compat(transferee, newchan)) { finishup(transferee); ast_party_connected_line_free(&connected_line); @@ -1577,7 +1584,7 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st ast_party_connected_line_free(&connected_line); return -1; } - xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Transfered/%s", transferee->name); + xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", transferee->linkedid, 0, "Transfered/%s", transferee->name); if (!xferchan) { ast_hangup(newchan); ast_party_connected_line_free(&connected_line); @@ -1731,6 +1738,8 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st if (!newchan) return -1; + ast_cel_report_event(transferee, AST_CEL_ATTENDEDTRANSFER, NULL, NULL, newchan); + /* newchan is up, we should prepare transferee and bridge them */ if (check_compat(transferee, newchan)) { finishup(transferee); @@ -1746,7 +1755,7 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st return -1; } - xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Transfered/%s", transferee->name); + xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", transferee->linkedid, 0, "Transfered/%s", transferee->name); if (!xferchan) { ast_hangup(newchan); return -1; @@ -2315,7 +2324,7 @@ static struct ast_channel *feature_request_and_dial(struct ast_channel *caller, int x, len = 0; char *disconnect_code = NULL, *dialed_code = NULL; - if (!(chan = ast_request(type, format, data, &cause))) { + if (!(chan = ast_request(type, format, caller, data, &cause))) { ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data); switch(cause) { case AST_CAUSE_BUSY: @@ -2482,6 +2491,27 @@ done: return chan; } +void ast_channel_log(char *title, struct ast_channel *chan); + +void ast_channel_log(char *title, struct ast_channel *chan) /* for debug, this is handy enough to justify keeping it in the source */ +{ + ast_log(LOG_NOTICE, "______ %s (%lx)______\n", title, (unsigned long)chan); + ast_log(LOG_NOTICE, "CHAN: name: %s; appl: %s; data: %s; contxt: %s; exten: %s; pri: %d;\n", + chan->name, chan->appl, chan->data, chan->context, chan->exten, chan->priority); + ast_log(LOG_NOTICE, "CHAN: acctcode: %s; dialcontext: %s; amaflags: %x; maccontxt: %s; macexten: %s; macpri: %d;\n", + chan->accountcode, chan->dialcontext, chan->amaflags, chan->macrocontext, chan->macroexten, chan->macropriority); + ast_log(LOG_NOTICE, "CHAN: masq: %p; masqr: %p; _bridge: %p; uniqueID: %s; linkedID:%s\n", + chan->masq, chan->masqr, + chan->_bridge, chan->uniqueid, chan->linkedid); + if (chan->masqr) + ast_log(LOG_NOTICE, "CHAN: masquerading as: %s; cdr: %p;\n", + chan->masqr->name, chan->masqr->cdr); + if (chan->_bridge) + ast_log(LOG_NOTICE, "CHAN: Bridged to %s\n", chan->_bridge->name); + + ast_log(LOG_NOTICE, "===== done ====\n"); +} + /*! * \brief return the first unlocked cdr in a possible chain */ @@ -2661,6 +2691,26 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast } } +#ifdef FOR_DEBUG + /* show the two channels and cdrs involved in the bridge for debug & devel purposes */ + ast_channel_log("Pre-bridge CHAN Channel info", chan); + ast_channel_log("Pre-bridge PEER Channel info", peer); +#endif + /* two channels are being marked as linked here */ + ast_channel_set_linkgroup(chan,peer); + + /* copy the userfield from the B-leg to A-leg if applicable */ + if (chan->cdr && peer->cdr && !ast_strlen_zero(peer->cdr->userfield)) { + char tmp[256]; + if (!ast_strlen_zero(chan->cdr->userfield)) { + snprintf(tmp, sizeof(tmp), "%s;%s", chan->cdr->userfield, peer->cdr->userfield); + ast_cdr_appenduserfield(chan, tmp); + } else + ast_cdr_setuserfield(chan, peer->cdr->userfield); + /* free the peer's cdr without ast_cdr_free complaining */ + ast_free(peer->cdr); + peer->cdr = NULL; + } ast_copy_string(orig_channame,chan->name,sizeof(orig_channame)); ast_copy_string(orig_peername,peer->name,sizeof(orig_peername)); orig_peer_cdr = peer_cdr; @@ -2733,6 +2783,7 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast } } } + ast_cel_report_event(chan, AST_CEL_BRIDGE_START, NULL, NULL, NULL); for (;;) { struct ast_channel *other; /* used later */ @@ -3213,6 +3264,7 @@ int manage_parkinglot(struct ast_parkinglot *curlot, fd_set *rfds, fd_set *efds, set_c_e_p(chan, pu->context, pu->exten, pu->priority); } post_manager_event("ParkedCallTimeOut", pu); + ast_cel_report_event(pu->chan, AST_CEL_PARK_END, NULL, "ParkedCallTimeOut", NULL); ast_verb(2, "Timeout for %s parked on %d (%s). Returning to %s,%s,%d\n", pu->chan->name, pu->parkingnum, pu->parkinglot->name, pu->chan->context, pu->chan->exten, pu->chan->priority); /* Start up the PBX, or hang them up */ @@ -3251,6 +3303,7 @@ int manage_parkinglot(struct ast_parkinglot *curlot, fd_set *rfds, fd_set *efds, if (f) ast_frfree(f); post_manager_event("ParkedCallGiveUp", pu); + ast_cel_report_event(pu->chan, AST_CEL_PARK_END, NULL, "ParkedCallGiveUp", NULL); /* There's a problem, hang them up*/ ast_verb(2, "%s got tired of being parked\n", chan->name); @@ -3495,6 +3548,7 @@ static int park_exec_full(struct ast_channel *chan, const char *data, struct ast } else ast_log(LOG_WARNING, "Whoa, no parking context?\n"); + ast_cel_report_event(pu->chan, AST_CEL_PARK_END, NULL, "UnParkedCall", chan); manager_event(EVENT_FLAG_CALL, "UnParkedCall", "Exten: %s\r\n" "Channel: %s\r\n" @@ -4294,7 +4348,7 @@ static int action_bridge(struct mansession *s, const struct message *m) /* create the placeholder channels and grab the other channels */ if (!(tmpchana = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, - NULL, NULL, 0, "Bridge/%s", chana->name))) { + NULL, NULL, chana->linkedid, 0, "Bridge/%s", chana->name))) { astman_send_error(s, m, "Unable to create temporary channel!"); chana = ast_channel_unref(chana); return 1; @@ -4321,7 +4375,7 @@ static int action_bridge(struct mansession *s, const struct message *m) /* create the placeholder channels and grab the other channels */ if (!(tmpchanb = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, - NULL, NULL, 0, "Bridge/%s", chanb->name))) { + NULL, NULL, chanb->linkedid, 0, "Bridge/%s", chanb->name))) { astman_send_error(s, m, "Unable to create temporary channels!"); ast_hangup(tmpchana); chanb = ast_channel_unref(chanb); @@ -4715,7 +4769,7 @@ static int bridge_exec(struct ast_channel *chan, const char *data) /* try to allocate a place holder where current_dest_chan will be placed */ if (!(final_dest_chan = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, - NULL, NULL, 0, "Bridge/%s", current_dest_chan->name))) { + NULL, NULL, current_dest_chan->linkedid, 0, "Bridge/%s", current_dest_chan->name))) { ast_log(LOG_WARNING, "Cannot create placeholder channel for chan %s\n", args.dest_chan); manager_event(EVENT_FLAG_CALL, "BridgeExec", "Response: Failed\r\n" diff --git a/main/loader.c b/main/loader.c index a8e986409..451d33194 100644 --- a/main/loader.c +++ b/main/loader.c @@ -258,6 +258,7 @@ static struct reload_classes { { "dsp", ast_dsp_reload}, { "udptl", ast_udptl_reload }, { "indications", ast_indications_reload }, + { "cel", ast_cel_engine_reload }, { NULL, NULL } }; diff --git a/main/logger.c b/main/logger.c index 34009053a..02cf4f4fc 100644 --- a/main/logger.c +++ b/main/logger.c @@ -535,7 +535,7 @@ static int rotate_file(const char *filename) } if (!ast_strlen_zero(exec_after_rotate)) { - struct ast_channel *c = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Logger/rotate"); + struct ast_channel *c = ast_dummy_channel_alloc(); char buf[512]; pbx_builtin_setvar_helper(c, "filename", filename); pbx_substitute_variables_helper(c, exec_after_rotate, buf, sizeof(buf)); diff --git a/main/manager.c b/main/manager.c index a2e85f351..76e24d170 100644 --- a/main/manager.c +++ b/main/manager.c @@ -2488,7 +2488,7 @@ static int action_getvar(struct mansession *s, const struct message *m) if (varname[strlen(varname) - 1] == ')') { if (!c) { - c = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/manager"); + c = ast_dummy_channel_alloc(); if (c) { ast_func_read(c, (char *) varname, workspace, sizeof(workspace)); c = ast_channel_release(c); diff --git a/main/pbx.c b/main/pbx.c index 32838a0e6..c7de70f87 100644 --- a/main/pbx.c +++ b/main/pbx.c @@ -46,6 +46,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/file.h" #include "asterisk/callerid.h" #include "asterisk/cdr.h" +#include "asterisk/cel.h" #include "asterisk/config.h" #include "asterisk/term.h" #include "asterisk/time.h" @@ -1356,6 +1357,8 @@ int pbx_exec(struct ast_channel *c, /*!< Channel */ c->appl = app->name; c->data = data; + ast_cel_report_event(c, AST_CEL_APP_START, NULL, NULL, NULL); + if (app->module) u = __ast_module_user_add(app->module, c); if (strcasecmp(app->name, "system") && !ast_strlen_zero(data) && @@ -1367,6 +1370,7 @@ int pbx_exec(struct ast_channel *c, /*!< Channel */ res = app->execute(c, S_OR(data, "")); if (app->module && u) __ast_module_user_remove(app->module, u); + ast_cel_report_event(c, AST_CEL_APP_END, NULL, NULL, NULL); /* restore channel values */ c->appl = saved_c_appl; c->data = saved_c_data; @@ -3622,7 +3626,7 @@ void ast_str_substitute_variables_full(struct ast_str **buf, ssize_t maxlen, str cp4 = ast_func_read2(c, finalvars, &substr3, 0) ? NULL : ast_str_buffer(substr3); } else { struct varshead old; - struct ast_channel *bogus = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/%p", vars); + struct ast_channel *bogus = ast_dummy_channel_alloc(); if (bogus) { memcpy(&old, &bogus->varshead, sizeof(old)); memcpy(&bogus->varshead, headp, sizeof(bogus->varshead)); @@ -3821,14 +3825,14 @@ void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace; else { struct varshead old; - struct ast_channel *bogus = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/%p", vars); - if (bogus) { - memcpy(&old, &bogus->varshead, sizeof(old)); - memcpy(&bogus->varshead, headp, sizeof(bogus->varshead)); - cp4 = ast_func_read(bogus, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace; + struct ast_channel *c = ast_dummy_channel_alloc(); + if (c) { + memcpy(&old, &c->varshead, sizeof(old)); + memcpy(&c->varshead, headp, sizeof(c->varshead)); + cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace; /* Don't deallocate the varshead that was passed in */ - memcpy(&bogus->varshead, &old, sizeof(bogus->varshead)); - bogus = ast_channel_release(bogus); + memcpy(&c->varshead, &old, sizeof(c->varshead)); + c = ast_channel_release(c); } else { ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution. Function results may be blank.\n"); } @@ -7598,7 +7602,7 @@ int ast_async_goto(struct ast_channel *chan, const char *context, const char *ex /* In order to do it when the channel doesn't really exist within the PBX, we have to make a new channel, masquerade, and start the PBX at the new location */ - struct ast_channel *tmpchan = ast_channel_alloc(0, chan->_state, 0, 0, chan->accountcode, chan->exten, chan->context, chan->amaflags, "AsyncGoto/%s", chan->name); + struct ast_channel *tmpchan = ast_channel_alloc(0, chan->_state, 0, 0, chan->accountcode, chan->exten, chan->context, chan->linkedid, chan->amaflags, "AsyncGoto/%s", chan->name); if (!tmpchan) { res = -1; } else { @@ -7905,7 +7909,10 @@ static int ast_add_extension2_lockopt(struct ast_context *con, /* If we are adding a hint evalulate in variables and global variables */ if (priority == PRIORITY_HINT && strstr(application, "${") && !strstr(extension, "_")) { - struct ast_channel *c = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", extension, con->name, 0, "Bogus/%s", __PRETTY_FUNCTION__); + struct ast_channel *c = ast_dummy_channel_alloc(); + ast_copy_string(c->exten, extension, sizeof(c->exten)); + ast_copy_string(c->context, con->name, sizeof(c->context)); + pbx_substitute_variables_helper(c, application, expand_buf, sizeof(expand_buf)); application = expand_buf; ast_channel_release(c); @@ -8154,11 +8161,12 @@ static void *async_wait(void *data) static int ast_pbx_outgoing_cdr_failed(void) { /* allocate a channel */ - struct ast_channel *chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "%s", ""); + struct ast_channel *chan = ast_dummy_channel_alloc(); if (!chan) return -1; /* failure */ + chan->cdr = ast_cdr_alloc(); if (!chan->cdr) { /* allocation of the cdr failed */ chan = ast_channel_release(chan); /* free the channel */ @@ -8194,7 +8202,7 @@ int ast_pbx_outgoing_exten(const char *type, int format, void *data, int timeout oh.vars = vars; oh.parent_channel = NULL; - chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh); + chan = __ast_request_and_dial(type, format, NULL, data, timeout, reason, cid_num, cid_name, &oh); if (channel) { *channel = chan; if (chan) @@ -8260,7 +8268,7 @@ int ast_pbx_outgoing_exten(const char *type, int format, void *data, int timeout /* create a fake channel and execute the "failed" extension (if it exists) within the requested context */ /* check if "failed" exists */ if (ast_exists_extension(chan, context, "failed", 1, NULL)) { - chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "OutgoingSpoolFailed"); + chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", NULL, 0, "OutgoingSpoolFailed"); if (chan) { char failed_reason[4] = ""; if (!ast_strlen_zero(context)) @@ -8284,7 +8292,7 @@ int ast_pbx_outgoing_exten(const char *type, int format, void *data, int timeout res = -1; goto outgoing_exten_cleanup; } - chan = ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name); + chan = ast_request_and_dial(type, format, NULL, data, timeout, reason, cid_num, cid_name); if (channel) { *channel = chan; if (chan) @@ -8361,7 +8369,7 @@ int ast_pbx_outgoing_app(const char *type, int format, void *data, int timeout, goto outgoing_app_cleanup; } if (synchronous) { - chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh); + chan = __ast_request_and_dial(type, format, NULL, data, timeout, reason, cid_num, cid_name, &oh); if (chan) { ast_set_variables(chan, vars); if (account) @@ -8426,7 +8434,7 @@ int ast_pbx_outgoing_app(const char *type, int format, void *data, int timeout, res = -1; goto outgoing_app_cleanup; } - chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh); + chan = __ast_request_and_dial(type, format, NULL, data, timeout, reason, cid_num, cid_name, &oh); if (!chan) { ast_free(as); res = -1; @@ -8833,6 +8841,8 @@ static int pbx_builtin_setamaflags(struct ast_channel *chan, const char *data) */ static int pbx_builtin_hangup(struct ast_channel *chan, const char *data) { + ast_set_hangupsource(chan, "dialplan/builtin", 0); + if (!ast_strlen_zero(data)) { int cause; char *endptr; @@ -9549,7 +9559,7 @@ int load_pbx(void) /* Register manager application */ ast_manager_register_xml("ShowDialPlan", EVENT_FLAG_CONFIG | EVENT_FLAG_REPORTING, manager_show_dialplan); - if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, NULL, + if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE_CHANGE, device_state_cb, "pbx Device State Change", NULL, AST_EVENT_IE_END))) { return -1; } diff --git a/res/ais/evt.c b/res/ais/evt.c index f551321e0..54a87935b 100644 --- a/res/ais/evt.c +++ b/res/ais/evt.c @@ -325,7 +325,7 @@ static void add_publish_event(struct event_channel *event_channel, const char *e publish_event->type = type; ast_log(LOG_DEBUG, "Subscribing to event type %d\n", type); - publish_event->sub = ast_event_subscribe(type, ast_event_cb, event_channel, + publish_event->sub = ast_event_subscribe(type, ast_event_cb, "AIS", event_channel, AST_EVENT_IE_END); ast_event_dump_cache(publish_event->sub); diff --git a/res/res_agi.c b/res/res_agi.c index dff477e09..164c5ec62 100644 --- a/res/res_agi.c +++ b/res/res_agi.c @@ -2256,6 +2256,7 @@ static int handle_hangup(struct ast_channel *chan, AGI *agi, int argc, const cha if (argc == 1) { /* no argument: hangup the current channel */ + ast_set_hangupsource(chan, "dialplan/agi", 0); ast_softhangup(chan,AST_SOFTHANGUP_EXPLICIT); ast_agi_send(agi->fd, chan, "200 result=1\n"); return RESULT_SUCCESS; @@ -2263,6 +2264,7 @@ static int handle_hangup(struct ast_channel *chan, AGI *agi, int argc, const cha /* one argument: look for info on the specified channel */ if ((c = ast_channel_get_by_name(argv[1]))) { /* we have a matching channel */ + ast_set_hangupsource(c, "dialplan/agi", 0); ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT); c = ast_channel_unref(c); ast_agi_send(agi->fd, chan, "200 result=1\n"); diff --git a/res/res_calendar.c b/res/res_calendar.c index 8e8156d13..a8bb8019e 100644 --- a/res/res_calendar.c +++ b/res/res_calendar.c @@ -631,7 +631,7 @@ static int calendar_event_notify(const void *data) ast_dial_set_global_timeout(dial, event->owner->notify_waittime); generate_random_string(buf, sizeof(buf)); - if (!(chan = ast_channel_alloc(1, AST_STATE_DOWN, 0, 0, 0, 0, 0, 0, "Calendar/%s-%s", event->owner->name, buf))) { + if (!(chan = ast_channel_alloc(1, AST_STATE_DOWN, 0, 0, 0, 0, 0, 0, 0, "Calendar/%s-%s", event->owner->name, buf))) { ast_log(LOG_ERROR, "Could not allocate notification channel\n"); goto notify_cleanup; } diff --git a/tests/test_substitution.c b/tests/test_substitution.c index c995e8ae6..ae5aa7b76 100644 --- a/tests/test_substitution.c +++ b/tests/test_substitution.c @@ -165,7 +165,7 @@ static char *handle_cli_test_substitution(struct ast_cli_entry *e, int cmd, stru } ast_cli(a->fd, "Testing variable substitution ...\n"); - c = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Test/substitution"); + c = ast_dummy_channel_alloc(); test_chan_integer(a->fd, c, &c->cid.cid_pres, "${CALLINGPRES}"); test_chan_integer(a->fd, c, &c->cid.cid_ani2, "${CALLINGANI2}"); @@ -217,7 +217,7 @@ static char *handle_cli_test_substitution(struct ast_cli_entry *e, int cmd, stru ast_free(cmd); } - ast_hangup(c); + ast_channel_release(c); return CLI_SUCCESS; } |