Merge the new Channel Event Logging (CEL) subsystem.
authorRussell Bryant <russell@russellbryant.com>
Fri, 26 Jun 2009 15:28:53 +0000 (15:28 +0000)
committerRussell Bryant <russell@russellbryant.com>
Fri, 26 Jun 2009 15:28:53 +0000 (15:28 +0000)
CEL is the new system for logging channel events.  This was inspired after
facing many problems trying to represent what is possible to happen to a call
in Asterisk using CDR records.  For more information on CEL, see the built in
HTML or PDF documentation generated from the files in doc/tex/.

Many thanks to Steve Murphy (murf) and Brian Degenhardt (bmd) for their hard
work developing this code.  Also, thanks to Matt Nicholson (mnicholson) and
Sean Bright (seanbright) for their assistance in the final push to get this
code ready for Asterisk trunk.

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

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

87 files changed:
CHANGES
Makefile
apps/app_celgenuserevent.c [new file with mode: 0644]
apps/app_chanisavail.c
apps/app_confbridge.c
apps/app_dial.c
apps/app_directed_pickup.c
apps/app_followme.c
apps/app_meetme.c
apps/app_minivm.c
apps/app_parkandannounce.c
apps/app_queue.c
apps/app_voicemail.c
bridges/bridge_builtin_features.c
cdr/cdr_custom.c
cdr/cdr_manager.c
cdr/cdr_sqlite.c
cdr/cdr_sqlite3_custom.c
cel/Makefile [new file with mode: 0644]
cel/cel_adaptive_odbc.c [new file with mode: 0644]
cel/cel_custom.c [new file with mode: 0644]
cel/cel_manager.c [new file with mode: 0644]
cel/cel_pgsql.c [new file with mode: 0644]
cel/cel_radius.c [new file with mode: 0644]
cel/cel_sqlite3_custom.c [new file with mode: 0644]
cel/cel_tds.c [new file with mode: 0644]
channels/chan_agent.c
channels/chan_alsa.c
channels/chan_bridge.c
channels/chan_console.c
channels/chan_dahdi.c
channels/chan_gtalk.c
channels/chan_h323.c
channels/chan_iax2.c
channels/chan_jingle.c
channels/chan_local.c
channels/chan_mgcp.c
channels/chan_misdn.c
channels/chan_multicast_rtp.c
channels/chan_nbs.c
channels/chan_oss.c
channels/chan_phone.c
channels/chan_sip.c
channels/chan_skinny.c
channels/chan_unistim.c
channels/chan_usbradio.c
channels/chan_vpb.cc
channels/sig_analog.c
channels/sig_analog.h
channels/sig_pri.c
channels/sig_pri.h
configs/cel.conf.sample [new file with mode: 0644]
configs/cel_adaptive_odbc.conf.sample [new file with mode: 0644]
configs/cel_custom.conf.sample [new file with mode: 0644]
configs/cel_pgsql.conf.sample [new file with mode: 0644]
configs/cel_sqlite3_custom.conf.sample [new file with mode: 0644]
configs/cel_tds.conf.sample [new file with mode: 0644]
doc/tex/asterisk.tex
doc/tex/cel-doc.tex [new file with mode: 0644]
doc/tex/celdriver.tex [new file with mode: 0644]
funcs/func_cdr.c
funcs/func_channel.c
funcs/func_odbc.c
include/asterisk/_private.h
include/asterisk/cdr.h
include/asterisk/cel.h [new file with mode: 0644]
include/asterisk/channel.h
include/asterisk/event.h
include/asterisk/event_defs.h
include/asterisk/utils.h
main/asterisk.c
main/cdr.c
main/cel.c [new file with mode: 0644]
main/channel.c
main/cli.c
main/devicestate.c
main/dial.c
main/event.c
main/features.c
main/loader.c
main/logger.c
main/manager.c
main/pbx.c
res/ais/evt.c
res/res_agi.c
res/res_calendar.c
tests/test_substitution.c

diff --git a/CHANGES b/CHANGES
index c66bf68..48b9098 100644 (file)
--- 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
index dd4ab60..a74b95b 100644 (file)
--- 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 (file)
index 0000000..da1c246
--- /dev/null
@@ -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,
+       );
index b1c426a..c1b33b9 100644 (file)
@@ -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);
index bb68f36..446cc09 100644 (file)
@@ -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;
                }
index a952265..92d1657 100644 (file)
@@ -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);
index 69015a0..9301395 100644 (file)
@@ -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;
index 504ff7c..6764833 100644 (file)
@@ -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);
index bfb61a3..23abe82 100644 (file)
@@ -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;
index ac48aa5..c4e7866 100644 (file)
@@ -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;
index d8858b6..4ae5251 100644 (file)
@@ -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) {
index bebe543..aa3564a 100644 (file)
@@ -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;
        }
 
index 51b78e2..b058ab1 100644 (file)
@@ -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);
 
index a61421c..67da648 100644 (file)
@@ -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;
        }
 
index c045241..0a88f3d 100644 (file)
@@ -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");
index 83e6054..49e9979 100644 (file)
@@ -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;
index 8eecee4..a5eb54f 100644 (file)
@@ -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");
index fd2d3e4..6f1961c 100644 (file)
@@ -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 (file)
index 0000000..5ac6d89
--- /dev/null
@@ -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 (file)
index 0000000..984f059
--- /dev/null
@@ -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 (file)
index 0000000..37a7419
--- /dev/null
@@ -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 (file)
index 0000000..a57c31e
--- /dev/null
@@ -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 (file)
index 0000000..8b10261
--- /dev/null
@@ -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 (file)
index 0000000..c44044f
--- /dev/null
@@ -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 (file)
index 0000000..c37641b
--- /dev/null
@@ -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 (file)
index 0000000..138a9cd
--- /dev/null
@@ -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,
+);
index f8205d4..a31a12f 100644 (file)
@@ -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 {
index e1fe2b3..48d7186 100644 (file)
@@ -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);
        }
index be41cd8..be823cd 100644 (file)
@@ -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;
index 091fcf1..55be768 100644 (file)
@@ -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);
index e1c3348..a57e221 100644 (file)
@@ -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);
index bfa0915..6ad6ed9 100644 (file)
@@ -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;
index fd32953..a4135a9 100644 (file)
@@ -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);
index 89ec843..3b6902e 100644 (file)
@@ -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);
index 01909f1..da98986 100644 (file)
@@ -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;
index 329b982..df1a2c4 100644 (file)
@@ -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);
index be38e87..d682c76 100644 (file)
@@ -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,
index 44e096d..3a11f74 100644 (file)
@@ -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");
index 589fa10..d2bbc68 100644 (file)
@@ -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;
        }
index 8729f10..89c5f64 100644 (file)
@@ -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);
        }
index b3ff448..9b2201a 100644 (file)
@@ -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)
index dd1578c..d313121 100644 (file)
@@ -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;
index 4a7d00e..00042eb 100644 (file)
@@ -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",
index 7271a2e..d88739b 100644 (file)
@@ -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,
index 27e8e0c..bb90301 100644 (file)
@@ -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)
index 2a829a6..b8e9d70 100644 (file)
@@ -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;
index e611ab0..fa82437 100644 (file)
@@ -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;
                        }
                }
index da7b713..79fc276 100644 (file)
@@ -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);
index 24c4d6c..6611798 100644 (file)
@@ -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);
 
index 1a648a1..f2eeb23 100644 (file)
@@ -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];
index 2c9f7b6..80cfdd1 100644 (file)
@@ -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);
 <