summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES25
-rw-r--r--Makefile102
-rw-r--r--apps/app_followme.c17
-rw-r--r--channels/chan_sip.c8
-rw-r--r--configs/basic-pbx/asterisk.conf25
-rw-r--r--configs/samples/hep.conf.sample4
-rw-r--r--configs/samples/pjsip.conf.sample2
-rwxr-xr-xconfigure14
-rw-r--r--configure.ac4
-rw-r--r--contrib/ast-db-manage/config/versions/837aa67461fb_add_pjsip_endpoint_ip_access_control_.py32
-rw-r--r--contrib/ast-db-manage/config/versions/837aa67461fb_ps_contacts_add_authenticate_qualify.py32
-rw-r--r--include/asterisk/parking.h11
-rw-r--r--include/asterisk/res_hep.h14
-rw-r--r--include/asterisk/res_pjsip.h4
-rw-r--r--include/asterisk/strings.h2
-rw-r--r--main/logger.c1
-rw-r--r--main/strings.c6
-rw-r--r--main/udptl.c12
-rw-r--r--res/ari/resource_bridges.c2
-rw-r--r--res/ari/resource_channels.c2
-rw-r--r--res/res_ari.c3
-rw-r--r--res/res_fax.c46
-rw-r--r--res/res_hep.c37
-rw-r--r--res/res_hep.exports.in1
-rw-r--r--res/res_hep_pjsip.c63
-rw-r--r--res/res_hep_rtcp.c33
-rw-r--r--res/res_pjsip.c57
-rw-r--r--res/res_pjsip/config_transport.c10
-rw-r--r--res/res_pjsip/location.c1
-rw-r--r--res/res_pjsip/pjsip_configuration.c71
-rw-r--r--res/res_pjsip/pjsip_distributor.c128
-rw-r--r--res/res_pjsip/pjsip_options.c64
-rw-r--r--res/res_pjsip_dtmf_info.c7
-rw-r--r--res/res_pjsip_empty_info.c89
-rw-r--r--res/res_pjsip_one_touch_record_info.c9
-rw-r--r--res/res_pjsip_outbound_publish.c187
-rw-r--r--res/res_pjsip_outbound_registration.c26
-rw-r--r--res/res_pjsip_publish_asterisk.c2
-rw-r--r--res/res_sorcery_astdb.c60
39 files changed, 995 insertions, 218 deletions
diff --git a/CHANGES b/CHANGES
index 9fe5ae7e0..63ca0bcf0 100644
--- a/CHANGES
+++ b/CHANGES
@@ -25,6 +25,15 @@ res_fax
res_pjsip
------------------
+ * Endpoint IP Access Controls
+ Added new configuration Endpoint options:
+ "acl" - list of IP ACL section names in acl.conf
+ "deny" - List of IP addresses to deny access from
+ "permit" - List of IP addresses to permit access from
+ "contact_acl" - List of Contact ACL section names in acl.conf
+ "contact_deny" - List of Contact header addresses to deny
+ "contact_permit" - List of Contact header addresses to permit
+
* Added new status Updated to AMI event ContactStatus on update registration
* Added "reg_server" to contacts.
@@ -32,6 +41,22 @@ res_pjsip
into the "reg_server" field in the ps_contacts table to facilitate
multi-server setups.
+res_hep
+------------------
+ * Added a new option, 'uuid_type', that sets the preferred source of the Homer
+ correlation UUID. The valid options are:
+ - call-id: Use the PJSIP SIP Call-ID header value
+ - channel: Use the Asterisk channel name
+ The default value is 'call-id'. In the event that a HEP module cannot find a
+ valid value using the specified 'uuid_type', the module may fallback to a
+ more readily available source for the correlation UUID.
+
+res_pjsip_info_empty
+--------------------
+ * A new module that can respond to empty Content-Type INFO packets during call.
+ Some SBCs will terminate a call if their empty INFO packets are not responded
+ to within a predefined time.
+
app_confbridge
------------------
* Added a bridge profile option called regcontext that allows you to
diff --git a/Makefile b/Makefile
index a717f4ab4..20b778762 100644
--- a/Makefile
+++ b/Makefile
@@ -825,60 +825,56 @@ install-logrotate:
rm -f contrib/scripts/asterisk.logrotate.tmp
config:
- @if [ "${OSARCH}" = "linux-gnu" -o "${OSARCH}" = "kfreebsd-gnu" ]; then \
- if [ -f /etc/redhat-release -o -f /etc/fedora-release ]; then \
- ./build_tools/install_subst contrib/init.d/rc.redhat.asterisk "$(DESTDIR)/etc/rc.d/init.d/asterisk"; \
- if [ ! -f "$(DESTDIR)/etc/sysconfig/asterisk" ] ; then \
- $(INSTALL) -m 644 contrib/init.d/etc_default_asterisk "$(DESTDIR)/etc/sysconfig/asterisk" ; \
- fi ; \
- if [ -z "$(DESTDIR)" ] ; then \
- /sbin/chkconfig --add asterisk ; \
- fi ; \
- elif [ -f /etc/debian_version ] ; then \
- ./build_tools/install_subst contrib/init.d/rc.debian.asterisk "$(DESTDIR)/etc/init.d/asterisk"; \
- if [ ! -f "$(DESTDIR)/etc/default/asterisk" ] ; then \
- $(INSTALL) -m 644 contrib/init.d/etc_default_asterisk "$(DESTDIR)/etc/default/asterisk" ; \
- fi ; \
- if [ -z "$(DESTDIR)" ] ; then \
- /usr/sbin/update-rc.d asterisk defaults 50 91 ; \
- fi ; \
- elif [ -f /etc/gentoo-release ] ; then \
- ./build_tools/install_subst contrib/init.d/rc.gentoo.asterisk "$(DESTDIR)/etc/init.d/asterisk"; \
- if [ -z "$(DESTDIR)" ] ; then \
- /sbin/rc-update add asterisk default ; \
- fi ; \
- elif [ -f /etc/mandrake-release -o -f /etc/mandriva-release ] ; then \
- ./build_tools/install_subst contrib/init.d/rc.mandriva.asterisk "$(DESTDIR)/etc/rc.d/init.d/asterisk"; \
- if [ ! -f /etc/sysconfig/asterisk ] ; then \
- $(INSTALL) -m 644 contrib/init.d/etc_default_asterisk "$(DESTDIR)/etc/sysconfig/asterisk" ; \
- fi ; \
- if [ -z "$(DESTDIR)" ] ; then \
- /sbin/chkconfig --add asterisk ; \
- fi ; \
- elif [ -f /etc/SuSE-release -o -f /etc/novell-release ] ; then \
- ./build_tools/install_subst contrib/init.d/rc.suse.asterisk "$(DESTDIR)/etc/init.d/asterisk"; \
- if [ ! -f /etc/sysconfig/asterisk ] ; then \
- $(INSTALL) -m 644 contrib/init.d/etc_default_asterisk "$(DESTDIR)/etc/sysconfig/asterisk" ; \
- fi ; \
- if [ -z "$(DESTDIR)" ] ; then \
- /sbin/chkconfig --add asterisk ; \
- fi ; \
- elif [ -f /etc/arch-release -o -f /etc/arch-release ] ; then \
- ./build_tools/install_subst contrib/init.d/rc.archlinux.asterisk "$(DESTDIR)/etc/init.d/asterisk"; \
- elif [ -d "$(DESTDIR)/Library/LaunchDaemons" ]; then \
- if [ ! -f "$(DESTDIR)/Library/LaunchDaemons/org.asterisk.asterisk.plist" ]; then \
- ./build_tools/install_subst contrib/init.d/org.asterisk.asterisk.plist "$(DESTDIR)/Library/LaunchDaemons/org.asterisk.asterisk.plist"; \
- fi; \
- if [ ! -f "$(DESTDIR)/Library/LaunchDaemons/org.asterisk.muted.plist" ]; then \
- ./build_tools/install_subst contrib/init.d/org.asterisk.muted.plist "$(DESTDIR)/Library/LaunchDaemons/org.asterisk.muted.plist"; \
- fi; \
- elif [ -f /etc/slackware-version ]; then \
- echo "Slackware is not currently supported, although an init script does exist for it."; \
- else \
- echo "We could not install init scripts for your distribution." ; \
- fi \
+ if [ -f /etc/redhat-release -o -f /etc/fedora-release ]; then \
+ ./build_tools/install_subst contrib/init.d/rc.redhat.asterisk "$(DESTDIR)/etc/rc.d/init.d/asterisk"; \
+ if [ ! -f "$(DESTDIR)/etc/sysconfig/asterisk" ] ; then \
+ $(INSTALL) -m 644 contrib/init.d/etc_default_asterisk "$(DESTDIR)/etc/sysconfig/asterisk" ; \
+ fi ; \
+ if [ -z "$(DESTDIR)" ] ; then \
+ /sbin/chkconfig --add asterisk ; \
+ fi ; \
+ elif [ -f /etc/debian_version ] ; then \
+ ./build_tools/install_subst contrib/init.d/rc.debian.asterisk "$(DESTDIR)/etc/init.d/asterisk"; \
+ if [ ! -f "$(DESTDIR)/etc/default/asterisk" ] ; then \
+ $(INSTALL) -m 644 contrib/init.d/etc_default_asterisk "$(DESTDIR)/etc/default/asterisk" ; \
+ fi ; \
+ if [ -z "$(DESTDIR)" ] ; then \
+ /usr/sbin/update-rc.d asterisk defaults 50 91 ; \
+ fi ; \
+ elif [ -f /etc/gentoo-release ] ; then \
+ ./build_tools/install_subst contrib/init.d/rc.gentoo.asterisk "$(DESTDIR)/etc/init.d/asterisk"; \
+ if [ -z "$(DESTDIR)" ] ; then \
+ /sbin/rc-update add asterisk default ; \
+ fi ; \
+ elif [ -f /etc/mandrake-release -o -f /etc/mandriva-release ] ; then \
+ ./build_tools/install_subst contrib/init.d/rc.mandriva.asterisk "$(DESTDIR)/etc/rc.d/init.d/asterisk"; \
+ if [ ! -f /etc/sysconfig/asterisk ] ; then \
+ $(INSTALL) -m 644 contrib/init.d/etc_default_asterisk "$(DESTDIR)/etc/sysconfig/asterisk" ; \
+ fi ; \
+ if [ -z "$(DESTDIR)" ] ; then \
+ /sbin/chkconfig --add asterisk ; \
+ fi ; \
+ elif [ -f /etc/SuSE-release -o -f /etc/novell-release ] ; then \
+ ./build_tools/install_subst contrib/init.d/rc.suse.asterisk "$(DESTDIR)/etc/init.d/asterisk"; \
+ if [ ! -f /etc/sysconfig/asterisk ] ; then \
+ $(INSTALL) -m 644 contrib/init.d/etc_default_asterisk "$(DESTDIR)/etc/sysconfig/asterisk" ; \
+ fi ; \
+ if [ -z "$(DESTDIR)" ] ; then \
+ /sbin/chkconfig --add asterisk ; \
+ fi ; \
+ elif [ -f /etc/arch-release -o -f /etc/arch-release ] ; then \
+ ./build_tools/install_subst contrib/init.d/rc.archlinux.asterisk "$(DESTDIR)/etc/init.d/asterisk"; \
+ elif [ -d "$(DESTDIR)/Library/LaunchDaemons" ]; then \
+ if [ ! -f "$(DESTDIR)/Library/LaunchDaemons/org.asterisk.asterisk.plist" ]; then \
+ ./build_tools/install_subst contrib/init.d/org.asterisk.asterisk.plist "$(DESTDIR)/Library/LaunchDaemons/org.asterisk.asterisk.plist"; \
+ fi; \
+ if [ ! -f "$(DESTDIR)/Library/LaunchDaemons/org.asterisk.muted.plist" ]; then \
+ ./build_tools/install_subst contrib/init.d/org.asterisk.muted.plist "$(DESTDIR)/Library/LaunchDaemons/org.asterisk.muted.plist"; \
+ fi; \
+ elif [ -f /etc/slackware-version ]; then \
+ echo "Slackware is not currently supported, although an init script does exist for it."; \
else \
- echo "We could not install init scripts for your operating system." ; \
+ echo "We could not install init scripts for your distribution." ; \
fi
sounds:
diff --git a/apps/app_followme.c b/apps/app_followme.c
index e5a5ee3c5..af6bb1039 100644
--- a/apps/app_followme.c
+++ b/apps/app_followme.c
@@ -66,6 +66,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/stasis_channels.h"
#include "asterisk/max_forwards.h"
+#define REC_FORMAT "sln"
+
/*** DOCUMENTATION
<application name="FollowMe" language="en_US">
<synopsis>
@@ -1421,7 +1423,7 @@ static int app_exec(struct ast_channel *chan, const char *data)
snprintf(targs->namerecloc, sizeof(targs->namerecloc), "%s/followme.%s",
ast_config_AST_SPOOL_DIR, ast_channel_uniqueid(chan));
- if (ast_play_and_record(chan, "vm-rec-name", targs->namerecloc, 5, "sln", &duration,
+ if (ast_play_and_record(chan, "vm-rec-name", targs->namerecloc, 5, REC_FORMAT, &duration,
NULL, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL) < 0) {
goto outrun;
}
@@ -1522,7 +1524,18 @@ outrun:
ast_free(nm);
}
if (!ast_strlen_zero(targs->namerecloc)) {
- unlink(targs->namerecloc);
+ int ret;
+ char fn[PATH_MAX];
+
+ snprintf(fn, sizeof(fn), "%s.%s", targs->namerecloc,
+ REC_FORMAT);
+ ret = unlink(fn);
+ if (ret != 0) {
+ ast_log(LOG_NOTICE, "Failed to delete recorded name file %s: %d (%s)\n",
+ fn, errno, strerror(errno));
+ } else {
+ ast_debug(2, "deleted recorded prompt %s.\n", fn);
+ }
}
ast_free((char *) targs->predial_callee);
ast_party_connected_line_free(&targs->connected_in);
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 7cc91486a..f64845472 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -14528,10 +14528,12 @@ static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init,
add_header(&req, "Require", "replaces");
}
- /* Add Session-Timers related headers */
- if (st_get_mode(p, 0) == SESSION_TIMER_MODE_ORIGINATE
+ /* Add Session-Timers related headers if not already there */
+ if (ast_strlen_zero(sip_get_header(&req, "Session-Expires")) &&
+ (sipmethod == SIP_INVITE || sipmethod == SIP_UPDATE) &&
+ (st_get_mode(p, 0) == SESSION_TIMER_MODE_ORIGINATE
|| (st_get_mode(p, 0) == SESSION_TIMER_MODE_ACCEPT
- && st_get_se(p, FALSE) != DEFAULT_MIN_SE)) {
+ && st_get_se(p, FALSE) != DEFAULT_MIN_SE))) {
char i2astr[10];
if (!p->stimer->st_interval) {
diff --git a/configs/basic-pbx/asterisk.conf b/configs/basic-pbx/asterisk.conf
index 576cc976b..ff66ceea7 100644
--- a/configs/basic-pbx/asterisk.conf
+++ b/configs/basic-pbx/asterisk.conf
@@ -1,26 +1,13 @@
-[directories]
-astetcdir => /etc/asterisk
-astmoddir => /usr/lib/asterisk/modules
-astvarlibdir => /var/lib/asterisk
-astdbdir => /var/lib/asterisk
-astkeydir => /var/lib/asterisk
-astdatadir => /var/lib/asterisk
-astagidir => /var/lib/asterisk/agi-bin
-astspooldir => /var/spool/asterisk
-astrundir => /var/run/asterisk
-astlogdir => /var/log/asterisk
-astsbindir => /usr/sbin
-
[options]
; If we want to start Asterisk with a default verbosity for the verbose
-; or debug logger channel types, then we use these settings.
+; or debug logger channel types, then we use these settings (by default
+; they are disabled).
;verbose = 5
-;debug = 5
+;debug = 2
; User and group to run asterisk as. NOTE: This will require changes to
; directory and device permissions.
-;runuser = asterisk ; The user to run as.
-;rungroup = asterisk ; The group to run as.
+;runuser = asterisk ; The user to run as. The default is root.
+;rungroup = asterisk ; The group to run as. The default is root
-defaultlanguage = en
-documentation_language = en_US
+;defaultlanguage = es
diff --git a/configs/samples/hep.conf.sample b/configs/samples/hep.conf.sample
index 40b17aa0e..6e409d151 100644
--- a/configs/samples/hep.conf.sample
+++ b/configs/samples/hep.conf.sample
@@ -13,4 +13,8 @@ capture_password = foo ; If specified, the authorization passsword
capture_id = 1234 ; A unique integer identifier for this
; server. This ID will be embedded sent
; with each packet from this server.
+uuid_type = call-id ; Specify the preferred source for the Homer
+ ; correlation UUID. Valid options are:
+ ; - 'call-id' for the PJSIP SIP Call-ID
+ ; - 'channel' for the Asterisk channel name
diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample
index a17dab7a7..3e007e48c 100644
--- a/configs/samples/pjsip.conf.sample
+++ b/configs/samples/pjsip.conf.sample
@@ -624,7 +624,7 @@
; identified.
; "username": Identify by the From or To username and domain
; "auth_username": Identify by the Authorization username and realm
- : In all cases, if an exact match on username and domain/realm fails,
+ ; In all cases, if an exact match on username and domain/realm fails,
; the match will be retried with just the username.
; (default: "username")
;redirect_method=user ; How redirects received from an endpoint are handled
diff --git a/configure b/configure
index 01604e0bb..ff34770a1 100755
--- a/configure
+++ b/configure
@@ -13612,7 +13612,7 @@ else
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -13658,7 +13658,7 @@ else
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -13682,7 +13682,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -13727,7 +13727,7 @@ else
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -13751,7 +13751,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -17614,7 +17614,7 @@ if ac_fn_c_try_compile "$LINENO"; then :
$as_echo "yes" >&6; }
AST_LEAK_SANITIZER=1
else
- AST_LEAK_SANITIZER=
+ AST_LEAK_SANITIZER=0
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
@@ -17646,7 +17646,7 @@ if ac_fn_c_try_compile "$LINENO"; then :
$as_echo "yes" >&6; }
AST_UNDEFINED_SANITIZER=1
else
- AST_UNDEFINED_SANITIZER=
+ AST_UNDEFINED_SANITIZER=0
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
diff --git a/configure.ac b/configure.ac
index 822303708..f2e42ba1f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1116,7 +1116,7 @@ AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM([], [int x = 1;])],
AC_MSG_RESULT(yes)
[AST_LEAK_SANITIZER=1],
- [AST_LEAK_SANITIZER=]
+ [AST_LEAK_SANITIZER=0]
AC_MSG_RESULT(no)
)
CFLAGS="${saved_sanitize_CFLAGS}"
@@ -1132,7 +1132,7 @@ AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM([], [int x = 1;])],
AC_MSG_RESULT(yes)
[AST_UNDEFINED_SANITIZER=1],
- [AST_UNDEFINED_SANITIZER=]
+ [AST_UNDEFINED_SANITIZER=0]
AC_MSG_RESULT(no)
)
CFLAGS="${saved_sanitize_CFLAGS}"
diff --git a/contrib/ast-db-manage/config/versions/837aa67461fb_add_pjsip_endpoint_ip_access_control_.py b/contrib/ast-db-manage/config/versions/837aa67461fb_add_pjsip_endpoint_ip_access_control_.py
new file mode 100644
index 000000000..56bd28499
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/837aa67461fb_add_pjsip_endpoint_ip_access_control_.py
@@ -0,0 +1,32 @@
+"""Add PJSIP Endpoint IP Access Control options
+
+Revision ID: bca7113d796f
+Revises: 6be31516058d
+Create Date: 2016-05-13 12:37:03.786359
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = 'bca7113d796f'
+down_revision = '6be31516058d'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+ op.add_column('ps_endpoints', sa.Column('deny', sa.String(95)))
+ op.add_column('ps_endpoints', sa.Column('permit', sa.String(95)))
+ op.add_column('ps_endpoints', sa.Column('acl', sa.String(40)))
+ op.add_column('ps_endpoints', sa.Column('contact_deny', sa.String(95)))
+ op.add_column('ps_endpoints', sa.Column('contact_permit', sa.String(95)))
+ op.add_column('ps_endpoints', sa.Column('contact_acl', sa.String(40)))
+
+
+def downgrade():
+ op.drop_column('ps_endpoints', 'contact_acl')
+ op.drop_column('ps_endpoints', 'contact_permit')
+ op.drop_column('ps_endpoints', 'contact_deny')
+ op.drop_column('ps_endpoints', 'acl')
+ op.drop_column('ps_endpoints', 'permit')
+ op.drop_column('ps_endpoints', 'deny')
diff --git a/contrib/ast-db-manage/config/versions/837aa67461fb_ps_contacts_add_authenticate_qualify.py b/contrib/ast-db-manage/config/versions/837aa67461fb_ps_contacts_add_authenticate_qualify.py
new file mode 100644
index 000000000..76faf394c
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/837aa67461fb_ps_contacts_add_authenticate_qualify.py
@@ -0,0 +1,32 @@
+"""ps_contacts add authenticate_qualify
+
+Revision ID: 6be31516058d
+Revises: 81b01a191a46
+Create Date: 2016-05-03 14:57:12.538179
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '6be31516058d'
+down_revision = '81b01a191a46'
+
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.dialects.postgresql import ENUM
+
+YESNO_NAME = 'yesno_values'
+YESNO_VALUES = ['yes', 'no']
+
+def upgrade():
+ ############################# Enums ##############################
+
+ # yesno_values have already been created, so use postgres enum object
+ # type to get around "already created" issue - works okay with mysql
+ yesno_values = ENUM(*YESNO_VALUES, name=YESNO_NAME, create_type=False)
+
+ op.add_column('ps_contacts', sa.Column('authenticate_qualify', yesno_values))
+
+
+def downgrade():
+ op.drop_column('ps_contacts', 'authenticate_qualify')
+
diff --git a/include/asterisk/parking.h b/include/asterisk/parking.h
index a8832cdb8..1cf750c08 100644
--- a/include/asterisk/parking.h
+++ b/include/asterisk/parking.h
@@ -211,12 +211,19 @@ int ast_parking_is_exten_park(const char *context, const char *exten);
* \brief Park the bridge and/or callers that this channel is in
*
* \param parker The bridge_channel parking the bridge
- * \param exten Optional. The extension the channel or bridge was parked at if the
- * call succeeds.
+ * \param[out] exten Optional. The parking exten to access the parking lot.
* \param length Optional. If \c exten is specified, the size of the buffer.
*
* \note This is safe to be called outside of the \ref AstBridging Bridging API.
*
+ * \note The exten parameter was intended to return the extension the channel or
+ * bridge was parked at if the call succeeds. However, accessing that information
+ * is very difficult to do with the new asynchronous design. That information may
+ * not be available anywhere by the time this function currently returns.
+ *
+ * Only, chan_skinny is known to call this function and use the exten parameter
+ * for the phone display.
+ *
* \retval 0 on success
* \retval non-zero on error
*/
diff --git a/include/asterisk/res_hep.h b/include/asterisk/res_hep.h
index 8839fd60a..bd0129eea 100644
--- a/include/asterisk/res_hep.h
+++ b/include/asterisk/res_hep.h
@@ -49,6 +49,11 @@ enum hepv3_capture_type {
HEPV3_CAPTURE_TYPE_IAX = 0x10,
};
+enum hep_uuid_type {
+ HEP_UUID_TYPE_CALL_ID = 0,
+ HEP_UUID_TYPE_CHANNEL,
+};
+
/*! \brief HEPv3 Capture Info */
struct hepv3_capture_info {
/*! The source address of the packet */
@@ -104,6 +109,15 @@ struct hepv3_capture_info *hepv3_create_capture_info(const void *payload, size_t
*/
int hepv3_send_packet(struct hepv3_capture_info *capture_info);
+/*!
+ * \brief Get the preferred UUID type
+ *
+ * \since 13.10.0
+ *
+ * \retval The type of UUID the packet should use
+ */
+enum hep_uuid_type hepv3_get_uuid_type(void);
+
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h
index f985e3254..d20d27e70 100644
--- a/include/asterisk/res_pjsip.h
+++ b/include/asterisk/res_pjsip.h
@@ -734,6 +734,10 @@ struct ast_sip_endpoint {
unsigned int usereqphone;
/*! Do we send messages for connected line updates for unanswered incoming calls immediately to this endpoint? */
unsigned int rpid_immediate;
+ /* Access control list */
+ struct ast_acl_list *acl;
+ /* Restrict what IPs are allowed in the Contact header (for registration) */
+ struct ast_acl_list *contact_acl;
};
/*!
diff --git a/include/asterisk/strings.h b/include/asterisk/strings.h
index 0e2f69ba8..2ca75a69c 100644
--- a/include/asterisk/strings.h
+++ b/include/asterisk/strings.h
@@ -688,7 +688,7 @@ void ast_str_trim_blanks(struct ast_str *buf),
if (!buf) {
return;
}
- while (buf->__AST_STR_USED && buf->__AST_STR_STR[buf->__AST_STR_USED - 1] < 33) {
+ while (buf->__AST_STR_USED && ((unsigned char) buf->__AST_STR_STR[buf->__AST_STR_USED - 1]) < 33) {
buf->__AST_STR_STR[--(buf->__AST_STR_USED)] = '\0';
}
}
diff --git a/main/logger.c b/main/logger.c
index 9db33c954..9f03b4efa 100644
--- a/main/logger.c
+++ b/main/logger.c
@@ -388,6 +388,7 @@ static struct logchannel *make_logchannel(const char *channel, const char *compo
}
chan->type = LOGTYPE_SYSLOG;
+ openlog("asterisk", LOG_PID, chan->facility);
} else {
if (!(chan->fileptr = fopen(chan->filename, "a"))) {
/* Can't do real logging here since we're called with a lock
diff --git a/main/strings.c b/main/strings.c
index 9e885ebc3..db78a6cd2 100644
--- a/main/strings.c
+++ b/main/strings.c
@@ -234,8 +234,8 @@ int ast_strings_match(const char *left, const char *op, const char *right)
{
char *internal_op = (char *)op;
char *internal_right = (char *)right;
- float left_num;
- float right_num;
+ double left_num;
+ double right_num;
int scan_numeric = 0;
if (!(left && right)) {
@@ -297,7 +297,7 @@ regex:
}
equals:
- scan_numeric = (sscanf(left, "%f", &left_num) && sscanf(internal_right, "%f", &right_num));
+ scan_numeric = (sscanf(left, "%lf", &left_num) && sscanf(internal_right, "%lf", &right_num));
if (internal_op[0] == '=') {
if (ast_strlen_zero(left) && ast_strlen_zero(internal_right)) {
diff --git a/main/udptl.c b/main/udptl.c
index e8410cc8b..a568cd1ec 100644
--- a/main/udptl.c
+++ b/main/udptl.c
@@ -769,6 +769,18 @@ struct ast_frame *ast_udptl_read(struct ast_udptl *udptl)
return &ast_null_frame;
}
+ /*
+ * If early media isn't turned on for the channel driver, it's going to
+ * drop this frame. By that time though, udptl has already incremented
+ * the expected sequence number so if the CPE re-sends, the second frame
+ * will be dropped as a dup even though the first frame never went through.
+ * So we drop the frame here if the channel isn't up. 'tag' is set by the
+ * channel drivers on T38_ENABLED or T38_PEER_REINVITE.
+ */
+ if (udptl->tag == NULL) {
+ return &ast_null_frame;
+ }
+
if (udptl->nat) {
/* Send to whoever sent to us */
if (ast_sockaddr_cmp(&udptl->them, &addr)) {
diff --git a/res/ari/resource_bridges.c b/res/ari/resource_bridges.c
index a37b83146..a86f3129c 100644
--- a/res/ari/resource_bridges.c
+++ b/res/ari/resource_bridges.c
@@ -381,7 +381,7 @@ static int ari_bridges_play_helper(const char *args_media,
return -1;
}
- if (ast_asprintf(playback_url, "/playback/%s",
+ if (ast_asprintf(playback_url, "/playbacks/%s",
stasis_app_playback_get_id(playback)) == -1) {
playback_url = NULL;
ast_ari_response_alloc_failed(response);
diff --git a/res/ari/resource_channels.c b/res/ari/resource_channels.c
index f722802d8..9e2db9de6 100644
--- a/res/ari/resource_channels.c
+++ b/res/ari/resource_channels.c
@@ -524,7 +524,7 @@ static void ari_channels_handle_play(
return;
}
- if (ast_asprintf(&playback_url, "/playback/%s",
+ if (ast_asprintf(&playback_url, "/playbacks/%s",
stasis_app_playback_get_id(playback)) == -1) {
playback_url = NULL;
ast_ari_response_error(
diff --git a/res/res_ari.c b/res/res_ari.c
index f39db16cd..4a0a22d79 100644
--- a/res/res_ari.c
+++ b/res/res_ari.c
@@ -304,10 +304,11 @@ void ast_ari_response_alloc_failed(struct ast_ari_response *response)
void ast_ari_response_created(struct ast_ari_response *response,
const char *url, struct ast_json *message)
{
+ RAII_VAR(struct stasis_rest_handlers *, root, get_root_handler(), ao2_cleanup);
response->message = message;
response->response_code = 201;
response->response_text = "Created";
- ast_str_append(&response->headers, 0, "Location: %s\r\n", url);
+ ast_str_append(&response->headers, 0, "Location: /%s%s\r\n", root->path_segment, url);
}
static void add_allow_header(struct stasis_rest_handlers *handler,
diff --git a/res/res_fax.c b/res/res_fax.c
index 2fa64bc0f..6282b13d7 100644
--- a/res/res_fax.c
+++ b/res/res_fax.c
@@ -626,6 +626,8 @@ static const struct ast_datastore_info fax_datastore = {
static int fax_gateway_attach(struct ast_channel *chan, struct ast_fax_session_details *details);
static int fax_detect_attach(struct ast_channel *chan, int timeout, int flags);
static struct ast_fax_session_details *find_or_create_details(struct ast_channel *chan);
+static struct ast_fax_session *fax_v21_session_new (struct ast_channel *chan);
+
/*! \brief Copies fax detection and gateway framehooks during masquerades
*
@@ -2835,6 +2837,23 @@ static void destroy_gateway(void *data)
ao2_cleanup(gateway->peer_write_format);
}
+static struct ast_fax_session *fax_v21_session_new (struct ast_channel *chan) {
+ struct ast_fax_session_details *v21_details;
+ struct ast_fax_session *v21_session;
+
+ if (!chan || !(v21_details = session_details_new())) {
+ return NULL;
+ }
+
+ v21_details->caps = AST_FAX_TECH_V21_DETECT;
+ if (!(v21_session = fax_session_new(v21_details, chan, NULL, NULL))) {
+ ao2_ref(v21_details, -1);
+ return NULL;
+ }
+
+ return v21_session;
+}
+
/*! \brief Create a new fax gateway object.
* \param chan the channel the gateway object will be attached to
* \param details the fax session details
@@ -2843,30 +2862,16 @@ static void destroy_gateway(void *data)
static struct fax_gateway *fax_gateway_new(struct ast_channel *chan, struct ast_fax_session_details *details)
{
struct fax_gateway *gateway = ao2_alloc(sizeof(*gateway), destroy_gateway);
- struct ast_fax_session_details *v21_details;
if (!gateway) {
return NULL;
}
- if (!(v21_details = session_details_new())) {
+ if (!(gateway->chan_v21_session = fax_v21_session_new(chan))) {
+ ast_log(LOG_ERROR, "Can't create V21 session on chan %s for T.38 gateway session\n", ast_channel_name(chan));
ao2_ref(gateway, -1);
return NULL;
}
- v21_details->caps = AST_FAX_TECH_V21_DETECT;
- if (!(gateway->chan_v21_session = fax_session_new(v21_details, chan, NULL, NULL))) {
- ao2_ref(v21_details, -1);
- ao2_ref(gateway, -1);
- return NULL;
- }
-
- if (!(gateway->peer_v21_session = fax_session_new(v21_details, chan, NULL, NULL))) {
- ao2_ref(v21_details, -1);
- ao2_ref(gateway, -1);
- return NULL;
- }
- ao2_ref(v21_details, -1);
-
gateway->framehook = -1;
details->caps = AST_FAX_TECH_GATEWAY;
@@ -3360,6 +3365,11 @@ static struct ast_frame *fax_gateway_framehook(struct ast_channel *chan, struct
ast_channel_unlock(peer);
gateway->bridged = 1;
+ if (!(gateway->peer_v21_session = fax_v21_session_new(peer))) {
+ ast_log(LOG_ERROR, "Can't create V21 session on chan %s for T.38 gateway session\n", ast_channel_name(peer));
+ ast_framehook_detach(chan, gateway->framehook);
+ return f;
+ }
}
if (gateway->bridged && !ast_tvzero(gateway->timeout_start)) {
@@ -3486,6 +3496,10 @@ static int fax_gateway_attach(struct ast_channel *chan, struct ast_fax_session_d
.disable_inheritance = 1, /* Masquerade inheritance is handled through the datastore fixup */
};
+ if (global_fax_debug) {
+ details->option.debug = AST_FAX_OPTFLAG_TRUE;
+ }
+
ast_string_field_set(details, result, "SUCCESS");
ast_string_field_set(details, resultstr, "gateway operation started successfully");
ast_string_field_set(details, error, "NO_ERROR");
diff --git a/res/res_hep.c b/res/res_hep.c
index 69a8ab391..723b27df8 100644
--- a/res/res_hep.c
+++ b/res/res_hep.c
@@ -60,6 +60,15 @@
</enumlist>
</description>
</configOption>
+ <configOption name="uuid_type" default="call-id">
+ <synopsis>The preferred type of UUID to pass to Homer.</synopsis>
+ <description>
+ <enumlist>
+ <enum name="call-id"><para>Use the PJSIP Call-Id</para></enum>
+ <enum name="channel"><para>Use the Asterisk channel name</para></enum>
+ </enumlist>
+ </description>
+ </configOption>
<configOption name="capture_address" default="192.168.1.1:9061">
<synopsis>The address and port of the Homer server to send packets to.</synopsis>
</configOption>
@@ -231,6 +240,7 @@ struct hep_generic {
struct hepv3_global_config {
unsigned int enabled; /*!< Whether or not sending is enabled */
unsigned int capture_id; /*!< Capture ID for this agent */
+ enum hep_uuid_type uuid_type; /*!< The preferred type of the UUID */
AST_DECLARE_STRING_FIELDS(
AST_STRING_FIELD(capture_address); /*!< Address to send to */
AST_STRING_FIELD(capture_password); /*!< Password for Homer server */
@@ -329,6 +339,25 @@ static void *module_config_alloc(void)
return config;
}
+/*! \brief Handler for the uuid_type attribute */
+static int uuid_type_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
+{
+ struct hepv3_global_config *global_config = obj;
+
+ if (strcasecmp(var->name, "uuid_type")) {
+ return -1;
+ }
+
+ if (!strcasecmp(var->value, "channel")) {
+ global_config->uuid_type = HEP_UUID_TYPE_CHANNEL;
+ } else if (!strcasecmp(var->value, "call-id")) {
+ global_config->uuid_type = HEP_UUID_TYPE_CALL_ID;
+ } else {
+ return -1;
+ }
+ return 0;
+}
+
/*! \brief HEPv3 run-time data destructor */
static void hepv3_data_dtor(void *obj)
{
@@ -376,6 +405,13 @@ static void capture_info_dtor(void *obj)
ast_free(info->payload);
}
+enum hep_uuid_type hepv3_get_uuid_type(void)
+{
+ RAII_VAR(struct module_config *, config, ao2_global_obj_ref(global_config), ao2_cleanup);
+
+ return config->general->uuid_type;
+}
+
struct hepv3_capture_info *hepv3_create_capture_info(const void *payload, size_t len)
{
struct hepv3_capture_info *info;
@@ -607,6 +643,7 @@ static int load_module(void)
aco_option_register(&cfg_info, "capture_address", ACO_EXACT, global_options, DEFAULT_HEP_SERVER, OPT_STRINGFIELD_T, 0, STRFLDSET(struct hepv3_global_config, capture_address));
aco_option_register(&cfg_info, "capture_password", ACO_EXACT, global_options, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct hepv3_global_config, capture_password));
aco_option_register(&cfg_info, "capture_id", ACO_EXACT, global_options, "0", OPT_UINT_T, 0, STRFLDSET(struct hepv3_global_config, capture_id));
+ aco_option_register_custom(&cfg_info, "uuid_type", ACO_EXACT, global_options, "call-id", uuid_type_handler, 0);
if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
goto error;
diff --git a/res/res_hep.exports.in b/res/res_hep.exports.in
index d09d3f409..df0f2b4f7 100644
--- a/res/res_hep.exports.in
+++ b/res/res_hep.exports.in
@@ -2,6 +2,7 @@
global:
LINKER_SYMBOL_PREFIX*hepv3_send_packet;
LINKER_SYMBOL_PREFIX*hepv3_create_capture_info;
+ LINKER_SYMBOL_PREFIX*hepv3_get_uuid_type;
local:
*;
};
diff --git a/res/res_hep_pjsip.c b/res/res_hep_pjsip.c
index b5cf0b81e..936db9300 100644
--- a/res/res_hep_pjsip.c
+++ b/res/res_hep_pjsip.c
@@ -51,13 +51,18 @@ static char *assign_uuid(const pj_str_t *call_id, const pj_str_t *local_tag, con
RAII_VAR(struct ast_sip_session *, session, NULL, ao2_cleanup);
pjsip_dialog *dlg;
char *uuid = NULL;
+ enum hep_uuid_type uuid_type = hepv3_get_uuid_type();
- if ((dlg = pjsip_ua_find_dialog(call_id, local_tag, remote_tag, PJ_FALSE))
+ if ((uuid_type == HEP_UUID_TYPE_CHANNEL)
+ && (dlg = pjsip_ua_find_dialog(call_id, local_tag, remote_tag, PJ_FALSE))
&& (session = ast_sip_dialog_get_session(dlg))
&& (session->channel)) {
uuid = ast_strdup(ast_channel_name(session->channel));
- } else {
+ }
+
+ /* If we couldn't get the channel or we never wanted it, default to the call-id */
+ if (!uuid) {
uuid = ast_malloc(pj_strlen(call_id) + 1);
if (uuid) {
@@ -77,13 +82,35 @@ static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata)
pjsip_cid_hdr *cid_hdr;
pjsip_from_hdr *from_hdr;
pjsip_to_hdr *to_hdr;
+ pjsip_tpmgr_fla2_param prm;
capture_info = hepv3_create_capture_info(tdata->buf.start, (size_t)(tdata->buf.cur - tdata->buf.start));
if (!capture_info) {
return PJ_SUCCESS;
}
- pj_sockaddr_print(&tdata->tp_info.transport->local_addr, local_buf, sizeof(local_buf), 3);
+ /* Attempt to determine what IP address will we send this packet out of */
+ pjsip_tpmgr_fla2_param_default(&prm);
+ prm.tp_type = tdata->tp_info.transport->key.type;
+ pj_strset2(&prm.dst_host, tdata->tp_info.dst_name);
+ prm.local_if = PJ_TRUE;
+
+ /* If we can't get the local address use what we have already */
+ if (pjsip_tpmgr_find_local_addr2(pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()), tdata->pool, &prm) != PJ_SUCCESS) {
+ pj_sockaddr_print(&tdata->tp_info.transport->local_addr, local_buf, sizeof(local_buf), 3);
+ } else {
+ if (prm.tp_type & PJSIP_TRANSPORT_IPV6) {
+ snprintf(local_buf, sizeof(local_buf), "[%.*s]:%hu",
+ (int)pj_strlen(&prm.ret_addr),
+ pj_strbuf(&prm.ret_addr),
+ prm.ret_port);
+ } else {
+ snprintf(local_buf, sizeof(local_buf), "%.*s:%hu",
+ (int)pj_strlen(&prm.ret_addr),
+ pj_strbuf(&prm.ret_addr),
+ prm.ret_port);
+ }
+ }
pj_sockaddr_print(&tdata->tp_info.dst_addr, remote_buf, sizeof(remote_buf), 3);
cid_hdr = PJSIP_MSG_CID_HDR(tdata->msg);
@@ -115,17 +142,39 @@ static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata)
char remote_buf[256];
char *uuid;
struct hepv3_capture_info *capture_info;
+ pjsip_tpmgr_fla2_param prm;
capture_info = hepv3_create_capture_info(&rdata->pkt_info.packet, rdata->pkt_info.len);
if (!capture_info) {
return PJ_SUCCESS;
}
- if (rdata->tp_info.transport->addr_len) {
- pj_sockaddr_print(&rdata->tp_info.transport->local_addr, local_buf, sizeof(local_buf), 3);
+ if (!rdata->pkt_info.src_addr_len) {
+ return PJ_SUCCESS;
}
- if (rdata->pkt_info.src_addr_len) {
- pj_sockaddr_print(&rdata->pkt_info.src_addr, remote_buf, sizeof(remote_buf), 3);
+ pj_sockaddr_print(&rdata->pkt_info.src_addr, remote_buf, sizeof(remote_buf), 3);
+
+ /* Attempt to determine what IP address we probably received this packet on */
+ pjsip_tpmgr_fla2_param_default(&prm);
+ prm.tp_type = rdata->tp_info.transport->key.type;
+ pj_strset2(&prm.dst_host, rdata->pkt_info.src_name);
+ prm.local_if = PJ_TRUE;
+
+ /* If we can't get the local address use what we have already */
+ if (pjsip_tpmgr_find_local_addr2(pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()), rdata->tp_info.pool, &prm) != PJ_SUCCESS) {
+ pj_sockaddr_print(&rdata->tp_info.transport->local_addr, local_buf, sizeof(local_buf), 3);
+ } else {
+ if (prm.tp_type & PJSIP_TRANSPORT_IPV6) {
+ snprintf(local_buf, sizeof(local_buf), "[%.*s]:%hu",
+ (int)pj_strlen(&prm.ret_addr),
+ pj_strbuf(&prm.ret_addr),
+ prm.ret_port);
+ } else {
+ snprintf(local_buf, sizeof(local_buf), "%.*s:%hu",
+ (int)pj_strlen(&prm.ret_addr),
+ pj_strbuf(&prm.ret_addr),
+ prm.ret_port);
+ }
}
uuid = assign_uuid(&rdata->msg_info.cid->id, &rdata->msg_info.to->tag, &rdata->msg_info.from->tag);
diff --git a/res/res_hep_rtcp.c b/res/res_hep_rtcp.c
index 787512bbf..49a92539f 100644
--- a/res/res_hep_rtcp.c
+++ b/res/res_hep_rtcp.c
@@ -36,6 +36,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/res_hep.h"
#include "asterisk/module.h"
#include "asterisk/netsock2.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
#include "asterisk/stasis.h"
#include "asterisk/rtp_engine.h"
#include "asterisk/json.h"
@@ -43,6 +45,35 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
static struct stasis_subscription *stasis_rtp_subscription;
+static char *assign_uuid(struct ast_json *json_channel)
+{
+ const char *channel_name = ast_json_string_get(ast_json_object_get(json_channel, "name"));
+ enum hep_uuid_type uuid_type = hepv3_get_uuid_type();
+ char *uuid = NULL;
+
+ if (!channel_name) {
+ return NULL;
+ }
+
+ if (uuid_type == HEP_UUID_TYPE_CALL_ID && ast_begins_with(channel_name, "PJSIP")) {
+ struct ast_channel *chan = ast_channel_get_by_name(channel_name);
+ char buf[128];
+
+ if (chan && !ast_func_read(chan, "CHANNEL(pjsip,call-id)", buf, sizeof(buf))) {
+ uuid = ast_strdup(buf);
+ }
+
+ ast_channel_cleanup(chan);
+ }
+
+ /* If we couldn't get the call-id or didn't want it, just use the channel name */
+ if (!uuid) {
+ uuid = ast_strdup(channel_name);
+ }
+
+ return uuid;
+}
+
static void rtcp_message_handler(struct stasis_message *message)
{
@@ -94,7 +125,7 @@ static void rtcp_message_handler(struct stasis_message *message)
ast_sockaddr_parse(&capture_info->src_addr, ast_json_string_get(from), PARSE_PORT_REQUIRE);
ast_sockaddr_parse(&capture_info->dst_addr, ast_json_string_get(to), PARSE_PORT_REQUIRE);
- capture_info->uuid = ast_strdup(ast_json_string_get(ast_json_object_get(json_channel, "name")));
+ capture_info->uuid = assign_uuid(json_channel);
if (!capture_info->uuid) {
ao2_ref(capture_info, -1);
return;
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index ae7155b6b..9162431a1 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -846,6 +846,56 @@
channel is hung up. By default this option is set to 0, which means do not check.
</para></description>
</configOption>
+ <configOption name="acl">
+ <synopsis>List of IP ACL section names in acl.conf</synopsis>
+ <description><para>
+ This matches sections configured in <literal>acl.conf</literal>. The value is
+ defined as a list of comma-delimited section names.
+ </para></description>
+ </configOption>
+ <configOption name="deny">
+ <synopsis>List of IP addresses to deny access from</synopsis>
+ <description><para>
+ The value is a comma-delimited list of IP addresses. IP addresses may
+ have a subnet mask appended. The subnet mask may be written in either
+ CIDR or dotted-decimal notation. Separate the IP address and subnet
+ mask with a slash ('/')
+ </para></description>
+ </configOption>
+ <configOption name="permit">
+ <synopsis>List of IP addresses to permit access from</synopsis>
+ <description><para>
+ The value is a comma-delimited list of IP addresses. IP addresses may
+ have a subnet mask appended. The subnet mask may be written in either
+ CIDR or dotted-decimal notation. Separate the IP address and subnet
+ mask with a slash ('/')
+ </para></description>
+ </configOption>
+ <configOption name="contact_acl">
+ <synopsis>List of Contact ACL section names in acl.conf</synopsis>
+ <description><para>
+ This matches sections configured in <literal>acl.conf</literal>. The value is
+ defined as a list of comma-delimited section names.
+ </para></description>
+ </configOption>
+ <configOption name="contact_deny">
+ <synopsis>List of Contact header addresses to deny</synopsis>
+ <description><para>
+ The value is a comma-delimited list of IP addresses. IP addresses may
+ have a subnet mask appended. The subnet mask may be written in either
+ CIDR or dotted-decimal notation. Separate the IP address and subnet
+ mask with a slash ('/')
+ </para></description>
+ </configOption>
+ <configOption name="contact_permit">
+ <synopsis>List of Contact header addresses to permit</synopsis>
+ <description><para>
+ The value is a comma-delimited list of IP addresses. IP addresses may
+ have a subnet mask appended. The subnet mask may be written in either
+ CIDR or dotted-decimal notation. Separate the IP address and subnet
+ mask with a slash ('/')
+ </para></description>
+ </configOption>
</configObject>
<configObject name="auth">
<synopsis>Authentication type</synopsis>
@@ -1092,6 +1142,13 @@
If <literal>0</literal> no timeout. Time in fractional seconds.
</para></description>
</configOption>
+ <configOption name="authenticate_qualify" default="no">
+ <synopsis>Authenticates a qualify request if needed</synopsis>
+ <description><para>
+ If true and a qualify request receives a challenge or authenticate response
+ authentication is attempted before declaring the contact available.
+ </para></description>
+ </configOption>
<configOption name="outbound_proxy">
<synopsis>Outbound proxy used when sending OPTIONS request</synopsis>
<description><para>
diff --git a/res/res_pjsip/config_transport.c b/res/res_pjsip/config_transport.c
index d2c087487..b9208976f 100644
--- a/res/res_pjsip/config_transport.c
+++ b/res/res_pjsip/config_transport.c
@@ -378,6 +378,10 @@ static struct ast_sip_transport_state *find_or_create_temporary_state(struct ast
new_state->type = transport->type;
pjsip_tls_setting_default(&new_state->tls);
+#ifdef HAVE_PJSIP_TLS_TRANSPORT_PROTO
+ /* proto must be forced to 0 to enable all protocols otherwise only TLS will work */
+ new_state->tls.proto = 0;
+#endif
new_state->tls.ciphers = new_state->ciphers;
ao2_ref(new_state, +1);
@@ -558,11 +562,17 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
}
} else if (transport->type == AST_TRANSPORT_TCP) {
pjsip_tcp_transport_cfg cfg;
+ int option = 1;
pjsip_tcp_transport_cfg_default(&cfg, temp_state->state->host.addr.sa_family);
cfg.bind_addr = temp_state->state->host;
cfg.async_cnt = transport->async_operations;
set_qos(transport, &cfg.qos_params);
+ cfg.sockopt_params.options[0].level = pj_SOL_TCP();
+ cfg.sockopt_params.options[0].optname = pj_TCP_NODELAY();
+ cfg.sockopt_params.options[0].optval = &option;
+ cfg.sockopt_params.options[0].optlen = sizeof(option);
+ cfg.sockopt_params.cnt = 1;
for (i = 0; i < BIND_TRIES && res != PJ_SUCCESS; i++) {
if (perm_state && perm_state->state && perm_state->state->factory
diff --git a/res/res_pjsip/location.c b/res/res_pjsip/location.c
index db1d0794b..ef06456a7 100644
--- a/res/res_pjsip/location.c
+++ b/res/res_pjsip/location.c
@@ -1121,6 +1121,7 @@ int ast_sip_initialize_sorcery_location(void)
ast_sorcery_object_field_register(sorcery, "contact", "qualify_frequency", 0, OPT_UINT_T,
PARSE_IN_RANGE, FLDSET(struct ast_sip_contact, qualify_frequency), 0, 86400);
ast_sorcery_object_field_register(sorcery, "contact", "qualify_timeout", "3.0", OPT_DOUBLE_T, 0, FLDSET(struct ast_sip_contact, qualify_timeout));
+ ast_sorcery_object_field_register(sorcery, "contact", "authenticate_qualify", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_contact, authenticate_qualify));
ast_sorcery_object_field_register(sorcery, "contact", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, outbound_proxy));
ast_sorcery_object_field_register(sorcery, "contact", "user_agent", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, user_agent));
ast_sorcery_object_field_register(sorcery, "contact", "reg_server", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, reg_server));
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index 8e7e95a5d..2e0df75e3 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -265,6 +265,65 @@ static const struct ast_sorcery_observer endpoint_observers = {
.deleted = endpoint_deleted_observer,
};
+static int endpoint_acl_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
+{
+ struct ast_sip_endpoint *endpoint = obj;
+ int error = 0;
+ int ignore;
+
+ if (ast_strlen_zero(var->value)) return 0;
+
+ if (!strncmp(var->name, "contact_", 8)) {
+ ast_append_acl(var->name + 8, var->value, &endpoint->contact_acl, &error, &ignore);
+ } else {
+ ast_append_acl(var->name, var->value, &endpoint->acl, &error, &ignore);
+ }
+
+ return error;
+}
+
+static int acl_to_str(const void *obj, const intptr_t *args, char **buf)
+{
+ const struct ast_sip_endpoint *endpoint = obj;
+ struct ast_acl_list *acl_list;
+ struct ast_acl *first_acl;
+
+ if (endpoint && !ast_acl_list_is_empty(acl_list=endpoint->acl)) {
+ AST_LIST_LOCK(acl_list);
+ first_acl = AST_LIST_FIRST(acl_list);
+ if (ast_strlen_zero(first_acl->name)) {
+ *buf = "deny/permit";
+ } else {
+ *buf = first_acl->name;
+ }
+ AST_LIST_UNLOCK(acl_list);
+ }
+
+ *buf = ast_strdup(*buf);
+ return 0;
+}
+
+static int contact_acl_to_str(const void *obj, const intptr_t *args, char **buf)
+{
+ const struct ast_sip_endpoint *endpoint = obj;
+ struct ast_acl_list *acl_list;
+ struct ast_acl *first_acl;
+
+ if (endpoint && !ast_acl_list_is_empty(acl_list=endpoint->contact_acl)) {
+ AST_LIST_LOCK(acl_list);
+ first_acl = AST_LIST_FIRST(acl_list);
+ if (ast_strlen_zero(first_acl->name)) {
+ *buf = "deny/permit";
+ } else {
+ *buf = first_acl->name;
+ }
+ AST_LIST_UNLOCK(acl_list);
+ }
+
+ *buf = ast_strdup(*buf);
+ return 0;
+}
+
static int dtmf_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
{
struct ast_sip_endpoint *endpoint = obj;
@@ -275,8 +334,8 @@ static int dtmf_handler(const struct aco_option *opt, struct ast_variable *var,
endpoint->dtmf = AST_SIP_DTMF_INBAND;
} else if (!strcasecmp(var->value, "info")) {
endpoint->dtmf = AST_SIP_DTMF_INFO;
- } else if (!strcasecmp(var->value, "auto")) {
- endpoint->dtmf = AST_SIP_DTMF_AUTO;
+ } else if (!strcasecmp(var->value, "auto")) {
+ endpoint->dtmf = AST_SIP_DTMF_AUTO;
} else if (!strcasecmp(var->value, "none")) {
endpoint->dtmf = AST_SIP_DTMF_NONE;
} else {
@@ -298,7 +357,7 @@ static int dtmf_to_str(const void *obj, const intptr_t *args, char **buf)
case AST_SIP_DTMF_INFO :
*buf = "info"; break;
case AST_SIP_DTMF_AUTO :
- *buf = "auto"; break;
+ *buf = "auto"; break;
default:
*buf = "none";
}
@@ -1762,6 +1821,12 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "set_var", "", set_var_handler, set_var_to_str, set_var_to_vl, 0, 0);
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "message_context", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, message_context));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "accountcode", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, accountcode));
+ ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "deny", "", endpoint_acl_handler, NULL, NULL, 0, 0);
+ ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "permit", "", endpoint_acl_handler, NULL, NULL, 0, 0);
+ ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "acl", "", endpoint_acl_handler, acl_to_str, NULL, 0, 0);
+ ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "contact_deny", "", endpoint_acl_handler, NULL, NULL, 0, 0);
+ ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "contact_permit", "", endpoint_acl_handler, NULL, NULL, 0, 0);
+ ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "contact_acl", "", endpoint_acl_handler, contact_acl_to_str, NULL, 0, 0);
if (ast_sip_initialize_sorcery_transport()) {
ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");
diff --git a/res/res_pjsip/pjsip_distributor.c b/res/res_pjsip/pjsip_distributor.c
index cbe955728..2ab954eb0 100644
--- a/res/res_pjsip/pjsip_distributor.c
+++ b/res/res_pjsip/pjsip_distributor.c
@@ -21,6 +21,7 @@
#include <pjsip.h>
#include "asterisk/res_pjsip.h"
+#include "asterisk/acl.h"
#include "include/res_pjsip_private.h"
#include "asterisk/taskprocessor.h"
#include "asterisk/threadpool.h"
@@ -230,20 +231,33 @@ static pjsip_dialog *find_dialog(pjsip_rx_data *rdata)
if (rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG ||
pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_cancel_method) ||
rdata->msg_info.to->tag.slen != 0) {
- return pjsip_ua_find_dialog(&rdata->msg_info.cid->id, local_tag,
+ dlg = pjsip_ua_find_dialog(&rdata->msg_info.cid->id, local_tag,
remote_tag, PJ_TRUE);
+ if (dlg) {
+ return dlg;
+ }
}
- /* Incoming CANCEL without a to-tag can't use same method for finding the
- * dialog. Instead, we have to find the matching INVITE transaction and
- * then get the dialog from the transaction
+ /*
+ * There may still be a matching dialog if this is
+ * 1) an incoming CANCEL request without a to-tag
+ * 2) an incoming response to a dialog-creating request.
*/
- pjsip_tsx_create_key(rdata->tp_info.pool, &tsx_key, PJSIP_ROLE_UAS,
- pjsip_get_invite_method(), rdata);
+ if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) {
+ /* CANCEL requests will need to match the INVITE we initially received. Any
+ * other request type will either have been matched already or is not in
+ * dialog
+ */
+ pjsip_tsx_create_key(rdata->tp_info.pool, &tsx_key, PJSIP_ROLE_UAS,
+ pjsip_get_invite_method(), rdata);
+ } else {
+ pjsip_tsx_create_key(rdata->tp_info.pool, &tsx_key, PJSIP_ROLE_UAC,
+ &rdata->msg_info.cseq->method, rdata);
+ }
tsx = pjsip_tsx_layer_find_tsx(&tsx_key, PJ_TRUE);
if (!tsx) {
- ast_log(LOG_ERROR, "Could not find matching INVITE transaction for CANCEL request\n");
+ ast_debug(3, "Could not find matching transaction for %s\n", rdata->msg_info.info);
return NULL;
}
@@ -380,19 +394,21 @@ struct ast_sip_endpoint *ast_sip_get_artificial_endpoint(void)
return artificial_endpoint;
}
-static void log_unidentified_request(pjsip_rx_data *rdata, unsigned int count, unsigned int period)
+static void log_failed_request(pjsip_rx_data *rdata, char *msg, unsigned int count, unsigned int period)
{
char from_buf[PJSIP_MAX_URL_SIZE];
char callid_buf[PJSIP_MAX_URL_SIZE];
+ char method_buf[PJSIP_MAX_URL_SIZE];
pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, rdata->msg_info.from->uri, from_buf, PJSIP_MAX_URL_SIZE);
ast_copy_pj_str(callid_buf, &rdata->msg_info.cid->id, PJSIP_MAX_URL_SIZE);
+ ast_copy_pj_str(method_buf, &rdata->msg_info.msg->line.req.method.name, PJSIP_MAX_URL_SIZE);
if (count) {
- ast_log(LOG_NOTICE, "Request from '%s' failed for '%s:%d' (callid: %s) - No matching endpoint found"
+ ast_log(LOG_NOTICE, "Request '%s' from '%s' failed for '%s:%d' (callid: %s) - %s"
" after %u tries in %.3f ms\n",
- from_buf, rdata->pkt_info.src_name, rdata->pkt_info.src_port, callid_buf, count, period / 1000.0);
+ method_buf, from_buf, rdata->pkt_info.src_name, rdata->pkt_info.src_port, callid_buf, msg, count, period / 1000.0);
} else {
- ast_log(LOG_NOTICE, "Request from '%s' failed for '%s:%d' (callid: %s) - No matching endpoint found",
- from_buf, rdata->pkt_info.src_name, rdata->pkt_info.src_port, callid_buf);
+ ast_log(LOG_NOTICE, "Request '%s' from '%s' failed for '%s:%d' (callid: %s) - %s\n",
+ method_buf, from_buf, rdata->pkt_info.src_name, rdata->pkt_info.src_port, callid_buf, msg);
}
}
@@ -405,7 +421,7 @@ static void check_endpoint(pjsip_rx_data *rdata, struct unidentified_request *un
unid->count++;
if (ms < (unidentified_period * 1000) && unid->count >= unidentified_count) {
- log_unidentified_request(rdata, unid->count, ms);
+ log_failed_request(rdata, "No matching endpoint found", unid->count, ms);
ast_sip_report_invalid_endpoint(name, rdata);
}
ao2_unlock(unid);
@@ -479,7 +495,7 @@ static pj_bool_t endpoint_lookup(pjsip_rx_data *rdata)
ao2_ref(unid, -1);
ao2_unlock(unidentified_requests);
} else {
- log_unidentified_request(rdata, 0, 0);
+ log_failed_request(rdata, "No matching endpoint found", 0, 0);
ast_sip_report_invalid_endpoint(name, rdata);
}
}
@@ -487,6 +503,79 @@ static pj_bool_t endpoint_lookup(pjsip_rx_data *rdata)
return PJ_FALSE;
}
+static int apply_endpoint_acl(pjsip_rx_data *rdata, struct ast_sip_endpoint *endpoint)
+{
+ struct ast_sockaddr addr;
+
+ if (ast_acl_list_is_empty(endpoint->acl)) {
+ return 0;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ ast_sockaddr_parse(&addr, rdata->pkt_info.src_name, PARSE_PORT_FORBID);
+ ast_sockaddr_set_port(&addr, rdata->pkt_info.src_port);
+
+ if (ast_apply_acl(endpoint->acl, &addr, "SIP ACL: ") != AST_SENSE_ALLOW) {
+ log_failed_request(rdata, "Not match Endpoint ACL", 0, 0);
+ ast_sip_report_failed_acl(endpoint, rdata, "not_match_endpoint_acl");
+ return 1;
+ }
+ return 0;
+}
+
+static int extract_contact_addr(pjsip_contact_hdr *contact, struct ast_sockaddr **addrs)
+{
+ pjsip_sip_uri *sip_uri;
+ char host[256];
+
+ if (!contact || contact->star) {
+ *addrs = NULL;
+ return 0;
+ }
+ if (!PJSIP_URI_SCHEME_IS_SIP(contact->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact->uri)) {
+ *addrs = NULL;
+ return 0;
+ }
+ sip_uri = pjsip_uri_get_uri(contact->uri);
+ ast_copy_pj_str(host, &sip_uri->host, sizeof(host));
+ return ast_sockaddr_resolve(addrs, host, PARSE_PORT_FORBID, AST_AF_UNSPEC);
+}
+
+static int apply_endpoint_contact_acl(pjsip_rx_data *rdata, struct ast_sip_endpoint *endpoint)
+{
+ int num_contact_addrs;
+ int forbidden = 0;
+ struct ast_sockaddr *contact_addrs;
+ int i;
+ pjsip_contact_hdr *contact = (pjsip_contact_hdr *)&rdata->msg_info.msg->hdr;
+
+ if (ast_acl_list_is_empty(endpoint->contact_acl)) {
+ return 0;
+ }
+
+ while ((contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact->next))) {
+ num_contact_addrs = extract_contact_addr(contact, &contact_addrs);
+ if (num_contact_addrs <= 0) {
+ continue;
+ }
+ for (i = 0; i < num_contact_addrs; ++i) {
+ if (ast_apply_acl(endpoint->contact_acl, &contact_addrs[i], "SIP Contact ACL: ") != AST_SENSE_ALLOW) {
+ log_failed_request(rdata, "Not match Endpoint Contact ACL", 0, 0);
+ ast_sip_report_failed_acl(endpoint, rdata, "not_match_endpoint_contact_acl");
+ forbidden = 1;
+ break;
+ }
+ }
+ ast_free(contact_addrs);
+ if (forbidden) {
+ /* No use checking other contacts if we already have failed ACL check */
+ break;
+ }
+ }
+
+ return forbidden;
+}
+
static pj_bool_t authenticate(pjsip_rx_data *rdata)
{
RAII_VAR(struct ast_sip_endpoint *, endpoint, ast_pjsip_rdata_get_endpoint(rdata), ao2_cleanup);
@@ -494,6 +583,15 @@ static pj_bool_t authenticate(pjsip_rx_data *rdata)
ast_assert(endpoint != NULL);
+ if (endpoint!=artificial_endpoint) {
+ if (apply_endpoint_acl(rdata, endpoint) || apply_endpoint_contact_acl(rdata, endpoint)) {
+ if (!is_ack) {
+ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
+ }
+ return PJ_TRUE;
+ }
+ }
+
if (!is_ack && ast_sip_requires_authentication(endpoint, rdata)) {
pjsip_tx_data *tdata;
struct unidentified_request *unid;
@@ -515,10 +613,12 @@ static pj_bool_t authenticate(pjsip_rx_data *rdata)
pjsip_tx_data_dec_ref(tdata);
return PJ_FALSE;
case AST_SIP_AUTHENTICATION_FAILED:
+ log_failed_request(rdata, "Failed to authenticate", 0, 0);
ast_sip_report_auth_failed_challenge_response(endpoint, rdata);
pjsip_endpt_send_response2(ast_sip_get_pjsip_endpoint(), rdata, tdata, NULL, NULL);
return PJ_TRUE;
case AST_SIP_AUTHENTICATION_ERROR:
+ log_failed_request(rdata, "Error to authenticate", 0, 0);
ast_sip_report_auth_failed_challenge_response(endpoint, rdata);
pjsip_tx_data_dec_ref(tdata);
pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL);
diff --git a/res/res_pjsip/pjsip_options.c b/res/res_pjsip/pjsip_options.c
index f832e6f78..b9339f60b 100644
--- a/res/res_pjsip/pjsip_options.c
+++ b/res/res_pjsip/pjsip_options.c
@@ -1036,17 +1036,11 @@ int ast_sip_initialize_sorcery_qualify(void)
return 0;
}
-static int qualify_and_schedule_cb(void *obj, void *arg, int flags)
+static void qualify_and_schedule_contact(struct ast_sip_contact *contact)
{
- struct ast_sip_contact *contact = obj;
- struct ast_sip_aor *aor = arg;
int initial_interval;
int max_time = ast_sip_get_max_initial_qualify_time();
- contact->qualify_frequency = aor->qualify_frequency;
- contact->qualify_timeout = aor->qualify_timeout;
- contact->authenticate_qualify = aor->authenticate_qualify;
-
/* Delay initial qualification by a random fraction of the specified interval */
if (max_time && max_time < contact->qualify_frequency) {
initial_interval = max_time;
@@ -1062,26 +1056,47 @@ static int qualify_and_schedule_cb(void *obj, void *arg, int flags)
} else {
update_contact_status(contact, UNKNOWN);
}
+}
+
+static int qualify_and_schedule_cb_with_aor(void *obj, void *arg, int flags)
+{
+ struct ast_sip_contact *contact = obj;
+ struct ast_sip_aor *aor = arg;
+
+ contact->qualify_frequency = aor->qualify_frequency;
+ contact->qualify_timeout = aor->qualify_timeout;
+ contact->authenticate_qualify = aor->authenticate_qualify;
+
+ qualify_and_schedule_contact(contact);
+
+ return 0;
+}
+
+static int qualify_and_schedule_cb_without_aor(void *obj, void *arg, int flags)
+{
+ qualify_and_schedule_contact((struct ast_sip_contact *) obj);
return 0;
}
/*!
* \internal
- * \brief Qualify and schedule an endpoint's contacts
+ * \brief Qualify and schedule an aor's contacts
*
- * \details For the given endpoint retrieve its list of aors, qualify all
- * contacts, and schedule for checks if configured.
+ * \details For the given aor check if it has permanent contacts,
+ * qualify all contacts and schedule for checks if configured.
*/
static int qualify_and_schedule_all_cb(void *obj, void *arg, int flags)
{
struct ast_sip_aor *aor = obj;
struct ao2_container *contacts;
- contacts = ast_sip_location_retrieve_aor_contacts(aor);
- if (contacts) {
- ao2_callback(contacts, OBJ_NODATA, qualify_and_schedule_cb, aor);
- ao2_ref(contacts, -1);
+ if (aor->permanent_contacts) {
+ contacts = ast_sip_location_retrieve_aor_contacts(aor);
+ if (contacts) {
+ ao2_callback(contacts, OBJ_NODATA, qualify_and_schedule_cb_with_aor, aor);
+ ao2_ref(contacts, -1);
+ }
}
return 0;
@@ -1104,6 +1119,7 @@ static void qualify_and_schedule_all(void)
{
struct ast_variable *var = ast_variable_new("qualify_frequency >", "0", "");
struct ao2_container *aors;
+ struct ao2_container *contacts;
if (!var) {
return;
@@ -1111,16 +1127,22 @@ static void qualify_and_schedule_all(void)
aors = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(),
"aor", AST_RETRIEVE_FLAG_MULTIPLE, var);
- ast_variables_destroy(var);
-
ao2_callback(sched_qualifies, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, unschedule_all_cb, NULL);
- if (!aors) {
- return;
+ if (aors) {
+ ao2_callback(aors, OBJ_NODATA, qualify_and_schedule_all_cb, NULL);
+ ao2_ref(aors, -1);
}
- ao2_callback(aors, OBJ_NODATA, qualify_and_schedule_all_cb, NULL);
- ao2_ref(aors, -1);
+ contacts = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(),
+ "contact", AST_RETRIEVE_FLAG_MULTIPLE, var);
+ if (contacts) {
+ ao2_callback(contacts, OBJ_NODATA, qualify_and_schedule_cb_without_aor, NULL);
+ ao2_ref(contacts, -1);
+ }
+
+ ast_variables_destroy(var);
+
}
static int format_contact_status(void *obj, void *arg, int flags)
@@ -1186,7 +1208,7 @@ static void aor_observer_modified(const void *obj)
contacts = ast_sip_location_retrieve_aor_contacts(aor);
if (contacts) {
- ao2_callback(contacts, OBJ_NODATA, qualify_and_schedule_cb, aor);
+ ao2_callback(contacts, OBJ_NODATA, qualify_and_schedule_cb_with_aor, aor);
ao2_ref(contacts, -1);
}
}
diff --git a/res/res_pjsip_dtmf_info.c b/res/res_pjsip_dtmf_info.c
index ede515d1c..17297e117 100644
--- a/res/res_pjsip_dtmf_info.c
+++ b/res/res_pjsip_dtmf_info.c
@@ -103,13 +103,13 @@ static int dtmf_info_incoming_request(struct ast_sip_session *session, struct pj
if (!body || !body->len) {
/* need to return 200 OK on empty body */
send_response(session, rdata, 200);
- return 0;
+ return 1;
}
res = body->print_body(body, buf, body->len);
if (res < 0) {
send_response(session, rdata, 500);
- return 0;
+ return 1;
}
buf[res] = '\0';
@@ -150,11 +150,12 @@ static int dtmf_info_incoming_request(struct ast_sip_session *session, struct pj
}
send_response(session, rdata, event ? 200 : 500);
- return event ? 0 : -1;
+ return 1;
}
static struct ast_sip_session_supplement dtmf_info_supplement = {
.method = "INFO",
+ .priority = AST_SIP_SUPPLEMENT_PRIORITY_FIRST,
.incoming_request = dtmf_info_incoming_request,
};
diff --git a/res/res_pjsip_empty_info.c b/res/res_pjsip_empty_info.c
new file mode 100644
index 000000000..09109ba2c
--- /dev/null
+++ b/res/res_pjsip_empty_info.c
@@ -0,0 +1,89 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2016, Digium, Inc.
+ *
+ * Bradley Latus <brad.latus@gmail.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*** MODULEINFO
+ <depend>pjproject</depend>
+ <depend>res_pjsip</depend>
+ <depend>res_pjsip_session</depend>
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_REGISTER_FILE()
+
+#include <pjsip.h>
+#include <pjsip_ua.h>
+
+#include "asterisk/res_pjsip.h"
+#include "asterisk/res_pjsip_session.h"
+#include "asterisk/module.h"
+
+static void send_response(struct ast_sip_session *session,
+ struct pjsip_rx_data *rdata, int code)
+{
+ pjsip_tx_data *tdata;
+ pjsip_dialog *dlg = session->inv_session->dlg;
+
+ if (pjsip_dlg_create_response(dlg, rdata, code, NULL, &tdata) == PJ_SUCCESS) {
+ struct pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
+ pjsip_dlg_send_response(dlg, tsx, tdata);
+ }
+}
+
+static int empty_info_incoming_request(struct ast_sip_session *session,
+ struct pjsip_rx_data *rdata)
+{
+ if (!rdata->msg_info.ctype) {
+ /* Need to return 200 OK on empty body */
+ /* Some SBCs use empty INFO as a KEEPALIVE */
+ send_response(session, rdata, 200);
+ return 1;
+ }
+
+ /* Let another module respond */
+ return 0;
+
+}
+
+static struct ast_sip_session_supplement empty_info_supplement = {
+ .method = "INFO",
+ .priority = AST_SIP_SUPPLEMENT_PRIORITY_LAST,
+ .incoming_request = empty_info_incoming_request,
+};
+
+static int load_module(void)
+{
+ CHECK_PJSIP_SESSION_MODULE_LOADED();
+
+ ast_sip_session_register_supplement(&empty_info_supplement);
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ ast_sip_session_unregister_supplement(&empty_info_supplement);
+ return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Empty INFO Support",
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_APP_DEPEND,
+);
diff --git a/res/res_pjsip_one_touch_record_info.c b/res/res_pjsip_one_touch_record_info.c
index 8b1ff9dab..e15b0d815 100644
--- a/res/res_pjsip_one_touch_record_info.c
+++ b/res/res_pjsip_one_touch_record_info.c
@@ -72,13 +72,13 @@ static int handle_incoming_request(struct ast_sip_session *session, struct pjsip
if (!session->channel) {
send_response(session, 481, rdata);
- return 0;
+ return 1;
}
/* Is this endpoint configured with One Touch Recording? */
if (!session->endpoint->info.recording.enabled || ast_strlen_zero(feature)) {
send_response(session, 403, rdata);
- return 0;
+ return 1;
}
ast_channel_lock(session->channel);
@@ -87,7 +87,7 @@ static int handle_incoming_request(struct ast_sip_session *session, struct pjsip
if (feature_res || ast_strlen_zero(feature_code)) {
send_response(session, 403, rdata);
- return 0;
+ return 1;
}
for (digit = feature_code; *digit; ++digit) {
@@ -97,11 +97,12 @@ static int handle_incoming_request(struct ast_sip_session *session, struct pjsip
send_response(session, 200, rdata);
- return 0;
+ return 1;
}
static struct ast_sip_session_supplement info_supplement = {
.method = "INFO",
+ .priority = AST_SIP_SUPPLEMENT_PRIORITY_FIRST,
.incoming_request = handle_incoming_request,
};
diff --git a/res/res_pjsip_outbound_publish.c b/res/res_pjsip_outbound_publish.c
index 60f9bbb17..ab8a6c9bd 100644
--- a/res/res_pjsip_outbound_publish.c
+++ b/res/res_pjsip_outbound_publish.c
@@ -406,22 +406,30 @@ static void sip_outbound_publish_synchronize(struct ast_sip_event_publisher_hand
ao2_ref(states, -1);
}
-struct ast_sip_outbound_publish_client *ast_sip_publish_client_get(const char *name)
+static struct ast_sip_outbound_publish_state *sip_publish_state_get(const char *id)
{
- RAII_VAR(struct ao2_container *, states,
- ao2_global_obj_ref(current_states), ao2_cleanup);
- RAII_VAR(struct ast_sip_outbound_publish_state *, state, NULL, ao2_cleanup);
+ struct ao2_container *states = ao2_global_obj_ref(current_states);
+ struct ast_sip_outbound_publish_state *res;
if (!states) {
return NULL;
}
- state = ao2_find(states, name, OBJ_SEARCH_KEY);
+ res = ao2_find(states, id, OBJ_SEARCH_KEY);
+ ao2_ref(states, -1);
+ return res;
+}
+
+struct ast_sip_outbound_publish_client *ast_sip_publish_client_get(const char *name)
+{
+ struct ast_sip_outbound_publish_state *state = sip_publish_state_get(name);
+
if (!state) {
return NULL;
}
ao2_ref(state->client, +1);
+ ao2_ref(state, -1);
return state->client;
}
@@ -687,8 +695,15 @@ static int explicit_publish_destroy(void *data)
{
struct ast_sip_outbound_publish_client *client = data;
- pjsip_publishc_destroy(client->client);
- ao2_ref(client, -1);
+ /*
+ * If there is no pjsip publishing client then we obviously don't need
+ * to destroy it. Also, the ref for the Asterisk publishing client that
+ * pjsip had would not exist or should already be gone as well.
+ */
+ if (client->client) {
+ pjsip_publishc_destroy(client->client);
+ ao2_ref(client, -1);
+ }
return 0;
}
@@ -868,6 +883,11 @@ static int sip_outbound_publish_client_alloc(void *data)
/*! \brief Callback function for publish client responses */
static void sip_outbound_publish_callback(struct pjsip_publishc_cbparam *param)
{
+#define DESTROY_CLIENT() do { \
+ pjsip_publishc_destroy(client->client); \
+ client->client = NULL; \
+ ao2_ref(client, -1); } while (0)
+
RAII_VAR(struct ast_sip_outbound_publish_client *, client, ao2_bump(param->token), ao2_cleanup);
RAII_VAR(struct ast_sip_outbound_publish *, publish, ao2_bump(client->publish), ao2_cleanup);
SCOPED_AO2LOCK(lock, client);
@@ -885,8 +905,7 @@ static void sip_outbound_publish_callback(struct pjsip_publishc_cbparam *param)
ao2_ref(client, -1);
}
/* Once the destroy is called this callback will not get called any longer, so drop the client ref */
- pjsip_publishc_destroy(client->client);
- ao2_ref(client, -1);
+ DESTROY_CLIENT();
return;
}
@@ -904,9 +923,7 @@ static void sip_outbound_publish_callback(struct pjsip_publishc_cbparam *param)
client->auth_attempts++;
if (client->auth_attempts == publish->max_auth_attempts) {
- pjsip_publishc_destroy(client->client);
- client->client = NULL;
-
+ DESTROY_CLIENT();
ast_log(LOG_ERROR, "Reached maximum number of PUBLISH authentication attempts on outbound publish '%s'\n",
ast_sorcery_object_get_id(publish));
@@ -918,9 +935,7 @@ static void sip_outbound_publish_callback(struct pjsip_publishc_cbparam *param)
client->auth_attempts = 0;
if (param->code == 412) {
- pjsip_publishc_destroy(client->client);
- client->client = NULL;
-
+ DESTROY_CLIENT();
if (sip_outbound_publish_client_alloc(client)) {
ast_log(LOG_ERROR, "Failed to create a new outbound publish client for '%s' on 412 response\n",
ast_sorcery_object_get_id(publish));
@@ -935,10 +950,9 @@ static void sip_outbound_publish_callback(struct pjsip_publishc_cbparam *param)
expires = pjsip_msg_find_hdr(param->rdata->msg_info.msg, PJSIP_H_MIN_EXPIRES, NULL);
if (!expires || !expires->ivalue) {
+ DESTROY_CLIENT();
ast_log(LOG_ERROR, "Received 423 response on outbound publish '%s' without a Min-Expires header\n",
ast_sorcery_object_get_id(publish));
- pjsip_publishc_destroy(client->client);
- client->client = NULL;
goto end;
}
@@ -1065,25 +1079,89 @@ static struct ast_sip_outbound_publish_state *sip_outbound_publish_state_alloc(
return state;
}
-/*! \brief Apply function which finds or allocates a state structure */
-static int sip_outbound_publish_apply(const struct ast_sorcery *sorcery, void *obj)
+static int initialize_publish_client(struct ast_sip_outbound_publish *publish,
+ struct ast_sip_outbound_publish_state *state)
{
- RAII_VAR(struct ao2_container *, states, ao2_global_obj_ref(current_states), ao2_cleanup);
- RAII_VAR(struct ast_sip_outbound_publish_state *, state, NULL, ao2_cleanup);
- struct ast_sip_outbound_publish *applied = obj;
+ if (ast_sip_push_task_synchronous(NULL, sip_outbound_publish_client_alloc, state->client)) {
+ ast_log(LOG_ERROR, "Unable to create client for outbound publish '%s'\n",
+ ast_sorcery_object_get_id(publish));
+ return -1;
+ }
- if (ast_strlen_zero(applied->server_uri)) {
+ return 0;
+}
+
+static int validate_publish_config(struct ast_sip_outbound_publish *publish)
+{
+ if (ast_strlen_zero(publish->server_uri)) {
ast_log(LOG_ERROR, "No server URI specified on outbound publish '%s'\n",
- ast_sorcery_object_get_id(applied));
+ ast_sorcery_object_get_id(publish));
return -1;
- } else if (ast_strlen_zero(applied->event)) {
+ } else if (ast_strlen_zero(publish->event)) {
ast_log(LOG_ERROR, "No event type specified for outbound publish '%s'\n",
- ast_sorcery_object_get_id(applied));
+ ast_sorcery_object_get_id(publish));
return -1;
}
+ return 0;
+}
+
+static int current_state_reusable(struct ast_sip_outbound_publish *publish,
+ struct ast_sip_outbound_publish_state *current_state)
+{
+ struct ast_sip_outbound_publish *old_publish;
+ if (!can_reuse_publish(current_state->client->publish, publish)) {
+ /*
+ * Something significant has changed in the configuration, so we are
+ * unable to use the old state object. The current state needs to go
+ * away and a new one needs to be created.
+ */
+ return 0;
+ }
+
+ /*
+ * We can reuse the current state object so keep it, but swap out the
+ * underlying publish object with the new one.
+ */
+ old_publish = current_state->client->publish;
+ current_state->client->publish = publish;
+ if (initialize_publish_client(publish, current_state)) {
+ /*
+ * If the state object fails to re-initialize then swap
+ * the old publish info back in.
+ */
+ current_state->client->publish = publish;
+ return -1;
+ }
+
+ /*
+ * Since we swapped out the publish object the new one needs a ref
+ * while the old one needs to go away.
+ */
+ ao2_ref(current_state->client->publish, +1);
+ ao2_cleanup(old_publish);
+
+ /* Tell the caller that the current state object should be used */
+ return 1;
+}
+
+/*! \brief Apply function which finds or allocates a state structure */
+static int sip_outbound_publish_apply(const struct ast_sorcery *sorcery, void *obj)
+{
+#define ADD_TO_NEW_STATES(__obj) \
+ do { if (__obj) { \
+ ao2_link(new_states, __obj); \
+ ao2_ref(__obj, -1); } } while (0)
+
+ struct ast_sip_outbound_publish *applied = obj;
+ struct ast_sip_outbound_publish_state *current_state, *new_state;
+ int res;
+
+ /*
+ * New states are being loaded or reloaded. We'll need to add the new
+ * object if created/updated, or keep the old object if an error occurs.
+ */
if (!new_states) {
- /* make sure new_states has been allocated as we will be adding to it */
new_states = ao2_container_alloc_options(
AO2_ALLOC_OPT_LOCK_NOLOCK, DEFAULT_STATE_BUCKETS,
outbound_publish_state_hash, outbound_publish_state_cmp);
@@ -1094,35 +1172,44 @@ static int sip_outbound_publish_apply(const struct ast_sorcery *sorcery, void *o
}
}
- if (states) {
- state = ao2_find(states, ast_sorcery_object_get_id(obj), OBJ_SEARCH_KEY);
- if (state) {
- if (can_reuse_publish(state->client->publish, applied)) {
- ao2_replace(state->client->publish, applied);
- } else {
- ao2_ref(state, -1);
- state = NULL;
- }
- }
+ /* If there is current state we'll want to maintain it if any errors occur */
+ current_state = sip_publish_state_get(ast_sorcery_object_get_id(applied));
+
+ if ((res = validate_publish_config(applied))) {
+ ADD_TO_NEW_STATES(current_state);
+ return res;
}
- if (!state) {
- state = sip_outbound_publish_state_alloc(applied);
- if (!state) {
- ast_log(LOG_ERROR, "Unable to create state for outbound publish '%s'\n",
- ast_sorcery_object_get_id(applied));
- return -1;
- };
+ if (current_state && (res = current_state_reusable(applied, current_state))) {
+ /*
+ * The current state object was able to be reused, or an error
+ * occurred. Either way we keep the current state and be done.
+ */
+ ADD_TO_NEW_STATES(current_state);
+ return res == 1 ? 0 : -1;
}
- if (ast_sip_push_task_synchronous(NULL, sip_outbound_publish_client_alloc, state->client)) {
- ast_log(LOG_ERROR, "Unable to create client for outbound publish '%s'\n",
+ /*
+ * No current state was found or it was unable to be reused. Either way
+ * we'll need to create a new state object.
+ */
+ new_state = sip_outbound_publish_state_alloc(applied);
+ if (!new_state) {
+ ast_log(LOG_ERROR, "Unable to create state for outbound publish '%s'\n",
ast_sorcery_object_get_id(applied));
+ ADD_TO_NEW_STATES(current_state);
+ return -1;
+ };
+
+ if (initialize_publish_client(applied, new_state)) {
+ ADD_TO_NEW_STATES(current_state);
+ ao2_ref(new_state, -1);
return -1;
}
- ao2_link(new_states, state);
- return 0;
+ ADD_TO_NEW_STATES(new_state);
+ ao2_cleanup(current_state);
+ return res;
}
static int outbound_auth_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
@@ -1141,6 +1228,7 @@ static int load_module(void)
if (ast_sorcery_object_register(ast_sip_get_sorcery(), "outbound-publish", sip_outbound_publish_alloc, NULL,
sip_outbound_publish_apply)) {
+ ast_log(LOG_ERROR, "Unable to register 'outbound-publish' type with sorcery\n");
return AST_MODULE_LOAD_DECLINE;
}
@@ -1200,7 +1288,7 @@ static int unload_module(void)
/* wait for items to unpublish */
ast_verb(5, "Waiting to complete unpublishing task(s)\n");
- while (unloading.count) {
+ while (unloading.count && !res) {
res = ast_cond_timedwait(&unloading.cond, &unloading.lock, &end);
}
ast_mutex_unlock(&unloading.lock);
@@ -1213,6 +1301,7 @@ static int unload_module(void)
"in the allowed time\n", unloading.count);
} else {
ast_verb(5, "All items successfully unpublished\n");
+ ast_sorcery_object_unregister(ast_sip_get_sorcery(), "outbound-publish");
}
return res;
diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c
index 8a40cce23..6f17b2072 100644
--- a/res/res_pjsip_outbound_registration.c
+++ b/res/res_pjsip_outbound_registration.c
@@ -1096,7 +1096,7 @@ static int sip_dialog_create_contact(pj_pool_t *pool, pj_str_t *contact, const c
contact->ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
contact->slen = pj_ansi_snprintf(contact->ptr, PJSIP_MAX_URL_SIZE,
"<%s:%s@%s%.*s%s:%d%s%s%s%s>",
- (pjsip_transport_get_flag_from_type(type) & PJSIP_TRANSPORT_SECURE) ? "sips" : "sip",
+ ((pjsip_transport_get_flag_from_type(type) & PJSIP_TRANSPORT_SECURE) && PJSIP_URI_SCHEME_IS_SIPS(uri)) ? "sips" : "sip",
user,
(type & PJSIP_TRANSPORT_IPV6) ? "[" : "",
(int)local_addr.slen,
@@ -1912,6 +1912,26 @@ static const struct ast_sorcery_instance_observer observer_callbacks_registratio
.object_type_loaded = registration_loaded_observer,
};
+static void registration_deleted_observer(const void *obj)
+{
+ const struct sip_outbound_registration *registration = obj;
+ struct ao2_container *states;
+
+ states = ao2_global_obj_ref(current_states);
+ if (!states) {
+ /* Global container has gone. Likely shutting down. */
+ return;
+ }
+
+ ao2_find(states, ast_sorcery_object_get_id(registration), OBJ_UNLINK | OBJ_NODATA | OBJ_SEARCH_KEY);
+
+ ao2_ref(states, -1);
+}
+
+static const struct ast_sorcery_observer registration_observer = {
+ .deleted = registration_deleted_observer,
+};
+
static int unload_module(void)
{
int remaining;
@@ -2011,7 +2031,9 @@ static int load_module(void)
if (ast_sorcery_instance_observer_add(ast_sip_get_sorcery(),
&observer_callbacks_registrations)
|| ast_sorcery_observer_add(ast_sip_get_sorcery(), "auth",
- &observer_callbacks_auth)) {
+ &observer_callbacks_auth)
+ || ast_sorcery_observer_add(ast_sip_get_sorcery(), "registration",
+ &registration_observer)) {
ast_log(LOG_ERROR, "Unable to register observers.\n");
unload_module();
return AST_MODULE_LOAD_FAILURE;
diff --git a/res/res_pjsip_publish_asterisk.c b/res/res_pjsip_publish_asterisk.c
index 3218b0a0c..002d976d4 100644
--- a/res/res_pjsip_publish_asterisk.c
+++ b/res/res_pjsip_publish_asterisk.c
@@ -862,6 +862,7 @@ static int load_module(void)
ast_sorcery_apply_default(ast_sip_get_sorcery(), "asterisk-publication", "config", "pjsip.conf,criteria=type=asterisk-publication");
if (ast_sorcery_object_register(ast_sip_get_sorcery(), "asterisk-publication", asterisk_publication_config_alloc, NULL, NULL)) {
+ ast_log(LOG_ERROR, "Unable to register 'asterisk-publication' type with sorcery\n");
return AST_MODULE_LOAD_DECLINE;
}
@@ -919,6 +920,7 @@ static int unload_module(void)
ast_sip_unregister_publish_handler(&asterisk_mwi_publication_handler);
ast_sip_unregister_event_publisher_handler(&asterisk_devicestate_publisher_handler);
ast_sip_unregister_event_publisher_handler(&asterisk_mwi_publisher_handler);
+ ast_sorcery_object_unregister(ast_sip_get_sorcery(), "asterisk-publication");
return 0;
}
diff --git a/res/res_sorcery_astdb.c b/res/res_sorcery_astdb.c
index e5de9f7bb..1aec0be95 100644
--- a/res/res_sorcery_astdb.c
+++ b/res/res_sorcery_astdb.c
@@ -79,6 +79,58 @@ static int sorcery_astdb_create(const struct ast_sorcery *sorcery, void *data, v
return ast_db_put(family, ast_sorcery_object_get_id(object), value);
}
+/*! \brief Internal helper function which returns a filtered objectset.
+ *
+ * The following are filtered out of the objectset:
+ * \li Fields that are not registered with sorcery.
+ *
+ * \param objectset Objectset to filter.
+ * \param sorcery The sorcery instance that is requesting an objectset.
+ * \param type The object type
+ *
+ * \return The filtered objectset
+ */
+static struct ast_variable *sorcery_astdb_filter_objectset(struct ast_variable *objectset, const struct ast_sorcery *sorcery,
+ const char *type)
+{
+ struct ast_variable *previous = NULL, *field = objectset;
+ struct ast_sorcery_object_type *object_type;
+
+ object_type = ast_sorcery_get_object_type(sorcery, type);
+ if (!object_type) {
+ ast_log(LOG_WARNING, "Unknown sorcery object type %s. Expect errors\n", type);
+ return objectset;
+ }
+
+ while (field) {
+ struct ast_variable *removed;
+
+ if (ast_sorcery_is_object_field_registered(object_type, field->name)) {
+ previous = field;
+ field = field->next;
+ continue;
+ }
+
+ ast_debug(1, "Filtering out astdb field '%s' from retrieval\n", field->name);
+
+ if (previous) {
+ previous->next = field->next;
+ } else {
+ objectset = field->next;
+ }
+
+ removed = field;
+ field = field->next;
+ removed->next = NULL;
+
+ ast_variables_destroy(removed);
+ }
+
+ ao2_cleanup(object_type);
+
+ return objectset;
+}
+
/*! \brief Internal helper function which retrieves an object, or multiple objects, using fields for criteria */
static void *sorcery_astdb_retrieve_fields_common(const struct ast_sorcery *sorcery, void *data, const char *type, const struct ast_variable *fields, struct ao2_container *objects)
{
@@ -97,23 +149,25 @@ static void *sorcery_astdb_retrieve_fields_common(const struct ast_sorcery *sorc
const char *key = entry->key + strlen(family) + 2;
RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
struct ast_json_error error;
- RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy);
RAII_VAR(struct ast_variable *, existing, NULL, ast_variables_destroy);
void *object = NULL;
if (!(json = ast_json_load_string(entry->data, &error))) {
return NULL;
}
+
if (ast_json_to_ast_variables(json, &existing) != AST_JSON_TO_AST_VARS_CODE_SUCCESS) {
return NULL;
}
+ existing = sorcery_astdb_filter_objectset(existing, sorcery, type);
+
if (fields && !ast_variable_lists_match(existing, fields, 0)) {
continue;
}
if (!(object = ast_sorcery_alloc(sorcery, type, key)) ||
- ast_sorcery_objectset_apply(sorcery, object, objset)) {
+ ast_sorcery_objectset_apply(sorcery, object, existing)) {
ao2_cleanup(object);
return NULL;
}
@@ -149,6 +203,7 @@ static void *sorcery_astdb_retrieve_id(const struct ast_sorcery *sorcery, void *
if (ast_db_get_allocated(family, id, &value)
|| !(json = ast_json_load_string(value, &error))
|| (ast_json_to_ast_variables(json, &objset) != AST_JSON_TO_AST_VARS_CODE_SUCCESS)
+ || !(objset = sorcery_astdb_filter_objectset(objset, sorcery, type))
|| !(object = ast_sorcery_alloc(sorcery, type, id))
|| ast_sorcery_objectset_apply(sorcery, object, objset)) {
ast_debug(3, "Failed to retrieve object '%s' from astdb\n", id);
@@ -261,6 +316,7 @@ static void sorcery_astdb_retrieve_regex(const struct ast_sorcery *sorcery, void
continue;
} else if (!(json = ast_json_load_string(entry->data, &error))
|| (ast_json_to_ast_variables(json, &objset) != AST_JSON_TO_AST_VARS_CODE_SUCCESS)
+ || !(objset = sorcery_astdb_filter_objectset(objset, sorcery, type))
|| !(object = ast_sorcery_alloc(sorcery, type, key))
|| ast_sorcery_objectset_apply(sorcery, object, objset)) {
regfree(&expression);