summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES6
-rw-r--r--apps/app_dial.c56
-rw-r--r--cel/cel_odbc.c1
-rw-r--r--contrib/ast-db-manage/README.md1
l---------[-rwxr-xr-x]contrib/ast-db-manage/cdr/env.py75
l---------[-rwxr-xr-x]contrib/ast-db-manage/config/env.py75
-rwxr-xr-xcontrib/ast-db-manage/env.py140
l---------[-rwxr-xr-x]contrib/ast-db-manage/voicemail/env.py75
-rw-r--r--include/asterisk/utils.h4
-rw-r--r--include/asterisk/vector.h54
-rw-r--r--main/astobj2.c25
-rw-r--r--main/astobj2_container.c36
-rw-r--r--main/astobj2_hash.c4
-rw-r--r--main/astobj2_rbtree.c4
-rw-r--r--main/channel.c41
-rw-r--r--main/utils.c6
-rw-r--r--res/res_fax.c7
-rw-r--r--res/res_pjsip_config_wizard.c4
-rw-r--r--res/res_rtp_asterisk.c17
19 files changed, 361 insertions, 270 deletions
diff --git a/CHANGES b/CHANGES
index 4a866829a..11b357419 100644
--- a/CHANGES
+++ b/CHANGES
@@ -83,6 +83,12 @@ Core
notify systemd of its state using sd_notify. Use 'Type=notify' in
asterisk.service.
+app_dial
+------------------
+ * Added the "Q" option which sets the Q.850/Q.931 cause on unanswered channels
+ when another channel answers the call. The default of ANSWERED_ELSEWHERE
+ is unchanged.
+
------------------------------------------------------------------------------
--- Functionality changes from Asterisk 13.10.0 to Asterisk 13.11.0 ----------
------------------------------------------------------------------------------
diff --git a/apps/app_dial.c b/apps/app_dial.c
index 7b7c70201..893898100 100644
--- a/apps/app_dial.c
+++ b/apps/app_dial.c
@@ -375,6 +375,25 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>Enable privacy mode. Use <replaceable>x</replaceable> as the family/key in the AstDB database if
it is provided. The current extension is used if a database family/key is not specified.</para>
</option>
+ <option name="Q">
+ <argument name="cause" required="true"/>
+ <para>Specify the Q.850/Q.931 <replaceable>cause</replaceable> to send on
+ unanswered channels when another channel answers the call.
+ As with <literal>Hangup()</literal>, <replaceable>cause</replaceable>
+ can be a numeric cause code or a name such as
+ <literal>NO_ANSWER</literal>,
+ <literal>USER_BUSY</literal>,
+ <literal>CALL_REJECTED</literal> or
+ <literal>ANSWERED_ELSEWHERE</literal> (the default if Q isn't specified).
+ You can also specify <literal>0</literal> or <literal>NONE</literal>
+ to send no cause. See the <filename>causes.h</filename> file for the
+ full list of valid causes and names.
+ </para>
+ <note>
+ <para>chan_sip does not support setting the cause on a CANCEL to anything
+ other than ANSWERED_ELSEWHERE.</para>
+ </note>
+ </option>
<option name="r">
<para>Default: Indicate ringing to the calling party, even if the called party isn't actually ringing. Pass no audio to the calling
party until the called channel has answered.</para>
@@ -520,6 +539,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<example title="Dial with call length limit">
same => n,Dial(PJSIP/alice,,L(60000:30000:10000))
</example>
+ <example title="Dial alice and bob and send NO_ANSWER to bob instead of ANSWERED_ELSEWHERE when alice answers">
+ same => n,Dial(PJSIP/alice&amp;PJSIP/bob,,Q(NO_ANSWER))
+ </example>
<example title="Dial with pre-dial subroutines">
[default]
@@ -684,6 +706,7 @@ enum {
#define OPT_PREDIAL_CALLEE (1LLU << 41)
#define OPT_PREDIAL_CALLER (1LLU << 42)
#define OPT_RING_WITH_EARLY_MEDIA (1LLU << 43)
+#define OPT_HANGUPCAUSE (1LLU << 44)
enum {
OPT_ARG_ANNOUNCE = 0,
@@ -705,6 +728,7 @@ enum {
OPT_ARG_FORCE_CID_PRES,
OPT_ARG_PREDIAL_CALLEE,
OPT_ARG_PREDIAL_CALLER,
+ OPT_ARG_HANGUPCAUSE,
/* note: this entry _MUST_ be the last one in the enum */
OPT_ARG_ARRAY_SIZE
};
@@ -738,6 +762,7 @@ AST_APP_OPTIONS(dial_exec_options, BEGIN_OPTIONS
AST_APP_OPTION_ARG('O', OPT_OPERMODE, OPT_ARG_OPERMODE),
AST_APP_OPTION('p', OPT_SCREENING),
AST_APP_OPTION_ARG('P', OPT_PRIVACY, OPT_ARG_PRIVACY),
+ AST_APP_OPTION_ARG('Q', OPT_HANGUPCAUSE, OPT_ARG_HANGUPCAUSE),
AST_APP_OPTION_ARG('r', OPT_RINGBACK, OPT_ARG_RINGBACK),
AST_APP_OPTION('R', OPT_RING_WITH_EARLY_MEDIA),
AST_APP_OPTION_ARG('S', OPT_DURATION_STOP, OPT_ARG_DURATION_STOP),
@@ -796,7 +821,7 @@ static void chanlist_free(struct chanlist *outgoing)
ast_free(outgoing);
}
-static void hanguptree(struct dial_head *out_chans, struct ast_channel *exception, int answered_elsewhere)
+static void hanguptree(struct dial_head *out_chans, struct ast_channel *exception, int hangupcause)
{
/* Hang up a tree of stuff */
struct chanlist *outgoing;
@@ -804,9 +829,9 @@ static void hanguptree(struct dial_head *out_chans, struct ast_channel *exceptio
while ((outgoing = AST_LIST_REMOVE_HEAD(out_chans, node))) {
/* Hangup any existing lines we have open */
if (outgoing->chan && (outgoing->chan != exception)) {
- if (answered_elsewhere) {
+ if (hangupcause >= 0) {
/* This is for the channel drivers */
- ast_channel_hangupcause_set(outgoing->chan, AST_CAUSE_ANSWERED_ELSEWHERE);
+ ast_channel_hangupcause_set(outgoing->chan, hangupcause);
}
ast_hangup(outgoing->chan);
}
@@ -2763,6 +2788,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
} else {
const char *number;
int dial_end_raised = 0;
+ int cause = -1;
if (ast_test_flag64(&opts, OPT_CALLER_ANSWER))
ast_answer(chan);
@@ -2773,7 +2799,23 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
/* Ah ha! Someone answered within the desired timeframe. Of course after this
we will always return with -1 so that it is hung up properly after the
conversation. */
- hanguptree(&out_chans, peer, 1);
+
+ if (ast_test_flag64(&opts, OPT_HANGUPCAUSE)
+ && !ast_strlen_zero(opt_args[OPT_ARG_HANGUPCAUSE])) {
+ cause = ast_str2cause(opt_args[OPT_ARG_HANGUPCAUSE]);
+ if (cause <= 0) {
+ if (!strcasecmp(opt_args[OPT_ARG_HANGUPCAUSE], "NONE")) {
+ cause = 0;
+ } else if (sscanf(opt_args[OPT_ARG_HANGUPCAUSE], "%30d", &cause) != 1
+ || cause < 0) {
+ ast_log(LOG_WARNING, "Invalid cause given to Dial(...Q(<cause>)): \"%s\"\n",
+ opt_args[OPT_ARG_HANGUPCAUSE]);
+ cause = -1;
+ }
+ }
+ }
+ hanguptree(&out_chans, peer, cause >= 0 ? cause : AST_CAUSE_ANSWERED_ELSEWHERE);
+
/* If appropriate, log that we have a destination channel and set the answer time */
if (ast_channel_name(peer))
pbx_builtin_setvar_helper(chan, "DIALEDPEERNAME", ast_channel_name(peer));
@@ -3177,7 +3219,11 @@ out:
}
ast_channel_early_bridge(chan, NULL);
- hanguptree(&out_chans, NULL, ast_channel_hangupcause(chan)==AST_CAUSE_ANSWERED_ELSEWHERE || ast_test_flag64(&opts, OPT_CANCEL_ELSEWHERE) ? 1 : 0 ); /* forward 'answered elsewhere' if we received it */
+ /* forward 'answered elsewhere' if we received it */
+ hanguptree(&out_chans, NULL,
+ ast_channel_hangupcause(chan) == AST_CAUSE_ANSWERED_ELSEWHERE
+ || ast_test_flag64(&opts, OPT_CANCEL_ELSEWHERE)
+ ? AST_CAUSE_ANSWERED_ELSEWHERE : -1);
pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
ast_debug(1, "Exiting with DIALSTATUS=%s.\n", pa.status);
diff --git a/cel/cel_odbc.c b/cel/cel_odbc.c
index 11e333097..701883bce 100644
--- a/cel/cel_odbc.c
+++ b/cel/cel_odbc.c
@@ -291,6 +291,7 @@ static int load_config(void)
else
ast_free(tableptr);
}
+ ast_config_destroy(cfg);
return res;
}
diff --git a/contrib/ast-db-manage/README.md b/contrib/ast-db-manage/README.md
index 3444dd5cd..7add3ee66 100644
--- a/contrib/ast-db-manage/README.md
+++ b/contrib/ast-db-manage/README.md
@@ -9,6 +9,7 @@ This is implemented as a set of repositories that contain database schema
migrations, using [Alembic](http://alembic.readthedocs.org). The existing
repositories include:
+ * `cdr` - Table used for Asterisk to store CDR records
* `config` - Tables used for Asterisk realtime configuration
* `voicemail` - Tables used for `ODBC_STOARGE` of voicemail messages
diff --git a/contrib/ast-db-manage/cdr/env.py b/contrib/ast-db-manage/cdr/env.py
index 6740d5906..74b15c930 100755..120000
--- a/contrib/ast-db-manage/cdr/env.py
+++ b/contrib/ast-db-manage/cdr/env.py
@@ -1,74 +1 @@
-from __future__ import with_statement
-from alembic import context
-from sqlalchemy import engine_from_config, pool
-from logging.config import fileConfig
-
-# this is the Alembic Config object, which provides
-# access to the values within the .ini file in use.
-config = context.config
-
-# Interpret the config file for Python logging.
-# This line sets up loggers basically.
-try:
- fileConfig(config.config_file_name)
-except:
- pass
-
-# add your model's MetaData object here
-# for 'autogenerate' support
-# from myapp import mymodel
-# target_metadata = mymodel.Base.metadata
-target_metadata = None
-
-# other values from the config, defined by the needs of env.py,
-# can be acquired:
-# my_important_option = config.get_main_option("my_important_option")
-# ... etc.
-
-def run_migrations_offline():
- """Run migrations in 'offline' mode.
-
- This configures the context with just a URL
- and not an Engine, though an Engine is acceptable
- here as well. By skipping the Engine creation
- we don't even need a DBAPI to be available.
-
- Calls to context.execute() here emit the given string to the
- script output.
-
- """
- url = config.get_main_option("sqlalchemy.url")
- context.configure(url=url)
-
- with context.begin_transaction():
- context.run_migrations()
-
-def run_migrations_online():
- """Run migrations in 'online' mode.
-
- In this scenario we need to create an Engine
- and associate a connection with the context.
-
- """
- engine = engine_from_config(
- config.get_section(config.config_ini_section),
- prefix='sqlalchemy.',
- poolclass=pool.NullPool)
-
- connection = engine.connect()
- context.configure(
- connection=connection,
- target_metadata=target_metadata
- )
-
- try:
- with context.begin_transaction():
- context.run_migrations()
- finally:
- connection.close()
-
-if context.is_offline_mode():
- run_migrations_offline()
-else:
- run_migrations_online()
-
+../env.py \ No newline at end of file
diff --git a/contrib/ast-db-manage/config/env.py b/contrib/ast-db-manage/config/env.py
index 6740d5906..74b15c930 100755..120000
--- a/contrib/ast-db-manage/config/env.py
+++ b/contrib/ast-db-manage/config/env.py
@@ -1,74 +1 @@
-from __future__ import with_statement
-from alembic import context
-from sqlalchemy import engine_from_config, pool
-from logging.config import fileConfig
-
-# this is the Alembic Config object, which provides
-# access to the values within the .ini file in use.
-config = context.config
-
-# Interpret the config file for Python logging.
-# This line sets up loggers basically.
-try:
- fileConfig(config.config_file_name)
-except:
- pass
-
-# add your model's MetaData object here
-# for 'autogenerate' support
-# from myapp import mymodel
-# target_metadata = mymodel.Base.metadata
-target_metadata = None
-
-# other values from the config, defined by the needs of env.py,
-# can be acquired:
-# my_important_option = config.get_main_option("my_important_option")
-# ... etc.
-
-def run_migrations_offline():
- """Run migrations in 'offline' mode.
-
- This configures the context with just a URL
- and not an Engine, though an Engine is acceptable
- here as well. By skipping the Engine creation
- we don't even need a DBAPI to be available.
-
- Calls to context.execute() here emit the given string to the
- script output.
-
- """
- url = config.get_main_option("sqlalchemy.url")
- context.configure(url=url)
-
- with context.begin_transaction():
- context.run_migrations()
-
-def run_migrations_online():
- """Run migrations in 'online' mode.
-
- In this scenario we need to create an Engine
- and associate a connection with the context.
-
- """
- engine = engine_from_config(
- config.get_section(config.config_ini_section),
- prefix='sqlalchemy.',
- poolclass=pool.NullPool)
-
- connection = engine.connect()
- context.configure(
- connection=connection,
- target_metadata=target_metadata
- )
-
- try:
- with context.begin_transaction():
- context.run_migrations()
- finally:
- connection.close()
-
-if context.is_offline_mode():
- run_migrations_offline()
-else:
- run_migrations_online()
-
+../env.py \ No newline at end of file
diff --git a/contrib/ast-db-manage/env.py b/contrib/ast-db-manage/env.py
new file mode 100755
index 000000000..a903451b4
--- /dev/null
+++ b/contrib/ast-db-manage/env.py
@@ -0,0 +1,140 @@
+from __future__ import with_statement
+from alembic import context
+from alembic.script import ScriptDirectory
+from alembic.operations import Operations
+from sqlalchemy import engine_from_config, pool
+from logging.config import fileConfig
+import logging
+
+# this is the Alembic Config object, which provides
+# access to the values within the .ini file in use.
+config = context.config
+
+# Interpret the config file for Python logging.
+# This line sets up loggers basically.
+try:
+ fileConfig(config.config_file_name)
+except:
+ pass
+
+logger = logging.getLogger('alembic.runtime.setup')
+# add your model's MetaData object here
+# for 'autogenerate' support
+# from myapp import mymodel
+# target_metadata = mymodel.Base.metadata
+target_metadata = None
+
+# other values from the config, defined by the needs of env.py,
+# can be acquired:
+# my_important_option = config.get_main_option("my_important_option")
+# ... etc.
+
+def run_migrations_offline():
+ """Run migrations in 'offline' mode.
+
+ This configures the context with just a URL
+ and not an Engine, though an Engine is acceptable
+ here as well. By skipping the Engine creation
+ we don't even need a DBAPI to be available.
+
+ Calls to context.execute() here emit the given string to the
+ script output.
+
+ """
+ url = config.get_main_option("sqlalchemy.url")
+ context.configure(url=url)
+
+ with context.begin_transaction():
+ context.run_migrations()
+
+def run_migrations_online():
+ """Run migrations in 'online' mode.
+
+ In this scenario we need to create an Engine
+ and associate a connection with the context.
+
+ """
+ engine = engine_from_config(
+ config.get_section(config.config_ini_section),
+ prefix='sqlalchemy.',
+ poolclass=pool.NullPool)
+
+ logger.info('Testing for an old alembic_version table.')
+
+ connection = engine.connect()
+ context.configure(
+ connection=connection,
+ target_metadata=target_metadata,
+ version_table='alembic_version'
+ )
+
+ script_location = config.get_main_option('script_location')
+ found = False
+ mc = context.get_context()
+ current_db_revision = mc.get_current_revision()
+ script = ScriptDirectory.from_config(config)
+ """ If there was an existing alembic_version table, we need to
+ check that it's current revision is in the history for the tree
+ we're working with.
+ """
+ for x in script.iterate_revisions('head', 'base'):
+ if x.revision == current_db_revision:
+ """ An alembic_versions table was found and it belongs to
+ this alembic tree
+ """
+ logger.info(
+ ('An old alembic_version table at revision %s was '
+ 'found for %s. Renaming to alembic_version_%s.'),
+ current_db_revision, script_location,
+ script_location)
+ op = Operations(mc)
+ try:
+ with context.begin_transaction():
+ op.rename_table(
+ 'alembic_version', 'alembic_version_%s'
+ % script_location)
+ found = True
+ except:
+ logger.error(('Unable to rename alembic_version to '
+ 'alembic_version_%s.'),
+ script_location)
+ connection.close()
+ return
+
+ break
+
+ if not found:
+ logger.info('Didn\'t find an old alembic_version table.')
+ logger.info('Trying alembic_version_%s.' % script_location)
+
+ """ We MAY have an alembic_version table that doesn't belong to
+ this tree but if we still don't have an alembic_version_<tree>
+ table, alembic will create it.
+ """
+ context.configure(
+ connection=connection,
+ target_metadata=target_metadata,
+ version_table='alembic_version_' + script_location
+ )
+ mc = context.get_context()
+ current_db_revision = mc.get_current_revision()
+ if current_db_revision:
+ logger.info(
+ 'Using the alembic_version_%s table at revision %s.',
+ script_location, current_db_revision)
+ else:
+ logger.info('Creating new alembic_version_%s table.',
+ script_location)
+
+ try:
+ with context.begin_transaction():
+ context.run_migrations()
+ finally:
+ connection.close()
+
+
+if context.is_offline_mode():
+ run_migrations_offline()
+else:
+ run_migrations_online()
+
diff --git a/contrib/ast-db-manage/voicemail/env.py b/contrib/ast-db-manage/voicemail/env.py
index 6740d5906..74b15c930 100755..120000
--- a/contrib/ast-db-manage/voicemail/env.py
+++ b/contrib/ast-db-manage/voicemail/env.py
@@ -1,74 +1 @@
-from __future__ import with_statement
-from alembic import context
-from sqlalchemy import engine_from_config, pool
-from logging.config import fileConfig
-
-# this is the Alembic Config object, which provides
-# access to the values within the .ini file in use.
-config = context.config
-
-# Interpret the config file for Python logging.
-# This line sets up loggers basically.
-try:
- fileConfig(config.config_file_name)
-except:
- pass
-
-# add your model's MetaData object here
-# for 'autogenerate' support
-# from myapp import mymodel
-# target_metadata = mymodel.Base.metadata
-target_metadata = None
-
-# other values from the config, defined by the needs of env.py,
-# can be acquired:
-# my_important_option = config.get_main_option("my_important_option")
-# ... etc.
-
-def run_migrations_offline():
- """Run migrations in 'offline' mode.
-
- This configures the context with just a URL
- and not an Engine, though an Engine is acceptable
- here as well. By skipping the Engine creation
- we don't even need a DBAPI to be available.
-
- Calls to context.execute() here emit the given string to the
- script output.
-
- """
- url = config.get_main_option("sqlalchemy.url")
- context.configure(url=url)
-
- with context.begin_transaction():
- context.run_migrations()
-
-def run_migrations_online():
- """Run migrations in 'online' mode.
-
- In this scenario we need to create an Engine
- and associate a connection with the context.
-
- """
- engine = engine_from_config(
- config.get_section(config.config_ini_section),
- prefix='sqlalchemy.',
- poolclass=pool.NullPool)
-
- connection = engine.connect()
- context.configure(
- connection=connection,
- target_metadata=target_metadata
- )
-
- try:
- with context.begin_transaction():
- context.run_migrations()
- finally:
- connection.close()
-
-if context.is_offline_mode():
- run_migrations_offline()
-else:
- run_migrations_online()
-
+../env.py \ No newline at end of file
diff --git a/include/asterisk/utils.h b/include/asterisk/utils.h
index c3df4779f..a504a5da1 100644
--- a/include/asterisk/utils.h
+++ b/include/asterisk/utils.h
@@ -849,8 +849,10 @@ int ast_parse_digest(const char *digest, struct ast_http_digest *d, int request,
#define DO_CRASH_NORETURN
#endif
+void DO_CRASH_NORETURN __ast_assert_failed(int condition, const char *condition_str,
+ const char *file, int line, const char *function);
+
#ifdef AST_DEVMODE
-void DO_CRASH_NORETURN __ast_assert_failed(int condition, const char *condition_str, const char *file, int line, const char *function);
#define ast_assert(a) _ast_assert(a, # a, __FILE__, __LINE__, __PRETTY_FUNCTION__)
static void force_inline _ast_assert(int condition, const char *condition_str, const char *file, int line, const char *function)
{
diff --git a/include/asterisk/vector.h b/include/asterisk/vector.h
index 0a13c560b..4306670e7 100644
--- a/include/asterisk/vector.h
+++ b/include/asterisk/vector.h
@@ -354,6 +354,32 @@
AST_VECTOR_REMOVE(vec, idx, 1)
/*!
+ * \brief Remove all elements from a vector that matches the given comparison
+ *
+ * \param vec Vector to remove from.
+ * \param value Value to pass into comparator.
+ * \param cmp Comparator function/macros (called as \c cmp(elem, value))
+ * \param cleanup How to cleanup a removed element macro/function.
+ *
+ * \return the number of deleted elements.
+ */
+#define AST_VECTOR_REMOVE_ALL_CMP_UNORDERED(vec, value, cmp, cleanup) ({ \
+ int count = 0; \
+ size_t idx; \
+ typeof(value) __value = (value); \
+ for (idx = 0; idx < (vec)->current; ) { \
+ if (cmp((vec)->elems[idx], __value)) { \
+ cleanup((vec)->elems[idx]); \
+ AST_VECTOR_REMOVE_UNORDERED((vec), idx); \
+ ++count; \
+ } else { \
+ ++idx; \
+ } \
+ } \
+ count; \
+})
+
+/*!
* \brief Remove an element from a vector that matches the given comparison
*
* \param vec Vector to remove from.
@@ -380,6 +406,32 @@
})
/*!
+ * \brief Remove all elements from a vector that matches the given comparison while maintaining order
+ *
+ * \param vec Vector to remove from.
+ * \param value Value to pass into comparator.
+ * \param cmp Comparator function/macros (called as \c cmp(elem, value))
+ * \param cleanup How to cleanup a removed element macro/function.
+ *
+ * \return the number of deleted elements.
+ */
+#define AST_VECTOR_REMOVE_ALL_CMP_ORDERED(vec, value, cmp, cleanup) ({ \
+ int count = 0; \
+ size_t idx; \
+ typeof(value) __value = (value); \
+ for (idx = 0; idx < (vec)->current; ) { \
+ if (cmp((vec)->elems[idx], __value)) { \
+ cleanup((vec)->elems[idx]); \
+ AST_VECTOR_REMOVE_ORDERED((vec), idx); \
+ ++count; \
+ } else { \
+ ++idx; \
+ } \
+ } \
+ count; \
+})
+
+/*!
* \brief Remove an element from a vector that matches the given comparison while maintaining order
*
* \param vec Vector to remove from.
@@ -397,7 +449,7 @@
for (idx = 0; idx < (vec)->current; ++idx) { \
if (cmp((vec)->elems[idx], __value)) { \
cleanup((vec)->elems[idx]); \
- AST_VECTOR_REMOVE_ORDERED((vec), idx); \
+ AST_VECTOR_REMOVE_ORDERED((vec), idx); \
res = 0; \
break; \
} \
diff --git a/main/astobj2.c b/main/astobj2.c
index 1bb5237f1..f5175ea15 100644
--- a/main/astobj2.c
+++ b/main/astobj2.c
@@ -116,21 +116,17 @@ static struct astobj2 *INTERNAL_OBJ(void *user_data)
struct astobj2 *p;
if (!user_data) {
- ast_log(LOG_ERROR, "user_data is NULL\n");
+ __ast_assert_failed(0, "user_data is NULL", __FILE__, __LINE__, __PRETTY_FUNCTION__);
return NULL;
}
p = (struct astobj2 *) ((char *) user_data - sizeof(*p));
if (AO2_MAGIC != p->priv_data.magic) {
- if (p->priv_data.magic) {
- ast_log(LOG_ERROR, "bad magic number 0x%x for object %p\n",
- p->priv_data.magic, user_data);
- } else {
- ast_log(LOG_ERROR,
- "bad magic number for object %p. Object is likely destroyed.\n",
- user_data);
- }
- ast_assert(0);
+ char bad_magic[100];
+
+ snprintf(bad_magic, sizeof(bad_magic), "bad magic number 0x%x for object %p",
+ p->priv_data.magic, user_data);
+ __ast_assert_failed(0, bad_magic, __FILE__, __LINE__, __PRETTY_FUNCTION__);
return NULL;
}
@@ -157,7 +153,6 @@ int __ao2_lock(void *user_data, enum ao2_lock_req lock_how, const char *file, co
int res = 0;
if (obj == NULL) {
- ast_assert(0);
return -1;
}
@@ -216,7 +211,6 @@ int __ao2_unlock(void *user_data, const char *file, const char *func, int line,
int current_value;
if (obj == NULL) {
- ast_assert(0);
return -1;
}
@@ -265,7 +259,6 @@ int __ao2_trylock(void *user_data, enum ao2_lock_req lock_how, const char *file,
int res = 0;
if (obj == NULL) {
- ast_assert(0);
return -1;
}
@@ -385,7 +378,6 @@ void *ao2_object_get_lockaddr(void *user_data)
struct astobj2_lock *obj_mutex;
if (obj == NULL) {
- ast_assert(0);
return NULL;
}
@@ -409,7 +401,6 @@ static int internal_ao2_ref(void *user_data, int delta, const char *file, int li
int ret;
if (obj == NULL) {
- ast_assert(0);
return -1;
}
@@ -504,10 +495,6 @@ int __ao2_ref_debug(void *user_data, int delta, const char *tag, const char *fil
}
}
- if (obj == NULL) {
- ast_assert(0);
- }
-
return old_refcount;
}
diff --git a/main/astobj2_container.c b/main/astobj2_container.c
index 5a27a0ae5..466c10372 100644
--- a/main/astobj2_container.c
+++ b/main/astobj2_container.c
@@ -103,8 +103,11 @@ static int internal_ao2_link(struct ao2_container *self, void *obj_new, int flag
enum ao2_lock_req orig_lock;
struct ao2_container_node *node;
- if (!is_ao2_object(obj_new) || !is_ao2_object(self)
- || !self->v_table || !self->v_table->new_node || !self->v_table->insert) {
+ if (!is_ao2_object(obj_new) || !is_ao2_object(self)) {
+ return 0;
+ }
+
+ if (!self->v_table || !self->v_table->new_node || !self->v_table->insert) {
/* Sanity checks. */
ast_assert(0);
return 0;
@@ -187,8 +190,6 @@ void *__ao2_unlink_debug(struct ao2_container *c, void *user_data, int flags,
const char *tag, const char *file, int line, const char *func)
{
if (!is_ao2_object(user_data)) {
- /* Sanity checks. */
- ast_assert(0);
return NULL;
}
@@ -202,8 +203,6 @@ void *__ao2_unlink_debug(struct ao2_container *c, void *user_data, int flags,
void *__ao2_unlink(struct ao2_container *c, void *user_data, int flags)
{
if (!is_ao2_object(user_data)) {
- /* Sanity checks. */
- ast_assert(0);
return NULL;
}
@@ -268,7 +267,11 @@ static void *internal_ao2_traverse(struct ao2_container *self, enum search_flags
struct ao2_container *multi_container = NULL;
struct ao2_iterator *multi_iterator = NULL;
- if (!is_ao2_object(self) || !self->v_table || !self->v_table->traverse_first
+ if (!is_ao2_object(self)) {
+ return NULL;
+ }
+
+ if (!self->v_table || !self->v_table->traverse_first
|| !self->v_table->traverse_next) {
/* Sanity checks. */
ast_assert(0);
@@ -512,7 +515,6 @@ void ao2_iterator_restart(struct ao2_iterator *iter)
{
if (!is_ao2_object(iter->c)) {
ast_log(LOG_ERROR, "Iterator container is not valid.\n");
- ast_assert(0);
return;
}
@@ -577,7 +579,11 @@ static void *internal_ao2_iterator_next(struct ao2_iterator *iter, const char *t
struct ao2_container_node *node;
void *ret;
- if (!is_ao2_object(iter->c) || !iter->c->v_table || !iter->c->v_table->iterator_next) {
+ if (!is_ao2_object(iter->c)) {
+ return NULL;
+ }
+
+ if (!iter->c->v_table || !iter->c->v_table->iterator_next) {
/* Sanity checks. */
ast_assert(0);
return NULL;
@@ -748,7 +754,11 @@ struct ao2_container *__ao2_container_clone(struct ao2_container *orig, enum sea
int failed;
/* Create the clone container with the same properties as the original. */
- if (!is_ao2_object(orig) || !orig->v_table || !orig->v_table->alloc_empty_clone) {
+ if (!is_ao2_object(orig)) {
+ return NULL;
+ }
+
+ if (!orig->v_table || !orig->v_table->alloc_empty_clone) {
/* Sanity checks. */
ast_assert(0);
return NULL;
@@ -779,7 +789,11 @@ struct ao2_container *__ao2_container_clone_debug(struct ao2_container *orig, en
int failed;
/* Create the clone container with the same properties as the original. */
- if (!is_ao2_object(orig) || !orig->v_table || !orig->v_table->alloc_empty_clone_debug) {
+ if (!is_ao2_object(orig)) {
+ return NULL;
+ }
+
+ if (!orig->v_table || !orig->v_table->alloc_empty_clone_debug) {
/* Sanity checks. */
ast_assert(0);
return NULL;
diff --git a/main/astobj2_hash.c b/main/astobj2_hash.c
index 341ff79e0..f5c678740 100644
--- a/main/astobj2_hash.c
+++ b/main/astobj2_hash.c
@@ -186,7 +186,9 @@ static void hash_ao2_node_destructor(void *v_doomed)
* same node.
*/
my_container = (struct ao2_container_hash *) doomed->common.my_container;
- ast_assert(is_ao2_object(my_container));
+#if defined(AST_DEVMODE)
+ is_ao2_object(my_container);
+#endif
__adjust_lock(my_container, AO2_LOCK_REQ_WRLOCK, 1);
diff --git a/main/astobj2_rbtree.c b/main/astobj2_rbtree.c
index a8d5e3ac1..4362b83cb 100644
--- a/main/astobj2_rbtree.c
+++ b/main/astobj2_rbtree.c
@@ -878,7 +878,9 @@ static void rb_ao2_node_destructor(void *v_doomed)
* same node.
*/
my_container = (struct ao2_container_rbtree *) doomed->common.my_container;
- ast_assert(is_ao2_object(my_container));
+#if defined(AST_DEVMODE)
+ is_ao2_object(my_container);
+#endif
__adjust_lock(my_container, AO2_LOCK_REQ_WRLOCK, 1);
diff --git a/main/channel.c b/main/channel.c
index f3f79399f..c6cb925b8 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -3914,6 +3914,7 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
struct ast_frame *readq_tail = AST_LIST_LAST(ast_channel_readq(chan));
struct ast_control_read_action_payload *read_action_payload;
struct ast_party_connected_line connected;
+ int hooked = 0;
/* if the channel driver returned more than one frame, stuff the excess
into the readq for the next ast_read call
@@ -4191,15 +4192,22 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
break;
}
}
- /* Send frame to audiohooks if present */
- if (ast_channel_audiohooks(chan)) {
+ /*
+ * Send frame to audiohooks if present, if frametype is linear, to preserve
+ * functional compatibility with previous behavior. If not linear, hold off
+ * until transcoding is done where we are more likely to have a linear frame
+ */
+ if (ast_channel_audiohooks(chan) && ast_format_cache_is_slinear(f->subclass.format)) {
+ /* Place hooked after declaration */
struct ast_frame *old_frame = f;
+ hooked = 1;
f = ast_audiohook_write_list(chan, ast_channel_audiohooks(chan), AST_AUDIOHOOK_DIRECTION_READ, f);
if (old_frame != f) {
ast_frfree(old_frame);
}
}
+
if (ast_channel_monitor(chan) && ast_channel_monitor(chan)->read_stream) {
/* XXX what does this do ? */
#ifndef MONITOR_CONSTANT_DELAY
@@ -4242,6 +4250,16 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
}
}
+ /* Second chance at hooking a linear frame, also the last chance */
+ if (ast_channel_audiohooks(chan) && !hooked) {
+ struct ast_frame *old_frame = f;
+
+ f = ast_audiohook_write_list(chan, ast_channel_audiohooks(chan), AST_AUDIOHOOK_DIRECTION_READ, f);
+ if (old_frame != f) {
+ ast_frfree(old_frame);
+ }
+ }
+
/*
* It is possible for the translation process on the channel to have
* produced multiple frames from the single input frame we passed it; if
@@ -5032,6 +5050,7 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
int res = -1;
struct ast_frame *f = NULL;
int count = 0;
+ int hooked = 0;
/*Deadlock avoidance*/
while(ast_channel_trylock(chan)) {
@@ -5149,6 +5168,22 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
apply_plc(chan, fr);
}
+ /*
+ * Send frame to audiohooks if present, if frametype is linear (else, later as per
+ * previous behavior)
+ */
+ if (ast_channel_audiohooks(chan)) {
+ if (ast_format_cache_is_slinear(fr->subclass.format)) {
+ struct ast_frame *old_frame;
+ hooked = 1;
+ old_frame = fr;
+ fr = ast_audiohook_write_list(chan, ast_channel_audiohooks(chan), AST_AUDIOHOOK_DIRECTION_WRITE, fr);
+ if (old_frame != fr) {
+ ast_frfree(old_frame);
+ }
+ }
+ }
+
/* If the frame is in the raw write format, then it's easy... just use the frame - otherwise we will have to translate */
if (ast_format_cmp(fr->subclass.format, ast_channel_rawwriteformat(chan)) == AST_FORMAT_CMP_EQUAL) {
f = fr;
@@ -5186,7 +5221,7 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
break;
}
- if (ast_channel_audiohooks(chan)) {
+ if (ast_channel_audiohooks(chan) && !hooked) {
struct ast_frame *prev = NULL, *new_frame, *cur, *dup;
int freeoldlist = 0;
diff --git a/main/utils.c b/main/utils.c
index 1a034c101..af0ee7f6b 100644
--- a/main/utils.c
+++ b/main/utils.c
@@ -2437,17 +2437,16 @@ void DO_CRASH_NORETURN ast_do_crash(void)
#endif /* defined(DO_CRASH) */
}
-#if defined(AST_DEVMODE)
void DO_CRASH_NORETURN __ast_assert_failed(int condition, const char *condition_str, const char *file, int line, const char *function)
{
/*
* Attempt to put it into the logger, but hope that at least
* someone saw the message on stderr ...
*/
- ast_log(__LOG_ERROR, file, line, function, "FRACK!, Failed assertion %s (%d)\n",
- condition_str, condition);
fprintf(stderr, "FRACK!, Failed assertion %s (%d) at line %d in %s of %s\n",
condition_str, condition, line, function, file);
+ ast_log(__LOG_ERROR, file, line, function, "FRACK!, Failed assertion %s (%d)\n",
+ condition_str, condition);
/* Generate a backtrace for the assert */
ast_log_backtrace();
@@ -2460,7 +2459,6 @@ void DO_CRASH_NORETURN __ast_assert_failed(int condition, const char *condition_
usleep(1);
ast_do_crash();
}
-#endif /* defined(AST_DEVMODE) */
char *ast_eid_to_str(char *s, int maxlen, struct ast_eid *eid)
{
diff --git a/res/res_fax.c b/res/res_fax.c
index e37091b31..ab0945a89 100644
--- a/res/res_fax.c
+++ b/res/res_fax.c
@@ -1600,6 +1600,13 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det
chancount = 1;
+ /* Make sure one or the other is set to avoid race condition */
+ if (t38negotiated) {
+ details->caps |= AST_FAX_TECH_T38;
+ } else {
+ details->caps |= AST_FAX_TECH_AUDIO;
+ }
+
/* create the FAX session */
if (!(fax = fax_session_new(details, chan, reserved, token))) {
ast_log(LOG_ERROR, "Can't create a FAX session, FAX attempt failed.\n");
diff --git a/res/res_pjsip_config_wizard.c b/res/res_pjsip_config_wizard.c
index e263437d7..1967d87ab 100644
--- a/res/res_pjsip_config_wizard.c
+++ b/res/res_pjsip_config_wizard.c
@@ -989,7 +989,7 @@ static int wizard_apply_handler(const struct ast_sorcery *sorcery, struct object
rc = handle_registrations(sorcery, otw, wiz, &remote_hosts_vector);
}
- AST_VECTOR_REMOVE_CMP_UNORDERED(&remote_hosts_vector, NULL, NOT_EQUALS, ast_free);
+ AST_VECTOR_REMOVE_ALL_CMP_UNORDERED(&remote_hosts_vector, NULL, NOT_EQUALS, ast_free);
AST_VECTOR_FREE(&remote_hosts_vector);
ast_debug(4, "%s handler complete. rc: %d\n", otw->object_type, rc);
@@ -1293,7 +1293,7 @@ static int unload_module(void)
{
ast_cli_unregister_multiple(config_wizard_cli, ARRAY_LEN(config_wizard_cli));
ast_sorcery_global_observer_remove(&global_observer);
- AST_VECTOR_REMOVE_CMP_UNORDERED(&object_type_wizards, NULL, NOT_EQUALS, OTW_DELETE_CB);
+ AST_VECTOR_REMOVE_ALL_CMP_UNORDERED(&object_type_wizards, NULL, NOT_EQUALS, OTW_DELETE_CB);
AST_VECTOR_RW_FREE(&object_type_wizards);
return 0;
diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c
index 627f800a4..95fe6d3ea 100644
--- a/res/res_rtp_asterisk.c
+++ b/res/res_rtp_asterisk.c
@@ -4286,6 +4286,7 @@ static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance, unsigned int
int reconstruct = ntohl(rtpheader[0]);
struct ast_sockaddr remote_address = { {0,} };
int ice;
+ unsigned int timestamp = ntohl(rtpheader[1]);
/* Get fields from packet */
payload = (reconstruct & 0x7f0000) >> 16;
@@ -4314,6 +4315,22 @@ static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance, unsigned int
return -1;
}
+ /* If bridged peer is in dtmf, feed all packets to core until it finishes to avoid infinite dtmf */
+ if (bridged->sending_digit) {
+ ast_debug(1, "Feeding packets to core until DTMF finishes\n");
+ return -1;
+ }
+
+ /*
+ * Even if we are no longer in dtmf, we could still be receiving
+ * re-transmissions of the last dtmf end still. Feed those to the
+ * core so they can be filtered accordingly.
+ */
+ if (rtp->last_end_timestamp == timestamp) {
+ ast_debug(1, "Feeding packet with duplicate timestamp to core\n");
+ return -1;
+ }
+
/* If the marker bit has been explicitly set turn it on */
if (ast_test_flag(rtp, FLAG_NEED_MARKER_BIT)) {
mark = 1;