diff -Nru asterisk-11.7.0~dfsg/addons/app_mysql.c asterisk-13.1.0~dfsg/addons/app_mysql.c --- asterisk-11.7.0~dfsg/addons/app_mysql.c 2012-07-31 20:21:43.000000000 +0000 +++ asterisk-13.1.0~dfsg/addons/app_mysql.c 2014-07-25 16:47:17.000000000 +0000 @@ -23,6 +23,15 @@ * \ingroup applications */ +/*! \li \ref app_mysql.c uses the configuration file \ref app_mysql.conf + * \addtogroup configuration_file Configuration Files + */ + +/*! + * \page app_mysql.conf app_mysql.conf + * \verbinclude app_mysql.conf.sample + */ + /*** MODULEINFO mysqlclient no @@ -283,16 +292,17 @@ return res; } -static int aMYSQL_set(struct ast_channel *chan, char *data) +static int aMYSQL_set(struct ast_channel *chan, const char *data) { - char *var, *tmp; + char *var, *tmp, *parse; AST_DECLARE_APP_ARGS(args, AST_APP_ARG(set); AST_APP_ARG(variable); AST_APP_ARG(value); ); - AST_NONSTANDARD_APP_ARGS(args, data, ' '); + parse = ast_strdupa(data); + AST_NONSTANDARD_APP_ARGS(args, parse, ' '); if (args.argc == 3) { var = ast_alloca(6 + strlen(args.variable) + 1); @@ -308,7 +318,7 @@ } /* MYSQL operations */ -static int aMYSQL_connect(struct ast_channel *chan, char *data) +static int aMYSQL_connect(struct ast_channel *chan, const char *data) { AST_DECLARE_APP_ARGS(args, AST_APP_ARG(connect); @@ -324,8 +334,9 @@ const char *ctimeout; unsigned int port = 0; char *port_str; - - AST_NONSTANDARD_APP_ARGS(args, data, ' '); + char *parse = ast_strdupa(data); + + AST_NONSTANDARD_APP_ARGS(args, parse, ' '); if (args.argc < 6) { ast_log(LOG_WARNING, "MYSQL_connect is missing some arguments\n"); @@ -376,7 +387,7 @@ return 0; } -static int aMYSQL_query(struct ast_channel *chan, char *data) +static int aMYSQL_query(struct ast_channel *chan, const char *data) { AST_DECLARE_APP_ARGS(args, AST_APP_ARG(query); @@ -388,8 +399,9 @@ MYSQL_RES *mysqlres; int connid; int mysql_query_res; + char *parse = ast_strdupa(data); - AST_NONSTANDARD_APP_ARGS(args, data, ' '); + AST_NONSTANDARD_APP_ARGS(args, parse, ' '); if (args.argc != 4 || (connid = atoi(args.connid)) == 0) { ast_log(LOG_WARNING, "missing some arguments\n"); @@ -417,7 +429,7 @@ return -1; } -static int aMYSQL_nextresult(struct ast_channel *chan, char *data) +static int aMYSQL_nextresult(struct ast_channel *chan, const char *data) { MYSQL *mysql; MYSQL_RES *mysqlres; @@ -427,8 +439,9 @@ AST_APP_ARG(connid); ); int connid = -1; + char *parse = ast_strdupa(data); - AST_NONSTANDARD_APP_ARGS(args, data, ' '); + AST_NONSTANDARD_APP_ARGS(args, parse, ' '); sscanf(args.connid, "%30d", &connid); if (args.argc != 3 || connid <= 0) { @@ -457,7 +470,7 @@ } -static int aMYSQL_fetch(struct ast_channel *chan, char *data) +static int aMYSQL_fetch(struct ast_channel *chan, const char *data) { MYSQL_RES *mysqlres; MYSQL_ROW mysqlrow; @@ -509,13 +522,14 @@ return -1; } -static int aMYSQL_clear(struct ast_channel *chan, char *data) +static int aMYSQL_clear(struct ast_channel *chan, const char *data) { MYSQL_RES *mysqlres; int id; - strsep(&data, " "); /* eat the first token, we already know it :P */ - id = safe_scan_int(&data, " \n", -1); + char *parse = ast_strdupa(data); + strsep(&parse, " "); /* eat the first token, we already know it :P */ + id = safe_scan_int(&parse, " \n", -1); if ((mysqlres = find_identifier(id, AST_MYSQL_ID_RESID)) == NULL) { ast_log(LOG_WARNING, "Invalid result identifier %d passed in aMYSQL_clear\n", id); } else { @@ -526,13 +540,14 @@ return 0; } -static int aMYSQL_disconnect(struct ast_channel *chan, char *data) +static int aMYSQL_disconnect(struct ast_channel *chan, const char *data) { MYSQL *mysql; int id; - strsep(&data, " "); /* eat the first token, we already know it :P */ + char *parse = ast_strdupa(data); + strsep(&parse, " "); /* eat the first token, we already know it :P */ - id = safe_scan_int(&data, " \n", -1); + id = safe_scan_int(&parse, " \n", -1); if ((mysql = find_identifier(id, AST_MYSQL_ID_CONNID)) == NULL) { ast_log(LOG_WARNING, "Invalid connection identifier %d passed in aMYSQL_disconnect\n", id); } else { @@ -575,19 +590,19 @@ ast_mutex_lock(&_mysql_mutex); if (strncasecmp("connect", data, strlen("connect")) == 0) { - result = aMYSQL_connect(chan, ast_strdupa(data)); + result = aMYSQL_connect(chan, data); } else if (strncasecmp("query", data, strlen("query")) == 0) { - result = aMYSQL_query(chan, ast_strdupa(data)); + result = aMYSQL_query(chan, data); } else if (strncasecmp("nextresult", data, strlen("nextresult")) == 0) { - result = aMYSQL_nextresult(chan, ast_strdupa(data)); + result = aMYSQL_nextresult(chan, data); } else if (strncasecmp("fetch", data, strlen("fetch")) == 0) { - result = aMYSQL_fetch(chan, ast_strdupa(data)); + result = aMYSQL_fetch(chan, data); } else if (strncasecmp("clear", data, strlen("clear")) == 0) { - result = aMYSQL_clear(chan, ast_strdupa(data)); + result = aMYSQL_clear(chan, data); } else if (strncasecmp("disconnect", data, strlen("disconnect")) == 0) { - result = aMYSQL_disconnect(chan, ast_strdupa(data)); + result = aMYSQL_disconnect(chan, data); } else if (strncasecmp("set", data, 3) == 0) { - result = aMYSQL_set(chan, ast_strdupa(data)); + result = aMYSQL_set(chan, data); } else { ast_log(LOG_WARNING, "Unknown argument to MYSQL application : %s\n", data); result = -1; @@ -605,6 +620,16 @@ return ast_unregister_application(app); } +/*! + * \brief Load the module + * + * Module loading including tests for configuration or dependencies. + * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE, + * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails + * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the + * configuration file or other non-critical problem return + * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS. + */ static int load_module(void) { struct MYSQLidshead *headp = &_mysql_ids_head; @@ -639,4 +664,5 @@ return ast_register_application(app, MYSQL_exec, synopsis, descrip); } -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Simple Mysql Interface"); +AST_MODULE_INFO_STANDARD_DEPRECATED(ASTERISK_GPL_KEY, "Simple Mysql Interface"); + diff -Nru asterisk-11.7.0~dfsg/addons/app_saycountpl.c asterisk-13.1.0~dfsg/addons/app_saycountpl.c --- asterisk-11.7.0~dfsg/addons/app_saycountpl.c 2012-01-24 20:12:09.000000000 +0000 +++ asterisk-13.1.0~dfsg/addons/app_saycountpl.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,138 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 2004, Andy Powell & TAAN Softworks Corp. - * - * 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 Say Polish counting words - * \author Andy Powell - */ - -/*** MODULEINFO - no - deprecated - say.conf - ***/ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 352348 $") - -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/pbx.h" -#include "asterisk/module.h" -#include "asterisk/lock.h" -#include "asterisk/app.h" - -/*** DOCUMENTATION - - - Say Polish counting words. - - - - - - - - - Polish grammar has some funny rules for counting words. for example 1 zloty, - 2 zlote, 5 zlotych. This application will take the words for 1, 2-4 and 5 and - decide based on grammar rules which one to use with the number you pass to it. - Example: SayCountPL(zloty,zlote,zlotych,122) will give: zlote - - - - ***/ -static const char app[] = "SayCountPL"; - -static int saywords(struct ast_channel *chan, char *word1, char *word2, char *word5, int num) -{ - /* Put this in a separate proc because it's bound to change */ - int d = 0; - - if (num > 0) { - if (num % 1000 == 1) { - ast_streamfile(chan, word1, ast_channel_language(chan)); - d = ast_waitstream(chan,""); - } else if (((num % 10) >= 2) && ((num % 10) <= 4 ) && ((num % 100) < 10 || (num % 100) > 20)) { - ast_streamfile(chan, word2, ast_channel_language(chan)); - d = ast_waitstream(chan, ""); - } else { - ast_streamfile(chan, word5, ast_channel_language(chan)); - d = ast_waitstream(chan, ""); - } - } - - return d; -} - - -static int sayword_exec(struct ast_channel *chan, const char *data) -{ - int res = 0; - char *s; - int inum; - AST_DECLARE_APP_ARGS(args, - AST_APP_ARG(word1); - AST_APP_ARG(word2); - AST_APP_ARG(word5); - AST_APP_ARG(num); - ); - - if (!data) { - ast_log(LOG_WARNING, "SayCountPL requires 4 arguments: word-1,word-2,word-5,number\n"); - return -1; - } - - s = ast_strdupa(data); - - AST_STANDARD_APP_ARGS(args, s); - - /* Check to see if params passed */ - if (!args.word1 || !args.word2 || !args.word5 || !args.num) { - ast_log(LOG_WARNING, "SayCountPL requires 4 arguments: word-1,word-2,word-3,number\n"); - return -1; - } - - if (sscanf(args.num, "%30d", &inum) != 1) { - ast_log(LOG_WARNING, "'%s' is not a valid number\n", args.num); - return -1; - } - - /* do the saying part (after a bit of maths) */ - - res = saywords(chan, args.word1, args.word2, args.word5, inum); - - return res; -} - -static int unload_module(void) -{ - return ast_unregister_application(app); -} - -static int load_module(void) -{ - int res; - - res = ast_register_application_xml(app, sayword_exec); - - return res ? AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS; -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Say polish counting words"); diff -Nru asterisk-11.7.0~dfsg/addons/cdr_mysql.c asterisk-13.1.0~dfsg/addons/cdr_mysql.c --- asterisk-11.7.0~dfsg/addons/cdr_mysql.c 2012-12-10 01:27:47.000000000 +0000 +++ asterisk-13.1.0~dfsg/addons/cdr_mysql.c 2014-07-25 16:47:17.000000000 +0000 @@ -42,7 +42,7 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 377505 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $") #include #include @@ -251,7 +251,7 @@ char timestr[128]; ast_localtime(&tv, &tm, ast_str_strlen(cdrzone) ? ast_str_buffer(cdrzone) : NULL); ast_strftime(timestr, sizeof(timestr), "%Y-%m-%d %T", &tm); - ast_cdr_setvar(cdr, "calldate", timestr, 0); + value = ast_strdupa(timestr); cdrname = "calldate"; } else { cdrname = "start"; @@ -277,9 +277,11 @@ strstr(entry->type, "real") || strstr(entry->type, "numeric") || strstr(entry->type, "fixed"))) { - ast_cdr_getvar(cdr, cdrname, &value, workspace, sizeof(workspace), 0, 1); + ast_cdr_format_var(cdr, cdrname, &value, workspace, sizeof(workspace), 1); + } else if (!strcmp(cdrname, "calldate")) { + /* Skip calldate - the value has already been dup'd */ } else { - ast_cdr_getvar(cdr, cdrname, &value, workspace, sizeof(workspace), 0, 0); + ast_cdr_format_var(cdr, cdrname, &value, workspace, sizeof(workspace), 0); } if (value) { @@ -382,9 +384,11 @@ } dbport = 0; - ast_cdr_unregister(name); - - return 0; + if (reload) { + return ast_cdr_backend_suspend(name); + } else { + return ast_cdr_unregister(name); + } } static int my_load_config_string(struct ast_config *cfg, const char *category, const char *variable, struct ast_str **field, const char *def) @@ -658,7 +662,11 @@ return AST_MODULE_LOAD_FAILURE; } - res = ast_cdr_register(name, desc, mysql_log); + if (!reload) { + res = ast_cdr_register(name, desc, mysql_log); + } else { + res = ast_cdr_backend_unsuspend(name); + } if (res) { ast_log(LOG_ERROR, "Unable to register MySQL CDR handling\n"); } else { @@ -690,6 +698,7 @@ } AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "MySQL CDR Backend", + .support_level = AST_MODULE_SUPPORT_DEPRECATED, .load = load_module, .unload = unload_module, .reload = reload, diff -Nru asterisk-11.7.0~dfsg/addons/chan_mobile.c asterisk-13.1.0~dfsg/addons/chan_mobile.c --- asterisk-11.7.0~dfsg/addons/chan_mobile.c 2013-01-17 02:30:29.000000000 +0000 +++ asterisk-13.1.0~dfsg/addons/chan_mobile.c 2014-11-09 00:25:47.000000000 +0000 @@ -25,6 +25,15 @@ * \ingroup channel_drivers */ +/*! \li \ref chan_mobile.c uses the configuration file \ref chan_mobile.conf + * \addtogroup configuration_file Configuration Files + */ + +/*! + * \page chan_mobile.conf chan_mobile.conf + * \verbinclude chan_mobile.conf.sample + */ + /*** MODULEINFO bluetooth no @@ -33,7 +42,7 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 379343 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 427611 $") #include #include @@ -64,16 +73,16 @@ #include "asterisk/app.h" #include "asterisk/manager.h" #include "asterisk/io.h" +#include "asterisk/smoother.h" +#include "asterisk/format_cache.h" #define MBL_CONFIG "chan_mobile.conf" #define MBL_CONFIG_OLD "mobile.conf" #define DEVICE_FRAME_SIZE 48 -#define DEVICE_FRAME_FORMAT AST_FORMAT_SLINEAR +#define DEVICE_FRAME_FORMAT ast_format_slin #define CHANNEL_FRAME_SIZE 320 -static struct ast_format prefformat; - static int discovery_interval = 60; /* The device discovery interval, default 60 seconds. */ static pthread_t discovery_thread = AST_PTHREADT_NULL; /* The discovery thread */ static sdp_session_t *sdp_session; @@ -138,6 +147,7 @@ int ring_sched_id; struct ast_dsp *dsp; struct ast_sched_context *sched; + int hangupcause; /* flags */ unsigned int outgoing:1; /*!< outgoing call */ @@ -163,6 +173,9 @@ static int handle_response_cmti(struct mbl_pvt *pvt, char *buf); static int handle_response_cmgr(struct mbl_pvt *pvt, char *buf); static int handle_response_cusd(struct mbl_pvt *pvt, char *buf); +static int handle_response_busy(struct mbl_pvt *pvt); +static int handle_response_no_dialtone(struct mbl_pvt *pvt, char *buf); +static int handle_response_no_carrier(struct mbl_pvt *pvt, char *buf); static int handle_sms_prompt(struct mbl_pvt *pvt, char *buf); /* CLI stuff */ @@ -196,9 +209,9 @@ " Message - text of the message\n"; static struct ast_channel *mbl_new(int state, struct mbl_pvt *pvt, char *cid_num, - const struct ast_channel *requestor); + const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor); static struct ast_channel *mbl_request(const char *type, struct ast_format_cap *cap, - const struct ast_channel *requestor, const char *data, int *cause); + const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause); static int mbl_call(struct ast_channel *ast, const char *dest, int timeout); static int mbl_hangup(struct ast_channel *ast); static int mbl_answer(struct ast_channel *ast); @@ -332,6 +345,7 @@ struct hfp_cind cind_map; /*!< the cind name to index mapping for this AG */ int rsock; /*!< our rfcomm socket */ int rport; /*!< our rfcomm port */ + int sent_alerting; /*!< have we sent alerting? */ }; @@ -426,6 +440,10 @@ AT_CMER, AT_CIND_TEST, AT_CUSD, + AT_BUSY, + AT_NO_DIALTONE, + AT_NO_CARRIER, + AT_ECAM, } at_message_t; static int at_match_prefix(char *buf, char *prefix); @@ -820,9 +838,8 @@ */ static struct ast_channel *mbl_new(int state, struct mbl_pvt *pvt, char *cid_num, - const struct ast_channel *requestor) + const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor) { - struct ast_channel *chn; pvt->answered = 0; @@ -837,18 +854,18 @@ ast_dsp_digitreset(pvt->dsp); chn = ast_channel_alloc(1, state, cid_num, pvt->id, 0, 0, pvt->context, - requestor ? ast_channel_linkedid(requestor) : "", 0, + assignedids, requestor, 0, "Mobile/%s-%04lx", pvt->id, ast_random() & 0xffff); if (!chn) { goto e_return; } ast_channel_tech_set(chn, &mbl_tech); - ast_format_cap_add(ast_channel_nativeformats(chn), &prefformat); - ast_format_copy(ast_channel_rawreadformat(chn), &prefformat); - ast_format_copy(ast_channel_rawwriteformat(chn), &prefformat); - ast_format_copy(ast_channel_writeformat(chn), &prefformat); - ast_format_copy(ast_channel_readformat(chn), &prefformat); + ast_channel_nativeformats_set(chn, mbl_tech.capabilities); + ast_channel_set_rawreadformat(chn, DEVICE_FRAME_FORMAT); + ast_channel_set_rawwriteformat(chn, DEVICE_FRAME_FORMAT); + ast_channel_set_writeformat(chn, DEVICE_FRAME_FORMAT); + ast_channel_set_readformat(chn, DEVICE_FRAME_FORMAT); ast_channel_tech_pvt_set(chn, pvt); if (state == AST_STATE_RING) @@ -860,6 +877,7 @@ if (pvt->sco_socket != -1) { ast_channel_set_fd(chn, 0, pvt->sco_socket); } + ast_channel_unlock(chn); return chn; @@ -868,7 +886,7 @@ } static struct ast_channel *mbl_request(const char *type, struct ast_format_cap *cap, - const struct ast_channel *requestor, const char *data, int *cause) + const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause) { struct ast_channel *chn = NULL; @@ -883,9 +901,9 @@ return NULL; } - if (!(ast_format_cap_iscompatible(cap, &prefformat))) { - char tmp[256]; - ast_log(LOG_WARNING, "Asked to get a channel of unsupported format '%s'\n", ast_getformatname_multiple(tmp, sizeof(tmp), cap)); + if (ast_format_cap_iscompatible_format(cap, DEVICE_FRAME_FORMAT) == AST_FORMAT_CMP_NOT_EQUAL) { + struct ast_str *codec_buf = ast_str_alloca(64); + ast_log(LOG_WARNING, "Asked to get a channel of unsupported format '%s'\n", ast_format_cap_get_names(cap, &codec_buf)); *cause = AST_CAUSE_FACILITY_NOT_IMPLEMENTED; return NULL; } @@ -927,7 +945,7 @@ } ast_mutex_lock(&pvt->lock); - chn = mbl_new(AST_STATE_DOWN, pvt, NULL, requestor); + chn = mbl_new(AST_STATE_DOWN, pvt, NULL, assignedids, requestor); ast_mutex_unlock(&pvt->lock); if (!chn) { ast_log(LOG_WARNING, "Unable to allocate channel structure.\n"); @@ -972,6 +990,7 @@ ast_log(LOG_ERROR, "error sending ATD command on %s\n", pvt->id); return -1; } + pvt->hangupcause = 0; pvt->needchup = 1; msg_queue_push(pvt, AT_OK, AT_D); } else { @@ -1096,7 +1115,7 @@ memset(&pvt->fr, 0x00, sizeof(struct ast_frame)); pvt->fr.frametype = AST_FRAME_VOICE; - ast_format_set(&pvt->fr.subclass.format, DEVICE_FRAME_FORMAT, 0); + pvt->fr.subclass.format = DEVICE_FRAME_FORMAT; pvt->fr.src = "Mobile"; pvt->fr.offset = AST_FRIENDLY_OFFSET; pvt->fr.mallocd = 0; @@ -1305,6 +1324,9 @@ if (ast_channel_trylock(pvt->owner)) { DEADLOCK_AVOIDANCE(&pvt->lock); } else { + if (pvt->hangupcause != 0) { + ast_channel_hangupcause_set(pvt->owner, pvt->hangupcause); + } ast_queue_hangup(pvt->owner); ast_channel_unlock(pvt->owner); break; @@ -1317,9 +1339,7 @@ static int mbl_ast_hangup(struct mbl_pvt *pvt) { - if (pvt->owner) { - ast_hangup(pvt->owner); - } + ast_hangup(pvt->owner); return 0; } @@ -1524,8 +1544,8 @@ } /*! - * \brief Read until '\r\n'. - * This function consumes the '\r\n' but does not add it to buf. + * \brief Read until \verbatim '\r\n'. \endverbatim + * This function consumes the \verbatim'\r\n'\endverbatim but does not add it to buf. */ static int rfcomm_read_until_crlf(int rsock, char **buf, size_t count, size_t *in_count) { @@ -1552,7 +1572,7 @@ /*! * \brief Read the remainder of an AT SMS prompt. - * \note the entire parsed string is '\r\n> ' + * \note the entire parsed string is \verbatim '\r\n> ' \endverbatim * * By the time this function is executed, only a ' ' is left to read. */ @@ -1570,7 +1590,7 @@ } /*! - * \brief Read until a \r\nOK\r\n message. + * \brief Read until a \verbatim \r\nOK\r\n \endverbatim message. */ static int rfcomm_read_until_ok(int rsock, char **buf, size_t count, size_t *in_count) { @@ -1667,7 +1687,7 @@ /*! * \brief Read the remainder of a +CMGR message. - * \note the entire parsed string is '+CMGR: ...\r\n...\r\n...\r\n...\r\nOK\r\n' + * \note the entire parsed string is \verbatim '+CMGR: ...\r\n...\r\n...\r\n...\r\nOK\r\n' \endverbatim */ static int rfcomm_read_cmgr(int rsock, char **buf, size_t count, size_t *in_count) { @@ -1686,7 +1706,7 @@ /*! * \brief Read and AT result code. - * \note the entire parsed string is '\r\n\r\n' + * \note the entire parsed string is \verbatim '\r\n\r\n' \endverbatim */ static int rfcomm_read_result(int rsock, char **buf, size_t count, size_t *in_count) { @@ -1724,7 +1744,7 @@ /*! * \brief Read the remainder of an AT command. - * \note the entire parsed string is '\r' + * \note the entire parsed string is \verbatim '\r' \endverbatim */ static int rfcomm_read_command(int rsock, char **buf, size_t count, size_t *in_count) { @@ -1758,8 +1778,8 @@ * \endverbatim * * These formats correspond to AT result codes, AT commands, and the AT SMS - * prompt respectively. When messages are read the leading and trailing '\r' - * and '\n' characters are discarded. If the given buffer is not large enough + * prompt respectively. When messages are read the leading and trailing \verbatim '\r' \endverbatim + * and \verbatim '\n' \endverbatim characters are discarded. If the given buffer is not large enough * to hold the response, what does not fit in the buffer will be dropped. * * \note The rfcomm connection to the device is asynchronous, so there is no @@ -2021,6 +2041,14 @@ return AT_VGS; } else if (at_match_prefix(buf, "+CUSD:")) { return AT_CUSD; + } else if (at_match_prefix(buf, "BUSY")) { + return AT_BUSY; + } else if (at_match_prefix(buf, "NO DIALTONE")) { + return AT_NO_DIALTONE; + } else if (at_match_prefix(buf, "NO CARRIER")) { + return AT_NO_CARRIER; + } else if (at_match_prefix(buf, "*ECAV:")) { + return AT_ECAM; } else { return AT_UNKNOWN; } @@ -2065,6 +2093,12 @@ return "SMS PROMPT"; case AT_CMS_ERROR: return "+CMS ERROR"; + case AT_BUSY: + return "BUSY"; + case AT_NO_DIALTONE: + return "NO DIALTONE"; + case AT_NO_CARRIER: + return "NO CARRIER"; /* at commands */ case AT_A: return "ATA"; @@ -2092,6 +2126,8 @@ return "AT+CIND=?"; case AT_CUSD: return "AT+CUSD"; + case AT_ECAM: + return "AT*ECAM"; } } @@ -2100,6 +2136,40 @@ * bluetooth handsfree profile helpers */ + /*! + * \brief Parse a ECAV event. + * \param hfp an hfp_pvt struct + * \param buf the buffer to parse (null terminated) + * \return -1 on error (parse error) or a ECAM value on success + * + * Example string: *ECAV: ,,[,] + * [,exitcause][,,] + * + * Example indicating busy: *ECAV: 1,7,1 + */ +static int hfp_parse_ecav(struct hfp_pvt *hfp, char *buf) +{ + int ccid = 0; + int ccstatus = 0; + int calltype = 0; + + if (!sscanf(buf, "*ECAV: %2d,%2d,%2d", &ccid, &ccstatus, &calltype)) { + ast_debug(1, "[%s] error parsing ECAV event '%s'\n", hfp->owner->id, buf); + return -1; + } + + return ccstatus; +} + +/*! + * \brief Enable Sony Erricson extensions / indications. + * \param hfp an hfp_pvt struct + */ +static int hfp_send_ecam(struct hfp_pvt *hfp) +{ + return rfcomm_write(hfp->rsock, "AT*ECAM=1\r"); +} + /*! * \brief Parse a CIEV event. * \param hfp an hfp_pvt struct @@ -2132,7 +2202,7 @@ * \brief Parse a CLIP event. * \param hfp an hfp_pvt struct * \param buf the buffer to parse (null terminated) - * @note buf will be modified when the CID string is parsed + * \note buf will be modified when the CID string is parsed * \return NULL on error (parse error) or a pointer to the caller id * information in buf */ @@ -2178,7 +2248,7 @@ * \brief Parse a CMTI notification. * \param hfp an hfp_pvt struct * \param buf the buffer to parse (null terminated) - * @note buf will be modified when the CMTI message is parsed + * \note buf will be modified when the CMTI message is parsed * \return -1 on error (parse error) or the index of the new sms message */ static int hfp_parse_cmti(struct hfp_pvt *hfp, char *buf) @@ -2203,7 +2273,7 @@ * \param from_number a pointer to a char pointer which will store the from * number * \param text a pointer to a char pointer which will store the message text - * @note buf will be modified when the CMGR message is parsed + * \note buf will be modified when the CMGR message is parsed * \retval -1 parse error * \retval 0 success */ @@ -2267,7 +2337,7 @@ * \brief Parse a CUSD answer. * \param hfp an hfp_pvt struct * \param buf the buffer to parse (null terminated) - * @note buf will be modified when the CUSD string is parsed + * \note buf will be modified when the CUSD string is parsed * \return NULL on error (parse error) or a pointer to the cusd message * information in buf */ @@ -3206,6 +3276,14 @@ break; case AT_CLIP: ast_debug(1, "[%s] caling line indication enabled\n", pvt->id); + if (hfp_send_ecam(pvt->hfp) || msg_queue_push(pvt, AT_OK, AT_ECAM)) { + ast_debug(1, "[%s] error enabling Sony Ericsson call monitoring extensions\n", pvt->id); + goto e_return; + } + + break; + case AT_ECAM: + ast_debug(1, "[%s] Sony Ericsson call monitoring is active on device\n", pvt->id); if (hfp_send_vgs(pvt->hfp, 15) || msg_queue_push(pvt, AT_OK, AT_VGS)) { ast_debug(1, "[%s] error synchronizing gain settings\n", pvt->id); goto e_return; @@ -3337,6 +3415,21 @@ ast_debug(1, "[%s] error setting CNMI\n", pvt->id); ast_debug(1, "[%s] no SMS support\n", pvt->id); break; + case AT_ECAM: + ast_debug(1, "[%s] Mobile does not support Sony Ericsson extensions\n", pvt->id); + + /* this is not a fatal error, let's continue with the initialization */ + + if (hfp_send_vgs(pvt->hfp, 15) || msg_queue_push(pvt, AT_OK, AT_VGS)) { + ast_debug(1, "[%s] error synchronizing gain settings\n", pvt->id); + goto e_return; + } + + pvt->timeout = -1; + pvt->hfp->initialized = 1; + ast_verb(3, "Bluetooth Device %s initialized and ready.\n", pvt->id); + + break; /* end initialization stuff */ case AT_A: @@ -3432,6 +3525,9 @@ case HFP_CIND_CALLSETUP_NONE: if (pvt->hfp->cind_state[pvt->hfp->cind_map.call] != HFP_CIND_CALL_ACTIVE) { if (pvt->owner) { + if (pvt->hfp->sent_alerting == 1) { + handle_response_busy(pvt); + } if (mbl_queue_hangup(pvt)) { ast_log(LOG_ERROR, "[%s] error queueing hangup, disconnectiong...\n", pvt->id); return -1; @@ -3450,6 +3546,7 @@ break; case HFP_CIND_CALLSETUP_OUTGOING: if (pvt->outgoing) { + pvt->hfp->sent_alerting = 0; ast_debug(1, "[%s] outgoing call\n", pvt->id); } else { ast_verb(3, "[%s] user dialed from handset, disconnecting\n", pvt->id); @@ -3460,6 +3557,7 @@ if (pvt->outgoing) { ast_debug(1, "[%s] remote alerting\n", pvt->id); mbl_queue_control(pvt, AST_CONTROL_RINGING); + pvt->hfp->sent_alerting = 1; } break; } @@ -3492,7 +3590,7 @@ ast_debug(1, "[%s] error parsing CLIP: %s\n", pvt->id, buf); } - if (!(chan = mbl_new(AST_STATE_RING, pvt, clip, NULL))) { + if (!(chan = mbl_new(AST_STATE_RING, pvt, clip, NULL, NULL))) { ast_log(LOG_ERROR, "[%s] unable to allocate channel for incoming call\n", pvt->id); hfp_send_chup(pvt->hfp); msg_queue_push(pvt, AT_OK, AT_CHUP); @@ -3582,7 +3680,7 @@ pvt->incoming_sms = 0; /* XXX this channel probably does not need to be associated with this pvt */ - if (!(chan = mbl_new(AST_STATE_DOWN, pvt, NULL, NULL))) { + if (!(chan = mbl_new(AST_STATE_DOWN, pvt, NULL, NULL, NULL))) { ast_debug(1, "[%s] error creating sms message channel, disconnecting\n", pvt->id); return -1; } @@ -3654,12 +3752,56 @@ return 0; } +/*! + * \brief Handle BUSY messages. + * \param pvt a mbl_pvt structure + * \retval 0 success + * \retval -1 error + */ +static int handle_response_busy(struct mbl_pvt *pvt) +{ + pvt->hangupcause = AST_CAUSE_USER_BUSY; + pvt->needchup = 1; + mbl_queue_control(pvt, AST_CONTROL_BUSY); + return 0; +} + +/*! + * \brief Handle NO DIALTONE messages. + * \param pvt a mbl_pvt structure + * \param buf a null terminated buffer containing an AT message + * \retval 0 success + * \retval -1 error + */ +static int handle_response_no_dialtone(struct mbl_pvt *pvt, char *buf) +{ + ast_verb(1, "[%s] mobile reports NO DIALTONE\n", pvt->id); + pvt->needchup = 1; + mbl_queue_control(pvt, AST_CONTROL_CONGESTION); + return 0; +} + +/*! + * \brief Handle NO CARRIER messages. + * \param pvt a mbl_pvt structure + * \param buf a null terminated buffer containing an AT message + * \retval 0 success + * \retval -1 error + */ +static int handle_response_no_carrier(struct mbl_pvt *pvt, char *buf) +{ + ast_verb(1, "[%s] mobile reports NO CARRIER\n", pvt->id); + pvt->needchup = 1; + mbl_queue_control(pvt, AST_CONTROL_CONGESTION); + return 0; +} + static void *do_monitor_phone(void *data) { struct mbl_pvt *pvt = (struct mbl_pvt *)data; struct hfp_pvt *hfp = pvt->hfp; - char buf[256]; + char buf[350]; int t; at_message_t at_msg; struct msg_queue_entry *entry; @@ -3812,6 +3954,40 @@ } ast_mutex_unlock(&pvt->lock); break; + case AT_BUSY: + ast_mutex_lock(&pvt->lock); + if (handle_response_busy(pvt)) { + ast_mutex_unlock(&pvt->lock); + goto e_cleanup; + } + ast_mutex_unlock(&pvt->lock); + break; + case AT_NO_DIALTONE: + ast_mutex_lock(&pvt->lock); + if (handle_response_no_dialtone(pvt, buf)) { + ast_mutex_unlock(&pvt->lock); + goto e_cleanup; + } + ast_mutex_unlock(&pvt->lock); + break; + case AT_NO_CARRIER: + ast_mutex_lock(&pvt->lock); + if (handle_response_no_carrier(pvt, buf)) { + ast_mutex_unlock(&pvt->lock); + goto e_cleanup; + } + ast_mutex_unlock(&pvt->lock); + break; + case AT_ECAM: + ast_mutex_lock(&pvt->lock); + if (hfp_parse_ecav(hfp, buf) == 7) { + if (handle_response_busy(pvt)) { + ast_mutex_unlock(&pvt->lock); + goto e_cleanup; + } + } + ast_mutex_unlock(&pvt->lock); + break; case AT_UNKNOWN: ast_debug(1, "[%s] ignoring unknown message: %s\n", pvt->id, buf); break; @@ -3963,7 +4139,7 @@ pvt->incoming = 1; - if (!(chan = mbl_new(AST_STATE_UP, pvt, NULL, NULL))) { + if (!(chan = mbl_new(AST_STATE_UP, pvt, NULL, NULL, NULL))) { ast_log(LOG_ERROR, "[%s] unable to allocate channel for incoming call\n", pvt->id); ast_mutex_unlock(&pvt->lock); goto e_cleanup; @@ -4520,7 +4696,8 @@ if (sdp_session) sdp_close(sdp_session); - mbl_tech.capabilities = ast_format_cap_destroy(mbl_tech.capabilities); + ao2_ref(mbl_tech.capabilities, -1); + mbl_tech.capabilities = NULL; return 0; } @@ -4529,11 +4706,11 @@ int dev_id, s; - if (!(mbl_tech.capabilities = ast_format_cap_alloc())) { + if (!(mbl_tech.capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) { return AST_MODULE_LOAD_DECLINE; } - ast_format_set(&prefformat, DEVICE_FRAME_FORMAT, 0); - ast_format_cap_add(mbl_tech.capabilities, &prefformat); + + ast_format_cap_append(mbl_tech.capabilities, DEVICE_FRAME_FORMAT, 0); /* Check if we have Bluetooth, no point loading otherwise... */ dev_id = hci_get_route(NULL); s = hci_open_dev(dev_id); @@ -4577,6 +4754,7 @@ } AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Bluetooth Mobile Device Channel Driver", + .support_level = AST_MODULE_SUPPORT_EXTENDED, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_CHANNEL_DRIVER, diff -Nru asterisk-11.7.0~dfsg/addons/chan_ooh323.c asterisk-13.1.0~dfsg/addons/chan_ooh323.c --- asterisk-11.7.0~dfsg/addons/chan_ooh323.c 2013-05-31 10:34:20.000000000 +0000 +++ asterisk-13.1.0~dfsg/addons/chan_ooh323.c 2014-10-15 09:59:09.000000000 +0000 @@ -71,7 +71,7 @@ /* Channel Definition */ static struct ast_channel *ooh323_request(const char *type, struct ast_format_cap *cap, - const struct ast_channel *requestor, const char *data, int *cause); + const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause); static int ooh323_digit_begin(struct ast_channel *ast, char digit); static int ooh323_digit_end(struct ast_channel *ast, char digit, unsigned int duration); static int ooh323_call(struct ast_channel *ast, const char *dest, int timeout); @@ -92,11 +92,6 @@ static void ooh323_get_codec(struct ast_channel *chan, struct ast_format_cap *result); void setup_rtp_remote(ooCallData *call, const char *remoteIp, int remotePort); -static struct ast_udptl *ooh323_get_udptl_peer(struct ast_channel *chan); -static int ooh323_set_udptl_peer(struct ast_channel *chan, struct ast_udptl *udptl); - -static void print_codec_to_cli(int fd, struct ast_codec_pref *pref); - struct ooh323_peer *find_friend(const char *name, int port); @@ -117,7 +112,6 @@ .fixup = ooh323_fixup, .send_html = 0, .queryoption = ooh323_queryoption, - .bridge = ast_rtp_instance_bridge, /* XXX chan unlocked ? */ .early_bridge = ast_rtp_instance_early_bridge, .func_channel_read = function_ooh323_read, .func_channel_write = function_ooh323_write, @@ -131,13 +125,6 @@ .get_codec = ooh323_get_codec, }; -static struct ast_udptl_protocol ooh323_udptl = { - .type = "H323", - .get_udptl_info = ooh323_get_udptl_peer, - .set_udptl_peer = ooh323_set_udptl_peer, -}; - - struct ooh323_user; @@ -185,10 +172,9 @@ char callee_url[AST_MAX_EXTENSION]; int port; - struct ast_format readformat; /* negotiated read format */ - struct ast_format writeformat; /* negotiated write format */ + struct ast_format *readformat; /* negotiated read format */ + struct ast_format *writeformat; /* negotiated write format */ struct ast_format_cap *cap; - struct ast_codec_pref prefs; int dtmfmode; int dtmfcodec; char exten[AST_MAX_EXTENSION]; /* Requested extension */ @@ -206,6 +192,7 @@ char rtpmaskstr[120]; int rtdrcount, rtdrinterval; /* roundtripdelayreq */ int faststart, h245tunneling; /* faststart & h245 tunneling */ + int aniasdni; /* use dialed number as answering identification */ struct ooh323_pvt *next; /* Next entity */ } *iflist = NULL; @@ -222,7 +209,6 @@ char accountcode[20]; int amaflags; struct ast_format_cap *cap; - struct ast_codec_pref prefs; int dtmfmode; int dtmfcodec; int faxdetect; @@ -238,6 +224,7 @@ int directrtp; int earlydirect; int g729onlyA; + int aniasdni; struct ooh323_user *next; }; @@ -248,7 +235,6 @@ unsigned outgoinglimit; unsigned outUse; struct ast_format_cap *cap; - struct ast_codec_pref prefs; char accountcode[20]; int amaflags; int dtmfmode; @@ -294,8 +280,6 @@ static int usecnt = 0; AST_MUTEX_DEFINE_STATIC(usecnt_lock); -AST_MUTEX_DEFINE_STATIC(ooh323c_cmd_lock); - static long callnumber = 0; AST_MUTEX_DEFINE_STATIC(ooh323c_cn_lock); @@ -318,7 +302,6 @@ static char gCallerID[AST_MAX_EXTENSION] = ""; static struct ooAliases *gAliasList; static struct ast_format_cap *gCap; -static struct ast_codec_pref gPrefs; static int gDTMFMode = H323_DTMF_RFC2833; static int gDTMFCodec = 101; static int gFAXdetect = FAXDETECT_CNG; @@ -345,6 +328,7 @@ static int gTRCLVL = OOTRCLVLERR; static int gRTDRCount = 0, gRTDRInterval = 0; static int gNat = FALSE; +static int gANIasDNI = 0; static int t35countrycode = 0; static int t35extensions = 0; @@ -373,40 +357,50 @@ static struct ast_channel *ooh323_new(struct ooh323_pvt *i, int state, - const char *host, struct ast_format_cap *cap, const char *linkedid) + const char *host, struct ast_format_cap *cap, + const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor) { + struct ast_format_cap *caps = NULL; struct ast_channel *ch = NULL; - struct ast_format tmpfmt; + struct ast_format *tmpfmt = NULL; int features = 0; if (gH323Debug) { ast_verb(0, "--- ooh323_new - %s\n", host); } - ast_format_clear(&tmpfmt); + caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); + /* Don't hold a h323 pvt lock while we allocate a channel */ ast_mutex_unlock(&i->lock); ast_mutex_lock(&ooh323c_cn_lock); ch = ast_channel_alloc(1, state, i->callerid_num, i->callerid_name, - i->accountcode, i->exten, i->context, linkedid, i->amaflags, + i->accountcode, i->exten, i->context, assignedids, requestor, i->amaflags, "OOH323/%s-%ld", host, callnumber); callnumber++; ast_mutex_unlock(&ooh323c_cn_lock); ast_mutex_lock(&i->lock); - if (ch) { - ast_channel_lock(ch); + if (ch && caps) { ast_channel_tech_set(ch, &ooh323_tech); - if (cap) - ast_best_codec(cap, &tmpfmt); - if (!tmpfmt.id) - ast_codec_pref_index(&i->prefs, 0, &tmpfmt); - - ast_format_cap_add(ast_channel_nativeformats(ch), &tmpfmt); - ast_format_copy(ast_channel_rawwriteformat(ch), &tmpfmt); - ast_format_copy(ast_channel_rawreadformat(ch), &tmpfmt); + if (cap) { + tmpfmt = ast_format_cap_get_format(cap, 0); + } + if (!tmpfmt) { + tmpfmt = ast_format_cap_get_format(i->cap, 0); + } + + ast_format_cap_append(caps, tmpfmt, 0); + ast_channel_nativeformats_set(ch, caps); + ao2_ref(caps, -1); + + ast_channel_set_rawwriteformat(ch, tmpfmt); + ast_channel_set_rawreadformat(ch, tmpfmt); + ast_set_write_format(ch, tmpfmt); + ast_set_read_format(ch, tmpfmt); + ao2_ref(tmpfmt, -1); ast_jb_configure(ch, &global_jbconf); @@ -414,8 +408,6 @@ ast_channel_rings_set(ch, 1); ast_channel_adsicpe_set(ch, AST_ADSI_UNAVAILABLE); - ast_set_write_format(ch, &tmpfmt); - ast_set_read_format(ch, &tmpfmt); ast_channel_tech_pvt_set(ch, i); i->owner = ch; ast_module_ref(myself); @@ -492,12 +484,13 @@ } if (ch) { - manager_event(EVENT_FLAG_SYSTEM, "ChannelUpdate", - "Channel: %s\r\nChanneltype: %s\r\n" - "CallRef: %d\r\n", ast_channel_name(ch), "OOH323", i->call_reference); + ast_publish_channel_state(ch); + } - } else + } else { + ao2_cleanup(caps); ast_log(LOG_WARNING, "Unable to allocate channel structure\n"); + } if(ch) ast_channel_unlock(ch); @@ -523,7 +516,7 @@ ast_log(LOG_ERROR, "Couldn't allocate private ooh323 structure\n"); return NULL; } - if (!(pvt->cap = ast_format_cap_alloc_nolock())) { + if (!(pvt->cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) { ast_free(pvt); ast_log(LOG_ERROR, "Couldn't allocate private ooh323 structure\n"); return NULL; @@ -557,8 +550,9 @@ ast_copy_string(pvt->accountcode, gAccountcode, sizeof(pvt->accountcode)); pvt->amaflags = gAMAFLAGS; - ast_format_cap_copy(pvt->cap, gCap); - memcpy(&pvt->prefs, &gPrefs, sizeof(pvt->prefs)); + ast_format_cap_append_from_cap(pvt->cap, gCap, AST_MEDIA_TYPE_UNKNOWN); + + pvt->aniasdni = gANIasDNI; ast_mutex_unlock(&pvt->lock); /* Add to interface list */ @@ -579,24 +573,24 @@ Possible data values - peername, exten/peername, exten@ip */ static struct ast_channel *ooh323_request(const char *type, struct ast_format_cap *cap, - const struct ast_channel *requestor, const char *data, int *cause) + const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause) { + struct ast_str *codec_buf = ast_str_alloca(64); struct ast_channel *chan = NULL; struct ooh323_pvt *p = NULL; struct ooh323_peer *peer = NULL; char *dest = NULL; char *ext = NULL; char tmp[256]; - char formats[FORMAT_STRING_SIZE]; int port = 0; - if (gH323Debug) - ast_verb(0, "--- ooh323_request - data %s format %s\n", data, - ast_getformatname_multiple(formats,FORMAT_STRING_SIZE,cap)); + if (gH323Debug) { + ast_verb(0, "--- ooh323_request - data %s format %s\n", data, ast_format_cap_get_names(cap, &codec_buf)); + } - if (!(ast_format_cap_has_type(cap, AST_FORMAT_TYPE_AUDIO))) { - ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%s'\n", ast_getformatname_multiple(formats,FORMAT_STRING_SIZE,cap)); + if (!(ast_format_cap_has_type(cap, AST_MEDIA_TYPE_AUDIO))) { + ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%s'\n", ast_format_cap_get_names(cap, &codec_buf)); return NULL; } @@ -660,8 +654,7 @@ if (ext) ast_copy_string(p->exten, ext, sizeof(p->exten)); - ast_format_cap_copy(p->cap, peer->cap); - memcpy(&p->prefs, &peer->prefs, sizeof(struct ast_codec_pref)); + ast_format_cap_append_from_cap(p->cap, peer->cap, AST_MEDIA_TYPE_UNKNOWN); p->g729onlyA = peer->g729onlyA; p->dtmfmode |= peer->dtmfmode; p->dtmfcodec = peer->dtmfcodec; @@ -694,7 +687,7 @@ ooh323_destroy(p); ast_mutex_unlock(&iflock); return NULL; - } else if (gH323ep.gkClient && gH323ep.gkClient->state != GkClientRegistered) { + } else if (!gH323ep.gkClient || (gH323ep.gkClient && gH323ep.gkClient->state != GkClientRegistered)) { ast_log(LOG_ERROR, "Gatekeeper client is configured but not registered\n"); *cause = AST_CAUSE_NORMAL_TEMPORARY_FAILURE; return NULL; @@ -706,7 +699,7 @@ p->t38support = gT38Support; p->rtptimeout = gRTPTimeout; p->nat = gNat; - ast_format_cap_copy(p->cap, gCap); + ast_format_cap_append_from_cap(p->cap, gCap, AST_MEDIA_TYPE_UNKNOWN); p->rtdrinterval = gRTDRInterval; p->rtdrcount = gRTDRCount; p->faststart = gFastStart; @@ -714,7 +707,6 @@ p->directrtp = gDirectRTP; p->earlydirect = gEarlyDirect; - memcpy(&p->prefs, &gPrefs, sizeof(struct ast_codec_pref)); p->username = strdup(dest); p->host = strdup(dest); @@ -728,7 +720,7 @@ chan = ooh323_new(p, AST_STATE_DOWN, p->username, cap, - requestor ? ast_channel_linkedid(requestor) : NULL); + assignedids, requestor); ast_mutex_unlock(&p->lock); @@ -748,17 +740,13 @@ return NULL; } - ast_mutex_unlock(&p->lock); - ast_mutex_lock(&ooh323c_cmd_lock); ast_cond_init(&p->rtpcond, NULL); ooMakeCall(data, p->callToken, AST_MAX_EXTENSION, NULL); - ast_mutex_lock(&p->lock); if (!p->rtp) { ast_cond_wait(&p->rtpcond, &p->lock); } ast_mutex_unlock(&p->lock); ast_cond_destroy(&p->rtpcond); - ast_mutex_unlock(&ooh323c_cmd_lock); } restart_monitor(); @@ -1130,7 +1118,7 @@ ast_channel_lock(ast); if (!p->alertsent) { if (gH323Debug) { - ast_debug(1, "Sending forced ringback for %s, res = %d\n", + ast_debug(1, "Sending forced ringback for %s, res = %u\n", callToken, ooManualRingback(callToken)); } else { ooManualRingback(callToken); @@ -1177,7 +1165,6 @@ { struct ooh323_pvt *p = ast_channel_tech_pvt(ast); int res = 0; - char buf[256]; if (p) { ast_mutex_lock(&p->lock); @@ -1185,7 +1172,7 @@ p->lastrtptx = time(NULL); if (f->frametype == AST_FRAME_MODEM) { - ast_debug(1, "Send UDPTL %d/%d len %d for %s\n", + ast_debug(1, "Send UDPTL %u/%d len %d for %s\n", f->frametype, f->subclass.integer, f->datalen, ast_channel_name(ast)); if (p->udptl) res = ast_udptl_write(p->udptl, f); @@ -1203,16 +1190,17 @@ } - if (!(ast_format_cap_iscompatible(ast_channel_nativeformats(ast), &f->subclass.format))) { - if (!(ast_format_cap_is_empty(ast_channel_nativeformats(ast)))) { + if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), f->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) { + if (ast_format_cap_count(ast_channel_nativeformats(ast))) { + struct ast_str *codec_buf = ast_str_alloca(64); ast_log(LOG_WARNING, "Asked to transmit frame type %s, while native formats is %s (read/write = %s/%s)\n", - ast_getformatname(&f->subclass.format), - ast_getformatname_multiple(buf, sizeof(buf), ast_channel_nativeformats(ast)), - ast_getformatname(ast_channel_readformat(ast)), - ast_getformatname(ast_channel_writeformat(ast))); + ast_format_get_name(f->subclass.format), + ast_format_cap_get_names(ast_channel_nativeformats(ast), &codec_buf), + ast_format_get_name(ast_channel_readformat(ast)), + ast_format_get_name(ast_channel_writeformat(ast))); - ast_set_write_format(ast, &f->subclass.format); + ast_set_write_format(ast, f->subclass.format); } else { /* ast_set_write_format(ast, f->subclass); ast->nativeformats = f->subclass; */ @@ -1230,7 +1218,7 @@ ast_mutex_unlock(&p->lock); return 0; } else { - ast_log(LOG_WARNING, "Can't send %d type frames with OOH323 write\n", + ast_log(LOG_WARNING, "Can't send %u type frames with OOH323 write\n", f->frametype); ast_mutex_unlock(&p->lock); return 0; @@ -1294,7 +1282,7 @@ if (ast_channel_state(ast) != AST_STATE_UP) { if (!p->progsent) { if (gH323Debug) { - ast_debug(1, "Sending manual progress for %s, res = %d\n", callToken, + ast_debug(1, "Sending manual progress for %s, res = %u\n", callToken, ooManualProgress(callToken)); } else { ooManualProgress(callToken); @@ -1307,7 +1295,7 @@ if (ast_channel_state(ast) == AST_STATE_RING || ast_channel_state(ast) == AST_STATE_RINGING) { if (!p->alertsent) { if (gH323Debug) { - ast_debug(1, "Sending manual ringback for %s, res = %d\n", + ast_debug(1, "Sending manual ringback for %s, res = %u\n", callToken, ooManualRingback(callToken)); } else { @@ -1406,7 +1394,9 @@ } break; - case AST_CONTROL_PROCEEDING: + case AST_CONTROL_PROCEEDING: + case AST_CONTROL_PVT_CAUSE_CODE: + case AST_CONTROL_MASQUERADE_NOTIFY: case -1: break; default: @@ -1520,11 +1510,10 @@ void ooh323_set_write_format(ooCallData *call, struct ast_format *fmt, int txframes) { struct ooh323_pvt *p = NULL; - char formats[FORMAT_STRING_SIZE]; if (gH323Debug) ast_verb(0, "--- ooh323_update_writeformat %s/%d\n", - ast_getformatname(fmt), txframes); + ast_format_get_name(fmt), txframes); p = find_call(call); if (!p) { @@ -1534,9 +1523,17 @@ ast_mutex_lock(&p->lock); - ast_format_copy(&(p->writeformat), fmt); + ao2_replace(p->writeformat, fmt); if (p->owner) { + struct ast_format_cap *caps; + + caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); + if (!caps) { + ast_log(LOG_ERROR, "Could not allocate capabilities structure\n"); + return; + } + while (p->owner && ast_channel_trylock(p->owner)) { ast_debug(1,"Failed to grab lock, trying again\n"); DEADLOCK_AVOIDANCE(&p->lock); @@ -1544,15 +1541,16 @@ if (!p->owner) { ast_mutex_unlock(&p->lock); ast_log(LOG_ERROR, "Channel has no owner\n"); + ao2_ref(caps, -1); return; } - if (gH323Debug) - ast_verb(0, "Writeformat before update %s/%s\n", - ast_getformatname(ast_channel_writeformat(p->owner)), - ast_getformatname_multiple(formats, sizeof(formats), ast_channel_nativeformats(p->owner))); - if (txframes) - ast_codec_pref_setsize(&p->prefs, fmt, txframes); - ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(p->rtp), p->rtp, &p->prefs); + if (gH323Debug) { + struct ast_str *codec_buf = ast_str_alloca(64); + ast_verb(0, "Writeformat before update %s/%s\n", + ast_format_get_name(ast_channel_writeformat(p->owner)), + ast_format_cap_get_names(ast_channel_nativeformats(p->owner), &codec_buf)); + } + if (p->dtmfmode & H323_DTMF_RFC2833 && p->dtmfcodec) { ast_rtp_codecs_payloads_set_rtpmap_type(ast_rtp_instance_get_codecs(p->rtp), p->rtp, p->dtmfcodec, "audio", "telephone-event", 0); @@ -1562,7 +1560,12 @@ p->rtp, p->dtmfcodec, "audio", "cisco-telephone-event", 0); } - ast_format_cap_set(ast_channel_nativeformats(p->owner), fmt); + if (txframes) { + ast_format_cap_set_framing(caps, txframes); + } + ast_format_cap_append(caps, fmt, 0); + ast_channel_nativeformats_set(p->owner, caps); + ao2_ref(caps, -1); ast_set_write_format(p->owner, ast_channel_writeformat(p->owner)); ast_set_read_format(p->owner, ast_channel_readformat(p->owner)); ast_channel_unlock(p->owner); @@ -1582,7 +1585,7 @@ if (gH323Debug) ast_verb(0, "--- ooh323_update_readformat %s\n", - ast_getformatname(fmt)); + ast_format_get_name(fmt)); p = find_call(call); if (!p) { @@ -1592,9 +1595,17 @@ ast_mutex_lock(&p->lock); - ast_format_copy(&(p->readformat), fmt); + ao2_replace(p->readformat, fmt); if (p->owner) { + struct ast_format_cap *caps; + + caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); + if (!caps) { + ast_log(LOG_ERROR, "Could not allocate capabilities structure\n"); + return; + } + while (p->owner && ast_channel_trylock(p->owner)) { ast_debug(1,"Failed to grab lock, trying again\n"); DEADLOCK_AVOIDANCE(&p->lock); @@ -1602,14 +1613,18 @@ if (!p->owner) { ast_mutex_unlock(&p->lock); ast_log(LOG_ERROR, "Channel has no owner\n"); + ao2_ref(caps, -1); return; } - if (gH323Debug) - ast_verb(0, "Readformat before update %s\n", - ast_getformatname(ast_channel_readformat(p->owner))); - ast_format_cap_set(ast_channel_nativeformats(p->owner), fmt); - ast_set_read_format(p->owner, ast_channel_readformat(p->owner)); + if (gH323Debug) { + ast_verb(0, "Readformat before update %s\n", + ast_format_get_name(ast_channel_readformat(p->owner))); + } + ast_format_cap_append(caps, fmt, 0); + ast_channel_nativeformats_set(p->owner, caps); + ao2_ref(caps, -1); + ast_set_read_format(p->owner, ast_channel_readformat(p->owner)); ast_channel_unlock(p->owner); } else ast_log(LOG_ERROR, "No owner found\n"); @@ -1866,9 +1881,8 @@ ast_copy_string(p->context, user->context, sizeof(p->context)); ast_copy_string(p->accountcode, user->accountcode, sizeof(p->accountcode)); p->amaflags = user->amaflags; - ast_format_cap_copy(p->cap, user->cap); + ast_format_cap_append_from_cap(p->cap, user->cap, AST_MEDIA_TYPE_UNKNOWN); p->g729onlyA = user->g729onlyA; - memcpy(&p->prefs, &user->prefs, sizeof(struct ast_codec_pref)); p->dtmfmode |= user->dtmfmode; p->dtmfcodec = user->dtmfcodec; p->faxdetect = user->faxdetect; @@ -1900,6 +1914,9 @@ p->rtdrcount = user->rtdrcount; p->rtdrinterval = user->rtdrinterval; } + + p->aniasdni = user->aniasdni; + if (user->incominglimit) user->inUse++; ast_mutex_unlock(&user->lock); } else { @@ -1923,15 +1940,19 @@ } } - ooh323c_set_capability_for_call(call, &p->prefs, p->cap, p->dtmfmode, p->dtmfcodec, + ooh323c_set_capability_for_call(call, p->cap, p->dtmfmode, p->dtmfcodec, p->t38support, p->g729onlyA); /* Incoming call */ - c = ooh323_new(p, AST_STATE_RING, p->username, 0, NULL); + c = ooh323_new(p, AST_STATE_RING, p->username, 0, NULL, NULL); if(!c) { ast_mutex_unlock(&p->lock); ast_log(LOG_ERROR, "Could not create ast_channel\n"); return -1; } + + if (p->aniasdni) { + ooCallSetCallerId(call, p->exten); + } if (!configure_local_rtp(p, call)) { ast_mutex_unlock(&p->lock); ast_log(LOG_ERROR, "Couldn't create rtp structure\n"); @@ -2093,13 +2114,14 @@ } if (gH323Debug) { - char prefsBuf[256]; - ast_codec_pref_string(&p->prefs, prefsBuf, sizeof(prefsBuf)); + struct ast_str *codec_buf = ast_str_alloca(64); + ast_verb(0, " Outgoing call %s(%s) - Codec prefs - %s\n", - p->username?p->username:"NULL", call->callToken, prefsBuf); + p->username?p->username:"NULL", call->callToken, + ast_format_cap_get_names(p->cap, &codec_buf)); } - ooh323c_set_capability_for_call(call, &p->prefs, p->cap, + ooh323c_set_capability_for_call(call, p->cap, p->dtmfmode, p->dtmfcodec, p->t38support, p->g729onlyA); configure_local_rtp(p, call); @@ -2155,9 +2177,8 @@ } ast_queue_control(c, AST_CONTROL_ANSWER); - ast_channel_unlock(p->owner); - manager_event(EVENT_FLAG_SYSTEM,"ChannelUpdate","Channel: %s\r\nChanneltype: %s\r\n" - "CallRef: %d\r\n", ast_channel_name(c), "OOH323", p->call_reference); + ast_publish_channel_state(c); + ast_channel_unlock(p->owner); } ast_mutex_unlock(&p->lock); @@ -2208,6 +2229,10 @@ ast_module_unref(myself); } + if (!p->rtp) { + ast_cond_signal(&p->rtpcond); + } + ast_set_flag(p, H323_NEEDDESTROY); ooh323c_stop_call_thread(call); @@ -2286,7 +2311,7 @@ if(peer->url) free(peer->url); if(peer->e164) free(peer->e164); - peer->cap = ast_format_cap_destroy(peer->cap); + ao2_cleanup(peer->cap); free(peer); } @@ -2307,14 +2332,13 @@ user = ast_calloc(1,sizeof(struct ooh323_user)); if (user) { memset(user, 0, sizeof(struct ooh323_user)); - if (!(user->cap = ast_format_cap_alloc())) { + if (!(user->cap = ast_format_cap_alloc(0))) { ast_free(user); return NULL; } ast_mutex_init(&user->lock); ast_copy_string(user->name, name, sizeof(user->name)); - ast_format_cap_copy(user->cap, gCap); - memcpy(&user->prefs, &gPrefs, sizeof(user->prefs)); + ast_format_cap_append_from_cap(user->cap, gCap, AST_MEDIA_TYPE_UNKNOWN); user->rtptimeout = gRTPTimeout; user->nat = gNat; user->dtmfmode = gDTMFMode; @@ -2369,17 +2393,15 @@ sizeof(user->rtpmaskstr)); } else user->rtpmask = NULL; } else if (!strcasecmp(v->name, "disallow")) { - ast_parse_allow_disallow(&user->prefs, - user->cap, v->value, 0); + ast_format_cap_update_by_allow_disallow(user->cap, v->value, 0); } else if (!strcasecmp(v->name, "allow")) { const char* tcodecs = v->value; if (!strcasecmp(v->value, "all")) { tcodecs = "ulaw,alaw,g729,g723,gsm"; } - ast_parse_allow_disallow(&user->prefs, - user->cap, tcodecs, 1); + ast_format_cap_update_by_allow_disallow(user->cap, tcodecs, 1); } else if (!strcasecmp(v->name, "amaflags")) { - user->amaflags = ast_cdr_amaflags2int(v->value); + user->amaflags = ast_channel_string2amaflag(v->value); } else if (!strcasecmp(v->name, "ip") || !strcasecmp(v->name, "host")) { struct ast_sockaddr p; if (!ast_parse_arg(v->value, PARSE_ADDR, &p)) { @@ -2434,6 +2456,8 @@ user->t38support = T38_FAXGW; else if (!strcasecmp(v->value, "yes")) user->t38support = T38_ENABLED; + } else if (!strcasecmp(v->name, "aniasdni")) { + user->aniasdni = ast_true(v->value); } v = v->next; } @@ -2455,14 +2479,13 @@ peer = ast_calloc(1, sizeof(*peer)); if (peer) { memset(peer, 0, sizeof(struct ooh323_peer)); - if (!(peer->cap = ast_format_cap_alloc())) { + if (!(peer->cap = ast_format_cap_alloc(0))) { ast_free(peer); return NULL; } ast_mutex_init(&peer->lock); ast_copy_string(peer->name, name, sizeof(peer->name)); - ast_format_cap_copy(peer->cap, gCap); - memcpy(&peer->prefs, &gPrefs, sizeof(peer->prefs)); + ast_format_cap_append_from_cap(peer->cap, gCap, AST_MEDIA_TYPE_UNKNOWN); peer->rtptimeout = gRTPTimeout; peer->nat = gNat; ast_copy_string(peer->accountcode, gAccountcode, sizeof(peer->accountcode)); @@ -2490,11 +2513,23 @@ return NULL; } } else if (!strcasecmp(v->name, "e164")) { - if (!(peer->e164 = ast_strdup(v->value))) { - ast_log(LOG_ERROR, "Could not allocate memory for e164 of " + int valid = 1; + const char *tmp; + for(tmp = v->value; *tmp; tmp++) { + if (!isdigit(*tmp)) { + valid = 0; + break; + } + } + if (valid) { + if (!(peer->e164 = ast_strdup(v->value))) { + ast_log(LOG_ERROR, "Could not allocate memory for e164 of " "peer %s\n", name); - ooh323_delete_peer(peer); - return NULL; + ooh323_delete_peer(peer); + return NULL; + } + } else { + ast_log(LOG_ERROR, "Invalid e164: %s for peer %s\n", v->value, name); } } else if (!strcasecmp(v->name, "email")) { if (!(peer->email = ast_strdup(v->value))) { @@ -2553,17 +2588,15 @@ sizeof(peer->rtpmaskstr)); } else peer->rtpmask = NULL; } else if (!strcasecmp(v->name, "disallow")) { - ast_parse_allow_disallow(&peer->prefs, peer->cap, - v->value, 0); + ast_format_cap_update_by_allow_disallow(peer->cap, v->value, 0); } else if (!strcasecmp(v->name, "allow")) { const char* tcodecs = v->value; if (!strcasecmp(v->value, "all")) { tcodecs = "ulaw,alaw,g729,g723,gsm"; } - ast_parse_allow_disallow(&peer->prefs, peer->cap, - tcodecs, 1); + ast_format_cap_update_by_allow_disallow(peer->cap, tcodecs, 1); } else if (!strcasecmp(v->name, "amaflags")) { - peer->amaflags = ast_cdr_amaflags2int(v->value); + peer->amaflags = ast_channel_string2amaflag(v->value); } else if (!strcasecmp(v->name, "roundtrip")) { sscanf(v->value, "%d,%d", &peer->rtdrcount, &peer->rtdrinterval); } else if (!strcasecmp(v->name, "dtmfmode")) { @@ -2625,6 +2658,9 @@ static int ooh323_do_reload(void) { + struct ooAliases * pNewAlias = NULL; + struct ooh323_peer *peer = NULL; + if (gH323Debug) { ast_verb(0, "--- ooh323_do_reload\n"); } @@ -2644,6 +2680,46 @@ ooGkClientStart(gH323ep.gkClient); } + /* Set aliases if any */ + if (gH323Debug) { + ast_verb(0, "updating local aliases\n"); + } + + for (pNewAlias = gAliasList; pNewAlias; pNewAlias = pNewAlias->next) { + switch (pNewAlias->type) { + case T_H225AliasAddress_h323_ID: + ooH323EpAddAliasH323ID(pNewAlias->value); + break; + case T_H225AliasAddress_dialedDigits: + ooH323EpAddAliasDialedDigits(pNewAlias->value); + break; + case T_H225AliasAddress_email_ID: + ooH323EpAddAliasEmailID(pNewAlias->value); + break; + default: + ; + } + } + + ast_mutex_lock(&peerl.lock); + peer = peerl.peers; + while (peer) { + if(peer->h323id) { + ooH323EpAddAliasH323ID(peer->h323id); + } + if(peer->email) { + ooH323EpAddAliasEmailID(peer->email); + } + if(peer->e164) { + ooH323EpAddAliasDialedDigits(peer->e164); + } + if(peer->url) { + ooH323EpAddAliasURLID(peer->url); + } + peer = peer->next; + } + ast_mutex_unlock(&peerl.lock); + if (gH323Debug) { ast_verb(0, "+++ ooh323_do_reload\n"); } @@ -2699,7 +2775,6 @@ struct ooh323_peer *peer = NULL; char *cat; const char *utype; - struct ast_format tmpfmt; if (gH323Debug) ast_verb(0, "--- reload_config\n"); @@ -2727,6 +2802,7 @@ free(prev); } gAliasList = NULL; + ooH323EpClearAllAliases(); } /* Inintialize everything to default */ @@ -2734,8 +2810,8 @@ gPort = 1720; gIP[0] = '\0'; strcpy(gCallerID, DEFAULT_H323ID); - ast_format_cap_set(gCap, ast_format_set(&tmpfmt, AST_FORMAT_ALAW, 0)); - memset(&gPrefs, 0, sizeof(struct ast_codec_pref)); + ast_format_cap_remove_by_type(gCap, AST_MEDIA_TYPE_UNKNOWN); + ast_format_cap_append(gCap, ast_format_ulaw, 0); gDTMFMode = H323_DTMF_RFC2833; gDTMFCodec = 101; gFAXdetect = FAXDETECT_CNG; @@ -2843,17 +2919,29 @@ gAliasList = pNewAlias; pNewAlias = NULL; } else if (!strcasecmp(v->name, "e164")) { - pNewAlias = ast_calloc(1, sizeof(struct ooAliases)); - if (!pNewAlias) { - ast_log(LOG_ERROR, "Failed to allocate memory for e164 alias\n"); - ast_config_destroy(cfg); - return 1; + int valid = 1; + const char *tmp; + for(tmp = v->value; *tmp; tmp++) { + if (!isdigit(*tmp)) { + valid = 0; + break; + } + } + if (valid) { + pNewAlias = ast_calloc(1, sizeof(struct ooAliases)); + if (!pNewAlias) { + ast_log(LOG_ERROR, "Failed to allocate memory for e164 alias\n"); + ast_config_destroy(cfg); + return 1; + } + pNewAlias->type = T_H225AliasAddress_dialedDigits; + pNewAlias->value = strdup(v->value); + pNewAlias->next = gAliasList; + gAliasList = pNewAlias; + pNewAlias = NULL; + } else { + ast_log(LOG_ERROR, "Invalid e164: %s\n", v->value); } - pNewAlias->type = T_H225AliasAddress_dialedDigits; - pNewAlias->value = strdup(v->value); - pNewAlias->next = gAliasList; - gAliasList = pNewAlias; - pNewAlias = NULL; } else if (!strcasecmp(v->name, "email")) { pNewAlias = ast_calloc(1, sizeof(struct ooAliases)); if (!pNewAlias) { @@ -2900,7 +2988,7 @@ gNat = ast_true(v->value); } else if (!strcasecmp(v->name, "rtptimeout")) { gRTPTimeout = atoi(v->value); - if (gRTPTimeout <= 0) + if (gRTPTimeout < 0) gRTPTimeout = 60; } else if (!strcasecmp(v->name, "tos")) { if (sscanf(v->value, "%30i", &format) == 1) @@ -2920,17 +3008,17 @@ "'lowdelay', 'throughput', 'reliability', " "'mincost', or 'none'\n", v->lineno); } else if (!strcasecmp(v->name, "amaflags")) { - gAMAFLAGS = ast_cdr_amaflags2int(v->value); + gAMAFLAGS = ast_channel_string2amaflag(v->value); } else if (!strcasecmp(v->name, "accountcode")) { ast_copy_string(gAccountcode, v->value, sizeof(gAccountcode)); } else if (!strcasecmp(v->name, "disallow")) { - ast_parse_allow_disallow(&gPrefs, gCap, v->value, 0); + ast_format_cap_update_by_allow_disallow(gCap, v->value, 0); } else if (!strcasecmp(v->name, "allow")) { const char* tcodecs = v->value; if (!strcasecmp(v->value, "all")) { tcodecs = "ulaw,alaw,g729,g723,gsm"; } - ast_parse_allow_disallow(&gPrefs, gCap, tcodecs, 1); + ast_format_cap_update_by_allow_disallow(gCap, tcodecs, 1); } else if (!strcasecmp(v->name, "dtmfmode")) { if (!strcasecmp(v->value, "inband")) gDTMFMode = H323_DTMF_INBAND; @@ -2985,6 +3073,8 @@ } else if (!strcasecmp(v->name, "tracelevel")) { gTRCLVL = atoi(v->value); ooH323EpSetTraceLevel(gTRCLVL); + } else if (!strcasecmp(v->name, "aniasdni")) { + gANIasDNI = ast_true(v->value); } v = v->next; } @@ -3080,9 +3170,6 @@ peer->h245tunneling?"yes":"no"); ast_cli(a->fd, "%-15s%s\n", "DirectRTP", peer->directrtp ? "yes" : "no"); ast_cli(a->fd, "%-15s%s\n", "EarlyDirectRTP", peer->earlydirect ? "yes" : "no"); - ast_cli(a->fd, "%-15.15s%s", "Format Prefs: ", "("); - print_codec_to_cli(a->fd, &peer->prefs); - ast_cli(a->fd, ")\n"); ast_cli(a->fd, "%-15.15s", "DTMF Mode: "); if (peer->dtmfmode & H323_DTMF_CISCO) { ast_cli(a->fd, "%s\n", "cisco"); @@ -3120,9 +3207,9 @@ } ast_cli(a->fd, "%-15.15s%s\n", "AccountCode: ", peer->accountcode); - ast_cli(a->fd, "%-15.15s%s\n", "AMA flags: ", ast_cdr_flags2str(peer->amaflags)); + ast_cli(a->fd, "%-15.15s%s\n", "AMA flags: ", ast_channel_amaflags2string(peer->amaflags)); ast_cli(a->fd, "%-15.15s%s\n", "IP:Port: ", ip_port); - ast_cli(a->fd, "%-15.15s%d\n", "OutgoingLimit: ", peer->outgoinglimit); + ast_cli(a->fd, "%-15.15s%u\n", "OutgoingLimit: ", peer->outgoinglimit); ast_cli(a->fd, "%-15.15s%d\n", "rtptimeout: ", peer->rtptimeout); ast_cli(a->fd, "%-15.15s%s\n", "nat: ", peer->nat?"yes":"no"); if (peer->rtpmaskstr[0]) { @@ -3144,7 +3231,7 @@ static char *handle_cli_ooh323_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { struct ooh323_peer *prev = NULL, *peer = NULL; - char formats[FORMAT_STRING_SIZE]; + struct ast_str *codec_buf = ast_str_alloca(64); char ip_port[30]; #define FORMAT "%-15.15s %-15.15s %-23.23s %-s\n" @@ -3169,10 +3256,10 @@ while (peer) { ast_mutex_lock(&peer->lock); snprintf(ip_port, sizeof(ip_port), "%s:%d", peer->ip, peer->port); - ast_cli(a->fd, FORMAT, peer->name, + ast_cli(a->fd, FORMAT, peer->name, peer->accountcode, ip_port, - ast_getformatname_multiple(formats,FORMAT_STRING_SIZE,peer->cap)); + ast_format_cap_get_names(peer->cap, &codec_buf)); prev = peer; peer = peer->next; ast_mutex_unlock(&prev->lock); @@ -3183,24 +3270,6 @@ return CLI_SUCCESS; } -/*! \brief Print codec list from preference to CLI/manager */ -static void print_codec_to_cli(int fd, struct ast_codec_pref *pref) -{ - int x; - struct ast_format tmpfmt; - for (x = 0; x < 32; x++) { - ast_codec_pref_index(pref, x, &tmpfmt); - if (!tmpfmt.id) - break; - ast_cli(fd, "%s", ast_getformatname(&tmpfmt)); - ast_cli(fd, ":%d", pref->framing[x]); - if (x < 31 && ast_codec_pref_index(pref, x + 1, &tmpfmt)) - ast_cli(fd, ","); - } - if (!x) - ast_cli(fd, "none"); -} - static char *handle_cli_ooh323_show_user(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { struct ooh323_user *prev = NULL, *user = NULL; @@ -3239,9 +3308,6 @@ user->h245tunneling?"yes":"no"); ast_cli(a->fd, "%-15s%s\n", "DirectRTP", user->directrtp ? "yes" : "no"); ast_cli(a->fd, "%-15s%s\n", "EarlyDirectRTP", user->earlydirect ? "yes" : "no"); - ast_cli(a->fd, "%-15.15s%s", "Format Prefs: ", "("); - print_codec_to_cli(a->fd, &user->prefs); - ast_cli(a->fd, ")\n"); ast_cli(a->fd, "%-15.15s", "DTMF Mode: "); if (user->dtmfmode & H323_DTMF_CISCO) { ast_cli(a->fd, "%s\n", "cisco"); @@ -3279,10 +3345,10 @@ } ast_cli(a->fd, "%-15.15s%s\n", "AccountCode: ", user->accountcode); - ast_cli(a->fd, "%-15.15s%s\n", "AMA flags: ", ast_cdr_flags2str(user->amaflags)); + ast_cli(a->fd, "%-15.15s%s\n", "AMA flags: ", ast_channel_amaflags2string(user->amaflags)); ast_cli(a->fd, "%-15.15s%s\n", "Context: ", user->context); ast_cli(a->fd, "%-15.15s%d\n", "IncomingLimit: ", user->incominglimit); - ast_cli(a->fd, "%-15.15s%d\n", "InUse: ", user->inUse); + ast_cli(a->fd, "%-15.15s%u\n", "InUse: ", user->inUse); ast_cli(a->fd, "%-15.15s%d\n", "rtptimeout: ", user->rtptimeout); ast_cli(a->fd, "%-15.15s%s\n", "nat: ", user->nat?"yes":"no"); if (user->rtpmaskstr[0]) { @@ -3304,7 +3370,7 @@ static char *handle_cli_ooh323_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { struct ooh323_user *prev = NULL, *user = NULL; - char formats[FORMAT_STRING_SIZE]; + struct ast_str *codec_buf = ast_str_alloca(64); #define FORMAT1 "%-15.15s %-15.15s %-15.15s %-s\n" switch (cmd) { @@ -3331,7 +3397,7 @@ ast_mutex_lock(&user->lock); ast_cli(a->fd, FORMAT1, user->name, user->accountcode, user->context, - ast_getformatname_multiple(formats, FORMAT_STRING_SIZE, user->cap)); + ast_format_cap_get_names(user->cap, &codec_buf)); prev = user; user = user->next; ast_mutex_unlock(&prev->lock); @@ -3425,6 +3491,9 @@ case GkClientFailed: ast_cli(a->fd, "%-20s%s\n", "GK state:", "Failed"); break; + case GkClientStopped: + ast_cli(a->fd, "%-20s%s\n", "GK state:", "Shutdown"); + break; default: break; } @@ -3435,6 +3504,7 @@ static char *handle_cli_ooh323_show_config(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { char value[FORMAT_STRING_SIZE]; + struct ast_str *codec_buf = ast_str_alloca(64); ooAliases *pAlias = NULL, *pAliasNext = NULL;; switch (cmd) { @@ -3483,7 +3553,7 @@ ast_cli(a->fd, "%-20s%s\n", "H.323 LogFile:", gLogFile); ast_cli(a->fd, "%-20s%s\n", "Context:", gContext); ast_cli(a->fd, "%-20s%s\n", "Capability:", - ast_getformatname_multiple(value,FORMAT_STRING_SIZE,gCap)); + ast_format_cap_get_names(gCap, &codec_buf)); ast_cli(a->fd, "%-20s", "DTMF Mode: "); if (gDTMFMode & H323_DTMF_CISCO) { ast_cli(a->fd, "%s\n", "cisco"); @@ -3527,7 +3597,7 @@ ast_cli(a->fd, "%-20s%ld\n", "Call counter: ", callnumber); ast_cli(a->fd, "%-20s%s\n", "AccountCode: ", gAccountcode); - ast_cli(a->fd, "%-20s%s\n", "AMA flags: ", ast_cdr_flags2str(gAMAFLAGS)); + ast_cli(a->fd, "%-20s%s\n", "AMA flags: ", ast_channel_amaflags2string(gAMAFLAGS)); pAlias = gAliasList; if(pAlias) { @@ -3664,7 +3734,6 @@ { struct ooAliases * pNewAlias = NULL; struct ooh323_peer *peer = NULL; - struct ast_format tmpfmt; OOH225MsgCallbacks h225Callbacks = {0, 0, 0, 0}; OOH323CALLBACKS h323Callbacks = { @@ -3680,14 +3749,16 @@ .onModeChanged = onModeChanged, .onMediaChanged = (cb_OnMediaChanged) setup_rtp_remote, }; - if (!(gCap = ast_format_cap_alloc())) { - return 1; + if (!(gCap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) { + return AST_MODULE_LOAD_DECLINE; } - if (!(ooh323_tech.capabilities = ast_format_cap_alloc())) { - return 1; + if (!(ooh323_tech.capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) { + ao2_ref(gCap, -1); + gCap = NULL; + return AST_MODULE_LOAD_DECLINE; } - ast_format_cap_add(gCap, ast_format_set(&tmpfmt, AST_FORMAT_ULAW, 0)); - ast_format_cap_add_all(ooh323_tech.capabilities); + ast_format_cap_append(gCap, ast_format_ulaw, 0); + ast_format_cap_append_by_type(ooh323_tech.capabilities, AST_MEDIA_TYPE_UNKNOWN); myself = ast_module_info->self; @@ -3711,22 +3782,30 @@ if (!reload_config(0)) { + + /* fire up the H.323 Endpoint */ + if (OO_OK != ooH323EpInitialize(OO_CALLMODE_AUDIOCALL, gLogFile)) { + ast_log(LOG_ERROR, "Failed to initialize OOH323 endpoint-" + "OOH323 Disabled\n"); + ao2_ref(gCap, -1); + gCap = NULL; + ao2_ref(ooh323_tech.capabilities, -1); + ooh323_tech.capabilities = NULL; + return AST_MODULE_LOAD_DECLINE; + } + /* Make sure we can register our OOH323 channel type */ if (ast_channel_register(&ooh323_tech)) { ast_log(LOG_ERROR, "Unable to register channel class %s\n", type); - return 0; + ao2_ref(gCap, -1); + gCap = NULL; + ao2_ref(ooh323_tech.capabilities, -1); + ooh323_tech.capabilities = NULL; + return AST_MODULE_LOAD_DECLINE; } ast_rtp_glue_register(&ooh323_rtp); - ast_udptl_proto_register(&ooh323_udptl); ast_cli_register_multiple(cli_ooh323, sizeof(cli_ooh323) / sizeof(struct ast_cli_entry)); - /* fire up the H.323 Endpoint */ - if (OO_OK != ooH323EpInitialize(OO_CALLMODE_AUDIOCALL, gLogFile)) { - ast_log(LOG_ERROR, "Failed to initialize OOH323 endpoint-" - "OOH323 Disabled\n"); - return 1; - } - if (gIsGateway) ooH323EpSetAsGateway(); @@ -3741,10 +3820,9 @@ } ooH323EpSetCallerID(gCallerID); - if(ooH323EpSetTCPPortRange(ooconfig.mTCPPortStart, - ooconfig.mTCPPortEnd) == OO_FAILED) { - ast_log(LOG_ERROR, "h225portrange: Failed to set range\n"); - } + if(ooH323EpSetTCPPortRange(ooconfig.mTCPPortStart, ooconfig.mTCPPortEnd) == OO_FAILED) { + ast_log(LOG_ERROR, "h225portrange: Failed to set range\n"); + } /* Set aliases if any */ for (pNewAlias = gAliasList; pNewAlias; pNewAlias = pNewAlias->next) { @@ -3758,8 +3836,8 @@ case T_H225AliasAddress_email_ID: ooH323EpAddAliasEmailID(pNewAlias->value); break; - default: - ; + default: + ; } } @@ -3804,8 +3882,12 @@ ooH323EpSetH323Callbacks(h323Callbacks); /* Add endpoint capabilities */ - if (ooh323c_set_capability(&gPrefs, gCap, gDTMFMode, gDTMFCodec) < 0) { + if (ooh323c_set_capability(gCap, gDTMFMode, gDTMFCodec) < 0) { ast_log(LOG_ERROR, "Capabilities failure for OOH323. OOH323 Disabled.\n"); + ao2_ref(gCap, -1); + gCap = NULL; + ao2_ref(ooh323_tech.capabilities, -1); + ooh323_tech.capabilities = NULL; return 1; } @@ -3815,6 +3897,10 @@ "OOH323 DISABLED\n"); ooH323EpDestroy(); + ao2_ref(gCap, -1); + gCap = NULL; + ao2_ref(ooh323_tech.capabilities, -1); + ooh323_tech.capabilities = NULL; return 1; } @@ -3822,15 +3908,38 @@ ast_log(LOG_ERROR, "Failed to start OOH323 stack thread. " "OOH323 DISABLED\n"); ooH323EpDestroy(); + ao2_ref(gCap, -1); + gCap = NULL; + ao2_ref(ooh323_tech.capabilities, -1); + ooh323_tech.capabilities = NULL; return 1; } /* And start the monitor for the first time */ restart_monitor(); + } else { + ast_log(LOG_ERROR, "Can't load ooh323 config file, OOH323 Disabled\n"); + return AST_MODULE_LOAD_DECLINE; } return 0; } +static int reload_module(void) +{ + ast_mutex_lock(&h323_reload_lock); + if (h323_reloading) { + ast_verb(0, "Previous OOH323 reload not yet done\n"); + } else { + h323_reloading = 1; + } + ast_mutex_unlock(&h323_reload_lock); + restart_monitor(); + + if (gH323Debug) + ast_verb(0, "+++ ooh323_reload\n"); + + return 0; +} static void *do_monitor(void *data) { @@ -3850,6 +3959,13 @@ ast_verb(1, "Reloading H.323\n"); ooh323_do_reload(); } + if (gH323ep.gkClient && gH323ep.gkClient->state == GkClientStopped) { + ooGkClientDestroy(); + ast_verb(0, "Restart stopped gatekeeper client\n"); + ooGkClientInit(gRasGkMode, (gRasGkMode == RasUseSpecificGatekeeper) ? + gGatekeeper : 0, 0); + ooGkClientStart(gH323ep.gkClient); + } /* Check for interfaces needing to be killed */ ast_mutex_lock(&iflock); @@ -4044,7 +4160,9 @@ ast_mutex_unlock(&cur->lock); ast_mutex_destroy(&cur->lock); - cur->cap = ast_format_cap_destroy(cur->cap); + ao2_cleanup(cur->writeformat); + ao2_cleanup(cur->readformat); + ao2_cleanup(cur->cap); ast_free(cur); } @@ -4109,7 +4227,7 @@ free(prev->rtpmask); } } - prev->cap = ast_format_cap_destroy(prev->cap); + ao2_cleanup(prev->cap); free(prev); if (cur == userl.users) { break; @@ -4131,7 +4249,6 @@ /* First, take us out of the channel loop */ ast_cli_unregister_multiple(cli_ooh323, sizeof(cli_ooh323) / sizeof(struct ast_cli_entry)); ast_rtp_glue_unregister(&ooh323_rtp); - ast_udptl_proto_unregister(&ooh323_udptl); ast_channel_unregister(&ooh323_tech); #if 0 ast_unregister_atexit(&ast_ooh323c_exit); @@ -4238,8 +4355,10 @@ ast_verb(0, "+++ ooh323 unload_module \n"); } - gCap = ast_format_cap_destroy(gCap); - ooh323_tech.capabilities = ast_format_cap_destroy(ooh323_tech.capabilities); + ao2_ref(gCap, -1); + gCap = NULL; + ao2_ref(ooh323_tech.capabilities, -1); + ooh323_tech.capabilities = NULL; return 0; } @@ -4251,8 +4370,11 @@ } if (p) { - ast_format_cap_append(result, ast_format_cap_is_empty(ast_channel_nativeformats(chan)) ? - (ast_format_cap_is_empty(p->cap) ? NULL : p->cap) : ast_channel_nativeformats(chan)); + if (ast_format_cap_count(ast_channel_nativeformats(chan))) { + ast_format_cap_append_from_cap(result, ast_channel_nativeformats(chan), AST_MEDIA_TYPE_UNKNOWN); + } else if (ast_format_cap_count(p->cap)) { + ast_format_cap_append_from_cap(result, p->cap, AST_MEDIA_TYPE_UNKNOWN); + } } if (gH323Debug) { @@ -4295,7 +4417,7 @@ ast_rtp_instance_get_remote_address(*rtp, &tmp); if (gH323Debug) { - ast_verb(0, "ooh323_get_rtp_peer %s -> %s:%d, %d\n", ast_channel_name(chan), ast_sockaddr_stringify_addr(&tmp), + ast_verb(0, "ooh323_get_rtp_peer %s -> %s:%d, %u\n", ast_channel_name(chan), ast_sockaddr_stringify_addr(&tmp), ast_sockaddr_port(&tmp), res); } if (gH323Debug) { @@ -4326,56 +4448,28 @@ return res; } - -int ooh323_update_capPrefsOrderForCall - (ooCallData *call, struct ast_codec_pref *prefs) -{ - int i = 0; - struct ast_format tmpfmt; - - ast_codec_pref_index(prefs, i, &tmpfmt); - - ooResetCapPrefs(call); - while (tmpfmt.id) { - ooAppendCapToCapPrefs(call, ooh323_convertAsteriskCapToH323Cap(&tmpfmt)); - ast_codec_pref_index(prefs, ++i, &tmpfmt); - } - - return 0; -} - - int ooh323_convertAsteriskCapToH323Cap(struct ast_format *format) { - switch (format->id) { - case AST_FORMAT_ULAW: + if (ast_format_cmp(format, ast_format_ulaw) == AST_FORMAT_CMP_EQUAL) { return OO_G711ULAW64K; - case AST_FORMAT_ALAW: + } else if (ast_format_cmp(format, ast_format_alaw) == AST_FORMAT_CMP_EQUAL) { return OO_G711ALAW64K; - case AST_FORMAT_GSM: + } else if (ast_format_cmp(format, ast_format_gsm) == AST_FORMAT_CMP_EQUAL) { return OO_GSMFULLRATE; - -#ifdef AST_FORMAT_AMRNB - case AST_FORMAT_AMRNB: - return OO_AMRNB; -#endif -#ifdef AST_FORMAT_SPEEX - case AST_FORMAT_SPEEX: + } else if (ast_format_cmp(format, ast_format_speex) == AST_FORMAT_CMP_EQUAL) { return OO_SPEEX; -#endif - - case AST_FORMAT_G729A: + } else if (ast_format_cmp(format, ast_format_g729) == AST_FORMAT_CMP_EQUAL) { return OO_G729A; - case AST_FORMAT_G726: + } else if (ast_format_cmp(format, ast_format_g726) == AST_FORMAT_CMP_EQUAL) { return OO_G726; - case AST_FORMAT_G726_AAL2: + } else if (ast_format_cmp(format, ast_format_g726_aal2) == AST_FORMAT_CMP_EQUAL) { return OO_G726AAL2; - case AST_FORMAT_G723_1: + } else if (ast_format_cmp(format, ast_format_g723) == AST_FORMAT_CMP_EQUAL) { return OO_G7231; - case AST_FORMAT_H263: + } else if (ast_format_cmp(format, ast_format_h263) == AST_FORMAT_CMP_EQUAL) { return OO_H263VIDEO; - default: - ast_log(LOG_NOTICE, "Don't know how to deal with mode %s\n", ast_getformatname(format)); + } else { + ast_log(LOG_NOTICE, "Don't know how to deal with mode %s\n", ast_format_get_name(format)); return -1; } } @@ -4454,9 +4548,6 @@ struct ast_sockaddr tmp; ooMediaInfo mediaInfo; int x; - struct ast_format tmpfmt; - - ast_format_clear(&tmpfmt); if (gH323Debug) ast_verb(0, "--- configure_local_rtp\n"); @@ -4502,7 +4593,10 @@ ast_channel_unlock(p->owner); if (p->rtp) { - ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(p->rtp), p->rtp, &p->prefs); + if (p->cap) { + ast_rtp_codecs_set_framing(ast_rtp_instance_get_codecs(p->rtp), + ast_format_cap_get_framing(p->cap)); + } if (p->nat) { ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_NAT, 1); } @@ -4539,9 +4633,11 @@ ast_copy_string(mediaInfo.lMediaIP, lhost, sizeof(mediaInfo.lMediaIP)); mediaInfo.lMediaPort = lport; mediaInfo.lMediaCntrlPort = mediaInfo.lMediaPort + 1; - for (x = 0; ast_codec_pref_index(&p->prefs, x, &tmpfmt); x++) { + for (x = 0; x < ast_format_cap_count(p->cap); x++) { + struct ast_format *format = ast_format_cap_get_format(p->cap, x); + strcpy(mediaInfo.dir, "transmit"); - mediaInfo.cap = ooh323_convertAsteriskCapToH323Cap(&tmpfmt); + mediaInfo.cap = ooh323_convertAsteriskCapToH323Cap(format); ooAddMediaInfo(call, mediaInfo); strcpy(mediaInfo.dir, "receive"); ooAddMediaInfo(call, mediaInfo); @@ -4558,6 +4654,8 @@ strcpy(mediaInfo.dir, "receive"); ooAddMediaInfo(call, mediaInfo); } + + ao2_ref(format, -1); } if (p->udptl) { @@ -4641,7 +4739,7 @@ ast_sockaddr_set_port(&tmp, remotePort); ast_rtp_instance_set_remote_address(p->rtp, &tmp); - if (p->writeformat.id == AST_FORMAT_G726_AAL2) { + if (ast_format_cmp(p->writeformat, ast_format_g726_aal2) == AST_FORMAT_CMP_EQUAL) { ast_rtp_codecs_payloads_set_rtpmap_type(ast_rtp_instance_get_codecs(p->rtp), p->rtp, 2, "audio", "G726-32", AST_RTP_OPT_G726_NONSTANDARD); } @@ -4683,41 +4781,6 @@ udptl handling functions */ -static struct ast_udptl *ooh323_get_udptl_peer(struct ast_channel *chan) -{ - struct ooh323_pvt *p; - struct ast_udptl *udptl = NULL; - - p = ast_channel_tech_pvt(chan); - if (!p) - return NULL; - - ast_mutex_lock(&p->lock); - if (p->udptl) - udptl = p->udptl; - ast_mutex_unlock(&p->lock); - return udptl; -} - -static int ooh323_set_udptl_peer(struct ast_channel *chan, struct ast_udptl *udptl) -{ - struct ooh323_pvt *p; - - p = ast_channel_tech_pvt(chan); - if (!p) - return -1; - ast_mutex_lock(&p->lock); - - if (udptl) { - ast_udptl_get_peer(udptl, &p->udptlredirip); - } else - memset(&p->udptlredirip, 0, sizeof(p->udptlredirip)); - - ast_mutex_unlock(&p->lock); - /* free(callToken); */ - return 0; -} - void setup_udptl_connection(ooCallData *call, const char *remoteIp, int remotePort) { @@ -4881,7 +4944,7 @@ case 5: f = ast_udptl_read(p->udptl); /* UDPTL t.38 data */ if (gH323Debug) { - ast_debug(1, "Got UDPTL %d/%d len %d for %s\n", + ast_debug(1, "Got UDPTL %u/%d len %d for %s\n", f->frametype, f->subclass.integer, f->datalen, ast_channel_name(ast)); } p->lastrtprx = time(NULL); @@ -4893,15 +4956,24 @@ if (f && p->owner && !p->faxmode && (f->frametype == AST_FRAME_VOICE)) { /* We already hold the channel lock */ - if (!(ast_format_cap_iscompatible(ast_channel_nativeformats(p->owner), &f->subclass.format))) { - ast_debug(1, "Oooh, voice format changed to %s\n", ast_getformatname(&f->subclass.format)); - ast_format_cap_set(ast_channel_nativeformats(p->owner), &f->subclass.format); + if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(p->owner), f->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) { + struct ast_format_cap *caps; + + ast_debug(1, "Oooh, voice format changed to %s\n", ast_format_get_name(f->subclass.format)); + + caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); + if (caps) { + ast_format_cap_append(caps, f->subclass.format, 0); + ast_channel_nativeformats_set(p->owner, caps); + ao2_ref(caps, -1); + } ast_set_read_format(p->owner, ast_channel_readformat(p->owner)); ast_set_write_format(p->owner, ast_channel_writeformat(p->owner)); } if (((p->dtmfmode & H323_DTMF_INBAND) || (p->faxdetect & FAXDETECT_CNG)) && p->vad && - (f->subclass.format.id == AST_FORMAT_SLINEAR || f->subclass.format.id == AST_FORMAT_ALAW || - f->subclass.format.id == AST_FORMAT_ULAW)) { + ((ast_format_cmp(f->subclass.format, ast_format_slin) == AST_FORMAT_CMP_EQUAL) || + (ast_format_cmp(f->subclass.format, ast_format_ulaw) == AST_FORMAT_CMP_EQUAL) || + (ast_format_cmp(f->subclass.format, ast_format_alaw) == AST_FORMAT_CMP_EQUAL))) { dfr = ast_frdup(f); dfr = ast_dsp_process(p->owner, p->vad, dfr); } @@ -5115,4 +5187,10 @@ } #endif -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Objective Systems H323 Channel"); +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Objective Systems H323 Channel", + .support_level = AST_MODULE_SUPPORT_EXTENDED, + .load = load_module, + .unload = unload_module, + .reload = reload_module, + .load_pri = AST_MODPRI_CHANNEL_DRIVER + ); diff -Nru asterisk-11.7.0~dfsg/addons/chan_ooh323.h asterisk-13.1.0~dfsg/addons/chan_ooh323.h --- asterisk-11.7.0~dfsg/addons/chan_ooh323.h 2011-02-03 16:22:10.000000000 +0000 +++ asterisk-13.1.0~dfsg/addons/chan_ooh323.h 2014-07-20 22:06:33.000000000 +0000 @@ -64,6 +64,8 @@ #include "asterisk/format.h" #include "asterisk/format_cap.h" #include "asterisk/udptl.h" +#include "asterisk/stasis_channels.h" +#include "asterisk/format_cache.h" #include "ootypes.h" #include "ooUtils.h" @@ -103,9 +105,6 @@ void ooh323_set_write_format(ooCallData *call, struct ast_format *fmt, int txframes); void ooh323_set_read_format(ooCallData *call, struct ast_format *fmt); -int ooh323_update_capPrefsOrderForCall - (ooCallData *call, struct ast_codec_pref *prefs); - int ooh323_convertAsteriskCapToH323Cap(struct ast_format *format); int ooh323_convert_hangupcause_asteriskToH323(int cause); diff -Nru asterisk-11.7.0~dfsg/addons/format_mp3.c asterisk-13.1.0~dfsg/addons/format_mp3.c --- asterisk-11.7.0~dfsg/addons/format_mp3.c 2012-05-12 00:03:42.000000000 +0000 +++ asterisk-13.1.0~dfsg/addons/format_mp3.c 2014-07-25 16:47:17.000000000 +0000 @@ -34,7 +34,7 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 366298 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $") #include "mp3/mpg123.h" #include "mp3/mpglib.h" @@ -42,6 +42,7 @@ #include "asterisk/module.h" #include "asterisk/mod_format.h" #include "asterisk/logger.h" +#include "asterisk/format_cache.h" #define MP3_BUFLEN 320 #define MP3_SCACHE 16384 @@ -229,10 +230,7 @@ p->offset += p->buflen; delay = p->buflen / 2; - s->fr.frametype = AST_FRAME_VOICE; - ast_format_set(&s->fr.subclass.format, AST_FORMAT_SLINEAR, 0); AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, p->buflen); - s->fr.mallocd = 0; s->fr.samples = delay; *whennext = delay; return &s->fr; @@ -318,7 +316,7 @@ static int load_module(void) { - ast_format_set(&mp3_f.format, AST_FORMAT_SLINEAR, 0); + mp3_f.format = ast_format_slin; InitMP3Constants(); return ast_format_def_register(&mp3_f); } @@ -328,4 +326,5 @@ return ast_format_def_unregister(name); } -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "MP3 format [Any rate but 8000hz mono is optimal]"); +AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "MP3 format [Any rate but 8000hz mono is optimal]"); + diff -Nru asterisk-11.7.0~dfsg/addons/Makefile asterisk-13.1.0~dfsg/addons/Makefile --- asterisk-11.7.0~dfsg/addons/Makefile 2010-08-03 18:50:14.000000000 +0000 +++ asterisk-13.1.0~dfsg/addons/Makefile 2014-07-04 13:26:37.000000000 +0000 @@ -1,5 +1,5 @@ # -# Asterisk -- A telephony toolkit for Linux. +# Asterisk -- An open source telephony toolkit. # # Makefile for Add-on Modules # @@ -27,7 +27,6 @@ H323CFLAGS:=-Iooh323c/src -Iooh323c/src/h323 ALL_C_MODS:=app_mysql \ - app_saycountpl \ cdr_mysql \ chan_mobile \ chan_ooh323 \ diff -Nru asterisk-11.7.0~dfsg/addons/ooh323c/src/ooCalls.h asterisk-13.1.0~dfsg/addons/ooh323c/src/ooCalls.h --- asterisk-11.7.0~dfsg/addons/ooh323c/src/ooCalls.h 2012-07-04 21:42:05.000000000 +0000 +++ asterisk-13.1.0~dfsg/addons/ooh323c/src/ooCalls.h 2014-02-21 10:45:05.000000000 +0000 @@ -228,7 +228,7 @@ char lastDTMF; ASN1UINT nextDTMFstamp; int rtdrInterval, rtdrCount; /* roundTripDelay interval and unreplied count */ - ASN1UINT rtdrSend, rtdrRecv; /* last sended/replied RTD request */ + ASN1UINT8 rtdrSend, rtdrRecv; /* last sended/replied RTD request */ void *usrData; /*!u.receiveAndTransmitUserInputCapability->t == + T_H245UserInputCapability_basicString) && + (call->dtmfmode & OO_CAP_DTMF_H245_alphanumeric)) + { + call->jointDtmfMode |= OO_CAP_DTMF_H245_alphanumeric; + return OO_OK; + } + else if((cap->u.receiveAndTransmitUserInputCapability->t == + T_H245UserInputCapability_dtmf) && + (call->dtmfmode & OO_CAP_DTMF_H245_signal)) + { + call->jointDtmfMode |= OO_CAP_DTMF_H245_signal; + return OO_OK; + } + case T_H245Capability_receiveUserInputCapability: if((cap->u.receiveUserInputCapability->t == T_H245UserInputCapability_basicString) && diff -Nru asterisk-11.7.0~dfsg/addons/ooh323c/src/oochannels.c asterisk-13.1.0~dfsg/addons/ooh323c/src/oochannels.c --- asterisk-11.7.0~dfsg/addons/ooh323c/src/oochannels.c 2011-11-09 19:08:44.000000000 +0000 +++ asterisk-13.1.0~dfsg/addons/ooh323c/src/oochannels.c 2014-03-28 18:00:18.000000000 +0000 @@ -603,11 +603,7 @@ if(gH323ep.gkClient->state == GkClientFailed || gH323ep.gkClient->state == GkClientGkErr) { - if(ooGkClientHandleClientOrGkFailure(gH323ep.gkClient)!=OO_OK) - { - //ooStopMonitorCalls(); //Function calling ooProcessFDSETsAndTimers is responsible for this. - return OO_FAILED; - } + ooGkClientHandleClientOrGkFailure(gH323ep.gkClient); } } @@ -826,7 +822,8 @@ if(OO_OK != ooGkClientStart(gH323ep.gkClient)) { OOTRACEERR1("Error:Failed to start Gatekeeper client\n"); - ooGkClientDestroy(); + // not need more, now it can be restarted correctly + // ooGkClientDestroy(); } } diff -Nru asterisk-11.7.0~dfsg/addons/ooh323c/src/ooCmdChannel.c asterisk-13.1.0~dfsg/addons/ooh323c/src/ooCmdChannel.c --- asterisk-11.7.0~dfsg/addons/ooh323c/src/ooCmdChannel.c 2012-07-05 11:42:23.000000000 +0000 +++ asterisk-13.1.0~dfsg/addons/ooh323c/src/ooCmdChannel.c 2014-03-28 18:00:18.000000000 +0000 @@ -172,11 +172,6 @@ if(cmd.type == OO_CMD_NOOP) continue; - if(gH323ep.gkClient && gH323ep.gkClient->state != GkClientRegistered && cmd.type != OO_CMD_STOPMONITOR) - { - OOTRACEINFO1("Ignoring stack command as Gk Client is not registered" - " yet\n"); - } else { switch(cmd.type) { case OO_CMD_MAKECALL: @@ -336,11 +331,6 @@ if(cmd.type == OO_CMD_NOOP) continue; - if(gH323ep.gkClient && gH323ep.gkClient->state != GkClientRegistered) - { - OOTRACEINFO1("Ignoring stack command as Gk Client is not registered" - " yet\n"); - } else { switch(cmd.type) { case OO_CMD_MAKECALL: diff -Nru asterisk-11.7.0~dfsg/addons/ooh323c/src/ooGkClient.c asterisk-13.1.0~dfsg/addons/ooh323c/src/ooGkClient.c --- asterisk-11.7.0~dfsg/addons/ooh323c/src/ooGkClient.c 2012-08-10 15:24:03.000000000 +0000 +++ asterisk-13.1.0~dfsg/addons/ooh323c/src/ooGkClient.c 2014-03-28 18:00:18.000000000 +0000 @@ -210,7 +210,7 @@ if(iRet != OO_OK) { OOTRACEERR1("Error:Failed to send GRQ message\n"); - pGkClient->state = GkClientFailed; + pGkClient->state = GkClientGkErr; ast_mutex_unlock(&pGkClient->Lock); return OO_FAILED; } @@ -433,7 +433,7 @@ if(iRet != OO_OK) { OOTRACEERR1("Error: Failed to handle received RAS message\n"); - pGkClient->state = GkClientFailed; + pGkClient->state = GkClientGkErr; } memReset(pctxt); } @@ -702,7 +702,7 @@ { OOTRACEERR1("Error: Failed to send GRQ message\n"); memReset(&pGkClient->msgCtxt); - pGkClient->state = GkClientFailed; + pGkClient->state = GkClientGkErr; ast_mutex_unlock(&pGkClient->Lock); return OO_FAILED; } @@ -1530,7 +1530,7 @@ { OOTRACEERR1("Error:Failed to send UnregistrationRequest message\n"); memReset(pctxt); - pGkClient->state = GkClientFailed; + pGkClient->state = GkClientGkErr; ast_mutex_unlock(&pGkClient->Lock); return OO_FAILED; } @@ -1909,7 +1909,7 @@ { OOTRACEERR1("Error:Failed to send AdmissionRequest message\n"); memReset(pctxt); - pGkClient->state = GkClientFailed; + pGkClient->state = GkClientGkErr; ast_mutex_unlock(&pGkClient->Lock); return OO_FAILED; } @@ -2423,7 +2423,7 @@ { OOTRACEERR1("Error:Failed to send IRR message\n"); memReset(pctxt); - pGkClient->state = GkClientFailed; + pGkClient->state = GkClientGkErr; ast_mutex_unlock(&pGkClient->Lock); return OO_FAILED; } @@ -2576,7 +2576,7 @@ if(iRet != OO_OK) { OOTRACEERR1("Error: Failed to send DRQ message\n"); - pGkClient->state = GkClientFailed; + pGkClient->state = GkClientGkErr; } @@ -2736,7 +2736,7 @@ if(ret != OO_OK) { OOTRACEERR1("Error:Failed to send Additive RRQ message\n"); - pGkClient->state = GkClientFailed; + pGkClient->state = GkClientGkErr; return OO_FAILED; } return OO_OK; @@ -2851,13 +2851,13 @@ { OOTRACEERR1("Error: Gatekeeper error detected. Closing GkClient as " "Gk mode is UseSpecifcGatekeeper\n"); - ooGkClientDestroy(); + pGkClient->state = GkClientStopped; return OO_FAILED; } else{ OOTRACEERR1("Error: Gatekeeper error detected. Closing GkClient. NEED" " to implement recovery by rediscovering another gk\n"); - ooGkClientDestroy(); + pGkClient->state = GkClientStopped; return OO_FAILED; } } diff -Nru asterisk-11.7.0~dfsg/addons/ooh323c/src/ooGkClient.h asterisk-13.1.0~dfsg/addons/ooh323c/src/ooGkClient.h --- asterisk-11.7.0~dfsg/addons/ooh323c/src/ooGkClient.h 2011-04-12 21:59:18.000000000 +0000 +++ asterisk-13.1.0~dfsg/addons/ooh323c/src/ooGkClient.h 2013-12-19 08:35:28.000000000 +0000 @@ -108,7 +108,8 @@ GkClientRegistered, /* registered with gk */ GkClientUnregistered, GkClientGkErr,/*Gk is not responding, in discover mode can look for new GK*/ - GkClientFailed + GkClientFailed, + GkClientStopped }; diff -Nru asterisk-11.7.0~dfsg/addons/ooh323c/src/ooh245.c asterisk-13.1.0~dfsg/addons/ooh323c/src/ooh245.c --- asterisk-11.7.0~dfsg/addons/ooh323c/src/ooh245.c 2013-09-03 19:45:44.000000000 +0000 +++ asterisk-13.1.0~dfsg/addons/ooh323c/src/ooh245.c 2014-02-19 12:04:32.000000000 +0000 @@ -713,8 +713,8 @@ memset(entry, 0, sizeof(H245CapabilityTableEntry)); entry->m.capabilityPresent = 1; - entry->capability.t = T_H245Capability_receiveUserInputCapability; - entry->capability.u.receiveUserInputCapability = userInputCap; + entry->capability.t = T_H245Capability_receiveAndTransmitUserInputCapability; + entry->capability.u.receiveAndTransmitUserInputCapability = userInputCap; entry->capabilityTableEntryNumber = i+1; dListAppend(pctxt , &(termCap->capabilityTable), entry); @@ -749,8 +749,8 @@ memset(entry, 0, sizeof(H245CapabilityTableEntry)); entry->m.capabilityPresent = 1; - entry->capability.t = T_H245Capability_receiveUserInputCapability; - entry->capability.u.receiveUserInputCapability = userInputCap; + entry->capability.t = T_H245Capability_receiveAndTransmitUserInputCapability; + entry->capability.u.receiveAndTransmitUserInputCapability = userInputCap; entry->capabilityTableEntryNumber = i+1; dListAppend(pctxt , &(termCap->capabilityTable), entry); diff -Nru asterisk-11.7.0~dfsg/addons/ooh323c/src/ooh323.c asterisk-13.1.0~dfsg/addons/ooh323c/src/ooh323.c --- asterisk-11.7.0~dfsg/addons/ooh323c/src/ooh323.c 2012-07-04 21:42:05.000000000 +0000 +++ asterisk-13.1.0~dfsg/addons/ooh323c/src/ooh323.c 2014-03-28 18:00:18.000000000 +0000 @@ -535,11 +535,13 @@ } } +/* Allow sourceCallSignallAddress different with socket IP for gk routed calls */ if (strncmp(remoteIP, call->remoteIP, strlen(remoteIP))) { - OOTRACEERR5("ERROR: Security denial remote sig IP isn't a socket ip, %s not %s " - "(%s, %s)\n", remoteIP, call->remoteIP, call->callType, - call->callToken); - return OO_FAILED; + if(!gH323ep.gkClient || OO_TESTFLAG(call->flags, OO_M_DISABLEGK) || (gH323ep.gkClient->state != GkClientRegistered)) { + OOTRACEERR5("ERROR: Security denial remote sig IP isn't a socket ip, %s not %s " + "(%s, %s)\n", remoteIP, call->remoteIP, call->callType, call->callToken); + return OO_FAILED; + } } /* check for fast start */ @@ -1717,12 +1719,12 @@ if(gH323ep.gkClient->state == GkClientRegistered) { call->callState = OO_CALL_WAITING_ADMISSION; + ast_mutex_lock(&call->GkLock); ret = ooGkClientSendAdmissionRequest(gH323ep.gkClient, call, FALSE); tv = ast_tvnow(); ts.tv_sec = tv.tv_sec + 24; ts.tv_nsec = tv.tv_usec * 1000; - ast_mutex_lock(&call->GkLock); if (call->callState == OO_CALL_WAITING_ADMISSION) ast_cond_timedwait(&call->gkWait, &call->GkLock, &ts); if (call->callState == OO_CALL_WAITING_ADMISSION) diff -Nru asterisk-11.7.0~dfsg/addons/ooh323c/src/ooq931.c asterisk-13.1.0~dfsg/addons/ooh323c/src/ooq931.c --- asterisk-11.7.0~dfsg/addons/ooh323c/src/ooq931.c 2012-07-05 11:42:23.000000000 +0000 +++ asterisk-13.1.0~dfsg/addons/ooh323c/src/ooq931.c 2014-07-22 14:22:00.000000000 +0000 @@ -319,7 +319,7 @@ strcpy(buf, "Escape"); break; default: - sprintf(buf, "<%u>", messageType); + sprintf(buf, "<%d>", messageType); } return buf; } @@ -360,7 +360,7 @@ strcpy(buf, "User-User"); break; default: - sprintf(buf, "0x%02x", number); + sprintf(buf, "0x%02x", (unsigned)number); } return buf; } @@ -371,8 +371,8 @@ unsigned int i; printf("Q.931 Message:\n"); - printf(" protocolDiscriminator: %i\n", q931msg->protocolDiscriminator); - printf(" callReference: %i\n", q931msg->callReference); + printf(" protocolDiscriminator: %u\n", q931msg->protocolDiscriminator); + printf(" callReference: %u\n", q931msg->callReference); printf(" from: %s\n", (q931msg->fromDestination ? "destination" : "originator")); printf(" messageType: %s (0x%X)\n\n", @@ -382,9 +382,9 @@ for(i = 0, curNode = q931msg->ies.head; i < q931msg->ies.count; i++) { Q931InformationElement *ie = (Q931InformationElement*) curNode->data; int length = (ie->length >= 0) ? ie->length : -ie->length; - printf(" IE[%i] (offset 0x%X):\n", i, ie->offset); + printf(" IE[%u] (offset 0x%X):\n", i, (unsigned)ie->offset); printf(" discriminator: %s (0x%X)\n", - ooQ931GetIEName(ie->discriminator, buf), ie->discriminator); + ooQ931GetIEName(ie->discriminator, buf), (unsigned)ie->discriminator); printf(" data length: %i\n", length); curNode = curNode->next; @@ -2604,11 +2604,11 @@ { if(gH323ep.gkClient->state == GkClientRegistered) { call->callState = OO_CALL_WAITING_ADMISSION; + ast_mutex_lock(&call->GkLock); ret = ooGkClientSendAdmissionRequest(gH323ep.gkClient, call, FALSE); tv = ast_tvnow(); ts.tv_sec = tv.tv_sec + 24; ts.tv_nsec = tv.tv_usec * 1000; - ast_mutex_lock(&call->GkLock); if (call->callState == OO_CALL_WAITING_ADMISSION) ast_cond_timedwait(&call->gkWait, &call->GkLock, &ts); if (call->callState == OO_CALL_WAITING_ADMISSION) diff -Nru asterisk-11.7.0~dfsg/addons/ooh323c/src/ooTimer.c asterisk-13.1.0~dfsg/addons/ooh323c/src/ooTimer.c --- asterisk-11.7.0~dfsg/addons/ooh323c/src/ooTimer.c 2012-08-10 14:45:33.000000000 +0000 +++ asterisk-13.1.0~dfsg/addons/ooh323c/src/ooTimer.c 2013-12-18 21:12:58.000000000 +0000 @@ -17,7 +17,6 @@ #include "asterisk.h" #include "asterisk/lock.h" -#include "ootypes.h" #include "ooDateTime.h" #include "ooTimer.h" #include "ootrace.h" @@ -102,7 +101,6 @@ void ooTimerFireExpired (OOCTXT* pctxt, DList *pList) { OOTimer* pTimer; - int ret = OO_OK; while (pList->count > 0) { pTimer = (OOTimer*) pList->head->data; @@ -114,7 +112,7 @@ */ if (pTimer->reRegister) ooTimerReset (pctxt, pList, pTimer); - ret = (*pTimer->timeoutCB)(pTimer->cbData); + (*pTimer->timeoutCB)(pTimer->cbData); if (!pTimer->reRegister) { ooTimerDelete (pctxt, pList, pTimer); @@ -122,8 +120,6 @@ } else break; } - - return (void)ret; } int ooTimerInsertEntry (OOCTXT* pctxt, DList *pList, OOTimer* pTimer) diff -Nru asterisk-11.7.0~dfsg/addons/ooh323c/src/printHandler.c asterisk-13.1.0~dfsg/addons/ooh323c/src/printHandler.c --- asterisk-11.7.0~dfsg/addons/ooh323c/src/printHandler.c 2011-11-04 19:50:10.000000000 +0000 +++ asterisk-13.1.0~dfsg/addons/ooh323c/src/printHandler.c 2014-07-22 14:22:00.000000000 +0000 @@ -268,7 +268,7 @@ if (bufsiz > 1) buffer[1] = '\0'; for (i = 0; i < numocts; i++) { if (i < bufsiz - 1) { - sprintf (lbuf, "%02x", data[i]); + sprintf (lbuf, "%02x", (unsigned)data[i]); strcat (&buffer[(i*2)+1], lbuf); } else break; diff -Nru asterisk-11.7.0~dfsg/addons/ooh323cDriver.c asterisk-13.1.0~dfsg/addons/ooh323cDriver.c --- asterisk-11.7.0~dfsg/addons/ooh323cDriver.c 2012-07-04 21:42:05.000000000 +0000 +++ asterisk-13.1.0~dfsg/addons/ooh323cDriver.c 2014-07-20 22:06:33.000000000 +0000 @@ -225,17 +225,17 @@ } int ooh323c_set_capability - (struct ast_codec_pref *prefs, struct ast_format_cap *cap, int dtmf, int dtmfcodec) + (struct ast_format_cap *cap, int dtmf, int dtmfcodec) { int ret = 0, x; - struct ast_format tmpfmt; if (gH323Debug) { ast_verb(0, "\tAdding capabilities to H323 endpoint\n"); } - for(x=0; ast_codec_pref_index(prefs, x, &tmpfmt); x++) + for(x=0; xcallType, call->callToken); @@ -423,15 +406,16 @@ &ooh323c_stop_transmit_datachannel, 0); - for(x=0; ast_codec_pref_index(prefs, x, &tmpfmt); x++) + for(x=0; xcallType, call->callToken); } - txframes = prefs->framing[x]; + txframes = ast_format_cap_get_format_framing(cap, format); ret= ooCallAddG711Capability(call, OO_G711ULAW64K, txframes, txframes, OORXANDTX, &ooh323c_start_receive_channel, @@ -439,13 +423,13 @@ &ooh323c_stop_receive_channel, &ooh323c_stop_transmit_channel); } - if(tmpfmt.id == AST_FORMAT_ALAW) + if(ast_format_cmp(format, ast_format_alaw) == AST_FORMAT_CMP_EQUAL) { if (gH323Debug) { ast_verb(0, "\tAdding g711 alaw capability to call(%s, %s)\n", call->callType, call->callToken); } - txframes = prefs->framing[x]; + txframes = ast_format_cap_get_format_framing(cap, format); ret= ooCallAddG711Capability(call, OO_G711ALAW64K, txframes, txframes, OORXANDTX, &ooh323c_start_receive_channel, @@ -454,13 +438,13 @@ &ooh323c_stop_transmit_channel); } - if(tmpfmt.id == AST_FORMAT_G726) + if(ast_format_cmp(format, ast_format_g726) == AST_FORMAT_CMP_EQUAL) { if (gH323Debug) { ast_verb(0, "\tAdding g726 capability to call (%s, %s)\n", call->callType, call->callToken); } - txframes = prefs->framing[x]; + txframes = ast_format_cap_get_format_framing(cap, format); ret = ooCallAddG726Capability(call, OO_G726, txframes, grxframes, FALSE, OORXANDTX, &ooh323c_start_receive_channel, &ooh323c_start_transmit_channel, @@ -469,13 +453,13 @@ } - if(tmpfmt.id == AST_FORMAT_G726_AAL2) + if(ast_format_cmp(format, ast_format_g726_aal2) == AST_FORMAT_CMP_EQUAL) { if (gH323Debug) { ast_verb(0, "\tAdding g726aal2 capability to call (%s, %s)\n", call->callType, call->callToken); } - txframes = prefs->framing[x]; + txframes = ast_format_cap_get_format_framing(cap, format); ret = ooCallAddG726Capability(call, OO_G726AAL2, txframes, grxframes, FALSE, OORXANDTX, &ooh323c_start_receive_channel, &ooh323c_start_transmit_channel, @@ -484,10 +468,10 @@ } - if(tmpfmt.id == AST_FORMAT_G729A) + if(ast_format_cmp(format, ast_format_g729) == AST_FORMAT_CMP_EQUAL) { - txframes = (prefs->framing[x])/10; + txframes = (ast_format_cap_get_format_framing(cap, format))/10; if (gH323Debug) { ast_verb(0, "\tAdding g729A capability to call(%s, %s)\n", call->callType, call->callToken); @@ -520,7 +504,7 @@ } - if(tmpfmt.id == AST_FORMAT_G723_1) + if(ast_format_cmp(format, ast_format_g723) == AST_FORMAT_CMP_EQUAL) { if (gH323Debug) { ast_verb(0, "\tAdding g7231 capability to call (%s, %s)\n", @@ -534,7 +518,7 @@ } - if(tmpfmt.id == AST_FORMAT_H263) + if(ast_format_cmp(format, ast_format_h263) == AST_FORMAT_CMP_EQUAL) { if (gH323Debug) { ast_verb(0, "\tAdding h263 capability to call (%s, %s)\n", @@ -548,7 +532,7 @@ } - if(tmpfmt.id == AST_FORMAT_GSM) + if(ast_format_cmp(format, ast_format_gsm) == AST_FORMAT_CMP_EQUAL) { if (gH323Debug) { ast_verb(0, "\tAdding gsm capability to call(%s, %s)\n", @@ -561,22 +545,7 @@ &ooh323c_stop_transmit_channel); } -#ifdef AST_FORMAT_AMRNB - if(tmpfmt.id == AST_FORMAT_AMRNB) - { - if (gH323Debug) { - ast_verb(0, "\tAdding AMR capability to call(%s, %s)\n", - call->callType, call->callToken); - } - ret = ooCallAddAMRNBCapability(call, OO_AMRNB, 4, 4, FALSE, - OORXANDTX, &ooh323c_start_receive_channel, - &ooh323c_start_transmit_channel, - &ooh323c_stop_receive_channel, - &ooh323c_stop_transmit_channel); - } -#endif -#ifdef AST_FORMAT_SPEEX - if(tmpfmt.id == AST_FORMAT_SPEEX) + if(ast_format_cmp(format, ast_format_speex) == AST_FORMAT_CMP_EQUAL) { if (gH323Debug) { ast_verb(0, "\tAdding Speex capability to call(%s, %s)\n", @@ -588,7 +557,8 @@ &ooh323c_stop_receive_channel, &ooh323c_stop_transmit_channel); } -#endif + + ao2_ref(format, -1); } return ret; } @@ -622,9 +592,9 @@ int ooh323c_start_receive_channel(ooCallData *call, ooLogicalChannel *pChannel) { - struct ast_format tmpfmt; - convertH323CapToAsteriskCap(pChannel->chanCap->cap, &tmpfmt); - if(tmpfmt.id) { + struct ast_format *tmpfmt = NULL; + tmpfmt = convertH323CapToAsteriskCap(pChannel->chanCap->cap); + if(tmpfmt) { /* ooh323_set_read_format(call, fmt); */ }else{ ast_log(LOG_ERROR, "Invalid capability type for receive channel %s\n", @@ -636,19 +606,17 @@ int ooh323c_start_transmit_channel(ooCallData *call, ooLogicalChannel *pChannel) { - struct ast_format tmpfmt; - convertH323CapToAsteriskCap(pChannel->chanCap->cap, &tmpfmt); - if(tmpfmt.id) { - switch (tmpfmt.id) { - case AST_FORMAT_ALAW: - case AST_FORMAT_ULAW: - ooh323_set_write_format(call, &tmpfmt, ((OOCapParams *)(pChannel->chanCap->params))->txframes); - break; - case AST_FORMAT_G729A: - ooh323_set_write_format(call, &tmpfmt, ((OOCapParams *)(pChannel->chanCap->params))->txframes*10); - break; - default: - ooh323_set_write_format(call, &tmpfmt, 0); + struct ast_format *tmpfmt = NULL; + tmpfmt = convertH323CapToAsteriskCap(pChannel->chanCap->cap); + + if (tmpfmt) { + if ((ast_format_cmp(tmpfmt, ast_format_alaw) == AST_FORMAT_CMP_EQUAL) || + (ast_format_cmp(tmpfmt, ast_format_ulaw) == AST_FORMAT_CMP_EQUAL)) { + ooh323_set_write_format(call, tmpfmt, ((OOCapParams *)(pChannel->chanCap->params))->txframes); + } else if (ast_format_cmp(tmpfmt, ast_format_g729) == AST_FORMAT_CMP_EQUAL) { + ooh323_set_write_format(call, tmpfmt, ((OOCapParams *)(pChannel->chanCap->params))->txframes*10); + } else { + ooh323_set_write_format(call, tmpfmt, 0); } }else{ ast_log(LOG_ERROR, "Invalid capability type for receive channel %s\n", @@ -693,41 +661,32 @@ return 1; } -struct ast_format *convertH323CapToAsteriskCap(int cap, struct ast_format *result) +struct ast_format *convertH323CapToAsteriskCap(int cap) { - ast_format_clear(result); switch(cap) { case OO_G711ULAW64K: - return ast_format_set(result, AST_FORMAT_ULAW, 0); + return ast_format_ulaw; case OO_G711ALAW64K: - return ast_format_set(result, AST_FORMAT_ALAW, 0); + return ast_format_alaw; case OO_GSMFULLRATE: - return ast_format_set(result, AST_FORMAT_GSM, 0); - -#ifdef AST_FORMAT_AMRNB - case OO_AMRNB: - return ast_format_set(result, AST_FORMAT_AMRNB, 0); -#endif -#ifdef AST_FORMAT_SPEEX + return ast_format_gsm; case OO_SPEEX: - return ast_format_set(result, AST_FORMAT_SPEEX, 0); -#endif - + return ast_format_speex; case OO_G729: - return ast_format_set(result, AST_FORMAT_G729A, 0); + return ast_format_g729; case OO_G729A: - return ast_format_set(result, AST_FORMAT_G729A, 0); + return ast_format_g729; case OO_G729B: - return ast_format_set(result, AST_FORMAT_G729A, 0); + return ast_format_g729; case OO_G7231: - return ast_format_set(result, AST_FORMAT_G723_1, 0); + return ast_format_g723; case OO_G726: - return ast_format_set(result, AST_FORMAT_G726, 0); + return ast_format_g726; case OO_G726AAL2: - return ast_format_set(result, AST_FORMAT_G726_AAL2, 0); + return ast_format_g726_aal2; case OO_H263VIDEO: - return ast_format_set(result, AST_FORMAT_H263, 0); + return ast_format_h263; default: ast_debug(1, "Cap %d is not supported by driver yet\n", cap); return NULL; diff -Nru asterisk-11.7.0~dfsg/addons/ooh323cDriver.h asterisk-13.1.0~dfsg/addons/ooh323cDriver.h --- asterisk-11.7.0~dfsg/addons/ooh323cDriver.h 2011-02-18 00:11:06.000000000 +0000 +++ asterisk-13.1.0~dfsg/addons/ooh323cDriver.h 2014-07-20 22:06:33.000000000 +0000 @@ -37,9 +37,9 @@ int ooh323c_start_call_thread(ooCallData *call); int ooh323c_stop_call_thread(ooCallData *call); int ooh323c_set_capability - (struct ast_codec_pref *prefs, struct ast_format_cap *cap, int dtmf, int dtmfcodec); -struct ast_format *convertH323CapToAsteriskCap(int cap, struct ast_format *format); + (struct ast_format_cap *cap, int dtmf, int dtmfcodec); +struct ast_format *convertH323CapToAsteriskCap(int cap); int ooh323c_set_capability_for_call - (ooCallData *call, struct ast_codec_pref *prefs, struct ast_format_cap *cap, int dtmf, int dtmfcodec, + (ooCallData *call, struct ast_format_cap *cap, int dtmf, int dtmfcodec, int t38support, int g729onlyA); #endif diff -Nru asterisk-11.7.0~dfsg/addons/res_config_mysql.c asterisk-13.1.0~dfsg/addons/res_config_mysql.c --- asterisk-11.7.0~dfsg/addons/res_config_mysql.c 2013-03-12 21:17:17.000000000 +0000 +++ asterisk-13.1.0~dfsg/addons/res_config_mysql.c 2014-07-25 16:47:17.000000000 +0000 @@ -30,7 +30,7 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 382943 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $") #include @@ -316,7 +316,7 @@ return orig; } -static struct ast_variable *realtime_mysql(const char *database, const char *table, va_list ap) +static struct ast_variable *realtime_mysql(const char *database, const char *table, const struct ast_variable *rt_fields) { struct mysql_conn *dbh; MYSQL_RES *result; @@ -328,7 +328,7 @@ char *stringp; char *chunk; char *op; - const char *newparam, *newval; + const struct ast_variable *field = rt_fields; struct ast_variable *var=NULL, *prev=NULL; if (!(dbh = find_database(database, 0))) { @@ -343,7 +343,7 @@ } /* Get the first parameter and first value in our list of passed paramater/value pairs */ - if (!(newparam = va_arg(ap, const char *)) || !(newval = va_arg(ap, const char *))) { + if (!field) { ast_log(LOG_WARNING, "MySQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n"); release_database(dbh); return NULL; @@ -358,21 +358,20 @@ /* Create the first part of the query using the first parameter/value pairs we just extracted If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */ - if (!strchr(newparam, ' ')) + if (!strchr(field->name, ' ')) op = " ="; else op = ""; - ESCAPE_STRING(buf, newval); - ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, ast_str_buffer(buf)); - while ((newparam = va_arg(ap, const char *))) { - newval = va_arg(ap, const char *); - if (!strchr(newparam, ' ')) + ESCAPE_STRING(buf, field->value); + ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'", table, field->name, op, ast_str_buffer(buf)); + while ((field = field->next)) { + if (!strchr(field->name, ' ')) op = " ="; else op = ""; - ESCAPE_STRING(buf, newval); - ast_str_append(&sql, 0, " AND %s%s '%s'", newparam, op, ast_str_buffer(buf)); + ESCAPE_STRING(buf, field->value); + ast_str_append(&sql, 0, " AND %s%s '%s'", field->name, op, ast_str_buffer(buf)); } ast_debug(1, "MySQL RealTime: Retrieve SQL: %s\n", ast_str_buffer(sql)); @@ -417,7 +416,7 @@ return var; } -static struct ast_config *realtime_multi_mysql(const char *database, const char *table, va_list ap) +static struct ast_config *realtime_multi_mysql(const char *database, const char *table, const struct ast_variable *rt_fields) { struct mysql_conn *dbh; MYSQL_RES *result; @@ -430,7 +429,7 @@ char *stringp; char *chunk; char *op; - const char *newparam, *newval; + const struct ast_variable *field = rt_fields; struct ast_variable *var = NULL; struct ast_config *cfg = NULL; struct ast_category *cat = NULL; @@ -454,14 +453,14 @@ } /* Get the first parameter and first value in our list of passed paramater/value pairs */ - if (!(newparam = va_arg(ap, const char *)) || !(newval = va_arg(ap, const char *))) { + if (!field) { ast_log(LOG_WARNING, "MySQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n"); ast_config_destroy(cfg); release_database(dbh); return NULL; } - initfield = ast_strdupa(newparam); + initfield = ast_strdupa(field->name); if ((op = strchr(initfield, ' '))) { *op = '\0'; } @@ -476,18 +475,17 @@ /* Create the first part of the query using the first parameter/value pairs we just extracted If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */ - if (!strchr(newparam, ' ')) + if (!strchr(field->name, ' ')) op = " ="; else op = ""; - ESCAPE_STRING(buf, newval); - ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, ast_str_buffer(buf)); - while ((newparam = va_arg(ap, const char *))) { - newval = va_arg(ap, const char *); - if (!strchr(newparam, ' ')) op = " ="; else op = ""; - ESCAPE_STRING(buf, newval); - ast_str_append(&sql, 0, " AND %s%s '%s'", newparam, op, ast_str_buffer(buf)); + ESCAPE_STRING(buf, field->value); + ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'", table, field->name, op, ast_str_buffer(buf)); + while ((field = field->next)) { + if (!strchr(field->name, ' ')) op = " ="; else op = ""; + ESCAPE_STRING(buf, field->value); + ast_str_append(&sql, 0, " AND %s%s '%s'", field->name, op, ast_str_buffer(buf)); } if (initfield) { @@ -540,11 +538,11 @@ return cfg; } -static int update_mysql(const char *database, const char *tablename, const char *keyfield, const char *lookup, va_list ap) +static int update_mysql(const char *database, const char *tablename, const char *keyfield, const char *lookup, const struct ast_variable *rt_fields) { struct mysql_conn *dbh; my_ulonglong numrows; - const char *newparam, *newval; + const struct ast_variable *field = rt_fields; struct ast_str *sql = ast_str_thread_get(&sql_buf, 100), *buf = ast_str_thread_get(&scratch_buf, 100); struct tables *table; struct columns *column = NULL; @@ -574,7 +572,7 @@ } /* Get the first parameter and first value in our list of passed paramater/value pairs */ - if (!(newparam = va_arg(ap, const char *)) || !(newval = va_arg(ap, const char *))) { + if (!field) { ast_log(LOG_WARNING, "MySQL RealTime: Realtime update requires at least 1 parameter and 1 value to update.\n"); release_table(table); release_database(dbh); @@ -582,8 +580,8 @@ } /* Check that the column exists in the table */ - if (!(column = find_column(table, newparam))) { - ast_log(LOG_ERROR, "MySQL RealTime: Updating column '%s', but that column does not exist within the table '%s' (first pair MUST exist)!\n", newparam, tablename); + if (!(column = find_column(table, field->name))) { + ast_log(LOG_ERROR, "MySQL RealTime: Updating column '%s', but that column does not exist within the table '%s' (first pair MUST exist)!\n", field->name, tablename); release_table(table); release_database(dbh); return -1; @@ -599,29 +597,27 @@ /* Create the first part of the query using the first parameter/value pairs we just extracted If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */ - ESCAPE_STRING(buf, newval); - ast_str_set(&sql, 0, "UPDATE %s SET `%s` = '%s'", tablename, newparam, ast_str_buffer(buf)); + ESCAPE_STRING(buf, field->value); + ast_str_set(&sql, 0, "UPDATE %s SET `%s` = '%s'", tablename, field->name, ast_str_buffer(buf)); /* If the column length isn't long enough, give a chance to lengthen it. */ if (strncmp(column->type, "char", 4) == 0 || strncmp(column->type, "varchar", 7) == 0) { - internal_require(database, tablename, newparam, RQ_CHAR, ast_str_strlen(buf), SENTINEL); + internal_require(database, tablename, field->name, RQ_CHAR, ast_str_strlen(buf), SENTINEL); } - while ((newparam = va_arg(ap, const char *))) { - newval = va_arg(ap, const char *); - + while ((field = field->next)) { /* If the column is not within the table, then skip it */ - if (!(column = find_column(table, newparam))) { - ast_log(LOG_WARNING, "Attempted to update column '%s' in table '%s', but column does not exist!\n", newparam, tablename); + if (!(column = find_column(table, field->name))) { + ast_log(LOG_WARNING, "Attempted to update column '%s' in table '%s', but column does not exist!\n", field->name, tablename); continue; } - ESCAPE_STRING(buf, newval); - ast_str_append(&sql, 0, ", `%s` = '%s'", newparam, ast_str_buffer(buf)); + ESCAPE_STRING(buf, field->value); + ast_str_append(&sql, 0, ", `%s` = '%s'", field->name, ast_str_buffer(buf)); /* If the column length isn't long enough, give a chance to lengthen it. */ if (strncmp(column->type, "char", 4) == 0 || strncmp(column->type, "varchar", 7) == 0) { - internal_require(database, tablename, newparam, RQ_CHAR, ast_str_strlen(buf), SENTINEL); + internal_require(database, tablename, field->name, RQ_CHAR, ast_str_strlen(buf), SENTINEL); } } @@ -653,12 +649,12 @@ return (int)numrows; } -static int update2_mysql(const char *database, const char *tablename, va_list ap) +static int update2_mysql(const char *database, const char *tablename, const struct ast_variable *lookup_fields, const struct ast_variable *update_fields) { struct mysql_conn *dbh; my_ulonglong numrows; int first; - const char *newparam, *newval; + const struct ast_variable *field; struct ast_str *sql = ast_str_thread_get(&sql_buf, 100), *buf = ast_str_thread_get(&scratch_buf, 100); struct ast_str *where = ast_str_thread_get(&sql2_buf, 100); struct tables *table; @@ -697,51 +693,38 @@ } first = 1; - while ((newparam = va_arg(ap, const char *))) { - if (!(column = find_column(table, newparam))) { - ast_log(LOG_ERROR, "Updating on column '%s', but that column does not exist within the table '%s'!\n", newparam, tablename); + for (field = lookup_fields; field; field = field->next) { + if (!(column = find_column(table, field->name))) { + ast_log(LOG_ERROR, "Updating on column '%s', but that column does not exist within the table '%s'!\n", field->name, tablename); release_table(table); release_database(dbh); return -1; } - if (!(newval = va_arg(ap, const char *))) { - ast_log(LOG_ERROR, "Invalid arguments: no value specified for column '%s' on '%s@%s'\n", newparam, tablename, database); - release_table(table); - release_database(dbh); - return -1; - } - ESCAPE_STRING(buf, newval); - ast_str_append(&where, 0, "%s `%s` = '%s'", first ? "" : " AND", newparam, ast_str_buffer(buf)); + ESCAPE_STRING(buf, field->value); + ast_str_append(&where, 0, "%s `%s` = '%s'", first ? "" : " AND", field->name, ast_str_buffer(buf)); first = 0; /* If the column length isn't long enough, give a chance to lengthen it. */ if (strncmp(column->type, "char", 4) == 0 || strncmp(column->type, "varchar", 7) == 0) { - internal_require(database, tablename, newparam, RQ_CHAR, ast_str_strlen(buf), SENTINEL); + internal_require(database, tablename, field->name, RQ_CHAR, ast_str_strlen(buf), SENTINEL); } } first = 1; - while ((newparam = va_arg(ap, const char *))) { - if (!(newval = va_arg(ap, const char *))) { - ast_log(LOG_ERROR, "Invalid arguments: no value specified for column '%s' on '%s@%s'\n", newparam, tablename, database); - release_table(table); - release_database(dbh); - return -1; - } - + for (field = update_fields; field; field = field->next) { /* If the column is not within the table, then skip it */ - if (!(column = find_column(table, newparam))) { - ast_log(LOG_WARNING, "Attempted to update column '%s' in table '%s', but column does not exist!\n", newparam, tablename); + if (!(column = find_column(table, field->name))) { + ast_log(LOG_WARNING, "Attempted to update column '%s' in table '%s', but column does not exist!\n", field->name, tablename); continue; } - ESCAPE_STRING(buf, newval); - ast_str_append(&sql, 0, "%s `%s` = '%s'", first ? "" : ",", newparam, ast_str_buffer(buf)); + ESCAPE_STRING(buf, field->value); + ast_str_append(&sql, 0, "%s `%s` = '%s'", first ? "" : ",", field->name, ast_str_buffer(buf)); first = 0; /* If the column length isn't long enough, give a chance to lengthen it. */ if (strncmp(column->type, "char", 4) == 0 || strncmp(column->type, "varchar", 7) == 0) { - internal_require(database, tablename, newparam, RQ_CHAR, ast_str_strlen(buf), SENTINEL); + internal_require(database, tablename, field->name, RQ_CHAR, ast_str_strlen(buf), SENTINEL); } } @@ -773,14 +756,14 @@ return (int)numrows; } -static int store_mysql(const char *database, const char *table, va_list ap) +static int store_mysql(const char *database, const char *table, const struct ast_variable *rt_fields) { struct mysql_conn *dbh; my_ulonglong insertid; struct ast_str *sql = ast_str_thread_get(&sql_buf, 16); struct ast_str *sql2 = ast_str_thread_get(&sql2_buf, 16); struct ast_str *buf = ast_str_thread_get(&scratch_buf, 16); - const char *newparam, *newval; + const struct ast_variable *field = rt_fields; if (!(dbh = find_database(database, 1))) { ast_log(LOG_WARNING, "MySQL RealTime: Invalid database specified: '%s' (check res_mysql.conf)\n", database); @@ -793,7 +776,7 @@ return -1; } /* Get the first parameter and first value in our list of passed paramater/value pairs */ - if (!(newparam = va_arg(ap, const char *)) || !(newval = va_arg(ap, const char *))) { + if (!field) { ast_log(LOG_WARNING, "MySQL RealTime: Realtime storage requires at least 1 parameter and 1 value to search on.\n"); release_database(dbh); return -1; @@ -805,20 +788,17 @@ } /* Create the first part of the query using the first parameter/value pairs we just extracted If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */ - ESCAPE_STRING(buf, newval); - ast_str_set(&sql, 0, "INSERT INTO %s (`%s`", table, newparam); + ESCAPE_STRING(buf, field->value); + ast_str_set(&sql, 0, "INSERT INTO %s (`%s`", table, field->name); ast_str_set(&sql2, 0, ") VALUES ('%s'", ast_str_buffer(buf)); - internal_require(database, table, newparam, RQ_CHAR, ast_str_strlen(buf), SENTINEL); + internal_require(database, table, field->name, RQ_CHAR, ast_str_strlen(buf), SENTINEL); - while ((newparam = va_arg(ap, const char *))) { - if ((newval = va_arg(ap, const char *))) { - ESCAPE_STRING(buf, newval); - } else { - ast_str_reset(buf); - } - if (internal_require(database, table, newparam, RQ_CHAR, ast_str_strlen(buf), SENTINEL) == 0) { - ast_str_append(&sql, 0, ", `%s`", newparam); + while ((field = field->next)) { + ESCAPE_STRING(buf, field->value); + + if (internal_require(database, table, field->name, RQ_CHAR, ast_str_strlen(buf), SENTINEL) == 0) { + ast_str_append(&sql, 0, ", `%s`", field->name); ast_str_append(&sql2, 0, ", '%s'", ast_str_buffer(buf)); } } @@ -846,13 +826,13 @@ return (int)insertid; } -static int destroy_mysql(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap) +static int destroy_mysql(const char *database, const char *table, const char *keyfield, const char *lookup, const struct ast_variable *rt_fields) { struct mysql_conn *dbh; my_ulonglong numrows; struct ast_str *sql = ast_str_thread_get(&sql_buf, 16); struct ast_str *buf = ast_str_thread_get(&scratch_buf, 16); - const char *newparam, *newval; + const struct ast_variable *field; if (!(dbh = find_database(database, 1))) { ast_log(LOG_WARNING, "MySQL RealTime: Invalid database specified: '%s' (check res_mysql.conf)\n", database); @@ -884,10 +864,9 @@ If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */ ESCAPE_STRING(buf, lookup); ast_str_set(&sql, 0, "DELETE FROM %s WHERE `%s` = '%s'", table, keyfield, ast_str_buffer(buf)); - while ((newparam = va_arg(ap, const char *))) { - newval = va_arg(ap, const char *); - ESCAPE_STRING(buf, newval); - ast_str_append(&sql, 0, " AND `%s` = '%s'", newparam, ast_str_buffer(buf)); + for (field = rt_fields; field; field = field->next) { + ESCAPE_STRING(buf, field->value); + ast_str_append(&sql, 0, " AND `%s` = '%s'", field->name, ast_str_buffer(buf)); } ast_debug(1, "MySQL RealTime: Delete SQL: %s\n", ast_str_buffer(sql)); @@ -1769,6 +1748,7 @@ } AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "MySQL RealTime Configuration Driver", + .support_level = AST_MODULE_SUPPORT_EXTENDED, .load = load_module, .unload = unload_module, .reload = reload, diff -Nru asterisk-11.7.0~dfsg/agi/Makefile asterisk-13.1.0~dfsg/agi/Makefile --- asterisk-11.7.0~dfsg/agi/Makefile 2011-08-17 19:30:50.000000000 +0000 +++ asterisk-13.1.0~dfsg/agi/Makefile 2012-10-14 21:56:13.000000000 +0000 @@ -1,5 +1,5 @@ # -# Asterisk -- A telephony toolkit for Linux. +# Asterisk -- An open source telephony toolkit. # # Makefile for AGI-related stuff # diff -Nru asterisk-11.7.0~dfsg/apps/app_adsiprog.c asterisk-13.1.0~dfsg/apps/app_adsiprog.c --- asterisk-11.7.0~dfsg/apps/app_adsiprog.c 2012-01-09 22:15:50.000000000 +0000 +++ asterisk-13.1.0~dfsg/apps/app_adsiprog.c 2014-07-25 16:47:17.000000000 +0000 @@ -25,6 +25,15 @@ * \ingroup applications */ +/*! \li \ref app_adsiprog.c uses the configuration file \ref adsi.conf + * \addtogroup configuration_file Configuration Files + */ + +/*! + * \page adsi.conf adsi.conf + * \verbinclude adsi.conf.sample + */ + /*** MODULEINFO res_adsi extended @@ -32,7 +41,7 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 350223 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $") #include #include @@ -202,7 +211,7 @@ if (!(argtype & ARG_NUMBER)) return -1; /* Octal value */ - if (sscanf(src, "%30o", (int *)out) != 1) + if (sscanf(src, "%30o", (unsigned *)out) != 1) return -1; if (argtype & ARG_STRING) { /* Convert */ @@ -1585,6 +1594,16 @@ return ast_unregister_application(app); } +/*! + * \brief Load the module + * + * Module loading including tests for configuration or dependencies. + * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE, + * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails + * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the + * configuration file or other non-critical problem return + * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS. + */ static int load_module(void) { if (ast_register_application_xml(app, adsi_exec)) @@ -1593,6 +1612,7 @@ } AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Asterisk ADSI Programming Application", + .support_level = AST_MODULE_SUPPORT_EXTENDED, .load = load_module, .unload = unload_module, .nonoptreq = "res_adsi", diff -Nru asterisk-11.7.0~dfsg/apps/app_agent_pool.c asterisk-13.1.0~dfsg/apps/app_agent_pool.c --- asterisk-11.7.0~dfsg/apps/app_agent_pool.c 1970-01-01 00:00:00.000000000 +0000 +++ asterisk-13.1.0~dfsg/apps/app_agent_pool.c 2014-11-06 19:22:22.000000000 +0000 @@ -0,0 +1,2707 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013 Digium, Inc. + * + * Richard Mudgett + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! + * \file + * \brief Call center agent pool. + * + * \author Richard Mudgett + * + * See Also: + * \arg \ref AstCREDITS + * \arg \ref Config_agent + */ +/*** MODULEINFO + core + ***/ + + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 427512 $") + +#include "asterisk/cli.h" +#include "asterisk/app.h" +#include "asterisk/pbx.h" +#include "asterisk/module.h" +#include "asterisk/channel.h" +#include "asterisk/bridge.h" +#include "asterisk/bridge_internal.h" +#include "asterisk/bridge_basic.h" +#include "asterisk/bridge_after.h" +#include "asterisk/config_options.h" +#include "asterisk/features_config.h" +#include "asterisk/astobj2.h" +#include "asterisk/stringfields.h" +#include "asterisk/stasis_channels.h" +#include "asterisk/causes.h" + +/*** DOCUMENTATION + + + Login an agent. + + + + + + + + + + + Login an agent to the system. Any agent authentication is assumed to + already be done by dialplan. While logged in, the agent can receive calls + and will hear the sound file specified by the config option custom_beep + when a new call comes in for the agent. Login failures will continue in + the dialplan with AGENT_STATUS set. + Before logging in, you can setup on the real agent channel the + CHANNEL(dtmf-features) an agent will have when talking to a caller + and you can setup on the channel running this application the + CONNECTEDLINE() information the agent will see while waiting for a + caller. + AGENT_STATUS enumeration values: + + The specified agent is invalid. + The agent is already logged in. + + The Agents:AgentId device state is + available to monitor the status of the agent. + + + Authenticate + Queue + AddQueueMember + RemoveQueueMember + PauseQueueMember + UnpauseQueueMember + AGENT + CHANNEL(dtmf-features) + CONNECTEDLINE() + agents.conf + queues.conf + + + + + Request an agent to connect with the channel. + + + + + + Request an agent to connect with the channel. Failure to find, + alert the agent, or acknowledge the call will continue in the dialplan + with AGENT_STATUS set. + AGENT_STATUS enumeration values: + + The specified agent is invalid. + The agent is not available. + The agent is on another call. + The agent did not connect with the + call. The agent most likely did not acknowledge the call. + Alerting the agent failed. + + + + AgentLogin + + + + + Gets information about an Agent + + + + + The valid items to retrieve are: + + + (default) The status of the agent (LOGGEDIN | LOGGEDOUT) + + + Deprecated. The dialplan handles any agent authentication. + + + The name of the agent + + + MusicOnHold class + + + The name of the active channel for the Agent (AgentLogin) + + + The untruncated name of the active channel for the Agent (AgentLogin) + + + + + + + + + Lists agents and their status. + + + + + + Will list info about all defined agents. + + + Agents + AgentsComplete + + + + + + Response event in a series to the Agents AMI action containing + information about a defined agent. + + + + Agent ID of the agent. + + + User friendly name of the agent. + + + Current status of the agent. + The valid values are: + + + + + + + + BRIDGEPEER value on agent channel. + Present if Status value is AGENT_ONCALL. + + + Epoche time when the agent started talking with the caller. + Present if Status value is AGENT_ONCALL. + + + Epoche time when the agent logged in. + Present if Status value is AGENT_IDLE or AGENT_ONCALL. + + + + + + The channel snapshot is present if the Status value is AGENT_IDLE or AGENT_ONCALL. + + + Agents + + + + + + + Final response event in a series of events to the Agents AMI action. + + + + + + Agents + + + + + + Sets an agent as no longer logged in. + + + + + Agent ID of the agent to log off. + + + Set to true to not hangup existing calls. + + + + Sets an agent as no longer logged in. + + + + Agent pool applications + + Option changes take effect on agent login or after an agent + disconnects from a call. + + + + Unused, but reserved. + + + Configure an agent for the pool. + + + + + Enable to require the agent to acknowledge a call. + + Enable to require the agent to give a DTMF acknowledgement + when the agent receives a call. + The option is overridden by AGENTACKCALL on agent login. + + + + + DTMF key sequence the agent uses to acknowledge a call. + + The option is overridden by AGENTACCEPTDTMF on agent login. + The option is ignored unless the ackcall option is enabled. + + + + + Time the agent has to acknowledge a call before being logged off. + + Set how many seconds a call for the agent has to wait for the + agent to acknowledge the call before the agent is automatically + logged off. If set to zero then the call will wait forever for + the agent to acknowledge. + The option is overridden by AGENTAUTOLOGOFF on agent login. + The option is ignored unless the ackcall option is enabled. + + + + + Minimum time the agent has between calls. + + Set the minimum amount of time in milliseconds after + disconnecting a call before the agent can receive a new call. + The option is overridden by AGENTWRAPUPTIME on agent login. + + + + + Music on hold class the agent listens to between calls. + + + + + + Enable to automatically record calls the agent takes. + + Enable recording calls the agent takes automatically by + invoking the automixmon DTMF feature when the agent connects + to a caller. See features.conf.sample for information about + the automixmon feature. + + + + + Sound file played to alert the agent when a call is present. + + + + + + A friendly name for the agent used in log messages. + + + + + + + + ***/ + +/* ------------------------------------------------------------------- */ + +#define AST_MAX_BUF 256 + +/*! Maximum wait time (in ms) for the custom_beep file to play announcing the caller. */ +#define CALLER_SAFETY_TIMEOUT_TIME (2 * 60 * 1000) + +/*! Number of seconds to wait for local channel optimizations to complete. */ +#define LOGIN_WAIT_TIMEOUT_TIME 5 + +static const char app_agent_login[] = "AgentLogin"; +static const char app_agent_request[] = "AgentRequest"; + +/*! Agent config parameters. */ +struct agent_cfg { + AST_DECLARE_STRING_FIELDS( + /*! Identification of the agent. (agents config container key) */ + AST_STRING_FIELD(username); + /*! Name of agent for logging and querying purposes */ + AST_STRING_FIELD(full_name); + + /*! + * \brief DTMF string for an agent to accept a call. + * + * \note The channel variable AGENTACCEPTDTMF overrides on login. + */ + AST_STRING_FIELD(dtmf_accept); + /*! Beep sound file to use. Alert the agent a call is waiting. */ + AST_STRING_FIELD(beep_sound); + /*! MOH class to use while agent waiting for call. */ + AST_STRING_FIELD(moh); + ); + /*! + * \brief Number of seconds for agent to ack a call before being logged off. + * + * \note The channel variable AGENTAUTOLOGOFF overrides on login. + * \note If zero then timer is disabled. + */ + unsigned int auto_logoff; + /*! + * \brief Time after a call in ms before the agent can get a new call. + * + * \note The channel variable AGENTWRAPUPTIME overrides on login. + */ + unsigned int wrapup_time; + /*! + * \brief TRUE if agent needs to ack a call to accept it. + * + * \note The channel variable AGENTACKCALL overrides on login. + */ + int ack_call; + /*! TRUE if agent calls are automatically recorded. */ + int record_agent_calls; +}; + +/*! + * \internal + * \brief Agent config ao2 container sort function. + * \since 12.0.0 + * + * \param obj_left pointer to the (user-defined part) of an object. + * \param obj_right pointer to the (user-defined part) of an object. + * \param flags flags from ao2_callback() + * OBJ_POINTER - if set, 'obj_right', is an object. + * OBJ_KEY - if set, 'obj_right', is a search key item that is not an object. + * OBJ_PARTIAL_KEY - if set, 'obj_right', is a partial search key item that is not an object. + * + * \retval <0 if obj_left < obj_right + * \retval =0 if obj_left == obj_right + * \retval >0 if obj_left > obj_right + */ +static int agent_cfg_sort_cmp(const void *obj_left, const void *obj_right, int flags) +{ + const struct agent_cfg *cfg_left = obj_left; + const struct agent_cfg *cfg_right = obj_right; + const char *right_key = obj_right; + int cmp; + + switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) { + default: + case OBJ_POINTER: + right_key = cfg_right->username; + /* Fall through */ + case OBJ_KEY: + cmp = strcmp(cfg_left->username, right_key); + break; + case OBJ_PARTIAL_KEY: + cmp = strncmp(cfg_left->username, right_key, strlen(right_key)); + break; + } + return cmp; +} + +static void agent_cfg_destructor(void *vdoomed) +{ + struct agent_cfg *doomed = vdoomed; + + ast_string_field_free_memory(doomed); +} + +static void *agent_cfg_alloc(const char *name) +{ + struct agent_cfg *cfg; + + cfg = ao2_alloc_options(sizeof(*cfg), agent_cfg_destructor, + AO2_ALLOC_OPT_LOCK_NOLOCK); + if (!cfg || ast_string_field_init(cfg, 64)) { + return NULL; + } + ast_string_field_set(cfg, username, name); + return cfg; +} + +static void *agent_cfg_find(struct ao2_container *agents, const char *username) +{ + return ao2_find(agents, username, OBJ_KEY); +} + +/*! Agents configuration */ +struct agents_cfg { + /*! Master configured agents container. */ + struct ao2_container *agents; +}; + +static struct aco_type agent_type = { + .type = ACO_ITEM, + .name = "agent-id", + .category_match = ACO_BLACKLIST, + .category = "^(general|agents)$", + .item_alloc = agent_cfg_alloc, + .item_find = agent_cfg_find, + .item_offset = offsetof(struct agents_cfg, agents), +}; + +static struct aco_type *agent_types[] = ACO_TYPES(&agent_type); + +/* The general category is reserved, but unused */ +static struct aco_type general_type = { + .type = ACO_GLOBAL, + .name = "global", + .category_match = ACO_WHITELIST, + .category = "^general$", +}; + +static struct aco_file agents_conf = { + .filename = "agents.conf", + .types = ACO_TYPES(&general_type, &agent_type), +}; + +static AO2_GLOBAL_OBJ_STATIC(cfg_handle); + +static void agents_cfg_destructor(void *vdoomed) +{ + struct agents_cfg *doomed = vdoomed; + + ao2_cleanup(doomed->agents); + doomed->agents = NULL; +} + +/*! + * \internal + * \brief Create struct agents_cfg object. + * \since 12.0.0 + * + * \note A lock is not needed for the object or any secondary + * created cfg objects. These objects are immutable after the + * config is loaded and applied. + * + * \retval New struct agents_cfg object. + * \retval NULL on error. + */ +static void *agents_cfg_alloc(void) +{ + struct agents_cfg *cfg; + + cfg = ao2_alloc_options(sizeof(*cfg), agents_cfg_destructor, + AO2_ALLOC_OPT_LOCK_NOLOCK); + if (!cfg) { + return NULL; + } + cfg->agents = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK, + AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT, agent_cfg_sort_cmp, NULL); + if (!cfg->agents) { + ao2_ref(cfg, -1); + cfg = NULL; + } + return cfg; +} + +static void agents_post_apply_config(void); + +CONFIG_INFO_STANDARD(cfg_info, cfg_handle, agents_cfg_alloc, + .files = ACO_FILES(&agents_conf), + .post_apply_config = agents_post_apply_config, +); + +static void destroy_config(void) +{ + ao2_global_obj_release(cfg_handle); + aco_info_destroy(&cfg_info); +} + +static int load_config(void) +{ + if (aco_info_init(&cfg_info)) { + return -1; + } + + /* Agent options */ + aco_option_register(&cfg_info, "ackcall", ACO_EXACT, agent_types, "no", OPT_BOOL_T, 1, FLDSET(struct agent_cfg, ack_call)); + aco_option_register(&cfg_info, "acceptdtmf", ACO_EXACT, agent_types, "#", OPT_STRINGFIELD_T, 1, STRFLDSET(struct agent_cfg, dtmf_accept)); + aco_option_register(&cfg_info, "autologoff", ACO_EXACT, agent_types, "0", OPT_UINT_T, 0, FLDSET(struct agent_cfg, auto_logoff)); + aco_option_register(&cfg_info, "wrapuptime", ACO_EXACT, agent_types, "0", OPT_UINT_T, 0, FLDSET(struct agent_cfg, wrapup_time)); + aco_option_register(&cfg_info, "musiconhold", ACO_EXACT, agent_types, "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct agent_cfg, moh)); + aco_option_register(&cfg_info, "recordagentcalls", ACO_EXACT, agent_types, "no", OPT_BOOL_T, 1, FLDSET(struct agent_cfg, record_agent_calls)); + aco_option_register(&cfg_info, "custom_beep", ACO_EXACT, agent_types, "beep", OPT_STRINGFIELD_T, 0, STRFLDSET(struct agent_cfg, beep_sound)); + aco_option_register(&cfg_info, "fullname", ACO_EXACT, agent_types, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct agent_cfg, full_name)); + + if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) { + goto error; + } + + return 0; + +error: + destroy_config(); + return -1; +} + +enum agent_state { + /*! The agent is defined but an agent is not present. */ + AGENT_STATE_LOGGED_OUT, + /*! Forced initial login wait to allow any local channel optimizations to happen. */ + AGENT_STATE_PROBATION_WAIT, + /*! The agent is ready for a call. */ + AGENT_STATE_READY_FOR_CALL, + /*! The agent has a call waiting to connect. */ + AGENT_STATE_CALL_PRESENT, + /*! The agent needs to ack the call. */ + AGENT_STATE_CALL_WAIT_ACK, + /*! The agent is connected with a call. */ + AGENT_STATE_ON_CALL, + /*! The agent is resting between calls. */ + AGENT_STATE_CALL_WRAPUP, + /*! The agent is being kicked out. */ + AGENT_STATE_LOGGING_OUT, +}; + +/*! Agent config option override flags. */ +enum agent_override_flags { + AGENT_FLAG_ACK_CALL = (1 << 0), + AGENT_FLAG_DTMF_ACCEPT = (1 << 1), + AGENT_FLAG_AUTO_LOGOFF = (1 << 2), + AGENT_FLAG_WRAPUP_TIME = (1 << 3), +}; + +/*! \brief Structure representing an agent. */ +struct agent_pvt { + AST_DECLARE_STRING_FIELDS( + /*! Identification of the agent. (agents container key) */ + AST_STRING_FIELD(username); + /*! Login override DTMF string for an agent to accept a call. */ + AST_STRING_FIELD(override_dtmf_accept); + ); + /*! Connected line information to send when reentering the holding bridge. */ + struct ast_party_connected_line waiting_colp; + /*! Flags show if settings were overridden by channel vars. */ + unsigned int flags; + /*! Login override number of seconds for agent to ack a call before being logged off. */ + unsigned int override_auto_logoff; + /*! Login override time after a call in ms before the agent can get a new call. */ + unsigned int override_wrapup_time; + /*! Login override if agent needs to ack a call to accept it. */ + unsigned int override_ack_call:1; + + /*! TRUE if the agent is requested to logoff when the current call ends. */ + unsigned int deferred_logoff:1; + + /*! Mark and sweep config update to determine if an agent is dead. */ + unsigned int the_mark:1; + /*! + * \brief TRUE if the agent is no longer configured and is being destroyed. + * + * \note Agents cannot log in if they are dead. + */ + unsigned int dead:1; + + /*! Agent control state variable. */ + enum agent_state state; + /*! Custom device state of agent. */ + enum ast_device_state devstate; + + /*! When agent first logged in */ + time_t login_start; + /*! When agent login probation started. */ + time_t probation_start; + /*! When call started */ + time_t call_start; + /*! When ack timer started */ + struct timeval ack_time; + /*! When last disconnected */ + struct timeval last_disconnect; + + /*! Caller is waiting in this bridge for agent to join. (Holds ref) */ + struct ast_bridge *caller_bridge; + /*! Agent is logged in with this channel. (Holds ref) (NULL if not logged in.) */ + struct ast_channel *logged; + /*! Active config values from config file. (Holds ref) */ + struct agent_cfg *cfg; +}; + +/*! Container of defined agents. */ +static struct ao2_container *agents; + +/*! + * \brief Lock the agent. + * + * \param agent Agent to lock + * + * \return Nothing + */ +#define agent_lock(agent) _agent_lock(agent, __FILE__, __PRETTY_FUNCTION__, __LINE__, #agent) +static inline void _agent_lock(struct agent_pvt *agent, const char *file, const char *function, int line, const char *var) +{ + __ao2_lock(agent, AO2_LOCK_REQ_MUTEX, file, function, line, var); +} + +/*! + * \brief Unlock the agent. + * + * \param agent Agent to unlock + * + * \return Nothing + */ +#define agent_unlock(agent) _agent_unlock(agent, __FILE__, __PRETTY_FUNCTION__, __LINE__, #agent) +static inline void _agent_unlock(struct agent_pvt *agent, const char *file, const char *function, int line, const char *var) +{ + __ao2_unlock(agent, file, function, line, var); +} + +/*! + * \internal + * \brief Obtain the agent logged in channel lock if it exists. + * \since 12.0.0 + * + * \param agent Pointer to the LOCKED agent_pvt. + * + * \note Assumes the agent lock is already obtained. + * + * \note Defined locking order is channel lock then agent lock. + * + * \return Nothing + */ +static struct ast_channel *agent_lock_logged(struct agent_pvt *agent) +{ + struct ast_channel *logged; + + for (;;) { + if (!agent->logged) { /* No owner. Nothing to do. */ + return NULL; + } + + /* If we don't ref the logged, it could be killed when we unlock the agent. */ + logged = ast_channel_ref(agent->logged); + + /* Locking logged requires us to lock channel, then agent. */ + agent_unlock(agent); + ast_channel_lock(logged); + agent_lock(agent); + + /* Check if logged changed during agent unlock period */ + if (logged != agent->logged) { + /* Channel changed. Unref and do another pass. */ + ast_channel_unlock(logged); + ast_channel_unref(logged); + } else { + /* Channel stayed the same. Return it. */ + return logged; + } + } +} + +/*! + * \internal + * \brief Get the Agent:agent_id device state. + * \since 12.0.0 + * + * \param agent_id Username of the agent. + * + * \details + * Search the agents container for the agent and return the + * current state. + * + * \return Device state of the agent. + */ +static enum ast_device_state agent_pvt_devstate_get(const char *agent_id) +{ + RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, agent_id, OBJ_KEY), ao2_cleanup); + + if (agent) { + return agent->devstate; + } + return AST_DEVICE_INVALID; +} + +/*! + * \internal + * \brief Request an agent device state be updated. + * \since 12.0.0 + * + * \param agent_id Which agent needs the device state updated. + * + * \return Nothing + */ +static void agent_devstate_changed(const char *agent_id) +{ + ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "Agent:%s", agent_id); +} + +static void agent_pvt_destructor(void *vdoomed) +{ + struct agent_pvt *doomed = vdoomed; + + /* Make sure device state reflects agent destruction. */ + if (!ast_strlen_zero(doomed->username)) { + ast_debug(1, "Agent %s: Destroyed.\n", doomed->username); + agent_devstate_changed(doomed->username); + } + + ast_party_connected_line_free(&doomed->waiting_colp); + if (doomed->caller_bridge) { + ast_bridge_destroy(doomed->caller_bridge, 0); + doomed->caller_bridge = NULL; + } + if (doomed->logged) { + doomed->logged = ast_channel_unref(doomed->logged); + } + ao2_cleanup(doomed->cfg); + doomed->cfg = NULL; + ast_string_field_free_memory(doomed); +} + +static struct agent_pvt *agent_pvt_new(struct agent_cfg *cfg) +{ + struct agent_pvt *agent; + + agent = ao2_alloc(sizeof(*agent), agent_pvt_destructor); + if (!agent) { + return NULL; + } + if (ast_string_field_init(agent, 32)) { + ao2_ref(agent, -1); + return NULL; + } + ast_string_field_set(agent, username, cfg->username); + ast_party_connected_line_init(&agent->waiting_colp); + ao2_ref(cfg, +1); + agent->cfg = cfg; + agent->devstate = AST_DEVICE_UNAVAILABLE; + return agent; +} + +/*! + * \internal + * \brief Agents ao2 container sort function. + * \since 12.0.0 + * + * \param obj_left pointer to the (user-defined part) of an object. + * \param obj_right pointer to the (user-defined part) of an object. + * \param flags flags from ao2_callback() + * OBJ_POINTER - if set, 'obj_right', is an object. + * OBJ_KEY - if set, 'obj_right', is a search key item that is not an object. + * OBJ_PARTIAL_KEY - if set, 'obj_right', is a partial search key item that is not an object. + * + * \retval <0 if obj_left < obj_right + * \retval =0 if obj_left == obj_right + * \retval >0 if obj_left > obj_right + */ +static int agent_pvt_sort_cmp(const void *obj_left, const void *obj_right, int flags) +{ + const struct agent_pvt *agent_left = obj_left; + const struct agent_pvt *agent_right = obj_right; + const char *right_key = obj_right; + int cmp; + + switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) { + default: + case OBJ_POINTER: + right_key = agent_right->username; + /* Fall through */ + case OBJ_KEY: + cmp = strcmp(agent_left->username, right_key); + break; + case OBJ_PARTIAL_KEY: + cmp = strncmp(agent_left->username, right_key, strlen(right_key)); + break; + } + return cmp; +} + +/*! + * \internal + * \brief ao2_find() callback function. + * \since 12.0.0 + * + * Usage: + * found = ao2_find(agents, agent, OBJ_POINTER); + * found = ao2_find(agents, "agent-id", OBJ_KEY); + * found = ao2_find(agents, agent->logged, 0); + */ +static int agent_pvt_cmp(void *obj, void *arg, int flags) +{ + const struct agent_pvt *agent = obj; + int cmp; + + switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) { + case OBJ_POINTER: + case OBJ_KEY: + case OBJ_PARTIAL_KEY: + cmp = CMP_MATCH; + break; + default: + if (agent->logged == arg) { + cmp = CMP_MATCH; + } else { + cmp = 0; + } + break; + } + return cmp; +} + +static int agent_mark(void *obj, void *arg, int flags) +{ + struct agent_pvt *agent = obj; + + agent_lock(agent); + agent->the_mark = 1; + agent_unlock(agent); + return 0; +} + +static void agents_mark(void) +{ + ao2_callback(agents, 0, agent_mark, NULL); +} + +static int agent_sweep(void *obj, void *arg, int flags) +{ + struct agent_pvt *agent = obj; + int cmp = 0; + + agent_lock(agent); + if (agent->the_mark) { + agent->the_mark = 0; + agent->dead = 1; + /* Unlink dead agents immediately. */ + cmp = CMP_MATCH; + } + agent_unlock(agent); + return cmp; +} + +static void agents_sweep(void) +{ + struct ao2_iterator *iter; + struct agent_pvt *agent; + struct ast_channel *logged; + + iter = ao2_callback(agents, OBJ_MULTIPLE | OBJ_UNLINK, agent_sweep, NULL); + if (!iter) { + return; + } + for (; (agent = ao2_iterator_next(iter)); ao2_ref(agent, -1)) { + agent_lock(agent); + if (agent->logged) { + logged = ast_channel_ref(agent->logged); + } else { + logged = NULL; + } + agent_unlock(agent); + if (!logged) { + continue; + } + ast_log(LOG_NOTICE, + "Forced logoff of agent %s(%s). Agent no longer configured.\n", + agent->username, ast_channel_name(logged)); + ast_softhangup(logged, AST_SOFTHANGUP_EXPLICIT); + ast_channel_unref(logged); + } + ao2_iterator_destroy(iter); +} + +static void agents_post_apply_config(void) +{ + struct ao2_iterator iter; + struct agent_cfg *cfg; + RAII_VAR(struct agents_cfg *, cfgs, ao2_global_obj_ref(cfg_handle), ao2_cleanup); + + ast_assert(cfgs != NULL); + + agents_mark(); + iter = ao2_iterator_init(cfgs->agents, 0); + for (; (cfg = ao2_iterator_next(&iter)); ao2_ref(cfg, -1)) { + RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, cfg->username, OBJ_KEY), ao2_cleanup); + + if (agent) { + agent_lock(agent); + agent->the_mark = 0; + if (!agent->logged) { + struct agent_cfg *cfg_old; + + /* Replace the config of agents not logged in. */ + cfg_old = agent->cfg; + ao2_ref(cfg, +1); + agent->cfg = cfg; + ao2_cleanup(cfg_old); + } + agent_unlock(agent); + continue; + } + agent = agent_pvt_new(cfg); + if (!agent) { + continue; + } + ao2_link(agents, agent); + ast_debug(1, "Agent %s: Created.\n", agent->username); + agent_devstate_changed(agent->username); + } + ao2_iterator_destroy(&iter); + agents_sweep(); +} + +static int agent_logoff_request(const char *agent_id, int soft) +{ + struct ast_channel *logged; + RAII_VAR(struct agent_pvt *, agent, ao2_find(agents, agent_id, OBJ_KEY), ao2_cleanup); + + if (!agent) { + return -1; + } + + agent_lock(agent); + logged = agent_lock_logged(agent); + if (logged) { + if (soft) { + agent->deferred_logoff = 1; + } else { + ast_softhangup(logged, AST_SOFTHANGUP_EXPLICIT); + } + ast_channel_unlock(logged); + ast_channel_unref(logged); + } + agent_unlock(agent); + return 0; +} + +/*! Agent holding bridge instance holder. */ +static AO2_GLOBAL_OBJ_STATIC(agent_holding); + +/*! Agent holding bridge deferred creation lock. */ +AST_MUTEX_DEFINE_STATIC(agent_holding_lock); + +/*! + * \internal + * \brief Callback to clear AGENT_STATUS on the caller channel. + * + * \param bridge_channel Which channel to operate on. + * \param payload Data to pass to the callback. (NULL if none). + * \param payload_size Size of the payload if payload is non-NULL. A number otherwise. + * + * \note The payload MUST NOT have any resources that need to be freed. + * + * \return Nothing + */ +static void clear_agent_status(struct ast_bridge_channel *bridge_channel, const void *payload, size_t payload_size) +{ + pbx_builtin_setvar_helper(bridge_channel->chan, "AGENT_STATUS", NULL); +} + +/*! + * \internal + * \brief Connect the agent with the waiting caller. + * \since 12.0.0 + * + * \param bridge_channel Agent channel connecting to the caller. + * \param agent Which agent is connecting to the caller. + * + * \note The agent is locked on entry and not locked on exit. + * + * \return Nothing + */ +static void agent_connect_caller(struct ast_bridge_channel *bridge_channel, struct agent_pvt *agent) +{ + struct ast_bridge *caller_bridge; + int record_agent_calls; + int res; + + record_agent_calls = agent->cfg->record_agent_calls; + caller_bridge = agent->caller_bridge; + agent->caller_bridge = NULL; + agent->state = AGENT_STATE_ON_CALL; + time(&agent->call_start); + agent_unlock(agent); + + if (!caller_bridge) { + /* Reset agent. */ + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, + AST_CAUSE_NORMAL_CLEARING); + return; + } + res = ast_bridge_move(caller_bridge, bridge_channel->bridge, bridge_channel->chan, + NULL, 0); + if (res) { + /* Reset agent. */ + ast_bridge_destroy(caller_bridge, 0); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, + AST_CAUSE_NORMAL_CLEARING); + return; + } + res = ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_ANSWER, NULL, 0) + || ast_bridge_channel_write_callback(bridge_channel, 0, clear_agent_status, NULL, 0); + if (res) { + /* Reset agent. */ + ast_bridge_destroy(caller_bridge, 0); + return; + } + + if (record_agent_calls) { + struct ast_bridge_features_automixmonitor options = { + .start_stop = AUTO_MONITOR_START, + }; + + /* + * The agent is in the new bridge so we can invoke the + * mixmonitor hook to only start recording. + */ + ast_bridge_features_do(AST_BRIDGE_BUILTIN_AUTOMIXMON, bridge_channel, &options); + } + + ao2_t_ref(caller_bridge, -1, "Agent successfully in caller_bridge"); +} + +static int bridge_agent_hold_ack(struct ast_bridge_channel *bridge_channel, void *hook_pvt) +{ + struct agent_pvt *agent = hook_pvt; + + agent_lock(agent); + switch (agent->state) { + case AGENT_STATE_CALL_WAIT_ACK: + /* Connect to caller now. */ + ast_debug(1, "Agent %s: Acked call.\n", agent->username); + agent_connect_caller(bridge_channel, agent);/* Will unlock agent. */ + return 0; + default: + break; + } + agent_unlock(agent); + return 0; +} + +static int bridge_agent_hold_heartbeat(struct ast_bridge_channel *bridge_channel, void *hook_pvt) +{ + struct agent_pvt *agent = hook_pvt; + int probation_timedout = 0; + int ack_timedout = 0; + int wrapup_timedout = 0; + int deferred_logoff; + unsigned int wrapup_time; + unsigned int auto_logoff; + + agent_lock(agent); + deferred_logoff = agent->deferred_logoff; + if (deferred_logoff) { + agent->state = AGENT_STATE_LOGGING_OUT; + } + + switch (agent->state) { + case AGENT_STATE_PROBATION_WAIT: + probation_timedout = + LOGIN_WAIT_TIMEOUT_TIME <= (time(NULL) - agent->probation_start); + if (probation_timedout) { + /* Now ready for a caller. */ + agent->state = AGENT_STATE_READY_FOR_CALL; + agent->devstate = AST_DEVICE_NOT_INUSE; + } + break; + case AGENT_STATE_CALL_WAIT_ACK: + /* Check ack call time. */ + auto_logoff = agent->cfg->auto_logoff; + if (ast_test_flag(agent, AGENT_FLAG_AUTO_LOGOFF)) { + auto_logoff = agent->override_auto_logoff; + } + if (auto_logoff) { + auto_logoff *= 1000; + ack_timedout = ast_tvdiff_ms(ast_tvnow(), agent->ack_time) > auto_logoff; + if (ack_timedout) { + agent->state = AGENT_STATE_LOGGING_OUT; + } + } + break; + case AGENT_STATE_CALL_WRAPUP: + /* Check wrapup time. */ + wrapup_time = agent->cfg->wrapup_time; + if (ast_test_flag(agent, AGENT_FLAG_WRAPUP_TIME)) { + wrapup_time = agent->override_wrapup_time; + } + wrapup_timedout = ast_tvdiff_ms(ast_tvnow(), agent->last_disconnect) > wrapup_time; + if (wrapup_timedout) { + agent->state = AGENT_STATE_READY_FOR_CALL; + agent->devstate = AST_DEVICE_NOT_INUSE; + } + break; + default: + break; + } + agent_unlock(agent); + + if (deferred_logoff) { + ast_debug(1, "Agent %s: Deferred logoff.\n", agent->username); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, + AST_CAUSE_NORMAL_CLEARING); + } else if (probation_timedout) { + ast_debug(1, "Agent %s: Login complete.\n", agent->username); + agent_devstate_changed(agent->username); + } else if (ack_timedout) { + ast_debug(1, "Agent %s: Ack call timeout.\n", agent->username); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, + AST_CAUSE_NORMAL_CLEARING); + } else if (wrapup_timedout) { + ast_debug(1, "Agent %s: Wrapup timeout. Ready for new call.\n", agent->username); + agent_devstate_changed(agent->username); + } + + return 0; +} + +static void agent_after_bridge_cb(struct ast_channel *chan, void *data); +static void agent_after_bridge_cb_failed(enum ast_bridge_after_cb_reason reason, void *data); + +/*! + * \internal + * \brief ast_bridge agent_hold push method. + * \since 12.0.0 + * + * \param self Bridge to operate upon. + * \param bridge_channel Bridge channel to push. + * \param swap Bridge channel to swap places with if not NULL. + * + * \note On entry, self is already locked. + * + * \retval 0 on success + * \retval -1 on failure + */ +static int bridge_agent_hold_push(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap) +{ + int res = 0; + unsigned int wrapup_time; + char dtmf[AST_FEATURE_MAX_LEN]; + struct ast_channel *chan; + const char *moh_class; + RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup); + + chan = bridge_channel->chan; + + agent = ao2_find(agents, swap ? swap->chan : chan, 0); + if (!agent) { + /* Could not find the agent. */ + return -1; + } + + /* Setup agent entertainment */ + agent_lock(agent); + moh_class = ast_strdupa(agent->cfg->moh); + agent_unlock(agent); + res |= ast_channel_add_bridge_role(chan, "holding_participant"); + res |= ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "musiconhold"); + res |= ast_channel_set_bridge_role_option(chan, "holding_participant", "moh_class", moh_class); + + /* Add DTMF acknowledge hook. */ + dtmf[0] = '\0'; + agent_lock(agent); + if (ast_test_flag(agent, AGENT_FLAG_ACK_CALL) + ? agent->override_ack_call : agent->cfg->ack_call) { + const char *dtmf_accept; + + dtmf_accept = ast_test_flag(agent, AGENT_FLAG_DTMF_ACCEPT) + ? agent->override_dtmf_accept : agent->cfg->dtmf_accept; + ast_copy_string(dtmf, dtmf_accept, sizeof(dtmf)); + } + agent_unlock(agent); + if (!ast_strlen_zero(dtmf)) { + ao2_ref(agent, +1); + if (ast_bridge_dtmf_hook(bridge_channel->features, dtmf, bridge_agent_hold_ack, + agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) { + ao2_ref(agent, -1); + res = -1; + } + } + + /* Add heartbeat interval hook. */ + ao2_ref(agent, +1); + if (ast_bridge_interval_hook(bridge_channel->features, 0, 1000, + bridge_agent_hold_heartbeat, agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) { + ao2_ref(agent, -1); + res = -1; + } + + res |= ast_bridge_base_v_table.push(self, bridge_channel, swap); + if (res) { + ast_channel_remove_bridge_role(chan, "holding_participant"); + return -1; + } + + if (swap) { + res = ast_bridge_set_after_callback(chan, agent_after_bridge_cb, + agent_after_bridge_cb_failed, chan); + if (res) { + ast_channel_remove_bridge_role(chan, "holding_participant"); + return -1; + } + + agent_lock(agent); + ast_channel_unref(agent->logged); + agent->logged = ast_channel_ref(chan); + agent_unlock(agent); + + /* + * Kick the channel out so it can come back in fully controlled. + * Otherwise, the after bridge callback will linger and the + * agent will have some slightly different behavior in corner + * cases. + */ + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, + AST_CAUSE_NORMAL_CLEARING); + return 0; + } + + agent_lock(agent); + switch (agent->state) { + case AGENT_STATE_LOGGED_OUT: + /*! + * \todo XXX the login probation time should be only if it is needed. + * + * Need to determine if there are any local channels that can + * optimize and wait until they actually do before leaving the + * AGENT_STATE_PROBATION_WAIT state. For now, the blind + * timer of LOGIN_WAIT_TIMEOUT_TIME will do. + */ + /* + * Start the login probation timer. + * + * We cannot handle an agent local channel optimization when the + * agent is on a call. The optimization may kick the agent + * channel we know about out of the call without our being able + * to switch to the replacement channel. Get any agent local + * channel optimization out of the way while the agent is in the + * holding bridge. + */ + time(&agent->probation_start); + agent->state = AGENT_STATE_PROBATION_WAIT; + agent_unlock(agent); + break; + case AGENT_STATE_PROBATION_WAIT: + /* Restart the probation timer. */ + time(&agent->probation_start); + agent_unlock(agent); + break; + case AGENT_STATE_READY_FOR_CALL: + /* + * Likely someone manually kicked us out of the holding bridge + * and we came right back in. + */ + agent_unlock(agent); + break; + default: + /* Unexpected agent state. */ + ast_assert(0); + /* Fall through */ + case AGENT_STATE_CALL_PRESENT: + case AGENT_STATE_CALL_WAIT_ACK: + agent->state = AGENT_STATE_READY_FOR_CALL; + agent->devstate = AST_DEVICE_NOT_INUSE; + agent_unlock(agent); + ast_debug(1, "Agent %s: Call abort recovery complete.\n", agent->username); + agent_devstate_changed(agent->username); + break; + case AGENT_STATE_ON_CALL: + case AGENT_STATE_CALL_WRAPUP: + wrapup_time = agent->cfg->wrapup_time; + if (ast_test_flag(agent, AGENT_FLAG_WRAPUP_TIME)) { + wrapup_time = agent->override_wrapup_time; + } + if (wrapup_time) { + agent->state = AGENT_STATE_CALL_WRAPUP; + } else { + agent->state = AGENT_STATE_READY_FOR_CALL; + agent->devstate = AST_DEVICE_NOT_INUSE; + } + agent_unlock(agent); + if (!wrapup_time) { + /* No wrapup time. */ + ast_debug(1, "Agent %s: Ready for new call.\n", agent->username); + agent_devstate_changed(agent->username); + } + break; + } + + return 0; +} + +/*! + * \internal + * \brief ast_bridge agent_hold pull method. + * + * \param self Bridge to operate upon. + * \param bridge_channel Bridge channel to pull. + * + * \details + * Remove any channel hooks controlled by the bridge. Release + * any resources held by bridge_channel->bridge_pvt and release + * bridge_channel->bridge_pvt. + * + * \note On entry, self is already locked. + * + * \return Nothing + */ +static void bridge_agent_hold_pull(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel) +{ + ast_channel_remove_bridge_role(bridge_channel->chan, "holding_participant"); + ast_bridge_base_v_table.pull(self, bridge_channel); +} + +/*! + * \brief The bridge is being dissolved. + * + * \param self Bridge to operate upon. + * + * \details + * The bridge is being dissolved. Remove any external + * references to the bridge so it can be destroyed. + * + * \note On entry, self must NOT be locked. + * + * \return Nothing + */ +static void bridge_agent_hold_dissolving(struct ast_bridge *self) +{ + ao2_global_obj_release(agent_holding); + ast_bridge_base_v_table.dissolving(self); +} + +static struct ast_bridge_methods bridge_agent_hold_v_table; + +static struct ast_bridge *bridge_agent_hold_new(void) +{ + struct ast_bridge *bridge; + + bridge = bridge_alloc(sizeof(struct ast_bridge), &bridge_agent_hold_v_table); + bridge = bridge_base_init(bridge, AST_BRIDGE_CAPABILITY_HOLDING, + AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM + | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | AST_BRIDGE_FLAG_TRANSFER_PROHIBITED, + "AgentPool", NULL, NULL); + bridge = bridge_register(bridge); + return bridge; +} + +static void bridge_init_agent_hold(void) +{ + /* Setup bridge agent_hold subclass v_table. */ + bridge_agent_hold_v_table = ast_bridge_base_v_table; + bridge_agent_hold_v_table.name = "agent_hold"; + bridge_agent_hold_v_table.dissolving = bridge_agent_hold_dissolving; + bridge_agent_hold_v_table.push = bridge_agent_hold_push; + bridge_agent_hold_v_table.pull = bridge_agent_hold_pull; +} + +static int bridge_agent_hold_deferred_create(void) +{ + RAII_VAR(struct ast_bridge *, holding, ao2_global_obj_ref(agent_holding), ao2_cleanup); + + if (!holding) { + ast_mutex_lock(&agent_holding_lock); + holding = ao2_global_obj_ref(agent_holding); + if (!holding) { + holding = bridge_agent_hold_new(); + ao2_global_obj_replace_unref(agent_holding, holding); + } + ast_mutex_unlock(&agent_holding_lock); + if (!holding) { + ast_log(LOG_ERROR, "Could not create agent holding bridge.\n"); + return -1; + } + } + return 0; +} + +static void send_agent_login(struct ast_channel *chan, const char *agent) +{ + RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref); + + ast_assert(agent != NULL); + + blob = ast_json_pack("{s: s}", + "agent", agent); + if (!blob) { + return; + } + + ast_channel_publish_cached_blob(chan, ast_channel_agent_login_type(), blob); +} + +static void send_agent_logoff(struct ast_channel *chan, const char *agent, long logintime) +{ + RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref); + + ast_assert(agent != NULL); + + blob = ast_json_pack("{s: s, s: i}", + "agent", agent, + "logintime", logintime); + if (!blob) { + return; + } + + ast_channel_publish_cached_blob(chan, ast_channel_agent_logoff_type(), blob); +} + +/*! + * \internal + * \brief Logout the agent. + * \since 12.0.0 + * + * \param agent Which agent logging out. + * + * \note On entry agent is already locked. On exit it is no longer locked. + * + * \return Nothing + */ +static void agent_logout(struct agent_pvt *agent) +{ + struct ast_channel *logged; + struct ast_bridge *caller_bridge; + long time_logged_in; + + time_logged_in = time(NULL) - agent->login_start; + logged = agent->logged; + agent->logged = NULL; + caller_bridge = agent->caller_bridge; + agent->caller_bridge = NULL; + agent->state = AGENT_STATE_LOGGED_OUT; + agent->devstate = AST_DEVICE_UNAVAILABLE; + ast_clear_flag(agent, AST_FLAGS_ALL); + agent_unlock(agent); + agent_devstate_changed(agent->username); + + if (caller_bridge) { + ast_bridge_destroy(caller_bridge, 0); + } + + ast_channel_lock(logged); + send_agent_logoff(logged, agent->username, time_logged_in); + ast_channel_unlock(logged); + ast_verb(2, "Agent '%s' logged out. Logged in for %ld seconds.\n", + agent->username, time_logged_in); + ast_channel_unref(logged); +} + +/*! + * \internal + * \brief Agent driver loop. + * \since 12.0.0 + * + * \param agent Which agent. + * \param logged The logged in channel. + * + * \return Nothing + */ +static void agent_run(struct agent_pvt *agent, struct ast_channel *logged) +{ + struct ast_bridge_features features; + + if (ast_bridge_features_init(&features)) { + ast_channel_hangupcause_set(logged, AST_CAUSE_NORMAL_CLEARING); + goto agent_run_cleanup; + } + for (;;) { + struct agents_cfg *cfgs; + struct agent_cfg *cfg_new; + struct agent_cfg *cfg_old; + struct ast_bridge *holding; + struct ast_bridge *caller_bridge; + + ast_channel_hangupcause_set(logged, AST_CAUSE_NORMAL_CLEARING); + + holding = ao2_global_obj_ref(agent_holding); + if (!holding) { + ast_debug(1, "Agent %s: Someone destroyed the agent holding bridge.\n", + agent->username); + break; + } + + /* + * When the agent channel leaves the bridging system we usually + * want to put the agent back into the holding bridge for the + * next caller. + */ + ast_bridge_join(holding, logged, NULL, &features, NULL, + AST_BRIDGE_JOIN_PASS_REFERENCE); + if (logged != agent->logged) { + /* This channel is no longer the logged in agent. */ + break; + } + + if (agent->dead) { + /* The agent is no longer configured. */ + break; + } + + /* Update the agent's config before rejoining the holding bridge. */ + cfgs = ao2_global_obj_ref(cfg_handle); + if (!cfgs) { + /* There is no agent configuration. All agents were destroyed. */ + break; + } + cfg_new = ao2_find(cfgs->agents, agent->username, OBJ_KEY); + ao2_ref(cfgs, -1); + if (!cfg_new) { + /* The agent is no longer configured. */ + break; + } + agent_lock(agent); + cfg_old = agent->cfg; + agent->cfg = cfg_new; + + agent->last_disconnect = ast_tvnow(); + + /* Clear out any caller bridge before rejoining the holding bridge. */ + caller_bridge = agent->caller_bridge; + agent->caller_bridge = NULL; + agent_unlock(agent); + ao2_ref(cfg_old, -1); + if (caller_bridge) { + ast_bridge_destroy(caller_bridge, 0); + } + + if (agent->state == AGENT_STATE_LOGGING_OUT + || agent->deferred_logoff + || ast_check_hangup_locked(logged)) { + /* The agent was requested to logout or hungup. */ + break; + } + + /* + * It is safe to access agent->waiting_colp without a lock. It + * is only setup on agent login and not changed. + */ + ast_channel_update_connected_line(logged, &agent->waiting_colp, NULL); + } + ast_bridge_features_cleanup(&features); + +agent_run_cleanup: + agent_lock(agent); + if (logged != agent->logged) { + /* + * We are no longer the agent channel because of local channel + * optimization. + */ + agent_unlock(agent); + ast_debug(1, "Agent %s: Channel %s is no longer the agent.\n", + agent->username, ast_channel_name(logged)); + return; + } + agent_logout(agent); +} + +static void agent_after_bridge_cb(struct ast_channel *chan, void *data) +{ + struct agent_pvt *agent; + + agent = ao2_find(agents, chan, 0); + if (!agent) { + return; + } + + ast_debug(1, "Agent %s: New agent channel %s.\n", + agent->username, ast_channel_name(chan)); + agent_run(agent, chan); + ao2_ref(agent, -1); +} + +static void agent_after_bridge_cb_failed(enum ast_bridge_after_cb_reason reason, void *data) +{ + struct ast_channel *chan = data; + struct agent_pvt *agent; + + agent = ao2_find(agents, chan, 0); + if (!agent) { + return; + } + ast_log(LOG_WARNING, "Agent %s: Forced logout. Lost control of %s because: %s\n", + agent->username, ast_channel_name(chan), + ast_bridge_after_cb_reason_string(reason)); + agent_lock(agent); + agent_logout(agent); + ao2_ref(agent, -1); +} + +/*! + * \internal + * \brief Get the lock on the agent bridge_channel and return it. + * \since 12.0.0 + * + * \param agent Whose bridge_channel to get. + * + * \retval bridge_channel on success (Reffed and locked). + * \retval NULL on error. + */ +static struct ast_bridge_channel *agent_bridge_channel_get_lock(struct agent_pvt *agent) +{ + struct ast_channel *logged; + struct ast_bridge_channel *bc; + + for (;;) { + agent_lock(agent); + logged = agent->logged; + if (!logged) { + agent_unlock(agent); + return NULL; + } + ast_channel_ref(logged); + agent_unlock(agent); + + ast_channel_lock(logged); + bc = ast_channel_get_bridge_channel(logged); + ast_channel_unlock(logged); + ast_channel_unref(logged); + if (!bc) { + if (agent->logged != logged) { + continue; + } + return NULL; + } + + ast_bridge_channel_lock(bc); + if (bc->chan != logged || agent->logged != logged) { + ast_bridge_channel_unlock(bc); + ao2_ref(bc, -1); + continue; + } + return bc; + } +} + +static void caller_abort_agent(struct agent_pvt *agent) +{ + struct ast_bridge_channel *logged; + + logged = agent_bridge_channel_get_lock(agent); + if (!logged) { + struct ast_bridge *caller_bridge; + + ast_debug(1, "Agent '%s' no longer logged in.\n", agent->username); + + agent_lock(agent); + caller_bridge = agent->caller_bridge; + agent->caller_bridge = NULL; + agent_unlock(agent); + if (caller_bridge) { + ast_bridge_destroy(caller_bridge, 0); + } + return; + } + + /* Kick the agent out of the holding bridge to reset it. */ + ast_bridge_channel_leave_bridge_nolock(logged, BRIDGE_CHANNEL_STATE_END, + AST_CAUSE_NORMAL_CLEARING); + ast_bridge_channel_unlock(logged); +} + +static int caller_safety_timeout(struct ast_bridge_channel *bridge_channel, void *hook_pvt) +{ + struct agent_pvt *agent = hook_pvt; + + if (agent->state == AGENT_STATE_CALL_PRESENT) { + ast_log(LOG_WARNING, "Agent '%s' process did not respond. Safety timeout.\n", + agent->username); + pbx_builtin_setvar_helper(bridge_channel->chan, "AGENT_STATUS", "ERROR"); + + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, 0); + caller_abort_agent(agent); + } + + return -1; +} + +static void agent_alert(struct ast_bridge_channel *bridge_channel, const void *payload, size_t payload_size) +{ + const char *agent_id = payload; + const char *playfile; + const char *dtmf_accept; + struct agent_pvt *agent; + int digit; + char dtmf[2]; + + agent = ao2_find(agents, agent_id, OBJ_KEY); + if (!agent) { + ast_debug(1, "Agent '%s' does not exist. Where did it go?\n", agent_id); + return; + } + + /* Change holding bridge participant role's idle mode to silence */ + ast_bridge_channel_lock_bridge(bridge_channel); + ast_bridge_channel_clear_roles(bridge_channel); + ast_channel_set_bridge_role_option(bridge_channel->chan, "holding_participant", "idle_mode", "silence"); + ast_bridge_channel_establish_roles(bridge_channel); + ast_bridge_unlock(bridge_channel->bridge); + + agent_lock(agent); + playfile = ast_strdupa(agent->cfg->beep_sound); + + /* Determine which DTMF digits interrupt the alerting signal. */ + if (ast_test_flag(agent, AGENT_FLAG_ACK_CALL) + ? agent->override_ack_call : agent->cfg->ack_call) { + dtmf_accept = ast_test_flag(agent, AGENT_FLAG_DTMF_ACCEPT) + ? agent->override_dtmf_accept : agent->cfg->dtmf_accept; + + /* Only the first digit of the ack will stop playback. */ + dtmf[0] = *dtmf_accept; + dtmf[1] = '\0'; + dtmf_accept = dtmf; + } else { + dtmf_accept = NULL; + } + agent_unlock(agent); + + /* Alert the agent. */ + digit = ast_stream_and_wait(bridge_channel->chan, playfile, + ast_strlen_zero(dtmf_accept) ? AST_DIGIT_ANY : dtmf_accept); + ast_stopstream(bridge_channel->chan); + + agent_lock(agent); + switch (agent->state) { + case AGENT_STATE_CALL_PRESENT: + if (!ast_strlen_zero(dtmf_accept)) { + agent->state = AGENT_STATE_CALL_WAIT_ACK; + agent->ack_time = ast_tvnow(); + + if (0 < digit) { + /* Playback was interrupted by a digit. */ + agent_unlock(agent); + ao2_ref(agent, -1); + ast_bridge_channel_feature_digit(bridge_channel, digit); + return; + } + break; + } + + /* Connect to caller now. */ + ast_debug(1, "Agent %s: Immediately connecting to call.\n", agent->username); + agent_connect_caller(bridge_channel, agent);/* Will unlock agent. */ + ao2_ref(agent, -1); + return; + default: + break; + } + agent_unlock(agent); + ao2_ref(agent, -1); +} + +static int send_alert_to_agent(struct ast_bridge_channel *bridge_channel, const char *agent_id) +{ + return ast_bridge_channel_queue_callback(bridge_channel, + AST_BRIDGE_CHANNEL_CB_OPTION_MEDIA, agent_alert, agent_id, strlen(agent_id) + 1); +} + +static int send_colp_to_agent(struct ast_bridge_channel *bridge_channel, struct ast_party_connected_line *connected) +{ + struct ast_set_party_connected_line update = { + .id.name = 1, + .id.number = 1, + .id.subaddress = 1, + }; + unsigned char data[1024]; /* This should be large enough */ + size_t datalen; + + datalen = ast_connected_line_build_data(data, sizeof(data), connected, &update); + if (datalen == (size_t) -1) { + return 0; + } + + return ast_bridge_channel_queue_control_data(bridge_channel, + AST_CONTROL_CONNECTED_LINE, data, datalen); +} + +/*! + * \internal + * \brief Caller joined the bridge event callback. + * + * \param bridge_channel Channel executing the feature + * \param hook_pvt Private data passed in when the hook was created + * + * \retval 0 Keep the callback hook. + * \retval -1 Remove the callback hook. + */ +static int caller_joined_bridge(struct ast_bridge_channel *bridge_channel, void *hook_pvt) +{ + struct agent_pvt *agent = hook_pvt; + struct ast_bridge_channel *logged; + int res; + + logged = agent_bridge_channel_get_lock(agent); + if (!logged) { + ast_verb(3, "Agent '%s' not logged in.\n", agent->username); + pbx_builtin_setvar_helper(bridge_channel->chan, "AGENT_STATUS", "NOT_LOGGED_IN"); + + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, 0); + caller_abort_agent(agent); + return -1; + } + + res = send_alert_to_agent(logged, agent->username); + ast_bridge_channel_unlock(logged); + ao2_ref(logged, -1); + if (res) { + ast_verb(3, "Agent '%s': Failed to alert the agent.\n", agent->username); + pbx_builtin_setvar_helper(bridge_channel->chan, "AGENT_STATUS", "ERROR"); + + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, 0); + caller_abort_agent(agent); + return -1; + } + + pbx_builtin_setvar_helper(bridge_channel->chan, "AGENT_STATUS", "NOT_CONNECTED"); + ast_indicate(bridge_channel->chan, AST_CONTROL_RINGING); + return -1; +} + +/*! + * \brief Dialplan AgentRequest application to locate an agent to talk with. + * + * \param chan Channel wanting to talk with an agent. + * \param data Application parameters + * + * \retval 0 To continue in dialplan. + * \retval -1 To hangup. + */ +static int agent_request_exec(struct ast_channel *chan, const char *data) +{ + struct ast_bridge *caller_bridge; + struct ast_bridge_channel *logged; + char *parse; + int res; + struct ast_bridge_features caller_features; + struct ast_party_connected_line connected; + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(agent_id); + AST_APP_ARG(other); /* Any remaining unused arguments */ + ); + + RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup); + + if (bridge_agent_hold_deferred_create()) { + return -1; + } + + parse = ast_strdupa(data); + AST_STANDARD_APP_ARGS(args, parse); + + if (ast_strlen_zero(args.agent_id)) { + ast_log(LOG_WARNING, "AgentRequest requires an AgentId\n"); + return -1; + } + + /* Find the agent. */ + agent = ao2_find(agents, args.agent_id, OBJ_KEY); + if (!agent) { + ast_verb(3, "Agent '%s' does not exist.\n", args.agent_id); + pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "INVALID"); + return 0; + } + + if (ast_bridge_features_init(&caller_features)) { + return -1; + } + + /* Add safety timeout hook. */ + ao2_ref(agent, +1); + if (ast_bridge_interval_hook(&caller_features, 0, CALLER_SAFETY_TIMEOUT_TIME, + caller_safety_timeout, agent, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) { + ao2_ref(agent, -1); + ast_bridge_features_cleanup(&caller_features); + return -1; + } + + /* Setup the alert agent on caller joining the bridge hook. */ + ao2_ref(agent, +1); + if (ast_bridge_join_hook(&caller_features, caller_joined_bridge, agent, + __ao2_cleanup, 0)) { + ao2_ref(agent, -1); + ast_bridge_features_cleanup(&caller_features); + return -1; + } + + caller_bridge = ast_bridge_basic_new(); + if (!caller_bridge) { + ast_bridge_features_cleanup(&caller_features); + return -1; + } + + agent_lock(agent); + switch (agent->state) { + case AGENT_STATE_LOGGED_OUT: + case AGENT_STATE_LOGGING_OUT: + agent_unlock(agent); + ast_bridge_destroy(caller_bridge, 0); + ast_bridge_features_cleanup(&caller_features); + ast_verb(3, "Agent '%s' not logged in.\n", agent->username); + pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "NOT_LOGGED_IN"); + return 0; + case AGENT_STATE_READY_FOR_CALL: + ao2_ref(caller_bridge, +1); + agent->caller_bridge = caller_bridge; + agent->state = AGENT_STATE_CALL_PRESENT; + agent->devstate = AST_DEVICE_INUSE; + break; + default: + agent_unlock(agent); + ast_bridge_destroy(caller_bridge, 0); + ast_bridge_features_cleanup(&caller_features); + ast_verb(3, "Agent '%s' is busy.\n", agent->username); + pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "BUSY"); + return 0; + } + agent_unlock(agent); + agent_devstate_changed(agent->username); + + /* Get COLP for agent. */ + ast_party_connected_line_init(&connected); + ast_channel_lock(chan); + ast_connected_line_copy_from_caller(&connected, ast_channel_caller(chan)); + ast_channel_unlock(chan); + + logged = agent_bridge_channel_get_lock(agent); + if (!logged) { + ast_party_connected_line_free(&connected); + caller_abort_agent(agent); + ast_bridge_destroy(caller_bridge, 0); + ast_bridge_features_cleanup(&caller_features); + ast_verb(3, "Agent '%s' not logged in.\n", agent->username); + pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "NOT_LOGGED_IN"); + return 0; + } + + send_colp_to_agent(logged, &connected); + ast_bridge_channel_unlock(logged); + ao2_ref(logged, -1); + ast_party_connected_line_free(&connected); + + if (ast_bridge_join(caller_bridge, chan, NULL, &caller_features, NULL, + AST_BRIDGE_JOIN_PASS_REFERENCE)) { + caller_abort_agent(agent); + ast_verb(3, "Agent '%s': Caller %s failed to join the bridge.\n", + agent->username, ast_channel_name(chan)); + pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "ERROR"); + } + ast_bridge_features_cleanup(&caller_features); + + /* Determine if we need to continue in the dialplan after the bridge. */ + ast_channel_lock(chan); + if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO) { + /* + * The bridge was broken for a hangup that isn't real. + * Don't run the h extension, because the channel isn't + * really hung up. This should really only happen with + * AST_SOFTHANGUP_ASYNCGOTO. + */ + res = 0; + } else { + res = ast_check_hangup(chan) + || ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE) + || ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENT_STATUS")); + } + ast_channel_unlock(chan); + + return res ? -1 : 0; +} + +/*! + * \internal + * \brief Get agent config values from the login channel. + * \since 12.0.0 + * + * \param agent What to setup channel config values on. + * \param chan Channel logging in as an agent. + * + * \return Nothing + */ +static void agent_login_channel_config(struct agent_pvt *agent, struct ast_channel *chan) +{ + struct ast_flags opts = { 0 }; + struct ast_party_connected_line connected; + unsigned int override_ack_call = 0; + unsigned int override_auto_logoff = 0; + unsigned int override_wrapup_time = 0; + const char *override_dtmf_accept = NULL; + const char *var; + + ast_party_connected_line_init(&connected); + + /* Get config values from channel. */ + ast_channel_lock(chan); + ast_party_connected_line_copy(&connected, ast_channel_connected(chan)); + + var = pbx_builtin_getvar_helper(chan, "AGENTACKCALL"); + if (!ast_strlen_zero(var)) { + override_ack_call = ast_true(var) ? 1 : 0; + ast_set_flag(&opts, AGENT_FLAG_ACK_CALL); + } + + var = pbx_builtin_getvar_helper(chan, "AGENTACCEPTDTMF"); + if (!ast_strlen_zero(var)) { + override_dtmf_accept = ast_strdupa(var); + ast_set_flag(&opts, AGENT_FLAG_DTMF_ACCEPT); + } + + var = pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"); + if (!ast_strlen_zero(var)) { + if (sscanf(var, "%u", &override_auto_logoff) == 1) { + ast_set_flag(&opts, AGENT_FLAG_AUTO_LOGOFF); + } + } + + var = pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"); + if (!ast_strlen_zero(var)) { + if (sscanf(var, "%u", &override_wrapup_time) == 1) { + ast_set_flag(&opts, AGENT_FLAG_WRAPUP_TIME); + } + } + ast_channel_unlock(chan); + + /* Set config values on agent. */ + agent_lock(agent); + ast_party_connected_line_free(&agent->waiting_colp); + agent->waiting_colp = connected; + + ast_string_field_set(agent, override_dtmf_accept, override_dtmf_accept); + ast_copy_flags(agent, &opts, AST_FLAGS_ALL); + agent->override_auto_logoff = override_auto_logoff; + agent->override_wrapup_time = override_wrapup_time; + agent->override_ack_call = override_ack_call; + agent_unlock(agent); +} + +enum AGENT_LOGIN_OPT_FLAGS { + OPT_SILENT = (1 << 0), +}; +AST_APP_OPTIONS(agent_login_opts, BEGIN_OPTIONS + AST_APP_OPTION('s', OPT_SILENT), +END_OPTIONS); + +/*! + * \brief Dialplan AgentLogin application to log in an agent. + * + * \param chan Channel attempting to login as an agent. + * \param data Application parameters + * + * \retval 0 To continue in dialplan. + * \retval -1 To hangup. + */ +static int agent_login_exec(struct ast_channel *chan, const char *data) +{ + char *parse; + struct ast_flags opts; + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(agent_id); + AST_APP_ARG(options); + AST_APP_ARG(other); /* Any remaining unused arguments */ + ); + + RAII_VAR(struct agent_pvt *, agent, NULL, ao2_cleanup); + + if (bridge_agent_hold_deferred_create()) { + return -1; + } + + if (ast_channel_state(chan) != AST_STATE_UP && ast_answer(chan)) { + return -1; + } + + parse = ast_strdupa(data); + AST_STANDARD_APP_ARGS(args, parse); + + if (ast_strlen_zero(args.agent_id)) { + ast_log(LOG_WARNING, "AgentLogin requires an AgentId\n"); + return -1; + } + + if (ast_app_parse_options(agent_login_opts, &opts, NULL, args.options)) { + /* General invalid option syntax. */ + return -1; + } + + /* Find the agent. */ + agent = ao2_find(agents, args.agent_id, OBJ_KEY); + if (!agent) { + ast_verb(3, "Agent '%s' does not exist.\n", args.agent_id); + pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "INVALID"); + return 0; + } + + /* Has someone already logged in as this agent already? */ + agent_lock(agent); + if (agent->logged) { + agent_unlock(agent); + ast_verb(3, "Agent '%s' already logged in.\n", agent->username); + pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "ALREADY_LOGGED_IN"); + return 0; + } + agent->logged = ast_channel_ref(chan); + agent->last_disconnect = ast_tvnow(); + time(&agent->login_start); + agent->deferred_logoff = 0; + agent_unlock(agent); + + agent_login_channel_config(agent, chan); + + if (!ast_test_flag(&opts, OPT_SILENT)) { + ast_stream_and_wait(chan, "agent-loginok", AST_DIGIT_NONE); + } + + ast_verb(2, "Agent '%s' logged in (format %s/%s)\n", agent->username, + ast_format_get_name(ast_channel_readformat(chan)), + ast_format_get_name(ast_channel_writeformat(chan))); + ast_channel_lock(chan); + send_agent_login(chan, agent->username); + ast_channel_unlock(chan); + + agent_run(agent, chan); + return -1; +} + +static int agent_function_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) +{ + char *parse; + struct agent_pvt *agent; + struct ast_channel *logged; + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(agentid); + AST_APP_ARG(item); + ); + + buf[0] = '\0'; + + parse = ast_strdupa(data ?: ""); + AST_NONSTANDARD_APP_ARGS(args, parse, ':'); + + if (ast_strlen_zero(args.agentid)) { + ast_log(LOG_WARNING, "The AGENT function requires an argument - agentid!\n"); + return -1; + } + if (!args.item) { + args.item = "status"; + } + + agent = ao2_find(agents, args.agentid, OBJ_KEY); + if (!agent) { + ast_log(LOG_WARNING, "Agent '%s' not found!\n", args.agentid); + return -1; + } + + agent_lock(agent); + if (!strcasecmp(args.item, "status")) { + const char *status; + + if (agent->logged) { + status = "LOGGEDIN"; + } else { + status = "LOGGEDOUT"; + } + ast_copy_string(buf, status, len); + } else if (!strcasecmp(args.item, "name")) { + ast_copy_string(buf, agent->cfg->full_name, len); + } else if (!strcasecmp(args.item, "mohclass")) { + ast_copy_string(buf, agent->cfg->moh, len); + } else if (!strcasecmp(args.item, "channel")) { + logged = agent_lock_logged(agent); + if (logged) { + char *pos; + + ast_copy_string(buf, ast_channel_name(logged), len); + ast_channel_unlock(logged); + ast_channel_unref(logged); + + pos = strrchr(buf, '-'); + if (pos) { + *pos = '\0'; + } + } + } else if (!strcasecmp(args.item, "fullchannel")) { + logged = agent_lock_logged(agent); + if (logged) { + ast_copy_string(buf, ast_channel_name(logged), len); + ast_channel_unlock(logged); + ast_channel_unref(logged); + } + } + agent_unlock(agent); + ao2_ref(agent, -1); + + return 0; +} + +static struct ast_custom_function agent_function = { + .name = "AGENT", + .read = agent_function_read, +}; + +struct agent_complete { + /*! Nth match to return. */ + int state; + /*! Which match currently on. */ + int which; +}; + +static int complete_agent_search(void *obj, void *arg, void *data, int flags) +{ + struct agent_complete *search = data; + + if (++search->which > search->state) { + return CMP_MATCH; + } + return 0; +} + +static char *complete_agent(const char *word, int state) +{ + char *ret; + struct agent_pvt *agent; + struct agent_complete search = { + .state = state, + }; + + agent = ao2_callback_data(agents, ast_strlen_zero(word) ? 0 : OBJ_PARTIAL_KEY, + complete_agent_search, (char *) word, &search); + if (!agent) { + return NULL; + } + ret = ast_strdup(agent->username); + ao2_ref(agent, -1); + return ret; +} + +static int complete_agent_logoff_search(void *obj, void *arg, void *data, int flags) +{ + struct agent_pvt *agent = obj; + struct agent_complete *search = data; + + if (!agent->logged) { + return 0; + } + if (++search->which > search->state) { + return CMP_MATCH; + } + return 0; +} + +static char *complete_agent_logoff(const char *word, int state) +{ + char *ret; + struct agent_pvt *agent; + struct agent_complete search = { + .state = state, + }; + + agent = ao2_callback_data(agents, ast_strlen_zero(word) ? 0 : OBJ_PARTIAL_KEY, + complete_agent_logoff_search, (char *) word, &search); + if (!agent) { + return NULL; + } + ret = ast_strdup(agent->username); + ao2_ref(agent, -1); + return ret; +} + +static void agent_show_requested(struct ast_cli_args *a, int online_only) +{ +#define FORMAT_HDR "%-8s %-20s %-11s %-30s %s\n" +#define FORMAT_ROW "%-8s %-20s %-11s %-30s %s\n" + + struct ao2_iterator iter; + struct agent_pvt *agent; + struct ast_str *out = ast_str_alloca(512); + unsigned int agents_total = 0; + unsigned int agents_logged_in = 0; + unsigned int agents_talking = 0; + + ast_cli(a->fd, FORMAT_HDR, "Agent-ID", "Name", "State", "Channel", "Talking with"); + iter = ao2_iterator_init(agents, 0); + for (; (agent = ao2_iterator_next(&iter)); ao2_ref(agent, -1)) { + struct ast_channel *logged; + + ++agents_total; + + agent_lock(agent); + logged = agent_lock_logged(agent); + if (logged) { + const char *talking_with; + + ++agents_logged_in; + + talking_with = pbx_builtin_getvar_helper(logged, "BRIDGEPEER"); + if (!ast_strlen_zero(talking_with)) { + ++agents_talking; + } else { + talking_with = ""; + } + ast_str_set(&out, 0, FORMAT_ROW, agent->username, agent->cfg->full_name, + ast_devstate_str(agent->devstate), ast_channel_name(logged), talking_with); + ast_channel_unlock(logged); + ast_channel_unref(logged); + } else { + ast_str_set(&out, 0, FORMAT_ROW, agent->username, agent->cfg->full_name, + ast_devstate_str(agent->devstate), "", ""); + } + agent_unlock(agent); + + if (!online_only || logged) { + ast_cli(a->fd, "%s", ast_str_buffer(out)); + } + } + ao2_iterator_destroy(&iter); + + ast_cli(a->fd, "\nDefined agents: %u, Logged in: %u, Talking: %u\n", + agents_total, agents_logged_in, agents_talking); + +#undef FORMAT_HDR +#undef FORMAT_ROW +} + +static char *agent_handle_show_online(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + switch (cmd) { + case CLI_INIT: + e->command = "agent show online"; + e->usage = + "Usage: agent show online\n" + " Provides summary information for logged in agents.\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if (a->argc != 3) { + return CLI_SHOWUSAGE; + } + + agent_show_requested(a, 1); + + return CLI_SUCCESS; +} + +static char *agent_handle_show_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + switch (cmd) { + case CLI_INIT: + e->command = "agent show all"; + e->usage = + "Usage: agent show all\n" + " Provides summary information for all agents.\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if (a->argc != 3) { + return CLI_SHOWUSAGE; + } + + agent_show_requested(a, 0); + + return CLI_SUCCESS; +} + +static char *agent_handle_show_specific(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + struct agent_pvt *agent; + struct ast_channel *logged; + struct ast_str *out = ast_str_alloca(4096); + + switch (cmd) { + case CLI_INIT: + e->command = "agent show"; + e->usage = + "Usage: agent show \n" + " Show information about the agent\n"; + return NULL; + case CLI_GENERATE: + if (a->pos == 2) { + return complete_agent(a->word, a->n); + } + return NULL; + } + + if (a->argc != 3) { + return CLI_SHOWUSAGE; + } + + agent = ao2_find(agents, a->argv[2], OBJ_KEY); + if (!agent) { + ast_cli(a->fd, "Agent '%s' not found\n", a->argv[2]); + return CLI_SUCCESS; + } + + agent_lock(agent); + logged = agent_lock_logged(agent); + ast_str_set(&out, 0, "Id: %s\n", agent->username); + ast_str_append(&out, 0, "Name: %s\n", agent->cfg->full_name); + ast_str_append(&out, 0, "Beep: %s\n", agent->cfg->beep_sound); + ast_str_append(&out, 0, "MOH: %s\n", agent->cfg->moh); + ast_str_append(&out, 0, "RecordCalls: %s\n", AST_CLI_YESNO(agent->cfg->record_agent_calls)); + ast_str_append(&out, 0, "State: %s\n", ast_devstate_str(agent->devstate)); + if (logged) { + const char *talking_with; + + ast_str_append(&out, 0, "LoggedInChannel: %s\n", ast_channel_name(logged)); + ast_str_append(&out, 0, "LoggedInTime: %ld\n", (long) agent->login_start); + talking_with = pbx_builtin_getvar_helper(logged, "BRIDGEPEER"); + if (!ast_strlen_zero(talking_with)) { + ast_str_append(&out, 0, "TalkingWith: %s\n", talking_with); + ast_str_append(&out, 0, "CallStarted: %ld\n", (long) agent->call_start); + } + ast_channel_unlock(logged); + ast_channel_unref(logged); + } + agent_unlock(agent); + ao2_ref(agent, -1); + + ast_cli(a->fd, "%s", ast_str_buffer(out)); + + return CLI_SUCCESS; +} + +static char *agent_handle_logoff_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + switch (cmd) { + case CLI_INIT: + e->command = "agent logoff"; + e->usage = + "Usage: agent logoff [soft]\n" + " Sets an agent as no longer logged in.\n" + " If 'soft' is specified, do not hangup existing calls.\n"; + return NULL; + case CLI_GENERATE: + if (a->pos == 2) { + return complete_agent_logoff(a->word, a->n); + } else if (a->pos == 3 && a->n == 0 + && (ast_strlen_zero(a->word) + || !strncasecmp("soft", a->word, strlen(a->word)))) { + return ast_strdup("soft"); + } + return NULL; + } + + if (a->argc < 3 || 4 < a->argc) { + return CLI_SHOWUSAGE; + } + if (a->argc == 4 && strcasecmp(a->argv[3], "soft")) { + return CLI_SHOWUSAGE; + } + + if (!agent_logoff_request(a->argv[2], a->argc == 4)) { + ast_cli(a->fd, "Logging out %s\n", a->argv[2]); + } + + return CLI_SUCCESS; +} + +static struct ast_cli_entry cli_agents[] = { + AST_CLI_DEFINE(agent_handle_show_online, "Show status of online agents"), + AST_CLI_DEFINE(agent_handle_show_all, "Show status of all agents"), + AST_CLI_DEFINE(agent_handle_show_specific, "Show information about an agent"), + AST_CLI_DEFINE(agent_handle_logoff_cmd, "Sets an agent offline"), +}; + +static int action_agents(struct mansession *s, const struct message *m) +{ + const char *id = astman_get_header(m, "ActionID"); + char id_text[AST_MAX_BUF]; + struct ao2_iterator iter; + struct agent_pvt *agent; + struct ast_str *out = ast_str_alloca(4096); + + if (!ast_strlen_zero(id)) { + snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id); + } else { + id_text[0] = '\0'; + } + astman_send_ack(s, m, "Agents will follow"); + + iter = ao2_iterator_init(agents, 0); + for (; (agent = ao2_iterator_next(&iter)); ao2_ref(agent, -1)) { + struct ast_channel *logged; + + agent_lock(agent); + logged = agent_lock_logged(agent); + + /* + * Status Values: + * AGENT_LOGGEDOFF - Agent isn't logged in + * AGENT_IDLE - Agent is logged in, and waiting for call + * AGENT_ONCALL - Agent is logged in, and on a call + * AGENT_UNKNOWN - Don't know anything about agent. Shouldn't ever get this. + */ + ast_str_set(&out, 0, "Agent: %s\r\n", agent->username); + ast_str_append(&out, 0, "Name: %s\r\n", agent->cfg->full_name); + + if (logged) { + const char *talking_to_chan; + struct ast_str *logged_headers; + RAII_VAR(struct ast_channel_snapshot *, logged_snapshot, ast_channel_snapshot_create(logged), ao2_cleanup); + + if (!logged_snapshot + || !(logged_headers = + ast_manager_build_channel_state_string(logged_snapshot))) { + ast_channel_unlock(logged); + ast_channel_unref(logged); + agent_unlock(agent); + continue; + } + + talking_to_chan = pbx_builtin_getvar_helper(logged, "BRIDGEPEER"); + if (!ast_strlen_zero(talking_to_chan)) { + ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_ONCALL"); + ast_str_append(&out, 0, "TalkingToChan: %s\r\n", talking_to_chan); + ast_str_append(&out, 0, "CallStarted: %ld\n", (long) agent->call_start); + } else { + ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_IDLE"); + } + ast_str_append(&out, 0, "LoggedInTime: %ld\r\n", (long) agent->login_start); + ast_str_append(&out, 0, "%s", ast_str_buffer(logged_headers)); + ast_channel_unlock(logged); + ast_channel_unref(logged); + ast_free(logged_headers); + } else { + ast_str_append(&out, 0, "Status: %s\r\n", "AGENT_LOGGEDOFF"); + } + + agent_unlock(agent); + + astman_append(s, "Event: Agents\r\n" + "%s%s\r\n", + ast_str_buffer(out), id_text); + } + ao2_iterator_destroy(&iter); + + astman_append(s, "Event: AgentsComplete\r\n" + "%s" + "\r\n", id_text); + return 0; +} + +static int action_agent_logoff(struct mansession *s, const struct message *m) +{ + const char *agent = astman_get_header(m, "Agent"); + const char *soft_s = astman_get_header(m, "Soft"); /* "true" is don't hangup */ + + if (ast_strlen_zero(agent)) { + astman_send_error(s, m, "No agent specified"); + return 0; + } + + if (!agent_logoff_request(agent, ast_true(soft_s))) { + astman_send_ack(s, m, "Agent logged out"); + } else { + astman_send_error(s, m, "No such agent"); + } + + return 0; +} + +static int unload_module(void) +{ + struct ast_bridge *holding; + + /* Unregister dialplan applications */ + ast_unregister_application(app_agent_login); + ast_unregister_application(app_agent_request); + + /* Unregister dialplan functions */ + ast_custom_function_unregister(&agent_function); + + /* Unregister manager command */ + ast_manager_unregister("Agents"); + ast_manager_unregister("AgentLogoff"); + + /* Unregister CLI commands */ + ast_cli_unregister_multiple(cli_agents, ARRAY_LEN(cli_agents)); + + ast_devstate_prov_del("Agent"); + + /* Destroy agent holding bridge. */ + holding = ao2_global_obj_replace(agent_holding, NULL); + if (holding) { + ast_bridge_destroy(holding, 0); + } + + destroy_config(); + ao2_ref(agents, -1); + agents = NULL; + return 0; +} + +static int load_module(void) +{ + int res = 0; + + agents = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_MUTEX, + AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, agent_pvt_sort_cmp, agent_pvt_cmp); + if (!agents) { + return AST_MODULE_LOAD_FAILURE; + } + if (load_config()) { + ast_log(LOG_ERROR, "Unable to load config. Not loading module.\n"); + ao2_ref(agents, -1); + agents = NULL; + return AST_MODULE_LOAD_DECLINE; + } + + /* Init agent holding bridge v_table. */ + bridge_init_agent_hold(); + + /* Setup to provide Agent:agent-id device state. */ + res |= ast_devstate_prov_add("Agent", agent_pvt_devstate_get); + + /* CLI Commands */ + res |= ast_cli_register_multiple(cli_agents, ARRAY_LEN(cli_agents)); + + /* Manager commands */ + res |= ast_manager_register_xml("Agents", EVENT_FLAG_AGENT, action_agents); + res |= ast_manager_register_xml("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff); + + /* Dialplan Functions */ + res |= ast_custom_function_register(&agent_function); + + /* Dialplan applications */ + res |= ast_register_application_xml(app_agent_login, agent_login_exec); + res |= ast_register_application_xml(app_agent_request, agent_request_exec); + + if (res) { + unload_module(); + return AST_MODULE_LOAD_FAILURE; + } + return AST_MODULE_LOAD_SUCCESS; +} + +static int reload(void) +{ + if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) { + /* Just keep the config we already have in place. */ + return -1; + } + return 0; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Call center agent pool applications", + .support_level = AST_MODULE_SUPPORT_CORE, + .load = load_module, + .unload = unload_module, + .reload = reload, + .load_pri = AST_MODPRI_DEVSTATE_PROVIDER, +); diff -Nru asterisk-11.7.0~dfsg/apps/app_alarmreceiver.c asterisk-13.1.0~dfsg/apps/app_alarmreceiver.c --- asterisk-11.7.0~dfsg/apps/app_alarmreceiver.c 2012-04-19 19:05:17.000000000 +0000 +++ asterisk-13.1.0~dfsg/apps/app_alarmreceiver.c 2014-07-25 16:47:17.000000000 +0000 @@ -17,6 +17,7 @@ */ /*! \file + * * \brief Central Station Alarm receiver for Ademco Contact ID * \author Steve Rodgers * @@ -29,13 +30,22 @@ * \ingroup applications */ +/*! \li \ref app_alarmreceiver.c uses the configuration file \ref alarmreceiver.conf + * \addtogroup configuration_file Configuration Files + */ + +/*! + * \page alarmreceiver.conf alarmreceiver.conf + * \verbinclude alarmreceiver.conf.sample + */ + /*** MODULEINFO extended ***/ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 362635 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $") #include #include @@ -47,7 +57,6 @@ #include "asterisk/pbx.h" #include "asterisk/module.h" #include "asterisk/translate.h" -#include "asterisk/ulaw.h" #include "asterisk/app.h" #include "asterisk/dsp.h" #include "asterisk/config.h" @@ -55,9 +64,77 @@ #include "asterisk/callerid.h" #include "asterisk/astdb.h" #include "asterisk/utils.h" +#include "asterisk/indications.h" +#include "asterisk/format_cache.h" #define ALMRCV_CONFIG "alarmreceiver.conf" +#define UNKNOWN_FORMAT "UNKNOWN_FORMAT" + #define ADEMCO_CONTACT_ID "ADEMCO_CONTACT_ID" +/* + AAAA _ID_ P CCC XX ZZZ S + +where AAAA is the account number, _ID_ is 18 or 98, P is the pin status (alarm or restore), CCC +is the alarm code which is pre-defined by Ademco (but you may be able to reprogram it in the panel), XX +is the dialer group, partition or area number, ZZZ is the zone or user number and S is the checksum +*/ + +#define ADEMCO_EXPRESS_4_1 "ADEMCO_EXPRESS_4_1" +/* + AAAA _ID_ C S + +where AAAA is the account number, _ID_ is 17, C is the alarm code and S is the checksum. +*/ + +#define ADEMCO_EXPRESS_4_2 "ADEMCO_EXPRESS_4_2" +/* + AAAA _ID_ C Z S + +where AAAA is the account number, _ID_ is 27, C is the alarm code, Z is the zone or user number and S is the checksum. +*/ + +#define ADEMCO_HIGH_SPEED "ADEMCO_HIGH_SPEED" +/* + AAAA _ID_ PPPP PPPP X S + +where AAAA is the account number, _ID_ is 55, PPPP PPPP is the status of each zone, X +is a special digit which describes the type of information in the PPPP PPPP fields and S is checksum. +Each P field contains one of the following values: + 1 new alarm 3 new restore 5 normal + 2 new opening 4 new closing 6 outstanding +The X field contains one of the following values: + 0 AlarmNet messages + 1 ambush or duress + 2 opening by user (the first P field contains the user number) + 3 bypass (the P fields indicate which zones are bypassed) + 4 closing by user (the first P field contain the user number) + 5 trouble (the P fields contain which zones are in trouble) + 6 system trouble + 7 normal message (the P fields indicate zone status) + 8 low battery (the P fields indicate zone status) + 9 test (the P fields indicate zone status) +*/ +#define ADEMCO_SUPER_FAST "ADEMCO_SUPER_FAST" +/* + AAAA _ID_ PPPP PPPP X +where AAA is the account number, _ID_ is 56 +*/ + +#define ADEMCO_MSG_TYPE_1 "18" +#define ADEMCO_MSG_TYPE_2 "98" +#define ADEMCO_MSG_TYPE_3 "17" +#define ADEMCO_MSG_TYPE_4 "27" +#define ADEMCO_MSG_TYPE_5 "55" +#define ADEMCO_MSG_TYPE_6 "56" + +#define ADEMCO_AUDIO_CALL_NEXT "606" + +struct { + char digit; + char weight; +} digits_mapping[] = { {'0', 10}, {'1', 1} , {'2', 2}, {'3', 3}, {'4', 4}, {'5', 5}, + {'6', 6}, {'7', 7}, {'8', 8}, {'9', 9}, {'*', 11}, {'#', 12}, + {'A', 13}, {'B', 14}, {'C', 15} }; struct event_node{ char data[17]; @@ -66,6 +143,8 @@ typedef struct event_node event_node_t; +struct timeval call_start_time; + static const char app[] = "AlarmReceiver"; /*** DOCUMENTATION @@ -81,7 +160,19 @@ events to the standard input of the application. The configuration file also contains settings for DTMF timing, and for the loudness of the acknowledgement tones. - Only 1 signalling format is supported at this time: Ademco Contact ID. + Few Ademco DTMF signalling formats are detected automaticaly: Contact ID, Express 4+1, + Express 4+2, High Speed and Super Fast. + The application is affected by the following variables: + + + Maximum call time, in milliseconds. + If set, this variable causes application to exit after the specified time. + + + Maximum number of retries per call. + If set, this variable causes application to exit after the specified number of messages. + + alarmreceiver.conf @@ -92,8 +183,10 @@ /* Config Variables */ static int fdtimeout = 2000; static int sdtimeout = 200; +static int answait = 1250; static int toneloudness = 4096; static int log_individual_events = 0; +static int no_group_meta = 0; static char event_spool_dir[128] = {'\0'}; static char event_app[128] = {'\0'}; static char db_family[128] = {'\0'}; @@ -102,152 +195,77 @@ /* Misc variables */ static char event_file[14] = "/event-XXXXXX"; -/* -* Attempt to access a database variable and increment it, -* provided that the user defined db-family in alarmreceiver.conf -* The alarmreceiver app will write statistics to a few variables -* in this family if it is defined. If the new key doesn't exist in the -* family, then create it and set its value to 1. -*/ -static void database_increment( char *key ) +/*! + * \brief Attempt to access a database variable and increment it + * + * \note Only if the user defined db-family in alarmreceiver.conf + * + * The alarmreceiver app will write statistics to a few variables + * in this family if it is defined. If the new key doesn't exist in the + * family, then create it and set its value to 1. + * + * \param key A database key to increment + * \return Nothing + */ +static void database_increment(char *key) { - int res = 0; unsigned v; char value[16]; - - - if (ast_strlen_zero(db_family)) - return; /* If not defined, don't do anything */ - - res = ast_db_get(db_family, key, value, sizeof(value) - 1); - - if (res) { + + if (ast_strlen_zero(db_family)) { + return; /* If not defined, don't do anything */ + } + + if (ast_db_get(db_family, key, value, sizeof(value) - 1)) { ast_verb(4, "AlarmReceiver: Creating database entry %s and setting to 1\n", key); /* Guess we have to create it */ - res = ast_db_put(db_family, key, "1"); + ast_db_put(db_family, key, "1"); return; } - + sscanf(value, "%30u", &v); v++; ast_verb(4, "AlarmReceiver: New value for %s: %u\n", key, v); - snprintf(value, sizeof(value), "%u", v); - res = ast_db_put(db_family, key, value); - - if (res) + if (ast_db_put(db_family, key, value)) { ast_verb(4, "AlarmReceiver: database_increment write error\n"); - - return; -} - - -/* -* Build a MuLaw data block for a single frequency tone -*/ -static void make_tone_burst(unsigned char *data, float freq, float loudness, int len, int *x) -{ - int i; - float val; - - for (i = 0; i < len; i++) { - val = loudness * sin((freq * 2.0 * M_PI * (*x)++)/8000.0); - data[i] = AST_LIN2MU((int)val); } - /* wrap back around from 8000 */ - - if (*x >= 8000) - *x = 0; return; } -/* -* Send a single tone burst for a specifed duration and frequency. -* Returns 0 if successful -*/ -static int send_tone_burst(struct ast_channel *chan, float freq, int duration, int tldn) -{ - int res = 0; - int i = 0; - int x = 0; - struct ast_frame *f, wf; - - struct { - unsigned char offset[AST_FRIENDLY_OFFSET]; - unsigned char buf[640]; - } tone_block; - - for (;;) { - - if (ast_waitfor(chan, -1) < 0) { - res = -1; - break; - } - - f = ast_read(chan); - if (!f) { - res = -1; - break; - } - - if (f->frametype == AST_FRAME_VOICE) { - wf.frametype = AST_FRAME_VOICE; - ast_format_set(&wf.subclass.format, AST_FORMAT_ULAW, 0); - wf.offset = AST_FRIENDLY_OFFSET; - wf.mallocd = 0; - wf.data.ptr = tone_block.buf; - wf.datalen = f->datalen; - wf.samples = wf.datalen; - - make_tone_burst(tone_block.buf, freq, (float) tldn, wf.datalen, &x); - - i += wf.datalen / 8; - if (i > duration) { - ast_frfree(f); - break; - } - if (ast_write(chan, &wf)) { - ast_verb(4, "AlarmReceiver: Failed to write frame on %s\n", ast_channel_name(chan)); - ast_log(LOG_WARNING, "AlarmReceiver Failed to write frame on %s\n",ast_channel_name(chan)); - res = -1; - ast_frfree(f); - break; - } - } - - ast_frfree(f); - } - return res; -} - -/* -* Receive a string of DTMF digits where the length of the digit string is known in advance. Do not give preferential -* treatment to any digit value, and allow separate time out values to be specified for the first digit and all subsequent -* digits. -* -* Returns 0 if all digits successfully received. -* Returns 1 if a digit time out occurred -* Returns -1 if the caller hung up or there was a channel error. -* -*/ -static int receive_dtmf_digits(struct ast_channel *chan, char *digit_string, int length, int fdto, int sdto) +/*! + * \brief Receive a fixed length DTMF string. + * + * \note Doesn't give preferential treatment to any digit, + * \note allow different timeout values for the first and all subsequent digits + * + * \param chan Asterisk Channel + * \param digit_string Digits String + * \param buf_size The size of the Digits String buffer + * \param expected Digits expected for this message type + * \param received Pointer to number of digits received so far + * + * \retval 0 if all digits were successfully received + * \retval 1 if a timeout occurred + * \retval -1 if the caller hung up or on channel errors + */ +static int receive_dtmf_digits(struct ast_channel *chan, char *digit_string, int buf_size, int expected, int *received) { - int res = 0; - int i = 0; + int rtn = 0; int r; struct ast_frame *f; struct timeval lastdigittime; lastdigittime = ast_tvnow(); - for (;;) { - /* if outa time, leave */ - if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) > ((i > 0) ? sdto : fdto)) { + while (*received < expected && *received < buf_size - 1) { + /* If timed out, leave */ + if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) > ((*received > 0) ? sdtimeout : fdtimeout)) { ast_verb(4, "AlarmReceiver: DTMF Digit Timeout on %s\n", ast_channel_name(chan)); - ast_debug(1,"AlarmReceiver: DTMF timeout on chan %s\n",ast_channel_name(chan)); - res = 1; + ast_debug(1, "AlarmReceiver: DTMF timeout on chan %s\n", ast_channel_name(chan)); + rtn = 1; break; } @@ -256,68 +274,73 @@ continue; } - f = ast_read(chan); - - if (f == NULL) { - res = -1; + if ((f = ast_read(chan)) == NULL) { + rtn = -1; break; } /* If they hung up, leave */ - if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP)) { + if ((f->frametype == AST_FRAME_CONTROL) + && (f->subclass.integer == AST_CONTROL_HANGUP)) { if (f->data.uint32) { ast_channel_hangupcause_set(chan, f->data.uint32); } ast_frfree(f); - res = -1; + rtn = -1; break; } - /* if not DTMF, just do it again */ + /* If not DTMF, just do it again */ if (f->frametype != AST_FRAME_DTMF) { ast_frfree(f); continue; } - digit_string[i++] = f->subclass.integer; /* save digit */ - + /* Save digit */ + digit_string[(*received)++] = f->subclass.integer; ast_frfree(f); - /* If we have all the digits we expect, leave */ - if(i >= length) - break; - lastdigittime = ast_tvnow(); } - digit_string[i] = '\0'; /* Nul terminate the end of the digit string */ - return res; + /* Null terminate the end of the digit_string */ + digit_string[*received] = '\0'; + + return rtn; } -/* -* Write the metadata to the log file -*/ -static int write_metadata( FILE *logfile, char *signalling_type, struct ast_channel *chan) +/*! + * \brief Write metadata to log file + * + * \param logfile Log File Pointer + * \param signalling_type Signaling Type + * \param chan Asterisk Channel + * \param no_checksum Expecting messages without checksum + * + * \retval 0 success + * \retval -1 failure + */ +static int write_metadata(FILE *logfile, char *signalling_type, struct ast_channel *chan, int no_checksum) { - int res = 0; struct timeval t; struct ast_tm now; char *cl; char *cn; char workstring[80]; char timestamp[80]; - + /* Extract the caller ID location */ ast_copy_string(workstring, - S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, ""), - sizeof(workstring)); + S_COR(ast_channel_caller(chan)->id.number.valid, + ast_channel_caller(chan)->id.number.str, ""), sizeof(workstring)); ast_shrink_phone_number(workstring); if (ast_strlen_zero(workstring)) { cl = ""; } else { cl = workstring; } - cn = S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, ""); + cn = S_COR(ast_channel_caller(chan)->id.name.valid, + ast_channel_caller(chan)->id.name.str, ""); /* Get the current time */ t = ast_tvnow(); @@ -326,155 +349,369 @@ /* Format the time */ ast_strftime(timestamp, sizeof(timestamp), time_stamp_format, &now); - res = fprintf(logfile, "\n\n[metadata]\n\n"); - if (res >= 0) { - res = fprintf(logfile, "PROTOCOL=%s\n", signalling_type); - } - if (res >= 0) { - res = fprintf(logfile, "CALLINGFROM=%s\n", cl); - } - if (res >= 0) { - res = fprintf(logfile, "CALLERNAME=%s\n", cn); - } - if (res >= 0) { - res = fprintf(logfile, "TIMESTAMP=%s\n\n", timestamp); - } - if (res >= 0) { - res = fprintf(logfile, "[events]\n\n"); - } - if (res < 0) { - ast_verb(3, "AlarmReceiver: can't write metadata\n"); - ast_debug(1,"AlarmReceiver: can't write metadata\n"); - } else { - res = 0; + if (no_group_meta && fprintf(logfile, "PROTOCOL=%s\n" + "CHECKSUM=%s\n" + "CALLINGFROM=%s\n" + "CALLERNAME=%s\n" + "TIMESTAMP=%s\n\n", + signalling_type, (!no_checksum) ? "yes" : "no", cl, cn, timestamp) > -1) { + return 0; + } else if (fprintf(logfile, "\n\n[metadata]\n\n" + "PROTOCOL=%s\n" + "CHECKSUM=%s\n" + "CALLINGFROM=%s\n" + "CALLERNAME=%s\n" + "TIMESTAMP=%s\n\n" + "[events]\n\n", + signalling_type, (!no_checksum) ? "yes" : "no", cl, cn, timestamp) > -1) { + return 0; } - return res; + ast_verb(3, "AlarmReceiver: can't write metadata\n"); + ast_debug(1, "AlarmReceiver: can't write metadata\n"); + return -1; } -/* -* Write a single event to the log file -*/ -static int write_event( FILE *logfile, event_node_t *event) +/*! + * \brief Log a single event + * + * \param logfile Log File Pointer + * \param event Event Structure + * + * \retval 0 success + * \retval -1 failure + */ +static int write_event(FILE *logfile, event_node_t *event) { - int res = 0; - - if (fprintf(logfile, "%s\n", event->data) < 0) - res = -1; + if (fprintf(logfile, "%s%s\n", no_group_meta ? "event=" : "", event->data) < 0) { + return -1; + } - return res; + return 0; } -/* -* If we are configured to log events, do so here. -* -*/ -static int log_events(struct ast_channel *chan, char *signalling_type, event_node_t *event) +/*! + * \brief Log events if configuration key logindividualevents is enabled or on exit + * + * \param chan Asterisk Channel + * \param signalling_type Signaling Type + * \param event Event Structure + * \param no_checksum Expecting messages without checksum + * + * \retval 0 success + * \retval -1 failure + */ +static int log_events(struct ast_channel *chan, char *signalling_type, event_node_t *event, int no_checksum) { - - int res = 0; - char workstring[sizeof(event_spool_dir)+sizeof(event_file)] = ""; + char workstring[sizeof(event_spool_dir) + sizeof(event_file)] = ""; int fd; FILE *logfile; event_node_t *elp = event; - + if (!ast_strlen_zero(event_spool_dir)) { - + /* Make a template */ ast_copy_string(workstring, event_spool_dir, sizeof(workstring)); strncat(workstring, event_file, sizeof(workstring) - strlen(workstring) - 1); - + /* Make the temporary file */ fd = mkstemp(workstring); - + if (fd == -1) { ast_verb(3, "AlarmReceiver: can't make temporary file\n"); - ast_debug(1,"AlarmReceiver: can't make temporary file\n"); - res = -1; + ast_debug(1, "AlarmReceiver: can't make temporary file\n"); + return -1; } - if (!res) { - logfile = fdopen(fd, "w"); - if (logfile) { - /* Write the file */ - res = write_metadata(logfile, signalling_type, chan); - if (!res) - while ((!res) && (elp != NULL)) { - res = write_event(logfile, elp); - elp = elp->next; - } - if (!res) { - if (fflush(logfile) == EOF) - res = -1; - if (!res) { - if (fclose(logfile) == EOF) - res = -1; - } - } - } else - res = -1; + if ((logfile = fdopen(fd, "w")) == NULL) { + return -1; } + + /* Write the file */ + if (write_metadata(logfile, signalling_type, chan, no_checksum)) { + fflush(logfile); + fclose(logfile); + return -1; + } + + while ((elp != NULL) && (write_event(logfile, elp) == 0)) { + elp = elp->next; + } + + fflush(logfile); + fclose(logfile); } - return res; + return 0; } -/* -* This function implements the logic to receive the Ademco contact ID format. -* -* The function will return 0 when the caller hangs up, else a -1 if there was a problem. -*/ -static int receive_ademco_contact_id(struct ast_channel *chan, const void *data, int fdto, int sdto, int tldn, event_node_t **ehead) +/*! + * \brief Verify Ademco checksum + * \since 11.0 + * + * \param event Received DTMF String + * \param expected Number of Digits expected + * + * \retval 0 success + * \retval -1 failure + */ +static int ademco_verify_checksum(char *event, int expected) { + int checksum = 0; int i, j; + + for (j = 0; j < expected; j++) { + for (i = 0; i < ARRAY_LEN(digits_mapping); i++) { + if (digits_mapping[i].digit == event[j]) { + break; + } + } + + if (i >= ARRAY_LEN(digits_mapping)) { + ast_verb(2, "AlarmReceiver: Bad DTMF character %c, trying again\n", event[j]); + return -1; + } + + checksum += digits_mapping[i].weight; + } + + /* Checksum is mod(15) of the total */ + if (!(checksum % 15)) { + return 0; + } + + return -1; +} + +/*! + * \brief Send a single tone burst for a specifed duration and frequency. + * \since 11.0 + * + * \param chan Asterisk Channel + * \param tone_freq Frequency of the tone to send + * \param tone_duration Tone duration in ms + * \param delay Delay before sending the tone + * + * \retval 0 success + * \retval -1 failure + */ +static int send_tone_burst(struct ast_channel *chan, const char *tone_freq, int tone_duration, int delay) +{ + if (delay && ast_safe_sleep(chan, delay)) { + return -1; + } + + if (ast_playtones_start(chan, toneloudness, tone_freq, 0)) { + return -1; + } + + if (ast_safe_sleep(chan, tone_duration)) { + return -1; + } + + ast_playtones_stop(chan); + return 0; +} + +/*! + * \brief Check if the message is in known and valid Ademco format + * + * \param signalling_type Expected signalling type for the message + * \param event event received + * + * \retval 0 The event is valid + * \retval -1 The event is not valid + */ +static int ademco_check_valid(char *signalling_type, char *event) +{ + if (!strcmp(signalling_type, UNKNOWN_FORMAT)) { + return 1; + } + + if (!strcmp(signalling_type, ADEMCO_CONTACT_ID) + && strncmp(event + 4, ADEMCO_MSG_TYPE_1, 2) + && strncmp(event + 4, ADEMCO_MSG_TYPE_2, 2)) { + return -1; + } + + if (!strcmp(signalling_type, ADEMCO_EXPRESS_4_1) && strncmp(event + 4, ADEMCO_MSG_TYPE_3, 2)) { + return -1; + } + + if (!strcmp(signalling_type, ADEMCO_EXPRESS_4_2) && strncmp(event + 4, ADEMCO_MSG_TYPE_4, 2)) { + return -1; + } + + if (!strcmp(signalling_type, ADEMCO_HIGH_SPEED) && strncmp(event + 4, ADEMCO_MSG_TYPE_5, 2)) { + return -1; + } + + if (!strcmp(signalling_type, ADEMCO_SUPER_FAST) && strncmp(event + 4, ADEMCO_MSG_TYPE_6, 2)) { + return -1; + } + + return 0; +} + +/*! + * \brief Detect the message format of an event + * + * \param signalling_type Expected signalling type for the message + * \param event event received + * \param no_checksum Should we calculate checksum for the message + * + * \returns The expected digits for the detected event type + */ +static int ademco_detect_format(char *signalling_type, char *event, int *no_checksum) +{ + int res = 16; + + if (!strncmp(event + 4, ADEMCO_MSG_TYPE_1, 2) + || !strncmp(event + 4, ADEMCO_MSG_TYPE_2, 2)) { + sprintf(signalling_type, "%s", ADEMCO_CONTACT_ID); + } + + if (!strncmp(event + 4, ADEMCO_MSG_TYPE_3, 2)) { + sprintf(signalling_type, "%s", ADEMCO_EXPRESS_4_1); + res = 8; + } + + if (!strncmp(event + 4, ADEMCO_MSG_TYPE_4, 2)) { + sprintf(signalling_type, "%s", ADEMCO_EXPRESS_4_2); + res = 9; + } + + if (!strncmp(event + 4, ADEMCO_MSG_TYPE_5, 2)) { + sprintf(signalling_type, "%s", ADEMCO_HIGH_SPEED); + } + + if (!strncmp(event + 4, ADEMCO_MSG_TYPE_6, 2)) { + sprintf(signalling_type, "%s", ADEMCO_SUPER_FAST); + *no_checksum = 1; + res = 15; + } + + if (strcmp(signalling_type, UNKNOWN_FORMAT)) { + ast_verb(4, "AlarmMonitoring: Detected format %s.\n", signalling_type); + ast_debug(1, "AlarmMonitoring: Autodetected format %s.\n", signalling_type); + } + + return res; +} + +/*! + * \brief Receive Ademco ContactID or other format Data String + * + * \param chan Asterisk Channel + * \param ehead Pointer to events list + * \param signalling_type Expected signalling type for the message + * \param no_checksum Should we calculate checksum for the message + * + * \retval 0 success + * \retval -1 failure + */ +static int receive_ademco_event(struct ast_channel *chan, event_node_t **ehead, char *signalling_type, int *no_checksum) +{ int res = 0; - int checksum; + const char *limit; char event[17]; event_node_t *enew, *elp; int got_some_digits = 0; int events_received = 0; int ack_retries = 0; - - static char digit_map[15] = "0123456789*#ABC"; - static unsigned char digit_weights[15] = {10,1,2,3,4,5,6,7,8,9,11,12,13,14,15}; + int limit_retries = 0; + int expected_length = sizeof(event) - 1; database_increment("calls-received"); /* Wait for first event */ - ast_verb(4, "AlarmReceiver: Waiting for first event from panel\n"); + ast_verb(4, "AlarmReceiver: Waiting for first event from panel...\n"); while (res >= 0) { + int digits_received = 0; + + res = 0; + + if (log_individual_events) { + sprintf(signalling_type, "%s", UNKNOWN_FORMAT); + expected_length = 16; + *no_checksum = 0; + } + if (got_some_digits == 0) { /* Send ACK tone sequence */ ast_verb(4, "AlarmReceiver: Sending 1400Hz 100ms burst (ACK)\n"); - res = send_tone_burst(chan, 1400.0, 100, tldn); - if (!res) - res = ast_safe_sleep(chan, 100); + res = send_tone_burst(chan, "1400", 100, 0); if (!res) { ast_verb(4, "AlarmReceiver: Sending 2300Hz 100ms burst (ACK)\n"); - res = send_tone_burst(chan, 2300.0, 100, tldn); + res = send_tone_burst(chan, "2300", 100, 100); } } - if ( res >= 0) - res = receive_dtmf_digits(chan, event, sizeof(event) - 1, fdto, sdto); + if (res) { + return -1; + } + + res = receive_dtmf_digits(chan, event, sizeof(event), expected_length, &digits_received); if (res < 0) { if (events_received == 0) { /* Hangup with no events received should be logged in the DB */ database_increment("no-events-received"); + ast_verb(4, "AlarmReceiver: No events received!\n"); } else { if (ack_retries) { - ast_verb(4, "AlarmReceiver: ACK retries during this call: %d\n", ack_retries); database_increment("ack-retries"); + ast_verb(4, "AlarmReceiver: ACK retries during this call: %d\n", ack_retries); } } ast_verb(4, "AlarmReceiver: App exiting...\n"); - res = -1; break; } - if (res != 0) { + if (!strcmp(signalling_type, UNKNOWN_FORMAT) && digits_received > 5) { + expected_length = ademco_detect_format(signalling_type, event, no_checksum); + + if (res > 0) { + if (digits_received == expected_length) { + res = limit_retries = 0; + } else if (digits_received == expected_length - 1 + && (!strcmp(signalling_type, ADEMCO_EXPRESS_4_2) + || !strcmp(signalling_type, ADEMCO_EXPRESS_4_1))) { + /* ADEMCO EXPRESS without checksum */ + res = limit_retries = 0; + expected_length--; + *no_checksum = 1; + ast_verb(4, "AlarmMonitoring: Skipping checksum for format %s.\n", signalling_type); + ast_debug(1, "AlarmMonitoring: Skipping checksum for format %s.\n", signalling_type); + } + } + } + + ast_channel_lock(chan); + limit = pbx_builtin_getvar_helper(chan, "ALARMRECEIVER_CALL_LIMIT"); + if (!ast_strlen_zero(limit)) { + if (ast_tvdiff_ms(ast_tvnow(), call_start_time) > atoi(limit)) { + ast_channel_unlock(chan); + return -1; + } + } + limit = pbx_builtin_getvar_helper(chan, "ALARMRECEIVER_RETRIES_LIMIT"); + ast_channel_unlock(chan); + if (!ast_strlen_zero(limit)) { + if (limit_retries + 1 >= atoi(limit)) { + return -1; + } + } + + if (res) { /* Didn't get all of the digits */ ast_verb(2, "AlarmReceiver: Incomplete string: %s, trying again...\n", event); + limit_retries++; + + if (!events_received && strcmp(signalling_type, UNKNOWN_FORMAT)) + { + sprintf(signalling_type, "%s", UNKNOWN_FORMAT); + expected_length = sizeof(event) - 1; + } if (!got_some_digits) { got_some_digits = (!ast_strlen_zero(event)) ? 1 : 0; @@ -489,28 +726,7 @@ ast_debug(1, "AlarmReceiver: Received event: %s\n", event); /* Calculate checksum */ - - for (j = 0, checksum = 0; j < 16; j++) { - for (i = 0; i < sizeof(digit_map); i++) { - if (digit_map[i] == event[j]) - break; - } - - if (i == 16) - break; - - checksum += digit_weights[i]; - } - if (i == 16) { - ast_verb(2, "AlarmReceiver: Bad DTMF character %c, trying again\n", event[j]); - continue; /* Bad character */ - } - - /* Checksum is mod(15) of the total */ - - checksum = checksum % 15; - - if (checksum) { + if (!(*no_checksum) && ademco_verify_checksum(event, expected_length)) { database_increment("checksum-errors"); ast_verb(2, "AlarmReceiver: Nonzero checksum\n"); ast_debug(1, "AlarmReceiver: Nonzero checksum\n"); @@ -518,120 +734,122 @@ } /* Check the message type for correctness */ - - if (strncmp(event + 4, "18", 2)) { - if (strncmp(event + 4, "98", 2)) { - database_increment("format-errors"); - ast_verb(2, "AlarmReceiver: Wrong message type\n"); - ast_debug(1, "AlarmReceiver: Wrong message type\n"); + if (ademco_check_valid(signalling_type, event)) { + database_increment("format-errors"); + ast_verb(2, "AlarmReceiver: Wrong message type\n"); + ast_debug(1, "AlarmReceiver: Wrong message type\n"); continue; - } } events_received++; /* Queue the Event */ if (!(enew = ast_calloc(1, sizeof(*enew)))) { - res = -1; - break; + return -1; } enew->next = NULL; ast_copy_string(enew->data, event, sizeof(enew->data)); - /* - * Insert event onto end of list - */ - if (*ehead == NULL) + /* Insert event onto end of list */ + if (*ehead == NULL) { *ehead = enew; - else { - for(elp = *ehead; elp->next != NULL; elp = elp->next) - ; + } else { + for (elp = *ehead; elp->next != NULL; elp = elp->next) { + ; + } elp->next = enew; } - if (res > 0) - res = 0; - /* Let the user have the option of logging the single event before sending the kissoff tone */ - if ((res == 0) && (log_individual_events)) - res = log_events(chan, ADEMCO_CONTACT_ID, enew); - /* Wait 200 msec before sending kissoff */ - if (res == 0) - res = ast_safe_sleep(chan, 200); - - /* Send the kissoff tone */ - if (res == 0) - res = send_tone_burst(chan, 1400.0, 900, tldn); + if (log_individual_events && log_events(chan, signalling_type, enew, *no_checksum)) { + return -1; + } + + /* Send the kissoff tone (1400 Hz, 900 ms, after 200ms delay) */ + if (send_tone_burst(chan, "1400", 900, 200)) { + return -1; + } + + /* If audio call follows, exit alarm receiver app */ + if (!strcmp(signalling_type, ADEMCO_CONTACT_ID) + && !strncmp(event + 7, ADEMCO_AUDIO_CALL_NEXT, 3)) { + ast_verb(4, "AlarmReceiver: App exiting... Audio call next!\n"); + return 0; + } } return res; } -/* -* This is the main function called by Asterisk Core whenever the App is invoked in the extension logic. -* This function will always return 0. -*/ +/*! + * \brief This is the main function called by Asterisk Core whenever the App is invoked in the extension logic. + * + * \param chan Asterisk Channel + * \param data Application data + * + * \retval 0 success + * \retval -1 failure + */ static int alarmreceiver_exec(struct ast_channel *chan, const char *data) { int res = 0; + int no_checksum = 0; event_node_t *elp, *efree; char signalling_type[64] = ""; event_node_t *event_head = NULL; - /* Set write and read formats to ULAW */ - ast_verb(4, "AlarmReceiver: Setting read and write formats to ULAW\n"); - - if (ast_set_write_format_by_id(chan,AST_FORMAT_ULAW)) { - ast_log(LOG_WARNING, "AlarmReceiver: Unable to set write format to Mu-law on %s\n",ast_channel_name(chan)); - return -1; + if ((ast_format_cmp(ast_channel_writeformat(chan), ast_format_ulaw) == AST_FORMAT_CMP_NOT_EQUAL) && + (ast_format_cmp(ast_channel_writeformat(chan), ast_format_alaw) == AST_FORMAT_CMP_NOT_EQUAL)) { + ast_verb(4, "AlarmReceiver: Setting write format to Mu-law\n"); + if (ast_set_write_format(chan, ast_format_ulaw)) { + ast_log(LOG_WARNING, "AlarmReceiver: Unable to set write format to Mu-law on %s\n",ast_channel_name(chan)); + return -1; + } } - if (ast_set_read_format_by_id(chan,AST_FORMAT_ULAW)) { - ast_log(LOG_WARNING, "AlarmReceiver: Unable to set read format to Mu-law on %s\n",ast_channel_name(chan)); - return -1; + if ((ast_format_cmp(ast_channel_readformat(chan), ast_format_ulaw) == AST_FORMAT_CMP_NOT_EQUAL) && + (ast_format_cmp(ast_channel_readformat(chan), ast_format_alaw) == AST_FORMAT_CMP_NOT_EQUAL)) { + ast_verb(4, "AlarmReceiver: Setting read format to Mu-law\n"); + if (ast_set_read_format(chan, ast_format_ulaw)) { + ast_log(LOG_WARNING, "AlarmReceiver: Unable to set read format to Mu-law on %s\n",ast_channel_name(chan)); + return -1; + } } /* Set default values for this invocation of the application */ - ast_copy_string(signalling_type, ADEMCO_CONTACT_ID, sizeof(signalling_type)); + ast_copy_string(signalling_type, UNKNOWN_FORMAT, sizeof(signalling_type)); + call_start_time = ast_tvnow(); /* Answer the channel if it is not already */ - ast_verb(4, "AlarmReceiver: Answering channel\n"); if (ast_channel_state(chan) != AST_STATE_UP) { - if ((res = ast_answer(chan))) + ast_verb(4, "AlarmReceiver: Answering channel\n"); + if (ast_answer(chan)) { return -1; + } } /* Wait for the connection to settle post-answer */ ast_verb(4, "AlarmReceiver: Waiting for connection to stabilize\n"); - res = ast_safe_sleep(chan, 1250); + if (ast_safe_sleep(chan, answait)) { + return -1; + } /* Attempt to receive the events */ - if (!res) { - /* Determine the protocol to receive in advance */ - /* Note: Ademco contact is the only one supported at this time */ - /* Others may be added later */ - if(!strcmp(signalling_type, ADEMCO_CONTACT_ID)) - receive_ademco_contact_id(chan, data, fdtimeout, sdtimeout, toneloudness, &event_head); - else - res = -1; - } + receive_ademco_event(chan, &event_head, signalling_type, &no_checksum); /* Events queued by receiver, write them all out here if so configured */ - if ((!res) && (log_individual_events == 0)) - res = log_events(chan, signalling_type, event_head); + if (!log_individual_events) { + res = log_events(chan, signalling_type, event_head, no_checksum); + } - /* - * Do we exec a command line at the end? - */ + /* Do we exec a command line at the end? */ if ((!res) && (!ast_strlen_zero(event_app)) && (event_head)) { ast_debug(1,"Alarmreceiver: executing: %s\n", event_app); ast_safe_system(event_app); } - /* - * Free up the data allocated in our linked list - */ + /* Free up the data allocated in our linked list */ for (elp = event_head; (elp != NULL);) { efree = elp; elp = elp->next; @@ -641,14 +859,19 @@ return 0; } -/* -* Load the configuration from the configuration file -*/ -static int load_config(void) +/*! + * \brief Load the configuration from the configuration file + * + * \param reload True on reload + * + * \retval 1 success + * \retval 0 failure + */ +static int load_config(int reload) { struct ast_config *cfg; - const char *p; - struct ast_flags config_flags = { 0 }; + const char *value; + struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; /* Read in the config file */ cfg = ast_config_load(ALMRCV_CONFIG, config_flags); @@ -656,79 +879,124 @@ if (!cfg) { ast_verb(4, "AlarmReceiver: No config file\n"); return 0; + } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) { + return 1; } else if (cfg == CONFIG_STATUS_FILEINVALID) { - ast_log(LOG_ERROR, "Config file %s is in an invalid format. Aborting.\n", ALMRCV_CONFIG); + ast_log(LOG_ERROR, "Config file %s is in an invalid format. Aborting.\n", + ALMRCV_CONFIG); return 0; - } else { - p = ast_variable_retrieve(cfg, "general", "eventcmd"); - if (p) { - ast_copy_string(event_app, p, sizeof(event_app)); - } - p = ast_variable_retrieve(cfg, "general", "loudness"); - if (p) { - toneloudness = atoi(p); - if(toneloudness < 100) - toneloudness = 100; - if(toneloudness > 8192) - toneloudness = 8192; - } - p = ast_variable_retrieve(cfg, "general", "fdtimeout"); - if (p) { - fdtimeout = atoi(p); - if(fdtimeout < 1000) - fdtimeout = 1000; - if(fdtimeout > 10000) - fdtimeout = 10000; - } - - p = ast_variable_retrieve(cfg, "general", "sdtimeout"); - if (p) { - sdtimeout = atoi(p); - if(sdtimeout < 110) - sdtimeout = 110; - if(sdtimeout > 4000) - sdtimeout = 4000; - } - - p = ast_variable_retrieve(cfg, "general", "logindividualevents"); - if (p) - log_individual_events = ast_true(p); - - p = ast_variable_retrieve(cfg, "general", "eventspooldir"); - if (p) { - ast_copy_string(event_spool_dir, p, sizeof(event_spool_dir)); - } - - p = ast_variable_retrieve(cfg, "general", "timestampformat"); - if (p) { - ast_copy_string(time_stamp_format, p, sizeof(time_stamp_format)); - } - - p = ast_variable_retrieve(cfg, "general", "db-family"); - if (p) { - ast_copy_string(db_family, p, sizeof(db_family)); + } + + if ((value = ast_variable_retrieve(cfg, "general", "eventcmd")) != NULL) { + ast_copy_string(event_app, value, sizeof(event_app)); + } + + if ((value = ast_variable_retrieve(cfg, "general", "loudness")) != NULL) { + toneloudness = atoi(value); + if (toneloudness < 100) { + toneloudness = 100; + } else if (toneloudness > 8192) { + toneloudness = 8192; + } + } + + if ((value = ast_variable_retrieve(cfg, "general", "fdtimeout")) != NULL) { + fdtimeout = atoi(value); + if (fdtimeout < 1000) { + fdtimeout = 1000; + } else if (fdtimeout > 10000) { + fdtimeout = 10000; + } + } + + if ((value = ast_variable_retrieve(cfg, "general", "sdtimeout")) != NULL) { + sdtimeout = atoi(value); + if (sdtimeout < 110) { + sdtimeout = 110; + } else if (sdtimeout > 4000) { + sdtimeout = 4000; } - ast_config_destroy(cfg); } + + if ((value = ast_variable_retrieve(cfg, "general", "answait")) != NULL) { + answait = atoi(value); + if (answait < 500) { + answait = 500; + } else if (answait > 10000) { + answait = 10000; + } + } + + if ((value = ast_variable_retrieve(cfg, "general", "no_group_meta")) != NULL) { + no_group_meta = ast_true(value); + } + + if ((value = ast_variable_retrieve(cfg, "general", "logindividualevents")) != NULL) { + log_individual_events = ast_true(value); + } + + if ((value = ast_variable_retrieve(cfg, "general", "eventspooldir")) != NULL) { + ast_copy_string(event_spool_dir, value, sizeof(event_spool_dir)); + } + + if ((value = ast_variable_retrieve(cfg, "general", "timestampformat")) != NULL) { + ast_copy_string(time_stamp_format, value, sizeof(time_stamp_format)); + } + + if ((value = ast_variable_retrieve(cfg, "general", "db-family")) != NULL) { + ast_copy_string(db_family, value, sizeof(db_family)); + } + + ast_config_destroy(cfg); + return 1; } -/* -* These functions are required to implement an Asterisk App. -*/ +/*! + * \brief Unregister Alarm Receiver App + * + * \retval 0 success + * \retval -1 failure + */ static int unload_module(void) { return ast_unregister_application(app); } +/*! + * \brief Load the module + * + * Module loading including tests for configuration or dependencies. + * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE, + * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails + * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the + * configuration file or other non-critical problem return + * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS. + */ static int load_module(void) { - if (load_config()) { - if (ast_register_application_xml(app, alarmreceiver_exec)) + if (load_config(0)) { + if (ast_register_application_xml(app, alarmreceiver_exec)) { return AST_MODULE_LOAD_FAILURE; + } + return AST_MODULE_LOAD_SUCCESS; + } + + return AST_MODULE_LOAD_DECLINE; +} + +static int reload(void) +{ + if (load_config(1)) { return AST_MODULE_LOAD_SUCCESS; - } else - return AST_MODULE_LOAD_DECLINE; + } + + return AST_MODULE_LOAD_DECLINE; } -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Alarm Receiver for Asterisk"); +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Alarm Receiver for Asterisk", + .support_level = AST_MODULE_SUPPORT_EXTENDED, + .load = load_module, + .unload = unload_module, + .reload = reload, +); diff -Nru asterisk-11.7.0~dfsg/apps/app_amd.c asterisk-13.1.0~dfsg/apps/app_amd.c --- asterisk-11.7.0~dfsg/apps/app_amd.c 2012-02-29 16:52:47.000000000 +0000 +++ asterisk-13.1.0~dfsg/apps/app_amd.c 2014-07-25 16:47:17.000000000 +0000 @@ -24,6 +24,17 @@ * \brief Answering machine detection * * \author Claude Klimos (claude.klimos@aheeva.com) + * + * \ingroup applications + */ + +/*! \li \ref app_amd.c uses the configuration file \ref amd.conf + * \addtogroup configuration_file Configuration Files + */ + +/*! + * \page amd.conf amd.conf + * \verbinclude amd.conf.sample */ /*** MODULEINFO @@ -32,7 +43,7 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 357542 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $") #include "asterisk/module.h" #include "asterisk/lock.h" @@ -41,6 +52,7 @@ #include "asterisk/pbx.h" #include "asterisk/config.h" #include "asterisk/app.h" +#include "asterisk/format_cache.h" /*** DOCUMENTATION @@ -152,7 +164,7 @@ struct ast_frame *f = NULL; struct ast_dsp *silenceDetector = NULL; int dspsilence = 0, framelength = 0; - struct ast_format readFormat; + RAII_VAR(struct ast_format *, readFormat, NULL, ao2_cleanup); int inInitialSilence = 1; int inGreeting = 0; int voiceDuration = 0; @@ -191,11 +203,10 @@ AST_APP_ARG(argMaximumWordLength); ); - ast_format_clear(&readFormat); ast_verb(3, "AMD: %s %s %s (Fmt: %s)\n", ast_channel_name(chan), S_COR(ast_channel_caller(chan)->ani.number.valid, ast_channel_caller(chan)->ani.number.str, "(N/A)"), S_COR(ast_channel_redirecting(chan)->from.number.valid, ast_channel_redirecting(chan)->from.number.str, "(N/A)"), - ast_getformatname(ast_channel_readformat(chan))); + ast_format_get_name(ast_channel_readformat(chan))); /* Lets parse the arguments. */ if (!ast_strlen_zero(parse)) { @@ -244,8 +255,8 @@ minimumWordLength, betweenWordsSilence, maximumNumberOfWords, silenceThreshold, maximumWordLength); /* Set read format to signed linear so we get signed linear frames in */ - ast_format_copy(&readFormat, ast_channel_readformat(chan)); - if (ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR) < 0 ) { + readFormat = ao2_bump(ast_channel_readformat(chan)); + if (ast_set_read_format(chan, ast_format_slin) < 0 ) { ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to set to linear mode, giving up\n", ast_channel_name(chan)); pbx_builtin_setvar_helper(chan , "AMDSTATUS", ""); pbx_builtin_setvar_helper(chan , "AMDCAUSE", ""); @@ -278,7 +289,7 @@ if (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_NULL || f->frametype == AST_FRAME_CNG) { /* If the total time exceeds the analysis time then give up as we are not too sure */ if (f->frametype == AST_FRAME_VOICE) { - framelength = (ast_codec_get_samples(f) / DEFAULT_SAMPLES_PER_MS); + framelength = (ast_codec_samples_count(f) / DEFAULT_SAMPLES_PER_MS); } else { framelength = 2 * maxWaitTimeForFrame; } @@ -401,7 +412,7 @@ pbx_builtin_setvar_helper(chan , "AMDCAUSE" , amdCause); /* Restore channel read format */ - if (readFormat.id && ast_set_read_format(chan, &readFormat)) + if (readFormat && ast_set_read_format(chan, readFormat)) ast_log(LOG_WARNING, "AMD: Unable to restore read format on '%s'\n", ast_channel_name(chan)); /* Free the DSP used to detect silence */ @@ -487,12 +498,22 @@ return ast_unregister_application(app); } +/*! + * \brief Load the module + * + * Module loading including tests for configuration or dependencies. + * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE, + * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails + * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the + * configuration file or other non-critical problem return + * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS. + */ static int load_module(void) { - if (load_config(0)) + if (load_config(0) || ast_register_application_xml(app, amd_exec)) { return AST_MODULE_LOAD_DECLINE; - if (ast_register_application_xml(app, amd_exec)) - return AST_MODULE_LOAD_FAILURE; + } + return AST_MODULE_LOAD_SUCCESS; } @@ -504,6 +525,7 @@ } AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Answering Machine Detection Application", + .support_level = AST_MODULE_SUPPORT_EXTENDED, .load = load_module, .unload = unload_module, .reload = reload, diff -Nru asterisk-11.7.0~dfsg/apps/app_authenticate.c asterisk-13.1.0~dfsg/apps/app_authenticate.c --- asterisk-11.7.0~dfsg/apps/app_authenticate.c 2012-02-20 23:43:27.000000000 +0000 +++ asterisk-13.1.0~dfsg/apps/app_authenticate.c 2013-06-17 03:00:38.000000000 +0000 @@ -31,7 +31,7 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 356042 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 391947 $") #include "asterisk/lock.h" #include "asterisk/file.h" @@ -213,9 +213,9 @@ continue; ast_md5_hash(md5passwd, passwd); if (!strcmp(md5passwd, md5secret)) { - if (ast_test_flag(&flags,OPT_ACCOUNT)) { + if (ast_test_flag(&flags, OPT_ACCOUNT)) { ast_channel_lock(chan); - ast_cdr_setaccount(chan, buf); + ast_channel_accountcode_set(chan, buf); ast_channel_unlock(chan); } break; @@ -224,7 +224,7 @@ if (!strcmp(passwd, buf)) { if (ast_test_flag(&flags, OPT_ACCOUNT)) { ast_channel_lock(chan); - ast_cdr_setaccount(chan, buf); + ast_channel_accountcode_set(chan, buf); ast_channel_unlock(chan); } break; @@ -250,7 +250,7 @@ if ((retries < 3) && !res) { if (ast_test_flag(&flags,OPT_ACCOUNT) && !ast_test_flag(&flags,OPT_MULTIPLE)) { ast_channel_lock(chan); - ast_cdr_setaccount(chan, passwd); + ast_channel_accountcode_set(chan, passwd); ast_channel_unlock(chan); } if (!(res = ast_streamfile(chan, "auth-thankyou", ast_channel_language(chan)))) diff -Nru asterisk-11.7.0~dfsg/apps/app_bridgewait.c asterisk-13.1.0~dfsg/apps/app_bridgewait.c --- asterisk-11.7.0~dfsg/apps/app_bridgewait.c 1970-01-01 00:00:00.000000000 +0000 +++ asterisk-13.1.0~dfsg/apps/app_bridgewait.c 2014-07-25 10:54:49.000000000 +0000 @@ -0,0 +1,514 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Mark Spencer + * + * Author: Jonathan Rose + * + * 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 Application to place the channel into a holding Bridge + * + * \author Jonathan Rose + * + * \ingroup applications + */ + +/*** MODULEINFO + bridge_holding + core + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419539 $") + +#include "asterisk/file.h" +#include "asterisk/channel.h" +#include "asterisk/pbx.h" +#include "asterisk/module.h" +#include "asterisk/features.h" +#include "asterisk/say.h" +#include "asterisk/lock.h" +#include "asterisk/utils.h" +#include "asterisk/app.h" +#include "asterisk/bridge.h" +#include "asterisk/musiconhold.h" +#include "asterisk/astobj2.h" +#include "asterisk/causes.h" + +/*** DOCUMENTATION + + + Put a call into the holding bridge. + + + + Name of the holding bridge to join. This is a handle for BridgeWait + only and does not affect the actual bridges that are created. If not provided, + the reserved name default will be used. + + + + Defines the channel's purpose for entering the holding bridge. Values are case sensitive. + + + + The channel will enter the holding bridge to be placed on hold + until it is removed from the bridge for some reason. (default) + + + The channel will enter the holding bridge to make announcements + to channels that are currently in the holding bridge. While an + announcer is present, holding for the participants will be + suspended. + + + + + + + + + + + + + This application places the incoming channel into a holding bridge. + The channel will then wait in the holding bridge until some event occurs + which removes it from the holding bridge. + This application will answer calls which haven't already + been answered. + + + ***/ + +#define APP_NAME "BridgeWait" +#define DEFAULT_BRIDGE_NAME "default" + +static struct ao2_container *wait_bridge_wrappers; + +struct wait_bridge_wrapper { + struct ast_bridge *bridge; /*!< Bridge being wrapped by this wrapper */ + char name[0]; /*!< Name of the holding bridge wrapper */ +}; + +static void wait_bridge_wrapper_destructor(void *obj) +{ + struct wait_bridge_wrapper *wrapper = obj; + + if (wrapper->bridge) { + ast_bridge_destroy(wrapper->bridge, 0); + } +} + +static struct wait_bridge_wrapper *wait_bridge_wrapper_find_by_name(const char *bridge_name) +{ + return ao2_find(wait_bridge_wrappers, bridge_name, OBJ_KEY); +} + +static int wait_bridge_hash_fn(const void *obj, const int flags) +{ + const struct wait_bridge_wrapper *entry; + const char *key; + + switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) { + case OBJ_KEY: + key = obj; + return ast_str_hash(key); + case OBJ_POINTER: + entry = obj; + return ast_str_hash(entry->name); + default: + /* Hash can only work on something with a full key. */ + ast_assert(0); + return 0; + } +} + +static int wait_bridge_sort_fn(const void *obj_left, const void *obj_right, const int flags) +{ + const struct wait_bridge_wrapper *left = obj_left; + const struct wait_bridge_wrapper *right = obj_right; + const char *right_key = obj_right; + int cmp; + + switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) { + case OBJ_POINTER: + right_key = right->name; + /* Fall through */ + case OBJ_KEY: + cmp = strcmp(left->name, right_key); + break; + case OBJ_PARTIAL_KEY: + cmp = strncmp(left->name, right_key, strlen(right_key)); + break; + default: + /* Sort can only work on something with a full or partial key. */ + ast_assert(0); + cmp = 0; + break; + } + return cmp; +} + +enum bridgewait_flags { + MUXFLAG_MOHCLASS = (1 << 0), + MUXFLAG_ENTERTAINMENT = (1 << 1), + MUXFLAG_TIMEOUT = (1 << 2), +}; + +enum bridgewait_args { + OPT_ARG_ENTERTAINMENT, + OPT_ARG_MOHCLASS, + OPT_ARG_TIMEOUT, + OPT_ARG_ARRAY_SIZE, /* Always the last element of the enum */ +}; + +AST_APP_OPTIONS(bridgewait_opts, { + AST_APP_OPTION_ARG('e', MUXFLAG_ENTERTAINMENT, OPT_ARG_ENTERTAINMENT), + AST_APP_OPTION_ARG('m', MUXFLAG_MOHCLASS, OPT_ARG_MOHCLASS), + AST_APP_OPTION_ARG('S', MUXFLAG_TIMEOUT, OPT_ARG_TIMEOUT), +}); + +static int bridgewait_timeout_callback(struct ast_bridge_channel *bridge_channel, void *hook_pvt) +{ + ast_verb(3, "Channel %s timed out.\n", ast_channel_name(bridge_channel->chan)); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, + AST_CAUSE_NORMAL_CLEARING); + return -1; +} + +static int apply_option_timeout(struct ast_bridge_features *features, char *duration_arg) +{ + unsigned int duration; + + if (ast_strlen_zero(duration_arg)) { + ast_log(LOG_ERROR, "Timeout option 'S': No value provided.\n"); + return -1; + } + if (sscanf(duration_arg, "%u", &duration) != 1 || duration == 0) { + ast_log(LOG_ERROR, "Timeout option 'S': Invalid value provided '%s'.\n", + duration_arg); + return -1; + } + + duration *= 1000; + if (ast_bridge_interval_hook(features, 0, duration, bridgewait_timeout_callback, + NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) { + ast_log(LOG_ERROR, "Timeout option 'S': Could not create timer.\n"); + return -1; + } + + return 0; +} + +static int apply_option_moh(struct ast_channel *chan, const char *class_arg) +{ + return ast_channel_set_bridge_role_option(chan, "holding_participant", "moh_class", class_arg); +} + +static int apply_option_entertainment(struct ast_channel *chan, const char *entertainment_arg) +{ + char entertainment = entertainment_arg[0]; + + switch (entertainment) { + case 'm': + return ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "musiconhold"); + case 'r': + return ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "ringing"); + case 's': + return ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "silence"); + case 'h': + return ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "hold"); + case 'n': + return ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "none"); + default: + ast_log(LOG_ERROR, "Invalid argument for BridgeWait entertainment '%s'\n", entertainment_arg); + return -1; + } +} + +enum wait_bridge_roles { + ROLE_PARTICIPANT = 0, + ROLE_ANNOUNCER, + ROLE_INVALID, +}; + +static int process_options(struct ast_channel *chan, struct ast_flags *flags, char **opts, struct ast_bridge_features *features, enum wait_bridge_roles role) +{ + if (ast_test_flag(flags, MUXFLAG_TIMEOUT)) { + if (apply_option_timeout(features, opts[OPT_ARG_TIMEOUT])) { + return -1; + } + } + + switch (role) { + case ROLE_PARTICIPANT: + if (ast_channel_add_bridge_role(chan, "holding_participant")) { + return -1; + } + + if (ast_test_flag(flags, MUXFLAG_MOHCLASS)) { + if (apply_option_moh(chan, opts[OPT_ARG_MOHCLASS])) { + return -1; + } + } + + if (ast_test_flag(flags, MUXFLAG_ENTERTAINMENT)) { + if (apply_option_entertainment(chan, opts[OPT_ARG_ENTERTAINMENT])) { + return -1; + } + } + + break; + case ROLE_ANNOUNCER: + if (ast_channel_add_bridge_role(chan, "announcer")) { + return -1; + } + break; + case ROLE_INVALID: + ast_assert(0); + return -1; + } + + return 0; +} + +/*! + * \internal + * \since 12.0.0 + * \brief Allocate a new holding bridge wrapper with the given bridge name and bridge ID. + * + * \param bridge_name name of the bridge wrapper + * \param bridge the bridge being wrapped + * + * \retval Pointer to the newly allocated holding bridge wrapper + * \retval NULL if allocation failed. The bridge will be destroyed if this function fails. + */ +static struct wait_bridge_wrapper *wait_bridge_wrapper_alloc(const char *bridge_name, struct ast_bridge *bridge) +{ + struct wait_bridge_wrapper *bridge_wrapper; + + bridge_wrapper = ao2_alloc_options(sizeof(*bridge_wrapper) + strlen(bridge_name) + 1, + wait_bridge_wrapper_destructor, AO2_ALLOC_OPT_LOCK_NOLOCK); + if (!bridge_wrapper) { + ast_bridge_destroy(bridge, 0); + return NULL; + } + + strcpy(bridge_wrapper->name, bridge_name); + bridge_wrapper->bridge = bridge; + + if (!ao2_link(wait_bridge_wrappers, bridge_wrapper)) { + ao2_cleanup(bridge_wrapper); + return NULL; + } + + return bridge_wrapper; +} + +static struct wait_bridge_wrapper *get_wait_bridge_wrapper(const char *bridge_name) +{ + struct wait_bridge_wrapper * wrapper; + struct ast_bridge *bridge = NULL; + + SCOPED_AO2LOCK(lock, wait_bridge_wrappers); + + if ((wrapper = wait_bridge_wrapper_find_by_name(bridge_name))) { + return wrapper; + } + + /* + * Holding bridges can allow local channel move/swap + * optimization to the bridge. However, we cannot allow it for + * this holding bridge because the call will lose the channel + * roles and dialplan location as a result. + */ + bridge = ast_bridge_base_new(AST_BRIDGE_CAPABILITY_HOLDING, + AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM + | AST_BRIDGE_FLAG_SWAP_INHIBIT_TO | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM + | AST_BRIDGE_FLAG_TRANSFER_PROHIBITED, APP_NAME, bridge_name, NULL); + + if (!bridge) { + return NULL; + } + + /* The bridge reference is unconditionally passed. */ + return wait_bridge_wrapper_alloc(bridge_name, bridge); +} + +/*! + * \internal + * \since 12.0.0 + * \brief If we are down to the last reference of a wrapper and it's still contained within the list, remove it from the list. + * + * \param wrapper reference to wait bridge wrapper being checked for list culling - will be cleared on exit + */ +static void wait_wrapper_removal(struct wait_bridge_wrapper *wrapper) +{ + if (!wrapper) { + return; + } + + ao2_lock(wait_bridge_wrappers); + if (ao2_ref(wrapper, 0) == 2) { + /* Either we have the only real reference or else wrapper isn't in the container anyway. */ + ao2_unlink(wait_bridge_wrappers, wrapper); + } + ao2_unlock(wait_bridge_wrappers); + + ao2_cleanup(wrapper); +} + +/*! + * \internal + * \since 12.0.0 + * \brief Application callback for the bridgewait application + * + * \param chan channel running the application + * \param data Arguments to the application + * + * \retval 0 Ran successfully and the call didn't hang up + * \retval -1 Failed or the call was hung up by the time the channel exited the holding bridge + */ +static enum wait_bridge_roles validate_role(const char *role) +{ + if (!strcmp(role, "participant")) { + return ROLE_PARTICIPANT; + } else if (!strcmp(role, "announcer")) { + return ROLE_ANNOUNCER; + } else { + return ROLE_INVALID; + } +} + +static int bridgewait_exec(struct ast_channel *chan, const char *data) +{ + char *bridge_name = DEFAULT_BRIDGE_NAME; + struct ast_bridge_features chan_features; + struct ast_flags flags = { 0 }; + char *parse; + enum wait_bridge_roles role = ROLE_PARTICIPANT; + char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, }; + struct wait_bridge_wrapper *bridge_wrapper; + int res; + + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(name); + AST_APP_ARG(role); + AST_APP_ARG(options); + AST_APP_ARG(other); /* Any remaining unused arguments */ + ); + + parse = ast_strdupa(data); + AST_STANDARD_APP_ARGS(args, parse); + + if (!ast_strlen_zero(args.name)) { + bridge_name = args.name; + } + + if (!ast_strlen_zero(args.role)) { + role = validate_role(args.role); + if (role == ROLE_INVALID) { + ast_log(LOG_ERROR, "Requested waiting bridge role '%s' is invalid.\n", args.role); + return -1; + } + } + + if (ast_bridge_features_init(&chan_features)) { + ast_bridge_features_cleanup(&chan_features); + ast_log(LOG_ERROR, "'%s' failed to enter the waiting bridge - could not set up channel features\n", + ast_channel_name(chan)); + return -1; + } + + if (args.options) { + ast_app_parse_options(bridgewait_opts, &flags, opts, args.options); + } + + /* Answer the channel if needed */ + if (ast_channel_state(chan) != AST_STATE_UP) { + ast_answer(chan); + } + + if (process_options(chan, &flags, opts, &chan_features, role)) { + ast_bridge_features_cleanup(&chan_features); + return -1; + } + + bridge_wrapper = get_wait_bridge_wrapper(bridge_name); + if (!bridge_wrapper) { + ast_log(LOG_WARNING, "Failed to find or create waiting bridge '%s' for '%s'.\n", bridge_name, ast_channel_name(chan)); + ast_bridge_features_cleanup(&chan_features); + return -1; + } + + ast_verb(3, "%s is entering waiting bridge %s:%s\n", ast_channel_name(chan), bridge_name, bridge_wrapper->bridge->uniqueid); + res = ast_bridge_join(bridge_wrapper->bridge, chan, NULL, &chan_features, NULL, 0); + wait_wrapper_removal(bridge_wrapper); + ast_bridge_features_cleanup(&chan_features); + + if (res) { + /* For the lifetime of the bridge wrapper the bridge itself will be valid, if an error occurs it is because + * of extreme situations. + */ + ast_log(LOG_WARNING, "Failed to join waiting bridge '%s' for '%s'.\n", bridge_name, ast_channel_name(chan)); + } + + return (res || ast_check_hangup_locked(chan)) ? -1 : 0; +} + +static int unload_module(void) +{ + ao2_cleanup(wait_bridge_wrappers); + + return ast_unregister_application(APP_NAME); +} + +static int load_module(void) +{ + wait_bridge_wrappers = ao2_container_alloc_hash( + AO2_ALLOC_OPT_LOCK_MUTEX, AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT, + 37, wait_bridge_hash_fn, wait_bridge_sort_fn, NULL); + + if (!wait_bridge_wrappers) { + return -1; + } + + return ast_register_application_xml(APP_NAME, bridgewait_exec); +} + +AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Place the channel into a holding bridge application"); diff -Nru asterisk-11.7.0~dfsg/apps/app_cdr.c asterisk-13.1.0~dfsg/apps/app_cdr.c --- asterisk-11.7.0~dfsg/apps/app_cdr.c 2012-02-20 23:43:27.000000000 +0000 +++ asterisk-13.1.0~dfsg/apps/app_cdr.c 2014-01-12 22:13:12.000000000 +0000 @@ -31,42 +31,238 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 356042 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 405314 $") #include "asterisk/channel.h" #include "asterisk/module.h" +#include "asterisk/app.h" +#include "asterisk/stasis.h" +#include "asterisk/stasis_message_router.h" /*** DOCUMENTATION - Tell Asterisk to not maintain a CDR for the current call + Tell Asterisk to not maintain a CDR for this channel. - This application will tell Asterisk not to maintain a CDR for the current call. + This application will tell Asterisk not to maintain a CDR for + the current channel. This does NOT mean that + information is not tracked; rather, if the channel is hung up no + CDRs will be created for that channel. + If a subsequent call to ResetCDR occurs, all non-finalized + CDRs created for the channel will be enabled. + This application is deprecated. Please use the CDR_PROP + function to disable CDRs on a channel. + + ResetCDR + CDR_PROP + + + + + Resets the Call Data Record. + + + + + + + + + + + This application causes the Call Data Record to be reset. + Depending on the flags passed in, this can have several effects. + With no options, a reset does the following: + 1. The start time is set to the current time. + 2. If the channel is answered, the answer time is set to the + current time. + 3. All variables are wiped from the CDR. Note that this step + can be prevented with the v option. + On the other hand, if the e option is + specified, the effects of the NoCDR application will be lifted. CDRs + will be re-enabled for this channel. + The e option is deprecated. Please + use the CDR_PROP function instead. + + + ForkCDR + NoCDR + CDR_PROP + ***/ static const char nocdr_app[] = "NoCDR"; +static const char resetcdr_app[] = "ResetCDR"; -static int nocdr_exec(struct ast_channel *chan, const char *data) +enum reset_cdr_options { + OPT_DISABLE_DISPATCH = (1 << 0), + OPT_KEEP_VARS = (1 << 1), + OPT_ENABLE = (1 << 2), +}; + +AST_APP_OPTIONS(resetcdr_opts, { + AST_APP_OPTION('v', AST_CDR_FLAG_KEEP_VARS), + AST_APP_OPTION('e', AST_CDR_FLAG_DISABLE_ALL), +}); + +STASIS_MESSAGE_TYPE_DEFN_LOCAL(appcdr_message_type); + +/*! \internal \brief Payload for the Stasis message sent to manipulate a CDR */ +struct app_cdr_message_payload { + /*! The name of the channel to be manipulated */ + const char *channel_name; + /*! Disable the CDR for this channel */ + int disable:1; + /*! Re-enable the CDR for this channel */ + int reenable:1; + /*! Reset the CDR */ + int reset:1; + /*! If reseting the CDR, keep the variables */ + int keep_variables:1; +}; + +static void appcdr_callback(void *data, struct stasis_subscription *sub, struct stasis_message *message) { - if (ast_channel_cdr(chan)) - ast_set_flag(ast_channel_cdr(chan), AST_CDR_FLAG_POST_DISABLED); + struct app_cdr_message_payload *payload; + + if (stasis_message_type(message) != appcdr_message_type()) { + return; + } + + payload = stasis_message_data(message); + if (!payload) { + return; + } + + if (payload->disable) { + if (ast_cdr_set_property(payload->channel_name, AST_CDR_FLAG_DISABLE_ALL)) { + ast_log(AST_LOG_WARNING, "Failed to disable CDRs on channel %s\n", + payload->channel_name); + } + } + + if (payload->reenable) { + if (ast_cdr_clear_property(payload->channel_name, AST_CDR_FLAG_DISABLE_ALL)) { + ast_log(AST_LOG_WARNING, "Failed to enable CDRs on channel %s\n", + payload->channel_name); + } + } + + if (payload->reset) { + if (ast_cdr_reset(payload->channel_name, payload->keep_variables)) { + ast_log(AST_LOG_WARNING, "Failed to reset CDRs on channel %s\n", + payload->channel_name); + } + } +} + +static int publish_app_cdr_message(struct ast_channel *chan, struct app_cdr_message_payload *payload) +{ + RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup); + RAII_VAR(struct stasis_message_router *, router, ast_cdr_message_router(), ao2_cleanup); + + if (!router) { + ast_log(AST_LOG_WARNING, "Failed to manipulate CDR for channel %s: no message router\n", + ast_channel_name(chan)); + return -1; + } + + message = stasis_message_create(appcdr_message_type(), payload); + if (!message) { + ast_log(AST_LOG_WARNING, "Failed to manipulate CDR for channel %s: unable to create message\n", + payload->channel_name); + return -1; + } + stasis_message_router_publish_sync(router, message); return 0; } +static int resetcdr_exec(struct ast_channel *chan, const char *data) +{ + RAII_VAR(struct app_cdr_message_payload *, payload, + ao2_alloc(sizeof(*payload), NULL), ao2_cleanup); + char *args; + struct ast_flags flags = { 0 }; + + if (!payload) { + return -1; + } + + if (!ast_strlen_zero(data)) { + args = ast_strdupa(data); + ast_app_parse_options(resetcdr_opts, &flags, NULL, args); + } + + payload->channel_name = ast_channel_name(chan); + payload->reset = 1; + + if (ast_test_flag(&flags, AST_CDR_FLAG_DISABLE_ALL)) { + payload->reenable = 1; + } + + if (ast_test_flag(&flags, AST_CDR_FLAG_KEEP_VARS)) { + payload->keep_variables = 1; + } + + return publish_app_cdr_message(chan, payload); +} + +static int nocdr_exec(struct ast_channel *chan, const char *data) +{ + RAII_VAR(struct app_cdr_message_payload *, payload, + ao2_alloc(sizeof(*payload), NULL), ao2_cleanup); + + if (!payload) { + return -1; + } + + payload->channel_name = ast_channel_name(chan); + payload->disable = 1; + + return publish_app_cdr_message(chan, payload); +} + static int unload_module(void) { - return ast_unregister_application(nocdr_app); + RAII_VAR(struct stasis_message_router *, router, ast_cdr_message_router(), ao2_cleanup); + + if (router) { + stasis_message_router_remove(router, appcdr_message_type()); + } + STASIS_MESSAGE_TYPE_CLEANUP(appcdr_message_type); + ast_unregister_application(nocdr_app); + ast_unregister_application(resetcdr_app); + return 0; } static int load_module(void) { - if (ast_register_application_xml(nocdr_app, nocdr_exec)) + RAII_VAR(struct stasis_message_router *, router, ast_cdr_message_router(), ao2_cleanup); + int res = 0; + + if (!router) { + return AST_MODULE_LOAD_FAILURE; + } + + res |= STASIS_MESSAGE_TYPE_INIT(appcdr_message_type); + res |= ast_register_application_xml(nocdr_app, nocdr_exec); + res |= ast_register_application_xml(resetcdr_app, resetcdr_exec); + res |= stasis_message_router_add(router, appcdr_message_type(), + appcdr_callback, NULL); + + if (res) { return AST_MODULE_LOAD_FAILURE; + } return AST_MODULE_LOAD_SUCCESS; } diff -Nru asterisk-11.7.0~dfsg/apps/app_celgenuserevent.c asterisk-13.1.0~dfsg/apps/app_celgenuserevent.c --- asterisk-11.7.0~dfsg/apps/app_celgenuserevent.c 2012-11-27 20:38:23.000000000 +0000 +++ asterisk-13.1.0~dfsg/apps/app_celgenuserevent.c 2014-07-25 16:47:17.000000000 +0000 @@ -29,7 +29,7 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 376659 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $") #include "asterisk/module.h" #include "asterisk/app.h" @@ -62,6 +62,7 @@ { int res = 0; char *parse; + RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref); AST_DECLARE_APP_ARGS(args, AST_APP_ARG(event); AST_APP_ARG(extra); @@ -74,7 +75,13 @@ parse = ast_strdupa(data); AST_STANDARD_APP_ARGS(args, parse); - ast_cel_report_event(chan, AST_CEL_USER_DEFINED, args.event, args.extra, NULL); + blob = ast_json_pack("{s: s, s: {s: s}}", + "event", args.event, + "extra", "extra", S_OR(args.extra, "")); + if (!blob) { + return res; + } + ast_cel_publish_event(chan, AST_CEL_USER_DEFINED, blob); return res; } @@ -95,6 +102,7 @@ } AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Generate an User-Defined CEL event", + .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, ); diff -Nru asterisk-11.7.0~dfsg/apps/app_chanisavail.c asterisk-13.1.0~dfsg/apps/app_chanisavail.c --- asterisk-11.7.0~dfsg/apps/app_chanisavail.c 2012-03-14 23:29:32.000000000 +0000 +++ asterisk-13.1.0~dfsg/apps/app_chanisavail.c 2014-07-25 16:47:17.000000000 +0000 @@ -33,7 +33,7 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 359495 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $") #include @@ -171,7 +171,7 @@ } snprintf(tmp, sizeof(tmp), "%d", status); ast_str_append(&tmp_availstat, 0, "%s%s", ast_str_strlen(tmp_availstat) ? "&" : "", tmp); - if ((inuse <= 1) && (tempchan = ast_request(tech, ast_channel_nativeformats(chan), chan, number, &status))) { + if ((inuse <= 1) && (tempchan = ast_request(tech, ast_channel_nativeformats(chan), NULL, chan, number, &status))) { ast_str_append(&tmp_availchan, 0, "%s%s", ast_str_strlen(tmp_availchan) ? "&" : "", ast_channel_name(tempchan)); snprintf(tmp, sizeof(tmp), "%s/%s", tech, number); @@ -211,4 +211,5 @@ AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS; } -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Check channel availability"); +AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Check channel availability"); + diff -Nru asterisk-11.7.0~dfsg/apps/app_channelredirect.c asterisk-13.1.0~dfsg/apps/app_channelredirect.c --- asterisk-11.7.0~dfsg/apps/app_channelredirect.c 2012-03-13 18:20:34.000000000 +0000 +++ asterisk-13.1.0~dfsg/apps/app_channelredirect.c 2013-05-21 18:00:22.000000000 +0000 @@ -29,7 +29,7 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 358907 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 389378 $") #include "asterisk/file.h" #include "asterisk/channel.h" @@ -96,10 +96,6 @@ return 0; } - if (ast_channel_pbx(chan2)) { - ast_set_flag(ast_channel_flags(chan2), AST_FLAG_BRIDGE_HANGUP_DONT); /* don't let the after-bridge code run the h-exten */ - } - res = ast_async_parseable_goto(chan2, args.label); chan2 = ast_channel_unref(chan2); diff -Nru asterisk-11.7.0~dfsg/apps/app_chanspy.c asterisk-13.1.0~dfsg/apps/app_chanspy.c --- asterisk-11.7.0~dfsg/apps/app_chanspy.c 2012-08-08 22:45:15.000000000 +0000 +++ asterisk-13.1.0~dfsg/apps/app_chanspy.c 2014-10-03 19:39:49.000000000 +0000 @@ -35,7 +35,7 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 370955 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 424507 $") #include #include @@ -55,6 +55,9 @@ #include "asterisk/lock.h" #include "asterisk/options.h" #include "asterisk/autochan.h" +#include "asterisk/stasis_channels.h" +#include "asterisk/json.h" +#include "asterisk/format_cache.h" #define AST_NAME_STRLEN 256 #define NUM_SPYGROUPS 128 @@ -143,6 +146,10 @@ + @@ -295,7 +306,9 @@ - Scan DAHDI channels to monitor calls. @@ -338,6 +352,10 @@ Allows a call center manager to monitor DAHDI channels in a convenient way. Use # to select the next channel and use * to exit. + + ChanSpyStart + ChanSpyStop + ***/ @@ -363,10 +381,11 @@ OPTION_NAME = (1 << 12), /* Say the name of the person on whom we will spy */ OPTION_DTMF_SWITCH_MODES = (1 << 13), /* Allow numeric DTMF to switch between chanspy modes */ OPTION_DTMF_EXIT = (1 << 14), /* Set DTMF to exit, added for DAHDIScan integration */ - OPTION_DTMF_CYCLE = (1 << 15), /* Custom DTMF for cycling next avaliable channel, (default is '*') */ + OPTION_DTMF_CYCLE = (1 << 15), /* Custom DTMF for cycling next available channel, (default is '*') */ OPTION_DAHDI_SCAN = (1 << 16), /* Scan groups in DAHDIScan mode */ OPTION_STOP = (1 << 17), OPTION_EXITONHANGUP = (1 << 18), /* Hang up when the spied-on channel hangs up. */ + OPTION_UNIQUEID = (1 << 19), /* The chanprefix is a channel uniqueid or fully specified channel name. */ }; enum { @@ -394,6 +413,7 @@ AST_APP_OPTION_ARG('r', OPTION_RECORD, OPT_ARG_RECORD), AST_APP_OPTION('s', OPTION_NOTECH), AST_APP_OPTION('S', OPTION_STOP), + AST_APP_OPTION('u', OPTION_UNIQUEID), AST_APP_OPTION_ARG('v', OPTION_VOLUME, OPT_ARG_VOLUME), AST_APP_OPTION('w', OPTION_WHISPER), AST_APP_OPTION('W', OPTION_PRIVATE), @@ -432,9 +452,6 @@ { struct chanspy_translation_helper *csth = data; struct ast_frame *f, *cur; - struct ast_format format_slin; - - ast_format_set(&format_slin, AST_FORMAT_SLINEAR, 0); ast_audiohook_lock(&csth->spy_audiohook); if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) { @@ -445,9 +462,9 @@ if (ast_test_flag(&csth->flags, OPTION_READONLY)) { /* Option 'o' was set, so don't mix channel audio */ - f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_READ, &format_slin); + f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_READ, ast_format_slin); } else { - f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, &format_slin); + f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, ast_format_slin); } ast_audiohook_unlock(&csth->spy_audiohook); @@ -481,18 +498,10 @@ static int start_spying(struct ast_autochan *autochan, const char *spychan_name, struct ast_audiohook *audiohook) { - int res = 0; - struct ast_channel *peer = NULL; - ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, ast_channel_name(autochan->chan)); ast_set_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC | AST_AUDIOHOOK_SMALL_QUEUE); - res = ast_audiohook_attach(autochan->chan, audiohook); - - if (!res && ast_test_flag(ast_channel_flags(autochan->chan), AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(autochan->chan))) { - ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE); - } - return res; + return ast_audiohook_attach(autochan->chan, audiohook); } static void change_spy_mode(const char digit, struct ast_flags *flags) @@ -509,50 +518,126 @@ } } +static int pack_channel_into_message(struct ast_channel *chan, const char *role, + struct ast_multi_channel_blob *payload) +{ + RAII_VAR(struct ast_channel_snapshot *, snapshot, + ast_channel_snapshot_get_latest(ast_channel_uniqueid(chan)), + ao2_cleanup); + + if (!snapshot) { + return -1; + } + ast_multi_channel_blob_add_channel(payload, role, snapshot); + return 0; +} + +/*! \internal + * \brief Publish the chanspy message over Stasis-Core + * \param spyer The channel doing the spying + * \param spyee Who is being spied upon + * \start start If non-zero, the spying is starting. Otherwise, the spyer is + * finishing + */ +static void publish_chanspy_message(struct ast_channel *spyer, + struct ast_channel *spyee, + int start) +{ + RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref); + RAII_VAR(struct ast_multi_channel_blob *, payload, NULL, ao2_cleanup); + RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup); + struct stasis_message_type *type = start ? ast_channel_chanspy_start_type(): ast_channel_chanspy_stop_type(); + + if (!spyer) { + ast_log(AST_LOG_WARNING, "Attempt to publish ChanSpy message for NULL spyer channel\n"); + return; + } + blob = ast_json_null(); + if (!blob || !type) { + return; + } + + payload = ast_multi_channel_blob_create(blob); + if (!payload) { + return; + } + + if (pack_channel_into_message(spyer, "spyer_channel", payload)) { + return; + } + + if (spyee) { + if (pack_channel_into_message(spyee, "spyee_channel", payload)) { + return; + } + } + + message = stasis_message_create(type, payload); + if (!message) { + return; + } + stasis_publish(ast_channel_topic(spyer), message); +} + +static int attach_barge(struct ast_autochan *spyee_autochan, + struct ast_autochan **spyee_bridge_autochan, struct ast_audiohook *bridge_whisper_audiohook, + const char *spyer_name, const char *name) +{ + int retval = 0; + struct ast_autochan *internal_bridge_autochan; + RAII_VAR(struct ast_channel *, bridged, ast_channel_bridge_peer(spyee_autochan->chan), ast_channel_cleanup); + + if (!bridged) { + return -1; + } + + ast_audiohook_init(bridge_whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Chanspy", 0); + + internal_bridge_autochan = ast_autochan_setup(bridged); + if (!internal_bridge_autochan) { + return -1; + } + + ast_channel_lock(internal_bridge_autochan->chan); + if (start_spying(internal_bridge_autochan, spyer_name, bridge_whisper_audiohook)) { + ast_log(LOG_WARNING, "Unable to attach barge audiohook on spyee '%s'. Barge mode disabled.\n", name); + retval = -1; + } + ast_channel_unlock(internal_bridge_autochan->chan); + + *spyee_bridge_autochan = internal_bridge_autochan; + + return retval; +} + static int channel_spy(struct ast_channel *chan, struct ast_autochan *spyee_autochan, int *volfactor, int fd, struct spy_dtmf_options *user_options, struct ast_flags *flags, char *exitcontext) { struct chanspy_translation_helper csth; - int running = 0, res, x = 0; + int running = 0, bridge_connected = 0, res, x = 0; char inp[24] = {0}; char *name; struct ast_frame *f; struct ast_silence_generator *silgen = NULL; struct ast_autochan *spyee_bridge_autochan = NULL; const char *spyer_name; - struct ast_channel *chans[] = { chan, spyee_autochan->chan }; - - ast_channel_lock(chan); - spyer_name = ast_strdupa(ast_channel_name(chan)); - ast_channel_unlock(chan); - - /* We now hold the channel lock on spyee */ if (ast_check_hangup(chan) || ast_check_hangup(spyee_autochan->chan) || ast_test_flag(ast_channel_flags(spyee_autochan->chan), AST_FLAG_ZOMBIE)) { return 0; } + ast_channel_lock(chan); + spyer_name = ast_strdupa(ast_channel_name(chan)); + ast_channel_unlock(chan); + ast_channel_lock(spyee_autochan->chan); name = ast_strdupa(ast_channel_name(spyee_autochan->chan)); ast_channel_unlock(spyee_autochan->chan); ast_verb(2, "Spying on channel %s\n", name); - /*** DOCUMENTATION - - Raised when a channel has started spying on another channel. - - ChanSpy - ExtenSpy - ChanSpyStop - - - ***/ - ast_manager_event_multichan(EVENT_FLAG_CALL, "ChanSpyStart", 2, chans, - "SpyerChannel: %s\r\n" - "SpyeeChannel: %s\r\n", - spyer_name, name); + publish_chanspy_message(chan, spyee_autochan->chan, 1); memset(&csth, 0, sizeof(csth)); ast_copy_flags(&csth.flags, flags, AST_FLAGS_ALL); @@ -578,21 +663,6 @@ } } - if (ast_test_flag(flags, OPTION_BARGE | OPTION_DTMF_SWITCH_MODES)) { - /* And this hook lets us inject audio into the channel that the spied on - channel is currently bridged with. - */ - ast_audiohook_init(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Chanspy", 0); - - if ((spyee_bridge_autochan = ast_autochan_setup(ast_bridged_channel(spyee_autochan->chan)))) { - ast_channel_lock(spyee_bridge_autochan->chan); - if (start_spying(spyee_bridge_autochan, spyer_name, &csth.bridge_whisper_audiohook)) { - ast_log(LOG_WARNING, "Unable to attach barge audiohook on spyee %s. Barge mode disabled!\n", name); - } - ast_channel_unlock(spyee_bridge_autochan->chan); - } - } - ast_channel_lock(chan); ast_set_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY); ast_channel_unlock(chan); @@ -632,12 +702,25 @@ } if (ast_test_flag(flags, OPTION_BARGE) && f->frametype == AST_FRAME_VOICE) { + /* This hook lets us inject audio into the channel that the spyee is currently + * bridged with. If the spyee isn't bridged with anything yet, nothing will + * be attached and we'll need to continue attempting to attach the barge + * audio hook. */ + if (!bridge_connected && attach_barge(spyee_autochan, &spyee_bridge_autochan, + &csth.bridge_whisper_audiohook, spyer_name, name) == 0) { + bridge_connected = 1; + } + ast_audiohook_lock(&csth.whisper_audiohook); - ast_audiohook_lock(&csth.bridge_whisper_audiohook); ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f); - ast_audiohook_write_frame(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f); ast_audiohook_unlock(&csth.whisper_audiohook); - ast_audiohook_unlock(&csth.bridge_whisper_audiohook); + + if (bridge_connected) { + ast_audiohook_lock(&csth.bridge_whisper_audiohook); + ast_audiohook_write_frame(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f); + ast_audiohook_unlock(&csth.bridge_whisper_audiohook); + } + ast_frfree(f); continue; } else if (ast_test_flag(flags, OPTION_WHISPER) && f->frametype == AST_FRAME_VOICE) { @@ -647,7 +730,7 @@ ast_frfree(f); continue; } - + res = (f->frametype == AST_FRAME_DTMF) ? f->subclass.integer : 0; ast_frfree(f); if (!res) @@ -737,15 +820,7 @@ } ast_verb(2, "Done Spying on channel %s\n", name); - /*** DOCUMENTATION - - Raised when a channel has stopped spying on another channel. - - ChanSpyStart - - - ***/ - ast_manager_event(chan, EVENT_FLAG_CALL, "ChanSpyStop", "SpyeeChannel: %s\r\n", name); + publish_chanspy_message(chan, NULL, 0); return running; } @@ -775,6 +850,15 @@ return NULL; } +static int spy_sayname(struct ast_channel *chan, const char *mailbox, const char *context) +{ + char *mailbox_id; + + mailbox_id = ast_alloca(strlen(mailbox) + strlen(context) + 2); + sprintf(mailbox_id, "%s@%s", mailbox, context); /* Safe */ + return ast_app_sayname(chan, mailbox_id); +} + static int common_exec(struct ast_channel *chan, struct ast_flags *flags, int volfactor, const int fd, struct spy_dtmf_options *user_options, const char *mygroup, const char *myenforced, const char *spec, const char *exten, @@ -833,7 +917,19 @@ /* Set up the iterator we'll be using during this call */ if (!ast_strlen_zero(spec)) { - iter = ast_channel_iterator_by_name_new(spec, strlen(spec)); + if (ast_test_flag(flags, OPTION_UNIQUEID)) { + struct ast_channel *unique_chan; + + unique_chan = ast_channel_get_by_name(spec); + if (!unique_chan) { + res = -1; + goto exit; + } + iter = ast_channel_iterator_by_name_new(ast_channel_name(unique_chan), 0); + ast_channel_unref(unique_chan); + } else { + iter = ast_channel_iterator_by_name_new(spec, strlen(spec)); + } } else if (!ast_strlen_zero(exten)) { iter = ast_channel_iterator_by_exten_new(exten, context); } else { @@ -885,7 +981,7 @@ break; } - if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(autochan->chan)) { + if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_channel_is_bridged(autochan->chan)) { continue; } @@ -977,8 +1073,9 @@ if (ast_test_flag(flags, OPTION_NAME)) { const char *local_context = S_OR(name_context, "default"); const char *local_mailbox = S_OR(mailbox, ptr); + if (local_mailbox) { - res = ast_app_sayname(chan, local_mailbox, local_context); + res = spy_sayname(chan, local_mailbox, local_context); } else { res = -1; } @@ -996,7 +1093,7 @@ break; } } else { - res = ast_say_character_str(chan, peer_name, "", ast_channel_language(chan)); + res = ast_say_character_str(chan, peer_name, "", ast_channel_language(chan), AST_SAY_CASE_NONE); } } if (ptr && (num = atoi(ptr))) { @@ -1017,7 +1114,7 @@ ast_autochan_destroy(autochan); iter = ast_channel_iterator_destroy(iter); goto exit; - } else if (res > 1 && spec) { + } else if (res > 1 && spec && !ast_test_flag(flags, OPTION_UNIQUEID)) { struct ast_channel *next; snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res); @@ -1070,7 +1167,7 @@ .volume = '#', .exit = '\0', }; - struct ast_format oldwf; + RAII_VAR(struct ast_format *, oldwf, NULL, ao2_cleanup); int volfactor = 0; int res; char *mailbox = NULL; @@ -1083,7 +1180,6 @@ char *parse = ast_strdupa(data); AST_STANDARD_APP_ARGS(args, parse); - ast_format_clear(&oldwf); if (args.spec && !strcmp(args.spec, "all")) args.spec = NULL; @@ -1147,8 +1243,8 @@ ast_clear_flag(&flags, AST_FLAGS_ALL); } - ast_format_copy(&oldwf, ast_channel_writeformat(chan)); - if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) { + oldwf = ao2_bump(ast_channel_writeformat(chan)); + if (ast_set_write_format(chan, ast_format_slin) < 0) { ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); return -1; } @@ -1168,7 +1264,7 @@ if (fd) close(fd); - if (oldwf.id && ast_set_write_format(chan, &oldwf) < 0) + if (oldwf && ast_set_write_format(chan, oldwf) < 0) ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); if (ast_test_flag(&flags, OPTION_EXITONHANGUP)) { @@ -1190,7 +1286,7 @@ .volume = '#', .exit = '\0', }; - struct ast_format oldwf; + RAII_VAR(struct ast_format *, oldwf, NULL, ao2_cleanup); int volfactor = 0; int res; char *mailbox = NULL; @@ -1202,7 +1298,6 @@ char *parse = ast_strdupa(data); AST_STANDARD_APP_ARGS(args, parse); - ast_format_clear(&oldwf); if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) { exten = args.context; @@ -1272,8 +1367,8 @@ ast_clear_flag(&flags, AST_FLAGS_ALL); } - ast_format_copy(&oldwf, ast_channel_writeformat(chan)); - if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) { + oldwf = ao2_bump(ast_channel_writeformat(chan)); + if (ast_set_write_format(chan, ast_format_slin) < 0) { ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); return -1; } @@ -1294,7 +1389,7 @@ if (fd) close(fd); - if (oldwf.id && ast_set_write_format(chan, &oldwf) < 0) + if (oldwf && ast_set_write_format(chan, oldwf) < 0) ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); return res; @@ -1309,13 +1404,13 @@ .volume = '\0', .exit = '*', }; - struct ast_format oldwf; + struct ast_format *oldwf; int res; char *mygroup = NULL; /* Coverity - This uninit_use should be ignored since this macro initializes the flags */ ast_clear_flag(&flags, AST_FLAGS_ALL); - ast_format_clear(&oldwf); + if (!ast_strlen_zero(data)) { mygroup = ast_strdupa(data); } @@ -1323,16 +1418,18 @@ ast_set_flag(&flags, OPTION_DTMF_CYCLE); ast_set_flag(&flags, OPTION_DAHDI_SCAN); - ast_format_copy(&oldwf, ast_channel_writeformat(chan)); - if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) { + oldwf = ao2_bump(ast_channel_writeformat(chan)); + if (ast_set_write_format(chan, ast_format_slin) < 0) { ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); + ao2_cleanup(oldwf); return -1; } res = common_exec(chan, &flags, 0, 0, &user_options, mygroup, NULL, spec, NULL, NULL, NULL, NULL); - if (oldwf.id && ast_set_write_format(chan, &oldwf) < 0) + if (oldwf && ast_set_write_format(chan, oldwf) < 0) ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); + ao2_cleanup(oldwf); return res; } diff -Nru asterisk-11.7.0~dfsg/apps/app_confbridge.c asterisk-13.1.0~dfsg/apps/app_confbridge.c --- asterisk-11.7.0~dfsg/apps/app_confbridge.c 2013-10-08 20:14:14.000000000 +0000 +++ asterisk-13.1.0~dfsg/apps/app_confbridge.c 2014-11-20 15:50:44.000000000 +0000 @@ -28,13 +28,22 @@ * \ingroup applications */ +/*! \li \ref app_confbridge.c uses the configuration file \ref confbridge.conf + * \addtogroup configuration_file Configuration Files + */ + +/*! + * \page confbridge.conf confbridge.conf + * \verbinclude confbridge.conf.sample + */ + /*** MODULEINFO core ***/ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 400741 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 428339 $") #include #include @@ -49,7 +58,7 @@ #include "asterisk/pbx.h" #include "asterisk/module.h" #include "asterisk/lock.h" -#include "asterisk/bridging.h" +#include "asterisk/bridge.h" #include "asterisk/musiconhold.h" #include "asterisk/say.h" #include "asterisk/audiohook.h" @@ -58,6 +67,10 @@ #include "asterisk/paths.h" #include "asterisk/manager.h" #include "asterisk/test.h" +#include "asterisk/stasis.h" +#include "asterisk/stasis_bridges.h" +#include "asterisk/json.h" +#include "asterisk/format_cache.h" /*** DOCUMENTATION @@ -88,13 +101,25 @@ The name of the DTMF menu in confbridge.conf to be applied to - this channel. No menu is applied by default if this option is left - blank. + this channel. When left blank, a dynamically built menu profile + created by the CONFBRIDGE dialplan function is searched for on + the channel and used. If no dynamic profile is present, the + 'default_menu' profile found in confbridge.conf is used. Enters the user into a specified conference bridge. The user can exit the conference by hangup or DTMF menu option. + This application sets the following channel variable upon completion: + + + The channel encountered an error and could not enter the conference. + The channel exited the conference by hanging up. + The channel was kicked from the conference. + The channel left the conference as a result of the last marked user leaving. + The channel pressed a DTMF sequence to exit the conference. + + ConfBridge @@ -104,25 +129,27 @@ - Set a custom dynamic bridge and user profile on a channel for the ConfBridge application using the same options defined in confbridge.conf. + Set a custom dynamic bridge, user, or menu profile on a channel for the ConfBridge application using the same options defined in confbridge.conf. - Type refers to which type of profile the option belongs too. Type can be bridge or user. + Type refers to which type of profile the option belongs too. Type can be bridge, user, or + menu. - Option refers to confbridge.conf option that is being set dynamically on this channel. + Option refers to confbridge.conf option that is being set dynamically on this channel, or + clear to remove already applied options from the channel. ---- Example 1 ---- - In this example the custom set user profile on this channel will automatically be used by the ConfBridge app. + In this example the custom set user profile on this channel will automatically be used by the ConfBridge app. exten => 1,1,Answer() exten => 1,n,Set(CONFBRIDGE(user,announce_join_leave)=yes) exten => 1,n,Set(CONFBRIDGE(user,startmuted)=yes) exten => 1,n,ConfBridge(1) ---- Example 2 ---- - This example shows how to use a predefined user or bridge profile in confbridge.conf as a template for a dynamic profile. Here we make a admin/marked user out of the default_user profile that is already defined in confbridge.conf. + This example shows how to use a predefined user or bridge profile in confbridge.conf as a template for a dynamic profile. Here we make a admin/marked user out of the default_user profile that is already defined in confbridge.conf. exten => 1,1,Answer() exten => 1,n,Set(CONFBRIDGE(user,template)=default_user) exten => 1,n,Set(CONFBRIDGE(user,admin)=yes) @@ -143,7 +170,7 @@ - This function returns a non-negative integer for valid conference identifiers (0 or 1 for locked) and "" for invalid conference identifiers. + This function returns a non-negative integer for valid conference identifiers (0 or 1 for locked) and "" for invalid conference identifiers. @@ -182,7 +209,11 @@ - + + If this parameter is not a complete channel name, the first channel with this prefix will be used. + If this parameter is "all", all channels will be muted. + If this parameter is "participants", all non-admin channels will be muted. + @@ -194,7 +225,11 @@ - + + If this parameter is not a complete channel name, the first channel with this prefix will be used. + If this parameter is "all", all channels will be unmuted. + If this parameter is "participants", all non-admin channels will be unmuted. + @@ -206,7 +241,10 @@ - + + If this parameter is "all", all channels will be kicked from the conference. + If this parameter is "participants", all non-admin channels will be kicked from the conference. + @@ -264,7 +302,9 @@ - + + If this parameter is not a complete channel name, the first channel with this prefix will be used. + @@ -294,12 +334,12 @@ }; /*! \brief Container to hold all conference bridges in progress */ -static struct ao2_container *conference_bridges; +struct ao2_container *conference_bridges; -static void leave_conference(struct conference_bridge_user *user); -static int play_sound_number(struct conference_bridge *conference_bridge, int say_number); -static int execute_menu_entry(struct conference_bridge *conference_bridge, - struct conference_bridge_user *conference_bridge_user, +static void leave_conference(struct confbridge_user *user); +static int play_sound_number(struct confbridge_conference *conference, int say_number); +static int execute_menu_entry(struct confbridge_conference *conference, + struct confbridge_user *user, struct ast_bridge_channel *bridge_channel, struct conf_menu_entry *menu_entry, struct conf_menu *menu); @@ -307,15 +347,48 @@ /*! \brief Hashing function used for conference bridges container */ static int conference_bridge_hash_cb(const void *obj, const int flags) { - const struct conference_bridge *conference_bridge = obj; - return ast_str_case_hash(conference_bridge->name); + const struct confbridge_conference *conference = obj; + const char *name = obj; + int hash; + + switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) { + default: + case OBJ_POINTER: + name = conference->name; + /* Fall through */ + case OBJ_KEY: + hash = ast_str_case_hash(name); + break; + case OBJ_PARTIAL_KEY: + /* Should never happen in hash callback. */ + ast_assert(0); + hash = 0; + break; + } + return hash; } /*! \brief Comparison function used for conference bridges container */ static int conference_bridge_cmp_cb(void *obj, void *arg, int flags) { - const struct conference_bridge *conference_bridge0 = obj, *conference_bridge1 = arg; - return (!strcasecmp(conference_bridge0->name, conference_bridge1->name) ? CMP_MATCH | CMP_STOP : 0); + const struct confbridge_conference *left = obj; + const struct confbridge_conference *right = arg; + const char *right_name = arg; + int cmp; + + switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) { + default: + case OBJ_POINTER: + right_name = right->name; + /* Fall through */ + case OBJ_KEY: + cmp = strcasecmp(left->name, right_name); + break; + case OBJ_PARTIAL_KEY: + cmp = strncasecmp(left->name, right_name, strlen(right_name)); + break; + } + return cmp ? 0 : CMP_MATCH; } const char *conf_get_sound(enum conf_sounds sound, struct bridge_profile_sounds *custom_sounds) @@ -365,63 +438,137 @@ return S_OR(custom_sounds->participantsmuted, "conf-now-muted"); case CONF_SOUND_PARTICIPANTS_UNMUTED: return S_OR(custom_sounds->participantsunmuted, "conf-now-unmuted"); + case CONF_SOUND_BEGIN: + return S_OR(custom_sounds->begin, "confbridge-conf-begin"); } return ""; } -static struct ast_frame *rec_read(struct ast_channel *ast) +static void send_conf_stasis(struct confbridge_conference *conference, struct ast_channel *chan, + struct stasis_message_type *type, struct ast_json *extras, int channel_topic) { - return &ast_null_frame; + RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup); + RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref); + + json_object = ast_json_pack("{s: s}", + "conference", conference->name); + if (!json_object) { + return; + } + + if (extras) { + ast_json_object_update(json_object, extras); + } + + ast_bridge_lock(conference->bridge); + msg = ast_bridge_blob_create(type, + conference->bridge, + chan, + json_object); + ast_bridge_unlock(conference->bridge); + if (!msg) { + return; + } + + if (channel_topic) { + stasis_publish(ast_channel_topic(chan), msg); + } else { + stasis_publish(ast_bridge_topic(conference->bridge), msg); + } } -static int rec_write(struct ast_channel *ast, struct ast_frame *f) + +static void send_conf_start_event(struct confbridge_conference *conference) { - return 0; + send_conf_stasis(conference, NULL, confbridge_start_type(), NULL, 0); } -static struct ast_channel *rec_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause); -static struct ast_channel_tech record_tech = { - .type = "ConfBridgeRec", - .description = "Conference Bridge Recording Channel", - .requester = rec_request, - .read = rec_read, - .write = rec_write, -}; -static struct ast_channel *rec_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause) + +static void send_conf_end_event(struct confbridge_conference *conference) { - struct ast_channel *tmp; - struct ast_format fmt; - const char *conf_name = data; - if (!(tmp = ast_channel_alloc(1, AST_STATE_UP, 0, 0, "", "", "", NULL, 0, - "ConfBridgeRecorder/conf-%s-uid-%d", - conf_name, - (int) ast_random()))) { - return NULL; - } - ast_format_set(&fmt, AST_FORMAT_SLINEAR, 0); - ast_channel_tech_set(tmp, &record_tech); - ast_format_cap_add_all(ast_channel_nativeformats(tmp)); - ast_format_copy(ast_channel_writeformat(tmp), &fmt); - ast_format_copy(ast_channel_rawwriteformat(tmp), &fmt); - ast_format_copy(ast_channel_readformat(tmp), &fmt); - ast_format_copy(ast_channel_rawreadformat(tmp), &fmt); - return tmp; + send_conf_stasis(conference, NULL, confbridge_end_type(), NULL, 0); +} + +static void send_join_event(struct confbridge_user *user, struct confbridge_conference *conference) +{ + struct ast_json *json_object; + + json_object = ast_json_pack("{s: b}", + "admin", ast_test_flag(&user->u_profile, USER_OPT_ADMIN) + ); + if (!json_object) { + return; + } + send_conf_stasis(conference, user->chan, confbridge_join_type(), json_object, 0); + ast_json_unref(json_object); +} + +static void send_leave_event(struct confbridge_user *user, struct confbridge_conference *conference) +{ + struct ast_json *json_object; + + json_object = ast_json_pack("{s: b}", + "admin", ast_test_flag(&user->u_profile, USER_OPT_ADMIN) + ); + if (!json_object) { + return; + } + send_conf_stasis(conference, user->chan, confbridge_leave_type(), json_object, 0); + ast_json_unref(json_object); +} + +static void send_start_record_event(struct confbridge_conference *conference) +{ + send_conf_stasis(conference, NULL, confbridge_start_record_type(), NULL, 0); +} + +static void send_stop_record_event(struct confbridge_conference *conference) +{ + send_conf_stasis(conference, NULL, confbridge_stop_record_type(), NULL, 0); +} + +static void send_mute_event(struct confbridge_user *user, struct confbridge_conference *conference) +{ + struct ast_json *json_object; + + json_object = ast_json_pack("{s: b}", + "admin", ast_test_flag(&user->u_profile, USER_OPT_ADMIN) + ); + if (!json_object) { + return; + } + send_conf_stasis(conference, user->chan, confbridge_mute_type(), json_object, 1); + ast_json_unref(json_object); +} + +static void send_unmute_event(struct confbridge_user *user, struct confbridge_conference *conference) +{ + struct ast_json *json_object; + + json_object = ast_json_pack("{s: b}", + "admin", ast_test_flag(&user->u_profile, USER_OPT_ADMIN) + ); + if (!json_object) { + return; + } + send_conf_stasis(conference, user->chan, confbridge_unmute_type(), json_object, 1); + ast_json_unref(json_object); } -static void set_rec_filename(struct conference_bridge *bridge, struct ast_str **filename, int is_new) +static void set_rec_filename(struct confbridge_conference *conference, struct ast_str **filename, int is_new) { - char *rec_file = bridge->b_profile.rec_file; + char *rec_file = conference->b_profile.rec_file; time_t now; char *ext; - if (ast_str_strlen(*filename) && !is_new) { - return; + if (ast_str_strlen(*filename) && ast_test_flag(&conference->b_profile, BRIDGE_OPT_RECORD_FILE_APPEND) && !is_new) { + return; } time(&now); ast_str_reset(*filename); if (ast_strlen_zero(rec_file)) { - ast_str_set(filename, 0, "confbridge-%s-%u.wav", bridge->name, (unsigned int)now); + ast_str_set(filename, 0, "confbridge-%s-%u.wav", conference->name, (unsigned int)now); } else { /* insert time before file extension */ ext = strrchr(rec_file, '.'); @@ -432,7 +579,10 @@ ast_str_set(filename, 0, "%s-%u", rec_file, (unsigned int)now); } } - ast_str_append(filename, 0, ",a"); + + if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_RECORD_FILE_APPEND)) { + ast_str_append(filename, 0, ",a"); + } } static int is_new_rec_file(const char *rec_file, struct ast_str **orig_rec_file) @@ -452,68 +602,79 @@ static void *record_thread(void *obj) { - struct conference_bridge *conference_bridge = obj; + struct confbridge_conference *conference = obj; struct ast_app *mixmonapp = pbx_findapp("MixMonitor"); struct ast_channel *chan; struct ast_str *filename = ast_str_alloca(PATH_MAX); struct ast_str *orig_rec_file = NULL; + struct ast_bridge_features features; - ast_mutex_lock(&conference_bridge->record_lock); + ast_mutex_lock(&conference->record_lock); if (!mixmonapp) { ast_log(LOG_WARNING, "Can not record ConfBridge, MixMonitor app is not installed\n"); - conference_bridge->record_thread = AST_PTHREADT_NULL; - ast_mutex_unlock(&conference_bridge->record_lock); - ao2_ref(conference_bridge, -1); + conference->record_thread = AST_PTHREADT_NULL; + ast_mutex_unlock(&conference->record_lock); + ao2_ref(conference, -1); + return NULL; + } + if (ast_bridge_features_init(&features)) { + ast_bridge_features_cleanup(&features); + conference->record_thread = AST_PTHREADT_NULL; + ast_mutex_unlock(&conference->record_lock); + ao2_ref(conference, -1); return NULL; } + ast_set_flag(&features.feature_flags, AST_BRIDGE_CHANNEL_FLAG_IMMOVABLE); /* XXX If we get an EXIT right here, START will essentially be a no-op */ - while (conference_bridge->record_state != CONF_RECORD_EXIT) { - set_rec_filename(conference_bridge, &filename, - is_new_rec_file(conference_bridge->b_profile.rec_file, &orig_rec_file)); - chan = ast_channel_ref(conference_bridge->record_chan); + while (conference->record_state != CONF_RECORD_EXIT) { + set_rec_filename(conference, &filename, + is_new_rec_file(conference->b_profile.rec_file, &orig_rec_file)); + chan = ast_channel_ref(conference->record_chan); ast_answer(chan); pbx_exec(chan, mixmonapp, ast_str_buffer(filename)); - ast_bridge_join(conference_bridge->bridge, chan, NULL, NULL, NULL); + ast_bridge_join(conference->bridge, chan, NULL, &features, NULL, 0); ast_hangup(chan); /* This will eat this thread's reference to the channel as well */ /* STOP has been called. Wait for either a START or an EXIT */ - ast_cond_wait(&conference_bridge->record_cond, &conference_bridge->record_lock); + ast_cond_wait(&conference->record_cond, &conference->record_lock); } + ast_bridge_features_cleanup(&features); ast_free(orig_rec_file); - ast_mutex_unlock(&conference_bridge->record_lock); - ao2_ref(conference_bridge, -1); + ast_mutex_unlock(&conference->record_lock); + ao2_ref(conference, -1); return NULL; } /*! \brief Returns whether or not conference is being recorded. - * \param conference_bridge The bridge to check for recording + * \param conference The bridge to check for recording * \retval 1, conference is recording. * \retval 0, conference is NOT recording. */ -static int conf_is_recording(struct conference_bridge *conference_bridge) +static int conf_is_recording(struct confbridge_conference *conference) { - return conference_bridge->record_state == CONF_RECORD_START; + return conference->record_state == CONF_RECORD_START; } /*! \brief Stop recording a conference bridge * \internal - * \param conference_bridge The conference bridge on which to stop the recording + * \param conference The conference bridge on which to stop the recording * \retval -1 Failure * \retval 0 Success */ -static int conf_stop_record(struct conference_bridge *conference_bridge) +static int conf_stop_record(struct confbridge_conference *conference) { struct ast_channel *chan; - if (conference_bridge->record_thread == AST_PTHREADT_NULL || !conf_is_recording(conference_bridge)) { + if (conference->record_thread == AST_PTHREADT_NULL || !conf_is_recording(conference)) { return -1; } - conference_bridge->record_state = CONF_RECORD_STOP; - chan = ast_channel_ref(conference_bridge->record_chan); - ast_bridge_remove(conference_bridge->bridge, chan); + conference->record_state = CONF_RECORD_STOP; + chan = ast_channel_ref(conference->record_chan); + ast_bridge_remove(conference->bridge, chan); ast_queue_frame(chan, &ast_null_frame); chan = ast_channel_unref(chan); - ast_test_suite_event_notify("CONF_STOP_RECORD", "Message: stopped conference recording channel\r\nConference: %s", conference_bridge->b_profile.name); + ast_test_suite_event_notify("CONF_STOP_RECORD", "Message: stopped conference recording channel\r\nConference: %s", conference->b_profile.name); + send_stop_record_event(conference); return 0; } @@ -522,26 +683,26 @@ * \internal * \brief Stops the confbridge recording thread. * - * \note Must be called with the conference_bridge locked + * \note Must be called with the conference locked */ -static int conf_stop_record_thread(struct conference_bridge *conference_bridge) +static int conf_stop_record_thread(struct confbridge_conference *conference) { - if (conference_bridge->record_thread == AST_PTHREADT_NULL) { + if (conference->record_thread == AST_PTHREADT_NULL) { return -1; } - conf_stop_record(conference_bridge); + conf_stop_record(conference); - ast_mutex_lock(&conference_bridge->record_lock); - conference_bridge->record_state = CONF_RECORD_EXIT; - ast_cond_signal(&conference_bridge->record_cond); - ast_mutex_unlock(&conference_bridge->record_lock); + ast_mutex_lock(&conference->record_lock); + conference->record_state = CONF_RECORD_EXIT; + ast_cond_signal(&conference->record_cond); + ast_mutex_unlock(&conference->record_lock); - pthread_join(conference_bridge->record_thread, NULL); - conference_bridge->record_thread = AST_PTHREADT_NULL; + pthread_join(conference->record_thread, NULL); + conference->record_thread = AST_PTHREADT_NULL; /* this is the reference given to the channel during the channel alloc */ - if (conference_bridge->record_chan) { - conference_bridge->record_chan = ast_channel_unref(conference_bridge->record_chan); + if (conference->record_chan) { + conference->record_chan = ast_channel_unref(conference->record_chan); } return 0; @@ -549,18 +710,16 @@ /*! \brief Start recording the conference * \internal - * \note conference_bridge must be locked when calling this function - * \param conference_bridge The conference bridge to start recording + * \note The conference must be locked when calling this function + * \param conference The conference bridge to start recording * \retval 0 success * \rteval non-zero failure */ -static int conf_start_record(struct conference_bridge *conference_bridge) +static int conf_start_record(struct confbridge_conference *conference) { struct ast_format_cap *cap; - struct ast_format tmpfmt; - int cause; - if (conference_bridge->record_state != CONF_RECORD_STOP) { + if (conference->record_state != CONF_RECORD_STOP) { return -1; } @@ -569,146 +728,58 @@ return -1; } - if (!(cap = ast_format_cap_alloc_nolock())) { + cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); + if (!cap) { return -1; } - ast_format_cap_add(cap, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0)); + ast_format_cap_append(cap, ast_format_slin, 0); - if (!(conference_bridge->record_chan = ast_request("ConfBridgeRec", cap, NULL, conference_bridge->name, &cause))) { - cap = ast_format_cap_destroy(cap); + conference->record_chan = ast_request("CBRec", cap, NULL, NULL, + conference->name, NULL); + ao2_ref(cap, -1); + if (!conference->record_chan) { return -1; } - cap = ast_format_cap_destroy(cap); - - conference_bridge->record_state = CONF_RECORD_START; - ast_mutex_lock(&conference_bridge->record_lock); - ast_cond_signal(&conference_bridge->record_cond); - ast_mutex_unlock(&conference_bridge->record_lock); - ast_test_suite_event_notify("CONF_START_RECORD", "Message: started conference recording channel\r\nConference: %s", conference_bridge->b_profile.name); + conference->record_state = CONF_RECORD_START; + ast_mutex_lock(&conference->record_lock); + ast_cond_signal(&conference->record_cond); + ast_mutex_unlock(&conference->record_lock); + ast_test_suite_event_notify("CONF_START_RECORD", "Message: started conference recording channel\r\nConference: %s", conference->b_profile.name); + send_start_record_event(conference); return 0; } /*! \brief Start the recording thread on a conference bridge * \internal - * \param conference_bridge The conference bridge on which to start the recording thread + * \param conference The conference bridge on which to start the recording thread * \retval 0 success * \retval -1 failure */ -static int start_conf_record_thread(struct conference_bridge *conference_bridge) +static int start_conf_record_thread(struct confbridge_conference *conference) { - conf_start_record(conference_bridge); + conf_start_record(conference); /* * if the thread has already been started, don't start another */ - if (conference_bridge->record_thread != AST_PTHREADT_NULL) { + if (conference->record_thread != AST_PTHREADT_NULL) { return 0; } - ao2_ref(conference_bridge, +1); /* give the record thread a ref */ + ao2_ref(conference, +1); /* give the record thread a ref */ - if (ast_pthread_create_background(&conference_bridge->record_thread, NULL, record_thread, conference_bridge)) { - ast_log(LOG_WARNING, "Failed to create recording channel for conference %s\n", conference_bridge->name); - ao2_ref(conference_bridge, -1); /* error so remove ref */ + if (ast_pthread_create_background(&conference->record_thread, NULL, record_thread, conference)) { + ast_log(LOG_WARNING, "Failed to create recording channel for conference %s\n", conference->name); + ao2_ref(conference, -1); /* error so remove ref */ return -1; } return 0; } -static void send_conf_start_event(const char *conf_name) -{ - /*** DOCUMENTATION - - Raised when a conference starts. - - - The name of the Confbridge conference. - - - - ConfbridgeEnd - - - ***/ - manager_event(EVENT_FLAG_CALL, "ConfbridgeStart", "Conference: %s\r\n", conf_name); -} - -static void send_conf_end_event(const char *conf_name) -{ - /*** DOCUMENTATION - - Raised when a conference ends. - - - - - ConfbridgeStart - ConfBridge - - - ***/ - manager_event(EVENT_FLAG_CALL, "ConfbridgeEnd", "Conference: %s\r\n", conf_name); -} - -static void send_join_event(struct ast_channel *chan, const char *conf_name) -{ - /*** DOCUMENTATION - - Raised when a channel joins a Confbridge conference. - - - - - ConfbridgeLeave - ConfBridge - - - ***/ - ast_manager_event(chan, EVENT_FLAG_CALL, "ConfbridgeJoin", - "Channel: %s\r\n" - "Uniqueid: %s\r\n" - "Conference: %s\r\n" - "CallerIDnum: %s\r\n" - "CallerIDname: %s\r\n", - ast_channel_name(chan), - ast_channel_uniqueid(chan), - conf_name, - S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, ""), - S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, "") - ); -} - -static void send_leave_event(struct ast_channel *chan, const char *conf_name) -{ - /*** DOCUMENTATION - - Raised when a channel leaves a Confbridge conference. - - - - - ConfbridgeJoin - - - ***/ - ast_manager_event(chan, EVENT_FLAG_CALL, "ConfbridgeLeave", - "Channel: %s\r\n" - "Uniqueid: %s\r\n" - "Conference: %s\r\n" - "CallerIDnum: %s\r\n" - "CallerIDname: %s\r\n", - ast_channel_name(chan), - ast_channel_uniqueid(chan), - conf_name, - S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, ""), - S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, "") - ); -} - /*! * \internal * \brief Complain if the given sound file does not exist. @@ -729,52 +800,52 @@ /*! * \brief Announce number of users in the conference bridge to the caller * - * \param conference_bridge Conference bridge to peek at - * \param (OPTIONAL) conference_bridge_user Caller + * \param conference Conference bridge to peek at + * \param user Optional Caller * * \note if caller is NULL, the announcment will be sent to all participants in the conference. * \return Returns 0 on success, -1 if the user hung up */ -static int announce_user_count(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user) +static int announce_user_count(struct confbridge_conference *conference, struct confbridge_user *user) { - const char *other_in_party = conf_get_sound(CONF_SOUND_OTHER_IN_PARTY, conference_bridge->b_profile.sounds); - const char *only_one = conf_get_sound(CONF_SOUND_ONLY_ONE, conference_bridge->b_profile.sounds); - const char *there_are = conf_get_sound(CONF_SOUND_THERE_ARE, conference_bridge->b_profile.sounds); + const char *other_in_party = conf_get_sound(CONF_SOUND_OTHER_IN_PARTY, conference->b_profile.sounds); + const char *only_one = conf_get_sound(CONF_SOUND_ONLY_ONE, conference->b_profile.sounds); + const char *there_are = conf_get_sound(CONF_SOUND_THERE_ARE, conference->b_profile.sounds); - if (conference_bridge->activeusers <= 1) { + if (conference->activeusers <= 1) { /* Awww we are the only person in the conference bridge OR we only have waitmarked users */ return 0; - } else if (conference_bridge->activeusers == 2) { - if (conference_bridge_user) { + } else if (conference->activeusers == 2) { + if (user) { /* Eep, there is one other person */ - if (ast_stream_and_wait(conference_bridge_user->chan, + if (ast_stream_and_wait(user->chan, only_one, "")) { return -1; } } else { - play_sound_file(conference_bridge, only_one); + play_sound_file(conference, only_one); } } else { /* Alas multiple others in here */ - if (conference_bridge_user) { - if (ast_stream_and_wait(conference_bridge_user->chan, + if (user) { + if (ast_stream_and_wait(user->chan, there_are, "")) { return -1; } - if (ast_say_number(conference_bridge_user->chan, conference_bridge->activeusers - 1, "", ast_channel_language(conference_bridge_user->chan), NULL)) { + if (ast_say_number(user->chan, conference->activeusers - 1, "", ast_channel_language(user->chan), NULL)) { return -1; } - if (ast_stream_and_wait(conference_bridge_user->chan, + if (ast_stream_and_wait(user->chan, other_in_party, "")) { return -1; } } else if (sound_file_exists(there_are) && sound_file_exists(other_in_party)) { - play_sound_file(conference_bridge, there_are); - play_sound_number(conference_bridge, conference_bridge->activeusers - 1); - play_sound_file(conference_bridge, other_in_party); + play_sound_file(conference, there_are); + play_sound_number(conference, conference->activeusers - 1); + play_sound_file(conference, other_in_party); } } return 0; @@ -783,7 +854,7 @@ /*! * \brief Play back an audio file to a channel * - * \param cbu User to play audio prompt to + * \param user User to play audio prompt to * \param filename Prompt to play * * \return Returns 0 on success, -1 if the user hung up @@ -791,78 +862,79 @@ * the entire conference while the sound is played. But don't unlock the conference bridge * in the middle of a state transition. */ -static int play_prompt_to_user(struct conference_bridge_user *cbu, const char *filename) +static int play_prompt_to_user(struct confbridge_user *user, const char *filename) { - return ast_stream_and_wait(cbu->chan, filename, ""); + return ast_stream_and_wait(user->chan, filename, ""); } -static void handle_video_on_join(struct conference_bridge *conference_bridge, struct ast_channel *chan, int marked) +static void handle_video_on_join(struct confbridge_conference *conference, struct ast_channel *chan, int marked) { /* Right now, only marked users are automatically set as the single src of video.*/ if (!marked) { return; } - if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED)) { + if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED)) { int set = 1; - struct conference_bridge_user *tmp_user = NULL; - ao2_lock(conference_bridge); + struct confbridge_user *user = NULL; + + ao2_lock(conference); /* see if anyone is already the video src */ - AST_LIST_TRAVERSE(&conference_bridge->active_list, tmp_user, list) { - if (tmp_user->chan == chan) { + AST_LIST_TRAVERSE(&conference->active_list, user, list) { + if (user->chan == chan) { continue; } - if (ast_bridge_is_video_src(conference_bridge->bridge, tmp_user->chan)) { + if (ast_bridge_is_video_src(conference->bridge, user->chan)) { set = 0; break; } } - ao2_unlock(conference_bridge); + ao2_unlock(conference); if (set) { - ast_bridge_set_single_src_video_mode(conference_bridge->bridge, chan); + ast_bridge_set_single_src_video_mode(conference->bridge, chan); } - } else if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED)) { + } else if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED)) { /* we joined and are video capable, we override anyone else that may have already been the video feed */ - ast_bridge_set_single_src_video_mode(conference_bridge->bridge, chan); + ast_bridge_set_single_src_video_mode(conference->bridge, chan); } } -static void handle_video_on_exit(struct conference_bridge *conference_bridge, struct ast_channel *chan) +static void handle_video_on_exit(struct confbridge_conference *conference, struct ast_channel *chan) { - struct conference_bridge_user *tmp_user = NULL; + struct confbridge_user *user = NULL; /* if this isn't a video source, nothing to update */ - if (!ast_bridge_is_video_src(conference_bridge->bridge, chan)) { + if (!ast_bridge_is_video_src(conference->bridge, chan)) { return; } - ast_bridge_remove_video_src(conference_bridge->bridge, chan); + ast_bridge_remove_video_src(conference->bridge, chan); /* If in follow talker mode, make sure to restore this mode on the * bridge when a source is removed. It is possible this channel was * only set temporarily as a video source by an AMI or DTMF action. */ - if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER)) { - ast_bridge_set_talker_src_video_mode(conference_bridge->bridge); + if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER)) { + ast_bridge_set_talker_src_video_mode(conference->bridge); } /* if the video_mode isn't set to automatically pick the video source, do nothing on exit. */ - if (!ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED) && - !ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED)) { + if (!ast_test_flag(&conference->b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED) && + !ast_test_flag(&conference->b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED)) { return; } /* Make the next available marked user the video src. */ - ao2_lock(conference_bridge); - AST_LIST_TRAVERSE(&conference_bridge->active_list, tmp_user, list) { - if (tmp_user->chan == chan) { + ao2_lock(conference); + AST_LIST_TRAVERSE(&conference->active_list, user, list) { + if (user->chan == chan) { continue; } - if (ast_test_flag(&tmp_user->u_profile, USER_OPT_MARKEDUSER)) { - ast_bridge_set_single_src_video_mode(conference_bridge->bridge, tmp_user->chan); + if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) { + ast_bridge_set_single_src_video_mode(conference->bridge, user->chan); break; } } - ao2_unlock(conference_bridge); + ao2_unlock(conference); } /*! @@ -874,75 +946,72 @@ */ static void destroy_conference_bridge(void *obj) { - struct conference_bridge *conference_bridge = obj; + struct confbridge_conference *conference = obj; - ast_debug(1, "Destroying conference bridge '%s'\n", conference_bridge->name); + ast_debug(1, "Destroying conference bridge '%s'\n", conference->name); - if (conference_bridge->playback_chan) { - struct ast_channel *underlying_channel = ast_channel_tech(conference_bridge->playback_chan)->bridged_channel(conference_bridge->playback_chan, NULL); - if (underlying_channel) { - ast_hangup(underlying_channel); - } - ast_hangup(conference_bridge->playback_chan); - conference_bridge->playback_chan = NULL; + if (conference->playback_chan) { + conf_announce_channel_depart(conference->playback_chan); + ast_hangup(conference->playback_chan); + conference->playback_chan = NULL; } /* Destroying a conference bridge is simple, all we have to do is destroy the bridging object */ - if (conference_bridge->bridge) { - ast_bridge_destroy(conference_bridge->bridge); - conference_bridge->bridge = NULL; + if (conference->bridge) { + ast_bridge_destroy(conference->bridge, 0); + conference->bridge = NULL; } - conf_bridge_profile_destroy(&conference_bridge->b_profile); - ast_cond_destroy(&conference_bridge->record_cond); - ast_mutex_destroy(&conference_bridge->record_lock); - ast_mutex_destroy(&conference_bridge->playback_lock); + conf_bridge_profile_destroy(&conference->b_profile); + ast_cond_destroy(&conference->record_cond); + ast_mutex_destroy(&conference->record_lock); + ast_mutex_destroy(&conference->playback_lock); } /*! \brief Call the proper join event handler for the user for the conference bridge's current state * \internal - * \param cbu The conference bridge user that is joining + * \param user The conference bridge user that is joining * \retval 0 success * \retval -1 failure */ -static int handle_conf_user_join(struct conference_bridge_user *cbu) +static int handle_conf_user_join(struct confbridge_user *user) { conference_event_fn handler; - if (ast_test_flag(&cbu->u_profile, USER_OPT_MARKEDUSER)) { - handler = cbu->conference_bridge->state->join_marked; - } else if (ast_test_flag(&cbu->u_profile, USER_OPT_WAITMARKED)) { - handler = cbu->conference_bridge->state->join_waitmarked; + if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) { + handler = user->conference->state->join_marked; + } else if (ast_test_flag(&user->u_profile, USER_OPT_WAITMARKED)) { + handler = user->conference->state->join_waitmarked; } else { - handler = cbu->conference_bridge->state->join_unmarked; + handler = user->conference->state->join_unmarked; } ast_assert(handler != NULL); if (!handler) { - conf_invalid_event_fn(cbu); + conf_invalid_event_fn(user); return -1; } - handler(cbu); + handler(user); return 0; } /*! \brief Call the proper leave event handler for the user for the conference bridge's current state * \internal - * \param cbu The conference bridge user that is leaving + * \param user The conference bridge user that is leaving * \retval 0 success * \retval -1 failure */ -static int handle_conf_user_leave(struct conference_bridge_user *cbu) +static int handle_conf_user_leave(struct confbridge_user *user) { conference_event_fn handler; - if (ast_test_flag(&cbu->u_profile, USER_OPT_MARKEDUSER)) { - handler = cbu->conference_bridge->state->leave_marked; - } else if (ast_test_flag(&cbu->u_profile, USER_OPT_WAITMARKED)) { - handler = cbu->conference_bridge->state->leave_waitmarked; + if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) { + handler = user->conference->state->leave_marked; + } else if (ast_test_flag(&user->u_profile, USER_OPT_WAITMARKED)) { + handler = user->conference->state->leave_waitmarked; } else { - handler = cbu->conference_bridge->state->leave_unmarked; + handler = user->conference->state->leave_unmarked; } ast_assert(handler != NULL); @@ -951,16 +1020,49 @@ /* This should never happen. If it does, though, it is bad. The user will not have been removed * from the appropriate list, so counts will be off and stuff. The conference won't be torn down, etc. * Shouldn't happen, though. */ - conf_invalid_event_fn(cbu); + conf_invalid_event_fn(user); return -1; } - handler(cbu); + handler(user); return 0; } -void conf_moh_stop(struct conference_bridge_user *user) +void conf_update_user_mute(struct confbridge_user *user) +{ + int mute_user; + int mute_system; + int mute_effective; + + /* User level mute request. */ + mute_user = user->muted; + + /* System level mute request. */ + mute_system = user->playing_moh + /* + * Do not allow waitmarked users to talk to anyone unless there + * is a marked user present. + */ + || (!user->conference->markedusers + && ast_test_flag(&user->u_profile, USER_OPT_WAITMARKED)); + + mute_effective = mute_user || mute_system; + + ast_debug(1, "User %s is %s: user:%d system:%d.\n", + ast_channel_name(user->chan), mute_effective ? "muted" : "unmuted", + mute_user, mute_system); + user->features.mute = mute_effective; + ast_test_suite_event_notify("CONF_MUTE_UPDATE", + "Mode: %s\r\n" + "Conference: %s\r\n" + "Channel: %s", + mute_effective ? "muted" : "unmuted", + user->b_profile.name, + ast_channel_name(user->chan)); +} + +void conf_moh_stop(struct confbridge_user *user) { user->playing_moh = 0; if (!user->suspended_moh) { @@ -971,23 +1073,23 @@ * call to ast_bridge_join() in confbridge_exec() from * interfering with the bridge and MOH operations here. */ - ast_bridge_lock(user->conference_bridge->bridge); + ast_bridge_lock(user->conference->bridge); /* * Temporarily suspend the user from the bridge so we have * control to stop MOH if needed. */ - in_bridge = !ast_bridge_suspend(user->conference_bridge->bridge, user->chan); + in_bridge = !ast_bridge_suspend(user->conference->bridge, user->chan); ast_moh_stop(user->chan); if (in_bridge) { - ast_bridge_unsuspend(user->conference_bridge->bridge, user->chan); + ast_bridge_unsuspend(user->conference->bridge, user->chan); } - ast_bridge_unlock(user->conference_bridge->bridge); + ast_bridge_unlock(user->conference->bridge); } } -void conf_moh_start(struct conference_bridge_user *user) +void conf_moh_start(struct confbridge_user *user) { user->playing_moh = 1; if (!user->suspended_moh) { @@ -998,19 +1100,19 @@ * call to ast_bridge_join() in confbridge_exec() from * interfering with the bridge and MOH operations here. */ - ast_bridge_lock(user->conference_bridge->bridge); + ast_bridge_lock(user->conference->bridge); /* * Temporarily suspend the user from the bridge so we have * control to start MOH if needed. */ - in_bridge = !ast_bridge_suspend(user->conference_bridge->bridge, user->chan); + in_bridge = !ast_bridge_suspend(user->conference->bridge, user->chan); ast_moh_start(user->chan, user->u_profile.moh_class, NULL); if (in_bridge) { - ast_bridge_unsuspend(user->conference_bridge->bridge, user->chan); + ast_bridge_unsuspend(user->conference->bridge, user->chan); } - ast_bridge_unlock(user->conference_bridge->bridge); + ast_bridge_unlock(user->conference->bridge); } } @@ -1022,13 +1124,13 @@ * * \return Nothing */ -static void conf_moh_unsuspend(struct conference_bridge_user *user) +static void conf_moh_unsuspend(struct confbridge_user *user) { - ao2_lock(user->conference_bridge); + ao2_lock(user->conference); if (--user->suspended_moh == 0 && user->playing_moh) { ast_moh_start(user->chan, user->u_profile.moh_class, NULL); } - ao2_unlock(user->conference_bridge); + ao2_unlock(user->conference); } /*! @@ -1039,40 +1141,32 @@ * * \return Nothing */ -static void conf_moh_suspend(struct conference_bridge_user *user) +static void conf_moh_suspend(struct confbridge_user *user) { - ao2_lock(user->conference_bridge); + ao2_lock(user->conference); if (user->suspended_moh++ == 0 && user->playing_moh) { ast_moh_stop(user->chan); } - ao2_unlock(user->conference_bridge); + ao2_unlock(user->conference); } -int conf_handle_first_marked_common(struct conference_bridge_user *cbu) -{ - if (!ast_test_flag(&cbu->u_profile, USER_OPT_QUIET) && play_prompt_to_user(cbu, conf_get_sound(CONF_SOUND_PLACE_IN_CONF, cbu->b_profile.sounds))) { - return -1; - } - return 0; -} - -int conf_handle_inactive_waitmarked(struct conference_bridge_user *cbu) +int conf_handle_inactive_waitmarked(struct confbridge_user *user) { /* If we have not been quieted play back that they are waiting for the leader */ - if (!ast_test_flag(&cbu->u_profile, USER_OPT_QUIET) && play_prompt_to_user(cbu, - conf_get_sound(CONF_SOUND_WAIT_FOR_LEADER, cbu->b_profile.sounds))) { + if (!ast_test_flag(&user->u_profile, USER_OPT_QUIET) && play_prompt_to_user(user, + conf_get_sound(CONF_SOUND_WAIT_FOR_LEADER, user->b_profile.sounds))) { /* user hungup while the sound was playing */ return -1; } return 0; } -int conf_handle_only_unmarked(struct conference_bridge_user *cbu) +int conf_handle_only_unmarked(struct confbridge_user *user) { /* If audio prompts have not been quieted or this prompt quieted play it on out */ - if (!ast_test_flag(&cbu->u_profile, USER_OPT_QUIET | USER_OPT_NOONLYPERSON)) { - if (play_prompt_to_user(cbu, - conf_get_sound(CONF_SOUND_ONLY_PERSON, cbu->b_profile.sounds))) { + if (!ast_test_flag(&user->u_profile, USER_OPT_QUIET | USER_OPT_NOONLYPERSON)) { + if (play_prompt_to_user(user, + conf_get_sound(CONF_SOUND_ONLY_PERSON, user->b_profile.sounds))) { /* user hungup while the sound was playing */ return -1; } @@ -1080,217 +1174,214 @@ return 0; } -int conf_add_post_join_action(struct conference_bridge_user *cbu, int (*func)(struct conference_bridge_user *cbu)) +int conf_add_post_join_action(struct confbridge_user *user, int (*func)(struct confbridge_user *user)) { struct post_join_action *action; if (!(action = ast_calloc(1, sizeof(*action)))) { return -1; } action->func = func; - AST_LIST_INSERT_TAIL(&cbu->post_join_list, action, list); + AST_LIST_INSERT_TAIL(&user->post_join_list, action, list); return 0; } -void conf_handle_first_join(struct conference_bridge *conference_bridge) +void conf_handle_first_join(struct confbridge_conference *conference) { - ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, "confbridge:%s", conference_bridge->name); + ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, "confbridge:%s", conference->name); } -void conf_handle_second_active(struct conference_bridge *conference_bridge) +void conf_handle_second_active(struct confbridge_conference *conference) { /* If we are the second participant we may need to stop music on hold on the first */ - struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->active_list); + struct confbridge_user *first_user = AST_LIST_FIRST(&conference->active_list); - if (ast_test_flag(&first_participant->u_profile, USER_OPT_MUSICONHOLD)) { - conf_moh_stop(first_participant); - } - if (!ast_test_flag(&first_participant->u_profile, USER_OPT_STARTMUTED)) { - first_participant->features.mute = 0; + if (ast_test_flag(&first_user->u_profile, USER_OPT_MUSICONHOLD)) { + conf_moh_stop(first_user); } + conf_update_user_mute(first_user); } -void conf_ended(struct conference_bridge *conference_bridge) +void conf_ended(struct confbridge_conference *conference) { - /* Called with a reference to conference_bridge */ - ao2_unlink(conference_bridges, conference_bridge); - send_conf_end_event(conference_bridge->name); - conf_stop_record_thread(conference_bridge); + /* Called with a reference to conference */ + ao2_unlink(conference_bridges, conference); + send_conf_end_event(conference); + conf_stop_record_thread(conference); } /*! * \brief Join a conference bridge * - * \param name The conference name - * \param conference_bridge_user Conference bridge user structure + * \param conference_name The conference name + * \param user Conference bridge user structure * * \return A pointer to the conference bridge struct, or NULL if the conference room wasn't found. */ -static struct conference_bridge *join_conference_bridge(const char *name, struct conference_bridge_user *conference_bridge_user) +static struct confbridge_conference *join_conference_bridge(const char *conference_name, struct confbridge_user *user) { - struct conference_bridge *conference_bridge = NULL; + struct confbridge_conference *conference; struct post_join_action *action; - struct conference_bridge tmp; int max_members_reached = 0; - ast_copy_string(tmp.name, name, sizeof(tmp.name)); - /* We explictly lock the conference bridges container ourselves so that other callers can not create duplicate conferences at the same */ ao2_lock(conference_bridges); - ast_debug(1, "Trying to find conference bridge '%s'\n", name); + ast_debug(1, "Trying to find conference bridge '%s'\n", conference_name); /* Attempt to find an existing conference bridge */ - conference_bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER); - - if (conference_bridge && conference_bridge->b_profile.max_members) { - max_members_reached = conference_bridge->b_profile.max_members > conference_bridge->activeusers ? 0 : 1; + conference = ao2_find(conference_bridges, conference_name, OBJ_KEY); + if (conference && conference->b_profile.max_members) { + max_members_reached = conference->b_profile.max_members > conference->activeusers ? 0 : 1; } /* When finding a conference bridge that already exists make sure that it is not locked, and if so that we are not an admin */ - if (conference_bridge && (max_members_reached || conference_bridge->locked) && !ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ADMIN)) { + if (conference && (max_members_reached || conference->locked) && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN)) { ao2_unlock(conference_bridges); - ao2_ref(conference_bridge, -1); - ast_debug(1, "Conference '%s' is locked and caller is not an admin\n", name); - ast_stream_and_wait(conference_bridge_user->chan, - conf_get_sound(CONF_SOUND_LOCKED, conference_bridge_user->b_profile.sounds), + ao2_ref(conference, -1); + ast_debug(1, "Conference '%s' is locked and caller is not an admin\n", conference_name); + ast_stream_and_wait(user->chan, + conf_get_sound(CONF_SOUND_LOCKED, user->b_profile.sounds), ""); return NULL; } /* If no conference bridge was found see if we can create one */ - if (!conference_bridge) { + if (!conference) { /* Try to allocate memory for a new conference bridge, if we fail... this won't end well. */ - if (!(conference_bridge = ao2_alloc(sizeof(*conference_bridge), destroy_conference_bridge))) { + if (!(conference = ao2_alloc(sizeof(*conference), destroy_conference_bridge))) { ao2_unlock(conference_bridges); - ast_log(LOG_ERROR, "Conference '%s' could not be created.\n", name); + ast_log(LOG_ERROR, "Conference '%s' could not be created.\n", conference_name); return NULL; } /* Setup lock for playback channel */ - ast_mutex_init(&conference_bridge->playback_lock); + ast_mutex_init(&conference->playback_lock); /* Setup lock for the record channel */ - ast_mutex_init(&conference_bridge->record_lock); - ast_cond_init(&conference_bridge->record_cond, NULL); + ast_mutex_init(&conference->record_lock); + ast_cond_init(&conference->record_cond, NULL); /* Setup conference bridge parameters */ - conference_bridge->record_thread = AST_PTHREADT_NULL; - ast_copy_string(conference_bridge->name, name, sizeof(conference_bridge->name)); - conf_bridge_profile_copy(&conference_bridge->b_profile, &conference_bridge_user->b_profile); + conference->record_thread = AST_PTHREADT_NULL; + ast_copy_string(conference->name, conference_name, sizeof(conference->name)); + conf_bridge_profile_copy(&conference->b_profile, &user->b_profile); /* Create an actual bridge that will do the audio mixing */ - if (!(conference_bridge->bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_MULTIMIX, 0))) { - ao2_ref(conference_bridge, -1); - conference_bridge = NULL; + conference->bridge = ast_bridge_base_new(AST_BRIDGE_CAPABILITY_MULTIMIX, + AST_BRIDGE_FLAG_MASQUERADE_ONLY | AST_BRIDGE_FLAG_TRANSFER_BRIDGE_ONLY, + app, conference_name, NULL); + if (!conference->bridge) { + ao2_ref(conference, -1); + conference = NULL; ao2_unlock(conference_bridges); - ast_log(LOG_ERROR, "Conference '%s' mixing bridge could not be created.\n", name); + ast_log(LOG_ERROR, "Conference '%s' mixing bridge could not be created.\n", conference_name); return NULL; } /* Set the internal sample rate on the bridge from the bridge profile */ - ast_bridge_set_internal_sample_rate(conference_bridge->bridge, conference_bridge->b_profile.internal_sample_rate); + ast_bridge_set_internal_sample_rate(conference->bridge, conference->b_profile.internal_sample_rate); /* Set the internal mixing interval on the bridge from the bridge profile */ - ast_bridge_set_mixing_interval(conference_bridge->bridge, conference_bridge->b_profile.mix_interval); + ast_bridge_set_mixing_interval(conference->bridge, conference->b_profile.mix_interval); - if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER)) { - ast_bridge_set_talker_src_video_mode(conference_bridge->bridge); + if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER)) { + ast_bridge_set_talker_src_video_mode(conference->bridge); } /* Link it into the conference bridges container */ - if (!ao2_link(conference_bridges, conference_bridge)) { - ao2_ref(conference_bridge, -1); - conference_bridge = NULL; + if (!ao2_link(conference_bridges, conference)) { + ao2_ref(conference, -1); + conference = NULL; ao2_unlock(conference_bridges); ast_log(LOG_ERROR, - "Conference '%s' could not be added to the conferences list.\n", name); + "Conference '%s' could not be added to the conferences list.\n", conference_name); return NULL; } /* Set the initial state to EMPTY */ - conference_bridge->state = CONF_STATE_EMPTY; + conference->state = CONF_STATE_EMPTY; - conference_bridge->record_state = CONF_RECORD_STOP; - if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_RECORD_CONFERENCE)) { - ao2_lock(conference_bridge); - start_conf_record_thread(conference_bridge); - ao2_unlock(conference_bridge); + conference->record_state = CONF_RECORD_STOP; + if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_RECORD_CONFERENCE)) { + ao2_lock(conference); + start_conf_record_thread(conference); + ao2_unlock(conference); } - send_conf_start_event(conference_bridge->name); - ast_debug(1, "Created conference '%s' and linked to container.\n", name); + send_conf_start_event(conference); + ast_debug(1, "Created conference '%s' and linked to container.\n", conference_name); } ao2_unlock(conference_bridges); /* Setup conference bridge user parameters */ - conference_bridge_user->conference_bridge = conference_bridge; + user->conference = conference; - ao2_lock(conference_bridge); + ao2_lock(conference); /* * Suspend any MOH until the user actually joins the bridge of * the conference. This way any pre-join file playback does not * need to worry about MOH. */ - conference_bridge_user->suspended_moh = 1; + user->suspended_moh = 1; - if (handle_conf_user_join(conference_bridge_user)) { + if (handle_conf_user_join(user)) { /* Invalid event, nothing was done, so we don't want to process a leave. */ - ao2_unlock(conference_bridge); - ao2_ref(conference_bridge, -1); + ao2_unlock(conference); + ao2_ref(conference, -1); return NULL; } - if (ast_check_hangup(conference_bridge_user->chan)) { - ao2_unlock(conference_bridge); - leave_conference(conference_bridge_user); + if (ast_check_hangup(user->chan)) { + ao2_unlock(conference); + leave_conference(user); return NULL; } - ao2_unlock(conference_bridge); + ao2_unlock(conference); /* If an announcement is to be played play it */ - if (!ast_strlen_zero(conference_bridge_user->u_profile.announcement)) { - if (play_prompt_to_user(conference_bridge_user, - conference_bridge_user->u_profile.announcement)) { - leave_conference(conference_bridge_user); + if (!ast_strlen_zero(user->u_profile.announcement)) { + if (play_prompt_to_user(user, + user->u_profile.announcement)) { + leave_conference(user); return NULL; } } /* Announce number of users if need be */ - if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ANNOUNCEUSERCOUNT)) { - if (announce_user_count(conference_bridge, conference_bridge_user)) { - leave_conference(conference_bridge_user); + if (ast_test_flag(&user->u_profile, USER_OPT_ANNOUNCEUSERCOUNT)) { + if (announce_user_count(conference, user)) { + leave_conference(user); return NULL; } } - if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ANNOUNCEUSERCOUNTALL) && - (conference_bridge->activeusers > conference_bridge_user->u_profile.announce_user_count_all_after)) { + if (ast_test_flag(&user->u_profile, USER_OPT_ANNOUNCEUSERCOUNTALL) && + (conference->activeusers > user->u_profile.announce_user_count_all_after)) { int user_count_res; /* * We have to autoservice the new user because he has not quite * joined the conference yet. */ - ast_autoservice_start(conference_bridge_user->chan); - user_count_res = announce_user_count(conference_bridge, NULL); - ast_autoservice_stop(conference_bridge_user->chan); + ast_autoservice_start(user->chan); + user_count_res = announce_user_count(conference, NULL); + ast_autoservice_stop(user->chan); if (user_count_res) { - leave_conference(conference_bridge_user); + leave_conference(user); return NULL; } } /* Handle post-join actions */ - while ((action = AST_LIST_REMOVE_HEAD(&conference_bridge_user->post_join_list, list))) { - action->func(conference_bridge_user); + while ((action = AST_LIST_REMOVE_HEAD(&user->post_join_list, list))) { + action->func(user); ast_free(action); } - return conference_bridge; + return conference; } /*! @@ -1298,13 +1389,13 @@ * * \param user The conference user */ -static void leave_conference(struct conference_bridge_user *user) +static void leave_conference(struct confbridge_user *user) { struct post_join_action *action; - ao2_lock(user->conference_bridge); + ao2_lock(user->conference); handle_conf_user_leave(user); - ao2_unlock(user->conference_bridge); + ao2_unlock(user->conference); /* Discard any post-join actions */ while ((action = AST_LIST_REMOVE_HEAD(&user->post_join_list, list))) { @@ -1312,156 +1403,122 @@ } /* Done mucking with the conference, huzzah */ - ao2_ref(user->conference_bridge, -1); - user->conference_bridge = NULL; + ao2_ref(user->conference, -1); + user->conference = NULL; } /*! * \internal - * \brief allocates playback chan on a channel + * \brief Allocate playback channel for a conference. * \pre expects conference to be locked before calling this function */ -static int alloc_playback_chan(struct conference_bridge *conference_bridge) +static int alloc_playback_chan(struct confbridge_conference *conference) { - int cause; struct ast_format_cap *cap; - struct ast_format tmpfmt; - if (conference_bridge->playback_chan) { - return 0; - } - if (!(cap = ast_format_cap_alloc_nolock())) { + cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); + if (!cap) { return -1; } - ast_format_cap_add(cap, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0)); - if (!(conference_bridge->playback_chan = ast_request("Bridge", cap, NULL, "", &cause))) { - cap = ast_format_cap_destroy(cap); + ast_format_cap_append(cap, ast_format_slin, 0); + conference->playback_chan = ast_request("CBAnn", cap, NULL, NULL, + conference->name, NULL); + ao2_ref(cap, -1); + if (!conference->playback_chan) { return -1; } - cap = ast_format_cap_destroy(cap); - - ast_channel_internal_bridge_set(conference_bridge->playback_chan, conference_bridge->bridge); /* To make sure playback_chan has the same language of that profile */ - ast_channel_language_set(conference_bridge->playback_chan, conference_bridge->b_profile.language); - - if (ast_call(conference_bridge->playback_chan, "", 0)) { - ast_hangup(conference_bridge->playback_chan); - conference_bridge->playback_chan = NULL; - return -1; - } + ast_channel_lock(conference->playback_chan); + ast_channel_language_set(conference->playback_chan, conference->b_profile.language); + ast_channel_unlock(conference->playback_chan); - ast_debug(1, "Created a playback channel to conference bridge '%s'\n", conference_bridge->name); + ast_debug(1, "Created announcer channel '%s' to conference bridge '%s'\n", + ast_channel_name(conference->playback_chan), conference->name); return 0; } -static int play_sound_helper(struct conference_bridge *conference_bridge, const char *filename, int say_number) +static int play_sound_helper(struct confbridge_conference *conference, const char *filename, int say_number) { - struct ast_channel *underlying_channel; - /* Do not waste resources trying to play files that do not exist */ if (!ast_strlen_zero(filename) && !sound_file_exists(filename)) { return 0; } - ast_mutex_lock(&conference_bridge->playback_lock); - if (!(conference_bridge->playback_chan)) { - if (alloc_playback_chan(conference_bridge)) { - ast_mutex_unlock(&conference_bridge->playback_lock); - return -1; - } - underlying_channel = ast_channel_tech(conference_bridge->playback_chan)->bridged_channel(conference_bridge->playback_chan, NULL); - } else { - /* Channel was already available so we just need to add it back into the bridge */ - underlying_channel = ast_channel_tech(conference_bridge->playback_chan)->bridged_channel(conference_bridge->playback_chan, NULL); - if (ast_bridge_impart(conference_bridge->bridge, underlying_channel, NULL, NULL, 0)) { - ast_mutex_unlock(&conference_bridge->playback_lock); - return -1; - } + ast_mutex_lock(&conference->playback_lock); + if (!conference->playback_chan && alloc_playback_chan(conference)) { + ast_mutex_unlock(&conference->playback_lock); + return -1; + } + if (conf_announce_channel_push(conference->playback_chan)) { + ast_mutex_unlock(&conference->playback_lock); + return -1; } /* The channel is all under our control, in goes the prompt */ if (!ast_strlen_zero(filename)) { - ast_stream_and_wait(conference_bridge->playback_chan, filename, ""); + ast_stream_and_wait(conference->playback_chan, filename, ""); } else if (say_number >= 0) { - ast_say_number(conference_bridge->playback_chan, say_number, "", ast_channel_language(conference_bridge->playback_chan), NULL); + ast_say_number(conference->playback_chan, say_number, "", + ast_channel_language(conference->playback_chan), NULL); } - ast_debug(1, "Departing underlying channel '%s' from bridge '%p'\n", ast_channel_name(underlying_channel), conference_bridge->bridge); - ast_bridge_depart(conference_bridge->bridge, underlying_channel); + ast_debug(1, "Departing announcer channel '%s' from conference bridge '%s'\n", + ast_channel_name(conference->playback_chan), conference->name); + conf_announce_channel_depart(conference->playback_chan); - ast_mutex_unlock(&conference_bridge->playback_lock); + ast_mutex_unlock(&conference->playback_lock); return 0; } -int play_sound_file(struct conference_bridge *conference_bridge, const char *filename) +int play_sound_file(struct confbridge_conference *conference, const char *filename) { - return play_sound_helper(conference_bridge, filename, -1); + return play_sound_helper(conference, filename, -1); } /*! * \brief Play number into the conference bridge * - * \param conference_bridge The conference bridge to say the number into - * \param number to say + * \param conference The conference bridge to say the number into + * \param say_number number to say * * \retval 0 success * \retval -1 failure */ -static int play_sound_number(struct conference_bridge *conference_bridge, int say_number) +static int play_sound_number(struct confbridge_conference *conference, int say_number) { - return play_sound_helper(conference_bridge, NULL, say_number); + return play_sound_helper(conference, NULL, say_number); } -static void conf_handle_talker_destructor(void *pvt_data) +static int conf_handle_talker_cb(struct ast_bridge_channel *bridge_channel, void *hook_pvt, int talking) { - ast_free(pvt_data); -} + const struct confbridge_user *user = hook_pvt; + RAII_VAR(struct confbridge_conference *, conference, NULL, ao2_cleanup); + struct ast_json *talking_extras; -static void conf_handle_talker_cb(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *pvt_data) -{ - char *conf_name = pvt_data; - int talking; + conference = ao2_find(conference_bridges, user->conference->name, OBJ_KEY); + if (!conference) { + /* Remove the hook since the conference does not exist. */ + return -1; + } - switch (bridge_channel->state) { - case AST_BRIDGE_CHANNEL_STATE_START_TALKING: - talking = 1; - break; - case AST_BRIDGE_CHANNEL_STATE_STOP_TALKING: - talking = 0; - break; - default: - return; /* uhh this shouldn't happen, but bail if it does. */ + talking_extras = ast_json_pack("{s: s, s: b}", + "talking_status", talking ? "on" : "off", + "admin", ast_test_flag(&user->u_profile, USER_OPT_ADMIN)); + if (!talking_extras) { + return 0; } - /* notify AMI someone is has either started or stopped talking */ - /*** DOCUMENTATION - - Raised when a conference participant has started or stopped talking. - - - - - - - - - - - ***/ - ast_manager_event(bridge_channel->chan, EVENT_FLAG_CALL, "ConfbridgeTalking", - "Channel: %s\r\n" - "Uniqueid: %s\r\n" - "Conference: %s\r\n" - "TalkingStatus: %s\r\n", - ast_channel_name(bridge_channel->chan), ast_channel_uniqueid(bridge_channel->chan), conf_name, talking ? "on" : "off"); + send_conf_stasis(conference, bridge_channel->chan, confbridge_talking_type(), talking_extras, 0); + ast_json_unref(talking_extras); + return 0; } -static int conf_get_pin(struct ast_channel *chan, struct conference_bridge_user *conference_bridge_user) +static int conf_get_pin(struct ast_channel *chan, struct confbridge_user *user) { char pin_guess[MAX_PIN+1] = { 0, }; - const char *pin = conference_bridge_user->u_profile.pin; + const char *pin = user->u_profile.pin; char *tmp = pin_guess; int i, res; unsigned int len = MAX_PIN ; @@ -1469,14 +1526,14 @@ /* give them three tries to get the pin right */ for (i = 0; i < 3; i++) { if (ast_app_getdata(chan, - conf_get_sound(CONF_SOUND_GET_PIN, conference_bridge_user->b_profile.sounds), + conf_get_sound(CONF_SOUND_GET_PIN, user->b_profile.sounds), tmp, len, 0) >= 0) { if (!strcasecmp(pin, pin_guess)) { return 0; } } ast_streamfile(chan, - conf_get_sound(CONF_SOUND_INVALID_PIN, conference_bridge_user->b_profile.sounds), + conf_get_sound(CONF_SOUND_INVALID_PIN, user->b_profile.sounds), ast_channel_language(chan)); res = ast_waitstream(chan, AST_DIGIT_ANY); if (res > 0) { @@ -1495,7 +1552,7 @@ return -1; } -static int conf_rec_name(struct conference_bridge_user *user, const char *conf_name) +static int conf_rec_name(struct confbridge_user *user, const char *conf_name) { char destdir[PATH_MAX]; int res; @@ -1511,16 +1568,26 @@ "%s/confbridge-name-%s-%s", destdir, conf_name, ast_channel_uniqueid(user->chan)); - res = ast_play_and_record(user->chan, - "vm-rec-name", - user->name_rec_location, - 10, - "sln", - &duration, - NULL, - ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), - 0, - NULL); + if (!(ast_test_flag(&user->u_profile, USER_OPT_ANNOUNCE_JOIN_LEAVE_REVIEW))) { + res = ast_play_and_record(user->chan, + "vm-rec-name", + user->name_rec_location, + 10, + "sln", + &duration, + NULL, + ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), + 0, + NULL); + } else { + res = ast_record_review(user->chan, + "vm-rec-name", + user->name_rec_location, + 10, + "sln", + &duration, + NULL); + } if (res == -1) { user->name_rec_location[0] = '\0'; @@ -1535,10 +1602,11 @@ int res = 0, volume_adjustments[2]; int quiet = 0; char *parse; - const char *b_profile_name = DEFAULT_BRIDGE_PROFILE; - const char *u_profile_name = DEFAULT_USER_PROFILE; - struct conference_bridge *conference_bridge = NULL; - struct conference_bridge_user conference_bridge_user = { + const char *b_profile_name = NULL; + const char *u_profile_name = NULL; + const char *menu_profile_name = NULL; + struct confbridge_conference *conference = NULL; + struct confbridge_user user = { .chan = chan, .tech_args.talking_threshold = DEFAULT_TALKING_THRESHOLD, .tech_args.silence_threshold = DEFAULT_SILENCE_THRESHOLD, @@ -1548,17 +1616,16 @@ AST_APP_ARG(conf_name); AST_APP_ARG(b_profile_name); AST_APP_ARG(u_profile_name); - AST_APP_ARG(menu_name); + AST_APP_ARG(menu_profile_name); ); - ast_bridge_features_init(&conference_bridge_user.features); if (ast_channel_state(chan) != AST_STATE_UP) { ast_answer(chan); } - if (ast_strlen_zero(data)) { - ast_log(LOG_WARNING, "%s requires an argument (conference name[,options])\n", app); - res = -1; /* invalid PIN */ + if (ast_bridge_features_init(&user.features)) { + pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED"); + res = -1; goto confbridge_cleanup; } @@ -1567,12 +1634,28 @@ AST_STANDARD_APP_ARGS(args, parse); + if (ast_strlen_zero(args.conf_name)) { + pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED"); + ast_log(LOG_WARNING, "%s requires an argument (conference name[,options])\n", app); + res = -1; + goto confbridge_cleanup; + } + + if (strlen(args.conf_name) >= MAX_CONF_NAME) { + pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED"); + ast_log(LOG_WARNING, "%s does not accept conference names longer than %d\n", app, MAX_CONF_NAME - 1); + res = -1; + goto confbridge_cleanup; + } + /* bridge profile name */ if (args.argc > 1 && !ast_strlen_zero(args.b_profile_name)) { b_profile_name = args.b_profile_name; } - if (!conf_find_bridge_profile(chan, b_profile_name, &conference_bridge_user.b_profile)) { - ast_log(LOG_WARNING, "Conference bridge profile %s does not exist\n", b_profile_name); + if (!conf_find_bridge_profile(chan, b_profile_name, &user.b_profile)) { + pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED"); + ast_log(LOG_WARNING, "Conference bridge profile %s does not exist\n", b_profile_name ? + b_profile_name : DEFAULT_BRIDGE_PROFILE); res = -1; goto confbridge_cleanup; } @@ -1581,68 +1664,81 @@ if (args.argc > 2 && !ast_strlen_zero(args.u_profile_name)) { u_profile_name = args.u_profile_name; } - if (!conf_find_user_profile(chan, u_profile_name, &conference_bridge_user.u_profile)) { - ast_log(LOG_WARNING, "Conference user profile %s does not exist\n", u_profile_name); + if (!conf_find_user_profile(chan, u_profile_name, &user.u_profile)) { + pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED"); + ast_log(LOG_WARNING, "Conference user profile %s does not exist\n", u_profile_name ? + u_profile_name : DEFAULT_USER_PROFILE); res = -1; goto confbridge_cleanup; } - quiet = ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_QUIET); + quiet = ast_test_flag(&user.u_profile, USER_OPT_QUIET); /* ask for a PIN immediately after finding user profile. This has to be * prompted for requardless of quiet setting. */ - if (!ast_strlen_zero(conference_bridge_user.u_profile.pin)) { - if (conf_get_pin(chan, &conference_bridge_user)) { + if (!ast_strlen_zero(user.u_profile.pin)) { + if (conf_get_pin(chan, &user)) { + pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED"); res = -1; /* invalid PIN */ goto confbridge_cleanup; } } /* See if we need them to record a intro name */ - if (!quiet && ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_ANNOUNCE_JOIN_LEAVE)) { - conf_rec_name(&conference_bridge_user, args.conf_name); + if (!quiet && + (ast_test_flag(&user.u_profile, USER_OPT_ANNOUNCE_JOIN_LEAVE) || + (ast_test_flag(&user.u_profile, USER_OPT_ANNOUNCE_JOIN_LEAVE_REVIEW)))) { + conf_rec_name(&user, args.conf_name); } /* menu name */ - if (args.argc > 3 && !ast_strlen_zero(args.menu_name)) { - ast_copy_string(conference_bridge_user.menu_name, args.menu_name, sizeof(conference_bridge_user.menu_name)); - if (conf_set_menu_to_user(conference_bridge_user.menu_name, &conference_bridge_user)) { - ast_log(LOG_WARNING, "Conference menu %s does not exist and can not be applied to confbridge user.\n", - args.menu_name); - res = -1; /* invalid PIN */ - goto confbridge_cleanup; - } + if (args.argc > 3 && !ast_strlen_zero(args.menu_profile_name)) { + menu_profile_name = args.menu_profile_name; + } + + if (conf_set_menu_to_user(chan, &user, menu_profile_name)) { + pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED"); + ast_log(LOG_WARNING, "Conference menu profile %s does not exist\n", menu_profile_name ? + menu_profile_name : DEFAULT_MENU_PROFILE); + res = -1; + goto confbridge_cleanup; } /* Set if DTMF should pass through for this user or not */ - if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_DTMF_PASS)) { - conference_bridge_user.features.dtmf_passthrough = 1; + if (ast_test_flag(&user.u_profile, USER_OPT_DTMF_PASS)) { + user.features.dtmf_passthrough = 1; + } else { + user.features.dtmf_passthrough = 0; } /* Set dsp threshold values if present */ - if (conference_bridge_user.u_profile.talking_threshold) { - conference_bridge_user.tech_args.talking_threshold = conference_bridge_user.u_profile.talking_threshold; + if (user.u_profile.talking_threshold) { + user.tech_args.talking_threshold = user.u_profile.talking_threshold; } - if (conference_bridge_user.u_profile.silence_threshold) { - conference_bridge_user.tech_args.silence_threshold = conference_bridge_user.u_profile.silence_threshold; + if (user.u_profile.silence_threshold) { + user.tech_args.silence_threshold = user.u_profile.silence_threshold; } /* Set a talker indicate call back if talking detection is requested */ - if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_TALKER_DETECT)) { - char *conf_name = ast_strdup(args.conf_name); /* this is freed during feature cleanup */ - if (!(conf_name)) { - res = -1; /* invalid PIN */ + if (ast_test_flag(&user.u_profile, USER_OPT_TALKER_DETECT)) { + if (ast_bridge_talk_detector_hook(&user.features, conf_handle_talker_cb, + &user, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) { + pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED"); + res = -1; goto confbridge_cleanup; } - ast_bridge_features_set_talk_detector(&conference_bridge_user.features, - conf_handle_talker_cb, - conf_handle_talker_destructor, - conf_name); + } + + /* If the caller should be joined already muted, set the flag before we join. */ + if (ast_test_flag(&user.u_profile, USER_OPT_STARTMUTED)) { + /* Set user level mute request. */ + user.muted = 1; } /* Look for a conference bridge matching the provided name */ - if (!(conference_bridge = join_conference_bridge(args.conf_name, &conference_bridge_user))) { - res = -1; /* invalid PIN */ + if (!(conference = join_conference_bridge(args.conf_name, &user))) { + pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED"); + res = -1; goto confbridge_cleanup; } @@ -1650,16 +1746,11 @@ volume_adjustments[0] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_READ); volume_adjustments[1] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_WRITE); - /* If the caller should be joined already muted, make it so */ - if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_STARTMUTED)) { - conference_bridge_user.features.mute = 1; - } - - if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_DROP_SILENCE)) { - conference_bridge_user.tech_args.drop_silence = 1; + if (ast_test_flag(&user.u_profile, USER_OPT_DROP_SILENCE)) { + user.tech_args.drop_silence = 1; } - if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_JITTERBUFFER)) { + if (ast_test_flag(&user.u_profile, USER_OPT_JITTERBUFFER)) { char *func_jb; if ((func_jb = ast_module_helper("", "func_jitterbuffer", 0, 0, 0, 0))) { ast_free(func_jb); @@ -1667,7 +1758,7 @@ } } - if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_DENOISE)) { + if (ast_test_flag(&user.u_profile, USER_OPT_DENOISE)) { char *mod_speex; /* Reduce background noise from each participant */ if ((mod_speex = ast_module_helper("", "codec_speex", 0, 0, 0, 0))) { @@ -1677,73 +1768,79 @@ } /* if this user has a intro, play it before entering */ - if (!ast_strlen_zero(conference_bridge_user.name_rec_location)) { + if (!ast_strlen_zero(user.name_rec_location)) { ast_autoservice_start(chan); - play_sound_file(conference_bridge, conference_bridge_user.name_rec_location); - play_sound_file(conference_bridge, - conf_get_sound(CONF_SOUND_HAS_JOINED, conference_bridge_user.b_profile.sounds)); + play_sound_file(conference, user.name_rec_location); + play_sound_file(conference, + conf_get_sound(CONF_SOUND_HAS_JOINED, user.b_profile.sounds)); ast_autoservice_stop(chan); } /* Play the Join sound to both the conference and the user entering. */ if (!quiet) { - const char *join_sound = conf_get_sound(CONF_SOUND_JOIN, conference_bridge_user.b_profile.sounds); + const char *join_sound = conf_get_sound(CONF_SOUND_JOIN, user.b_profile.sounds); ast_stream_and_wait(chan, join_sound, ""); ast_autoservice_start(chan); - play_sound_file(conference_bridge, join_sound); + play_sound_file(conference, join_sound); ast_autoservice_stop(chan); } /* See if we need to automatically set this user as a video source or not */ - handle_video_on_join(conference_bridge, conference_bridge_user.chan, ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_MARKEDUSER)); + handle_video_on_join(conference, user.chan, ast_test_flag(&user.u_profile, USER_OPT_MARKEDUSER)); - conf_moh_unsuspend(&conference_bridge_user); + conf_moh_unsuspend(&user); /* Join our conference bridge for real */ - send_join_event(conference_bridge_user.chan, conference_bridge->name); - ast_bridge_join(conference_bridge->bridge, + send_join_event(&user, conference); + ast_bridge_join(conference->bridge, chan, NULL, - &conference_bridge_user.features, - &conference_bridge_user.tech_args); - send_leave_event(conference_bridge_user.chan, conference_bridge->name); + &user.features, + &user.tech_args, + 0); + + if (!user.kicked && ast_check_hangup(chan)) { + pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "HANGUP"); + } + + send_leave_event(&user, conference); /* if we're shutting down, don't attempt to do further processing */ if (ast_shutting_down()) { - leave_conference(&conference_bridge_user); - conference_bridge = NULL; + leave_conference(&user); + conference = NULL; goto confbridge_cleanup; } /* If this user was a video source, we need to clean up and possibly pick a new source. */ - handle_video_on_exit(conference_bridge, conference_bridge_user.chan); + handle_video_on_exit(conference, user.chan); /* if this user has a intro, play it when leaving */ - if (!quiet && !ast_strlen_zero(conference_bridge_user.name_rec_location)) { + if (!quiet && !ast_strlen_zero(user.name_rec_location)) { ast_autoservice_start(chan); - play_sound_file(conference_bridge, conference_bridge_user.name_rec_location); - play_sound_file(conference_bridge, - conf_get_sound(CONF_SOUND_HAS_LEFT, conference_bridge_user.b_profile.sounds)); + play_sound_file(conference, user.name_rec_location); + play_sound_file(conference, + conf_get_sound(CONF_SOUND_HAS_LEFT, user.b_profile.sounds)); ast_autoservice_stop(chan); } /* play the leave sound */ if (!quiet) { - const char *leave_sound = conf_get_sound(CONF_SOUND_LEAVE, conference_bridge_user.b_profile.sounds); + const char *leave_sound = conf_get_sound(CONF_SOUND_LEAVE, user.b_profile.sounds); ast_autoservice_start(chan); - play_sound_file(conference_bridge, leave_sound); + play_sound_file(conference, leave_sound); ast_autoservice_stop(chan); } /* Easy as pie, depart this channel from the conference bridge */ - leave_conference(&conference_bridge_user); - conference_bridge = NULL; + leave_conference(&user); + conference = NULL; /* If the user was kicked from the conference play back the audio prompt for it */ - if (!quiet && conference_bridge_user.kicked) { + if (!quiet && user.kicked) { res = ast_stream_and_wait(chan, - conf_get_sound(CONF_SOUND_KICKED, conference_bridge_user.b_profile.sounds), + conf_get_sound(CONF_SOUND_KICKED, user.b_profile.sounds), ""); } @@ -1755,58 +1852,78 @@ ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_WRITE, volume_adjustments[1]); } - if (!ast_strlen_zero(conference_bridge_user.name_rec_location)) { - ast_filedelete(conference_bridge_user.name_rec_location, NULL); + if (!ast_strlen_zero(user.name_rec_location)) { + ast_filedelete(user.name_rec_location, NULL); } confbridge_cleanup: - ast_bridge_features_cleanup(&conference_bridge_user.features); - conf_bridge_profile_destroy(&conference_bridge_user.b_profile); + ast_bridge_features_cleanup(&user.features); + conf_bridge_profile_destroy(&user.b_profile); return res; } -static int action_toggle_mute(struct conference_bridge *conference_bridge, - struct conference_bridge_user *conference_bridge_user, - struct ast_channel *chan) -{ - /* Mute or unmute yourself, note we only allow manipulation if they aren't waiting for a marked user or if marked users exist */ - if (!ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_WAITMARKED) || conference_bridge->markedusers) { - conference_bridge_user->features.mute = (!conference_bridge_user->features.mute ? 1 : 0); - ast_test_suite_event_notify("CONF_MUTE", "Message: participant %s %s\r\nConference: %s\r\nChannel: %s", ast_channel_name(chan), conference_bridge_user->features.mute ? "muted" : "unmuted", conference_bridge_user->b_profile.name, ast_channel_name(chan)); - } - return ast_stream_and_wait(chan, (conference_bridge_user->features.mute ? - conf_get_sound(CONF_SOUND_MUTED, conference_bridge_user->b_profile.sounds) : - conf_get_sound(CONF_SOUND_UNMUTED, conference_bridge_user->b_profile.sounds)), +static int action_toggle_mute(struct confbridge_conference *conference, + struct confbridge_user *user) +{ + int mute; + + /* Toggle user level mute request. */ + mute = !user->muted; + user->muted = mute; + + conf_update_user_mute(user); + ast_test_suite_event_notify("CONF_MUTE", + "Message: participant %s %s\r\n" + "Conference: %s\r\n" + "Channel: %s", + ast_channel_name(user->chan), + mute ? "muted" : "unmuted", + user->b_profile.name, + ast_channel_name(user->chan)); + if (mute) { + send_mute_event(user, conference); + } else { + send_unmute_event(user, conference); + } + + return ast_stream_and_wait(user->chan, (mute ? + conf_get_sound(CONF_SOUND_MUTED, user->b_profile.sounds) : + conf_get_sound(CONF_SOUND_UNMUTED, user->b_profile.sounds)), ""); } -static int action_toggle_mute_participants(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user) +static int action_toggle_mute_participants(struct confbridge_conference *conference, struct confbridge_user *user) { - struct conference_bridge_user *participant = NULL; + struct confbridge_user *cur_user = NULL; const char *sound_to_play; + int mute; - ao2_lock(conference_bridge); + ao2_lock(conference); - /* If already muted, then unmute */ - conference_bridge->muted = conference_bridge->muted ? 0 : 1; - sound_to_play = conf_get_sound((conference_bridge->muted ? CONF_SOUND_PARTICIPANTS_MUTED : CONF_SOUND_PARTICIPANTS_UNMUTED), - conference_bridge_user->b_profile.sounds); + /* Toggle bridge level mute request. */ + mute = !conference->muted; + conference->muted = mute; - AST_LIST_TRAVERSE(&conference_bridge->active_list, participant, list) { - if (!ast_test_flag(&participant->u_profile, USER_OPT_ADMIN)) { - participant->features.mute = conference_bridge->muted; + AST_LIST_TRAVERSE(&conference->active_list, cur_user, list) { + if (!ast_test_flag(&cur_user->u_profile, USER_OPT_ADMIN)) { + /* Set user level to bridge level mute request. */ + cur_user->muted = mute; + conf_update_user_mute(cur_user); } } - ao2_unlock(conference_bridge); + ao2_unlock(conference); + + sound_to_play = conf_get_sound((mute ? CONF_SOUND_PARTICIPANTS_MUTED : CONF_SOUND_PARTICIPANTS_UNMUTED), + user->b_profile.sounds); /* The host needs to hear it seperately, as they don't get the audio from play_sound_helper */ - ast_stream_and_wait(conference_bridge_user->chan, sound_to_play, ""); + ast_stream_and_wait(user->chan, sound_to_play, ""); /* Announce to the group that all participants are muted */ - ast_autoservice_start(conference_bridge_user->chan); - play_sound_helper(conference_bridge, sound_to_play, 0); - ast_autoservice_stop(conference_bridge_user->chan); + ast_autoservice_start(user->chan); + play_sound_helper(conference, sound_to_play, 0); + ast_autoservice_stop(user->chan); return 0; } @@ -1825,8 +1942,8 @@ return 0; } -static int action_playback_and_continue(struct conference_bridge *conference_bridge, - struct conference_bridge_user *conference_bridge_user, +static int action_playback_and_continue(struct confbridge_conference *conference, + struct confbridge_user *user, struct ast_bridge_channel *bridge_channel, struct conf_menu *menu, const char *playback_file, @@ -1887,8 +2004,8 @@ } if (conf_find_menu_entry_by_sequence(dtmf, menu, &new_menu_entry)) { - execute_menu_entry(conference_bridge, - conference_bridge_user, + execute_menu_entry(conference, + user, bridge_channel, &new_menu_entry, menu); conf_menu_entry_destroy(&new_menu_entry); @@ -1896,34 +2013,35 @@ return 0; } -static int action_kick_last(struct conference_bridge *conference_bridge, +static int action_kick_last(struct confbridge_conference *conference, struct ast_bridge_channel *bridge_channel, - struct conference_bridge_user *conference_bridge_user) + struct confbridge_user *user) { - struct conference_bridge_user *last_participant = NULL; - int isadmin = ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ADMIN); + struct confbridge_user *last_user = NULL; + int isadmin = ast_test_flag(&user->u_profile, USER_OPT_ADMIN); if (!isadmin) { ast_stream_and_wait(bridge_channel->chan, - conf_get_sound(CONF_SOUND_ERROR_MENU, conference_bridge_user->b_profile.sounds), + conf_get_sound(CONF_SOUND_ERROR_MENU, user->b_profile.sounds), ""); ast_log(LOG_WARNING, "Only admin users can use the kick_last menu action. Channel %s of conf %s is not an admin.\n", ast_channel_name(bridge_channel->chan), - conference_bridge->name); + conference->name); return -1; } - ao2_lock(conference_bridge); - if (((last_participant = AST_LIST_LAST(&conference_bridge->active_list)) == conference_bridge_user) - || (ast_test_flag(&last_participant->u_profile, USER_OPT_ADMIN))) { - ao2_unlock(conference_bridge); + ao2_lock(conference); + if (((last_user = AST_LIST_LAST(&conference->active_list)) == user) + || (ast_test_flag(&last_user->u_profile, USER_OPT_ADMIN))) { + ao2_unlock(conference); ast_stream_and_wait(bridge_channel->chan, - conf_get_sound(CONF_SOUND_ERROR_MENU, conference_bridge_user->b_profile.sounds), + conf_get_sound(CONF_SOUND_ERROR_MENU, user->b_profile.sounds), ""); - } else if (last_participant) { - last_participant->kicked = 1; - ast_bridge_remove(conference_bridge->bridge, last_participant->chan); - ao2_unlock(conference_bridge); + } else if (last_user && !last_user->kicked) { + last_user->kicked = 1; + pbx_builtin_setvar_helper(last_user->chan, "CONFBRIDGE_RESULT", "KICKED"); + ast_bridge_remove(conference->bridge, last_user->chan); + ao2_unlock(conference); } return 0; } @@ -1972,64 +2090,65 @@ return res; } -static int execute_menu_entry(struct conference_bridge *conference_bridge, - struct conference_bridge_user *conference_bridge_user, +static int execute_menu_entry(struct confbridge_conference *conference, + struct confbridge_user *user, struct ast_bridge_channel *bridge_channel, struct conf_menu_entry *menu_entry, struct conf_menu *menu) { struct conf_menu_action *menu_action; - int isadmin = ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ADMIN); + int isadmin = ast_test_flag(&user->u_profile, USER_OPT_ADMIN); int stop_prompts = 0; int res = 0; AST_LIST_TRAVERSE(&menu_entry->actions, menu_action, action) { switch (menu_action->id) { case MENU_ACTION_TOGGLE_MUTE: - res |= action_toggle_mute(conference_bridge, - conference_bridge_user, - bridge_channel->chan); + res |= action_toggle_mute(conference, user); break; case MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS: if (!isadmin) { break; } - action_toggle_mute_participants(conference_bridge, conference_bridge_user); + action_toggle_mute_participants(conference, user); break; case MENU_ACTION_PARTICIPANT_COUNT: - announce_user_count(conference_bridge, conference_bridge_user); + announce_user_count(conference, user); break; case MENU_ACTION_PLAYBACK: if (!stop_prompts) { res |= action_playback(bridge_channel, menu_action->data.playback_file); + ast_test_suite_event_notify("CONF_MENU_PLAYBACK", + "Message: %s\r\nChannel: %s", + menu_action->data.playback_file, ast_channel_name(bridge_channel->chan)); } break; case MENU_ACTION_RESET_LISTENING: - ast_audiohook_volume_set(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, 0); + ast_audiohook_volume_set(user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, 0); break; case MENU_ACTION_RESET_TALKING: - ast_audiohook_volume_set(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_READ, 0); + ast_audiohook_volume_set(user->chan, AST_AUDIOHOOK_DIRECTION_READ, 0); break; case MENU_ACTION_INCREASE_LISTENING: - ast_audiohook_volume_adjust(conference_bridge_user->chan, + ast_audiohook_volume_adjust(user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, 1); break; case MENU_ACTION_DECREASE_LISTENING: - ast_audiohook_volume_adjust(conference_bridge_user->chan, + ast_audiohook_volume_adjust(user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, -1); break; case MENU_ACTION_INCREASE_TALKING: - ast_audiohook_volume_adjust(conference_bridge_user->chan, + ast_audiohook_volume_adjust(user->chan, AST_AUDIOHOOK_DIRECTION_READ, 1); break; case MENU_ACTION_DECREASE_TALKING: - ast_audiohook_volume_adjust(conference_bridge_user->chan, + ast_audiohook_volume_adjust(user->chan, AST_AUDIOHOOK_DIRECTION_READ, -1); break; case MENU_ACTION_PLAYBACK_AND_CONTINUE: if (!(stop_prompts)) { - res |= action_playback_and_continue(conference_bridge, - conference_bridge_user, + res |= action_playback_and_continue(conference, + user, bridge_channel, menu, menu_action->data.playback_file, @@ -2044,31 +2163,35 @@ if (!isadmin) { break; } - conference_bridge->locked = (!conference_bridge->locked ? 1 : 0); + conference->locked = (!conference->locked ? 1 : 0); res |= ast_stream_and_wait(bridge_channel->chan, - (conference_bridge->locked ? - conf_get_sound(CONF_SOUND_LOCKED_NOW, conference_bridge_user->b_profile.sounds) : - conf_get_sound(CONF_SOUND_UNLOCKED_NOW, conference_bridge_user->b_profile.sounds)), + (conference->locked ? + conf_get_sound(CONF_SOUND_LOCKED_NOW, user->b_profile.sounds) : + conf_get_sound(CONF_SOUND_UNLOCKED_NOW, user->b_profile.sounds)), ""); break; case MENU_ACTION_ADMIN_KICK_LAST: - res |= action_kick_last(conference_bridge, bridge_channel, conference_bridge_user); + res |= action_kick_last(conference, bridge_channel, user); break; case MENU_ACTION_LEAVE: - ao2_lock(conference_bridge); - ast_bridge_remove(conference_bridge->bridge, bridge_channel->chan); - ao2_unlock(conference_bridge); + pbx_builtin_setvar_helper(bridge_channel->chan, "CONFBRIDGE_RESULT", "DTMF"); + ao2_lock(conference); + ast_bridge_remove(conference->bridge, bridge_channel->chan); + ast_test_suite_event_notify("CONF_MENU_LEAVE", + "Channel: %s", + ast_channel_name(bridge_channel->chan)); + ao2_unlock(conference); break; case MENU_ACTION_NOOP: break; case MENU_ACTION_SET_SINGLE_VIDEO_SRC: - ao2_lock(conference_bridge); - ast_bridge_set_single_src_video_mode(conference_bridge->bridge, bridge_channel->chan); - ao2_unlock(conference_bridge); + ao2_lock(conference); + ast_bridge_set_single_src_video_mode(conference->bridge, bridge_channel->chan); + ao2_unlock(conference); break; case MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC: - handle_video_on_exit(conference_bridge, bridge_channel->chan); + handle_video_on_exit(conference, bridge_channel->chan); break; } } @@ -2076,117 +2199,143 @@ } int conf_handle_dtmf(struct ast_bridge_channel *bridge_channel, - struct conference_bridge_user *conference_bridge_user, + struct confbridge_user *user, struct conf_menu_entry *menu_entry, struct conf_menu *menu) { /* See if music on hold is playing */ - conf_moh_suspend(conference_bridge_user); + conf_moh_suspend(user); /* execute the list of actions associated with this menu entry */ - execute_menu_entry(conference_bridge_user->conference_bridge, conference_bridge_user, bridge_channel, menu_entry, menu); + execute_menu_entry(user->conference, user, bridge_channel, menu_entry, menu); /* See if music on hold needs to be started back up again */ - conf_moh_unsuspend(conference_bridge_user); + conf_moh_unsuspend(user); return 0; } -static int kick_conference_participant(struct conference_bridge *bridge, const char *channel) +static int kick_conference_participant(struct confbridge_conference *conference, + const char *channel) { - struct conference_bridge_user *participant = NULL; + int res = -1; + int match; + struct confbridge_user *user = NULL; + int all = !strcasecmp("all", channel); + int participants = !strcasecmp("participants", channel); - ao2_lock(bridge); - AST_LIST_TRAVERSE(&bridge->active_list, participant, list) { - if (!strcasecmp(ast_channel_name(participant->chan), channel)) { - participant->kicked = 1; - ast_bridge_remove(bridge->bridge, participant->chan); - ao2_unlock(bridge); - return 0; + SCOPED_AO2LOCK(bridge_lock, conference); + + AST_LIST_TRAVERSE(&conference->active_list, user, list) { + if (user->kicked) { + continue; + } + match = !strcasecmp(channel, ast_channel_name(user->chan)); + if (match || all + || (participants && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN))) { + user->kicked = 1; + pbx_builtin_setvar_helper(user->chan, "CONFBRIDGE_RESULT", "KICKED"); + ast_bridge_remove(conference->bridge, user->chan); + res = 0; + if (match) { + return res; + } } } - AST_LIST_TRAVERSE(&bridge->waiting_list, participant, list) { - if (!strcasecmp(ast_channel_name(participant->chan), channel)) { - participant->kicked = 1; - ast_bridge_remove(bridge->bridge, participant->chan); - ao2_unlock(bridge); - return 0; + AST_LIST_TRAVERSE(&conference->waiting_list, user, list) { + if (user->kicked) { + continue; + } + match = !strcasecmp(channel, ast_channel_name(user->chan)); + if (match || all + || (participants && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN))) { + user->kicked = 1; + pbx_builtin_setvar_helper(user->chan, "CONFBRIDGE_RESULT", "KICKED"); + ast_bridge_remove(conference->bridge, user->chan); + res = 0; + if (match) { + return res; + } } } - ao2_unlock(bridge); - return -1; + return res; } static char *complete_confbridge_name(const char *line, const char *word, int pos, int state) { int which = 0; - struct conference_bridge *bridge = NULL; + struct confbridge_conference *conference; char *res = NULL; int wordlen = strlen(word); - struct ao2_iterator i; + struct ao2_iterator iter; - i = ao2_iterator_init(conference_bridges, 0); - while ((bridge = ao2_iterator_next(&i))) { - if (!strncasecmp(bridge->name, word, wordlen) && ++which > state) { - res = ast_strdup(bridge->name); - ao2_ref(bridge, -1); + iter = ao2_iterator_init(conference_bridges, 0); + while ((conference = ao2_iterator_next(&iter))) { + if (!strncasecmp(conference->name, word, wordlen) && ++which > state) { + res = ast_strdup(conference->name); + ao2_ref(conference, -1); break; } - ao2_ref(bridge, -1); + ao2_ref(conference, -1); } - ao2_iterator_destroy(&i); + ao2_iterator_destroy(&iter); return res; } -static char *complete_confbridge_participant(const char *bridge_name, const char *line, const char *word, int pos, int state) +static char *complete_confbridge_participant(const char *conference_name, const char *line, const char *word, int pos, int state) { int which = 0; - RAII_VAR(struct conference_bridge *, bridge, NULL, ao2_cleanup); - struct conference_bridge tmp; - struct conference_bridge_user *participant; + RAII_VAR(struct confbridge_conference *, conference, NULL, ao2_cleanup); + struct confbridge_user *user; char *res = NULL; int wordlen = strlen(word); - ast_copy_string(tmp.name, bridge_name, sizeof(tmp.name)); - bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER); - if (!bridge) { + conference = ao2_find(conference_bridges, conference_name, OBJ_KEY); + if (!conference) { return NULL; } - ao2_lock(bridge); - AST_LIST_TRAVERSE(&bridge->active_list, participant, list) { - if (!strncasecmp(ast_channel_name(participant->chan), word, wordlen) && ++which > state) { - res = ast_strdup(ast_channel_name(participant->chan)); - ao2_unlock(bridge); - return res; - } + if (!strncasecmp("all", word, wordlen) && ++which > state) { + return ast_strdup("all"); + } + + if (!strncasecmp("participants", word, wordlen) && ++which > state) { + return ast_strdup("participants"); } - AST_LIST_TRAVERSE(&bridge->waiting_list, participant, list) { - if (!strncasecmp(ast_channel_name(participant->chan), word, wordlen) && ++which > state) { - res = ast_strdup(ast_channel_name(participant->chan)); - ao2_unlock(bridge); - return res; + { + SCOPED_AO2LOCK(bridge_lock, conference); + AST_LIST_TRAVERSE(&conference->active_list, user, list) { + if (!strncasecmp(ast_channel_name(user->chan), word, wordlen) && ++which > state) { + res = ast_strdup(ast_channel_name(user->chan)); + return res; + } + } + AST_LIST_TRAVERSE(&conference->waiting_list, user, list) { + if (!strncasecmp(ast_channel_name(user->chan), word, wordlen) && ++which > state) { + res = ast_strdup(ast_channel_name(user->chan)); + return res; + } } } - ao2_unlock(bridge); return NULL; } static char *handle_cli_confbridge_kick(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { - struct conference_bridge *bridge = NULL; - struct conference_bridge tmp; + struct confbridge_conference *conference; + int not_found; switch (cmd) { case CLI_INIT: e->command = "confbridge kick"; e->usage = "Usage: confbridge kick \n" - " Kicks a channel out of the conference bridge.\n"; + " Kicks a channel out of the conference bridge.\n" + " (all to kick everyone, participants to kick non-admins).\n"; return NULL; case CLI_GENERATE: if (a->pos == 2) { @@ -2202,44 +2351,82 @@ return CLI_SHOWUSAGE; } - ast_copy_string(tmp.name, a->argv[2], sizeof(tmp.name)); - bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER); - if (!bridge) { + conference = ao2_find(conference_bridges, a->argv[2], OBJ_KEY); + if (!conference) { ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]); return CLI_SUCCESS; } - if (kick_conference_participant(bridge, a->argv[3])) { - ast_cli(a->fd, "No participant named '%s' found!\n", a->argv[3]); + not_found = kick_conference_participant(conference, a->argv[3]); + ao2_ref(conference, -1); + if (not_found) { + if (!strcasecmp("all", a->argv[3]) || !strcasecmp("participants", a->argv[3])) { + ast_cli(a->fd, "No participants found!\n"); + } else { + ast_cli(a->fd, "No participant named '%s' found!\n", a->argv[3]); + } return CLI_SUCCESS; } - ao2_ref(bridge, -1); - ast_cli(a->fd, "Participant '%s' kicked out of conference '%s'\n", a->argv[3], a->argv[2]); + ast_cli(a->fd, "Kicked '%s' out of conference '%s'\n", a->argv[3], a->argv[2]); return CLI_SUCCESS; } -static void handle_cli_confbridge_list_item(struct ast_cli_args *a, struct conference_bridge_user *participant) +static void handle_cli_confbridge_list_item(struct ast_cli_args *a, struct confbridge_user *user, int waiting) { - ast_cli(a->fd, "%-29s ", ast_channel_name(participant->chan)); - ast_cli(a->fd, "%-17s", participant->u_profile.name); - ast_cli(a->fd, "%-17s", participant->b_profile.name); - ast_cli(a->fd, "%-17s", participant->menu_name); - ast_cli(a->fd, "%-17s", S_COR(ast_channel_caller(participant->chan)->id.number.valid, ast_channel_caller(participant->chan)->id.number.str, "")); - ast_cli(a->fd, "\n"); + char flag_str[6 + 1];/* Max flags + terminator */ + int pos = 0; + + /* Build flags column string. */ + if (ast_test_flag(&user->u_profile, USER_OPT_ADMIN)) { + flag_str[pos++] = 'A'; + } + if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) { + flag_str[pos++] = 'M'; + } + if (ast_test_flag(&user->u_profile, USER_OPT_WAITMARKED)) { + flag_str[pos++] = 'W'; + } + if (ast_test_flag(&user->u_profile, USER_OPT_ENDMARKED)) { + flag_str[pos++] = 'E'; + } + if (user->muted) { + flag_str[pos++] = 'm'; + } + if (waiting) { + flag_str[pos++] = 'w'; + } + flag_str[pos] = '\0'; + + ast_cli(a->fd, "%-30s %-6s %-16s %-16s %-16s %s\n", + ast_channel_name(user->chan), + flag_str, + user->u_profile.name, + user->b_profile.name, + user->menu_name, + S_COR(ast_channel_caller(user->chan)->id.number.valid, + ast_channel_caller(user->chan)->id.number.str, "")); } static char *handle_cli_confbridge_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { - struct ao2_iterator i; - struct conference_bridge *bridge = NULL; - struct conference_bridge tmp; - struct conference_bridge_user *participant = NULL; + struct confbridge_conference *conference; switch (cmd) { case CLI_INIT: e->command = "confbridge list"; e->usage = "Usage: confbridge list []\n" - " Lists all currently active conference bridges.\n"; + " Lists all currently active conference bridges or a specific conference bridge.\n" + "\n" + " When a conference bridge name is provided, flags may be shown for users. Below\n" + " are the flags and what they represent.\n" + "\n" + " Flags:\n" + " A - The user is an admin\n" + " M - The user is a marked user\n" + " W - The user must wait for a marked user to join\n" + " E - The user will be kicked after the last marked user leaves the conference\n" + " m - The user is muted\n" + " w - The user is waiting for a marked user to join\n"; return NULL; case CLI_GENERATE: if (a->pos == 2) { @@ -2249,35 +2436,38 @@ } if (a->argc == 2) { + struct ao2_iterator iter; + ast_cli(a->fd, "Conference Bridge Name Users Marked Locked?\n"); ast_cli(a->fd, "================================ ====== ====== ========\n"); - i = ao2_iterator_init(conference_bridges, 0); - while ((bridge = ao2_iterator_next(&i))) { - ast_cli(a->fd, "%-32s %6i %6i %s\n", bridge->name, bridge->activeusers + bridge->waitingusers, bridge->markedusers, (bridge->locked ? "locked" : "unlocked")); - ao2_ref(bridge, -1); + iter = ao2_iterator_init(conference_bridges, 0); + while ((conference = ao2_iterator_next(&iter))) { + ast_cli(a->fd, "%-32s %6u %6u %s\n", conference->name, conference->activeusers + conference->waitingusers, conference->markedusers, (conference->locked ? "locked" : "unlocked")); + ao2_ref(conference, -1); } - ao2_iterator_destroy(&i); + ao2_iterator_destroy(&iter); return CLI_SUCCESS; } if (a->argc == 3) { - ast_copy_string(tmp.name, a->argv[2], sizeof(tmp.name)); - bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER); - if (!bridge) { + struct confbridge_user *user; + + conference = ao2_find(conference_bridges, a->argv[2], OBJ_KEY); + if (!conference) { ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]); return CLI_SUCCESS; } - ast_cli(a->fd, "Channel User Profile Bridge Profile Menu CallerID\n"); - ast_cli(a->fd, "============================= ================ ================ ================ ================\n"); - ao2_lock(bridge); - AST_LIST_TRAVERSE(&bridge->active_list, participant, list) { - handle_cli_confbridge_list_item(a, participant); + ast_cli(a->fd, "Channel Flags User Profile Bridge Profile Menu CallerID\n"); + ast_cli(a->fd, "============================== ====== ================ ================ ================ ================\n"); + ao2_lock(conference); + AST_LIST_TRAVERSE(&conference->active_list, user, list) { + handle_cli_confbridge_list_item(a, user, 0); } - AST_LIST_TRAVERSE(&bridge->waiting_list, participant, list) { - handle_cli_confbridge_list_item(a, participant); + AST_LIST_TRAVERSE(&conference->waiting_list, user, list) { + handle_cli_confbridge_list_item(a, user, 1); } - ao2_unlock(bridge); - ao2_ref(bridge, -1); + ao2_unlock(conference); + ao2_ref(conference, -1); return CLI_SUCCESS; } @@ -2290,58 +2480,97 @@ * \retval 0 success * \retval -1 conference not found */ -static int generic_lock_unlock_helper(int lock, const char *conference) +static int generic_lock_unlock_helper(int lock, const char *conference_name) { - struct conference_bridge *bridge = NULL; - struct conference_bridge tmp; + struct confbridge_conference *conference; int res = 0; - ast_copy_string(tmp.name, conference, sizeof(tmp.name)); - bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER); - if (!bridge) { + conference = ao2_find(conference_bridges, conference_name, OBJ_KEY); + if (!conference) { return -1; } - ao2_lock(bridge); - bridge->locked = lock; - ast_test_suite_event_notify("CONF_LOCK", "Message: conference %s\r\nConference: %s", bridge->locked ? "locked" : "unlocked", bridge->b_profile.name); - ao2_unlock(bridge); - ao2_ref(bridge, -1); + ao2_lock(conference); + conference->locked = lock; + ast_test_suite_event_notify("CONF_LOCK", "Message: conference %s\r\nConference: %s", conference->locked ? "locked" : "unlocked", conference->b_profile.name); + ao2_unlock(conference); + ao2_ref(conference, -1); return res; } /* \internal + * \brief Mute/unmute a single user. + */ +static void generic_mute_unmute_user(struct confbridge_conference *conference, struct confbridge_user *user, int mute) +{ + /* Set user level mute request. */ + user->muted = mute ? 1 : 0; + + conf_update_user_mute(user); + ast_test_suite_event_notify("CONF_MUTE", + "Message: participant %s %s\r\n" + "Conference: %s\r\n" + "Channel: %s", + ast_channel_name(user->chan), + mute ? "muted" : "unmuted", + conference->b_profile.name, + ast_channel_name(user->chan)); + if (mute) { + send_mute_event(user, conference); + } else { + send_unmute_event(user, conference); + } +} + +/* \internal * \brief finds a conference user by channel name and mutes/unmutes them. * * \retval 0 success * \retval -1 conference not found * \retval -2 user not found */ -static int generic_mute_unmute_helper(int mute, const char *conference, const char *user) +static int generic_mute_unmute_helper(int mute, const char *conference_name, + const char *chan_name) { - struct conference_bridge *bridge = NULL; - struct conference_bridge tmp; - struct conference_bridge_user *participant = NULL; - int res = 0; - ast_copy_string(tmp.name, conference, sizeof(tmp.name)); - bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER); - if (!bridge) { + RAII_VAR(struct confbridge_conference *, conference, NULL, ao2_cleanup); + struct confbridge_user *user; + int all = !strcasecmp("all", chan_name); + int participants = !strcasecmp("participants", chan_name); + int res = -2; + + conference = ao2_find(conference_bridges, conference_name, OBJ_KEY); + if (!conference) { return -1; } - ao2_lock(bridge); - AST_LIST_TRAVERSE(&bridge->active_list, participant, list) { - if (!strncmp(user, ast_channel_name(participant->chan), strlen(user))) { - break; + + { + SCOPED_AO2LOCK(bridge_lock, conference); + AST_LIST_TRAVERSE(&conference->active_list, user, list) { + int match = !strncasecmp(chan_name, ast_channel_name(user->chan), + strlen(chan_name)); + if (match || all + || (participants && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN))) { + generic_mute_unmute_user(conference, user, mute); + res = 0; + if (match) { + return res; + } + } + } + + AST_LIST_TRAVERSE(&conference->waiting_list, user, list) { + int match = !strncasecmp(chan_name, ast_channel_name(user->chan), + strlen(chan_name)); + if (match || all + || (participants && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN))) { + generic_mute_unmute_user(conference, user, mute); + res = 0; + if (match) { + return res; + } + } } } - if (participant) { - participant->features.mute = mute; - ast_test_suite_event_notify("CONF_MUTE", "Message: participant %s %s\r\nConference: %s\r\nChannel: %s", ast_channel_name(participant->chan), participant->features.mute ? "muted" : "unmuted", bridge->b_profile.name, ast_channel_name(participant->chan)); - } else { - res = -2;; - } - ao2_unlock(bridge); - ao2_ref(bridge, -1); return res; } @@ -2354,7 +2583,11 @@ ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]); return -1; } else if (res == -2) { - ast_cli(a->fd, "No channel named '%s' found in conference %s\n", a->argv[3], a->argv[2]); + if (!strcasecmp("all", a->argv[3]) || !strcasecmp("participants", a->argv[3])) { + ast_cli(a->fd, "No participants found in conference %s\n", a->argv[2]); + } else { + ast_cli(a->fd, "No channel named '%s' found in conference %s\n", a->argv[3], a->argv[2]); + } return -1; } ast_cli(a->fd, "%s %s from confbridge %s\n", mute ? "Muting" : "Unmuting", a->argv[3], a->argv[2]); @@ -2368,7 +2601,11 @@ e->command = "confbridge mute"; e->usage = "Usage: confbridge mute \n" - " Mute a channel in a conference.\n"; + " Mute a channel in a conference.\n" + " (all to mute everyone, participants to mute non-admins)\n" + " If the specified channel is a prefix,\n" + " the action will be taken on the first\n" + " matching channel.\n"; return NULL; case CLI_GENERATE: if (a->pos == 2) { @@ -2395,7 +2632,11 @@ e->command = "confbridge unmute"; e->usage = "Usage: confbridge unmute \n" - " Unmute a channel in a conference.\n"; + " Unmute a channel in a conference.\n" + " (all to unmute everyone, participants to unmute non-admins)\n" + " If the specified channel is a prefix,\n" + " the action will be taken on the first\n" + " matching channel.\n"; return NULL; case CLI_GENERATE: if (a->pos == 2) { @@ -2471,8 +2712,7 @@ static char *handle_cli_confbridge_start_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { const char *rec_file = NULL; - struct conference_bridge *bridge = NULL; - struct conference_bridge tmp; + struct confbridge_conference *conference; switch (cmd) { case CLI_INIT: @@ -2497,40 +2737,38 @@ rec_file = a->argv[4]; } - ast_copy_string(tmp.name, a->argv[3], sizeof(tmp.name)); - bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER); - if (!bridge) { + conference = ao2_find(conference_bridges, a->argv[3], OBJ_KEY); + if (!conference) { ast_cli(a->fd, "Conference not found.\n"); return CLI_FAILURE; } - ao2_lock(bridge); - if (conf_is_recording(bridge)) { + ao2_lock(conference); + if (conf_is_recording(conference)) { ast_cli(a->fd, "Conference is already being recorded.\n"); - ao2_unlock(bridge); - ao2_ref(bridge, -1); + ao2_unlock(conference); + ao2_ref(conference, -1); return CLI_SUCCESS; } if (!ast_strlen_zero(rec_file)) { - ast_copy_string(bridge->b_profile.rec_file, rec_file, sizeof(bridge->b_profile.rec_file)); + ast_copy_string(conference->b_profile.rec_file, rec_file, sizeof(conference->b_profile.rec_file)); } - if (start_conf_record_thread(bridge)) { + if (start_conf_record_thread(conference)) { ast_cli(a->fd, "Could not start recording due to internal error.\n"); - ao2_unlock(bridge); - ao2_ref(bridge, -1); + ao2_unlock(conference); + ao2_ref(conference, -1); return CLI_FAILURE; } - ao2_unlock(bridge); + ao2_unlock(conference); ast_cli(a->fd, "Recording started\n"); - ao2_ref(bridge, -1); + ao2_ref(conference, -1); return CLI_SUCCESS; } static char *handle_cli_confbridge_stop_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { - struct conference_bridge *bridge = NULL; - struct conference_bridge tmp; + struct confbridge_conference *conference; int ret; switch (cmd) { @@ -2550,25 +2788,24 @@ return CLI_SHOWUSAGE; } - ast_copy_string(tmp.name, a->argv[3], sizeof(tmp.name)); - bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER); - if (!bridge) { + conference = ao2_find(conference_bridges, a->argv[3], OBJ_KEY); + if (!conference) { ast_cli(a->fd, "Conference not found.\n"); return CLI_SUCCESS; } - ao2_lock(bridge); - ret = conf_stop_record(bridge); - ao2_unlock(bridge); + ao2_lock(conference); + ret = conf_stop_record(conference); + ao2_unlock(conference); ast_cli(a->fd, "Recording %sstopped.\n", ret ? "could not be " : ""); - ao2_ref(bridge, -1); + ao2_ref(conference, -1); return CLI_SUCCESS; } static struct ast_cli_entry cli_confbridge[] = { AST_CLI_DEFINE(handle_cli_confbridge_list, "List conference bridges and participants."), AST_CLI_DEFINE(handle_cli_confbridge_kick, "Kick participants out of conference bridges."), - AST_CLI_DEFINE(handle_cli_confbridge_mute, "Mute a participant."), - AST_CLI_DEFINE(handle_cli_confbridge_unmute, "Unmute a participant."), + AST_CLI_DEFINE(handle_cli_confbridge_mute, "Mute participants."), + AST_CLI_DEFINE(handle_cli_confbridge_unmute, "Unmute participants."), AST_CLI_DEFINE(handle_cli_confbridge_lock, "Lock a conference."), AST_CLI_DEFINE(handle_cli_confbridge_unlock, "Unlock a conference."), AST_CLI_DEFINE(handle_cli_confbridge_start_record, "Start recording a conference"), @@ -2585,7 +2822,7 @@ .read = func_confbridge_info, }; -static void action_confbridgelist_item(struct mansession *s, const char *id_text, struct conference_bridge *bridge, struct conference_bridge_user *participant) +static void action_confbridgelist_item(struct mansession *s, const char *id_text, struct confbridge_conference *conference, struct confbridge_user *user, int waiting) { astman_append(s, "Event: ConfbridgeList\r\n" @@ -2596,30 +2833,40 @@ "Channel: %s\r\n" "Admin: %s\r\n" "MarkedUser: %s\r\n" + "WaitMarked: %s\r\n" + "EndMarked: %s\r\n" + "Waiting: %s\r\n" + "Muted: %s\r\n" + "AnsweredTime: %d\r\n" "\r\n", id_text, - bridge->name, - S_COR(ast_channel_caller(participant->chan)->id.number.valid, ast_channel_caller(participant->chan)->id.number.str, ""), - S_COR(ast_channel_caller(participant->chan)->id.name.valid, ast_channel_caller(participant->chan)->id.name.str, ""), - ast_channel_name(participant->chan), - ast_test_flag(&participant->u_profile, USER_OPT_ADMIN) ? "Yes" : "No", - ast_test_flag(&participant->u_profile, USER_OPT_MARKEDUSER) ? "Yes" : "No"); + conference->name, + S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, ""), + S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, ""), + ast_channel_name(user->chan), + ast_test_flag(&user->u_profile, USER_OPT_ADMIN) ? "Yes" : "No", + ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER) ? "Yes" : "No", + ast_test_flag(&user->u_profile, USER_OPT_WAITMARKED) ? "Yes" : "No", + ast_test_flag(&user->u_profile, USER_OPT_ENDMARKED) ? "Yes" : "No", + waiting ? "Yes" : "No", + user->muted ? "Yes" : "No", + ast_channel_get_up_time(user->chan)); } static int action_confbridgelist(struct mansession *s, const struct message *m) { const char *actionid = astman_get_header(m, "ActionID"); - const char *conference = astman_get_header(m, "Conference"); - struct conference_bridge_user *participant = NULL; - struct conference_bridge *bridge = NULL; - struct conference_bridge tmp; - char id_text[80] = ""; + const char *conference_name = astman_get_header(m, "Conference"); + struct confbridge_user *user; + struct confbridge_conference *conference; + char id_text[80]; int total = 0; + id_text[0] = '\0'; if (!ast_strlen_zero(actionid)) { snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid); } - if (ast_strlen_zero(conference)) { + if (ast_strlen_zero(conference_name)) { astman_send_error(s, m, "No Conference name provided."); return 0; } @@ -2627,26 +2874,25 @@ astman_send_error(s, m, "No active conferences."); return 0; } - ast_copy_string(tmp.name, conference, sizeof(tmp.name)); - bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER); - if (!bridge) { + conference = ao2_find(conference_bridges, conference_name, OBJ_KEY); + if (!conference) { astman_send_error(s, m, "No Conference by that name found."); return 0; } astman_send_listack(s, m, "Confbridge user list will follow", "start"); - ao2_lock(bridge); - AST_LIST_TRAVERSE(&bridge->active_list, participant, list) { + ao2_lock(conference); + AST_LIST_TRAVERSE(&conference->active_list, user, list) { total++; - action_confbridgelist_item(s, id_text, bridge, participant); + action_confbridgelist_item(s, id_text, conference, user, 0); } - AST_LIST_TRAVERSE(&bridge->waiting_list, participant, list) { + AST_LIST_TRAVERSE(&conference->waiting_list, user, list) { total++; - action_confbridgelist_item(s, id_text, bridge, participant); + action_confbridgelist_item(s, id_text, conference, user, 1); } - ao2_unlock(bridge); - ao2_ref(bridge, -1); + ao2_unlock(conference); + ao2_ref(conference, -1); astman_append(s, "Event: ConfbridgeListComplete\r\n" @@ -2661,8 +2907,8 @@ static int action_confbridgelistrooms(struct mansession *s, const struct message *m) { const char *actionid = astman_get_header(m, "ActionID"); - struct conference_bridge *bridge = NULL; - struct ao2_iterator i; + struct confbridge_conference *conference; + struct ao2_iterator iter; char id_text[512] = ""; int totalitems = 0; @@ -2678,29 +2924,29 @@ astman_send_listack(s, m, "Confbridge conferences will follow", "start"); /* Traverse the conference list */ - i = ao2_iterator_init(conference_bridges, 0); - while ((bridge = ao2_iterator_next(&i))) { + iter = ao2_iterator_init(conference_bridges, 0); + while ((conference = ao2_iterator_next(&iter))) { totalitems++; - ao2_lock(bridge); + ao2_lock(conference); astman_append(s, "Event: ConfbridgeListRooms\r\n" "%s" "Conference: %s\r\n" - "Parties: %d\r\n" - "Marked: %d\r\n" + "Parties: %u\r\n" + "Marked: %u\r\n" "Locked: %s\r\n" "\r\n", id_text, - bridge->name, - bridge->activeusers + bridge->waitingusers, - bridge->markedusers, - bridge->locked ? "Yes" : "No"); - ao2_unlock(bridge); + conference->name, + conference->activeusers + conference->waitingusers, + conference->markedusers, + conference->locked ? "Yes" : "No"); + ao2_unlock(conference); - ao2_ref(bridge, -1); + ao2_ref(conference, -1); } - ao2_iterator_destroy(&i); + ao2_iterator_destroy(&iter); /* Send final confirmation */ astman_append(s, @@ -2714,15 +2960,15 @@ static int action_mute_unmute_helper(struct mansession *s, const struct message *m, int mute) { - const char *conference = astman_get_header(m, "Conference"); - const char *channel = astman_get_header(m, "Channel"); + const char *conference_name = astman_get_header(m, "Conference"); + const char *channel_name = astman_get_header(m, "Channel"); int res = 0; - if (ast_strlen_zero(conference)) { + if (ast_strlen_zero(conference_name)) { astman_send_error(s, m, "No Conference name provided."); return 0; } - if (ast_strlen_zero(channel)) { + if (ast_strlen_zero(channel_name)) { astman_send_error(s, m, "No channel name provided."); return 0; } @@ -2731,7 +2977,7 @@ return 0; } - res = generic_mute_unmute_helper(mute, conference, channel); + res = generic_mute_unmute_helper(mute, conference_name, channel_name); if (res == -1) { astman_send_error(s, m, "No Conference by that name found."); @@ -2756,10 +3002,10 @@ static int action_lock_unlock_helper(struct mansession *s, const struct message *m, int lock) { - const char *conference = astman_get_header(m, "Conference"); + const char *conference_name = astman_get_header(m, "Conference"); int res = 0; - if (ast_strlen_zero(conference)) { + if (ast_strlen_zero(conference_name)) { astman_send_error(s, m, "No Conference name provided."); return 0; } @@ -2767,7 +3013,7 @@ astman_send_error(s, m, "No active conferences."); return 0; } - if ((res = generic_lock_unlock_helper(lock, conference))) { + if ((res = generic_lock_unlock_helper(lock, conference_name))) { astman_send_error(s, m, "No Conference by that name found."); return 0; } @@ -2785,13 +3031,12 @@ static int action_confbridgekick(struct mansession *s, const struct message *m) { - const char *conference = astman_get_header(m, "Conference"); + const char *conference_name = astman_get_header(m, "Conference"); const char *channel = astman_get_header(m, "Channel"); - struct conference_bridge *bridge = NULL; - struct conference_bridge tmp; - int found = 0; + struct confbridge_conference *conference; + int found; - if (ast_strlen_zero(conference)) { + if (ast_strlen_zero(conference_name)) { astman_send_error(s, m, "No Conference name provided."); return 0; } @@ -2800,18 +3045,17 @@ return 0; } - ast_copy_string(tmp.name, conference, sizeof(tmp.name)); - bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER); - if (!bridge) { + conference = ao2_find(conference_bridges, conference_name, OBJ_KEY); + if (!conference) { astman_send_error(s, m, "No Conference by that name found."); return 0; } - found = !kick_conference_participant(bridge, channel); - ao2_ref(bridge, -1); + found = !kick_conference_participant(conference, channel); + ao2_ref(conference, -1); if (found) { - astman_send_ack(s, m, "User kicked"); + astman_send_ack(s, m, !strcmp("all", channel) ? "All participants kicked" : "User kicked"); } else { astman_send_error(s, m, "No Channel by that name found in Conference."); } @@ -2820,12 +3064,11 @@ static int action_confbridgestartrecord(struct mansession *s, const struct message *m) { - const char *conference = astman_get_header(m, "Conference"); + const char *conference_name = astman_get_header(m, "Conference"); const char *recordfile = astman_get_header(m, "RecordFile"); - struct conference_bridge *bridge = NULL; - struct conference_bridge tmp; + struct confbridge_conference *conference; - if (ast_strlen_zero(conference)) { + if (ast_strlen_zero(conference_name)) { astman_send_error(s, m, "No Conference name provided."); return 0; } @@ -2834,44 +3077,42 @@ return 0; } - ast_copy_string(tmp.name, conference, sizeof(tmp.name)); - bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER); - if (!bridge) { + conference = ao2_find(conference_bridges, conference_name, OBJ_KEY); + if (!conference) { astman_send_error(s, m, "No Conference by that name found."); return 0; } - ao2_lock(bridge); - if (conf_is_recording(bridge)) { + ao2_lock(conference); + if (conf_is_recording(conference)) { astman_send_error(s, m, "Conference is already being recorded."); - ao2_unlock(bridge); - ao2_ref(bridge, -1); + ao2_unlock(conference); + ao2_ref(conference, -1); return 0; } if (!ast_strlen_zero(recordfile)) { - ast_copy_string(bridge->b_profile.rec_file, recordfile, sizeof(bridge->b_profile.rec_file)); + ast_copy_string(conference->b_profile.rec_file, recordfile, sizeof(conference->b_profile.rec_file)); } - if (start_conf_record_thread(bridge)) { + if (start_conf_record_thread(conference)) { astman_send_error(s, m, "Internal error starting conference recording."); - ao2_unlock(bridge); - ao2_ref(bridge, -1); + ao2_unlock(conference); + ao2_ref(conference, -1); return 0; } - ao2_unlock(bridge); + ao2_unlock(conference); - ao2_ref(bridge, -1); + ao2_ref(conference, -1); astman_send_ack(s, m, "Conference Recording Started."); return 0; } static int action_confbridgestoprecord(struct mansession *s, const struct message *m) { - const char *conference = astman_get_header(m, "Conference"); - struct conference_bridge *bridge = NULL; - struct conference_bridge tmp; + const char *conference_name = astman_get_header(m, "Conference"); + struct confbridge_conference *conference; - if (ast_strlen_zero(conference)) { + if (ast_strlen_zero(conference_name)) { astman_send_error(s, m, "No Conference name provided."); return 0; } @@ -2880,36 +3121,34 @@ return 0; } - ast_copy_string(tmp.name, conference, sizeof(tmp.name)); - bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER); - if (!bridge) { + conference = ao2_find(conference_bridges, conference_name, OBJ_KEY); + if (!conference) { astman_send_error(s, m, "No Conference by that name found."); return 0; } - ao2_lock(bridge); - if (conf_stop_record(bridge)) { - ao2_unlock(bridge); + ao2_lock(conference); + if (conf_stop_record(conference)) { + ao2_unlock(conference); astman_send_error(s, m, "Internal error while stopping recording."); - ao2_ref(bridge, -1); + ao2_ref(conference, -1); return 0; } - ao2_unlock(bridge); + ao2_unlock(conference); - ao2_ref(bridge, -1); + ao2_ref(conference, -1); astman_send_ack(s, m, "Conference Recording Stopped."); return 0; } static int action_confbridgesetsinglevideosrc(struct mansession *s, const struct message *m) { - const char *conference = astman_get_header(m, "Conference"); + const char *conference_name = astman_get_header(m, "Conference"); const char *channel = astman_get_header(m, "Channel"); - struct conference_bridge_user *participant = NULL; - struct conference_bridge *bridge = NULL; - struct conference_bridge tmp; + struct confbridge_user *user; + struct confbridge_conference *conference; - if (ast_strlen_zero(conference)) { + if (ast_strlen_zero(conference_name)) { astman_send_error(s, m, "No Conference name provided."); return 0; } @@ -2922,27 +3161,26 @@ return 0; } - ast_copy_string(tmp.name, conference, sizeof(tmp.name)); - bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER); - if (!bridge) { + conference = ao2_find(conference_bridges, conference_name, OBJ_KEY); + if (!conference) { astman_send_error(s, m, "No Conference by that name found."); return 0; } /* find channel and set as video src. */ - ao2_lock(bridge); - AST_LIST_TRAVERSE(&bridge->active_list, participant, list) { - if (!strncmp(channel, ast_channel_name(participant->chan), strlen(channel))) { - ast_bridge_set_single_src_video_mode(bridge->bridge, participant->chan); + ao2_lock(conference); + AST_LIST_TRAVERSE(&conference->active_list, user, list) { + if (!strncmp(channel, ast_channel_name(user->chan), strlen(channel))) { + ast_bridge_set_single_src_video_mode(conference->bridge, user->chan); break; } } - ao2_unlock(bridge); - ao2_ref(bridge, -1); + ao2_unlock(conference); + ao2_ref(conference, -1); - /* do not access participant after bridge unlock. We are just + /* do not access user after conference unlock. We are just * using this check to see if it was found or not */ - if (!participant) { + if (!user) { astman_send_error(s, m, "No channel by that name found in conference."); return 0; } @@ -2952,10 +3190,9 @@ static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) { - char *parse = NULL; - struct conference_bridge *bridge = NULL; - struct conference_bridge_user *participant = NULL; - struct conference_bridge tmp; + char *parse; + struct confbridge_conference *conference; + struct confbridge_user *user; int count = 0; AST_DECLARE_APP_ARGS(args, AST_APP_ARG(type); @@ -2971,132 +3208,180 @@ if (ast_strlen_zero(args.confno) || ast_strlen_zero(args.type)) { return -1; } - if (!ao2_container_count(conference_bridges)) { - snprintf(buf, len, "0"); - return 0; - } - ast_copy_string(tmp.name, args.confno, sizeof(tmp.name)); - bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER); - if (!bridge) { + conference = ao2_find(conference_bridges, args.confno, OBJ_KEY); + if (!conference) { snprintf(buf, len, "0"); return 0; } /* get the correct count for the type requested */ - ao2_lock(bridge); + ao2_lock(conference); if (!strncasecmp(args.type, "parties", 7)) { - AST_LIST_TRAVERSE(&bridge->active_list, participant, list) { + AST_LIST_TRAVERSE(&conference->active_list, user, list) { count++; } - AST_LIST_TRAVERSE(&bridge->waiting_list, participant, list) { + AST_LIST_TRAVERSE(&conference->waiting_list, user, list) { count++; } } else if (!strncasecmp(args.type, "admins", 6)) { - AST_LIST_TRAVERSE(&bridge->active_list, participant, list) { - if (ast_test_flag(&participant->u_profile, USER_OPT_ADMIN)) { + AST_LIST_TRAVERSE(&conference->active_list, user, list) { + if (ast_test_flag(&user->u_profile, USER_OPT_ADMIN)) { count++; } } } else if (!strncasecmp(args.type, "marked", 6)) { - AST_LIST_TRAVERSE(&bridge->active_list, participant, list) { - if (ast_test_flag(&participant->u_profile, USER_OPT_MARKEDUSER)) { + AST_LIST_TRAVERSE(&conference->active_list, user, list) { + if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) { count++; } } } else if (!strncasecmp(args.type, "locked", 6)) { - count = bridge->locked; + count = conference->locked; } else { ast_log(LOG_ERROR, "Invalid keyword '%s' passed to CONFBRIDGE_INFO. Should be one of: " "parties, admins, marked, or locked.\n", args.type); } snprintf(buf, len, "%d", count); - ao2_unlock(bridge); - ao2_ref(bridge, -1); + ao2_unlock(conference); + ao2_ref(conference, -1); return 0; } -void conf_add_user_active(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu) +void conf_add_user_active(struct confbridge_conference *conference, struct confbridge_user *user) { - AST_LIST_INSERT_TAIL(&conference_bridge->active_list, cbu, list); - conference_bridge->activeusers++; + AST_LIST_INSERT_TAIL(&conference->active_list, user, list); + conference->activeusers++; } -void conf_add_user_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu) +void conf_add_user_marked(struct confbridge_conference *conference, struct confbridge_user *user) { - AST_LIST_INSERT_TAIL(&conference_bridge->active_list, cbu, list); - conference_bridge->activeusers++; - conference_bridge->markedusers++; + AST_LIST_INSERT_TAIL(&conference->active_list, user, list); + conference->activeusers++; + conference->markedusers++; } -void conf_add_user_waiting(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu) +void conf_add_user_waiting(struct confbridge_conference *conference, struct confbridge_user *user) { - AST_LIST_INSERT_TAIL(&conference_bridge->waiting_list, cbu, list); - conference_bridge->waitingusers++; + AST_LIST_INSERT_TAIL(&conference->waiting_list, user, list); + conference->waitingusers++; } -void conf_remove_user_active(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu) +void conf_remove_user_active(struct confbridge_conference *conference, struct confbridge_user *user) { - AST_LIST_REMOVE(&conference_bridge->active_list, cbu, list); - conference_bridge->activeusers--; + AST_LIST_REMOVE(&conference->active_list, user, list); + conference->activeusers--; } -void conf_remove_user_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu) +void conf_remove_user_marked(struct confbridge_conference *conference, struct confbridge_user *user) { - AST_LIST_REMOVE(&conference_bridge->active_list, cbu, list); - conference_bridge->activeusers--; - conference_bridge->markedusers--; + AST_LIST_REMOVE(&conference->active_list, user, list); + conference->activeusers--; + conference->markedusers--; } -void conf_mute_only_active(struct conference_bridge *conference_bridge) +void conf_mute_only_active(struct confbridge_conference *conference) { - struct conference_bridge_user *only_participant = AST_LIST_FIRST(&conference_bridge->active_list); + struct confbridge_user *only_user = AST_LIST_FIRST(&conference->active_list); - /* Turn on MOH/mute if the single participant is set up for it */ - if (ast_test_flag(&only_participant->u_profile, USER_OPT_MUSICONHOLD)) { - only_participant->features.mute = 1; - conf_moh_start(only_participant); + /* Turn on MOH if the single participant is set up for it */ + if (ast_test_flag(&only_user->u_profile, USER_OPT_MUSICONHOLD)) { + conf_moh_start(only_user); } + conf_update_user_mute(only_user); } -void conf_remove_user_waiting(struct conference_bridge *conference_bridge, struct conference_bridge_user *cbu) +void conf_remove_user_waiting(struct confbridge_conference *conference, struct confbridge_user *user) { - AST_LIST_REMOVE(&conference_bridge->waiting_list, cbu, list); - conference_bridge->waitingusers--; + AST_LIST_REMOVE(&conference->waiting_list, user, list); + conference->waitingusers--; +} + +/*! + * \internal + * \brief Unregister a ConfBridge channel technology. + * \since 12.0.0 + * + * \param tech What to unregister. + * + * \return Nothing + */ +static void unregister_channel_tech(struct ast_channel_tech *tech) +{ + ast_channel_unregister(tech); + ao2_cleanup(tech->capabilities); +} + +/*! + * \internal + * \brief Register a ConfBridge channel technology. + * \since 12.0.0 + * + * \param tech What to register. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int register_channel_tech(struct ast_channel_tech *tech) +{ + tech->capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); + if (!tech->capabilities) { + return -1; + } + ast_format_cap_append_by_type(tech->capabilities, AST_MEDIA_TYPE_UNKNOWN); + if (ast_channel_register(tech)) { + ast_log(LOG_ERROR, "Unable to register channel technology %s(%s).\n", + tech->type, tech->description); + return -1; + } + return 0; } /*! \brief Called when module is being unloaded */ static int unload_module(void) { - int res = ast_unregister_application(app); + ast_unregister_application(app); ast_custom_function_unregister(&confbridge_function); ast_custom_function_unregister(&confbridge_info_function); - ast_cli_unregister_multiple(cli_confbridge, sizeof(cli_confbridge) / sizeof(struct ast_cli_entry)); + ast_cli_unregister_multiple(cli_confbridge, ARRAY_LEN(cli_confbridge)); + + ast_manager_unregister("ConfbridgeList"); + ast_manager_unregister("ConfbridgeListRooms"); + ast_manager_unregister("ConfbridgeMute"); + ast_manager_unregister("ConfbridgeUnmute"); + ast_manager_unregister("ConfbridgeKick"); + ast_manager_unregister("ConfbridgeUnlock"); + ast_manager_unregister("ConfbridgeLock"); + ast_manager_unregister("ConfbridgeStartRecord"); + ast_manager_unregister("ConfbridgeStopRecord"); + ast_manager_unregister("ConfbridgeSetSingleVideoSrc"); + + /* Unsubscribe from stasis confbridge message type and clean it up. */ + manager_confbridge_shutdown(); /* Get rid of the conference bridges container. Since we only allow dynamic ones none will be active. */ - ao2_ref(conference_bridges, -1); + ao2_cleanup(conference_bridges); + conference_bridges = NULL; conf_destroy_config(); - ast_channel_unregister(&record_tech); - record_tech.capabilities = ast_format_cap_destroy(record_tech.capabilities); - - res |= ast_manager_unregister("ConfbridgeList"); - res |= ast_manager_unregister("ConfbridgeListRooms"); - res |= ast_manager_unregister("ConfbridgeMute"); - res |= ast_manager_unregister("ConfbridgeUnmute"); - res |= ast_manager_unregister("ConfbridgeKick"); - res |= ast_manager_unregister("ConfbridgeUnlock"); - res |= ast_manager_unregister("ConfbridgeLock"); - res |= ast_manager_unregister("ConfbridgeStartRecord"); - res |= ast_manager_unregister("ConfbridgeStopRecord"); - res |= ast_manager_unregister("ConfbridgeSetSingleVideoSrc"); + unregister_channel_tech(conf_announce_get_tech()); + unregister_channel_tech(conf_record_get_tech()); - return res; + return 0; } -/*! \brief Called when module is being loaded */ +/*! + * \brief Load the module + * + * Module loading including tests for configuration or dependencies. + * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE, + * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails + * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the + * configuration file or other non-critical problem return + * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS. + */ static int load_module(void) { int res = 0; @@ -3105,30 +3390,31 @@ ast_log(LOG_ERROR, "Unable to load config. Not loading module.\n"); return AST_MODULE_LOAD_DECLINE; } - if ((ast_custom_function_register(&confbridge_function))) { - return AST_MODULE_LOAD_FAILURE; - } - if ((ast_custom_function_register(&confbridge_info_function))) { - return AST_MODULE_LOAD_FAILURE; - } - if (!(record_tech.capabilities = ast_format_cap_alloc())) { - return AST_MODULE_LOAD_FAILURE; - } - ast_format_cap_add_all(record_tech.capabilities); - if (ast_channel_register(&record_tech)) { - ast_log(LOG_ERROR, "Unable to register ConfBridge recorder.\n"); + + if (register_channel_tech(conf_record_get_tech()) + || register_channel_tech(conf_announce_get_tech())) { + unload_module(); return AST_MODULE_LOAD_FAILURE; } + /* Create a container to hold the conference bridges */ - if (!(conference_bridges = ao2_container_alloc(CONFERENCE_BRIDGE_BUCKETS, conference_bridge_hash_cb, conference_bridge_cmp_cb))) { - return AST_MODULE_LOAD_FAILURE; - } - if (ast_register_application_xml(app, confbridge_exec)) { - ao2_ref(conference_bridges, -1); + conference_bridges = ao2_container_alloc(CONFERENCE_BRIDGE_BUCKETS, + conference_bridge_hash_cb, conference_bridge_cmp_cb); + if (!conference_bridges) { + unload_module(); return AST_MODULE_LOAD_FAILURE; } - res |= ast_cli_register_multiple(cli_confbridge, sizeof(cli_confbridge) / sizeof(struct ast_cli_entry)); + /* Setup manager stasis subscriptions */ + res |= manager_confbridge_init(); + + res |= ast_register_application_xml(app, confbridge_exec); + + res |= ast_custom_function_register_escalating(&confbridge_function, AST_CFE_WRITE); + res |= ast_custom_function_register(&confbridge_info_function); + + res |= ast_cli_register_multiple(cli_confbridge, ARRAY_LEN(cli_confbridge)); + res |= ast_manager_register_xml("ConfbridgeList", EVENT_FLAG_REPORTING, action_confbridgelist); res |= ast_manager_register_xml("ConfbridgeListRooms", EVENT_FLAG_REPORTING, action_confbridgelistrooms); res |= ast_manager_register_xml("ConfbridgeMute", EVENT_FLAG_CALL, action_confbridgemute); @@ -3136,10 +3422,11 @@ res |= ast_manager_register_xml("ConfbridgeKick", EVENT_FLAG_CALL, action_confbridgekick); res |= ast_manager_register_xml("ConfbridgeUnlock", EVENT_FLAG_CALL, action_confbridgeunlock); res |= ast_manager_register_xml("ConfbridgeLock", EVENT_FLAG_CALL, action_confbridgelock); - res |= ast_manager_register_xml("ConfbridgeStartRecord", EVENT_FLAG_CALL, action_confbridgestartrecord); + res |= ast_manager_register_xml("ConfbridgeStartRecord", EVENT_FLAG_SYSTEM, action_confbridgestartrecord); res |= ast_manager_register_xml("ConfbridgeStopRecord", EVENT_FLAG_CALL, action_confbridgestoprecord); res |= ast_manager_register_xml("ConfbridgeSetSingleVideoSrc", EVENT_FLAG_CALL, action_confbridgesetsinglevideosrc); if (res) { + unload_module(); return AST_MODULE_LOAD_FAILURE; } @@ -3152,6 +3439,7 @@ } AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Conference Bridge Application", + .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .reload = reload, diff -Nru asterisk-11.7.0~dfsg/apps/app_controlplayback.c asterisk-13.1.0~dfsg/apps/app_controlplayback.c --- asterisk-11.7.0~dfsg/apps/app_controlplayback.c 2011-07-14 20:28:54.000000000 +0000 +++ asterisk-13.1.0~dfsg/apps/app_controlplayback.c 2013-01-22 15:16:20.000000000 +0000 @@ -31,11 +31,14 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 328259 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 379830 $") #include "asterisk/pbx.h" #include "asterisk/app.h" #include "asterisk/module.h" +#include "asterisk/manager.h" +#include "asterisk/utils.h" +#include "asterisk/astobj2.h" /*** DOCUMENTATION @@ -82,6 +85,7 @@ Contains the status of the attempt as a text string + @@ -95,6 +99,69 @@ + + + Control the playback of a file being played to a channel. + + + + + The name of the channel that currently has a file being played back to it. + + + + + Stop the playback operation. + + + Move the current position in the media forward. The amount + of time that the stream moves forward is determined by the + skipms value passed to the application + that initiated the playback. + + The default skipms value is 3000 ms. + + + + Move the current position in the media backward. The amount + of time that the stream moves backward is determined by the + skipms value passed to the application + that initiated the playback. + + The default skipms value is 3000 ms. + + + + Pause/unpause the playback operation, if supported. + If not supported, stop the playback. + + + Restart the playback operation, if supported. + If not supported, stop the playback. + + + + + + Control the operation of a media file being played back to a channel. + Note that this AMI action does not initiate playback of media to channel, but + rather controls the operation of a media operation that was already initiated + on the channel. + + The pause and restart + Control options will stop a playback + operation if that operation was not initiated from the + ControlPlayback application or the + control stream file AGI command. + + + + Playback + ControlPlayback + stream file + control stream file + + ***/ static const char app[] = "ControlPlayback"; @@ -201,6 +268,9 @@ snprintf(stopkeybuf, sizeof(stopkeybuf), "%c", res); pbx_builtin_setvar_helper(chan, "CPLAYBACKSTOPKEY", stopkeybuf); res = 0; + } else if (res > 0 && res == AST_CONTROL_STREAM_STOP) { + pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "REMOTESTOPPED"); + res = 0; } else { if (res < 0) { res = 0; @@ -215,16 +285,67 @@ return res; } +static int controlplayback_manager(struct mansession *s, const struct message *m) +{ + const char *channel_name = astman_get_header(m, "Channel"); + const char *control_type = astman_get_header(m, "Control"); + struct ast_channel *chan; + + if (ast_strlen_zero(channel_name)) { + astman_send_error(s, m, "Channel not specified"); + return 0; + } + + if (ast_strlen_zero(control_type)) { + astman_send_error(s, m, "Control not specified"); + return 0; + } + + chan = ast_channel_get_by_name(channel_name); + if (!chan) { + astman_send_error(s, m, "No such channel"); + return 0; + } + + if (!strcasecmp(control_type, "stop")) { + ast_queue_control(chan, AST_CONTROL_STREAM_STOP); + } else if (!strcasecmp(control_type, "forward")) { + ast_queue_control(chan, AST_CONTROL_STREAM_FORWARD); + } else if (!strcasecmp(control_type, "reverse")) { + ast_queue_control(chan, AST_CONTROL_STREAM_REVERSE); + } else if (!strcasecmp(control_type, "pause")) { + ast_queue_control(chan, AST_CONTROL_STREAM_SUSPEND); + } else if (!strcasecmp(control_type, "restart")) { + ast_queue_control(chan, AST_CONTROL_STREAM_RESTART); + } else { + astman_send_error(s, m, "Unknown control type"); + chan = ast_channel_unref(chan); + return 0; + } + + chan = ast_channel_unref(chan); + astman_send_ack(s, m, NULL); + return 0; +} + static int unload_module(void) { - int res; - res = ast_unregister_application(app); + int res = 0; + + res |= ast_unregister_application(app); + res |= ast_manager_unregister("ControlPlayback"); + return res; } static int load_module(void) { - return ast_register_application_xml(app, controlplayback_exec); + int res = 0; + + res |= ast_register_application_xml(app, controlplayback_exec); + res |= ast_manager_register_xml("ControlPlayback", EVENT_FLAG_CALL, controlplayback_manager); + + return res; } AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Control Playback Application"); diff -Nru asterisk-11.7.0~dfsg/apps/app_dahdibarge.c asterisk-13.1.0~dfsg/apps/app_dahdibarge.c --- asterisk-11.7.0~dfsg/apps/app_dahdibarge.c 2012-03-01 22:09:18.000000000 +0000 +++ asterisk-13.1.0~dfsg/apps/app_dahdibarge.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,311 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * Mark Spencer - * - * Special thanks to comphealth.com for sponsoring this - * GPL application. - * - * 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 DAHDI Barge support - * - * \author Mark Spencer - * - * \note Special thanks to comphealth.com for sponsoring this - * GPL application. - * - * \ingroup applications - */ - -/*** MODULEINFO - dahdi - no - deprecated - app_chanspy - ***/ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 357721 $") - -#include - -#include "asterisk/lock.h" -#include "asterisk/file.h" -#include "asterisk/channel.h" -#include "asterisk/pbx.h" -#include "asterisk/module.h" -#include "asterisk/config.h" -#include "asterisk/app.h" -#include "asterisk/cli.h" -#include "asterisk/say.h" -#include "asterisk/utils.h" - -/*** DOCUMENTATION - - - Barge in (monitor) DAHDI channel. - - - - Channel to barge. - - - - Barges in on a specified DAHDI channel or prompts - if one is not specified. Returns -1 when caller user hangs - up and is independent of the state of the channel being monitored. - - - - ***/ -static const char app[] = "DAHDIBarge"; - -#define CONF_SIZE 160 - -static int careful_write(int fd, unsigned char *data, int len) -{ - int res; - while(len) { - res = write(fd, data, len); - if (res < 1) { - if (errno != EAGAIN) { - ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno)); - return -1; - } else - return 0; - } - len -= res; - data += res; - } - return 0; -} - -static int conf_run(struct ast_channel *chan, int confno, int confflags) -{ - int fd; - struct dahdi_confinfo dahdic; - struct ast_frame *f; - struct ast_channel *c; - struct ast_frame fr; - int outfd; - int ms; - int nfds; - int res; - int flags; - int retrydahdi; - int origfd; - int ret = -1; - - struct dahdi_bufferinfo bi; - char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET]; - char *buf = __buf + AST_FRIENDLY_OFFSET; - - /* Set it into U-law mode (write) */ - if (ast_set_write_format_by_id(chan, AST_FORMAT_ULAW) < 0) { - ast_log(LOG_WARNING, "Unable to set '%s' to write ulaw mode\n", ast_channel_name(chan)); - goto outrun; - } - - /* Set it into U-law mode (read) */ - if (ast_set_read_format_by_id(chan, AST_FORMAT_ULAW) < 0) { - ast_log(LOG_WARNING, "Unable to set '%s' to read ulaw mode\n", ast_channel_name(chan)); - goto outrun; - } - ast_indicate(chan, -1); - retrydahdi = strcasecmp(ast_channel_tech(chan)->type, "DAHDI"); -dahdiretry: - origfd = ast_channel_fd(chan, 0); - if (retrydahdi) { - fd = open("/dev/dahdi/pseudo", O_RDWR); - if (fd < 0) { - ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno)); - goto outrun; - } - /* Make non-blocking */ - flags = fcntl(fd, F_GETFL); - if (flags < 0) { - ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno)); - close(fd); - goto outrun; - } - if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) { - ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno)); - close(fd); - goto outrun; - } - /* Setup buffering information */ - memset(&bi, 0, sizeof(bi)); - bi.bufsize = CONF_SIZE; - bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE; - bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE; - bi.numbufs = 4; - if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) { - ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno)); - close(fd); - goto outrun; - } - nfds = 1; - } else { - /* XXX Make sure we're not running on a pseudo channel XXX */ - fd = ast_channel_fd(chan, 0); - nfds = 0; - } - memset(&dahdic, 0, sizeof(dahdic)); - /* Check to see if we're in a conference... */ - dahdic.chan = 0; - if (ioctl(fd, DAHDI_GETCONF, &dahdic)) { - ast_log(LOG_WARNING, "Error getting conference\n"); - close(fd); - goto outrun; - } - if (dahdic.confmode) { - /* Whoa, already in a conference... Retry... */ - if (!retrydahdi) { - ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n"); - retrydahdi = 1; - goto dahdiretry; - } - } - memset(&dahdic, 0, sizeof(dahdic)); - /* Add us to the conference */ - dahdic.chan = 0; - dahdic.confno = confno; - dahdic.confmode = DAHDI_CONF_MONITORBOTH; - - if (ioctl(fd, DAHDI_SETCONF, &dahdic)) { - ast_log(LOG_WARNING, "Error setting conference\n"); - close(fd); - goto outrun; - } - ast_debug(1, "Placed channel %s in DAHDI channel %d monitor\n", ast_channel_name(chan), confno); - - for(;;) { - outfd = -1; - ms = -1; - c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms); - if (c) { - if (ast_channel_fd(c, 0) != origfd) { - if (retrydahdi) { - /* Kill old pseudo */ - close(fd); - } - ast_debug(1, "Ooh, something swapped out under us, starting over\n"); - retrydahdi = 0; - goto dahdiretry; - } - f = ast_read(c); - if (!f) - break; - if ((f->frametype == AST_FRAME_DTMF) && (f->subclass.integer == '#')) { - ret = 0; - ast_frfree(f); - break; - } else if (fd != ast_channel_fd(chan, 0)) { - if (f->frametype == AST_FRAME_VOICE) { - if (f->subclass.format.id == AST_FORMAT_ULAW) { - /* Carefully write */ - careful_write(fd, f->data.ptr, f->datalen); - } else - ast_log(LOG_WARNING, "Huh? Got a non-ulaw (%s) frame in the conference\n", ast_getformatname(&f->subclass.format)); - } - } - ast_frfree(f); - } else if (outfd > -1) { - res = read(outfd, buf, CONF_SIZE); - if (res > 0) { - memset(&fr, 0, sizeof(fr)); - fr.frametype = AST_FRAME_VOICE; - ast_format_set(&fr.subclass.format, AST_FORMAT_ULAW, 0); - fr.datalen = res; - fr.samples = res; - fr.data.ptr = buf; - fr.offset = AST_FRIENDLY_OFFSET; - if (ast_write(chan, &fr) < 0) { - ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno)); - /* break; */ - } - } else - ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno)); - } - } - if (fd != ast_channel_fd(chan, 0)) - close(fd); - else { - /* Take out of conference */ - /* Add us to the conference */ - dahdic.chan = 0; - dahdic.confno = 0; - dahdic.confmode = 0; - if (ioctl(fd, DAHDI_SETCONF, &dahdic)) { - ast_log(LOG_WARNING, "Error setting conference\n"); - } - } - -outrun: - - return ret; -} - -static int conf_exec(struct ast_channel *chan, const char *data) -{ - int res = -1; - int retrycnt = 0; - int confflags = 0; - int confno = 0; - char confnostr[80] = ""; - - if (!ast_strlen_zero(data)) { - if ((sscanf(data, "DAHDI/%30d", &confno) != 1) && - (sscanf(data, "%30d", &confno) != 1)) { - ast_log(LOG_WARNING, "DAHDIBarge Argument (if specified) must be a channel number, not '%s'\n", (char *)data); - return 0; - } - } - - if (ast_channel_state(chan) != AST_STATE_UP) - ast_answer(chan); - - while(!confno && (++retrycnt < 4)) { - /* Prompt user for conference number */ - confnostr[0] = '\0'; - res = ast_app_getdata(chan, "conf-getchannel",confnostr, sizeof(confnostr) - 1, 0); - if (res <0) goto out; - if (sscanf(confnostr, "%30d", &confno) != 1) - confno = 0; - } - if (confno) { - /* XXX Should prompt user for pin if pin is required XXX */ - /* Run the conference */ - res = conf_run(chan, confno, confflags); - } -out: - /* Do the conference */ - return res; -} - -static int unload_module(void) -{ - return ast_unregister_application(app); -} - -static int load_module(void) -{ - return ((ast_register_application_xml(app, conf_exec)) ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_SUCCESS); -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Barge in on DAHDI channel application"); diff -Nru asterisk-11.7.0~dfsg/apps/app_dahdiras.c asterisk-13.1.0~dfsg/apps/app_dahdiras.c --- asterisk-11.7.0~dfsg/apps/app_dahdiras.c 2012-03-01 22:09:18.000000000 +0000 +++ asterisk-13.1.0~dfsg/apps/app_dahdiras.c 2014-07-25 16:47:17.000000000 +0000 @@ -32,7 +32,7 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 357721 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $") #include #include @@ -150,7 +150,7 @@ ast_log(LOG_WARNING, "Failed to spawn RAS\n"); } else { for (;;) { - res = wait4(pid, &status, WNOHANG, NULL); + res = waitpid(pid, &status, WNOHANG); if (!res) { /* Check for hangup */ if (ast_check_hangup(chan) && !signalled) { @@ -163,7 +163,7 @@ continue; } if (res < 0) { - ast_log(LOG_WARNING, "wait4 returned %d: %s\n", res, strerror(errno)); + ast_log(LOG_WARNING, "waitpid returned %d: %s\n", res, strerror(errno)); } if (WIFEXITED(status)) { ast_verb(3, "RAS on %s terminated with status %d\n", ast_channel_name(chan), WEXITSTATUS(status)); @@ -233,5 +233,6 @@ return ((ast_register_application_xml(app, dahdiras_exec)) ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_SUCCESS); } -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "DAHDI ISDN Remote Access Server"); +AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "DAHDI ISDN Remote Access Server"); + diff -Nru asterisk-11.7.0~dfsg/apps/app_db.c asterisk-13.1.0~dfsg/apps/app_db.c --- asterisk-11.7.0~dfsg/apps/app_db.c 2013-02-14 03:48:39.000000000 +0000 +++ asterisk-13.1.0~dfsg/apps/app_db.c 2013-02-14 03:49:52.000000000 +0000 @@ -34,7 +34,7 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 381365 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 381366 $") #include "asterisk/file.h" #include "asterisk/channel.h" diff -Nru asterisk-11.7.0~dfsg/apps/app_dial.c asterisk-13.1.0~dfsg/apps/app_dial.c --- asterisk-11.7.0~dfsg/apps/app_dial.c 2013-02-22 15:41:31.000000000 +0000 +++ asterisk-13.1.0~dfsg/apps/app_dial.c 2014-08-17 23:09:43.000000000 +0000 @@ -1,7 +1,7 @@ /* * Asterisk -- An open source telephony toolkit. * - * Copyright (C) 1999 - 2008, Digium, Inc. + * Copyright (C) 1999 - 2012, Digium, Inc. * * Mark Spencer * @@ -26,14 +26,13 @@ */ /*** MODULEINFO - chan_local core ***/ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 381880 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 421235 $") #include #include @@ -56,17 +55,19 @@ #include "asterisk/app.h" #include "asterisk/causes.h" #include "asterisk/rtp_engine.h" -#include "asterisk/cdr.h" #include "asterisk/manager.h" #include "asterisk/privacy.h" #include "asterisk/stringfields.h" #include "asterisk/global_datastores.h" #include "asterisk/dsp.h" -#include "asterisk/cel.h" #include "asterisk/aoc.h" #include "asterisk/ccss.h" #include "asterisk/indications.h" #include "asterisk/framehook.h" +#include "asterisk/dial.h" +#include "asterisk/stasis_channels.h" +#include "asterisk/bridge_after.h" +#include "asterisk/features_config.h" /*** DOCUMENTATION @@ -151,11 +152,12 @@ Send the specified DTMF strings after the called - party has answered, but before the call gets bridged. The - called DTMF string is sent to the called party, and the - calling DTMF string is sent to the calling party. Both arguments + party has answered, but before the call gets bridged. The + called DTMF string is sent to the called party, and the + calling DTMF string is sent to the calling party. Both arguments can be used alone. If progress is specified, its DTMF is sent - immediately after receiving a PROGRESS message. + to the called party immediately after receiving a PROGRESS message. + See SendDTMF for valid digits. + + @@ -135,6 +154,7 @@ OPT_LISTBYEITHER = OPT_LISTBYFIRSTNAME | OPT_LISTBYLASTNAME, OPT_PAUSE = (1 << 5), OPT_NOANSWER = (1 << 6), + OPT_ALIAS = (1 << 7), }; enum { @@ -164,6 +184,7 @@ AST_APP_OPTION('v', OPT_FROMVOICEMAIL), AST_APP_OPTION('m', OPT_SELECTFROMMENU), AST_APP_OPTION('n', OPT_NOANSWER), + AST_APP_OPTION('a', OPT_ALIAS), }); static int compare(const char *text, const char *template) @@ -268,18 +289,24 @@ const char *ext, const char *name, struct ast_flags *flags) { int res = 0; - if ((res = ast_app_sayname(chan, ext, context)) >= 0) { + char *mailbox_id; + + mailbox_id = ast_alloca(strlen(ext) + strlen(context) + 2); + sprintf(mailbox_id, "%s@%s", ext, context); /* Safe */ + + res = ast_app_sayname(chan, mailbox_id); + if (res >= 0) { ast_stopstream(chan); /* If Option 'e' was specified, also read the extension number with the name */ if (ast_test_flag(flags, OPT_SAYEXTENSION)) { ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY); - res = ast_say_character_str(chan, ext, AST_DIGIT_ANY, ast_channel_language(chan)); + res = ast_say_character_str(chan, ext, AST_DIGIT_ANY, ast_channel_language(chan), AST_SAY_CASE_NONE); } } else { - res = ast_say_character_str(chan, S_OR(name, ext), AST_DIGIT_ANY, ast_channel_language(chan)); + res = ast_say_character_str(chan, S_OR(name, ext), AST_DIGIT_ANY, ast_channel_language(chan), AST_SAY_CASE_NONE); if (!ast_strlen_zero(name) && ast_test_flag(flags, OPT_SAYEXTENSION)) { ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY); - res = ast_say_character_str(chan, ext, AST_DIGIT_ANY, ast_channel_language(chan)); + res = ast_say_character_str(chan, ext, AST_DIGIT_ANY, ast_channel_language(chan), AST_SAY_CASE_NONE); } } @@ -301,6 +328,7 @@ return -1; } + pbx_builtin_setvar_helper(chan, "DIRECTORY_RESULT", "SELECTED"); return 0; } @@ -341,6 +369,7 @@ if (res == '0') { /* operator selected */ goto_exten(chan, dialcontext, "o"); + pbx_builtin_setvar_helper(chan, "DIRECTORY_RESULT", "OPERATOR"); return '0'; } else if (res == '1') { /* Name selected */ return select_entry(chan, dialcontext, item, flags) ? -1 : 1; @@ -349,6 +378,7 @@ break; } else if (res == '#') { /* Exit reading, continue in dialplan */ + pbx_builtin_setvar_helper(chan, "DIRECTORY_RESULT", "USEREXIT"); return res; } @@ -414,6 +444,7 @@ } if (res && res > '0' && res < '1' + limit) { + pbx_builtin_setvar_helper(chan, "DIRECTORY_RESULT", "SELECTED"); return select_entry(chan, dialcontext, block[res - '1'], flags) ? -1 : 1; } @@ -427,17 +458,23 @@ return 0; } +AST_THREADSTORAGE(commonbuf); + static struct ast_config *realtime_directory(char *context) { struct ast_config *cfg; - struct ast_config *rtdata; + struct ast_config *rtdata = NULL; struct ast_category *cat; struct ast_variable *var; char *mailbox; const char *fullname; const char *hidefromdir, *searchcontexts = NULL; - char tmp[100]; struct ast_flags config_flags = { 0 }; + struct ast_str *tmp = ast_str_thread_get(&commonbuf, 100); + + if (!tmp) { + return NULL; + } /* Load flat file config. */ cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags); @@ -461,7 +498,7 @@ rtdata = ast_load_realtime_multientry("voicemail", "mailbox LIKE", "%", "context", "default", SENTINEL); context = "default"; } - } else { + } else if (!ast_strlen_zero(context)) { rtdata = ast_load_realtime_multientry("voicemail", "mailbox LIKE", "%", "context", context, SENTINEL); } @@ -472,6 +509,7 @@ mailbox = NULL; while ( (mailbox = ast_category_browse(rtdata, mailbox)) ) { + struct ast_variable *alias; const char *ctx = ast_variable_retrieve(rtdata, mailbox, "context"); fullname = ast_variable_retrieve(rtdata, mailbox, "fullname"); @@ -480,10 +518,17 @@ /* Skip hidden */ continue; } - snprintf(tmp, sizeof(tmp), "no-password,%s", S_OR(fullname, "")); + ast_str_set(&tmp, 0, "no-password,%s", S_OR(fullname, "")); + if (ast_variable_retrieve(rtdata, mailbox, "alias")) { + for (alias = ast_variable_browse(rtdata, mailbox); alias; alias = alias->next) { + if (!strcasecmp(alias->name, "alias")) { + ast_str_append(&tmp, 0, "|alias=%s", alias->value); + } + } + } /* Does the context exist within the config file? If not, make one */ - if (!(cat = ast_category_get(cfg, ctx))) { + if (!(cat = ast_category_get(cfg, ctx, NULL))) { if (!(cat = ast_category_new(ctx, "", 99999))) { ast_log(LOG_WARNING, "Out of memory\n"); ast_config_destroy(cfg); @@ -495,7 +540,7 @@ ast_category_append(cfg, cat); } - if ((var = ast_variable_new(mailbox, tmp, ""))) { + if ((var = ast_variable_new(mailbox, ast_str_buffer(tmp), ""))) { ast_variable_append(cat, var); } else { ast_log(LOG_WARNING, "Out of memory adding mailbox '%s'\n", mailbox); @@ -556,20 +601,26 @@ static int search_directory_sub(const char *context, struct ast_config *vmcfg, struct ast_config *ucfg, const char *ext, struct ast_flags flags, itemlist *alist) { struct ast_variable *v; - char buf[AST_MAX_EXTENSION + 1], *pos, *bufptr, *cat; + struct ast_str *buf = ast_str_thread_get(&commonbuf, 100); + char *pos, *bufptr, *cat, *alias; struct directory_item *item; int res; + if (!buf) { + return -1; + } + ast_debug(2, "Pattern: %s\n", ext); for (v = ast_variable_browse(vmcfg, context); v; v = v->next) { /* Ignore hidden */ - if (strcasestr(v->value, "hidefromdir=yes")) + if (strcasestr(v->value, "hidefromdir=yes")) { continue; + } - ast_copy_string(buf, v->value, sizeof(buf)); - bufptr = buf; + ast_str_set(&buf, 0, "%s", v->value); + bufptr = ast_str_buffer(buf); /* password,Full Name,email,pager,options */ strsep(&bufptr, ","); @@ -587,11 +638,23 @@ if (!res && ast_test_flag(&flags, OPT_LISTBYFIRSTNAME)) { res = check_match(&item, context, pos, v->name, ext, 1 /* use_first_name */); } + if (!res && ast_test_flag(&flags, OPT_ALIAS) && (alias = strcasestr(bufptr, "alias="))) { + char *a; + ast_debug(1, "Found alias: %s\n", alias); + while ((a = strsep(&alias, "|"))) { + if (!strncasecmp(a, "alias=", 6)) { + if ((res = check_match(&item, context, a + 6, v->name, ext, 1))) { + break; + } + } + } + } - if (!res) + if (!res) { continue; - else if (res < 0) + } else if (res < 0) { return -1; + } AST_LIST_INSERT_TAIL(alist, item, entry); } @@ -599,15 +662,18 @@ if (ucfg) { for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) { const char *position; - if (!strcasecmp(cat, "general")) + + if (!strcasecmp(cat, "general")) { continue; - if (!ast_true(ast_config_option(ucfg, cat, "hasdirectory"))) + } + if (!ast_true(ast_config_option(ucfg, cat, "hasdirectory"))) { continue; + } /* Find all candidate extensions */ - position = ast_variable_retrieve(ucfg, cat, "fullname"); - if (!position) + if (!(position = ast_variable_retrieve(ucfg, cat, "fullname"))) { continue; + } res = 0; if (ast_test_flag(&flags, OPT_LISTBYLASTNAME)) { @@ -616,11 +682,20 @@ if (!res && ast_test_flag(&flags, OPT_LISTBYFIRSTNAME)) { res = check_match(&item, context, position, cat, ext, 1 /* use_first_name */); } + if (!res && ast_test_flag(&flags, OPT_ALIAS)) { + struct ast_variable *alias; + for (alias = ast_variable_browse(ucfg, cat); alias; alias = alias->next) { + if (!strcasecmp(v->name, "alias") && (res = check_match(&item, context, v->value, cat, ext, 1))) { + break; + } + } + } - if (!res) + if (!res) { continue; - else if (res < 0) + } else if (res < 0) { return -1; + } AST_LIST_INSERT_TAIL(alist, item, entry); } @@ -689,10 +764,12 @@ char ext[10] = ""; if (digit == '0' && !goto_exten(chan, dialcontext, "o")) { + pbx_builtin_setvar_helper(chan, "DIRECTORY_RESULT", "OPERATOR"); return digit; } if (digit == '*' && !goto_exten(chan, dialcontext, "a")) { + pbx_builtin_setvar_helper(chan, "DIRECTORY_RESULT", "ASSISTANT"); return digit; } @@ -857,8 +934,12 @@ if (!res) res = ast_waitfordigit(chan, 5000); - if (res <= 0) + if (res <= 0) { + if (res == 0) { + pbx_builtin_setvar_helper(chan, "DIRECTORY_RESULT", "TIMEOUT"); + } break; + } res = do_directory(chan, cfg, ucfg, args.vmcontext, args.dialcontext, res, digit, &flags, opts); if (res) @@ -866,15 +947,22 @@ res = ast_waitstream(chan, AST_DIGIT_ANY); ast_stopstream(chan); - - if (res) + if (res < 0) { break; + } } if (ucfg) ast_config_destroy(ucfg); ast_config_destroy(cfg); + if (ast_check_hangup(chan)) { + pbx_builtin_setvar_helper(chan, "DIRECTORY_RESULT", "HANGUP"); + } else if (res < 0) { + /* If the res < 0 and we didn't hangup, an unaccounted for error must have happened. */ + pbx_builtin_setvar_helper(chan, "DIRECTORY_RESULT", "FAILED"); + } + return res < 0 ? -1 : 0; } diff -Nru asterisk-11.7.0~dfsg/apps/app_disa.c asterisk-13.1.0~dfsg/apps/app_disa.c --- asterisk-11.7.0~dfsg/apps/app_disa.c 2012-05-10 15:57:26.000000000 +0000 +++ asterisk-13.1.0~dfsg/apps/app_disa.c 2013-12-19 00:50:01.000000000 +0000 @@ -27,12 +27,13 @@ */ /*** MODULEINFO + app_cdr core ***/ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 366051 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 404295 $") #include #include @@ -362,7 +363,7 @@ if (k == 3) { int recheck = 0; - struct ast_flags cdr_flags = { AST_CDR_FLAG_POSTED }; + struct ast_app *app_reset_cdr; if (!ast_exists_extension(chan, args.context, exten, 1, S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) { @@ -381,11 +382,18 @@ ast_set_callerid(chan, ourcidnum, ourcidname, ourcidnum); } - if (!ast_strlen_zero(acctcode)) + if (!ast_strlen_zero(acctcode)) { + ast_channel_lock(chan); ast_channel_accountcode_set(chan, acctcode); + ast_channel_unlock(chan); + } - if (special_noanswer) cdr_flags.flags = 0; - ast_cdr_reset(ast_channel_cdr(chan), &cdr_flags); + app_reset_cdr = pbx_findapp("ResetCDR"); + if (app_reset_cdr) { + pbx_exec(chan, app_reset_cdr, special_noanswer ? "" : "e"); + } else { + ast_log(AST_LOG_NOTICE, "ResetCDR application not found; CDR will not be reset\n"); + } ast_explicit_goto(chan, args.context, exten, 1); return 0; } diff -Nru asterisk-11.7.0~dfsg/apps/app_dumpchan.c asterisk-13.1.0~dfsg/apps/app_dumpchan.c --- asterisk-11.7.0~dfsg/apps/app_dumpchan.c 2013-08-29 22:16:41.000000000 +0000 +++ asterisk-13.1.0~dfsg/apps/app_dumpchan.c 2014-07-20 22:06:33.000000000 +0000 @@ -34,13 +34,14 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 397948 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419044 $") #include "asterisk/pbx.h" #include "asterisk/module.h" #include "asterisk/channel.h" #include "asterisk/app.h" #include "asterisk/translate.h" +#include "asterisk/bridge.h" /*** DOCUMENTATION @@ -49,7 +50,7 @@ - Minimun verbose level + Minimum verbose level @@ -69,26 +70,27 @@ static int serialize_showchan(struct ast_channel *c, char *buf, size_t size) { - struct timeval now; long elapsed_seconds = 0; int hour = 0, min = 0, sec = 0; - char nf[256]; + struct ast_str *format_buf = ast_str_alloca(64); char cgrp[256]; char pgrp[256]; struct ast_str *write_transpath = ast_str_alloca(256); struct ast_str *read_transpath = ast_str_alloca(256); + struct ast_bridge *bridge; - now = ast_tvnow(); memset(buf, 0, size); if (!c) return 0; - if (ast_channel_cdr(c)) { - elapsed_seconds = now.tv_sec - ast_channel_cdr(c)->start.tv_sec; - hour = elapsed_seconds / 3600; - min = (elapsed_seconds % 3600) / 60; - sec = elapsed_seconds % 60; - } + elapsed_seconds = ast_channel_get_duration(c); + hour = elapsed_seconds / 3600; + min = (elapsed_seconds % 3600) / 60; + sec = elapsed_seconds % 60; + + ast_channel_lock(c); + bridge = ast_channel_get_bridge(c); + ast_channel_unlock(c); snprintf(buf,size, "Name= %s\n" @@ -103,7 +105,7 @@ "RDNIS= %s\n" "Parkinglot= %s\n" "Language= %s\n" - "State= %s (%d)\n" + "State= %s (%u)\n" "Rings= %d\n" "NativeFormat= %s\n" "WriteFormat= %s\n" @@ -113,12 +115,11 @@ "WriteTranscode= %s %s\n" "ReadTranscode= %s %s\n" "1stFileDescriptor= %d\n" - "Framesin= %d %s\n" - "Framesout= %d %s\n" + "Framesin= %u %s\n" + "Framesout= %u %s\n" "TimetoHangup= %ld\n" "ElapsedTime= %dh%dm%ds\n" - "DirectBridge= %s\n" - "IndirectBridge= %s\n" + "BridgeID= %s\n" "Context= %s\n" "Extension= %s\n" "Priority= %d\n" @@ -142,11 +143,11 @@ ast_state2str(ast_channel_state(c)), ast_channel_state(c), ast_channel_rings(c), - ast_getformatname_multiple(nf, sizeof(nf), ast_channel_nativeformats(c)), - ast_getformatname(ast_channel_writeformat(c)), - ast_getformatname(ast_channel_readformat(c)), - ast_getformatname(ast_channel_rawwriteformat(c)), - ast_getformatname(ast_channel_rawreadformat(c)), + ast_format_cap_get_names(ast_channel_nativeformats(c), &format_buf), + ast_format_get_name(ast_channel_writeformat(c)), + ast_format_get_name(ast_channel_readformat(c)), + ast_format_get_name(ast_channel_rawwriteformat(c)), + ast_format_get_name(ast_channel_rawreadformat(c)), ast_channel_writetrans(c) ? "Yes" : "No", ast_translate_path_to_str(ast_channel_writetrans(c), &write_transpath), ast_channel_readtrans(c) ? "Yes" : "No", @@ -158,8 +159,7 @@ hour, min, sec, - ast_channel_internal_bridged_channel(c) ? ast_channel_name(ast_channel_internal_bridged_channel(c)) : "", - ast_bridged_channel(c) ? ast_channel_name(ast_bridged_channel(c)) : "", + bridge ? bridge->uniqueid : "(Not bridged)", ast_channel_context(c), ast_channel_exten(c), ast_channel_priority(c), @@ -169,6 +169,7 @@ ast_channel_data(c) ? S_OR(ast_channel_data(c), "(Empty)") : "(None)", (ast_test_flag(ast_channel_flags(c), AST_FLAG_BLOCKING) ? ast_channel_blockproc(c) : "(Not Blocking)")); + ao2_cleanup(bridge); return 0; } @@ -182,15 +183,17 @@ if (!ast_strlen_zero(data)) level = atoi(data); - serialize_showchan(chan, info, sizeof(info)); - pbx_builtin_serialize_variables(chan, &vars); - ast_verb(level, "\n" - "Dumping Info For Channel: %s:\n" - "%s\n" - "Info:\n" - "%s\n" - "Variables:\n" - "%s%s\n", ast_channel_name(chan), line, info, ast_str_buffer(vars), line); + if (VERBOSITY_ATLEAST(level)) { + serialize_showchan(chan, info, sizeof(info)); + pbx_builtin_serialize_variables(chan, &vars); + ast_verb(level, "\n" + "Dumping Info For Channel: %s:\n" + "%s\n" + "Info:\n" + "%s\n" + "Variables:\n" + "%s%s\n", ast_channel_name(chan), line, info, ast_str_buffer(vars), line); + } return 0; } diff -Nru asterisk-11.7.0~dfsg/apps/app_echo.c asterisk-13.1.0~dfsg/apps/app_echo.c --- asterisk-11.7.0~dfsg/apps/app_echo.c 2012-03-20 20:42:34.000000000 +0000 +++ asterisk-13.1.0~dfsg/apps/app_echo.c 2014-07-20 22:06:33.000000000 +0000 @@ -31,7 +31,7 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 360036 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419044 $") #include "asterisk/file.h" #include "asterisk/module.h" @@ -58,11 +58,6 @@ static int echo_exec(struct ast_channel *chan, const char *data) { int res = -1; - struct ast_format format; - - ast_best_codec(ast_channel_nativeformats(chan), &format); - ast_set_write_format(chan, &format); - ast_set_read_format(chan, &format); while (ast_waitfor(chan, -1) > -1) { struct ast_frame *f = ast_read(chan); diff -Nru asterisk-11.7.0~dfsg/apps/app_externalivr.c asterisk-13.1.0~dfsg/apps/app_externalivr.c --- asterisk-11.7.0~dfsg/apps/app_externalivr.c 2012-06-20 11:47:12.000000000 +0000 +++ asterisk-13.1.0~dfsg/apps/app_externalivr.c 2014-07-25 16:47:17.000000000 +0000 @@ -37,7 +37,7 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 369142 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $") #include @@ -915,4 +915,5 @@ return ast_register_application_xml(app, app_exec); } -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "External IVR Interface Application"); +AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "External IVR Interface Application"); + diff -Nru asterisk-11.7.0~dfsg/apps/app_fax.c asterisk-13.1.0~dfsg/apps/app_fax.c --- asterisk-11.7.0~dfsg/apps/app_fax.c 2012-02-29 16:52:47.000000000 +0000 +++ asterisk-13.1.0~dfsg/apps/app_fax.c 2014-07-25 16:47:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Asterisk -- A telephony toolkit for Linux. + * Asterisk -- An open source telephony toolkit. * * Simple fax applications * @@ -21,7 +21,7 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 357542 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $") #include #include @@ -43,7 +43,9 @@ #include "asterisk/app.h" #include "asterisk/dsp.h" #include "asterisk/module.h" -#include "asterisk/manager.h" +#include "asterisk/stasis.h" +#include "asterisk/stasis_channels.h" +#include "asterisk/format_cache.h" /*** DOCUMENTATION @@ -202,6 +204,9 @@ static void phase_e_handler(t30_state_t *f, void *user_data, int result) { + RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref); + RAII_VAR(struct ast_json *, json_filenames, NULL, ast_json_unref); + RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup); const char *local_ident; const char *far_ident; char buf[20]; @@ -251,32 +256,24 @@ ast_debug(1, " Image resolution: %d x %d\n", stat.x_resolution, stat.y_resolution); ast_debug(1, " Transfer Rate: %d\n", stat.bit_rate); - ast_manager_event(s->chan, EVENT_FLAG_CALL, - s->direction ? "FaxSent" : "FaxReceived", - "Channel: %s\r\n" - "Exten: %s\r\n" - "CallerID: %s\r\n" - "CallerIDName: %s\r\n" - "ConnectedLineNum: %s\r\n" - "ConnectedLineName: %s\r\n" - "RemoteStationID: %s\r\n" - "LocalStationID: %s\r\n" - "PagesTransferred: %d\r\n" - "Resolution: %d\r\n" - "TransferRate: %d\r\n" - "FileName: %s\r\n", - ast_channel_name(s->chan), - ast_channel_exten(s->chan), - S_COR(ast_channel_caller(s->chan)->id.number.valid, ast_channel_caller(s->chan)->id.number.str, ""), - S_COR(ast_channel_caller(s->chan)->id.name.valid, ast_channel_caller(s->chan)->id.name.str, ""), - S_COR(ast_channel_connected(s->chan)->id.number.valid, ast_channel_connected(s->chan)->id.number.str, ""), - S_COR(ast_channel_connected(s->chan)->id.name.valid, ast_channel_connected(s->chan)->id.name.str, ""), - far_ident, - local_ident, - pages_transferred, - stat.y_resolution, - stat.bit_rate, - s->file_name); + json_filenames = ast_json_pack("[s]", s->file_name); + if (!json_filenames) { + return; + } + ast_json_ref(json_filenames); + json_object = ast_json_pack("{s: s, s: s, s: s, s: i, s: i, s: i, s: o}", + "type", s->direction ? "send" : "receive", + "remote_station_id", far_ident, + "local_station_id", local_ident, + "fax_pages", pages_transferred, + "fax_resolution", stat.y_resolution, + "fax_bitrate", stat.bit_rate, + "filenames", json_filenames); + message = ast_channel_blob_create_from_cache(ast_channel_uniqueid(s->chan), ast_channel_fax_type(), json_object); + if (!message) { + return; + } + stasis_publish(ast_channel_topic(s->chan), message); } /* === Helper functions to configure fax === */ @@ -336,9 +333,9 @@ struct ast_frame outf = { .frametype = AST_FRAME_VOICE, + .subclass.format = ast_format_slin, .src = __FUNCTION__, }; - ast_format_set(&outf.subclass.format, AST_FORMAT_SLINEAR, 0); if (samples > MAX_SAMPLES) { ast_log(LOG_WARNING, "Only generating %d samples, where %d requested\n", MAX_SAMPLES, samples); @@ -369,8 +366,8 @@ static int transmit_audio(fax_session *s) { int res = -1; - struct ast_format original_read_fmt; - struct ast_format original_write_fmt; + struct ast_format *original_read_fmt; + struct ast_format *original_write_fmt = NULL; fax_state_t fax; t30_state_t *t30state; struct ast_frame *inf = NULL; @@ -390,9 +387,6 @@ */ }; - ast_format_clear(&original_read_fmt); - ast_format_clear(&original_write_fmt); - /* if in called party mode, try to use T.38 */ if (s->caller_mode == FALSE) { /* check if we are already in T.38 mode (unlikely), or if we can request @@ -465,22 +459,18 @@ t30state = &fax.t30_state; #endif - ast_format_copy(&original_read_fmt, ast_channel_readformat(s->chan)); - if (original_read_fmt.id != AST_FORMAT_SLINEAR) { - res = ast_set_read_format_by_id(s->chan, AST_FORMAT_SLINEAR); - if (res < 0) { - ast_log(LOG_WARNING, "Unable to set to linear read mode, giving up\n"); - goto done; - } + original_read_fmt = ao2_bump(ast_channel_readformat(s->chan)); + res = ast_set_read_format(s->chan, ast_format_slin); + if (res < 0) { + ast_log(LOG_WARNING, "Unable to set to linear read mode, giving up\n"); + goto done; } - ast_format_copy(&original_write_fmt, ast_channel_writeformat(s->chan)); - if (original_write_fmt.id != AST_FORMAT_SLINEAR) { - res = ast_set_write_format_by_id(s->chan, AST_FORMAT_SLINEAR); - if (res < 0) { - ast_log(LOG_WARNING, "Unable to set to linear write mode, giving up\n"); - goto done; - } + original_write_fmt = ao2_bump(ast_channel_writeformat(s->chan)); + res = ast_set_write_format(s->chan, ast_format_slin); + if (res < 0) { + ast_log(LOG_WARNING, "Unable to set to linear write mode, giving up\n"); + goto done; } /* Initialize T30 terminal */ @@ -533,12 +523,13 @@ break; } - ast_debug(10, "frame %d/%u, len=%d\n", inf->frametype, (unsigned int) inf->subclass.format.id, inf->datalen); + ast_debug(10, "frame %d/%s, len=%d\n", inf->frametype, ast_format_get_name(inf->subclass.format), inf->datalen); /* Check the frame type. Format also must be checked because there is a chance that a frame in old format was already queued before we set channel format to slinear so it will still be received by ast_read */ - if (inf->frametype == AST_FRAME_VOICE && inf->subclass.format.id == AST_FORMAT_SLINEAR) { + if (inf->frametype == AST_FRAME_VOICE && + (ast_format_cmp(inf->subclass.format, ast_format_slin) == AST_FORMAT_CMP_EQUAL)) { if (fax_rx(&fax, inf->data.ptr, inf->samples) < 0) { /* I know fax_rx never returns errors. The check here is for good style only */ ast_log(LOG_WARNING, "fax_rx returned error\n"); @@ -592,14 +583,16 @@ fax_release(&fax); done: - if (original_write_fmt.id != AST_FORMAT_SLINEAR) { - if (ast_set_write_format(s->chan, &original_write_fmt) < 0) + if (original_write_fmt) { + if (ast_set_write_format(s->chan, original_write_fmt) < 0) ast_log(LOG_WARNING, "Unable to restore write format on '%s'\n", ast_channel_name(s->chan)); + ao2_ref(original_write_fmt, -1); } - if (original_read_fmt.id != AST_FORMAT_SLINEAR) { - if (ast_set_read_format(s->chan, &original_read_fmt) < 0) + if (original_read_fmt) { + if (ast_set_read_format(s->chan, original_read_fmt) < 0) ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", ast_channel_name(s->chan)); + ao2_ref(original_read_fmt, -1); } return res; @@ -1003,6 +996,7 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Simple FAX Application", + .support_level = AST_MODULE_SUPPORT_EXTENDED, .load = load_module, .unload = unload_module, ); diff -Nru asterisk-11.7.0~dfsg/apps/app_festival.c asterisk-13.1.0~dfsg/apps/app_festival.c --- asterisk-11.7.0~dfsg/apps/app_festival.c 2012-07-31 20:21:43.000000000 +0000 +++ asterisk-13.1.0~dfsg/apps/app_festival.c 2014-07-25 16:47:17.000000000 +0000 @@ -27,13 +27,22 @@ * \ingroup applications */ +/*! \li \ref app_festival.c uses the configuration file \ref festival.conf + * \addtogroup configuration_file Configuration Files + */ + +/*! + * \page festival.conf festival.conf + * \verbinclude festival.conf.sample + */ + /*** MODULEINFO extended ***/ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 370655 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $") #include #include @@ -54,6 +63,7 @@ #include "asterisk/lock.h" #include "asterisk/app.h" #include "asterisk/endian.h" +#include "asterisk/format_cache.h" #define FESTIVAL_CONFIG "festival.conf" #define MAXLEN 180 @@ -168,7 +178,7 @@ int res = 0; int fds[2]; int needed = 0; - struct ast_format owriteformat; + struct ast_format *owriteformat; struct ast_frame *f; struct myframe { struct ast_frame f; @@ -178,7 +188,6 @@ .f = { 0, }, }; - ast_format_clear(&owriteformat); if (pipe(fds)) { ast_log(LOG_WARNING, "Unable to create pipe\n"); return -1; @@ -190,12 +199,19 @@ ast_stopstream(chan); ast_indicate(chan, -1); - ast_format_copy(&owriteformat, ast_channel_writeformat(chan)); - res = ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR); + owriteformat = ao2_bump(ast_channel_writeformat(chan)); + res = ast_set_write_format(chan, ast_format_slin); if (res < 0) { ast_log(LOG_WARNING, "Unable to set write format to signed linear\n"); + ao2_cleanup(owriteformat); return -1; } + + myf.f.frametype = AST_FRAME_VOICE; + myf.f.subclass.format = ast_format_slin; + myf.f.offset = AST_FRIENDLY_OFFSET; + myf.f.src = __PRETTY_FUNCTION__; + myf.f.data.ptr = myf.frdata; res = send_waveform_to_fd(waveform, length, fds[1]); if (res >= 0) { @@ -231,13 +247,8 @@ } res = read(fds[0], myf.frdata, needed); if (res > 0) { - myf.f.frametype = AST_FRAME_VOICE; - ast_format_set(&myf.f.subclass.format, AST_FORMAT_SLINEAR, 0); myf.f.datalen = res; myf.f.samples = res / 2; - myf.f.offset = AST_FRIENDLY_OFFSET; - myf.f.src = __PRETTY_FUNCTION__; - myf.f.data.ptr = myf.frdata; if (ast_write(chan, &myf.f) < 0) { res = -1; ast_frfree(f); @@ -260,8 +271,10 @@ close(fds[0]); close(fds[1]); - if (!res && owriteformat.id) - ast_set_write_format(chan, &owriteformat); + if (!res && owriteformat) + ast_set_write_format(chan, owriteformat); + ao2_cleanup(owriteformat); + return res; } @@ -415,7 +428,7 @@ /* Convert to HEX and look if there is any matching file in the cache directory */ for (i = 0; i < 16; i++) { - snprintf(koko, sizeof(koko), "%X", MD5Res[i]); + snprintf(koko, sizeof(koko), "%X", (unsigned)MD5Res[i]); strncat(MD5Hex, koko, sizeof(MD5Hex) - strlen(MD5Hex) - 1); } readcache = 0; @@ -537,6 +550,16 @@ return ast_unregister_application(app); } +/*! + * \brief Load the module + * + * Module loading including tests for configuration or dependencies. + * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE, + * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails + * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the + * configuration file or other non-critical problem return + * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS. + */ static int load_module(void) { struct ast_flags config_flags = { 0 }; @@ -552,4 +575,5 @@ return ast_register_application_xml(app, festival_exec); } -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Simple Festival Interface"); +AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Simple Festival Interface"); + diff -Nru asterisk-11.7.0~dfsg/apps/app_followme.c asterisk-13.1.0~dfsg/apps/app_followme.c --- asterisk-11.7.0~dfsg/apps/app_followme.c 2012-09-06 00:59:23.000000000 +0000 +++ asterisk-13.1.0~dfsg/apps/app_followme.c 2014-07-25 16:47:17.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Asterisk -- A telephony toolkit for Linux. + * Asterisk -- An open source telephony toolkit. * * A full-featured Find-Me/Follow-Me Application * @@ -23,19 +23,25 @@ * * \author BJ Weschke * - * \arg See \ref Config_followme - * * \ingroup applications */ +/*! \li \ref app_followme.c uses the configuration file \ref followme.conf + * \addtogroup configuration_file Configuration Files + */ + +/*! + * \page followme.conf followme.conf + * \verbinclude followme.conf.sample + */ + /*** MODULEINFO - chan_local core ***/ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 372392 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $") #include @@ -57,6 +63,7 @@ #include "asterisk/astdb.h" #include "asterisk/dsp.h" #include "asterisk/app.h" +#include "asterisk/stasis_channels.h" /*** DOCUMENTATION @@ -550,6 +557,17 @@ return 1; } +static void publish_dial_end_event(struct ast_channel *in, struct findme_user_listptr *findme_user_list, struct ast_channel *exception, const char *status) +{ + struct findme_user *tmpuser; + + AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) { + if (tmpuser->ochan && tmpuser->ochan != exception) { + ast_channel_publish_dial(in, tmpuser->ochan, NULL, status); + } + } +} + static void clear_caller(struct findme_user *tmpuser) { struct ast_channel *outbound; @@ -560,29 +578,6 @@ } outbound = tmpuser->ochan; - ast_channel_lock(outbound); - if (!ast_channel_cdr(outbound)) { - ast_channel_cdr_set(outbound, ast_cdr_alloc()); - if (ast_channel_cdr(outbound)) { - ast_cdr_init(ast_channel_cdr(outbound), outbound); - } - } - if (ast_channel_cdr(outbound)) { - char tmp[256]; - - snprintf(tmp, sizeof(tmp), "Local/%s", tmpuser->dialarg); - ast_cdr_setapp(ast_channel_cdr(outbound), "FollowMe", tmp); - ast_cdr_update(outbound); - ast_cdr_start(ast_channel_cdr(outbound)); - ast_cdr_end(ast_channel_cdr(outbound)); - /* If the cause wasn't handled properly */ - if (ast_cdr_disposition(ast_channel_cdr(outbound), ast_channel_hangupcause(outbound))) { - ast_cdr_failed(ast_channel_cdr(outbound)); - } - } else { - ast_log(LOG_WARNING, "Unable to create Call Detail Record\n"); - } - ast_channel_unlock(outbound); ast_hangup(outbound); tmpuser->ochan = NULL; } @@ -771,6 +766,7 @@ } if (!tmpuser) { ast_verb(3, "The calling channel hungup. Need to drop everyone.\n"); + publish_dial_end_event(caller, findme_user_list, NULL, "CANCEL"); ast_frfree(f); return NULL; } @@ -782,6 +778,8 @@ break; } ast_verb(3, "%s answered %s\n", ast_channel_name(winner), ast_channel_name(caller)); + ast_channel_publish_dial(caller, winner, NULL, "ANSWER"); + publish_dial_end_event(caller, findme_user_list, winner, "CANCEL"); tmpuser->answered = 1; /* If call has been answered, then the eventual hangup is likely to be normal hangup */ ast_channel_hangupcause_set(winner, AST_CAUSE_NORMAL_CLEARING); @@ -809,6 +807,7 @@ ast_verb(3, "%s is busy\n", ast_channel_name(winner)); if (tmpuser) { /* Outbound call was busy. Drop it. */ + ast_channel_publish_dial(caller, winner, NULL, "BUSY"); clear_caller(tmpuser); } break; @@ -816,6 +815,7 @@ ast_verb(3, "%s is circuit-busy\n", ast_channel_name(winner)); if (tmpuser) { /* Outbound call was congested. Drop it. */ + ast_channel_publish_dial(caller, winner, NULL, "CONGESTION"); clear_caller(tmpuser); } break; @@ -964,6 +964,7 @@ return NULL; } /* Outgoing channel hung up. */ + ast_channel_publish_dial(caller, winner, NULL, "NOANSWER"); clear_caller(tmpuser); } } else { @@ -1055,7 +1056,7 @@ ? "/n" : "/m"); } - outbound = ast_request("Local", ast_channel_nativeformats(caller), caller, + outbound = ast_request("Local", ast_channel_nativeformats(caller), NULL, caller, tmpuser->dialarg, &dg); if (!outbound) { ast_log(LOG_WARNING, "Unable to allocate a channel for Local/%s cause: %s\n", @@ -1069,7 +1070,7 @@ ast_channel_inherit_variables(caller, outbound); ast_channel_datastore_inherit(caller, outbound); ast_channel_language_set(outbound, ast_channel_language(caller)); - ast_channel_accountcode_set(outbound, ast_channel_accountcode(caller)); + ast_channel_req_accountcodes(outbound, caller, AST_CHANNEL_REQUESTOR_BRIDGE_PEER); ast_channel_musicclass_set(outbound, ast_channel_musicclass(caller)); ast_channel_unlock(outbound); ast_channel_unlock(caller); @@ -1103,11 +1104,6 @@ * Destoy all new outgoing calls. */ while ((tmpuser = AST_LIST_REMOVE_HEAD(&new_user_list, entry))) { - ast_channel_lock(tmpuser->ochan); - if (ast_channel_cdr(tmpuser->ochan)) { - ast_cdr_init(ast_channel_cdr(tmpuser->ochan), tmpuser->ochan); - } - ast_channel_unlock(tmpuser->ochan); destroy_calling_node(tmpuser); } @@ -1129,13 +1125,11 @@ AST_LIST_REMOVE_CURRENT(entry); /* Destroy this failed new outgoing call. */ - ast_channel_lock(tmpuser->ochan); - if (ast_channel_cdr(tmpuser->ochan)) { - ast_cdr_init(ast_channel_cdr(tmpuser->ochan), tmpuser->ochan); - } - ast_channel_unlock(tmpuser->ochan); destroy_calling_node(tmpuser); + continue; } + + ast_channel_publish_dial(caller, tmpuser->ochan, tmpuser->dialarg, NULL); } AST_LIST_TRAVERSE_SAFE_END; @@ -1283,15 +1277,10 @@ time(&end); ast_channel_lock(chan); - if (ast_channel_cdr(chan)->answer.tv_sec) { - snprintf(buf, sizeof(buf), "%ld", (long) end - ast_channel_cdr(chan)->answer.tv_sec); - pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", buf); - } - - if (ast_channel_cdr(chan)->start.tv_sec) { - snprintf(buf, sizeof(buf), "%ld", (long) end - ast_channel_cdr(chan)->start.tv_sec); - pbx_builtin_setvar_helper(chan, "DIALEDTIME", buf); - } + snprintf(buf, sizeof(buf), "%d", ast_channel_get_up_time(chan)); + pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", buf); + snprintf(buf, sizeof(buf), "%d", ast_channel_get_duration(chan)); + pbx_builtin_setvar_helper(chan, "DIALEDTIME", buf); ast_channel_unlock(chan); } @@ -1435,7 +1424,7 @@ if (ast_waitstream(chan, "") < 0) goto outrun; } - ast_moh_start(chan, S_OR(targs->mohclass, NULL), NULL); + ast_moh_start(chan, targs->mohclass, NULL); } ast_channel_lock(chan); @@ -1513,7 +1502,6 @@ } res = ast_bridge_call(caller, outbound, &config); - ast_autoservice_chan_hangup_peer(caller, outbound); } outrun: @@ -1555,6 +1543,16 @@ return 0; } +/*! + * \brief Load the module + * + * Module loading including tests for configuration or dependencies. + * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE, + * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails + * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the + * configuration file or other non-critical problem return + * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS. + */ static int load_module(void) { if(!reload_followme(0)) @@ -1571,6 +1569,7 @@ } AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Find-Me/Follow-Me Application", + .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .reload = reload, diff -Nru asterisk-11.7.0~dfsg/apps/app_forkcdr.c asterisk-13.1.0~dfsg/apps/app_forkcdr.c --- asterisk-11.7.0~dfsg/apps/app_forkcdr.c 2012-04-13 16:12:17.000000000 +0000 +++ asterisk-13.1.0~dfsg/apps/app_forkcdr.c 2014-08-06 12:55:28.000000000 +0000 @@ -32,7 +32,7 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 362085 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420124 $") #include "asterisk/file.h" #include "asterisk/channel.h" @@ -40,102 +40,52 @@ #include "asterisk/cdr.h" #include "asterisk/app.h" #include "asterisk/module.h" +#include "asterisk/stasis.h" +#include "asterisk/stasis_message_router.h" /*** DOCUMENTATION - Forks the Call Data Record. + Forks the current Call Data Record for this channel. - - - - - - Causes the Call Data Record to fork an additional cdr record starting from the time - of the fork call. This new cdr record will be linked to end of the list of cdr records attached - to the channel. The original CDR has a LOCKED flag set, which forces most cdr operations to skip - it, except for the functions that set the answer and end times, which ignore the LOCKED flag. This - allows all the cdr records in the channel to be 'ended' together when the channel is closed. - The CDR() func (when setting CDR values) normally ignores the LOCKED flag also, but has options - to vary its behavior. The 'T' option (described below), can override this behavior, but beware - the risks. - First, this app finds the last cdr record in the list, and makes a copy of it. This new copy - will be the newly forked cdr record. Next, this new record is linked to the end of the cdr record list. - Next, The new cdr record is RESET (unless you use an option to prevent this) - This means that: - 1. All flags are unset on the cdr record - 2. the start, end, and answer times are all set to zero. - 3. the billsec and duration fields are set to zero. - 4. the start time is set to the current time. - 5. the disposition is set to NULL. - Next, unless you specified the v option, all variables will be removed from - the original cdr record. Thus, the v option allows any CDR variables to be replicated - to all new forked cdr records. Without the v option, the variables on the original - are effectively moved to the new forked cdr record. - Next, if the s option is set, the provided variable and value are set on the - original cdr record. - Next, if the a option is given, and the original cdr record has an answer time - set, then the new forked cdr record will have its answer time set to its start time. If the old answer - time were carried forward, the answer time would be earlier than the start time, giving strange - duration and billsec times. - If the d option was specified, the disposition is copied from - the original cdr record to the new forked cdr. If the D option was specified, - the destination channel field in the new forked CDR is erased. If the e option - was specified, the 'end' time for the original cdr record is set to the current time. Future hang-up or - ending events will not override this time stamp. If the A option is specified, - the original cdr record will have it ANS_LOCKED flag set, which prevent future answer events from updating - the original cdr record's disposition. Normally, an ANSWERED event would mark all cdr - records in the chain as ANSWERED. If the T option is specified, - the original cdr record will have its DONT_TOUCH flag set, which will force the - cdr_answer, cdr_end, and cdr_setvar functions to leave that cdr record alone. - And, last but not least, the original cdr record has its LOCKED flag set. Almost all internal - CDR functions (except for the funcs that set the end, and answer times, and set a variable) will honor - this flag and leave a LOCKED cdr record alone. This means that the newly created forked cdr record - will be affected by events transpiring within Asterisk, with the previously noted exceptions. + Causes the Call Data Record engine to fork a new CDR starting + from the time the application is executed. The forked CDR will be + linked to the end of the CDRs associated with the channel. CDR @@ -147,136 +97,122 @@ static char *app = "ForkCDR"; -enum { - OPT_SETANS = (1 << 0), - OPT_SETDISP = (1 << 1), - OPT_RESETDEST = (1 << 2), - OPT_ENDCDR = (1 << 3), - OPT_NORESET = (1 << 4), - OPT_KEEPVARS = (1 << 5), - OPT_VARSET = (1 << 6), - OPT_ANSLOCK = (1 << 7), - OPT_DONTOUCH = (1 << 8), -}; - -enum { - OPT_ARG_VARSET = 0, - /* note: this entry _MUST_ be the last one in the enum */ - OPT_ARG_ARRAY_SIZE, -}; - AST_APP_OPTIONS(forkcdr_exec_options, { - AST_APP_OPTION('a', OPT_SETANS), - AST_APP_OPTION('A', OPT_ANSLOCK), - AST_APP_OPTION('d', OPT_SETDISP), - AST_APP_OPTION('D', OPT_RESETDEST), - AST_APP_OPTION('e', OPT_ENDCDR), - AST_APP_OPTION('R', OPT_NORESET), - AST_APP_OPTION_ARG('s', OPT_VARSET, OPT_ARG_VARSET), - AST_APP_OPTION('T', OPT_DONTOUCH), - AST_APP_OPTION('v', OPT_KEEPVARS), + AST_APP_OPTION('a', AST_CDR_FLAG_SET_ANSWER), + AST_APP_OPTION('e', AST_CDR_FLAG_FINALIZE), + AST_APP_OPTION('r', AST_CDR_FLAG_RESET), + AST_APP_OPTION('v', AST_CDR_FLAG_KEEP_VARS), }); -static void ast_cdr_fork(struct ast_channel *chan, struct ast_flags optflags, char *set) +STASIS_MESSAGE_TYPE_DEFN_LOCAL(forkcdr_message_type); + +/*! \internal \brief Message payload for the Stasis message sent to fork the CDR */ +struct fork_cdr_message_payload { + /*! The name of the channel whose CDR will be forked */ + const char *channel_name; + /*! Option flags that control how the CDR will be forked */ + struct ast_flags *flags; +}; + +static void forkcdr_callback(void *data, struct stasis_subscription *sub, struct stasis_message *message) { - struct ast_cdr *cdr; - struct ast_cdr *newcdr; - struct ast_flags flags = { AST_CDR_FLAG_KEEP_VARS }; - - cdr = ast_channel_cdr(chan); - - while (cdr->next) - cdr = cdr->next; - - if (!(newcdr = ast_cdr_dup_unique(cdr))) + struct fork_cdr_message_payload *payload; + + if (stasis_message_type(message) != forkcdr_message_type()) { return; - - /* - * End the original CDR if requested BEFORE appending the new CDR - * otherwise we incorrectly end the new CDR also. - */ - if (ast_test_flag(&optflags, OPT_ENDCDR)) { - ast_cdr_end(cdr); - } - - ast_cdr_append(cdr, newcdr); - - if (!ast_test_flag(&optflags, OPT_NORESET)) - ast_cdr_reset(newcdr, &flags); - - if (!ast_test_flag(cdr, AST_CDR_FLAG_KEEP_VARS)) - ast_cdr_free_vars(cdr, 0); - - if (!ast_strlen_zero(set)) { - char *varname = ast_strdupa(set), *varval; - varval = strchr(varname,'='); - if (varval) { - *varval = 0; - varval++; - ast_cdr_setvar(cdr, varname, varval, 0); - } - } - - if (ast_test_flag(&optflags, OPT_SETANS) && !ast_tvzero(cdr->answer)) - newcdr->answer = newcdr->start; - - if (ast_test_flag(&optflags, OPT_SETDISP)) - newcdr->disposition = cdr->disposition; - - if (ast_test_flag(&optflags, OPT_RESETDEST)) - newcdr->dstchannel[0] = 0; - - if (ast_test_flag(&optflags, OPT_ANSLOCK)) - ast_set_flag(cdr, AST_CDR_FLAG_ANSLOCKED); - - if (ast_test_flag(&optflags, OPT_DONTOUCH)) - ast_set_flag(cdr, AST_CDR_FLAG_DONT_TOUCH); - - ast_set_flag(cdr, AST_CDR_FLAG_CHILD | AST_CDR_FLAG_LOCKED); + } + + payload = stasis_message_data(message); + if (!payload) { + return; + } + + if (ast_cdr_fork(payload->channel_name, payload->flags)) { + ast_log(AST_LOG_WARNING, "Failed to fork CDR for channel %s\n", + payload->channel_name); + } } static int forkcdr_exec(struct ast_channel *chan, const char *data) { - int res = 0; - char *argcopy = NULL; - struct ast_flags flags = {0}; - char *opts[OPT_ARG_ARRAY_SIZE]; - AST_DECLARE_APP_ARGS(arglist, + RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup); + RAII_VAR(struct fork_cdr_message_payload *, payload, NULL, ao2_cleanup); + RAII_VAR(struct stasis_message_router *, router, ast_cdr_message_router(), ao2_cleanup); + + char *parse; + struct ast_flags flags = { 0, }; + AST_DECLARE_APP_ARGS(args, AST_APP_ARG(options); ); - if (!ast_channel_cdr(chan)) { - ast_log(LOG_WARNING, "Channel does not have a CDR\n"); - return 0; - } + parse = ast_strdupa(data); - argcopy = ast_strdupa(data); + AST_STANDARD_APP_ARGS(args, parse); - AST_STANDARD_APP_ARGS(arglist, argcopy); + if (!ast_strlen_zero(args.options)) { + ast_app_parse_options(forkcdr_exec_options, &flags, NULL, args.options); + } - opts[OPT_ARG_VARSET] = 0; + if (!forkcdr_message_type()) { + ast_log(AST_LOG_WARNING, "Failed to manipulate CDR for channel %s: no message type\n", + ast_channel_name(chan)); + return -1; + } + + payload = ao2_alloc(sizeof(*payload), NULL); + if (!payload) { + return -1; + } - if (!ast_strlen_zero(arglist.options)) - ast_app_parse_options(forkcdr_exec_options, &flags, opts, arglist.options); + if (!router) { + ast_log(AST_LOG_WARNING, "Failed to manipulate CDR for channel %s: no message router\n", + ast_channel_name(chan)); + return -1; + } - if (!ast_strlen_zero(data)) { - int keepvars = ast_test_flag(&flags, OPT_KEEPVARS) ? 1 : 0; - ast_set2_flag(ast_channel_cdr(chan), keepvars, AST_CDR_FLAG_KEEP_VARS); + payload->channel_name = ast_channel_name(chan); + payload->flags = &flags; + message = stasis_message_create(forkcdr_message_type(), payload); + if (!message) { + ast_log(AST_LOG_WARNING, "Failed to fork CDR for channel %s: unable to create message\n", + ast_channel_name(chan)); + return -1; } - - ast_cdr_fork(chan, flags, opts[OPT_ARG_VARSET]); + stasis_message_router_publish_sync(router, message); - return res; + return 0; } static int unload_module(void) { - return ast_unregister_application(app); + RAII_VAR(struct stasis_message_router *, router, ast_cdr_message_router(), ao2_cleanup); + + if (router) { + stasis_message_router_remove(router, forkcdr_message_type()); + } + STASIS_MESSAGE_TYPE_CLEANUP(forkcdr_message_type); + ast_unregister_application(app); + return 0; } static int load_module(void) { - return ast_register_application_xml(app, forkcdr_exec); + RAII_VAR(struct stasis_message_router *, router, ast_cdr_message_router(), ao2_cleanup); + int res = 0; + + if (!router) { + return AST_MODULE_LOAD_FAILURE; + } + + res |= STASIS_MESSAGE_TYPE_INIT(forkcdr_message_type); + res |= ast_register_application_xml(app, forkcdr_exec); + res |= stasis_message_router_add(router, forkcdr_message_type(), + forkcdr_callback, NULL); + + if (res) { + return AST_MODULE_LOAD_FAILURE; + } + return AST_MODULE_LOAD_SUCCESS; } AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Fork The CDR into 2 separate entities"); diff -Nru asterisk-11.7.0~dfsg/apps/app_getcpeid.c asterisk-13.1.0~dfsg/apps/app_getcpeid.c --- asterisk-11.7.0~dfsg/apps/app_getcpeid.c 2012-07-31 20:21:43.000000000 +0000 +++ asterisk-13.1.0~dfsg/apps/app_getcpeid.c 2014-07-25 16:47:17.000000000 +0000 @@ -31,7 +31,7 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 370655 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $") #include "asterisk/lock.h" #include "asterisk/file.h" @@ -87,7 +87,9 @@ res = ast_adsi_get_cpeid(chan, cpeid, 0); if (res > 0) { gotcpeid = 1; - ast_verb(3, "Got CPEID of '%02x:%02x:%02x:%02x' on '%s'\n", cpeid[0], cpeid[1], cpeid[2], cpeid[3], ast_channel_name(chan)); + ast_verb(3, "Got CPEID of '%02x:%02x:%02x:%02x' on '%s'\n", + (unsigned)cpeid[0], (unsigned)cpeid[1], (unsigned)cpeid[2], + (unsigned)cpeid[3], ast_channel_name(chan)); } if (res > -1) { strcpy(data[1], "Measuring CPE..."); @@ -101,7 +103,9 @@ } if (res > -1) { if (gotcpeid) - snprintf(data[1], 80, "CPEID: %02x:%02x:%02x:%02x", cpeid[0], cpeid[1], cpeid[2], cpeid[3]); + snprintf(data[1], 80, "CPEID: %02x:%02x:%02x:%02x", + (unsigned)cpeid[0], (unsigned)cpeid[1], + (unsigned)cpeid[2], (unsigned)cpeid[3]); else strcpy(data[1], "CPEID Unknown"); if (gotgeometry) @@ -137,6 +141,7 @@ } AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Get ADSI CPE ID", + .support_level = AST_MODULE_SUPPORT_EXTENDED, .load = load_module, .unload = unload_module, .nonoptreq = "res_adsi", diff -Nru asterisk-11.7.0~dfsg/apps/app_ices.c asterisk-13.1.0~dfsg/apps/app_ices.c --- asterisk-11.7.0~dfsg/apps/app_ices.c 2012-04-06 18:19:03.000000000 +0000 +++ asterisk-13.1.0~dfsg/apps/app_ices.c 2014-07-25 16:47:17.000000000 +0000 @@ -22,7 +22,7 @@ * * \author Mark Spencer * - * \extref ICES - http://www.icecast.org/ices.php + * ICES - http://www.icecast.org/ices.php * * \ingroup applications */ @@ -33,7 +33,7 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 361476 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $") #include #include @@ -48,6 +48,7 @@ #include "asterisk/module.h" #include "asterisk/translate.h" #include "asterisk/app.h" +#include "asterisk/format_cache.h" /*** DOCUMENTATION @@ -115,12 +116,11 @@ int ms = -1; int pid = -1; int flags; - struct ast_format oreadformat; + struct ast_format *oreadformat; struct ast_frame *f; char filename[256]=""; char *c; - ast_format_clear(&oreadformat); if (ast_strlen_zero(data)) { ast_log(LOG_WARNING, "ICES requires an argument (configfile.xml)\n"); return -1; @@ -145,12 +145,13 @@ return -1; } - ast_format_copy(&oreadformat, ast_channel_readformat(chan)); - res = ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR); + oreadformat = ao2_bump(ast_channel_readformat(chan)); + res = ast_set_read_format(chan, ast_format_slin); if (res < 0) { close(fds[0]); close(fds[1]); ast_log(LOG_WARNING, "Unable to set write format to signed linear\n"); + ao2_cleanup(oreadformat); return -1; } if (((char *)data)[0] == '/') @@ -197,8 +198,9 @@ if (pid > -1) kill(pid, SIGKILL); - if (!res && oreadformat.id) - ast_set_read_format(chan, &oreadformat); + if (!res && oreadformat) + ast_set_read_format(chan, oreadformat); + ao2_cleanup(oreadformat); return res; } @@ -213,4 +215,5 @@ return ast_register_application_xml(app, ices_exec); } -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Encode and Stream via icecast and ices"); +AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Encode and Stream via icecast and ices"); + diff -Nru asterisk-11.7.0~dfsg/apps/app_image.c asterisk-13.1.0~dfsg/apps/app_image.c --- asterisk-11.7.0~dfsg/apps/app_image.c 2011-07-14 20:28:54.000000000 +0000 +++ asterisk-13.1.0~dfsg/apps/app_image.c 2014-07-25 16:47:17.000000000 +0000 @@ -31,7 +31,7 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 328259 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $") #include "asterisk/pbx.h" #include "asterisk/module.h" @@ -106,4 +106,5 @@ return ast_register_application_xml(app, sendimage_exec); } -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Image Transmission Application"); +AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Image Transmission Application"); + diff -Nru asterisk-11.7.0~dfsg/apps/app_ivrdemo.c asterisk-13.1.0~dfsg/apps/app_ivrdemo.c --- asterisk-11.7.0~dfsg/apps/app_ivrdemo.c 2012-02-20 23:43:27.000000000 +0000 +++ asterisk-13.1.0~dfsg/apps/app_ivrdemo.c 2014-07-25 16:47:17.000000000 +0000 @@ -32,7 +32,7 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 356042 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $") #include "asterisk/file.h" #include "asterisk/channel.h" @@ -126,4 +126,5 @@ return ast_register_application_xml(app, skel_exec); } -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "IVR Demo Application"); +AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "IVR Demo Application"); + diff -Nru asterisk-11.7.0~dfsg/apps/app_jack.c asterisk-13.1.0~dfsg/apps/app_jack.c --- asterisk-11.7.0~dfsg/apps/app_jack.c 2012-11-07 19:03:42.000000000 +0000 +++ asterisk-13.1.0~dfsg/apps/app_jack.c 2014-07-25 16:47:17.000000000 +0000 @@ -42,7 +42,7 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 376014 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $") #include @@ -58,10 +58,12 @@ #include "asterisk/app.h" #include "asterisk/pbx.h" #include "asterisk/audiohook.h" +#include "asterisk/format_cache.h" #define RESAMPLE_QUALITY 1 -#define RINGBUFFER_SIZE 16384 +/* The number of frames the ringbuffers can store. The actual size is RINGBUFFER_FRAME_CAPACITY * jack_data->frame_datalen */ +#define RINGBUFFER_FRAME_CAPACITY 100 /*! \brief Common options between the Jack() app and JACK_HOOK() function */ #define COMMON_OPTIONS \ @@ -128,6 +130,9 @@ jack_port_t *output_port; jack_ringbuffer_t *input_rb; jack_ringbuffer_t *output_rb; + struct ast_format *audiohook_format; + unsigned int audiohook_rate; + unsigned int frame_datalen; void *output_resampler; double output_resample_factor; void *input_resampler; @@ -201,10 +206,8 @@ jack_srate = jack_get_sample_rate(jack_data->client); - /* XXX Hard coded 8 kHz */ - - to_srate = input ? 8000.0 : jack_srate; - from_srate = input ? jack_srate : 8000.0; + to_srate = input ? jack_data->audiohook_rate : jack_srate; + from_srate = input ? jack_srate : jack_data->audiohook_rate; resample_factor = input ? &jack_data->input_resample_factor : &jack_data->output_resample_factor; @@ -289,7 +292,7 @@ res = jack_ringbuffer_write(jack_data->input_rb, (const char *) s_buf, write_len); if (res != write_len) { - ast_debug(2, "Tried to write %d bytes to the ringbuffer, but only wrote %d\n", + ast_log(LOG_WARNING, "Tried to write %d bytes to the ringbuffer, but only wrote %d\n", (int) sizeof(s_buf), (int) res); } } @@ -392,6 +395,25 @@ jack_status_t status = 0; jack_options_t jack_options = JackNullOption; + unsigned int channel_rate; + + unsigned int ringbuffer_size; + + /* Deducing audiohook sample rate from channel format + ATTENTION: Might be problematic, if channel has different sampling than used by audiohook! + */ + channel_rate = ast_format_get_sample_rate(ast_channel_readformat(chan)); + jack_data->audiohook_format = ast_format_cache_get_slin_by_rate(channel_rate); + jack_data->audiohook_rate = ast_format_get_sample_rate(jack_data->audiohook_format); + + /* Guessing frame->datalen assuming a ptime of 20ms */ + jack_data->frame_datalen = jack_data->audiohook_rate / 50; + + ringbuffer_size = jack_data->frame_datalen * RINGBUFFER_FRAME_CAPACITY; + + ast_debug(1, "Audiohook parameters: slin-format:%s, rate:%d, frame-len:%d, ringbuffer_size: %d\n", + ast_format_get_name(jack_data->audiohook_format), jack_data->audiohook_rate, jack_data->frame_datalen, ringbuffer_size); + if (!ast_strlen_zero(jack_data->client_name)) { client_name = jack_data->client_name; } else { @@ -400,10 +422,10 @@ ast_channel_unlock(chan); } - if (!(jack_data->output_rb = jack_ringbuffer_create(RINGBUFFER_SIZE))) + if (!(jack_data->output_rb = jack_ringbuffer_create(ringbuffer_size))) return -1; - if (!(jack_data->input_rb = jack_ringbuffer_create(RINGBUFFER_SIZE))) + if (!(jack_data->input_rb = jack_ringbuffer_create(ringbuffer_size))) return -1; if (jack_data->no_start_server) @@ -573,10 +595,9 @@ res = jack_ringbuffer_write(jack_data->output_rb, (const char *) f_buf, f_buf_used * sizeof(float)); if (res != (f_buf_used * sizeof(float))) { - ast_debug(2, "Tried to write %d bytes to the ringbuffer, but only wrote %d\n", + ast_log(LOG_WARNING, "Tried to write %d bytes to the ringbuffer, but only wrote %d\n", (int) (f_buf_used * sizeof(float)), (int) res); } - return 0; } @@ -602,15 +623,15 @@ static void handle_jack_audio(struct ast_channel *chan, struct jack_data *jack_data, struct ast_frame *out_frame) { - short buf[160]; + short buf[jack_data->frame_datalen]; struct ast_frame f = { .frametype = AST_FRAME_VOICE, + .subclass.format = jack_data->audiohook_format, .src = "JACK", .data.ptr = buf, .datalen = sizeof(buf), .samples = ARRAY_LEN(buf), }; - ast_format_set(&f.subclass.format, AST_FORMAT_SLINEAR, 0); for (;;) { size_t res, read_len; @@ -755,12 +776,12 @@ return -1; } - if (ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR)) { + if (ast_set_read_format(chan, jack_data->audiohook_format)) { destroy_jack_data(jack_data); return -1; } - if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR)) { + if (ast_set_write_format(chan, jack_data->audiohook_format)) { destroy_jack_data(jack_data); return -1; } @@ -826,12 +847,6 @@ if (frame->frametype != AST_FRAME_VOICE) return 0; - if (frame->subclass.format.id != AST_FORMAT_SLINEAR) { - ast_log(LOG_WARNING, "Expected frame in SLINEAR for the audiohook, but got format %s\n", - ast_getformatname(&frame->subclass.format)); - return 0; - } - ast_channel_lock(chan); if (!(datastore = ast_channel_datastore_find(chan, &jack_hook_ds_info, NULL))) { @@ -842,6 +857,14 @@ jack_data = datastore->data; + if (ast_format_cmp(frame->subclass.format, jack_data->audiohook_format) == AST_FORMAT_CMP_NOT_EQUAL) { + ast_log(LOG_WARNING, "Expected frame in %s for the audiohook, but got format %s\n", + ast_format_get_name(jack_data->audiohook_format), + ast_format_get_name(frame->subclass.format)); + ast_channel_unlock(chan); + return 0; + } + queue_voice_frame(jack_data, frame); handle_jack_audio(chan, jack_data, frame); @@ -888,7 +911,7 @@ goto return_error; jack_data->has_audiohook = 1; - ast_audiohook_init(&jack_data->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "JACK_HOOK", 0); + ast_audiohook_init(&jack_data->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "JACK_HOOK", AST_AUDIOHOOK_MANIPULATE_ALL_RATES); jack_data->audiohook.manipulate_callback = jack_hook_callback; datastore->data = jack_data; @@ -951,6 +974,11 @@ { int res; + if (!chan) { + ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd); + return -1; + } + if (!strcasecmp(value, "on")) res = enable_jack_hook(chan, data); else if (!strcasecmp(value, "off")) @@ -1023,4 +1051,5 @@ return AST_MODULE_LOAD_SUCCESS; } -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "JACK Interface"); +AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "JACK Interface"); + diff -Nru asterisk-11.7.0~dfsg/apps/app_macro.c asterisk-13.1.0~dfsg/apps/app_macro.c --- asterisk-11.7.0~dfsg/apps/app_macro.c 2012-07-31 20:21:43.000000000 +0000 +++ asterisk-13.1.0~dfsg/apps/app_macro.c 2014-09-05 22:03:45.000000000 +0000 @@ -32,7 +32,7 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 370655 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 422719 $") #include "asterisk/file.h" #include "asterisk/channel.h" @@ -248,6 +248,7 @@ char *save_macro_context; char *save_macro_priority; char *save_macro_offset; + int save_in_subroutine; struct ast_datastore *macro_store = ast_channel_datastore_find(chan, ¯o_ds_info, NULL); if (ast_strlen_zero(data)) { @@ -329,6 +330,7 @@ } /* Save old info */ + ast_channel_lock(chan); oldpriority = ast_channel_priority(chan); ast_copy_string(oldexten, ast_channel_exten(chan), sizeof(oldexten)); ast_copy_string(oldcontext, ast_channel_context(chan), sizeof(oldcontext)); @@ -355,12 +357,14 @@ pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc); + save_in_subroutine = ast_test_flag(ast_channel_flags(chan), AST_FLAG_SUBROUTINE_EXEC); + ast_set_flag(ast_channel_flags(chan), AST_FLAG_SUBROUTINE_EXEC); + /* Setup environment for new run */ ast_channel_exten_set(chan, "s"); ast_channel_context_set(chan, fullmacro); ast_channel_priority_set(chan, 1); - ast_channel_lock(chan); while((cur = strsep(&rest, ",")) && (argc < MAX_ARGS)) { const char *argp; /* Save copy of old arguments if we're overwriting some, otherwise @@ -513,6 +517,7 @@ snprintf(depthc, sizeof(depthc), "%d", depth); pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc); ast_set2_flag(ast_channel_flags(chan), autoloopflag, AST_FLAG_IN_AUTOLOOP); + ast_set2_flag(ast_channel_flags(chan), save_in_subroutine, AST_FLAG_SUBROUTINE_EXEC); for (x = 1; x < argc; x++) { /* Restore old arguments and delete ours */ diff -Nru asterisk-11.7.0~dfsg/apps/app_meetme.c asterisk-13.1.0~dfsg/apps/app_meetme.c --- asterisk-11.7.0~dfsg/apps/app_meetme.c 2013-09-13 13:48:34.000000000 +0000 +++ asterisk-13.1.0~dfsg/apps/app_meetme.c 2014-12-06 17:27:22.000000000 +0000 @@ -29,6 +29,15 @@ * \ingroup applications */ +/*! \li \ref app_meetme.c uses configuration file \ref meetme.conf + * \addtogroup configuration_file Configuration Files + */ + +/*! + * \page meetme.conf meetme.conf + * \verbinclude meetme.conf.sample + */ + /*** MODULEINFO dahdi no @@ -38,7 +47,7 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 399034 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 429029 $") #include @@ -64,6 +73,11 @@ #include "asterisk/paths.h" #include "asterisk/data.h" #include "asterisk/test.h" +#include "asterisk/stasis.h" +#include "asterisk/stasis_channels.h" +#include "asterisk/stasis_message_router.h" +#include "asterisk/json.h" +#include "asterisk/format_compatibility.h" #include "enter.h" #include "leave.h" @@ -544,6 +558,88 @@ MeetmeListRoomsComplete. + + + Raised when a user joins a MeetMe conference. + + + The identifier for the MeetMe conference. + + + The identifier of the MeetMe user who joined. + + + + + MeetmeLeave + MeetMe + + + + + + Raised when a user leaves a MeetMe conference. + + + + + The length of time in seconds that the Meetme user was in the conference. + + + + MeetmeJoin + + + + + + Raised when a MeetMe conference ends. + + + + + MeetmeJoin + + + + + + Raised when a MeetMe user has started talking. + + + + + The length of time in seconds that the Meetme user has been in the conference at the time of this event. + + + + + + + + + + + + + Raised when a MeetMe user begins or ends talking. + + + + + + + + + + Raised when a MeetMe user is muted or unmuted. + + + + + + + ***/ #define CONFIG_FILE_NAME "meetme.conf" @@ -1033,6 +1129,271 @@ 15, }; +/* Routes the various meetme message types to the meetme stasis callback function to turn them into events */ +static struct stasis_message_router *meetme_event_message_router; + +STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_join_type); +STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_leave_type); +STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_end_type); +STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_mute_type); +STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_talking_type); +STASIS_MESSAGE_TYPE_DEFN_LOCAL(meetme_talk_request_type); + +static void meetme_stasis_cb(void *data, struct stasis_subscription *sub, + struct stasis_message *message); + +static void meetme_stasis_cleanup(void) +{ + if (meetme_event_message_router) { + stasis_message_router_unsubscribe(meetme_event_message_router); + meetme_event_message_router = NULL; + } + + STASIS_MESSAGE_TYPE_CLEANUP(meetme_join_type); + STASIS_MESSAGE_TYPE_CLEANUP(meetme_leave_type); + STASIS_MESSAGE_TYPE_CLEANUP(meetme_end_type); + STASIS_MESSAGE_TYPE_CLEANUP(meetme_mute_type); + STASIS_MESSAGE_TYPE_CLEANUP(meetme_talking_type); + STASIS_MESSAGE_TYPE_CLEANUP(meetme_talk_request_type); +} + +static int meetme_stasis_init(void) +{ + + STASIS_MESSAGE_TYPE_INIT(meetme_join_type); + STASIS_MESSAGE_TYPE_INIT(meetme_leave_type); + STASIS_MESSAGE_TYPE_INIT(meetme_end_type); + STASIS_MESSAGE_TYPE_INIT(meetme_mute_type); + STASIS_MESSAGE_TYPE_INIT(meetme_talking_type); + STASIS_MESSAGE_TYPE_INIT(meetme_talk_request_type); + + meetme_event_message_router = stasis_message_router_create( + ast_channel_topic_all_cached()); + + if (!meetme_event_message_router) { + meetme_stasis_cleanup(); + return -1; + } + + if (stasis_message_router_add(meetme_event_message_router, + meetme_join_type(), + meetme_stasis_cb, + NULL)) { + meetme_stasis_cleanup(); + return -1; + } + + if (stasis_message_router_add(meetme_event_message_router, + meetme_leave_type(), + meetme_stasis_cb, + NULL)) { + meetme_stasis_cleanup(); + return -1; + } + + if (stasis_message_router_add(meetme_event_message_router, + meetme_end_type(), + meetme_stasis_cb, + NULL)) { + meetme_stasis_cleanup(); + return -1; + } + + if (stasis_message_router_add(meetme_event_message_router, + meetme_mute_type(), + meetme_stasis_cb, + NULL)) { + meetme_stasis_cleanup(); + return -1; + } + + if (stasis_message_router_add(meetme_event_message_router, + meetme_talking_type(), + meetme_stasis_cb, + NULL)) { + meetme_stasis_cleanup(); + return -1; + } + + if (stasis_message_router_add(meetme_event_message_router, + meetme_talk_request_type(), + meetme_stasis_cb, + NULL)) { + meetme_stasis_cleanup(); + return -1; + } + + return 0; +} + +static void meetme_stasis_cb(void *data, struct stasis_subscription *sub, + struct stasis_message *message) +{ + struct ast_channel_blob *channel_blob = stasis_message_data(message); + struct stasis_message_type *message_type; + const char *event; + const char *conference_num; + const char *status; + struct ast_json *json_cur; + RAII_VAR(struct ast_str *, channel_text, NULL, ast_free); + RAII_VAR(struct ast_str *, extra_text, NULL, ast_free); + + if (!channel_blob) { + ast_assert(0); + return; + } + + message_type = stasis_message_type(message); + + if (!message_type) { + ast_assert(0); + return; + } + + if (message_type == meetme_join_type()) { + event = "MeetmeJoin"; + } else if (message_type == meetme_leave_type()) { + event = "MeetmeLeave"; + } else if (message_type == meetme_end_type()) { + event = "MeetmeEnd"; + } else if (message_type == meetme_mute_type()) { + event = "MeetmeMute"; + } else if (message_type == meetme_talking_type()) { + event = "MeetmeTalking"; + } else if (message_type == meetme_talk_request_type()) { + event = "MeetmeTalkRequest"; + } else { + ast_assert(0); + return; + } + + if (!event) { + ast_assert(0); + return; + } + + conference_num = ast_json_string_get(ast_json_object_get(channel_blob->blob, "Meetme")); + if (!conference_num) { + ast_assert(0); + return; + } + + status = ast_json_string_get(ast_json_object_get(channel_blob->blob, "status")); + if (status) { + ast_str_append_event_header(&extra_text, "Status", status); + } + + if (channel_blob->snapshot) { + channel_text = ast_manager_build_channel_state_string(channel_blob->snapshot); + } + + if ((json_cur = ast_json_object_get(channel_blob->blob, "user"))) { + int user_number = ast_json_integer_get(json_cur); + RAII_VAR(struct ast_str *, user_prop_str, ast_str_create(32), ast_free); + if (!user_prop_str) { + return; + } + + ast_str_set(&user_prop_str, 0, "%d", user_number); + ast_str_append_event_header(&extra_text, "User", ast_str_buffer(user_prop_str)); + + if ((json_cur = ast_json_object_get(channel_blob->blob, "duration"))) { + int duration = ast_json_integer_get(json_cur); + ast_str_set(&user_prop_str, 0, "%d", duration); + ast_str_append_event_header(&extra_text, "Duration", ast_str_buffer(user_prop_str)); + } + + json_cur = NULL; + } + + manager_event(EVENT_FLAG_CALL, event, + "Meetme: %s\r\n" + "%s" + "%s", + conference_num, + channel_text ? ast_str_buffer(channel_text) : "", + extra_text ? ast_str_buffer(extra_text) : ""); +} + +/*! + * \internal + * \brief Build a json object from a status value for inclusion in json extras for meetme_stasis_generate_msg + * \since 12.0.0 + * + * \param on if true, then status is on. Otherwise status is off + * \retval NULL on failure to allocate the JSON blob. + * \retval pointer to the JSON blob if successful. + */ +static struct ast_json *status_to_json(int on) +{ + struct ast_json *json_object = ast_json_pack("{s: s}", + "status", on ? "on" : "off"); + + return json_object; +} + +/*! + * \internal + * \brief Generate a stasis message associated with a meetme event + * \since 12.0.0 + * + * \param meetme_confere The conference responsible for generating this message + * \param chan The channel involved in the message (NULL allowed) + * \param user The conference user involved in the message (NULL allowed) + * \param message_type the type the stasis message being generated + * \param extras Additional json fields desired for inclusion + */ +static void meetme_stasis_generate_msg(struct ast_conference *meetme_conference, struct ast_channel *chan, + struct ast_conf_user *user, struct stasis_message_type *message_type, struct ast_json *extras) +{ + RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup); + RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref); + + json_object = ast_json_pack("{s: s}", + "Meetme", meetme_conference->confno); + + if (!json_object) { + return; + } + + if (extras) { + ast_json_object_update(json_object, extras); + } + + if (user) { + struct timeval now = ast_tvnow(); + long duration = (long)(now.tv_sec - user->jointime); + struct ast_json *json_user; + struct ast_json *json_user_duration; + + json_user = ast_json_integer_create(user->user_no); + if (!json_user || ast_json_object_set(json_object, "user", json_user)) { + return; + } + + if (duration > 0) { + json_user_duration = ast_json_integer_create(duration); + if (!json_user_duration + || ast_json_object_set(json_object, "duration", json_user_duration)) { + return; + } + } + } + + if (chan) { + ast_channel_lock(chan); + } + msg = ast_channel_blob_create(chan, message_type, json_object); + if (chan) { + ast_channel_unlock(chan); + } + + if (!msg) { + return; + } + + stasis_publish(ast_channel_topic(chan), msg); +} static int admin_exec(struct ast_channel *chan, const char *data); static void *recordthread(void *args); @@ -1240,6 +1601,7 @@ * \param dynamic Mark the newly created conference as dynamic * \param refcount How many references to mark on the conference * \param chan The asterisk channel + * \param test * * \return A pointer to the conference struct, or NULL if it wasn't found and * make or dynamic were not set. @@ -1251,8 +1613,7 @@ struct ast_conference *cnf; struct dahdi_confinfo dahdic = { 0, }; int confno_int = 0; - struct ast_format_cap *cap_slin = ast_format_cap_alloc_nolock(); - struct ast_format tmp_fmt; + struct ast_format_cap *cap_slin = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); AST_LIST_LOCK(&confs); @@ -1264,7 +1625,7 @@ if (cnf || (!make && !dynamic) || !cap_slin) goto cnfout; - ast_format_cap_add(cap_slin, ast_format_set(&tmp_fmt, AST_FORMAT_SLINEAR, 0)); + ast_format_cap_append(cap_slin, ast_format_slin, 0); /* Make a new one */ if (!(cnf = ast_calloc(1, sizeof(*cnf))) || !(cnf->usercontainer = ao2_container_alloc(1, NULL, user_no_cmp))) { @@ -1310,10 +1671,10 @@ cnf->dahdiconf = dahdic.confno; /* Setup a new channel for playback of audio files */ - cnf->chan = ast_request("DAHDI", cap_slin, chan, "pseudo", NULL); + cnf->chan = ast_request("DAHDI", cap_slin, NULL, chan, "pseudo", NULL); if (cnf->chan) { - ast_set_read_format_by_id(cnf->chan, AST_FORMAT_SLINEAR); - ast_set_write_format_by_id(cnf->chan, AST_FORMAT_SLINEAR); + ast_set_read_format(cnf->chan, ast_format_slin); + ast_set_write_format(cnf->chan, ast_format_slin); dahdic.chan = 0; dahdic.confno = cnf->dahdiconf; dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON; @@ -1349,7 +1710,7 @@ conf_map[confno_int] = 1; cnfout: - cap_slin = ast_format_cap_destroy(cap_slin); + ao2_cleanup(cap_slin); if (cnf) ast_atomic_fetchadd_int(&cnf->refcount, refcount); @@ -1965,20 +2326,10 @@ { int x; struct announce_listitem *item; - + AST_LIST_REMOVE(&confs, conf, list); - /*** DOCUMENTATION - - Raised when a MeetMe conference ends. - - - - - MeetmeJoin - - - ***/ - manager_event(EVENT_FLAG_CALL, "MeetmeEnd", "Meetme: %s\r\n", conf->confno); + + meetme_stasis_generate_msg(conf, NULL, NULL, meetme_end_type(), NULL); if (conf->recording == MEETME_RECORD_ACTIVE) { conf->recording = MEETME_RECORD_TERMINATE; @@ -2018,10 +2369,8 @@ if (conf->origframe) ast_frfree(conf->origframe); - if (conf->lchan) - ast_hangup(conf->lchan); - if (conf->chan) - ast_hangup(conf->chan); + ast_hangup(conf->lchan); + ast_hangup(conf->chan); if (conf->fd >= 0) close(conf->fd); if (conf->recordingfilename) { @@ -2325,30 +2674,8 @@ static void send_talking_event(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking) { - /*** DOCUMENTATION - - Raised when a MeetMe user begins or ends talking. - - - - - - - - - - - - ***/ - ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalking", - "Channel: %s\r\n" - "Uniqueid: %s\r\n" - "Meetme: %s\r\n" - "Usernum: %d\r\n" - "Status: %s\r\n", - ast_channel_name(chan), ast_channel_uniqueid(chan), - conf->confno, - user->user_no, talking ? "on" : "off"); + RAII_VAR(struct ast_json *, status_blob, status_to_json(talking), ast_json_unref); + meetme_stasis_generate_msg(conf, chan, user, meetme_talking_type(), status_blob); } static void set_user_talking(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking, int monitor) @@ -2629,9 +2956,11 @@ * \param chan ast_channel belonging to the user who called the menu * \param user which meetme conference user invoked the menu * \param recordingtmp character buffer which may hold the name of the conference recording file - * \param dahdic dahdi configuration info used by the main conference loop */ -static void meetme_menu_admin_extended(enum menu_modes *menu_mode, int *dtmf, struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan, struct ast_conf_user *user, char *recordingtmp, struct dahdi_confinfo *dahdic, struct ast_format_cap *cap_slin) +static void meetme_menu_admin_extended(enum menu_modes *menu_mode, int *dtmf, + struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan, + struct ast_conf_user *user, char *recordingtmp, int recordingtmp_size, + struct ast_format_cap *cap_slin) { int keepplaying; int playednamerec; @@ -2752,7 +3081,7 @@ } ast_channel_unlock(chan); if (!conf->recordingfilename) { - snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, ast_channel_uniqueid(chan)); + snprintf(recordingtmp, recordingtmp_size, "meetme-conf-rec-%s-%s", conf->confno, ast_channel_uniqueid(chan)); conf->recordingfilename = ast_strdup(recordingtmp); } if (!conf->recordingformat) { @@ -2763,13 +3092,15 @@ } ast_mutex_lock(&conf->recordthreadlock); - if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("DAHDI", cap_slin, chan, "pseudo", NULL)))) { - ast_set_read_format_by_id(conf->lchan, AST_FORMAT_SLINEAR); - ast_set_write_format_by_id(conf->lchan, AST_FORMAT_SLINEAR); - dahdic->chan = 0; - dahdic->confno = conf->dahdiconf; - dahdic->confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON; - if (ioctl(ast_channel_fd(conf->lchan, 0), DAHDI_SETCONF, dahdic)) { + if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("DAHDI", cap_slin, NULL, chan, "pseudo", NULL)))) { + struct dahdi_confinfo dahdic; + + ast_set_read_format(conf->lchan, ast_format_slin); + ast_set_write_format(conf->lchan, ast_format_slin); + dahdic.chan = 0; + dahdic.confno = conf->dahdiconf; + dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON; + if (ioctl(ast_channel_fd(conf->lchan, 0), DAHDI_SETCONF, &dahdic)) { ast_log(LOG_WARNING, "Error starting listen channel\n"); ast_hangup(conf->lchan); conf->lchan = NULL; @@ -2812,10 +3143,11 @@ * \param chan ast_channel belonging to the user who called the menu * \param user which meetme conference user invoked the menu * \param recordingtmp character buffer which may hold the name of the conference recording file - * \param dahdic dahdi configuration info used by the main conference loop */ - -static void meetme_menu(enum menu_modes *menu_mode, int *dtmf, struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan, struct ast_conf_user *user, char *recordingtmp, struct dahdi_confinfo *dahdic, struct ast_format_cap *cap_slin) +static void meetme_menu(enum menu_modes *menu_mode, int *dtmf, + struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan, + struct ast_conf_user *user, char *recordingtmp, int recordingtmp_size, + struct ast_format_cap *cap_slin) { switch (*menu_mode) { case MENU_DISABLED: @@ -2830,7 +3162,8 @@ break; } case MENU_ADMIN_EXTENDED: - meetme_menu_admin_extended(menu_mode, dtmf, conf, confflags, chan, user, recordingtmp, dahdic, cap_slin); + meetme_menu_admin_extended(menu_mode, dtmf, conf, confflags, chan, user, + recordingtmp, recordingtmp_size, cap_slin); break; } } @@ -2890,13 +3223,12 @@ int setusercount = 0; int confsilence = 0, totalsilence = 0; char *mailbox, *context; - struct ast_format_cap *cap_slin = ast_format_cap_alloc_nolock(); - struct ast_format tmpfmt; + struct ast_format_cap *cap_slin = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); if (!cap_slin) { goto conf_run_cleanup; } - ast_format_cap_add(cap_slin, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0)); + ast_format_cap_append(cap_slin, ast_format_slin, 0); if (!(user = ao2_alloc(sizeof(*user), NULL))) { goto conf_run_cleanup; @@ -2912,7 +3244,7 @@ if (ast_test_flag64(confflags, CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) { calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]); - ast_verb(3, "Setting call duration limit to %d seconds.\n", calldurationlimit); + ast_verb(3, "Setting call duration limit to %u seconds.\n", calldurationlimit); } if (ast_test_flag64(confflags, CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) { @@ -3017,9 +3349,9 @@ ast_mutex_lock(&conf->recordthreadlock); if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) && - ((conf->lchan = ast_request("DAHDI", cap_slin, chan, "pseudo", NULL)))) { - ast_set_read_format_by_id(conf->lchan, AST_FORMAT_SLINEAR); - ast_set_write_format_by_id(conf->lchan, AST_FORMAT_SLINEAR); + ((conf->lchan = ast_request("DAHDI", cap_slin, NULL, chan, "pseudo", NULL)))) { + ast_set_read_format(conf->lchan, ast_format_slin); + ast_set_write_format(conf->lchan, ast_format_slin); dahdic.chan = 0; dahdic.confno = conf->dahdiconf; dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON; @@ -3076,9 +3408,9 @@ if (rt_schedule && conf->maxusers) { if (conf->users >= conf->maxusers) { /* Sorry, but this confernce has reached the participant limit! */ + ast_mutex_unlock(&conf->playlock); if (!ast_streamfile(chan, "conf-full", ast_channel_language(chan))) ast_waitstream(chan, ""); - ast_mutex_unlock(&conf->playlock); goto outrun; } } @@ -3245,12 +3577,12 @@ ast_indicate(chan, -1); } - if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) { + if (ast_set_write_format(chan, ast_format_slin) < 0) { ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", ast_channel_name(chan)); goto outrun; } - if (ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) { + if (ast_set_read_format(chan, ast_format_slin) < 0) { ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", ast_channel_name(chan)); goto outrun; } @@ -3362,39 +3694,7 @@ ast_debug(1, "Placed channel %s in DAHDI conf %d\n", ast_channel_name(chan), conf->dahdiconf); if (!sent_event) { - /*** DOCUMENTATION - - Raised when a user joins a MeetMe conference. - - - The identifier for the MeetMe conference. - - - The identifier of the MeetMe user who joined. - - - - MeetmeLeave - MeetMe - - - ***/ - ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeJoin", - "Channel: %s\r\n" - "Uniqueid: %s\r\n" - "Meetme: %s\r\n" - "Usernum: %d\r\n" - "CallerIDnum: %s\r\n" - "CallerIDname: %s\r\n" - "ConnectedLineNum: %s\r\n" - "ConnectedLineName: %s\r\n", - ast_channel_name(chan), ast_channel_uniqueid(chan), conf->confno, - user->user_no, - S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, ""), - S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, ""), - S_COR(ast_channel_connected(user->chan)->id.number.valid, ast_channel_connected(user->chan)->id.number.str, ""), - S_COR(ast_channel_connected(user->chan)->id.name.valid, ast_channel_connected(user->chan)->id.name.str, "") - ); + meetme_stasis_generate_msg(conf, chan, user, meetme_join_type(), NULL); sent_event = 1; } @@ -3743,6 +4043,7 @@ /* If I should be muted but am still talker, mute me */ if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (dahdic.confmode & DAHDI_CONF_TALKER)) { + RAII_VAR(struct ast_json *, status_blob, status_to_json(1), ast_json_unref); dahdic.confmode ^= DAHDI_CONF_TALKER; if (ioctl(fd, DAHDI_SETCONF, &dahdic)) { ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n"); @@ -3754,95 +4055,34 @@ if (ast_test_flag64(confflags, (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER))) { set_user_talking(chan, conf, user, -1, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER)); } - /*** DOCUMENTATION - - Raised when a MeetMe user is muted. - - - - - - - - - - - - ***/ - ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeMute", - "Channel: %s\r\n" - "Uniqueid: %s\r\n" - "Meetme: %s\r\n" - "Usernum: %d\r\n" - "Status: on\r\n", - ast_channel_name(chan), ast_channel_uniqueid(chan), conf->confno, user->user_no); + meetme_stasis_generate_msg(conf, chan, user, meetme_mute_type(), status_blob); } /* If I should be un-muted but am not talker, un-mute me */ if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !ast_test_flag64(confflags, CONFFLAG_MONITOR) && !(dahdic.confmode & DAHDI_CONF_TALKER)) { + RAII_VAR(struct ast_json *, status_blob, status_to_json(0), ast_json_unref); dahdic.confmode |= DAHDI_CONF_TALKER; if (ioctl(fd, DAHDI_SETCONF, &dahdic)) { ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n"); ret = -1; break; } - /*** DOCUMENTATION - - Raised when a MeetMe user is unmuted. - - ***/ - ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeMute", - "Channel: %s\r\n" - "Uniqueid: %s\r\n" - "Meetme: %s\r\n" - "Usernum: %d\r\n" - "Status: off\r\n", - ast_channel_name(chan), ast_channel_uniqueid(chan), conf->confno, user->user_no); + meetme_stasis_generate_msg(conf, chan, user, meetme_mute_type(), status_blob); } - + if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (user->adminflags & ADMINFLAG_T_REQUEST) && !(talkreq_manager)) { - talkreq_manager = 1; - /*** DOCUMENTATION - - Raised when a MeetMe user has started talking. - - - - - - - - - - - - ***/ - ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalkRequest", - "Channel: %s\r\n" - "Uniqueid: %s\r\n" - "Meetme: %s\r\n" - "Usernum: %d\r\n" - "Status: on\r\n", - ast_channel_name(chan), ast_channel_uniqueid(chan), conf->confno, user->user_no); + RAII_VAR(struct ast_json *, status_blob, status_to_json(1), ast_json_unref); + talkreq_manager = 1; + meetme_stasis_generate_msg(conf, chan, user, meetme_talk_request_type(), status_blob); } if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !(user->adminflags & ADMINFLAG_T_REQUEST) && (talkreq_manager)) { + RAII_VAR(struct ast_json *, status_blob, status_to_json(0), ast_json_unref); talkreq_manager = 0; - /*** DOCUMENTATION - - Raised when a MeetMe user has finished talking. - - ***/ - ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalkRequest", - "Channel: %s\r\n" - "Uniqueid: %s\r\n" - "Meetme: %s\r\n" - "Usernum: %d\r\n" - "Status: off\r\n", - ast_channel_name(chan), ast_channel_uniqueid(chan), conf->confno, user->user_no); + meetme_stasis_generate_msg(conf, chan, user, meetme_talk_request_type(), status_blob); } /* If user have been hung up, exit the conference */ @@ -3896,7 +4136,7 @@ dtmfstr[1] = '\0'; } - if ((f->frametype == AST_FRAME_VOICE) && (f->subclass.format.id == AST_FORMAT_SLINEAR)) { + if ((f->frametype == AST_FRAME_VOICE) && (ast_format_cmp(f->subclass.format, ast_format_slin) == AST_FORMAT_CMP_EQUAL)) { if (user->talk.actual) { ast_frame_adjust_volume(f, user->talk.actual); } @@ -3936,6 +4176,7 @@ if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) { conf_queue_dtmf(conf, user, f); } + /* Take out of conference */ if (ioctl(fd, DAHDI_SETCONF, &dahdic_empty)) { ast_log(LOG_WARNING, "Error setting conference\n"); close(fd); @@ -3973,13 +4214,15 @@ } if (dtmf > 0) { - meetme_menu(&menu_mode, &dtmf, conf, confflags, chan, user, recordingtmp, &dahdic, cap_slin); + meetme_menu(&menu_mode, &dtmf, conf, confflags, + chan, user, recordingtmp, sizeof(recordingtmp), cap_slin); } if (musiconhold && !menu_mode) { conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]); } + /* Put back into conference */ if (ioctl(fd, DAHDI_SETCONF, &dahdic)) { ast_log(LOG_WARNING, "Error setting conference\n"); close(fd); @@ -4037,12 +4280,12 @@ break; default: ast_debug(1, - "Got ignored control frame on channel %s, f->frametype=%d,f->subclass=%d\n", + "Got ignored control frame on channel %s, f->frametype=%u,f->subclass=%d\n", ast_channel_name(chan), f->frametype, f->subclass.integer); } } else { ast_debug(1, - "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n", + "Got unrecognized frame on channel %s, f->frametype=%u,f->subclass=%d\n", ast_channel_name(chan), f->frametype, f->subclass.integer); } ast_frfree(f); @@ -4051,7 +4294,7 @@ if (res > 0) { memset(&fr, 0, sizeof(fr)); fr.frametype = AST_FRAME_VOICE; - ast_format_set(&fr.subclass.format, AST_FORMAT_SLINEAR, 0); + fr.subclass.format = ast_format_slin; fr.datalen = res; fr.samples = res / 2; fr.data.ptr = buf; @@ -4063,7 +4306,7 @@ )) { int idx; for (idx = 0; idx < AST_FRAME_BITS; idx++) { - if (ast_format_to_old_bitfield(ast_channel_rawwriteformat(chan)) & (1 << idx)) { + if (ast_format_compatibility_format2bitfield(ast_channel_rawwriteformat(chan)) & (1 << idx)) { break; } } @@ -4073,16 +4316,15 @@ ast_mutex_lock(&conf->listenlock); if (!conf->transframe[idx]) { if (conf->origframe) { - if (musiconhold && !ast_dsp_silence(dsp, conf->origframe, &confsilence) && confsilence < MEETME_DELAYDETECTTALK) { + if (musiconhold + && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED) + && !ast_dsp_silence(dsp, conf->origframe, &confsilence) + && confsilence < MEETME_DELAYDETECTTALK) { ast_moh_stop(chan); mohtempstopped = 1; } if (!conf->transpath[idx]) { - struct ast_format src; - struct ast_format dst; - ast_format_set(&src, AST_FORMAT_SLINEAR, 0); - ast_format_from_old_bitfield(&dst, (1 << idx)); - conf->transpath[idx] = ast_translator_build_path(&dst, &src); + conf->transpath[idx] = ast_translator_build_path(ast_channel_rawwriteformat(chan), ast_format_slin); } if (conf->transpath[idx]) { conf->transframe[idx] = ast_translate(conf->transpath[idx], conf->origframe, 0); @@ -4117,7 +4359,10 @@ ast_mutex_unlock(&conf->listenlock); } else { bailoutandtrynormal: - if (musiconhold && !ast_dsp_silence(dsp, &fr, &confsilence) && confsilence < MEETME_DELAYDETECTTALK) { + if (musiconhold + && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED) + && !ast_dsp_silence(dsp, &fr, &confsilence) + && confsilence < MEETME_DELAYDETECTTALK) { ast_moh_stop(chan); mohtempstopped = 1; } @@ -4196,38 +4441,7 @@ now = ast_tvnow(); if (sent_event) { - /*** DOCUMENTATION - - Raised when a user leaves a MeetMe conference. - - - - - The length of time in seconds that the Meetme user was in the conference. - - - - MeetmeJoin - - - ***/ - ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeLeave", - "Channel: %s\r\n" - "Uniqueid: %s\r\n" - "Meetme: %s\r\n" - "Usernum: %d\r\n" - "CallerIDNum: %s\r\n" - "CallerIDName: %s\r\n" - "ConnectedLineNum: %s\r\n" - "ConnectedLineName: %s\r\n" - "Duration: %ld\r\n", - ast_channel_name(chan), ast_channel_uniqueid(chan), conf->confno, - user->user_no, - S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, ""), - S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, ""), - S_COR(ast_channel_connected(user->chan)->id.number.valid, ast_channel_connected(user->chan)->id.number.str, ""), - S_COR(ast_channel_connected(user->chan)->id.name.valid, ast_channel_connected(user->chan)->id.name.str, ""), - (long)(now.tv_sec - user->jointime)); + meetme_stasis_generate_msg(conf, chan, user, meetme_leave_type(), NULL); } if (setusercount) { @@ -4272,7 +4486,7 @@ conf_run_cleanup: - cap_slin = ast_format_cap_destroy(cap_slin); + ao2_cleanup(cap_slin); return ret; } @@ -5556,12 +5770,29 @@ return AST_DEVICE_INUSE; } -static void load_config_meetme(void) +static void meetme_set_defaults(void) +{ + /* Scheduling support is off by default */ + rt_schedule = 0; + fuzzystart = 0; + earlyalert = 0; + endalert = 0; + extendby = 0; + + /* Logging of participants defaults to ON for compatibility reasons */ + rt_log_members = 1; +} + +static void load_config_meetme(int reload) { struct ast_config *cfg; struct ast_flags config_flags = { 0 }; const char *val; + if (!reload) { + meetme_set_defaults(); + } + if (!(cfg = ast_config_load(CONFIG_FILE_NAME, config_flags))) { return; } else if (cfg == CONFIG_STATUS_FILEINVALID) { @@ -5569,17 +5800,9 @@ return; } - audio_buffers = DEFAULT_AUDIO_BUFFERS; - - /* Scheduling support is off by default */ - rt_schedule = 0; - fuzzystart = 0; - earlyalert = 0; - endalert = 0; - extendby = 0; - - /* Logging of participants defaults to ON for compatibility reasons */ - rt_log_members = 1; + if (reload) { + meetme_set_defaults(); + } if ((val = ast_variable_retrieve(cfg, "general", "audiobuffers"))) { if ((sscanf(val, "%30d", &audio_buffers) != 1)) { @@ -6143,7 +6366,7 @@ tech_data = ast_strdupa(station->device); tech = strsep(&tech_data, "/"); - if (ast_dial_append(dial, tech, tech_data) == -1) { + if (ast_dial_append(dial, tech, tech_data, NULL) == -1) { ast_dial_destroy(dial); return -1; } @@ -6649,7 +6872,7 @@ tech_data = ast_strdupa(trunk_ref->trunk->device); tech = strsep(&tech_data, "/"); - if (ast_dial_append(dial, tech, tech_data) == -1) { + if (ast_dial_append(dial, tech, tech_data, NULL) == -1) { ast_mutex_lock(args->cond_lock); ast_cond_signal(args->cond); ast_mutex_unlock(args->cond_lock); @@ -6891,7 +7114,7 @@ ast_cond_destroy(&cond); ast_autoservice_stop(chan); if (!trunk_ref->trunk->chan) { - ast_debug(1, "Trunk didn't get created. chan: %lx\n", (long) trunk_ref->trunk->chan); + ast_debug(1, "Trunk didn't get created. chan: %lx\n", (unsigned long) trunk_ref->trunk->chan); pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION"); sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL); trunk_ref->chan = NULL; @@ -7786,7 +8009,7 @@ static int load_config(int reload) { - load_config_meetme(); + load_config_meetme(reload); return sla_load_config(reload); } @@ -7931,12 +8154,14 @@ break; } - chan = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, NULL, NULL, 0, 0, "MeetMeTest"); + chan = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, "MeetMeTest"); if (!chan) { ast_test_status_update(test, "Channel allocation failed\n"); return AST_TEST_FAIL; } + ast_channel_unlock(chan); + cnf = build_conf("9898", "", "1234", 1, 1, 1, chan, test); if (!cnf) { ast_test_status_update(test, "Build of test conference 9898 failed\n"); @@ -7997,15 +8222,29 @@ res |= ast_custom_function_unregister(&meetme_info_acf); ast_unload_realtime("meetme"); + meetme_stasis_cleanup(); + return res; } +/*! + * \brief Load the module + * + * Module loading including tests for configuration or dependencies. + * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE, + * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails + * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the + * configuration file or other non-critical problem return + * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS. + */ static int load_module(void) { int res = 0; res |= load_config(0); + res |= meetme_stasis_init(); + ast_cli_register_multiple(cli_meetme, ARRAY_LEN(cli_meetme)); res |= ast_manager_register_xml("MeetmeMute", EVENT_FLAG_CALL, action_meetmemute); res |= ast_manager_register_xml("MeetmeUnmute", EVENT_FLAG_CALL, action_meetmeunmute); @@ -8039,6 +8278,7 @@ } AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "MeetMe conference bridge", + .support_level = AST_MODULE_SUPPORT_EXTENDED, .load = load_module, .unload = unload_module, .reload = reload, diff -Nru asterisk-11.7.0~dfsg/apps/app_milliwatt.c asterisk-13.1.0~dfsg/apps/app_milliwatt.c --- asterisk-11.7.0~dfsg/apps/app_milliwatt.c 2012-04-04 18:08:28.000000000 +0000 +++ asterisk-13.1.0~dfsg/apps/app_milliwatt.c 2014-07-20 22:06:33.000000000 +0000 @@ -31,12 +31,13 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 361155 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419044 $") #include "asterisk/module.h" #include "asterisk/channel.h" #include "asterisk/pbx.h" #include "asterisk/indications.h" +#include "asterisk/format_cache.h" /*** DOCUMENTATION @@ -79,13 +80,14 @@ { unsigned char buf[AST_FRIENDLY_OFFSET + 640]; const int maxsamples = ARRAY_LEN(buf) - (AST_FRIENDLY_OFFSET / sizeof(buf[0])); - int i, *indexp = (int *) data; + int i, *indexp = (int *) data, res; struct ast_frame wf = { .frametype = AST_FRAME_VOICE, .offset = AST_FRIENDLY_OFFSET, .src = __FUNCTION__, }; - ast_format_set(&wf.subclass.format, AST_FORMAT_ULAW, 0); + + wf.subclass.format = ast_format_ulaw; wf.data.ptr = buf + AST_FRIENDLY_OFFSET; /* Instead of len, use samples, because channel.c generator_force @@ -108,7 +110,10 @@ *indexp &= 7; } - if (ast_write(chan,&wf) < 0) { + res = ast_write(chan, &wf); + ast_frfree(&wf); + + if (res < 0) { ast_log(LOG_WARNING,"Failed to write frame to '%s': %s\n",ast_channel_name(chan),strerror(errno)); return -1; } @@ -124,8 +129,8 @@ static int old_milliwatt_exec(struct ast_channel *chan) { - ast_set_write_format_by_id(chan, AST_FORMAT_ULAW); - ast_set_read_format_by_id(chan, AST_FORMAT_ULAW); + ast_set_write_format(chan, ast_format_ulaw); + ast_set_read_format(chan, ast_format_ulaw); if (ast_channel_state(chan) != AST_STATE_UP) { ast_answer(chan); diff -Nru asterisk-11.7.0~dfsg/apps/app_minivm.c asterisk-13.1.0~dfsg/apps/app_minivm.c --- asterisk-11.7.0~dfsg/apps/app_minivm.c 2013-01-21 04:07:05.000000000 +0000 +++ asterisk-13.1.0~dfsg/apps/app_minivm.c 2014-07-25 16:47:17.000000000 +0000 @@ -26,7 +26,6 @@ * based on the Comedian Mail voicemail system (app_voicemail.c). * * \par See also - * \arg \ref Config_minivm * \arg \ref Config_minivm_examples * \arg \ref App_minivm * @@ -147,7 +146,7 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 379609 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $") #include #include @@ -167,14 +166,15 @@ #include "asterisk/say.h" #include "asterisk/module.h" #include "asterisk/app.h" -#include "asterisk/manager.h" #include "asterisk/dsp.h" #include "asterisk/localtime.h" #include "asterisk/cli.h" #include "asterisk/utils.h" #include "asterisk/linkedlists.h" #include "asterisk/callerid.h" -#include "asterisk/event.h" +#include "asterisk/stasis.h" +#include "asterisk/stasis_channels.h" +#include "asterisk/json.h" /*** DOCUMENTATION @@ -496,7 +496,23 @@ MINIVMCOUNTER - + + + Raised when a notification is sent out by a MiniVoiceMail application + + + + What action was taken. Currently, this will always be SentNotification + + + The mailbox that the notification was about, specified as mailbox@context + + + A message counter derived from the MVM_COUNTER channel variable. + + + + ***/ #ifndef TRUE @@ -1390,7 +1406,7 @@ } } - fprintf(p, "Message-ID: \n", (unsigned int)ast_random(), vmu->username, (int)getpid(), who); + fprintf(p, "Message-ID: \n", (unsigned int)ast_random(), vmu->username, (int)getpid(), who); if (ast_strlen_zero(vmu->email)) { snprintf(email, sizeof(email), "%s@%s", vmu->username, vmu->domain); @@ -1441,7 +1457,7 @@ fprintf(p, "MIME-Version: 1.0\n"); /* Something unique. */ - snprintf(bound, sizeof(bound), "voicemail_%s%d%d", vmu->username, (int)getpid(), (unsigned int)ast_random()); + snprintf(bound, sizeof(bound), "voicemail_%s%d%u", vmu->username, (int)getpid(), (unsigned int)ast_random()); fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound); @@ -1658,7 +1674,7 @@ ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0); if (ast_test_flag(vmu, MVM_OPERATOR)) canceldtmf = "0"; - cmd = ast_play_and_record_full(chan, playfile, recordfile, maxtime, fmt, duration, sound_duration, global_silencethreshold, global_maxsilence, unlockdir, acceptdtmf, canceldtmf); + cmd = ast_play_and_record_full(chan, playfile, recordfile, maxtime, fmt, duration, sound_duration, 0, global_silencethreshold, global_maxsilence, unlockdir, acceptdtmf, canceldtmf, 0, AST_RECORD_IF_EXISTS_OVERWRITE); if (record_gain) ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0); if (cmd == -1) /* User has hung up, no options to give */ @@ -1762,6 +1778,9 @@ * \brief Send message to voicemail account owner */ static int notify_new_message(struct ast_channel *chan, const char *templatename, struct minivm_account *vmu, const char *filename, long duration, const char *format, char *cidnum, char *cidname) { + RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref); + RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup); + RAII_VAR(struct ast_mwi_state *, mwi_state, NULL, ao2_cleanup); char *stringp; struct minivm_template *etemplate; char *messageformat; @@ -1827,8 +1846,26 @@ res = sendmail(etemplate, vmu, cidnum, cidname, filename, messageformat, duration, etemplate->attachment, MVM_MESSAGE_PAGE, counter); } - ast_manager_event(chan, EVENT_FLAG_CALL, "MiniVoiceMail", "Action: SentNotification\rn\nMailbox: %s@%s\r\nCounter: %s\r\n", vmu->username, vmu->domain, counter); + mwi_state = ast_mwi_create(vmu->username, vmu->domain); + if (!mwi_state) { + goto notify_cleanup; + } + mwi_state->snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(chan)); + + json_object = ast_json_pack("{s: s, s: s}", + "Event", "MiniVoiceMail" + "Action", "SentNotification", + "Counter", counter); + if (!json_object) { + goto notify_cleanup; + } + message = ast_mwi_blob_create(mwi_state, ast_mwi_vm_app_type(), json_object); + if (!message) { + goto notify_cleanup; + } + stasis_publish(ast_mwi_topic(mwi_state->uniqueid), message); +notify_cleanup: run_externnotify(chan, vmu); /* Run external notification */ if (etemplate->locale) { @@ -2012,9 +2049,8 @@ /*!\internal * \brief Queue a message waiting event */ -static void queue_mwi_event(const char *mbx, const char *ctx, int urgent, int new, int old) +static void queue_mwi_event(const char *channel_id, const char *mbx, const char *ctx, int urgent, int new, int old) { - struct ast_event *event; char *mailbox, *context; mailbox = ast_strdupa(mbx); @@ -2023,16 +2059,7 @@ context = "default"; } - if (!(event = ast_event_new(AST_EVENT_MWI, - 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_UINT, (new+urgent), - AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old, - AST_EVENT_IE_END))) { - return; - } - - ast_event_queue_and_cache(event); + ast_publish_mwi_state_channel(mailbox, context, new + urgent, old, channel_id); } /*!\internal @@ -2067,7 +2094,7 @@ ast_log(LOG_ERROR, "Need mailbox@context as argument. Sorry. Argument 0 %s\n", argv[0]); return -1; } - queue_mwi_event(mailbox, domain, atoi(argv[1]), atoi(argv[2]), atoi(argv[3])); + queue_mwi_event(ast_channel_uniqueid(chan), mailbox, domain, atoi(argv[1]), atoi(argv[2]), atoi(argv[3])); return res; } @@ -2089,7 +2116,6 @@ const char *filename; const char *format; const char *duration_string; - if (ast_strlen_zero(data)) { ast_log(LOG_ERROR, "Minivm needs at least an account argument \n"); return -1; @@ -3535,6 +3561,7 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Mini VoiceMail (A minimal Voicemail e-mail System)", + .support_level = AST_MODULE_SUPPORT_EXTENDED, .load = load_module, .unload = unload_module, .reload = reload, diff -Nru asterisk-11.7.0~dfsg/apps/app_mixmonitor.c asterisk-13.1.0~dfsg/apps/app_mixmonitor.c --- asterisk-11.7.0~dfsg/apps/app_mixmonitor.c 2013-08-30 16:20:21.000000000 +0000 +++ asterisk-13.1.0~dfsg/apps/app_mixmonitor.c 2014-10-03 19:39:49.000000000 +0000 @@ -39,7 +39,7 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 398011 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 424507 $") #include "asterisk/paths.h" /* use ast_config_AST_MONITOR_DIR */ #include "asterisk/stringfields.h" @@ -56,6 +56,9 @@ #include "asterisk/mod_format.h" #include "asterisk/linkedlists.h" #include "asterisk/test.h" +#include "asterisk/mixmonitor.h" +#include "asterisk/format_cache.h" +#include "asterisk/beep.h" /*** DOCUMENTATION @@ -78,11 +81,14 @@ + + +