aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGES14
-rw-r--r--Makefile4
-rw-r--r--apps/app_celgenuserevent.c98
-rw-r--r--apps/app_chanisavail.c2
-rw-r--r--apps/app_confbridge.c2
-rw-r--r--apps/app_dial.c12
-rw-r--r--apps/app_directed_pickup.c2
-rw-r--r--apps/app_followme.c2
-rw-r--r--apps/app_meetme.c4
-rw-r--r--apps/app_minivm.c6
-rw-r--r--apps/app_parkandannounce.c2
-rw-r--r--apps/app_queue.c11
-rw-r--r--apps/app_voicemail.c16
-rw-r--r--bridges/bridge_builtin_features.c2
-rw-r--r--cdr/cdr_custom.c2
-rw-r--r--cdr/cdr_manager.c2
-rw-r--r--cdr/cdr_sqlite.c8
-rw-r--r--cdr/cdr_sqlite3_custom.c2
-rw-r--r--cel/Makefile20
-rw-r--r--cel/cel_adaptive_odbc.c771
-rw-r--r--cel/cel_custom.c216
-rw-r--r--cel/cel_manager.c175
-rw-r--r--cel/cel_pgsql.c565
-rw-r--r--cel/cel_radius.c254
-rw-r--r--cel/cel_sqlite3_custom.c364
-rw-r--r--cel/cel_tds.c587
-rw-r--r--channels/chan_agent.c18
-rw-r--r--channels/chan_alsa.c12
-rw-r--r--channels/chan_bridge.c8
-rw-r--r--channels/chan_console.c14
-rw-r--r--channels/chan_dahdi.c58
-rw-r--r--channels/chan_gtalk.c12
-rw-r--r--channels/chan_h323.c12
-rw-r--r--channels/chan_iax2.c74
-rw-r--r--channels/chan_jingle.c12
-rw-r--r--channels/chan_local.c12
-rw-r--r--channels/chan_mgcp.c16
-rw-r--r--channels/chan_misdn.c14
-rw-r--r--channels/chan_multicast_rtp.c6
-rw-r--r--channels/chan_nbs.c10
-rw-r--r--channels/chan_oss.c13
-rw-r--r--channels/chan_phone.c18
-rw-r--r--channels/chan_sip.c55
-rw-r--r--channels/chan_skinny.c42
-rw-r--r--channels/chan_unistim.c16
-rw-r--r--channels/chan_usbradio.c13
-rw-r--r--channels/chan_vpb.cc24
-rw-r--r--channels/sig_analog.c20
-rw-r--r--channels/sig_analog.h4
-rw-r--r--channels/sig_pri.c14
-rw-r--r--channels/sig_pri.h4
-rw-r--r--configs/cel.conf.sample103
-rw-r--r--configs/cel_adaptive_odbc.conf.sample106
-rw-r--r--configs/cel_custom.conf.sample20
-rw-r--r--configs/cel_pgsql.conf.sample58
-rw-r--r--configs/cel_sqlite3_custom.conf.sample7
-rw-r--r--configs/cel_tds.conf.sample69
-rw-r--r--doc/tex/asterisk.tex4
-rw-r--r--doc/tex/cel-doc.tex958
-rw-r--r--doc/tex/celdriver.tex447
-rw-r--r--funcs/func_cdr.c2
-rw-r--r--funcs/func_channel.c79
-rw-r--r--funcs/func_odbc.c8
-rw-r--r--include/asterisk/_private.h2
-rw-r--r--include/asterisk/cdr.h15
-rw-r--r--include/asterisk/cel.h276
-rw-r--r--include/asterisk/channel.h53
-rw-r--r--include/asterisk/event.h11
-rw-r--r--include/asterisk/event_defs.h158
-rw-r--r--include/asterisk/utils.h1
-rw-r--r--main/asterisk.c6
-rw-r--r--main/cdr.c62
-rw-r--r--main/cel.c652
-rw-r--r--main/channel.c333
-rw-r--r--main/cli.c13
-rw-r--r--main/devicestate.c2
-rw-r--r--main/dial.c2
-rw-r--r--main/event.c64
-rw-r--r--main/features.c70
-rw-r--r--main/loader.c1
-rw-r--r--main/logger.c2
-rw-r--r--main/manager.c2
-rw-r--r--main/pbx.c44
-rw-r--r--res/ais/evt.c2
-rw-r--r--res/res_agi.c2
-rw-r--r--res/res_calendar.c2
-rw-r--r--tests/test_substitution.c4
87 files changed, 6919 insertions, 365 deletions
diff --git a/CHANGES b/CHANGES
index c66bf68ca..48b909847 100644
--- a/CHANGES
+++ b/CHANGES
@@ -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
diff --git a/Makefile b/Makefile
index dd4ab6089..a74b95b9d 100644
--- a/Makefile
+++ b/Makefile
@@ -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;
}